Files
mht-siemens/solution/client/static/js/script.js
T
2026-06-24 17:26:18 +02:00

266 lines
10 KiB
JavaScript

const fetchInterval = 500;
// Configuration constants from DemoPage.html
const MEAN = 210;
const WARNING_LIMIT = 2;
const ERROR_LIMIT = 3;
const TARGET_VALUE = 210;
const ACQUISITION_LEN = 500;
// State
let dataPoints = [];
let chart;
let timerInterval;
let isAcquiring = false;
let currentMode = 'live';
let activeSessionData = [];
let currentPage = 1;
let fullDataPage = 1;
// DOM Elements
const startBtn = document.getElementById('startBtn');
const progressBar = document.getElementById('progressBar');
const progressContainer = document.getElementById('progressContainer');
const timerDisplay = document.getElementById('timerDisplay');
const tableBody = document.getElementById('measurementsTable');
const detailPlaceholder = document.getElementById('detailPlaceholder');
const detailContent = document.getElementById('detailContent');
const fullDataTableBody = document.getElementById('fullDataTableBody');
const historyTableBody = document.getElementById('historyTableBody');
const pageInfo = document.getElementById('pageInfo');
const prevBtn = document.getElementById('prevPage');
const nextBtn = document.getElementById('nextPage');
const fullPrevBtn = document.getElementById('fullPrevPage');
const fullNextBtn = document.getElementById('fullNextPage');
const liveDataContainer = document.getElementById('liveDataContainer');
const historyDetailContainer = document.getElementById('historyDetailContainer');
const selectedData = document.getElementById('selectedData');
const plcIpDisplay = document.getElementById('plcIpDisplay');
const connStatusDot = document.getElementById('connStatusDot');
const latencyList = document.getElementById('latencyList');
const acquisitionStatusList = document.getElementById('acquisitionStatusList');
// Initialize Chart.js
function initChart() {
const ctx = document.getElementById('liveChart').getContext('2d');
chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{ label: 'Value (mm)', data: [], borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', fill: true, pointRadius: 0, borderWidth: 2, tension: 0 },
{ label: 'Warning UCL', data: [], borderColor: '#facc15', borderWidth: 1, pointRadius: 0, fill: false, borderDash: [5, 5] },
{ label: 'Warning LCL', data: [], borderColor: '#facc15', borderWidth: 1, pointRadius: 0, fill: false, borderDash: [5, 5] },
{ label: 'Error UCL', data: [], borderColor: '#f87171', borderWidth: 1, pointRadius: 0, fill: false, borderDash: [5, 5] },
{ label: 'Error LCL', data: [], borderColor: '#f87171', borderWidth: 1, pointRadius: 0, fill: false, borderDash: [5, 5] },
{ label: 'Target Value', data: [], borderColor: '#22c55e', borderWidth: 1.5, pointRadius: 0, fill: false, tension: 0 }
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: { type: 'linear', min: 0, max: ACQUISITION_LEN, title: { display: true, text: 'Position (mm)' } },
y: { min: 200, max: 220 }
},
animation: false
}
});
}
// Fetch data from Flask and update UI
async function updateData() {
try {
const response = await fetch('/data');
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
console.log("Data received:", data);
if (data) {
const db100 = data.db100;
// Update Connectivity Status
if (data.connected !== undefined) {
connStatusDot.className = data.connected
? 'w-2 h-2 bg-green-400 rounded-full mr-2 animate-pulse'
: 'w-2 h-2 bg-red-600 rounded-full mr-2';
plcIpDisplay.textContent = data.plc_ip || "Disconnected";
}
if (db100) {
// Update Latencies
if (db100.duration !== undefined) {
latencyList.textContent = `Latency: ${db100.duration.toFixed(2)}ms`;
}
// Update Live Values (if needed)
if (db100.params && db100.params.x_live !== undefined) {
// Could update a specific UI element for X_Live here
}
}
// Update Chart data if acquiring
if (isAcquiring && data.db101 && data.db101.values) {
chart.data.datasets[0].data = data.db101.values.map((v, i) => ({ x: i * (ACQUISITION_LEN / 40), y: v }));
chart.update();
}
// Update acquisition status text
if (isAcquiring) {
acquisitionStatusList.textContent = `• Mode: Active Acquisition`;
acquisitionStatusList.parentElement.classList.replace('text-gray-600', 'text-blue-400');
} else {
acquisitionStatusList.textContent = `• Mode: Polling (5Hz)`;
acquisitionStatusList.parentElement.classList.replace('text-blue-400', 'text-gray-600');
}
}
} catch (err) {
console.error("Error updating data:", err);
}
}
// Start acquisition (Mocked logic for now to match DemoPage.html)
startBtn.addEventListener('click', () => {
if (isAcquiring) return;
dataPoints = [];
activeSessionData = [];
chart.data.datasets.forEach(ds => ds.data = []);
chart.update();
isAcquiring = true;
startBtn.disabled = true;
startBtn.classList.replace('bg-green-600', 'bg-gray-400');
progressContainer.classList.remove('hidden');
currentMode = 'live';
toggleDetailVisibility();
let currentStep = 0;
let timerInterval = setInterval(() => {
const point = {
ts: new Date(),
val: generateNormal(MEAN, 1),
x: (currentStep / (TOTAL_SAMPLES - 1)) * ACQUISITION_LEN
};
dataPoints.push(point);
activeSessionData = [...dataPoints];
// Update Chart data
chart.data.datasets[0].data.push({x: point.x, y: point.val});
chart.data.datasets[1].data.push({x: point.x, y: MEAN + WARNING_LIMIT});
chart.data.datasets[2].data.push({x: point.x, y: MEAN - WARNING_LIMIT});
chart.data.datasets[3].data.push({x: point.x, y: MEAN + ERROR_LIMIT});
chart.data.datasets[4].data.push({x: point.x, y: MEAN - ERROR_LIMIT});
chart.data.datasets[5].data.push({x: point.x, y: TARGET_VALUE});
if (chart.data.datasets[0].data.length > 100) {
chart.data.datasets.forEach(ds => ds.data.shift());
}
chart.update();
if (currentStep % 3 === 0) {
updateTable();
}
currentStep++;
const progress = (currentStep / TOTAL_SAMPLES) * 100;
progressBar.style.width = `${progress}%`;
timerDisplay.innerText = `Time Remaining: ${Math.max(0, Math.ceil((TOTAL_SAMPLES - currentStep) / SAMPLE_RATE))}s`;
if (currentStep >= TOTAL_SAMPLES) {
clearInterval(timerInterval);
completeAcquisition();
}
}, 1000 / SAMPLE_RATE);
});
function completeAcquisition() {
isAcquiring = false;
startBtn.disabled = false;
startBtn.classList.replace('bg-gray-400', 'bg-green-600');
timerDisplay.innerText = "Acquisition Complete";
timerDisplay.classList.replace('text-blue-600', 'text-green-600');
activeSessionData = [...dataPoints].sort((a, b) => b.ts - a.ts);
liveDataContainer.classList.remove('hidden');
historyDetailContainer.classList.add('hidden');
renderFullSessionTable();
detailPlaceholder.classList.add('hidden');
detailContent.classList.remove('hidden');
selectedData.innerText = `Session completed. Captured ${dataPoints.length} points. Mean: ${dataPoints.reduce((a,b)=>a+b.val,0)/dataPoints.length}`;
}
function renderFullSessionTable() {
fullDataTableBody.innerHTML = '';
const start = (fullDataPage - 1) * itemsPerPage;
const end = start + itemsPerPage;
const pageData = activeSessionData.slice(start, end);
pageData.forEach((p, i) => {
const row = document.createElement('tr');
const diff = Math.abs(p.val - MEAN);
let status = 'OK';
let colorClass = 'text-green-600 border-green-600';
if (diff > ERROR_LIMIT) {
status = 'ERROR';
colorClass = 'text-red-600 border-red-600';
} else if (diff > WARNING_LIMIT) {
status = 'WARNING';
colorClass = 'text-yellow-600 border-yellow-600';
}
row.innerHTML = `
<td class="p-2 border-b">${start + i + 1}</td>
<td class="p-2 border-b">${p.ts.toLocaleTimeString()}</td>
<td class="p-2 border-b font-mono">${p.val.toFixed(2)}</td>
<td class="p-2 border-b">
<span class="px-1 py-0.5 bg-white rounded-full text-[9px] font-bold border ${colorClass}">${status}</span>
</td>
`;
fullDataTableBody.appendChild(row);
});
fullPageInfo.innerText = `Page ${fullDataPage} of ${Math.ceil(activeSessionData.length / itemsPerPage)}`;
fullPrevBtn.disabled = fullDataPage === 1;
fullNextBtn.disabled = fullDataPage * itemsPerPage >= activeSessionData.length;
}
function toggleDetailVisibility() {
if (isAcquiring || currentMode === 'history') {
liveDataContainer.classList.remove('hidden');
historyDetailContainer.classList.add('hidden');
if (isAcquiring) {
activeSessionData = [...dataPoints];
renderFullSessionTable();
}
} else {
liveDataContainer.classList.add('hidden');
historyDetailContainer.classList.add('hidden');
detailPlaceholder.classList.remove('hidden');
}
}
// Initialization
initChart();
setInterval(updateData, fetchInterval);
updateData();
// Pagination
fullPrevBtn.onclick = () => {
if (fullDataPage > 1) {
fullDataPage--;
renderFullSessionTable();
}
};
fullNextBtn.onclick = () => {
if (fullDataPage * itemsPerPage < activeSessionData.length) {
fullDataPage++;
renderFullSessionTable();
}
};