feat: Update application events and system state management with new plot session events, enhanced plot definitions, and improved PlotManager component for better session control and configuration handling.
This commit is contained in:
parent
a9396ec309
commit
16355c4106
|
@ -911,8 +911,586 @@
|
|||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:05:13.082016",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:05:19.544702",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:05:23.384537",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:05:25.630774",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:28:19.815284",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:28:19.884191",
|
||||
"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-14T15:28:19.888188",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:28:19.891188",
|
||||
"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-14T15:28:19.913733",
|
||||
"level": "error",
|
||||
"event_type": "csv_cleanup_failed",
|
||||
"message": "CSV cleanup failed: 'max_hours'",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:28:27.534594",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:28:41.233200",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:33:53.298915",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:33:53.350464",
|
||||
"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-14T15:33:53.353473",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:33:53.357410",
|
||||
"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-14T15:33:53.379922",
|
||||
"level": "error",
|
||||
"event_type": "csv_cleanup_failed",
|
||||
"message": "CSV cleanup failed: 'max_hours'",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:34:05.357970",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:49:43.574411",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:49:43.623131",
|
||||
"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-14T15:49:43.626396",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:49:43.628399",
|
||||
"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-14T15:49:43.654855",
|
||||
"level": "error",
|
||||
"event_type": "csv_cleanup_failed",
|
||||
"message": "CSV cleanup failed: 'max_hours'",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:49:51.181481",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:50:01.495815",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:50:11.269643",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:50:25.266473",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:51:28.701739",
|
||||
"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": 40,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:51:37.177827",
|
||||
"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": 40,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:52:00.981320",
|
||||
"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": 40,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:54:01.861161",
|
||||
"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": 40,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:54:09.237860",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:54:21.495214",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:58:52.564607",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:58:52.631147",
|
||||
"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-14T15:58:52.636144",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T15:58:52.640144",
|
||||
"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-14T15:58:52.661477",
|
||||
"level": "error",
|
||||
"event_type": "csv_cleanup_failed",
|
||||
"message": "CSV cleanup failed: 'max_hours'",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T16:00:00.366424",
|
||||
"level": "error",
|
||||
"event_type": "csv_cleanup_failed",
|
||||
"message": "CSV cleanup failed: 'max_hours'",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T16:00:38.957148",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T16:00:39.022563",
|
||||
"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-14T16:00:39.027564",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T16:00:39.029563",
|
||||
"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-14T16:00:39.084155",
|
||||
"level": "error",
|
||||
"event_type": "csv_cleanup_failed",
|
||||
"message": "CSV cleanup failed: 'max_hours'",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T16:01:29.356193",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T16:01:35.624303",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T16:01:44.863171",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-14T16:01:52.736771",
|
||||
"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": 20,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"last_updated": "2025-08-14T15:04:12.217187",
|
||||
"total_entries": 90
|
||||
"last_updated": "2025-08-14T16:01:52.736771",
|
||||
"total_entries": 137
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
"line_tension": 0,
|
||||
"name": "UR29",
|
||||
"point_hover_radius": 4,
|
||||
"point_radius": 1,
|
||||
"point_radius": 4,
|
||||
"stepped": true,
|
||||
"time_window": 20,
|
||||
"trigger_enabled": false,
|
||||
|
|
|
@ -4,20 +4,20 @@
|
|||
"plot_id": "plot_1",
|
||||
"variables": [
|
||||
{
|
||||
"color": "#3498db",
|
||||
"enabled": true,
|
||||
"label": "Brix",
|
||||
"line_width": 2,
|
||||
"variable_name": "UR29_Brix",
|
||||
"y_axis": "left"
|
||||
"label": "Brix",
|
||||
"color": "#3498db",
|
||||
"line_width": 2,
|
||||
"y_axis": "left",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"color": "#e74c3c",
|
||||
"enabled": true,
|
||||
"label": "ma",
|
||||
"line_width": 2,
|
||||
"variable_name": "UR29_ma",
|
||||
"y_axis": "left"
|
||||
"label": "ma",
|
||||
"color": "#dce740",
|
||||
"line_width": 2,
|
||||
"y_axis": "left",
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -35,13 +35,16 @@ import { useVariableContext } from '../contexts/VariableContext'
|
|||
import * as api from '../services/api'
|
||||
|
||||
// Collapsible Plot Items Form - Each item in the array is individually collapsible
|
||||
function CollapsiblePlotItemsForm({ data, schema, uiSchema, onSave, title, icon, getItemLabel }) {
|
||||
function CollapsiblePlotItemsForm({ data, schema, uiSchema, onSave, title, icon, getItemLabel, isExpanded, onToggleExpansion }) {
|
||||
const [formData, setFormData] = useState(data)
|
||||
const [expandedItems, setExpandedItems] = useState(new Set())
|
||||
|
||||
useEffect(() => {
|
||||
setFormData(data)
|
||||
}, [data])
|
||||
// Solo actualizar formData si data realmente cambió en contenido
|
||||
if (JSON.stringify(data) !== JSON.stringify(formData)) {
|
||||
setFormData(data)
|
||||
}
|
||||
}, [data]) // Removed formData from dependencies to avoid infinite loop
|
||||
|
||||
if (!schema || !formData) {
|
||||
return (
|
||||
|
@ -97,8 +100,13 @@ function CollapsiblePlotItemsForm({ data, schema, uiSchema, onSave, title, icon,
|
|||
setExpandedItems(newExpanded)
|
||||
}
|
||||
|
||||
const saveChanges = () => {
|
||||
onSave(formData)
|
||||
const saveChanges = async () => {
|
||||
try {
|
||||
await onSave(formData)
|
||||
// No hacer nada con la expansión aquí - será manejado por el componente padre
|
||||
} catch (error) {
|
||||
console.error('Error saving:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Get item schema from the array schema
|
||||
|
@ -119,6 +127,17 @@ function CollapsiblePlotItemsForm({ data, schema, uiSchema, onSave, title, icon,
|
|||
</Text>
|
||||
</Box>
|
||||
<HStack spacing={2}>
|
||||
{/* Si se proporciona toggle de expansión externa, agregar botón de colapso */}
|
||||
{onToggleExpansion && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={onToggleExpansion}
|
||||
rightIcon={isExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
>
|
||||
{isExpanded ? 'Collapse' : 'Expand'}
|
||||
</Button>
|
||||
)}
|
||||
<Button size="sm" colorScheme="green" onClick={addItem}>
|
||||
➕ Add Item
|
||||
</Button>
|
||||
|
@ -129,76 +148,144 @@ function CollapsiblePlotItemsForm({ data, schema, uiSchema, onSave, title, icon,
|
|||
</Flex>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
{items.length === 0 ? (
|
||||
<Box textAlign="center" py={8}>
|
||||
<Text color="gray.500" mb={4}>
|
||||
No items configured yet
|
||||
</Text>
|
||||
<Button colorScheme="green" onClick={addItem}>
|
||||
➕ Add First Item
|
||||
</Button>
|
||||
</Box>
|
||||
) : (
|
||||
<VStack spacing={3} align="stretch">
|
||||
{items.map((item, index) => {
|
||||
const isExpanded = expandedItems.has(index)
|
||||
const itemLabel = getItemLabel ? getItemLabel(item) : (item.name || item.id || `Item ${index + 1}`)
|
||||
|
||||
return (
|
||||
<Card key={index} variant="outline" size="sm">
|
||||
<CardHeader py={2}>
|
||||
<Flex align="center" justify="space-between">
|
||||
<HStack spacing={2}>
|
||||
{/* Usar Collapse si se proporciona estado de expansión externo */}
|
||||
{onToggleExpansion ? (
|
||||
<Collapse in={isExpanded}>
|
||||
<CardBody>
|
||||
{items.length === 0 ? (
|
||||
<Box textAlign="center" py={8}>
|
||||
<Text color="gray.500" mb={4}>
|
||||
No items configured yet
|
||||
</Text>
|
||||
<Button colorScheme="green" onClick={addItem}>
|
||||
➕ Add First Item
|
||||
</Button>
|
||||
</Box>
|
||||
) : (
|
||||
<VStack spacing={3} align="stretch">
|
||||
{items.map((item, index) => {
|
||||
const isItemExpanded = expandedItems.has(index)
|
||||
const itemLabel = getItemLabel ? getItemLabel(item) : (item.name || item.id || `Item ${index + 1}`)
|
||||
|
||||
return (
|
||||
<Card key={index} variant="outline" size="sm">
|
||||
<CardHeader py={2}>
|
||||
<Flex align="center" justify="space-between">
|
||||
<HStack spacing={2}>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
onClick={() => toggleItemExpansion(index)}
|
||||
rightIcon={isItemExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
>
|
||||
{itemLabel}
|
||||
</Button>
|
||||
<Badge colorScheme="green" size="sm">#{index + 1}</Badge>
|
||||
</HStack>
|
||||
<Button
|
||||
size="xs"
|
||||
colorScheme="red"
|
||||
variant="ghost"
|
||||
onClick={() => removeItem(index)}
|
||||
>
|
||||
🗑️
|
||||
</Button>
|
||||
</Flex>
|
||||
</CardHeader>
|
||||
|
||||
<Collapse in={isItemExpanded}>
|
||||
<CardBody pt={0}>
|
||||
<Form
|
||||
schema={itemSchema}
|
||||
uiSchema={itemUiSchema}
|
||||
formData={item}
|
||||
validator={validator}
|
||||
widgets={allWidgets}
|
||||
templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }}
|
||||
onChange={({ formData: newItemData }) => updateItem(index, newItemData)}
|
||||
>
|
||||
<div></div> {/* Prevents form buttons from showing */}
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Collapse>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</VStack>
|
||||
)}
|
||||
</CardBody>
|
||||
</Collapse>
|
||||
) : (
|
||||
<CardBody>
|
||||
{items.length === 0 ? (
|
||||
<Box textAlign="center" py={8}>
|
||||
<Text color="gray.500" mb={4}>
|
||||
No items configured yet
|
||||
</Text>
|
||||
<Button colorScheme="green" onClick={addItem}>
|
||||
➕ Add First Item
|
||||
</Button>
|
||||
</Box>
|
||||
) : (
|
||||
<VStack spacing={3} align="stretch">
|
||||
{items.map((item, index) => {
|
||||
const isItemExpanded = expandedItems.has(index)
|
||||
const itemLabel = getItemLabel ? getItemLabel(item) : (item.name || item.id || `Item ${index + 1}`)
|
||||
|
||||
return (
|
||||
<Card key={index} variant="outline" size="sm">
|
||||
<CardHeader py={2}>
|
||||
<Flex align="center" justify="space-between">
|
||||
<HStack spacing={2}>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
onClick={() => toggleItemExpansion(index)}
|
||||
rightIcon={isItemExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
>
|
||||
{itemLabel}
|
||||
</Button>
|
||||
<Badge colorScheme="green" size="sm">#{index + 1}</Badge>
|
||||
</HStack>
|
||||
<Button
|
||||
size="xs"
|
||||
colorScheme="red"
|
||||
variant="ghost"
|
||||
onClick={() => toggleItemExpansion(index)}
|
||||
rightIcon={isExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
onClick={() => removeItem(index)}
|
||||
>
|
||||
{itemLabel}
|
||||
🗑️
|
||||
</Button>
|
||||
<Badge colorScheme="green" size="sm">#{index + 1}</Badge>
|
||||
</HStack>
|
||||
<Button
|
||||
size="xs"
|
||||
colorScheme="red"
|
||||
variant="ghost"
|
||||
onClick={() => removeItem(index)}
|
||||
>
|
||||
🗑️
|
||||
</Button>
|
||||
</Flex>
|
||||
</CardHeader>
|
||||
|
||||
<Collapse in={isExpanded}>
|
||||
<CardBody pt={0}>
|
||||
<Form
|
||||
schema={itemSchema}
|
||||
uiSchema={itemUiSchema}
|
||||
formData={item}
|
||||
validator={validator}
|
||||
widgets={allWidgets}
|
||||
templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }}
|
||||
onChange={({ formData: newItemData }) => updateItem(index, newItemData)}
|
||||
>
|
||||
<div></div> {/* Prevents form buttons from showing */}
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Collapse>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</VStack>
|
||||
)}
|
||||
</CardBody>
|
||||
</Flex>
|
||||
</CardHeader>
|
||||
|
||||
<Collapse in={isItemExpanded}>
|
||||
<CardBody pt={0}>
|
||||
<Form
|
||||
schema={itemSchema}
|
||||
uiSchema={itemUiSchema}
|
||||
formData={item}
|
||||
validator={validator}
|
||||
widgets={allWidgets}
|
||||
templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }}
|
||||
onChange={({ formData: newItemData }) => updateItem(index, newItemData)}
|
||||
>
|
||||
<div></div> {/* Prevents form buttons from showing */}
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Collapse>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</VStack>
|
||||
)}
|
||||
</CardBody>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
// Collapsible Plot Component
|
||||
function CollapsiblePlotChart({ plotDefinition, plotVariables, onConfigUpdate, onRemove }) {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
function CollapsiblePlotChart({ plotDefinition, plotVariables, onConfigUpdate, onReloadConfig, onRemove, isExpanded, onToggleExpansion }) {
|
||||
|
||||
return (
|
||||
<Card>
|
||||
|
@ -214,10 +301,10 @@ function CollapsiblePlotChart({ plotDefinition, plotVariables, onConfigUpdate, o
|
|||
<Button
|
||||
size="xs"
|
||||
variant="outline"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
rightIcon={isOpen ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
onClick={() => onToggleExpansion(plotDefinition.id)}
|
||||
rightIcon={isExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
>
|
||||
{isOpen ? 'Hide' : 'Show'} Chart
|
||||
{isExpanded ? 'Hide' : 'Show'} Chart
|
||||
</Button>
|
||||
<Button size="xs" colorScheme="red" variant="outline" onClick={() => onRemove(plotDefinition.id)}>
|
||||
❌
|
||||
|
@ -226,12 +313,13 @@ function CollapsiblePlotChart({ plotDefinition, plotVariables, onConfigUpdate, o
|
|||
</Flex>
|
||||
</CardHeader>
|
||||
|
||||
<Collapse in={isOpen}>
|
||||
<Collapse in={isExpanded}>
|
||||
<CardBody pt={0}>
|
||||
<PlotRealtimeSession
|
||||
plotDefinition={plotDefinition}
|
||||
plotVariables={plotVariables}
|
||||
onConfigUpdate={onConfigUpdate}
|
||||
onReloadConfig={onReloadConfig}
|
||||
onRemove={onRemove}
|
||||
isCollapsed={true}
|
||||
/>
|
||||
|
@ -250,6 +338,10 @@ export default function PlotManager() {
|
|||
const [plotVariablesSchemaData, setPlotVariablesSchemaData] = useState(null)
|
||||
const [selectedPlotId, setSelectedPlotId] = useState('')
|
||||
const [loading, setLoading] = useState(true)
|
||||
// Estado para preservar qué plots están expandidos/colapsados
|
||||
const [expandedPlots, setExpandedPlots] = useState(new Set())
|
||||
// Estado para preservar si la configuración de plot definitions está expandida
|
||||
const [configExpanded, setConfigExpanded] = useState(false)
|
||||
const toast = useToast()
|
||||
|
||||
const loadPlotData = useCallback(async () => {
|
||||
|
@ -288,10 +380,38 @@ export default function PlotManager() {
|
|||
}
|
||||
}, [selectedPlotId, toast])
|
||||
|
||||
// Función para actualizar configuración de un plot específico sin recargar todo
|
||||
const updatePlotConfig = async (plotId, newConfig) => {
|
||||
try {
|
||||
// Actualizar solo el plot específico en la configuración local
|
||||
const updatedPlots = plotsConfig?.plots?.map(plot =>
|
||||
plot.id === plotId ? { ...plot, ...newConfig } : plot
|
||||
) || []
|
||||
|
||||
const updatedConfig = {
|
||||
...plotsConfig,
|
||||
plots: updatedPlots
|
||||
}
|
||||
|
||||
// Guardar en el backend
|
||||
await api.writeConfig('plot-definitions', updatedConfig)
|
||||
|
||||
// Actualizar estado local
|
||||
setPlotsConfig(updatedConfig)
|
||||
|
||||
console.log(`✅ Plot ${plotId} configuration updated locally`)
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to update plot ${plotId} config:`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const savePlotsConfig = async (formData) => {
|
||||
try {
|
||||
await api.writeConfig('plot-definitions', formData)
|
||||
setPlotsConfig(formData)
|
||||
// Mantener la configuración expandida después de Apply
|
||||
setConfigExpanded(true)
|
||||
toast({
|
||||
title: '✅ Plot definitions saved',
|
||||
status: 'success',
|
||||
|
@ -350,6 +470,26 @@ export default function PlotManager() {
|
|||
return plotVars?.variables || []
|
||||
}, [plotVariablesConfig])
|
||||
|
||||
// Functions to handle plot expansion state
|
||||
const togglePlotExpansion = (plotId) => {
|
||||
const newExpanded = new Set(expandedPlots)
|
||||
if (newExpanded.has(plotId)) {
|
||||
newExpanded.delete(plotId)
|
||||
} else {
|
||||
newExpanded.add(plotId)
|
||||
}
|
||||
setExpandedPlots(newExpanded)
|
||||
}
|
||||
|
||||
const isPlotExpanded = (plotId) => {
|
||||
return expandedPlots.has(plotId)
|
||||
}
|
||||
|
||||
// Function to handle configuration expansion toggle
|
||||
const toggleConfigExpansion = () => {
|
||||
setConfigExpanded(!configExpanded)
|
||||
}
|
||||
|
||||
// Helper functions for Type 3 form pattern (Plot Variables)
|
||||
const getSelectedPlotVariables = () => {
|
||||
if (!selectedPlotId || !plotVariablesConfig?.variables) return { variables: [] }
|
||||
|
@ -367,7 +507,7 @@ export default function PlotManager() {
|
|||
variables: [{ plot_id: selectedPlotId, ...formData }]
|
||||
}
|
||||
setPlotVariablesConfig(newConfig)
|
||||
return
|
||||
return newConfig
|
||||
}
|
||||
|
||||
const existingIndex = plotVariablesConfig.variables.findIndex(
|
||||
|
@ -383,10 +523,13 @@ export default function PlotManager() {
|
|||
updatedVars.push(newVarData)
|
||||
}
|
||||
|
||||
setPlotVariablesConfig({
|
||||
const updatedConfig = {
|
||||
...plotVariablesConfig,
|
||||
variables: updatedVars
|
||||
})
|
||||
}
|
||||
|
||||
setPlotVariablesConfig(updatedConfig)
|
||||
return updatedConfig
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -436,8 +579,11 @@ export default function PlotManager() {
|
|||
key={plotDef.id}
|
||||
plotDefinition={plotDef}
|
||||
plotVariables={getPlotVariables(plotDef.id)}
|
||||
onConfigUpdate={loadPlotData}
|
||||
onConfigUpdate={updatePlotConfig}
|
||||
onReloadConfig={loadPlotData}
|
||||
onRemove={() => {}}
|
||||
isExpanded={isPlotExpanded(plotDef.id)}
|
||||
onToggleExpansion={togglePlotExpansion}
|
||||
/>
|
||||
))}
|
||||
</VStack>
|
||||
|
@ -456,6 +602,8 @@ export default function PlotManager() {
|
|||
title="Plot Definitions"
|
||||
icon="📋"
|
||||
getItemLabel={(item) => `${item.name || item.id} (${item.time_window || 60}s)`}
|
||||
isExpanded={configExpanded}
|
||||
onToggleExpansion={toggleConfigExpansion}
|
||||
/>
|
||||
|
||||
{/* Plot Variables Configuration - Type 3 Form Pattern */}
|
||||
|
@ -611,8 +759,8 @@ export default function PlotManager() {
|
|||
widgets={allWidgets}
|
||||
templates={{ ObjectFieldTemplate: LayoutObjectFieldTemplate }}
|
||||
onSubmit={({ formData }) => {
|
||||
updateSelectedPlotVariables(formData)
|
||||
savePlotVariables(plotVariablesConfig).then(() => {
|
||||
const updatedConfig = updateSelectedPlotVariables(formData)
|
||||
savePlotVariables(updatedConfig).then(() => {
|
||||
// Additional trigger after successful save
|
||||
triggerVariableRefresh?.()
|
||||
})
|
||||
|
|
|
@ -44,7 +44,8 @@ export default function PlotRealtimeSession({
|
|||
plotDefinition,
|
||||
plotVariables = [],
|
||||
onRemove,
|
||||
onConfigUpdate
|
||||
onConfigUpdate,
|
||||
onReloadConfig // Nueva prop para recargar configuración desde backend
|
||||
}) {
|
||||
const [session, setSession] = useState({
|
||||
session_id: plotDefinition.id,
|
||||
|
@ -74,6 +75,28 @@ export default function PlotRealtimeSession({
|
|||
const intervalRef = useRef(null)
|
||||
const toast = useToast()
|
||||
|
||||
// Track if we're in the middle of applying changes to avoid conflicts
|
||||
const applyingChangesRef = useRef(false)
|
||||
|
||||
// Update localConfig when plotDefinition changes (but not during our own updates)
|
||||
useEffect(() => {
|
||||
if (!applyingChangesRef.current) {
|
||||
setLocalConfig({
|
||||
time_window: plotDefinition.time_window || 60,
|
||||
y_min: plotDefinition.y_min,
|
||||
y_max: plotDefinition.y_max,
|
||||
trigger_enabled: plotDefinition.trigger_enabled || false,
|
||||
trigger_variable: plotDefinition.trigger_variable,
|
||||
trigger_on_true: plotDefinition.trigger_on_true || true,
|
||||
// Visual style properties
|
||||
line_tension: plotDefinition.line_tension !== undefined ? plotDefinition.line_tension : 0.4,
|
||||
stepped: plotDefinition.stepped || false,
|
||||
point_radius: plotDefinition.point_radius !== undefined ? plotDefinition.point_radius : 1,
|
||||
point_hover_radius: plotDefinition.point_hover_radius !== undefined ? plotDefinition.point_hover_radius : 4
|
||||
})
|
||||
}
|
||||
}, [plotDefinition])
|
||||
|
||||
const cardBg = useColorModeValue('white', 'gray.700')
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.600')
|
||||
const muted = useColorModeValue('gray.600', 'gray.300')
|
||||
|
@ -231,8 +254,17 @@ export default function PlotRealtimeSession({
|
|||
}, [plotDefinition, plotVariables, localConfig, refreshSessionStatus, toast])
|
||||
|
||||
// Apply configuration changes
|
||||
const applyConfigChanges = async () => {
|
||||
const applyConfigChanges = useCallback(async () => {
|
||||
applyingChangesRef.current = true
|
||||
|
||||
// Remember the current active state before applying changes
|
||||
const wasActive = session.is_active
|
||||
const wasPaused = session.is_paused
|
||||
|
||||
try {
|
||||
console.log(`🔄 Applying configuration changes for plot ${plotDefinition.id}...`)
|
||||
console.log(`📊 Plot was active: ${wasActive}, paused: ${wasPaused}`)
|
||||
|
||||
// Update backend configuration
|
||||
await onConfigUpdate?.(plotDefinition.id, localConfig)
|
||||
|
||||
|
@ -241,13 +273,26 @@ export default function PlotRealtimeSession({
|
|||
chartControlsRef.current.updateConfig(localConfig)
|
||||
}
|
||||
|
||||
// Wait a moment for the configuration to be applied
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
// Refresh session status to get the latest state
|
||||
await refreshSessionStatus()
|
||||
|
||||
// If the plot was active before, restart it
|
||||
if (wasActive && !wasPaused) {
|
||||
console.log(`🔄 Restarting plot session that was active before Apply...`)
|
||||
await handleControlClick('start')
|
||||
}
|
||||
|
||||
toast({
|
||||
title: '✅ Configuration updated',
|
||||
status: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
setShowSettings(false)
|
||||
// No cerrar settings automáticamente - mantener abierto para continuar editando
|
||||
// setShowSettings(false)
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: '❌ Failed to update configuration',
|
||||
|
@ -255,8 +300,13 @@ export default function PlotRealtimeSession({
|
|||
status: 'error',
|
||||
duration: 3000
|
||||
})
|
||||
} finally {
|
||||
// Reset the flag after a short delay to allow the update to propagate
|
||||
setTimeout(() => {
|
||||
applyingChangesRef.current = false
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}, [plotDefinition.id, localConfig, onConfigUpdate, session.is_active, session.is_paused, refreshSessionStatus, handleControlClick, toast])
|
||||
|
||||
const resetConfigChanges = () => {
|
||||
setLocalConfig({
|
||||
|
@ -286,6 +336,13 @@ export default function PlotRealtimeSession({
|
|||
try {
|
||||
console.log(`🔄 Refreshing configuration for plot ${plotDefinition.id}...`)
|
||||
|
||||
// First, reload configuration from backend if the function is available
|
||||
if (onReloadConfig) {
|
||||
console.log(`📥 Reloading plot configuration from backend...`)
|
||||
await onReloadConfig()
|
||||
console.log(`✅ Configuration reloaded from backend`)
|
||||
}
|
||||
|
||||
// Trigger chart configuration refresh if available
|
||||
if (chartControlsRef.current?.refreshConfiguration) {
|
||||
await chartControlsRef.current.refreshConfiguration()
|
||||
|
@ -338,7 +395,7 @@ export default function PlotRealtimeSession({
|
|||
} finally {
|
||||
setIsRefreshing(false)
|
||||
}
|
||||
}, [plotDefinition.id, refreshSessionStatus, handleControlClick, session.is_active, session.is_paused, toast])
|
||||
}, [plotDefinition.id, refreshSessionStatus, handleControlClick, session.is_active, session.is_paused, onReloadConfig, toast])
|
||||
|
||||
// Auto-refresh session status
|
||||
useEffect(() => {
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
"should_stream": true,
|
||||
"active_datasets": [
|
||||
"DAR",
|
||||
"Test",
|
||||
"Fast"
|
||||
"Fast",
|
||||
"Test"
|
||||
]
|
||||
},
|
||||
"auto_recovery_enabled": true,
|
||||
"last_update": "2025-08-14T15:03:55.394271"
|
||||
"last_update": "2025-08-14T16:00:39.031573"
|
||||
}
|
Loading…
Reference in New Issue