Sfoglia il codice sorgente

Add hybrid chart-compare tool for activity states

anfeny 8 mesi fa
parent
commit
c2d20cc1da

+ 88 - 13
dashboard/src/static/js/dashboard.js

@@ -6,6 +6,10 @@
 //const socket = io(); // declared in main.js
 let workpiece_color = "";
 
+// CHARTS
+const stations = ['dsi', 'dso', 'vgr', 'hbw', 'mpo', 'sld'];
+const charts = {};
+
 // MQTT message filter
 const excludedTopics = ['i/cam', 'o/broadcast'];
 let allTopics = new Set(); // Tracks all seen topics
@@ -16,6 +20,57 @@ const selectAllBtn = document.getElementById('selectAll');
 const deselectAllBtn = document.getElementById('selectNone');
 const messageList = document.getElementById('mqtt-messages');
 
+// Draw initial charts
+stations.forEach(station => {
+    if (document.getElementById(`chart-${station}`) === null) return; // Skip if canvas not found
+    const ctx = document.getElementById(`chart-${station}`).getContext('2d');
+    
+    charts[station] = new Chart(ctx, {
+        type: 'line',
+        data: {
+            datasets: [
+                {
+                    label: 'Real',
+                    data: [],
+                    borderColor: '#0266ff',
+                    stepped: true,
+                },
+                {
+                    label: 'Sim',
+                    data: [],
+                    borderColor: '#fa33d7',
+                    stepped: true,
+                }
+            ]
+        },
+        options: {
+            responsive: true,
+            scales: {
+                x: {
+                    type: 'time',
+                    time: {
+                        tooltipFormat: 'HH:mm:ss.SSS',  // hover format
+                        displayFormats: { second: 'HH:mm:ss' }  // tick labels
+                    },
+                    ticks: {autoSkip: true}
+                },
+                y: {
+                    min: 0,
+                    max: 1.1,
+                    ticks: {
+                        stepSize: 0.1,
+                        callback: function(value, index, ticks){
+                            if (value === 0) return 'Inactive';
+                            if (value === 1) return 'Active';
+                            return '';
+                        }
+                    }
+                }
+            }
+        }
+    });
+});
+
 // Format timestamp as hh:mm:ss.mmm
 function formatTimestamp() {
     const now = new Date();
@@ -56,17 +111,21 @@ function updateMessageVisibility() {
 }
 
 // Button handlers
-selectAllBtn.addEventListener('click', () => {
-    allowedTopics = new Set(allTopics);
-    renderFilters();
-    updateMessageVisibility();
-});
+if (selectAllBtn !== null) {
+    selectAllBtn.addEventListener('click', () => {
+        allowedTopics = new Set(allTopics);
+        renderFilters();
+        updateMessageVisibility();
+    });
+}
+if (deselectAllBtn !== null) {
+    deselectAllBtn.addEventListener('click', () => {
+        allowedTopics.clear();
+        renderFilters();
+        updateMessageVisibility();
+    });
+}
 
-deselectAllBtn.addEventListener('click', () => {
-    allowedTopics.clear();
-    renderFilters();
-    updateMessageVisibility();
-});
 
 // Handle incoming MQTT messages
 socket.on('mqtt_message', function (data) {
@@ -173,10 +232,26 @@ socket.on('sensor_update', function (data) {
 
 // Update the state of a station
 socket.on('station_update', function (data) {
-    // Only show messages from origin that matches our view
-    if (data.origin !== dashboardMode) return;
-
     const state = JSON.parse(data.message);
+
+    // ---------- STATION ACTIVITY CHARTS ----------
+    if (Object.keys(charts).length > 0) {
+        const chart = charts[state.station];
+
+        const seriesIndex = data.origin === 'real' ? 0 : 1;
+        chart.data.datasets[seriesIndex].data.push({
+            x: new Date(),  // or parse data.ts
+            y: state.active ? 1 : 0
+        });
+
+        // Limit to last 30 points
+        chart.data.datasets[seriesIndex].data = chart.data.datasets[seriesIndex].data.slice(-30);
+        chart.update();
+    }; 
+
+    // ---------- STATION ICONS ---------- 
+    // Only show messages from origin that matches our view
+    if (data.origin !== dashboardMode) return;  
     
     // Change colour depending on the code
     let colorClass = "text-warning"; // default -> not active

+ 1 - 0
dashboard/src/static/js/sim/index.js

@@ -74,6 +74,7 @@ window.draw = draw;
 socket.on('visualization_update', function (data) {
     // Only show messages from origin that matches our view
     if (data.origin !== 'sim') return;
+    if (!factory) return; // No message to process
 
     console.log(data);
     let msg = JSON.parse(data.message);

+ 3 - 1
dashboard/src/templates/dashboard.html

@@ -9,7 +9,9 @@
     const dashboardMode = "{{ mode }}";
 </script>
 
+{% block extra_scripts %}
+{% endblock %}
+
 <!-- Dashboard -->
 <script src="{{ url_for('static', filename='js/dashboard.js') }}"></script>
-{% block extra_scripts %}{% endblock %}
 {% endblock %}

+ 29 - 5
dashboard/src/templates/dashboards/hybrid.html

@@ -8,15 +8,39 @@
     <p>Hybrid will be a combination of monitoring the real-world system and its comparison to its digital simulation</p>
     <div class="row row-cols-3 g-4">
     </div>
-    <br><br>
+    <br>
+
+    <div class="row row-cols-2 g-4">
+
+        {% for station in ['dsi', 'dso', 'vgr', 'hbw', 'mpo', 'sld'] %}
+        <div class="col">
+            <div class="card">
+                <div class="card-header"><i class="bi bi-activity me-2"></i>{{station}} Activity</div>
+                <div class="card-body">
+                    <div class="col-md-6" id="chart-container-{{station}}" style="width: 100%">
+                        <canvas id="chart-{{station}}"></canvas>
+                    </div>
+                </div>
+            </div>
+
+
+        </div>
+        {% endfor %}
+
+    </div>
 </div>
 {% endblock %}
 
 
 {% block extra_scripts %}
-<!-- p5.js (Local) -->
-<script src="{{ url_for('static', filename='js/vendor/p5.min.js') }}"></script>
+<!-- Chart.js TODO: local -->
+
+<!-- Luxon global build -->
+<script src="https://cdn.jsdelivr.net/npm/luxon@3/build/global/luxon.min.js"></script>
+
+<!-- Chart.js v4 (adapter-compatible) -->
+<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
 
-<!-- Simulation Visualization -->
-<script type="module" src="{{ url_for('static', filename='js/sim/index.js') }}"></script>
+<!-- Chart.js adapter for Luxon -->
+<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1"></script>
 {% endblock %}