diff --git a/application_events.json b/application_events.json index d4f4aea..09a4783 100644 --- a/application_events.json +++ b/application_events.json @@ -1,257 +1,5 @@ { "events": [ - { - "timestamp": "2025-08-14T16:52:25.768669", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: DB 2121", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.776678", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: UDT 82", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.785672", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: UDT 82", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.794673", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: UDT 1", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.803233", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: UDT 1", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.811243", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: VAT 22", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.819268", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: VAT 22", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.826782", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: DB 959", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.834787", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: DB 959", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.841796", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 1804", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.848796", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FC 1804", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.857991", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: DB 972", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.864990", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: DB 972", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.872997", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: DB 930", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.881999", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: DB 930", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.890523", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FB 1800", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.898534", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FB 1800", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.907703", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: DB 971", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.914685", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: DB 971", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.922698", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: DB 970", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.929693", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: DB 970", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.937844", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 2000", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.944860", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FC 2000", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.954008", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 2036", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.962012", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FC 2036", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.971003", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 2013", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.978013", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FC 2013", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.987006", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 2001", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:25.994321", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FC 2001", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:26.002877", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 2003", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:26.009876", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FC 2003", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:26.017866", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 2037", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:26.025863", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FC 2037", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:26.033864", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 2033", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:26.041875", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Could not parse PLC address: FC 2033", - "details": {} - }, - { - "timestamp": "2025-08-14T16:52:26.049093", - "level": "warning", - "event_type": "symbol_parse_warning", - "message": "Unrecognized address format: FC 2012", - "details": {} - }, { "timestamp": "2025-08-14T16:52:26.063705", "level": "warning", @@ -6999,8 +6747,354 @@ "event_type": "symbols_loaded", "message": "Loaded 2077 symbols from C:/Users/migue/Downloads/symSAE452.asc", "details": {} + }, + { + "timestamp": "2025-08-14T17:00:00.267126", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T17:06:22.110502", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T17:06:22.161221", + "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-14T17:06:22.174891", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T17:06:22.183906", + "level": "info", + "event_type": "udp_streaming_started", + "message": "UDP streaming to PlotJuggler started", + "details": { + "udp_host": "127.0.0.1", + "udp_port": 9870, + "datasets_available": 3 + } + }, + { + "timestamp": "2025-08-14T17:06:22.192523", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T17:10:14.827169", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T17:10:14.876601", + "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-14T17:10:14.884605", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T17:10:14.892976", + "level": "info", + "event_type": "udp_streaming_started", + "message": "UDP streaming to PlotJuggler started", + "details": { + "udp_host": "127.0.0.1", + "udp_port": 9870, + "datasets_available": 3 + } + }, + { + "timestamp": "2025-08-14T17:10:14.908569", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T17:10:32.756676", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T17:10:32.805384", + "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-14T17:10:32.814382", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T17:10:32.823381", + "level": "info", + "event_type": "udp_streaming_started", + "message": "UDP streaming to PlotJuggler started", + "details": { + "udp_host": "127.0.0.1", + "udp_port": 9870, + "datasets_available": 3 + } + }, + { + "timestamp": "2025-08-14T17:10:32.837621", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T17:14:00.334194", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T17:14:00.401621", + "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-14T17:14:00.410620", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T17:14:00.421480", + "level": "info", + "event_type": "udp_streaming_started", + "message": "UDP streaming to PlotJuggler started", + "details": { + "udp_host": "127.0.0.1", + "udp_port": 9870, + "datasets_available": 3 + } + }, + { + "timestamp": "2025-08-14T17:14:00.435620", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T17:25:34.393400", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T17:25:34.443828", + "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-14T17:25:34.451828", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T17:25:34.464832", + "level": "info", + "event_type": "udp_streaming_started", + "message": "UDP streaming to PlotJuggler started", + "details": { + "udp_host": "127.0.0.1", + "udp_port": 9870, + "datasets_available": 3 + } + }, + { + "timestamp": "2025-08-14T17:25:34.477358", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T17:31:05.141755", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T17:31:05.207794", + "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-14T17:31:05.219935", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T17:31:05.234342", + "level": "info", + "event_type": "udp_streaming_started", + "message": "UDP streaming to PlotJuggler started", + "details": { + "udp_host": "127.0.0.1", + "udp_port": 9870, + "datasets_available": 3 + } + }, + { + "timestamp": "2025-08-14T17:31:05.240939", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T17:32:09.019907", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-14T17:32:09.087100", + "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-14T17:32:09.107982", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 3 + } + }, + { + "timestamp": "2025-08-14T17:32:09.122999", + "level": "error", + "event_type": "csv_cleanup_failed", + "message": "CSV cleanup failed: 'max_hours'", + "details": {} + }, + { + "timestamp": "2025-08-14T17:32:09.144483", + "level": "info", + "event_type": "udp_streaming_started", + "message": "UDP streaming to PlotJuggler started", + "details": { + "udp_host": "127.0.0.1", + "udp_port": 9870, + "datasets_available": 3 + } } ], - "last_updated": "2025-08-14T16:52:36.475776", + "last_updated": "2025-08-14T17:32:09.144483", + "total_entries": 1000 +}ted", + "details": { + "udp_host": "127.0.0.1", + "udp_port": 9870, + "datasets_available": 3 + } + } + ], + "last_updated": "2025-08-14T17:32:09.123999", "total_entries": 1000 } \ No newline at end of file diff --git a/config/data/dataset_variables.json b/config/data/dataset_variables.json index c87504d..1c711e9 100644 --- a/config/data/dataset_variables.json +++ b/config/data/dataset_variables.json @@ -4,6 +4,7 @@ "dataset_id": "DAR", "variables": [ { + "configType": "manual", "area": "db", "db": 1011, "name": "UR29_Brix", @@ -12,6 +13,7 @@ "type": "real" }, { + "configType": "manual", "area": "db", "db": 1011, "name": "UR29_ma", @@ -23,7 +25,15 @@ }, { "dataset_id": "Fast", - "variables": [] + "variables": [ + { + "configType": "symbol", + "area": "db", + "type": "real", + "streaming": false, + "symbol": "FTP302_Brix" + } + ] } ] } \ No newline at end of file diff --git a/config/schema/ui/dataset-variables.uischema.json b/config/schema/ui/dataset-variables.uischema.json index 5fad8d7..24832ee 100644 --- a/config/schema/ui/dataset-variables.uischema.json +++ b/config/schema/ui/dataset-variables.uischema.json @@ -25,167 +25,215 @@ "removable": true }, "items": { - "ui:order": [ - "name", - "area", - "db", - "offset", - "bit", - "type", - "streaming" - ], - "ui:layout": [ - [ - { - "name": "name", - "width": 4 + "oneOf": [ + { + "ui:title": "Manual Configuration", + "ui:description": "Configure PLC variable parameters manually", + "ui:order": [ + "name", + "area", + "db", + "offset", + "bit", + "type", + "streaming" + ], + "ui:layout": [ + [ + { + "name": "name", + "width": 4 + }, + { + "name": "area", + "width": 2 + }, + { + "name": "db", + "width": 2 + }, + { + "name": "offset", + "width": 2 + }, + { + "name": "type", + "width": 2 + } + ], + [ + { + "name": "bit", + "width": 3 + }, + { + "name": "streaming", + "width": 9 + } + ] + ], + "name": { + "ui:widget": "text", + "ui:placeholder": "Variable name", + "ui:help": "📝 Human-readable name for this variable" }, - { - "name": "area", - "width": 2 - }, - { - "name": "db", - "width": 2 - }, - { - "name": "offset", - "width": 2 - }, - { - "name": "type", - "width": 2 - } - ], - [ - { - "name": "bit", - "width": 3 - }, - { - "name": "streaming", - "width": 9 - } - ] - ], - "name": { - "ui:widget": "text", - "ui:placeholder": "Variable name", - "ui:help": "📝 Human-readable name for this variable" - }, - "area": { - "ui:widget": "select", - "ui:help": "PLC memory area (DB=DataBlock, MW=MemoryWord, etc.)", - "ui:options": { - "enumOptions": [ - { - "value": "db", - "label": "🗃️ DB (Data Block)" - }, - { - "value": "mw", - "label": "📊 MW (Memory Word)" - }, - { - "value": "m", - "label": "💾 M (Memory)" - }, - { - "value": "pew", - "label": "📥 PEW (Process Input Word)" - }, - { - "value": "pe", - "label": "📥 PE (Process Input)" - }, - { - "value": "paw", - "label": "📤 PAW (Process Output Word)" - }, - { - "value": "pa", - "label": "📤 PA (Process Output)" - }, - { - "value": "e", - "label": "🔌 E (Input)" - }, - { - "value": "a", - "label": "🔌 A (Output)" - }, - { - "value": "mb", - "label": "💾 MB (Memory Byte)" + "area": { + "ui:widget": "select", + "ui:help": "PLC memory area (DB=DataBlock, MW=MemoryWord, etc.)", + "ui:options": { + "enumOptions": [ + { + "value": "db", + "label": "🗃️ DB (Data Block)" + }, + { + "value": "mw", + "label": "📊 MW (Memory Word)" + }, + { + "value": "m", + "label": "💾 M (Memory)" + }, + { + "value": "pew", + "label": "📥 PEW (Process Input Word)" + }, + { + "value": "pe", + "label": "📥 PE (Process Input)" + }, + { + "value": "paw", + "label": "📤 PAW (Process Output Word)" + }, + { + "value": "pa", + "label": "📤 PA (Process Output)" + }, + { + "value": "e", + "label": "🔌 E (Input)" + }, + { + "value": "a", + "label": "🔌 A (Output)" + }, + { + "value": "mb", + "label": "💾 MB (Memory Byte)" + } + ] } - ] - } - }, - "db": { - "ui:widget": "updown", - "ui:help": "Data Block number (required for DB area)", - "ui:placeholder": "1011" - }, - "offset": { - "ui:widget": "updown", - "ui:help": "Byte offset within the memory area" - }, - "bit": { - "ui:widget": "updown", - "ui:help": "Bit position (0-7) for bit-addressable areas" - }, - "type": { - "ui:widget": "select", - "ui:help": "PLC data type", - "ui:options": { - "enumOptions": [ - { - "value": "real", - "label": "🔢 REAL (32-bit float)" - }, - { - "value": "int", - "label": "🔢 INT (16-bit signed)" - }, - { - "value": "bool", - "label": "✅ BOOL (1-bit boolean)" - }, - { - "value": "dint", - "label": "🔢 DINT (32-bit signed)" - }, - { - "value": "word", - "label": "🔢 WORD (16-bit unsigned)" - }, - { - "value": "byte", - "label": "🔢 BYTE (8-bit unsigned)" - }, - { - "value": "uint", - "label": "🔢 UINT (16-bit unsigned)" - }, - { - "value": "udint", - "label": "🔢 UDINT (32-bit unsigned)" - }, - { - "value": "sint", - "label": "🔢 SINT (8-bit signed)" - }, - { - "value": "usint", - "label": "🔢 USINT (8-bit unsigned)" + }, + "db": { + "ui:widget": "updown", + "ui:help": "Data Block number (required for DB area)", + "ui:placeholder": "1011" + }, + "offset": { + "ui:widget": "updown", + "ui:help": "Byte offset within the memory area" + }, + "bit": { + "ui:widget": "updown", + "ui:help": "Bit position (0-7) for bit-addressable areas" + }, + "type": { + "ui:widget": "select", + "ui:help": "PLC data type", + "ui:options": { + "enumOptions": [ + { + "value": "real", + "label": "🔢 REAL (32-bit float)" + }, + { + "value": "int", + "label": "🔢 INT (16-bit signed)" + }, + { + "value": "bool", + "label": "✅ BOOL (1-bit boolean)" + }, + { + "value": "dint", + "label": "🔢 DINT (32-bit signed)" + }, + { + "value": "word", + "label": "🔢 WORD (16-bit unsigned)" + }, + { + "value": "byte", + "label": "🔢 BYTE (8-bit unsigned)" + }, + { + "value": "uint", + "label": "🔢 UINT (16-bit unsigned)" + }, + { + "value": "udint", + "label": "🔢 UDINT (32-bit unsigned)" + }, + { + "value": "sint", + "label": "🔢 SINT (8-bit signed)" + }, + { + "value": "usint", + "label": "🔢 USINT (8-bit unsigned)" + } + ] } - ] + }, + "streaming": { + "ui:widget": "checkbox", + "ui:help": "📡 Enable real-time streaming to PlotJuggler for visualization" + } + }, + { + "ui:title": "Symbol-based Configuration", + "ui:description": "Use a symbol from the loaded ASC file", + "ui:order": [ + "name", + "symbol", + "streaming" + ], + "ui:layout": [ + [ + { + "name": "name", + "width": 6 + }, + { + "name": "symbol", + "width": 6 + } + ], + [ + { + "name": "streaming", + "width": 12 + } + ] + ], + "name": { + "ui:widget": "text", + "ui:placeholder": "Variable name (auto-filled from symbol)", + "ui:help": "📝 Human-readable name for this variable", + "ui:readonly": true + }, + "symbol": { + "ui:widget": "dataset-variable-symbol", + "ui:placeholder": "Select a PLC symbol...", + "ui:help": "🔍 Search and select a symbol from the loaded ASC file" + }, + "streaming": { + "ui:widget": "checkbox", + "ui:help": "📡 Enable real-time streaming to PlotJuggler for visualization" + } } - }, - "streaming": { - "ui:widget": "checkbox", - "ui:help": "📡 Enable real-time streaming to PlotJuggler for visualization" - } + ] } } } diff --git a/frontend/src/components/DatasetVariablesRJSF.jsx b/frontend/src/components/DatasetVariablesRJSF.jsx new file mode 100644 index 0000000..9ceac98 --- /dev/null +++ b/frontend/src/components/DatasetVariablesRJSF.jsx @@ -0,0 +1,195 @@ +import React, { useState, useEffect } from 'react' +import { + Box, + VStack, + HStack, + Card, + CardBody, + CardHeader, + Heading, + Text, + Select, + Alert, + AlertIcon, + useColorModeValue, + Spinner +} from '@chakra-ui/react' +import Form from '@rjsf/chakra-ui' +import validator from '@rjsf/validator-ajv8' +import LayoutObjectFieldTemplate from './rjsf/LayoutObjectFieldTemplate.jsx' +import { allWidgets } from './widgets/AllWidgets.jsx' + +/** + * DatasetVariablesRJSF - Maneja variables de dataset usando RJSF Type 3 pattern + */ +export default function DatasetVariablesRJSF({ + datasets = {}, + selectedDatasetId, + onSelectDataset, + onVariablesUpdate +}) { + const [schema, setSchema] = useState(null) + const [uiSchema, setUiSchema] = useState(null) + const [variables, setVariables] = useState({ variables: [] }) + const [loading, setLoading] = useState(true) + const [saving, setSaving] = useState(false) + const [message, setMessage] = useState('') + + const muted = useColorModeValue('gray.600', 'gray.300') + + // Load schema and data + useEffect(() => { + loadSchemaAndData() + }, []) + + const loadSchemaAndData = async () => { + setLoading(true) + try { + // Load schema + const schemaResp = await fetch('/api/config/schema/dataset-variables') + const schemaData = await schemaResp.json() + + if (schemaData.success) { + setSchema(schemaData.schema) + setUiSchema(schemaData.ui_schema || {}) + } + + // Load data + const dataResp = await fetch('/api/config/dataset-variables') + const data = await dataResp.json() + + if (data.success) { + setVariables(data.data || { variables: [] }) + } + + } catch (error) { + console.error('Error loading schema/data:', error) + setMessage(`Error loading: ${error.message}`) + } finally { + setLoading(false) + } + } + + const saveVariables = async (formData) => { + setSaving(true) + try { + const response = await fetch('/api/config/dataset-variables', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData) + }) + + const result = await response.json() + + if (result.success) { + setVariables(formData) + setMessage('Variables saved successfully') + setTimeout(() => setMessage(''), 3000) + + if (onVariablesUpdate) { + onVariablesUpdate(formData) + } + } else { + throw new Error(result.error || 'Failed to save variables') + } + } catch (error) { + console.error('Error saving variables:', error) + setMessage(`Error saving: ${error.message}`) + } finally { + setSaving(false) + } + } + + // Get available datasets for selector + const datasetOptions = Object.entries(datasets).map(([id, dataset]) => ({ + value: id, + label: `${dataset.name || id} (${id})` + })) + + if (loading) { + return ( + + + + + Loading dataset variables configuration... + + + + ) + } + + if (!schema) { + return ( + + + + + Failed to load schema for dataset variables + + + + ) + } + + return ( + + + + 🔧 Dataset Variables + + Configure PLC variables for each dataset - use symbols or manual configuration + + + {datasetOptions.length > 0 && ( + + Reference Dataset: + + + )} + + + + {message && ( + + + {message} + + )} + +
setVariables(formData)} + onSubmit={({ formData }) => saveVariables(formData)} + templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }} + widgets={allWidgets} + showErrorList={false} + disabled={saving} + > + + + +
+
+
+ ) +} diff --git a/frontend/src/components/FormTable.jsx b/frontend/src/components/FormTable.jsx index baa32c2..5acc097 100644 --- a/frontend/src/components/FormTable.jsx +++ b/frontend/src/components/FormTable.jsx @@ -21,7 +21,7 @@ import { AddIcon, DeleteIcon, EditIcon } from '@chakra-ui/icons' import Form from '@rjsf/chakra-ui' import validator from '@rjsf/validator-ajv8' import LayoutObjectFieldTemplate from './rjsf/LayoutObjectFieldTemplate.jsx' -import { widgets } from './rjsf/widgets.jsx' +import { allWidgets } from './widgets/AllWidgets.jsx' /** * FormTable - Muestra objetos como filas de formularios usando schemas RJSF @@ -152,7 +152,7 @@ export default function FormTable({ validator={validator} onSubmit={({ formData }) => handleAdd(formData)} templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }} - widgets={widgets} + widgets={allWidgets} showErrorList={false} > @@ -231,7 +231,7 @@ export default function FormTable({ onChange={editingKey === key ? ({ formData }) => setEditingFormData(formData) : () => { }} onSubmit={editingKey === key ? ({ formData }) => handleEdit(key, formData) : undefined} templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }} - widgets={widgets} + widgets={allWidgets} readonly={editingKey !== key} showErrorList={false} > diff --git a/frontend/src/components/widgets/AllWidgets.jsx b/frontend/src/components/widgets/AllWidgets.jsx index fa1254e..27ea409 100644 --- a/frontend/src/components/widgets/AllWidgets.jsx +++ b/frontend/src/components/widgets/AllWidgets.jsx @@ -3,6 +3,7 @@ import { widgets } from '../rjsf/widgets' import VariableSelectorWidget from '../rjsf/VariableSelectorWidget' import FilePathWidget from './FilePathWidget' import SymbolSelectorWidget from './SymbolSelectorWidget' +import DatasetVariableSymbolWidget from './DatasetVariableSymbolWidget' // Comprehensive widget collection that merges all available widgets // for full UI schema support with layouts @@ -35,6 +36,11 @@ export const allWidgets = { 'symbol-selector': SymbolSelectorWidget, SymbolSelectorWidget: SymbolSelectorWidget, + // Dataset variable symbol widget with auto-fill + datasetVariableSymbol: DatasetVariableSymbolWidget, + 'dataset-variable-symbol': DatasetVariableSymbolWidget, + DatasetVariableSymbolWidget: DatasetVariableSymbolWidget, + // PLC-specific widget aliases (if available) plcArea: widgets.PlcAreaWidget, plcDataType: widgets.PlcDataTypeWidget, diff --git a/frontend/src/components/widgets/DatasetVariableSymbolWidget.jsx b/frontend/src/components/widgets/DatasetVariableSymbolWidget.jsx new file mode 100644 index 0000000..cf36748 --- /dev/null +++ b/frontend/src/components/widgets/DatasetVariableSymbolWidget.jsx @@ -0,0 +1,100 @@ +import React, { useState, useEffect } from 'react' +import { + Box, + VStack, + Text, + Badge, + useToast +} from '@chakra-ui/react' +import SymbolSelectorWidget from './SymbolSelectorWidget' + +const DatasetVariableSymbolWidget = ({ value, onChange, label, disabled, readonly, required, placeholder, formContext }) => { + const [selectedSymbol, setSelectedSymbol] = useState(null) + const toast = useToast() + + // Load symbol details when value changes + useEffect(() => { + if (value && !selectedSymbol) { + loadSymbolDetails(value) + } + }, [value]) + + const loadSymbolDetails = async (symbolName) => { + try { + const response = await fetch('/api/symbols') + const data = await response.json() + + if (data.success && data.symbols) { + const symbol = data.symbols.find(s => s.name === symbolName) + if (symbol) { + setSelectedSymbol(symbol) + } + } + } catch (error) { + console.error('Error loading symbol details:', error) + } + } + + const handleSymbolSelect = (symbolName) => { + // Update the symbol field + onChange(symbolName) + + // Show success message + toast({ + title: 'Symbol Selected', + description: `Selected: ${symbolName}`, + status: 'success', + duration: 2000, + isClosable: true, + }) + } + + const symbolOptions = { + onSymbolSelect: (symbol) => { + setSelectedSymbol(symbol) + handleSymbolSelect(symbol.name) + } + } + + return ( + + + + {selectedSymbol && ( + + + Symbol Information: + + + + 📝 Name: {selectedSymbol.description || selectedSymbol.name} + + + 📍 Address: {selectedSymbol.plc_address} + + + 🔧 Area: {selectedSymbol.area?.toUpperCase()}, Offset: {selectedSymbol.offset} + {selectedSymbol.db && `, DB: ${selectedSymbol.db}`} + {selectedSymbol.bit !== null && selectedSymbol.bit !== undefined && `, Bit: ${selectedSymbol.bit}`} + + + {selectedSymbol.data_type} + + + + )} + + ) +} + +export default DatasetVariableSymbolWidget diff --git a/frontend/src/components/widgets/SymbolSelectorWidget.jsx b/frontend/src/components/widgets/SymbolSelectorWidget.jsx index 71b83ce..831f634 100644 --- a/frontend/src/components/widgets/SymbolSelectorWidget.jsx +++ b/frontend/src/components/widgets/SymbolSelectorWidget.jsx @@ -26,7 +26,7 @@ import { } from '@chakra-ui/react' import { FiSearch, FiX, FiList, FiInfo } from 'react-icons/fi' -const SymbolSelectorWidget = ({ value, onChange, label, disabled, readonly, required, placeholder }) => { +const SymbolSelectorWidget = ({ value, onChange, label, disabled, readonly, required, placeholder, formContext, options }) => { const [symbols, setSymbols] = useState([]) const [searchQuery, setSearchQuery] = useState('') const [isLoading, setIsLoading] = useState(false) @@ -92,6 +92,12 @@ const SymbolSelectorWidget = ({ value, onChange, label, disabled, readonly, requ const handleSymbolSelect = (symbol) => { setSelectedSymbol(symbol) onChange(symbol.name) + + // If we have formContext and the widget options include callbacks for auto-fill + if (formContext && options && options.onSymbolSelect) { + options.onSymbolSelect(symbol) + } + onClose() toast({ diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 5ee7c2f..29b6680 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -783,25 +783,60 @@ function DatasetManager() { items: { type: "object", properties: { - name: { type: "string", title: "Variable Name" }, - area: { - type: "string", - title: "Memory Area", - enum: ["db", "mw", "m", "pew", "pe", "paw", "pa", "e", "a", "mb"], - default: "db" - }, - db: { type: "integer", title: "DB Number", minimum: 1, maximum: 9999 }, - offset: { type: "integer", title: "Offset", minimum: 0, maximum: 8191 }, - bit: { type: "integer", title: "Bit Position", minimum: 0, maximum: 7 }, - type: { - type: "string", - title: "Data Type", - enum: ["real", "int", "dint", "bool", "word", "byte"], - default: "real" - }, - streaming: { type: "boolean", title: "Stream to UDP", default: false } + configType: { + type: "string", + title: "Configuration Type", + enum: ["manual", "symbol"], + default: "manual" + } }, - required: ["name", "area", "offset", "type"] + allOf: [ + { + if: { properties: { configType: { const: "manual" } } }, + then: { + properties: { + name: { type: "string", title: "Variable Name" }, + area: { + type: "string", + title: "Memory Area", + enum: ["db", "mw", "m", "pew", "pe", "paw", "pa", "e", "a", "mb"], + default: "db" + }, + db: { type: "integer", title: "DB Number", minimum: 1, maximum: 9999 }, + offset: { type: "integer", title: "Offset", minimum: 0, maximum: 8191 }, + bit: { type: "integer", title: "Bit Position", minimum: 0, maximum: 7 }, + type: { + type: "string", + title: "Data Type", + enum: ["real", "int", "dint", "bool", "word", "byte"], + default: "real" + }, + streaming: { type: "boolean", title: "Stream to UDP", default: false } + }, + required: ["name", "area", "offset", "type"] + } + }, + { + if: { properties: { configType: { const: "symbol" } } }, + then: { + properties: { + name: { + type: "string", + title: "Variable Name", + description: "Auto-filled from symbol", + readOnly: true + }, + symbol: { + type: "string", + title: "PLC Symbol", + description: "Select a symbol from loaded ASC file" + }, + streaming: { type: "boolean", title: "Stream to UDP", default: false } + }, + required: ["symbol"] + } + } + ] } } } @@ -810,14 +845,49 @@ function DatasetManager() { const singleDatasetUiSchema = { variables: { items: { - "ui:layout": [[ - { "name": "name", "width": 3 }, - { "name": "area", "width": 2 }, - { "name": "db", "width": 1 }, - { "name": "offset", "width": 2 }, - { "name": "type", "width": 2 }, - { "name": "streaming", "width": 2 } - ]] + "ui:order": ["configType", "name", "symbol", "area", "db", "offset", "bit", "type", "streaming"], + "ui:layout": [ + [ + { "name": "configType", "width": 3 } + ], + [ + { "name": "name", "width": 4 }, + { "name": "symbol", "width": 8 } + ], + [ + { "name": "area", "width": 2 }, + { "name": "db", "width": 2 }, + { "name": "offset", "width": 2 }, + { "name": "bit", "width": 2 }, + { "name": "type", "width": 2 }, + { "name": "streaming", "width": 2 } + ] + ], + "configType": { + "ui:widget": "select", + "ui:help": "Choose between manual configuration or symbol-based setup", + "ui:enumNames": ["Manual Configuration", "Symbol-based Configuration"] + }, + "symbol": { + "ui:widget": "symbol-selector", + "ui:placeholder": "Select a PLC symbol...", + "ui:help": "🔍 Search and select a symbol from the loaded ASC file" + }, + "name": { + "ui:help": "Human-readable name for this variable" + }, + "area": { + "ui:widget": "select", + "ui:help": "PLC memory area" + }, + "type": { + "ui:widget": "select", + "ui:help": "PLC data type" + }, + "streaming": { + "ui:widget": "checkbox", + "ui:help": "Enable UDP streaming to PlotJuggler" + } } } } diff --git a/system_state.json b/system_state.json index 5e5fdcc..b901054 100644 --- a/system_state.json +++ b/system_state.json @@ -3,11 +3,11 @@ "should_connect": true, "should_stream": true, "active_datasets": [ + "Fast", "Test", - "DAR", - "Fast" + "DAR" ] }, "auto_recovery_enabled": true, - "last_update": "2025-08-14T16:52:19.757206" + "last_update": "2025-08-14T17:32:09.169720" } \ No newline at end of file