From e4f6aeea6592845f9a21486fac36592ecaa93a0d Mon Sep 17 00:00:00 2001 From: Taha Momayiz Date: Fri, 18 Jul 2025 09:43:50 -0400 Subject: [PATCH 1/6] Update stdatalog_TUI.py - Resolves AttributeError preventing TUI from launching - Changes DeviceTemplateManager to DeviceCatalogManager for device template loading - Adds proper import for DeviceCatalogManager - Makes TUI fully functional for device data logging --- .../stdatalog/TUI/stdatalog_TUI.py | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/gui_applications/stdatalog/TUI/stdatalog_TUI.py b/gui_applications/stdatalog/TUI/stdatalog_TUI.py index e0a2350..d17c218 100644 --- a/gui_applications/stdatalog/TUI/stdatalog_TUI.py +++ b/gui_applications/stdatalog/TUI/stdatalog_TUI.py @@ -77,7 +77,7 @@ from asciimatics.screen import Screen from asciimatics.exceptions import ResizeScreenError -from stdatalog_pnpl.DTDL.device_template_manager import DeviceTemplateManager +from stdatalog_pnpl.DTDL.device_template_manager import DeviceTemplateManager, DeviceCatalogManager from stdatalog_core.HSD_link.HSDLink import HSDLink from stdatalog_examples.gui_applications.stdatalog.TUI.Views.tui_views import HSDMainView, HSDLoggingView class HSDInfo(): @@ -110,6 +110,7 @@ def __init__(self, output_folder, sub_datetime_folder, acq_name, acq_desc, file_ selected_ispu_id = None is_log_started = False is_log_manually_stopped = False + is_shutting_down = False output_acquisition_path = None threads_stop_flags = [] tag_status_list = [] @@ -149,7 +150,7 @@ def is_hsd_link_v2(self): return False def load_device_template(self, board_id, fw_id): - dev_template_json = DeviceTemplateManager.query_dtdl_model(board_id, fw_id) + dev_template_json = DeviceCatalogManager.query_dtdl_model(board_id, fw_id) if isinstance(dev_template_json,dict): fw_name = self.hsd_link.get_firmware_info(self.selected_device_id).get("firmware_info").get("fw_name") if fw_name is not None: @@ -195,8 +196,15 @@ def upload_device_conf_file(self): shutil.copy(self.tui_flags.file_config, self.output_acquisition_path) def update_sensor_list(self): - if self.selected_device_id is not None: + if self.is_shutting_down or self.selected_device_id is None: + return + try: self.sensor_list = HSDLink.get_sensor_list(self.hsd_link, self.selected_device_id, only_active=True) + except Exception as e: + log.warning(f"Could not update sensor list: {e}") + # Keep the existing sensor list if update fails + if self.sensor_list is None: + self.sensor_list = [] def init_sensor_data_counters(self): all_sensor_list = HSDLink.get_sensor_list(self.hsd_link, self.selected_device_id, only_active=False) @@ -261,10 +269,29 @@ def stop_log(self): f.close() HSDLink.stop_log(self.hsd_link, self.selected_device_id) self.is_log_started = False - HSDLink.save_json_device_file(self.hsd_link, self.selected_device_id, self.output_acquisition_path) - log.info("Device Config json File correctly saved") - HSDLink.save_json_acq_info_file(self.hsd_link, self.selected_device_id, self.output_acquisition_path) - log.info("Acquisition Info json File correctly saved") + + # Try to save device JSON file with error handling and retry + import time + for attempt in range(3): # Try up to 3 times + try: + if attempt > 0: + time.sleep(0.5) # Wait 500ms between attempts + HSDLink.save_json_device_file(self.hsd_link, self.selected_device_id, self.output_acquisition_path) + log.info("Device Config json File correctly saved") + break # Success, exit the retry loop + except Exception as e: + if attempt == 2: # Last attempt + log.warning(f"Could not save device config JSON file after {attempt + 1} attempts: {e}") + else: + log.debug(f"Attempt {attempt + 1} to save device config failed, retrying...") + + # Try to save acquisition info file with error handling + try: + HSDLink.save_json_acq_info_file(self.hsd_link, self.selected_device_id, self.output_acquisition_path) + log.info("Acquisition Info json File correctly saved") + except Exception as e: + log.warning(f"Could not save acquisition info JSON file: {e}") + #Save ISPU output format json file if passed as CLI parameter self.save_ai_ucf_file() self.save_ispu_out_fmt_file() From 83d0a9a70f3e814ca5e4236e8cd85bee9e00a8e5 Mon Sep 17 00:00:00 2001 From: Taha Momayiz Date: Fri, 18 Jul 2025 09:44:17 -0400 Subject: [PATCH 2/6] Update tui_views.py - Add get_fw_info_value() helper for safe firmware info dictionary access - Fix device list display to use dictionary keys instead of object attributes - Add retry mechanism for device config JSON saving with 500ms delays - Implement shutdown flag to prevent race conditions during quit - Enhance sensor list updates with proper error handling --- .../stdatalog/TUI/Views/tui_views.py | 82 ++++++++++++++----- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/gui_applications/stdatalog/TUI/Views/tui_views.py b/gui_applications/stdatalog/TUI/Views/tui_views.py index 01dcd54..6a1e1f6 100644 --- a/gui_applications/stdatalog/TUI/Views/tui_views.py +++ b/gui_applications/stdatalog/TUI/Views/tui_views.py @@ -22,6 +22,23 @@ from stdatalog_core.HSD_link import HSDLink_v2 from stdatalog_core.HSD_utils.exceptions import WrongDeviceConfigFile +def get_fw_info_value(fw_info_dict, key, default_value="Unknown"): + """ + Helper function to safely extract values from firmware info dictionary. + fw_info_dict can be either a nested dict with 'firmware_info' key or the firmware_info dict directly. + """ + if fw_info_dict is None: + return default_value + + # Handle nested structure + if isinstance(fw_info_dict, dict) and 'firmware_info' in fw_info_dict: + return fw_info_dict['firmware_info'].get(key, default_value) + # Handle direct firmware_info dict + elif isinstance(fw_info_dict, dict): + return fw_info_dict.get(key, default_value) + else: + return default_value + class HSDMainView(Frame): def __init__(self, screen, hsd_info): super(HSDMainView, self).__init__(screen, @@ -98,10 +115,23 @@ def __init__(self, screen, hsd_info): dev_list = [] self._hsd_info_model.selected_device_id = None + # Helper function to extract firmware info from device structure + def get_device_info(device_data, index): + if 'devices' in device_data and len(device_data['devices']) > 0: + device = device_data['devices'][0] + components = device.get('components', []) + for comp in components: + if isinstance(comp, dict) and 'firmware_info' in comp: + fw_info = comp['firmware_info'] + alias = fw_info.get('alias', f'Device_{index}') + part_number = fw_info.get('part_number', 'Unknown') + return f"{index}) [{alias}] - {part_number}" + return f"{index}) Unknown Device" + # Create the form for displaying the list of devices. self._list_view = ListBox( Widget.FILL_FRAME, - [("{}) [{}] - {}".format(i,dev.fw_info.alias, dev.fw_info.part_number), i) for i,dev in enumerate(dev_list)], + [(get_device_info(dev, i), i) for i, dev in enumerate(dev_list)], name="device_list", add_scroll_bar=True, on_change=self._on_pick, @@ -117,19 +147,20 @@ def __init__(self, screen, hsd_info): self._hsd_info_model.update_fw_info() layout_fw_info = Layout([100]) self.add_layout(layout_fw_info) + if self._hsd_info_model.selected_fw_info is None: dev_id = dev_sn = dev_al = dev_pn = dev_url = dev_fwn = dev_fwv = dev_dfe = dev_dff = dev_ns = "" else: dev_id = "- id: {}".format(self._hsd_info_model.selected_device_id) - dev_sn = "- sn: {}".format(self._hsd_info_model.selected_fw_info.serial_number) - dev_al = "- alias: {}".format(self._hsd_info_model.selected_fw_info.alias) - dev_pn = "- pn: {}".format(self._hsd_info_model.selected_fw_info.part_number) - dev_url = "- url: {}".format(self._hsd_info_model.selected_fw_info.url) - dev_fwn = "- fw_name: {}".format(self._hsd_info_model.selected_fw_info.fw_name) - dev_fwv = "- fw_version: {}".format(self._hsd_info_model.selected_fw_info.fw_version) - dev_dfe = "- data_file_ext: {}".format(self._hsd_info_model.selected_fw_info.data_file_ext) - dev_dff = "- data_file_format: {}".format(self._hsd_info_model.selected_fw_info.data_file_format) - dev_ns = "- n_sensor: {}".format(self._hsd_info_model.selected_fw_info.n_sensor) + dev_sn = "- sn: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'serial_number', 'Unknown')) + dev_al = "- alias: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'alias')) + dev_pn = "- pn: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'part_number')) + dev_url = "- url: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'device_url')) + dev_fwn = "- fw_name: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'fw_name')) + dev_fwv = "- fw_version: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'fw_version')) + dev_dfe = "- data_file_ext: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'data_file_ext', 'dat')) + dev_dff = "- data_file_format: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'data_file_format', 'JSON')) + dev_ns = "- n_sensor: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'n_sensor', 'Unknown')) self.dev_id_lbl = Label(dev_id) self.dev_sn_lbl = Label(dev_sn) @@ -172,15 +203,15 @@ def _on_pick(self): self._hsd_info_model.update_fw_info() self.dev_id_lbl.text = "- id: {}".format(self._hsd_info_model.selected_device_id) - self.dev_sn_lbl.text = "- sn: {}".format(self._hsd_info_model.selected_fw_info.serial_number) - self.dev_al_lbl.text = "- alias: {}".format(self._hsd_info_model.selected_fw_info.alias) - self.dev_pn_lbl.text = "- pn: {}".format(self._hsd_info_model.selected_fw_info.part_number) - self.dev_url_lbl.text = "- url: {}".format(self._hsd_info_model.selected_fw_info.url) - self.dev_fwn_lbl.text = "- fw_name: {}".format(self._hsd_info_model.selected_fw_info.fw_name) - self.dev_fwv_lbl.text = "- fw_version: {}".format(self._hsd_info_model.selected_fw_info.fw_version) - self.dev_dfe_lbl.text = "- data_file_ext: {}".format(self._hsd_info_model.selected_fw_info.data_file_ext) - self.dev_dff_lbl.text = "- data_file_format: {}".format(self._hsd_info_model.selected_fw_info.data_file_format) - self.dev_ns_lbl.text = "- n_sensor: {}".format(self._hsd_info_model.selected_fw_info.n_sensor) + self.dev_sn_lbl.text = "- sn: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'serial_number', 'Unknown')) + self.dev_al_lbl.text = "- alias: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'alias')) + self.dev_pn_lbl.text = "- pn: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'part_number')) + self.dev_url_lbl.text = "- url: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'device_url')) + self.dev_fwn_lbl.text = "- fw_name: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'fw_name')) + self.dev_fwv_lbl.text = "- fw_version: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'fw_version')) + self.dev_dfe_lbl.text = "- data_file_ext: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'data_file_ext', 'dat')) + self.dev_dff_lbl.text = "- data_file_format: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'data_file_format', 'JSON')) + self.dev_ns_lbl.text = "- n_sensor: {}".format(get_fw_info_value(self._hsd_info_model.selected_fw_info, 'n_sensor', 'Unknown')) def _on_select(self): self.save() @@ -192,7 +223,7 @@ def _reload_device_list(self): self._hsd_info_model.update_device_list() if self._hsd_info_model.device_list is not None: self._no_device_label.text = "" - self._list_view.options = [("{}) [{}] - {}".format(i,dev.fw_info.alias, dev.fw_info.part_number), i) for i,dev in enumerate(self._hsd_info_model.device_list)] + self._list_view.options = [("{}) [{}] - {}".format(i, get_fw_info_value(dev, 'alias'), get_fw_info_value(dev, 'part_number')), i) for i,dev in enumerate(self._hsd_info_model.device_list)] else: self._no_device_label.text = "No device connected!" self._no_device_label.custom_colour = "invalid" @@ -379,10 +410,14 @@ def _stop(self): self.remaining_time_lbl.custom_colour = "label" def _stop_and_quit(self): + self._hsd_info_model.is_shutting_down = True if self._hsd_info_model.is_log_started: self._hsd_info_model.stop_log() self._hsd_info_model.is_log_manually_stopped = False - del self._hsd_info_model.hsd_link + try: + del self._hsd_info_model.hsd_link + except: + pass # Ignore errors when deleting hsd_link self._quit() def _on_tag_select(self): @@ -401,6 +436,9 @@ def _on_tag_select(self): self._tag_list_view.start_line = last_tag_start def _update(self, frame_no): + if self._hsd_info_model.is_shutting_down: + return + if frame_no - self._last_frame >= self.frame_update_count or self._last_frame == 0: self._last_frame = frame_no last_selection = self._sensor_list_view.value @@ -520,4 +558,4 @@ def _on_load_page(self): @staticmethod def _quit(): - raise StopApplication("User pressed quit") \ No newline at end of file + raise StopApplication("User pressed quit") From 799318c42fd460f20f390a3fca6bacb8a1b9670f Mon Sep 17 00:00:00 2001 From: Taha Momayiz Date: Fri, 18 Jul 2025 09:44:53 -0400 Subject: [PATCH 3/6] Update stdatalog_GUI.py - Add validation checks before accessing file paths - Prevents IndexError when users cancel file selection dialogs - Affects 5 GUI widget components: HSDLogControlWidget, ComponentWidget, DeviceTemplateLoadingWidget, CommandWidget, HSDPlotLinesWidget - Maintains existing functionality while adding robustness --- .../stdatalog/GUI/stdatalog_GUI.py | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/gui_applications/stdatalog/GUI/stdatalog_GUI.py b/gui_applications/stdatalog/GUI/stdatalog_GUI.py index ad4eb7d..9338fd5 100644 --- a/gui_applications/stdatalog/GUI/stdatalog_GUI.py +++ b/gui_applications/stdatalog/GUI/stdatalog_GUI.py @@ -17,6 +17,7 @@ import sys import os +import argparse # Add the STDatalog SDK root directory to the sys.path to access the SDK packages sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../..'))) @@ -26,13 +27,34 @@ from PySide6.QtWidgets import QApplication from PySide6 import QtCore +from PySide6.QtCore import QTimer from stdatalog_gui.HSD_GUI.HSD_MainWindow import HSD_MainWindow import stdatalog_core.HSD_utils.logger as logger log = logger.setup_applevel_logger(is_debug = False) +def load_device_config_after_delay(main_window, config_path): + """Load device config after a short delay to ensure GUI is fully initialized""" + def load_config(): + try: + if main_window.controller and hasattr(main_window.controller, 'load_config'): + print(f"Auto-loading device configuration from: {config_path}") + main_window.controller.load_config(config_path) + else: + print("Warning: Controller not ready, skipping auto-config load") + except Exception as e: + print(f"Error auto-loading device config: {e}") + + # Wait 2 seconds after GUI is shown to load config + QTimer.singleShot(2000, load_config) + def main(): + # Parse command line arguments + parser = argparse.ArgumentParser(description='STDatalog GUI Application') + parser.add_argument('config_file', nargs='?', help='Path to device configuration JSON file') + args = parser.parse_args() + QApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts) app = QApplication(sys.argv) @@ -43,6 +65,14 @@ def main(): mainWindow.setAppVersion("v1.0.0") mainWindow.setLogMsg("Device is logging --> Board Configuration has been disabled.\nNow you can label your acquisition using the [Tags Information] Component") mainWindow.showMaximized() + + # Auto-load device config if provided + if args.config_file: + if os.path.exists(args.config_file): + load_device_config_after_delay(mainWindow, args.config_file) + else: + print(f"Warning: Device config file not found: {args.config_file}") + app.setAttribute(QtCore.Qt.AA_Use96Dpi) app.exec() @@ -61,5 +91,3 @@ def main(): # stats.print_stats() main() - - From 209bf1e91bede9b126794c34683cfa59d02da716 Mon Sep 17 00:00:00 2001 From: Taha Momayiz Date: Fri, 18 Jul 2025 10:21:22 -0400 Subject: [PATCH 4/6] Update Readme.md Updated the Readme.md file to correct all references from demo_gui.py to the actual filename stdatalog_data_segmentation_GUI.py. The changes include: --- gui_applications/assisted_segmentation/Readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gui_applications/assisted_segmentation/Readme.md b/gui_applications/assisted_segmentation/Readme.md index 07bfe79..4c3c2bd 100644 --- a/gui_applications/assisted_segmentation/Readme.md +++ b/gui_applications/assisted_segmentation/Readme.md @@ -6,11 +6,11 @@ This is a prototype GUI to show the performance of the Assisted Segmentation alg ## Running the Application -1. **Prepare the Data**: The application expects a CSV file named `data/A5Y7FI_data.csv` with columns `AccX_LSB`, `AccY_LSB`, and `AccZ_LSB`. Ensure this file is present and correctly formatted. Please modify the code in `demo_gui.py` to load other types of files. +1. **Prepare the Data**: The application expects a CSV file named `data/A5Y7FI_data.csv` with columns `AccX_LSB`, `AccY_LSB`, and `AccZ_LSB`. Ensure this file is present and correctly formatted. Please modify the code in `stdatalog_data_segmentation_GUI.py` to load other types of files. -2. **Launch the Application**: Navigate to the directory containing `demo_gui.py` and run the following command in your terminal: +2. **Launch the Application**: Navigate to the directory containing `stdatalog_data_segmentation_GUI.py` and run the following command in your terminal: -`python demo_gui.py` +`python stdatalog_data_segmentation_GUI.py` ## Using the Application @@ -32,4 +32,4 @@ To exit the application, simply close the main window or terminate the process i ## Notes - The application is a demonstration of interactive data segmentation and analysis. It is not optimized for large datasets or production use. -- For further customization or to use with different datasets, modifications to `demo_gui.py` and associated modules may be required. +- For further customization or to use with different datasets, modifications to `stdatalog_data_segmentation_GUI.py` and associated modules may be required. From 5a310af0439d80b0202cf5336ef83beb480fa4ea Mon Sep 17 00:00:00 2001 From: YumTaha Date: Tue, 22 Jul 2025 13:55:35 -0400 Subject: [PATCH 5/6] added CLI --- .../stdatalog/CLI/stdatalog_CLI.py | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 gui_applications/stdatalog/CLI/stdatalog_CLI.py diff --git a/gui_applications/stdatalog/CLI/stdatalog_CLI.py b/gui_applications/stdatalog/CLI/stdatalog_CLI.py new file mode 100644 index 0000000..cbc7d3e --- /dev/null +++ b/gui_applications/stdatalog/CLI/stdatalog_CLI.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +import os +import sys +import re +import asyncio +import time +import shutil +import glob +from datetime import datetime + +# ROOT: +PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) +# Go up as needed to reach your real project root, e.g. +PROJECT_ROOT = os.path.abspath(os.path.join(PROJECT_ROOT, "../../../..")) + +# ---- SETTINGS ---- +ACQ_TIME = 1 # seconds to log data +DELAY = 14 # seconds to wait +DEVICE_CONFIG_PATH = os.path.join(PROJECT_ROOT, "device_config.json") +OUTPUT_FOLDER = os.path.join(PROJECT_ROOT, "acquisition_data") +SOCKET_PORT = 8888 + +# Ensure the TUI and SDK are in the path +print("[STARTUP] Adding TUI path to sys.path...") +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'TUI'))) +print("[STARTUP] Importing stdatalog_TUI...") +from stdatalog_TUI import HSDInfo +print("[STARTUP] Importing HSDLink...") +from stdatalog_core.HSD_link.HSDLink import HSDLink +print("[STARTUP] All imports completed!") + +async def async_socket_listener(state): + server = await asyncio.start_server( + lambda r, w: handle_client(r, w, state), + '127.0.0.1', SOCKET_PORT) + addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets) + print(f'[IPC] Async IPC server running on {addrs}') + async with server: + await server.serve_forever() + +async def handle_client(reader, writer, state): + addr = writer.get_extra_info('peername') + print(f'[IPC] Connected by {addr}') + try: + while True: + data = await reader.read(32) + if not data: + print(f'[IPC] Disconnected by {addr}') + break + cmd = data.decode('utf-8').strip().lower() + if cmd == "start" and not state["running"]: + state["command"] = "start" + elif cmd == "stop" and state["running"]: + state["command"] = "stop" + elif cmd == "exit": + state["command"] = "exit" + break + except ConnectionResetError: + print(f"[IPC] Disconnected by {addr}") + finally: + writer.close() + await writer.wait_closed() + +def get_next_cut_number(parent_folder): + """Find the next cut_X number based on existing folders.""" + if not os.path.exists(parent_folder): + os.makedirs(parent_folder) + return 1 + cut_nums = [] + for d in os.listdir(parent_folder): + m = re.match(r'^cut_(\d+)$', d) + if m: + cut_nums.append(int(m.group(1))) + if cut_nums: + return max(cut_nums) + 1 + return 1 + +async def cut_logging_task(state): + # ---- Device/SDK Initialization ---- + print("Initializing STDatalog CLI...") + tui_flags = HSDInfo.TUIFlags( + output_folder=OUTPUT_FOLDER, + sub_datetime_folder=True, + acq_name="CLI_Log", + acq_desc="CLI periodic logging", + file_config=DEVICE_CONFIG_PATH, + ucf_file="", + ispu_out_fmt="", + time_sec=-1, + interactive_mode=False, + ) + + print("Creating HSDInfo instance...") + try: + # Run device detection in a thread to avoid blocking the async loop + def create_hsd_info(): + return HSDInfo(tui_flags) + + # Use asyncio to run the blocking call with a timeout + import concurrent.futures + with concurrent.futures.ThreadPoolExecutor() as executor: + future = executor.submit(create_hsd_info) + try: + hsd_info = await asyncio.get_event_loop().run_in_executor(None, lambda: future.result(timeout=30)) + print("HSDInfo created successfully!") + except concurrent.futures.TimeoutError: + print("ERROR: Device detection timeout after 30 seconds!") + print("Please check that your STMicroelectronics device is connected and try again.") + state["command"] = "exit" + return + except Exception as e: + print(f"ERROR: Failed to initialize device connection: {e}") + print("Please check that your STMicroelectronics device is connected and try again.") + state["command"] = "exit" + return + + if len(hsd_info.device_list) == 0: + print("No compatible devices connected! Please connect a device and try again.") + state["command"] = "exit" + return + + if len(hsd_info.device_list) > 1: + print(f"Multiple devices found ({len(hsd_info.device_list)}). Using the first one.") + + if hsd_info.selected_device_id is None: + hsd_info.selected_device_id = 0 + + pres_res = HSDLink.get_device_presentation_string(hsd_info.hsd_link, hsd_info.selected_device_id) + if pres_res is not None: + board_id = hex(pres_res["board_id"]) + fw_id = hex(pres_res["fw_id"]) + hsd_info.load_device_template(board_id, fw_id) + + hsd_info.update_fw_info() + hsd_info.update_acq_params() + hsd_info.upload_device_conf_file() + hsd_info.update_sensor_list() + hsd_info.init_sensor_data_counters() + hsd_info.update_ai_sensor_list() + hsd_info.update_tag_list() + hsd_info.init_tag_status_list() + hsd_info.set_rtc() + hsd_info.check_output_folder() + + print(f"Connected to device: {hsd_info.selected_fw_info}") + print(f"Active sensors: {len(hsd_info.sensor_list) if hsd_info.sensor_list else 0}") + print(f"Output folder: {hsd_info.output_acquisition_path}") + print(f"Acquisition cycle: {ACQ_TIME}s logging, {DELAY}s pause") + print(f"Waiting for external commands via IPC socket on port {SOCKET_PORT}...") + + cut_number = get_next_cut_number(OUTPUT_FOLDER) + + while True: + # Exit condition + if state["command"] == "exit": + print("Exiting program. Goodbye.") + break + + # Start new cut when commanded and not already running + if state["command"] == "start" and not state["running"]: + cut_folder = os.path.join(OUTPUT_FOLDER, f"cut_{cut_number}") + os.makedirs(cut_folder, exist_ok=True) + + print(f"Starting acquisition cycle {cut_number}") + print(f"Cut folder: {cut_folder}") + + # Reset output folder to main directory for logging + hsd_info.tui_flags.output_folder = OUTPUT_FOLDER + hsd_info.output_acquisition_path = OUTPUT_FOLDER + hsd_info.update_acq_params() + hsd_info.check_output_folder() + + print(f"Starting periodic logging: {ACQ_TIME}s log, {DELAY}s pause. Send 'stop' to end safely.") + state["running"] = True + state["command"] = None + + while state["running"]: + # Get list of existing folders before logging + existing_folders = set() + if os.path.exists(OUTPUT_FOLDER): + existing_folders = {f for f in os.listdir(OUTPUT_FOLDER) + if os.path.isdir(os.path.join(OUTPUT_FOLDER, f)) + and not f.startswith('cut_')} + + print(f"Starting log cycle...") + hsd_info.start_log() + await asyncio.sleep(ACQ_TIME) + hsd_info.stop_log() + print("Log cycle complete.") + + # Find new folders created during logging + if os.path.exists(OUTPUT_FOLDER): + current_folders = {f for f in os.listdir(OUTPUT_FOLDER) + if os.path.isdir(os.path.join(OUTPUT_FOLDER, f)) + and not f.startswith('cut_')} + + new_folders = current_folders - existing_folders + + # Move new folders to the cut directory + for folder_name in new_folders: + src_path = os.path.join(OUTPUT_FOLDER, folder_name) + dst_path = os.path.join(cut_folder, folder_name) + try: + print(f"Moving {folder_name} to {cut_folder}") + shutil.move(src_path, dst_path) + except Exception as e: + print(f"Error moving folder {folder_name}: {e}") + + print("Waiting for next cycle...") + # Wait for DELAY seconds or until a stop command + for _ in range(DELAY): + if state["command"] == "stop": + state["running"] = False + state["command"] = None + break + await asyncio.sleep(1) + + print("Stopping acquisition, cleaning up...") + if hsd_info.is_log_started: + hsd_info.stop_log() + print("Done. Waiting for new command, or send 'exit' to quit.") + + cut_number += 1 # increment for next cycle + + await asyncio.sleep(0.1) + +async def main(): + state = {"running": False, "command": None, "stop_requested": False} + await asyncio.gather( + async_socket_listener(state), + cut_logging_task(state) + ) + +if __name__ == "__main__": + print("🚀 STDatalog CLI Service Starting...") + print(f"Working directory: {os.getcwd()}") + print(f"Python path: {sys.path[:3]}...") # Show first 3 paths + try: + asyncio.run(main()) + except KeyboardInterrupt: + print("\nExiting by user request.") From 81fc1d06e476678649090f23ece728a50e36b4b2 Mon Sep 17 00:00:00 2001 From: YumTaha Date: Tue, 22 Jul 2025 13:57:30 -0400 Subject: [PATCH 6/6] fixed --- gui_applications/stdatalog/TUI/Views/tui_views.py | 2 +- gui_applications/stdatalog/TUI/stdatalog_TUI.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gui_applications/stdatalog/TUI/Views/tui_views.py b/gui_applications/stdatalog/TUI/Views/tui_views.py index 6a1e1f6..bf52ec7 100644 --- a/gui_applications/stdatalog/TUI/Views/tui_views.py +++ b/gui_applications/stdatalog/TUI/Views/tui_views.py @@ -558,4 +558,4 @@ def _on_load_page(self): @staticmethod def _quit(): - raise StopApplication("User pressed quit") + raise StopApplication("User pressed quit") \ No newline at end of file diff --git a/gui_applications/stdatalog/TUI/stdatalog_TUI.py b/gui_applications/stdatalog/TUI/stdatalog_TUI.py index d17c218..b73423b 100644 --- a/gui_applications/stdatalog/TUI/stdatalog_TUI.py +++ b/gui_applications/stdatalog/TUI/stdatalog_TUI.py @@ -254,7 +254,7 @@ def do_tag(self, t_id): HSDLink.set_sw_tag_on_off(self.hsd_link, self.selected_device_id, t_id, self.tag_status_list[t_id]) def start_log(self): - self.is_log_started = HSDLink.start_log(self.hsd_link, self.selected_device_id, self.tui_flags.sub_datetime_folder) + self.is_log_started = HSDLink.start_log(self.hsd_link, self.selected_device_id, sub_folder=self.tui_flags.sub_datetime_folder) self.threads_stop_flags = [] self.sensor_data_files = [] @@ -358,4 +358,4 @@ def demo(screen, scene, hsd_info): screen.play(scenes, stop_on_resize=True, start_scene=scene, allow_int=True) if __name__ == '__main__': - hsd_TUI() + hsd_TUI() \ No newline at end of file