From d0d675d80427a6bdc0629da425fb00130969c000 Mon Sep 17 00:00:00 2001 From: Miguel Date: Thu, 14 Aug 2025 12:07:07 +0200 Subject: [PATCH] Enhance variable management and plotting features. Introduced VariableContext for managing variable updates, updated dataset variables and application events, improved plot variable schema, and refined UI components for better usability and performance. --- application_events.json | 309 +++++++++--------- config/data/dataset_variables.json | 24 +- config/schema/plot-variables.schema.json | 24 +- config/schema/ui/plot-variables.uischema.json | 38 ++- frontend/src/components/PlotManager.jsx | 34 +- .../rjsf/VariableSelectorWidget.jsx | 113 +++++-- .../src/components/widgets/AllWidgets.jsx | 8 +- frontend/src/contexts/VariableContext.jsx | 34 ++ frontend/src/pages/DashboardNew.jsx | 20 +- system_state.json | 6 +- 10 files changed, 386 insertions(+), 224 deletions(-) create mode 100644 frontend/src/contexts/VariableContext.jsx diff --git a/application_events.json b/application_events.json index 7efc965..1d8a2b4 100644 --- a/application_events.json +++ b/application_events.json @@ -1,165 +1,5 @@ { "events": [ - { - "timestamp": "2025-07-17T18:00:01.179115", - "level": "info", - "event_type": "dataset_activated", - "message": "Dataset activated: DAR", - "details": { - "dataset_id": "dar", - "variables_count": 4, - "streaming_count": 4, - "prefix": "dar" - } - }, - { - "timestamp": "2025-07-17T18:01:12.766609", - "level": "info", - "event_type": "dataset_activated", - "message": "Dataset activated: DAR", - "details": { - "dataset_id": "dar", - "variables_count": 4, - "streaming_count": 4, - "prefix": "dar" - } - }, - { - "timestamp": "2025-07-17T18:01:12.769824", - "level": "info", - "event_type": "streaming_started", - "message": "Multi-dataset streaming started: 1 datasets activated", - "details": { - "activated_datasets": 1, - "total_datasets": 2, - "udp_host": "127.0.0.1", - "udp_port": 9870 - } - }, - { - "timestamp": "2025-07-17T18:01:26.427183", - "level": "info", - "event_type": "Application started", - "message": "Application initialization completed successfully", - "details": {} - }, - { - "timestamp": "2025-07-17T18:01:26.451766", - "level": "info", - "event_type": "plc_connection", - "message": "Successfully connected to PLC 10.1.33.11", - "details": { - "ip": "10.1.33.11", - "rack": 0, - "slot": 2 - } - }, - { - "timestamp": "2025-07-17T18:01:26.456954", - "level": "info", - "event_type": "dataset_activated", - "message": "Dataset activated: DAR", - "details": { - "dataset_id": "dar", - "variables_count": 4, - "streaming_count": 4, - "prefix": "dar" - } - }, - { - "timestamp": "2025-07-17T18:01:30.909879", - "level": "info", - "event_type": "dataset_activated", - "message": "Dataset activated: DAR", - "details": { - "dataset_id": "dar", - "variables_count": 4, - "streaming_count": 4, - "prefix": "dar" - } - }, - { - "timestamp": "2025-07-17T18:01:30.911944", - "level": "info", - "event_type": "streaming_started", - "message": "Multi-dataset streaming started: 1 datasets activated", - "details": { - "activated_datasets": 1, - "total_datasets": 2, - "udp_host": "127.0.0.1", - "udp_port": 9870 - } - }, - { - "timestamp": "2025-07-17T18:20:09.887378", - "level": "info", - "event_type": "Application started", - "message": "Application initialization completed successfully", - "details": {} - }, - { - "timestamp": "2025-07-17T18:20:09.913286", - "level": "info", - "event_type": "plc_connection", - "message": "Successfully connected to PLC 10.1.33.11", - "details": { - "ip": "10.1.33.11", - "rack": 0, - "slot": 2 - } - }, - { - "timestamp": "2025-07-17T18:20:09.917270", - "level": "info", - "event_type": "dataset_activated", - "message": "Dataset activated: DAR", - "details": { - "dataset_id": "dar", - "variables_count": 4, - "streaming_count": 4, - "prefix": "dar" - } - }, - { - "timestamp": "2025-07-18T09:39:20.220724", - "level": "info", - "event_type": "dataset_activated", - "message": "Dataset activated: DAR", - "details": { - "dataset_id": "dar", - "variables_count": 4, - "streaming_count": 3, - "prefix": "dar" - } - }, - { - "timestamp": "2025-07-18T09:39:20.226708", - "level": "info", - "event_type": "streaming_started", - "message": "Multi-dataset streaming started: 1 datasets activated", - "details": { - "activated_datasets": 1, - "total_datasets": 2, - "udp_host": "127.0.0.1", - "udp_port": 9870 - } - }, - { - "timestamp": "2025-07-18T09:40:58.642342", - "level": "info", - "event_type": "dataset_deactivated", - "message": "Dataset deactivated: DAR", - "details": { - "dataset_id": "dar" - } - }, - { - "timestamp": "2025-07-18T09:40:58.644338", - "level": "info", - "event_type": "streaming_stopped", - "message": "Multi-dataset streaming stopped: 1 datasets deactivated", - "details": {} - }, { "timestamp": "2025-07-18T09:40:58.647328", "level": "info", @@ -10414,8 +10254,155 @@ "trigger_variable": null, "auto_started": true } + }, + { + "timestamp": "2025-08-14T11:42:26.264053", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T11:42:26.345728", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 3, + "streaming_count": 3, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-14T11:42:26.362513", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T11:42:26.424426", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T11:49:00.624419", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T11:49:00.691022", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 3, + "streaming_count": 3, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-14T11:49:00.703362", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T11:49:00.738130", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T12:00:00.237052", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T12:03:29.892080", + "level": "info", + "event_type": "plot_session_created", + "message": "Plot session 'UR29' created and started", + "details": { + "session_id": "plot_1", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 25, + "trigger_variable": null, + "auto_started": true + } + }, + { + "timestamp": "2025-08-14T12:05:58.231793", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T12:05:58.282781", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 2, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-14T12:05:58.294856", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T12:05:58.314287", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T12:06:22.595559", + "level": "info", + "event_type": "plot_session_created", + "message": "Plot session 'UR29' created and started", + "details": { + "session_id": "plot_1", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 25, + "trigger_variable": null, + "auto_started": true + } } ], - "last_updated": "2025-08-14T11:34:45.734619", + "last_updated": "2025-08-14T12:06:22.595559", "total_entries": 1000 } \ No newline at end of file diff --git a/config/data/dataset_variables.json b/config/data/dataset_variables.json index 1106d75..cff7e7c 100644 --- a/config/data/dataset_variables.json +++ b/config/data/dataset_variables.json @@ -4,28 +4,20 @@ "dataset_id": "DAR", "variables": [ { - "area": "db", - "db": 1011, "name": "UR29_Brix", - "offset": 1322, - "streaming": true, - "type": "real" - }, - { "area": "db", "db": 1011, + "offset": 1322, + "type": "real", + "streaming": true + }, + { "name": "UR29_ma", - "offset": 1296, - "streaming": true, - "type": "real" - }, - { "area": "db", "db": 1011, - "name": "fUR29_Brix", - "offset": 1322, - "streaming": true, - "type": "real" + "offset": 1296, + "type": "real", + "streaming": true } ] }, diff --git a/config/schema/plot-variables.schema.json b/config/schema/plot-variables.schema.json index 4419ff5..f7e47c4 100644 --- a/config/schema/plot-variables.schema.json +++ b/config/schema/plot-variables.schema.json @@ -28,7 +28,12 @@ "variable_name": { "type": "string", "title": "Variable Name", - "description": "Name of the variable to plot (must exist in dataset variables)" + "description": "Name of the variable to plot (selected from dataset variables using search widget)" + }, + "label": { + "type": "string", + "title": "Display Label", + "description": "Label shown in the plot legend" }, "color": { "type": "string", @@ -37,6 +42,21 @@ "pattern": "^#[0-9A-Fa-f]{6}$", "default": "#3498db" }, + "line_width": { + "type": "number", + "title": "Line Width", + "description": "Width of the line in the plot", + "default": 2, + "minimum": 1, + "maximum": 10 + }, + "y_axis": { + "type": "string", + "title": "Y-Axis", + "description": "Which Y-axis to use for this variable", + "enum": ["left", "right"], + "default": "left" + }, "enabled": { "type": "boolean", "title": "Enable Plotting", @@ -46,7 +66,7 @@ }, "required": [ "variable_name", - "color" + "label" ] } } diff --git a/config/schema/ui/plot-variables.uischema.json b/config/schema/ui/plot-variables.uischema.json index ba76382..dc7fd6e 100644 --- a/config/schema/ui/plot-variables.uischema.json +++ b/config/schema/ui/plot-variables.uischema.json @@ -27,29 +27,49 @@ "items": { "ui:order": [ "variable_name", + "label", "color", + "line_width", + "y_axis", "enabled" ], "ui:layout": [ [ { "name": "variable_name", - "width": 6 + "width": 4 + }, + { + "name": "label", + "width": 2 }, { "name": "color", - "width": 3 + "width": 2 + }, + { + "name": "line_width", + "width": 2 + }, + { + "name": "y_axis", + "width": 1 }, { "name": "enabled", - "width": 3 + "width": 1 } ] ], "variable_name": { + "ui:widget": "variableSelector", + "ui:placeholder": "Search and select variable from datasets...", + "ui:help": "🔍 Search and select a variable from the configured datasets (includes live values and metadata)" + }, + "label": { "ui:widget": "text", - "ui:placeholder": "UR29_Brix", - "ui:help": "� Name of the variable to plot (must exist in dataset variables)" + "ui:placeholder": "Chart legend label...", + "ui:help": "📊 Label shown in the plot legend for this variable" }, "color": { "ui:widget": "color", @@ -72,6 +92,14 @@ ] } }, + "line_width": { + "ui:widget": "updown", + "ui:help": "📏 Width of the line in the plot (1-10 pixels)" + }, + "y_axis": { + "ui:widget": "select", + "ui:help": "📊 Which Y-axis to use for this variable (left or right)" + }, "enabled": { "ui:widget": "checkbox", "ui:help": "📊 Enable this variable to be displayed in the real-time plot" diff --git a/frontend/src/components/PlotManager.jsx b/frontend/src/components/PlotManager.jsx index 58a77ad..6843c7f 100644 --- a/frontend/src/components/PlotManager.jsx +++ b/frontend/src/components/PlotManager.jsx @@ -36,10 +36,12 @@ import validator from '@rjsf/validator-ajv8' import allWidgets from './widgets/AllWidgets' import LayoutObjectFieldTemplate from './rjsf/LayoutObjectFieldTemplate' import PlotRealtimeSession from './PlotRealtimeSession' +import { useVariableContext } from '../contexts/VariableContext' import * as api from '../services/api' // Pure RJSF Plot Manager Component export default function PlotManager() { + const { triggerVariableRefresh } = useVariableContext() const [plots, setPlots] = useState({}) const [plotsSchemaData, setPlotsSchemaData] = useState(null) const [plotsVariablesSchemaData, setPlotsVariablesSchemaData] = useState(null) @@ -238,6 +240,8 @@ export default function PlotManager() { duration: 2000 }) setPlotsVariablesConfig(formData) + // Trigger refresh of variable selectors (though they don't depend on plot vars directly) + triggerVariableRefresh() } catch (error) { toast({ title: '❌ Failed to save plot variables', @@ -411,15 +415,10 @@ export default function PlotManager() { type: "object", title: "Plot Variable", properties: { - dataset_id: { - type: "string", - title: "Dataset Source", - description: "Which dataset contains this variable" - }, variable_name: { type: "string", title: "Variable Name", - description: "Name of the variable to plot" + description: "Select variable from datasets with search and metadata" }, label: { type: "string", @@ -445,7 +444,7 @@ export default function PlotManager() { default: "left" } }, - required: ["dataset_id", "variable_name", "label"] + required: ["variable_name", "label"] } } } @@ -455,13 +454,26 @@ export default function PlotManager() { variables: { items: { "ui:layout": [[ - { "name": "dataset_id", "width": 2 }, - { "name": "variable_name", "width": 3 }, + { "name": "variable_name", "width": 4 }, { "name": "label", "width": 2 }, { "name": "color", "width": 2 }, - { "name": "line_width", "width": 1 }, + { "name": "line_width", "width": 2 }, { "name": "y_axis", "width": 2 } - ]] + ]], + variable_name: { + "ui:widget": "variableSelector", + "ui:placeholder": "Search and select variable from datasets...", + "ui:help": "🔍 Search variables from configured datasets with live values and metadata" + }, + label: { + "ui:placeholder": "Chart legend label..." + }, + color: { + "ui:widget": "color" + }, + line_width: { + "ui:widget": "updown" + } } } } diff --git a/frontend/src/components/rjsf/VariableSelectorWidget.jsx b/frontend/src/components/rjsf/VariableSelectorWidget.jsx index 70894bb..cb45d81 100644 --- a/frontend/src/components/rjsf/VariableSelectorWidget.jsx +++ b/frontend/src/components/rjsf/VariableSelectorWidget.jsx @@ -1,14 +1,27 @@ -import React, { useState, useEffect, useMemo, useRef } from 'react' +import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react' import { FormControl, FormLabel, FormHelperText, Select, VStack, HStack, - Text, Badge, Box, Icon, Input, useColorModeValue, Spinner + Text, Badge, Box, Icon, Input, useColorModeValue, Spinner, IconButton, Tooltip } from '@chakra-ui/react' -import { SearchIcon } from '@chakra-ui/icons' +import { SearchIcon, RepeatIcon } from '@chakra-ui/icons' import { readConfig } from '../../services/api.js' +import { useVariableContext } from '../../contexts/VariableContext.jsx' // Widget for selecting existing dataset variables with filtering and search export function VariableSelectorWidget(props) { - const { id, value, required, disabled, readonly, label, onChange, onBlur, onFocus, rawErrors = [] } = props + const { id, value, required, disabled, readonly, label, onChange, onBlur, onFocus, rawErrors = [], options = {} } = props + + // Extract refresh trigger from options if provided + const refreshTrigger = options?.refreshTrigger || 0 + + // Use variable context if available, fallback to local state + let contextTrigger = 0 + try { + const variableContext = useVariableContext() + contextTrigger = variableContext?.variableRefreshTrigger || 0 + } catch { + // Context not available, use local trigger only + } const [datasetVariables, setDatasetVariables] = useState({}) const [loading, setLoading] = useState(true) @@ -16,6 +29,7 @@ export function VariableSelectorWidget(props) { const [selectedDataset, setSelectedDataset] = useState('all') const [liveValue, setLiveValue] = useState(undefined) const [liveStatus, setLiveStatus] = useState('idle') + const [refreshing, setRefreshing] = useState(false) const esRef = useRef(null) const borderColor = useColorModeValue('gray.300', 'gray.600') @@ -24,24 +38,67 @@ export function VariableSelectorWidget(props) { const selectedVarBgColor = useColorModeValue('blue.50', 'blue.900') const selectedVarBorderColor = useColorModeValue('blue.200', 'blue.700') - // Load dataset variables on mount - useEffect(() => { - const loadDatasetVariables = async () => { - try { - setLoading(true) - const response = await readConfig('dataset-variables') - setDatasetVariables(response.data?.dataset_variables || {}) - } catch (error) { - console.error('Error loading dataset variables:', error) - setDatasetVariables({}) - } finally { - setLoading(false) - } + // Load dataset variables function (extracted for reuse) + const loadDatasetVariables = useCallback(async () => { + try { + setLoading(true) + const response = await readConfig('dataset-variables') + // Handle the array-based structure: { variables: [{dataset_id, variables: [...]}] } + const datasetVariablesArray = response?.variables || [] + + // Convert array format to object format for easier processing + const datasetVariablesObj = {} + datasetVariablesArray.forEach(item => { + if (item.dataset_id && item.variables) { + datasetVariablesObj[item.dataset_id] = { + variables: {} + } + // Convert variables array to object with variable name as key + item.variables.forEach(variable => { + if (variable.name) { + datasetVariablesObj[item.dataset_id].variables[variable.name] = variable + } + }) + } + }) + + setDatasetVariables(datasetVariablesObj) + } catch (error) { + console.error('Error loading dataset variables:', error) + setDatasetVariables({}) + } finally { + setLoading(false) } - - loadDatasetVariables() }, []) + // Manual refresh function + const handleRefresh = useCallback(async () => { + setRefreshing(true) + await loadDatasetVariables() + setRefreshing(false) + }, [loadDatasetVariables]) + + // Load dataset variables on mount and when refresh trigger changes + useEffect(() => { + loadDatasetVariables() + }, [loadDatasetVariables, refreshTrigger, contextTrigger]) + + // Auto-refresh when component gains focus (optional behavior) + const handleFocusWithRefresh = useCallback((event) => { + // Call original onFocus if provided + if (onFocus) { + onFocus(id, event.target.value) + } + + // Auto-refresh data on focus (debounced to avoid excessive calls) + const now = Date.now() + const lastRefresh = window._lastVariableRefresh || 0 + if (now - lastRefresh > 30000) { // Refresh at most once every 30 seconds + window._lastVariableRefresh = now + handleRefresh() + } + }, [onFocus, id, handleRefresh]) + // Create flattened list of all variables with their metadata const allVariables = useMemo(() => { const variables = [] @@ -190,7 +247,7 @@ export function VariableSelectorWidget(props) { {label && {label}} - {/* Search and Dataset Filter */} + {/* Search and Dataset Filter with Refresh */} ))} + + } + onClick={handleRefresh} + isLoading={refreshing} + loadingText="Refreshing..." + size="md" + variant="outline" + colorScheme="blue" + aria-label="Refresh variables" + isDisabled={loading} + /> + {/* Variable Selection */} @@ -234,10 +304,11 @@ export function VariableSelectorWidget(props) { value={value || ''} onChange={(e) => onChange(e.target.value === '' ? undefined : e.target.value)} onBlur={onBlur && ((e) => onBlur(id, e.target.value))} - onFocus={onFocus && ((e) => onFocus(id, e.target.value))} + onFocus={handleFocusWithRefresh} borderColor={borderColor} _focus={{ borderColor: focusBorderColor }} bg={bgColor} + isDisabled={disabled || readonly} > {filteredVariables.map((variable, index) => ( diff --git a/frontend/src/components/widgets/AllWidgets.jsx b/frontend/src/components/widgets/AllWidgets.jsx index 73d94de..5a97837 100644 --- a/frontend/src/components/widgets/AllWidgets.jsx +++ b/frontend/src/components/widgets/AllWidgets.jsx @@ -1,5 +1,6 @@ import { customWidgets } from './CustomWidgets' import { widgets } from '../rjsf/widgets' +import VariableSelectorWidget from '../rjsf/VariableSelectorWidget' // Comprehensive widget collection that merges all available widgets // for full UI schema support with layouts @@ -17,9 +18,10 @@ export const allWidgets = { select: widgets.SelectWidget, checkbox: widgets.CheckboxWidget, - // Variable selector aliases - variableSelector: customWidgets.VariableSelectorWidget, - 'variable-selector': customWidgets.VariableSelectorWidget, + // Variable selector aliases - use the advanced version with search and metadata + variableSelector: VariableSelectorWidget, + 'variable-selector': VariableSelectorWidget, + VariableSelectorWidget: VariableSelectorWidget, // PLC-specific widget aliases (if available) plcArea: widgets.PlcAreaWidget, diff --git a/frontend/src/contexts/VariableContext.jsx b/frontend/src/contexts/VariableContext.jsx new file mode 100644 index 0000000..8bac30c --- /dev/null +++ b/frontend/src/contexts/VariableContext.jsx @@ -0,0 +1,34 @@ +import React, { createContext, useContext, useState, useCallback } from 'react' + +// Context for managing variable data updates across components +const VariableContext = createContext() + +export function VariableProvider({ children }) { + const [variableRefreshTrigger, setVariableRefreshTrigger] = useState(0) + + // Function to trigger refresh of all variable selectors + const triggerVariableRefresh = useCallback(() => { + setVariableRefreshTrigger(prev => prev + 1) + }, []) + + const value = { + variableRefreshTrigger, + triggerVariableRefresh + } + + return ( + + {children} + + ) +} + +export function useVariableContext() { + const context = useContext(VariableContext) + if (context === undefined) { + throw new Error('useVariableContext must be used within a VariableProvider') + } + return context +} + +export default VariableContext diff --git a/frontend/src/pages/DashboardNew.jsx b/frontend/src/pages/DashboardNew.jsx index 46e0d96..271849e 100644 --- a/frontend/src/pages/DashboardNew.jsx +++ b/frontend/src/pages/DashboardNew.jsx @@ -44,6 +44,7 @@ import validator from '@rjsf/validator-ajv8' import PlotManager from '../components/PlotManager' import allWidgets from '../components/widgets/AllWidgets' import LayoutObjectFieldTemplate from '../components/rjsf/LayoutObjectFieldTemplate' +import { VariableProvider, useVariableContext } from '../contexts/VariableContext' import * as api from '../services/api' // StatusBar Component - Real-time PLC status with action buttons @@ -312,6 +313,7 @@ function ConfigurationPanel({ schemaData, formData, onFormChange, onSave, saving // Dataset Manager - Type 3 Form Pattern implementation function DatasetManager() { + const { triggerVariableRefresh } = useVariableContext() const [datasetsConfig, setDatasetsConfig] = useState(null) const [variablesConfig, setVariablesConfig] = useState(null) const [datasetsSchemaData, setDatasetsSchemaData] = useState(null) @@ -379,6 +381,8 @@ function DatasetManager() { duration: 2000 }) setVariablesConfig(formData) + // Trigger refresh of all variable selector widgets + triggerVariableRefresh() } catch (error) { toast({ title: '❌ Failed to save variables', @@ -599,7 +603,10 @@ function DatasetManager() { templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }} onSubmit={({ formData }) => { updateSelectedDatasetVariables(formData) - saveVariables(variablesConfig) + saveVariables(variablesConfig).then(() => { + // Additional trigger after successful save + triggerVariableRefresh() + }) }} onChange={({ formData }) => updateSelectedDatasetVariables(formData)} > @@ -707,8 +714,17 @@ function EventsDisplay({ events, loading, onRefresh }) { ) } -// Main Dashboard Component - PLC S7-31x Streamer & Logger +// Main Dashboard Component - PLC S7-31x Streamer & Logger export default function Dashboard() { + return ( + + + + ) +} + +// Dashboard Content Component (separated to use context) +function DashboardContent() { const [status, setStatus] = useState(null) const [statusLoading, setStatusLoading] = useState(true) const [statusError, setStatusError] = useState('') diff --git a/system_state.json b/system_state.json index 07b1a7c..ce76b17 100644 --- a/system_state.json +++ b/system_state.json @@ -3,11 +3,11 @@ "should_connect": true, "should_stream": false, "active_datasets": [ + "Test", "Fast", - "DAR", - "Test" + "DAR" ] }, "auto_recovery_enabled": true, - "last_update": "2025-08-14T11:34:34.761474" + "last_update": "2025-08-14T12:05:58.306284" } \ No newline at end of file