Files
remote-sensing/batch_interface.html
T
2026-03-07 17:13:59 +07:00

789 lines
35 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Batch Processing - Land Classification</title>
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css" />
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1600px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
.content {
padding: 30px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
.section {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
}
.section h2 {
color: #667eea;
margin-bottom: 15px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #333;
font-weight: 600;
}
.form-group input, .form-group select {
width: 100%;
padding: 10px;
border: 2px solid #e0e0e0;
border-radius: 5px;
font-size: 1em;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 5px;
font-size: 1em;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
margin-right: 10px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-success {
background: #28a745;
color: white;
}
.btn-danger {
background: #dc3545;
color: white;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.batch-item {
background: white;
padding: 15px;
margin-bottom: 10px;
border-radius: 8px;
border-left: 4px solid #667eea;
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 20px;
}
.batch-item.completed {
border-left-color: #28a745;
}
.batch-item.failed {
border-left-color: #dc3545;
}
.batch-item.running {
border-left-color: #ffc107;
}
.progress {
height: 25px;
background: #e0e0e0;
border-radius: 12px;
overflow: hidden;
margin-top: 10px;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
transition: width 0.3s;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 600;
font-size: 0.9em;
}
.alert {
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.alert-info {
background: #d1ecf1;
border-left: 4px solid #0c5460;
color: #0c5460;
}
.alert-success {
background: #d4edda;
border-left: 4px solid #155724;
color: #155724;
}
.jobs-list {
max-height: 500px;
overflow-y: auto;
}
#batchMap {
height: 400px;
border-radius: 10px;
margin-top: 15px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 Batch Processing</h1>
<p>Xử lý nhiều khu vực cùng lúc với model đã train</p>
</div>
<div style="background: white; padding: 15px; display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; border-bottom: 2px solid #e0e0e0;">
<a href="/" style="padding: 10px 20px; background: #667eea; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🏠 Trang Chủ</a>
<a href="/training" style="padding: 10px 20px; background: #f093fb; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🎓 Training</a>
<a href="/prediction" style="padding: 10px 20px; background: #4facfe; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🗺️ Prediction</a>
<a href="/batch" style="padding: 10px 20px; background: #764ba2; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🚀 Batch Processing (Active)</a>
<a href="/ndvi" style="padding: 10px 20px; background: #2ecc71; color: white; border-radius: 8px; text-decoration: none; font-weight: 600;">🌿 NDVI Analysis</a>
</div>
<div class="content">
<!-- Configuration Section -->
<div class="section">
<h2>⚙️ Cấu hình Batch</h2>
<div class="form-group">
<label for="batchModelSelect">Model:</label>
<select id="batchModelSelect">
<option value="">Đang tải...</option>
</select>
</div>
<div class="form-group">
<label for="batchName">Tên khu vực:</label>
<input type="text" id="batchName" placeholder="Ví dụ: Khu vực A">
</div>
<div class="form-group">
<label>Bbox (từ bản đồ hoặc nhập thủ công):</label>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
<input type="number" id="batchMinLon" placeholder="Min Lon" step="0.0001">
<input type="number" id="batchMinLat" placeholder="Min Lat" step="0.0001">
<input type="number" id="batchMaxLon" placeholder="Max Lon" step="0.0001">
<input type="number" id="batchMaxLat" placeholder="Max Lat" step="0.0001">
</div>
</div>
<div class="form-group">
<label>Thời gian:</label>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
<input type="date" id="batchStartDate" value="2023-03-01">
<input type="date" id="batchEndDate" value="2023-05-31">
</div>
</div>
<button class="btn btn-primary" onclick="addBatchItem()">
Thêm vào Batch
</button>
<!-- Map for selecting bbox -->
<div id="batchMap"></div>
</div>
<!-- Batch Queue Section -->
<div class="section">
<h2>📋 Batch Queue (<span id="queueCount">0</span> items)</h2>
<div id="batchQueue" class="jobs-list">
<p style="text-align: center; color: #666;">Chưa có item nào. Thêm khu vực từ bên trái.</p>
</div>
<div style="margin-top: 20px;">
<button class="btn btn-success" onclick="startBatch()" id="startBatchBtn" disabled>
🚀 Start Batch Processing
</button>
<button class="btn btn-danger" onclick="clearBatchQueue()">
🗑️ Clear Queue
</button>
</div>
</div>
<!-- Status Section -->
<div class="section" style="grid-column: 1 / -1;">
<h2>📊 Batch Status</h2>
<div id="batchStatus" style="display: none;">
<div class="alert alert-info">
<p><strong>Batch ID:</strong> <span id="currentBatchId"></span></p>
<p><strong>Status:</strong> Queued: <span id="statusQueued">0</span> | Running: <span id="statusRunning">0</span> | Completed: <span id="statusCompleted">0</span> | Failed: <span id="statusFailed">0</span></p>
</div>
</div>
<div id="runningJobs" class="jobs-list">
<!-- Running jobs will appear here -->
</div>
</div>
<!-- Results Section -->
<div class="section" style="grid-column: 1 / -1;">
<h2>✅ Completed Results</h2>
<div style="margin-bottom: 15px; display: flex; gap: 10px; align-items: center;">
<button class="btn btn-primary" onclick="loadAllBatchResults()" style="padding: 8px 20px;">
🔄 Refresh Results
</button>
<button class="btn btn-success" onclick="downloadAllResults()" style="padding: 8px 20px;">
📦 Download All (Bulk)
</button>
</div>
<div id="completedResults" class="jobs-list">
<p style="text-align: center; color: #666;">Chưa có kết quả nào</p>
</div>
</div>
</div>
</div>
<!-- Modal for large preview -->
<div id="previewModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 10000; padding: 20px;">
<div style="position: relative; height: 100%; display: flex; align-items: center; justify-content: center;">
<button onclick="closePreviewModal()" style="position: absolute; top: 20px; right: 20px; background: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 18px; font-weight: bold;">
✕ Close
</button>
<img id="previewImage" style="max-width: 90%; max-height: 90%; border-radius: 10px;">
</div>
</div>
<!-- Scripts -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.js"></script>
<script>
let map, drawnItems, drawControl;
let batchQueue = [];
let currentBatchId = null;
let statusCheckInterval = null;
// Initialize map
function initMap() {
map = L.map('batchMap').setView([9.5, 105.9], 9);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
drawControl = new L.Control.Draw({
draw: {
rectangle: true,
polygon: false,
circle: false,
marker: false,
polyline: false,
circlemarker: false
},
edit: {
featureGroup: drawnItems,
remove: true
}
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, function(event) {
drawnItems.clearLayers();
const layer = event.layer;
drawnItems.addLayer(layer);
const bounds = layer.getBounds();
document.getElementById('batchMinLon').value = bounds.getWest().toFixed(4);
document.getElementById('batchMinLat').value = bounds.getSouth().toFixed(4);
document.getElementById('batchMaxLon').value = bounds.getEast().toFixed(4);
document.getElementById('batchMaxLat').value = bounds.getNorth().toFixed(4);
});
}
// Load models
async function loadModels() {
try {
const response = await fetch('/api/models/list');
const data = await response.json();
const select = document.getElementById('batchModelSelect');
select.innerHTML = '<option value="">Chọn model...</option>';
data.models.filter(m => m.filename.endsWith('.joblib')).forEach(model => {
const option = document.createElement('option');
option.value = model.filename;
option.textContent = `${model.filename} - ${model.created}`;
select.appendChild(option);
});
if (data.models.length > 0) {
select.value = data.models[0].filename;
}
} catch (error) {
console.error('Error loading models:', error);
}
}
// Add item to batch queue
function addBatchItem() {
const name = document.getElementById('batchName').value;
const minLon = parseFloat(document.getElementById('batchMinLon').value);
const minLat = parseFloat(document.getElementById('batchMinLat').value);
const maxLon = parseFloat(document.getElementById('batchMaxLon').value);
const maxLat = parseFloat(document.getElementById('batchMaxLat').value);
const startDate = document.getElementById('batchStartDate').value;
const endDate = document.getElementById('batchEndDate').value;
if (!name || isNaN(minLon) || isNaN(minLat) || isNaN(maxLon) || isNaN(maxLat)) {
alert('❌ Vui lòng điền đầy đủ thông tin!');
return;
}
const item = {
name,
min_lon: minLon,
min_lat: minLat,
max_lon: maxLon,
max_lat: maxLat,
start_date: startDate,
end_date: endDate,
max_scenes: 12,
cloud_cover: 30,
resolution: 20
};
batchQueue.push(item);
updateBatchQueueDisplay();
// Clear form
document.getElementById('batchName').value = '';
drawnItems.clearLayers();
}
// Update batch queue display
function updateBatchQueueDisplay() {
const queueDiv = document.getElementById('batchQueue');
const countSpan = document.getElementById('queueCount');
countSpan.textContent = batchQueue.length;
if (batchQueue.length === 0) {
queueDiv.innerHTML = '<p style="text-align: center; color: #666;">Chưa có item nào. Thêm khu vực từ bên trái.</p>';
document.getElementById('startBatchBtn').disabled = true;
return;
}
document.getElementById('startBatchBtn').disabled = false;
queueDiv.innerHTML = batchQueue.map((item, idx) => `
<div class="batch-item">
<div>
<strong>${item.name}</strong><br>
<small>Bbox: (${item.min_lon.toFixed(2)}, ${item.min_lat.toFixed(2)}) → (${item.max_lon.toFixed(2)}, ${item.max_lat.toFixed(2)})</small><br>
<small>Time: ${item.start_date}${item.end_date}</small>
</div>
<button class="btn btn-danger" style="padding: 5px 15px;" onclick="removeBatchItem(${idx})">
</button>
</div>
`).join('');
}
// Remove item from queue
function removeBatchItem(index) {
batchQueue.splice(index, 1);
updateBatchQueueDisplay();
}
// Clear batch queue
function clearBatchQueue() {
if (!confirm('Xóa tất cả items trong queue?')) return;
batchQueue = [];
updateBatchQueueDisplay();
}
// Start batch processing
async function startBatch() {
const modelFilename = document.getElementById('batchModelSelect').value;
if (!modelFilename) {
alert('❌ Vui lòng chọn model!');
return;
}
if (batchQueue.length === 0) {
alert('❌ Batch queue trống!');
return;
}
const config = {
model_filename: modelFilename,
items: batchQueue,
auto_retry: true,
max_retries: 3
};
try {
const response = await fetch('/api/batch/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
});
const result = await response.json();
if (response.ok) {
currentBatchId = result.batch_id;
document.getElementById('currentBatchId').textContent = currentBatchId;
document.getElementById('batchStatus').style.display = 'block';
// Clear local queue
batchQueue = [];
updateBatchQueueDisplay();
// Start monitoring
startStatusCheck();
alert(`✅ Đã bắt đầu batch processing với ${result.total_jobs} jobs!`);
} else {
throw new Error(result.detail || 'Lỗi khi bắt đầu batch');
}
} catch (error) {
console.error('Error starting batch:', error);
alert('❌ Lỗi: ' + error.message);
}
}
// Check batch status
async function checkBatchStatus() {
try {
const response = await fetch('/api/batch/status');
const status = await response.json();
// Update status counts
document.getElementById('statusQueued').textContent = status.queue.queued;
document.getElementById('statusRunning').textContent = status.queue.running;
document.getElementById('statusCompleted').textContent = status.queue.completed;
document.getElementById('statusFailed').textContent = status.queue.failed;
// Update running jobs
const runningDiv = document.getElementById('runningJobs');
if (status.jobs.running.length > 0) {
runningDiv.innerHTML = status.jobs.running.map(job => {
const outputFile = job.result?.output_file || '';
const pngFile = job.result?.png_file || '';
const outputFilename = outputFile ? outputFile.split('/').pop() : '';
const pngFilename = pngFile ? pngFile.split('/').pop() : '';
return `
<div class="batch-item running">
<div style="flex: 1;">
<strong>${job.name}</strong> - <span style="color: #ffc107;">Running</span><br>
<small>Job ID: ${job.job_id}</small>
<div class="progress">
<div class="progress-bar" style="width: ${job.progress}%">${job.progress}%</div>
</div>
</div>
<div style="display: flex; flex-direction: column; gap: 8px; min-width: 200px; flex-shrink: 0;">
<button class="btn btn-success" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="downloadResult('${outputFilename}')"
${outputFilename ? '' : 'disabled'}>
💾 Download GeoTIFF
</button>
<button class="btn btn-primary" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="downloadPNG('${pngFilename}')"
${pngFilename ? '' : 'disabled'}>
🖼️ Download PNG
</button>
<button class="btn btn-secondary" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="viewLargePNG('${pngFilename}')"
${pngFilename ? '' : 'disabled'}>
🔍 View Preview
</button>
</div>
</div>
`;
}).join('');
} else {
runningDiv.innerHTML = '<p style="text-align: center; color: #666;">Không có job nào đang chạy</p>';
}
// Update completed results
const completedDiv = document.getElementById('completedResults');
if (status.jobs.recent_completed.length > 0) {
completedDiv.innerHTML = status.jobs.recent_completed.map(job => {
const outputFile = job.result?.output_file || '';
const pngFile = job.result?.png_file || '';
const outputFilename = outputFile ? outputFile.split('/').pop() : '';
const pngFilename = pngFile ? pngFile.split('/').pop() : '';
return `
<div class="batch-item completed">
<div style="flex: 1;">
<strong>${job.name}</strong> - <span style="color: #28a745;">✓ Completed</span><br>
<small>Job ID: ${job.job_id}</small><br>
<small>Completed: ${new Date(job.completed_at).toLocaleString()}</small><br>
<small><strong>Shape:</strong> ${job.result?.shape ? job.result.shape.join(' x ') : 'N/A'}</small><br>
<small><strong>Classes:</strong> ${job.result?.unique_classes ? job.result.unique_classes.join(', ') : 'N/A'}</small><br>
<small><strong>Features:</strong> ${job.result?.n_features || 'N/A'}</small><br>
<small><strong>Output:</strong> ${outputFilename || 'N/A'}</small><br>
<div style="margin-top: 10px;">
<img src="/api/predictions/preview/${pngFilename}"
style="max-width: 100%; max-height: 300px; border-radius: 5px; cursor: pointer; ${pngFilename ? '' : 'display:none;'}"
onclick="viewLargePNG('${pngFilename}')"
title="Click để xem lớn hơn"
onerror="this.style.display='none'">
</div>
</div>
<div style="display: flex; flex-direction: column; gap: 8px; min-width: 200px; flex-shrink: 0;">
<button class="btn btn-success" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="downloadResult('${outputFilename}')"
${outputFilename ? '' : 'disabled'}>
💾 Download GeoTIFF
</button>
<button class="btn btn-primary" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="downloadPNG('${pngFilename}')"
${pngFilename ? '' : 'disabled'}>
🖼️ Download PNG
</button>
<button class="btn btn-secondary" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="viewLargePNG('${pngFilename}')"
${pngFilename ? '' : 'disabled'}>
🔍 View Preview
</button>
</div>
</div>
`;
}).join('');
} else {
completedDiv.innerHTML = '<p style="text-align: center; color: #666;">Chưa có kết quả nào</p>';
}
// Show failed jobs if any
if (status.jobs.recent_failed.length > 0) {
const failedHTML = status.jobs.recent_failed.map(job => {
const outputFile = job.result?.output_file || '';
const pngFile = job.result?.png_file || '';
const outputFilename = outputFile ? outputFile.split('/').pop() : '';
const pngFilename = pngFile ? pngFile.split('/').pop() : '';
return `
<div class="batch-item failed">
<div style="flex: 1;">
<strong>${job.name}</strong> - <span style="color: #dc3545;">✗ Failed</span><br>
<small>Job ID: ${job.job_id}</small><br>
<small style="color: #dc3545;">${job.error || 'Unknown error'}</small>
</div>
<div style="display: flex; flex-direction: column; gap: 8px; min-width: 200px; flex-shrink: 0;">
<button class="btn btn-success" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="downloadResult('${outputFilename}')"
${outputFilename ? '' : 'disabled'}>
💾 Download GeoTIFF
</button>
<button class="btn btn-primary" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="downloadPNG('${pngFilename}')"
${pngFilename ? '' : 'disabled'}>
🖼️ Download PNG
</button>
<button class="btn btn-secondary" style="padding: 10px 20px; margin: 0; white-space: nowrap;"
onclick="viewLargePNG('${pngFilename}')"
${pngFilename ? '' : 'disabled'}>
🔍 View Preview
</button>
</div>
</div>
`;
}).join('');
completedDiv.innerHTML += '<h3 style="margin-top: 20px; color: #dc3545;">❌ Failed Jobs</h3>' + failedHTML;
}
// Stop checking if all done
if (status.queue.running === 0 && status.queue.queued === 0 && currentBatchId) {
stopStatusCheck();
alert('✅ Batch processing hoàn thành!');
}
} catch (error) {
console.error('Error checking batch status:', error);
}
}
// Download result
function downloadResult(filename) {
window.location.href = `/api/predictions/download/${filename}`;
}
// Download PNG
function downloadPNG(filename) {
window.location.href = `/api/predictions/preview/${filename}`;
}
// View large PNG in new window
function viewLargePNG(filename) {
const modal = document.getElementById('previewModal');
const img = document.getElementById('previewImage');
img.src = `/api/predictions/preview/${filename}`;
modal.style.display = 'block';
}
// Close preview modal
function closePreviewModal() {
document.getElementById('previewModal').style.display = 'none';
}
// Load all batch results
async function loadAllBatchResults() {
try {
const response = await fetch('/api/batch/status');
const status = await response.json();
const completedDiv = document.getElementById('completedResults');
// Combine recent_completed from status
const allCompleted = status.jobs.recent_completed || [];
if (allCompleted.length === 0) {
completedDiv.innerHTML = '<p style="text-align: center; color: #666;">Chưa có kết quả nào</p>';
return;
}
completedDiv.innerHTML = allCompleted.map(job => `
<div class="batch-item completed">
<div style="flex: 1;">
<strong>${job.name}</strong> - <span style="color: #28a745;">✓ Completed</span><br>
<small>Job ID: ${job.job_id}</small><br>
<small>Completed: ${new Date(job.completed_at).toLocaleString()}</small><br>
${job.result ? `
<small><strong>Shape:</strong> ${job.result.shape.join(' x ')}</small><br>
<small><strong>Classes:</strong> ${job.result.unique_classes.join(', ')}</small><br>
<small><strong>Features:</strong> ${job.result.n_features}</small><br>
<small><strong>Model:</strong> ${job.result.model_used}</small><br>
${job.result.png_file ? `
<div style="margin-top: 10px;">
<img src="/api/predictions/preview/${job.result.png_file.split('/').pop()}"
style="max-width: 100%; border-radius: 5px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.2);"
onclick="viewLargePNG('${job.result.png_file.split('/').pop()}')"
title="Click để xem lớn hơn">
</div>
` : ''}
` : ''}
</div>
<div style="display: flex; flex-direction: column; gap: 5px; min-width: 200px;">
${job.result && job.result.output_file ? `
<button class="btn btn-success" style="padding: 8px 20px;" onclick="downloadResult('${job.result.output_file.split('/').pop()}')">
💾 Download GeoTIFF
</button>
${job.result.png_file ? `
<button class="btn btn-primary" style="padding: 8px 20px;" onclick="downloadPNG('${job.result.png_file.split('/').pop()}')">
🖼️ Download PNG
</button>
<button class="btn btn-secondary" style="padding: 8px 20px;" onclick="viewLargePNG('${job.result.png_file.split('/').pop()}')">
🔍 View Preview
</button>
` : ''}
` : ''}
</div>
</div>
`).join('');
} catch (error) {
console.error('Error loading batch results:', error);
}
}
// Download all results as ZIP (placeholder)
function downloadAllResults() {
alert('💡 Tính năng download tất cả batch results sẽ được thêm trong phiên bản tiếp theo.\\nHiện tại vui lòng download từng file riêng lẻ.');
}
// Start/stop status monitoring
function startStatusCheck() {
if (statusCheckInterval) clearInterval(statusCheckInterval);
statusCheckInterval = setInterval(checkBatchStatus, 3000);
}
function stopStatusCheck() {
if (statusCheckInterval) {
clearInterval(statusCheckInterval);
statusCheckInterval = null;
}
}
// Initialize on load
window.onload = function() {
initMap();
loadModels();
};
// Cleanup on unload
window.onbeforeunload = function() {
stopStatusCheck();
};
</script>
</body>
</html>