diff --git a/application_events.json b/application_events.json index 0fc7e0a..4dad8c6 100644 --- a/application_events.json +++ b/application_events.json @@ -8145,8 +8145,882 @@ "activated_datasets": 1, "total_datasets": 1 } + }, + { + "timestamp": "2025-08-09T01:15:37.931252", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:15:37.995825", + "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-09T01:15:38.011841", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T01:15:53.487852", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_0", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 20, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T01:27:47.413936", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:27:47.482826", + "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-09T01:27:47.500264", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T01:32:23.076094", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:32:23.143386", + "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-09T01:32:23.158384", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T01:37:00.136346", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:37:00.187014", + "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-09T01:37:00.197221", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T01:39:03.481139", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_0", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 120, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T01:39:35.253297", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_0", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 500, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T01:41:40.082777", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:41:40.149440", + "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-09T01:41:40.164902", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T01:42:00.022025", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_0", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 50, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T01:47:39.354931", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:47:39.421225", + "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-09T01:47:39.436596", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T01:50:13.074173", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:50:13.159596", + "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-09T01:50:13.174855", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T01:53:53.601636", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:53:53.683028", + "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-09T01:53:53.697931", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T01:54:09.106165", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_0", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 10, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T01:54:12.772022", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_0", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 24, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T01:54:24.565758", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_0", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 5, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T01:55:13.651682", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_0", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 20, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T01:58:11.878053", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T01:58:11.947102", + "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-09T01:58:11.963722", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T02:02:17.438722", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T02:02:17.493178", + "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-09T02:02:17.504654", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-09T02:04:32.017747", + "level": "info", + "event_type": "plot_session_removed", + "message": "Plot session 'UR29' removed", + "details": { + "session_id": "plot_0" + } + }, + { + "timestamp": "2025-08-09T02:05:35.486330", + "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": 60, + "trigger_variable": null, + "auto_started": true + } + }, + { + "timestamp": "2025-08-09T02:06:26.841012", + "level": "info", + "event_type": "dataset_created", + "message": "Dataset created: Fast (prefix: fast)", + "details": { + "dataset_id": "Fast", + "name": "Fast", + "prefix": "fast", + "sampling_interval": 0.1 + } + }, + { + "timestamp": "2025-08-09T02:06:51.166985", + "level": "info", + "event_type": "dataset_csv_file_created", + "message": "New CSV file created after variable modification for dataset 'DAR': gateway_phoenix_02_06_51.csv", + "details": { + "dataset_id": "DAR", + "file_path": "records\\09-08-2025\\gateway_phoenix_02_06_51.csv", + "variables_count": 3, + "reason": "variable_modification" + } + }, + { + "timestamp": "2025-08-09T02:06:51.178095", + "level": "info", + "event_type": "variable_added", + "message": "Variable added to dataset 'DAR': fUR29_Brix -> DB1011.1322 (real)", + "details": { + "dataset_id": "DAR", + "name": "fUR29_Brix", + "area": "db", + "db": 1011, + "offset": 1322, + "bit": null, + "type": "real", + "streaming": false + } + }, + { + "timestamp": "2025-08-09T02:07:40.947419", + "level": "info", + "event_type": "variable_added", + "message": "Variable added to dataset 'Fast': fUR29_Brix -> DB1011.1322 (real)", + "details": { + "dataset_id": "Fast", + "name": "fUR29_Brix", + "area": "db", + "db": 1011, + "offset": 1322, + "bit": null, + "type": "real", + "streaming": false + } + }, + { + "timestamp": "2025-08-09T02:08:07.444267", + "level": "info", + "event_type": "variable_added", + "message": "Variable added to dataset 'Fast': fUR29_ma -> DB1011.1296 (real)", + "details": { + "dataset_id": "Fast", + "name": "fUR29_ma", + "area": "db", + "db": 1011, + "offset": 1296, + "bit": null, + "type": "real", + "streaming": false + } + }, + { + "timestamp": "2025-08-09T02:08:26.500463", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: Fast", + "details": { + "dataset_id": "Fast", + "variables_count": 2, + "streaming_count": 0, + "prefix": "fast" + } + }, + { + "timestamp": "2025-08-09T02:09:13.534886", + "level": "info", + "event_type": "plot_session_created", + "message": "Plot session 'Fast' created and started", + "details": { + "session_id": "plot_2", + "variables": [ + "fUR29_ma", + "UR29_Brix", + "UR29_ma", + "fUR29_Brix" + ], + "time_window": 60, + "trigger_variable": null, + "auto_started": true + } + }, + { + "timestamp": "2025-08-09T22:43:54.204995", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 2 datasets after PLC reconnection", + "details": { + "resumed_datasets": 2, + "total_attempted": 2 + } + }, + { + "timestamp": "2025-08-09T23:22:59.642234", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T23:22:59.723755", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 3, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-09T23:22:59.737379", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: Fast", + "details": { + "dataset_id": "Fast", + "variables_count": 2, + "streaming_count": 0, + "prefix": "fast" + } + }, + { + "timestamp": "2025-08-09T23:22:59.748890", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 2 datasets activated", + "details": { + "activated_datasets": 2, + "total_datasets": 2 + } + }, + { + "timestamp": "2025-08-09T23:27:02.476867", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'Fast' configuration updated", + "details": { + "session_id": "plot_2", + "new_config": { + "name": "Fast", + "variables": [ + "fUR29_ma", + "UR29_Brix", + "UR29_ma", + "fUR29_Brix" + ], + "time_window": 10, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-09T23:42:15.949524", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-09T23:42:16.047945", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 3, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-09T23:42:16.061947", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: Fast", + "details": { + "dataset_id": "Fast", + "variables_count": 2, + "streaming_count": 0, + "prefix": "fast" + } + }, + { + "timestamp": "2025-08-09T23:42:16.079052", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 2 datasets activated", + "details": { + "activated_datasets": 2, + "total_datasets": 2 + } + }, + { + "timestamp": "2025-08-10T00:32:18.359734", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'Fast' configuration updated", + "details": { + "session_id": "plot_2", + "new_config": { + "name": "Fast", + "variables": [ + "fUR29_ma", + "UR29_Brix", + "UR29_ma", + "fUR29_Brix" + ], + "time_window": 60, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-10T00:32:50.990081", + "level": "info", + "event_type": "plot_session_removed", + "message": "Plot session 'Fast' removed", + "details": { + "session_id": "plot_2" + } + }, + { + "timestamp": "2025-08-10T00:33:55.840458", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_1", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 65, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-10T00:34:06.009225", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_1", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma" + ], + "time_window": 75, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-10T00:34:26.104532", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_1", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma", + "fUR29_Brix" + ], + "time_window": 75, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } + }, + { + "timestamp": "2025-08-10T00:37:18.004596", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-10T00:37:18.089614", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 3, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-10T00:37:18.104615", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: Fast", + "details": { + "dataset_id": "Fast", + "variables_count": 2, + "streaming_count": 0, + "prefix": "fast" + } + }, + { + "timestamp": "2025-08-10T00:37:18.121614", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 2 datasets activated", + "details": { + "activated_datasets": 2, + "total_datasets": 2 + } + }, + { + "timestamp": "2025-08-10T00:37:46.526185", + "level": "info", + "event_type": "plot_session_updated", + "message": "Plot session 'UR29' configuration updated", + "details": { + "session_id": "plot_1", + "new_config": { + "name": "UR29", + "variables": [ + "UR29_Brix", + "UR29_ma", + "fUR29_Brix", + "fUR29_ma" + ], + "time_window": 75, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true + } + } } ], - "last_updated": "2025-08-09T01:04:33.328051", - "total_entries": 767 + "last_updated": "2025-08-10T00:37:46.526185", + "total_entries": 834 } \ No newline at end of file diff --git a/core/plc_client.py b/core/plc_client.py index db7e3fd..f815444 100644 --- a/core/plc_client.py +++ b/core/plc_client.py @@ -35,6 +35,12 @@ class PLCClient: [] ) # List of callback functions for disconnection tracking + # Global I/O serialization to avoid concurrent snap7 calls + # Acts as a simple read queue to prevent 'CLI : Job pending' + self.io_lock = threading.RLock() + # Small inter-read delay to give PLC time between requests (seconds) + self.inter_read_delay_seconds = 0.002 + def connect(self, ip: str, rack: int, slot: int) -> bool: """Connect to S7-315 PLC""" try: @@ -223,65 +229,86 @@ class PLCClient: self.reconnection_thread.start() def read_variable(self, var_config: Dict[str, Any]) -> Any: - """Read a specific variable from the PLC""" + """Read a specific variable from the PLC, serialized across threads""" if not self.is_connected(): return None - try: - area_type = var_config.get("area", "db").lower() - offset = var_config["offset"] - var_type = var_config["type"] - bit = var_config.get("bit") + # Ensure only one snap7 operation at a time + with self.io_lock: + try: + area_type = var_config.get("area", "db").lower() + offset = var_config["offset"] + var_type = var_config["type"] + bit = var_config.get("bit") - if area_type == "db": - return self._read_db_variable(var_config, offset, var_type, bit) - elif area_type in ["mw", "m"]: - return self._read_memory_variable(offset, var_type) - elif area_type in ["pew", "pe"]: - return self._read_input_variable(offset, var_type) - elif area_type in ["paw", "pa"]: - return self._read_output_variable(offset, var_type) - elif area_type == "e": - return self._read_input_bit(offset, bit) - elif area_type == "a": - return self._read_output_bit(offset, bit) - elif area_type == "mb": - return self._read_memory_bit(offset, bit) - else: - if self.logger: - self.logger.error(f"Unsupported area type: {area_type}") - return None - - except Exception as e: - if self.logger: - self.logger.error(f"Error reading variable: {e}") - - # Check if this is a connection error and start automatic reconnection - if self._is_connection_error(str(e)): - was_connected_before = self.connected - self.connected = False - self.consecutive_failures += 1 - - if self.logger: - failure_num = self.consecutive_failures - msg = ( - "Connection error detected, starting automatic " - f"reconnection (failure #{failure_num})" + if area_type == "db": + result = self._read_db_variable( + var_config, + offset, + var_type, + bit, ) - self.logger.warning(msg) - - # If we were connected before, notify disconnection callbacks FIRST - if was_connected_before: + elif area_type in ["mw", "m"]: + result = self._read_memory_variable(offset, var_type) + elif area_type in [ + "pew", + "pe", + ]: + result = self._read_input_variable(offset, var_type) + elif area_type in [ + "paw", + "pa", + ]: + result = self._read_output_variable(offset, var_type) + elif area_type == "e": + result = self._read_input_bit(offset, bit) + elif area_type == "a": + result = self._read_output_bit(offset, bit) + elif area_type == "mb": + result = self._read_memory_bit(offset, bit) + else: if self.logger: - self.logger.info( - "Notifying disconnection callbacks for dataset tracking" + self.logger.error(f"Unsupported area type: {area_type}") + result = None + + # Small pacing delay between PLC I/O to avoid job overlap + if self.inter_read_delay_seconds and self.inter_read_delay_seconds > 0: + time.sleep(self.inter_read_delay_seconds) + + return result + + except Exception as e: + if self.logger: + self.logger.error(f"Error reading variable: {e}") + + # Check if this is a connection error and start automatic reconnection + if self._is_connection_error(str(e)): + was_connected_before = self.connected + self.connected = False + self.consecutive_failures += 1 + + if self.logger: + failure_num = self.consecutive_failures + msg = ( + "Connection error detected, starting automatic " + f"reconnection (failure #{failure_num})" ) - self._notify_disconnection_detected() + self.logger.warning(msg) - # Start automatic reconnection in background - self._start_automatic_reconnection() + # If we were connected before, notify disconnection + # callbacks FIRST + if was_connected_before: + if self.logger: + self.logger.info( + "Notifying disconnection callbacks for " + "dataset tracking" + ) + self._notify_disconnection_detected() - return None + # Start automatic reconnection in background + self._start_automatic_reconnection() + + return None def _read_db_variable( self, var_config: Dict[str, Any], offset: int, var_type: str, bit: Optional[int] @@ -451,7 +478,8 @@ class PLCClient: - DataStreamer.read_dataset_variables() during streaming cycles - Internal diagnostic operations - Public APIs should use cached values via DataStreamer.get_cached_dataset_values() + Public APIs should use cached values via + DataStreamer.get_cached_dataset_values() """ if not self.is_connected(): return {} @@ -478,9 +506,10 @@ class PLCClient: - DataStreamer.read_dataset_variables() during streaming cycles - Internal diagnostic operations - Public APIs should use cached values via DataStreamer.get_cached_dataset_values() - according to the application's single-read principle where variables are read - only once per dataset interval and cached for all other uses. + Public APIs should use cached values via + DataStreamer.get_cached_dataset_values() + according to the application's single-read principle where variables + are read only once per dataset interval and cached for all other uses. """ if not self.is_connected(): return { @@ -550,7 +579,7 @@ class PLCClient: } elif success_count < total_count: warning_msg = ( - f"Partial success: {success_count}/{total_count} " "variables read" + f"Partial success: {success_count}/{total_count} " f"variables read" ) return { "success": True, diff --git a/core/streamer.py b/core/streamer.py index c5d1cb9..6902dc7 100644 --- a/core/streamer.py +++ b/core/streamer.py @@ -481,11 +481,15 @@ class DataStreamer: try: start_time = time.time() - # Read variables for this dataset + # Read variables for this dataset (serialized across datasets) dataset_variables = self.config_manager.get_dataset_variables( dataset_id ) - all_data = self.read_dataset_variables(dataset_id, dataset_variables) + # Ensure entire dataset read is atomic w.r.t. other datasets + with self.plc_client.io_lock: + all_data = self.read_dataset_variables( + dataset_id, dataset_variables + ) if all_data: consecutive_errors = 0 diff --git a/plc_config.json b/plc_config.json index 0a5af50..755cc45 100644 --- a/plc_config.json +++ b/plc_config.json @@ -16,6 +16,6 @@ "max_days": 30, "max_hours": null, "cleanup_interval_hours": 24, - "last_cleanup": "2025-08-08T15:50:27.922821" + "last_cleanup": "2025-08-09T22:43:54.224975" } } \ No newline at end of file diff --git a/plc_datasets.json b/plc_datasets.json index 6dc3ee1..fe4e0a9 100644 --- a/plc_datasets.json +++ b/plc_datasets.json @@ -17,6 +17,13 @@ "type": "real", "streaming": true, "db": 1011 + }, + "fUR29_Brix": { + "area": "db", + "offset": 1322, + "type": "real", + "streaming": false, + "db": 1011 } }, "streaming_variables": [ @@ -26,12 +33,37 @@ "sampling_interval": 1.0, "enabled": true, "created": "2025-08-08T15:47:18.566053" + }, + "Fast": { + "name": "Fast", + "prefix": "fast", + "variables": { + "fUR29_Brix": { + "area": "db", + "offset": 1322, + "type": "real", + "streaming": false, + "db": 1011 + }, + "fUR29_ma": { + "area": "db", + "offset": 1296, + "type": "real", + "streaming": false, + "db": 1011 + } + }, + "streaming_variables": [], + "sampling_interval": 0.1, + "enabled": true, + "created": "2025-08-09T02:06:26.840011" } }, "active_datasets": [ - "DAR" + "DAR", + "Fast" ], - "current_dataset_id": "DAR", + "current_dataset_id": "Fast", "version": "1.0", - "last_update": "2025-08-09T01:04:33.316045" + "last_update": "2025-08-10T00:37:18.103618" } \ No newline at end of file diff --git a/plot_sessions.json b/plot_sessions.json index 0215368..177c2b7 100644 --- a/plot_sessions.json +++ b/plot_sessions.json @@ -1,21 +1,23 @@ { "plots": { - "plot_0": { + "plot_1": { "name": "UR29", "variables": [ "UR29_Brix", - "UR29_ma" + "UR29_ma", + "fUR29_Brix", + "fUR29_ma" ], - "time_window": 50, + "time_window": 75, "y_min": null, "y_max": null, "trigger_variable": null, "trigger_enabled": false, "trigger_on_true": true, - "session_id": "plot_0" + "session_id": "plot_1" } }, - "session_counter": 1, - "last_saved": "2025-08-09T00:59:52.219460", + "session_counter": 2, + "last_saved": "2025-08-10T00:37:46.525175", "version": "1.0" } \ No newline at end of file diff --git a/static/css/styles.css b/static/css/styles.css index 1ae5283..50bfd09 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -545,7 +545,8 @@ textarea { border: var(--pico-border-width) solid var(--pico-border-color); border-radius: var(--pico-border-radius); margin-bottom: 1rem; - overflow: hidden; + /* Evitar recortar etiquetas inferiores del eje del chart */ + overflow: visible; } .plot-header { @@ -631,7 +632,9 @@ textarea { .plot-canvas canvas { width: 100% !important; - height: 100% !important; + /* Permitir a Chart.js ajustar el alto con precisión */ + height: 100%; + display: block; max-height: 100%; } diff --git a/static/js/plotting.js b/static/js/plotting.js index 3165126..bd76fcb 100644 --- a/static/js/plotting.js +++ b/static/js/plotting.js @@ -166,7 +166,7 @@ class PlotManager {