#!/usr/bin/env python3 import json with open('/home/mdares/.node-red/flows.json', 'r') as f: flows = json.load(f) # New Format Graph Data function that handles KPI history data new_format_func = '''// Format Graph Data for KPI charts // Check if we have KPI history data (from global context) if (msg.topic === "kpiHistory" && msg.payload) { const kpiData = msg.payload; // Extract arrays const oeeHist = kpiData.oee || []; const availHist = kpiData.availability || []; const perfHist = kpiData.performance || []; const qualHist = kpiData.quality || []; // Build labels and data arrays const labels = []; const oeeData = []; const availData = []; const perfData = []; const qualData = []; // Use OEE timestamps as primary (they should all be the same length) oeeHist.forEach((point, index) => { const timestamp = new Date(point.timestamp); labels.push(timestamp.toLocaleString()); oeeData.push(point.value || 0); availData.push(availHist[index]?.value || 0); perfData.push(perfHist[index]?.value || 0); qualData.push(qualHist[index]?.value || 0); }); msg.graphData = { labels: labels, datasets: [ { label: 'OEE %', data: oeeData }, { label: 'Availability %', data: availData }, { label: 'Performance %', data: perfData }, { label: 'Quality %', data: qualData } ] }; node.warn(`[GRAPH DATA] Formatted ${labels.length} KPI history points`); delete msg.topic; delete msg.payload; return msg; } // Legacy support: work_orders query data (if needed) const rows = msg.payload || []; if (!Array.isArray(rows) || rows.length === 0) { msg.graphData = { labels: [], datasets: [ { label: 'OEE %', data: [] }, { label: 'Availability %', data: [] }, { label: 'Performance %', data: [] }, { label: 'Quality %', data: [] } ] }; delete msg.topic; delete msg.payload; return msg; } // If we have work_orders data, format it (though we won't use this path anymore) const labels = []; const goodData = []; const scrapData = []; const efficiencyData = []; const qualityData = []; rows.forEach(row => { const timestamp = new Date(row.updated_at); labels.push(timestamp.toLocaleString()); const good = Number(row.good_parts) || 0; const scrap = Number(row.scrap_parts) || 0; const target = Number(row.target_quantity) || 0; goodData.push(good); scrapData.push(scrap); let eff = (row.progress_percent != null) ? Number(row.progress_percent) : (target > 0 ? (good / target) * 100 : 0); efficiencyData.push(Math.min(eff, 100)); const total = good + scrap; const quality = total > 0 ? (good / total) * 100 : 100; qualityData.push(quality); }); msg.graphData = { labels: labels, datasets: [ { label: 'OEE %', data: efficiencyData }, // Use efficiency as fallback { label: 'Availability %', data: [] }, { label: 'Performance %', data: [] }, { label: 'Quality %', data: qualityData } ] }; delete msg.topic; delete msg.payload; return msg;''' # Update Format Graph Data function for node in flows: if node.get('id') == 'format_graph_data_node_id': node['func'] = new_format_func print("✓ Updated Format Graph Data function to handle KPI data") break with open('/home/mdares/.node-red/flows.json', 'w') as f: json.dump(flows, f, indent=4) print("✓ flows.json updated")