Files
mht-siemens/solution/client/static/js/script.js
T
2026-06-25 12:04:56 +02:00

309 lines
13 KiB
JavaScript

const fetchInterval = 500;
// State
let chart;
let activeSessionData = [];
let history = [];
let currentPage = 1;
let fullDataPage = 1;
// DOM Elements
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: 10000, title: { display: true, text: 'Position (mm)' } },
y: { min: -3000, max: 3000 }
},
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();
if (data) {
// 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";
}
// Update Latencies (Now shows for all areas)
if (data.latencies) {
// Find a representative latency or sum them
const totalLatency = Object.values(data.latencies).reduce((a, b) => a + b, 0);
latencyList.textContent = `Latency: ${totalLatency.toFixed(2)}ms`;
}
// Update acquisition status text
if (data.acq_status !== undefined) {
acquisitionStatusList.textContent = `• Mode: ${data.acq_status}`;
if (data.acq_status === "Acquisition") {
acquisitionStatusList.parentElement.classList.replace('text-gray-600', 'text-blue-400');
} else {
acquisitionStatusList.parentElement.classList.replace('text-blue-400', 'text-gray-600');
}
}
// Handle Live Chart Update
if (data.db901 && data.db901.params) {
const acq = data.db901.params.acquisizione;
const fine = data.db901.params.fine_acq;
const pronti = data.db901.params.pronti;
const limits = data.limits || { ucl: 212, lcl: 208, target: 0 };
if (acq && !fine) {
// MODE: ACQUISITION
if (self.last_data.get("acq_status") === "waiting") {
chart.data.datasets.forEach(ds => ds.data = []);
}
if (data.db901.params.x_val !== undefined && data.db901.params.y_val !== undefined) {
const x = data.db901.params.x_val;
const y = data.db901.params.y_val;
chart.data.datasets[0].data.push({ x, y });
chart.data.datasets[1].data.push({ x, y: limits.ucl });
chart.data.datasets[2].data.push({ x, y: limits.lcl });
chart.data.datasets[3].data.push({ x, y: limits.ucl });
chart.data.datasets[4].data.push({ x, y: limits.lcl });
chart.data.datasets[5].data.push({ x, y: limits.target });
if (chart.data.datasets[0].data.length > 100) {
chart.data.datasets.forEach(ds => ds.data.shift());
}
chart.update('none');
}
} else if (fine && pronti) {
// MODE: FINISHED
acquisitionStatusList.parentElement.classList.replace('text-blue-400', 'text-green-600');
if (data.db900 && data.db900.misure && data.db900.misure.length > 0) {
activeSessionData = data.db900.misure.map(m => ({
x: m.x,
y: m.y,
ts: m.ts || new Date()
}));
chart.data.datasets.forEach(ds => ds.data = []);
activeSessionData.forEach(p => {
chart.data.datasets[0].data.push({ x: p.x, y: p.y });
chart.data.datasets[1].data.push({ x: p.x, y: limits.ucl });
chart.data.datasets[2].data.push({ x: p.x, y: limits.lcl });
chart.data.datasets[3].data.push({ x: p.x, y: limits.ucl });
chart.data.datasets[4].data.push({ x: p.x, y: limits.lcl });
chart.data.datasets[5].data.push({ x: p.x, y: limits.target });
});
chart.update();
liveDataContainer.classList.remove('hidden');
historyDetailContainer.classList.add('hidden');
detailPlaceholder.classList.add('hidden');
detailContent.classList.remove('hidden');
renderFullSessionTable();
}
} else {
acquisitionStatusList.textContent = `• Mode: Polling (5Hz)`;
acquisitionStatusList.parentElement.classList.replace('text-blue-400', 'text-gray-600');
}
}
}
} catch (err) {
console.error("Error updating data:", err);
}
}
// Fetch history
async function fetchHistory() {
try {
const response = await fetch('/history');
if (response.ok) {
history = await response.json();
renderHistoryTable();
}
} catch (err) {
console.error("Error fetching history:", err);
}
}
function renderHistoryTable() {
historyTableBody.innerHTML = '';
if (history.length === 0) {
historyTableBody.innerHTML = '<tr><td colspan="4" class="p-2 text-center text-gray-400">No sessions recorded</td></tr>';
return;
}
const start = (currentPage - 1) * 10;
const end = start + 10;
const pageData = history.slice(start, end);
pageData.forEach((item, i) => {
const row = document.createElement('tr');
row.innerHTML = `
<td class="p-2 border-b">${item.filename}</td>
<td class="p-2 border-b">${item.articolo} / ${item.cod_produzione}</td>
<td class="p-2 border-b">${new Date().toLocaleDateString()}</td>
<td class="p-2 border-b">
<button class="bg-blue-600 text-white px-2 py-1 rounded text-[10px] hover:bg-blue-700">View</button>
</td>
`;
row.onclick = () => loadHistoryFile(item.file_path);
historyTableBody.appendChild(row);
});
pageInfo.innerText = `Page ${currentPage} of ${Math.ceil(history.length / 10)}`;
prevBtn.disabled = currentPage === 1;
nextBtn.disabled = currentPage * 10 >= history.length;
}
async function loadHistoryFile(filePath) {
try {
const response = await fetch(`/history/${filePath}`);
if (response.ok) {
const data = await response.json();
activeSessionData = data.misure.map(m => ({
x: m.x,
y: m.y,
ts: m.ts || new Date()
}));
chart.data.datasets.forEach(ds => ds.data = []);
activeSessionData.forEach(p => {
chart.data.datasets[0].data.push({ x: p.x, y: p.y });
chart.data.datasets[1].data.push({ x: p.x, y: 2000 }); // Fallback values
chart.data.datasets[2].data.push({ x: p.x, y: -2000 });
chart.data.datasets[3].data.push({ x: p.x, y: 2000 });
chart.data.datasets[4].data.push({ x: p.x, y: -2000 });
chart.data.datasets[5].data.push({ x: p.x, y: 0 });
});
chart.update();
liveDataContainer.classList.remove('hidden');
historyDetailContainer.classList.remove('hidden');
detailPlaceholder.classList.add('hidden');
detailContent.classList.remove('hidden');
renderFullSessionTable();
selectedData.innerText = `Session Loaded: ${data.articolo} | ${data.cod_produzione} | ${data.num_certificato}`;
}
} catch (err) {
console.error("Error loading history file:", err);
}
}
function renderFullSessionTable() {
fullDataTableBody.innerHTML = '';
if (activeSessionData.length === 0) return;
const start = (fullDataPage - 1) * 50;
const end = start + 50;
const pageData = activeSessionData.slice(start, end);
pageData.forEach((p, i) => {
const row = document.createElement('tr');
const diff = Math.abs(p.y - 0);
let status = 'OK';
let colorClass = 'text-green-600 border-green-600';
if (diff > 3000) {
status = 'ERROR';
colorClass = 'text-red-600 border-red-600';
} else if (diff > 2000) {
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.y.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 / 50)}`;
fullPrevBtn.disabled = fullDataPage === 1;
fullNextBtn.disabled = fullDataPage * 50 >= activeSessionData.length;
}
function updateTable() {
if (tableBody.innerHTML === '') {
tableBody.innerHTML = '<tr><td colspan="4" class="p-2 text-center text-gray-400">Initializing...</td></tr>';
}
// This is a simplified update for the simulation mode
}
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();
fetchHistory();
setInterval(updateData, fetchInterval);
updateData();
// Pagination Listeners
prevBtn.onclick = () => { if (currentPage > 1) { currentPage--; renderHistoryTable(); } };
nextBtn.onclick = () => { if (currentPage * 10 < history.length) { currentPage++; renderHistoryTable(); } };
fullPrevBtn.onclick = () => { if (fullDataPage > 1) { fullDataPage--; renderFullSessionTable(); } };
fullNextBtn.onclick = () => { if (fullDataPage * itemsPerPage < activeSessionData.length) { fullDataPage++; renderFullSessionTable(); } };