From 0e793c599ed8d460b853ec7e62535e10c019e055 Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Thu, 20 Feb 2025 14:54:37 -0800 Subject: [PATCH 01/12] Add basic function/UI to show the stage info (ts, sn, local, global) --- parallax/stage_listener.py | 36 +++- parallax/stage_server_ipconfig.py | 2 +- ui/stage_info.ui | 327 ++++++++++++++++-------------- 3 files changed, 211 insertions(+), 154 deletions(-) diff --git a/parallax/stage_listener.py b/parallax/stage_listener.py index 1c6cc325..e97d5f7c 100644 --- a/parallax/stage_listener.py +++ b/parallax/stage_listener.py @@ -14,7 +14,7 @@ # Set logger name logger = logging.getLogger(__name__) -logger.setLevel(logging.WARNING) +logger.setLevel(logging.DEBUG) # Set the logging level for PyQt5.uic.uiparser/properties to WARNING, to ignore DEBUG messages logging.getLogger("PyQt5.uic.uiparser").setLevel(logging.WARNING) logging.getLogger("PyQt5.uic.properties").setLevel(logging.WARNING) @@ -68,6 +68,7 @@ def __init__(self, stage_info=None): self.stage_x_global = None self.stage_y_global = None self.stage_z_global = None + self.timestamp = None class Worker(QObject): @@ -221,6 +222,7 @@ def __init__(self, model, stage_ui, probeCalibrationLabel): self.stage_global_data = None self.transM_dict = {} self.scale_dict = {} + self.stage_ui.ui.snapshot_btn.clicked.connect(self._snapshot_stage) def start(self): """Start the stage listener.""" @@ -291,6 +293,7 @@ def handleDataChange(self, probe): local_coords_x = round(probe["Stage_X"] * 1000, 1) local_coords_y = round(probe["Stage_Y"] * 1000, 1) local_coords_z = 15000 - round(probe["Stage_Z"] * 1000, 1) + logger.debug(f"timestamp_local: {self.timestamp_local}, sn: {sn}") # update into model moving_stage = self.model.stages.get(sn) @@ -299,6 +302,7 @@ def handleDataChange(self, probe): moving_stage.stage_x = local_coords_x moving_stage.stage_y = local_coords_y moving_stage.stage_z = local_coords_z + moving_stage.timestamp = self.timestamp_local # Update to buffer self.append_to_buffer(self.timestamp_local, moving_stage) @@ -316,8 +320,9 @@ def handleDataChange(self, probe): self._updateGlobalDataTransformM(sn, moving_stage, transM, scale) else: logger.debug(f"Transformation matrix or scale not found for serial number: {sn}") - else: - logger.debug(f"Serial number {sn} not found in transformation or scale dictionary") + + # Update stage info into JSON + self._write_stage_info_to_json(moving_stage) def _updateGlobalDataTransformM(self, sn, moving_stage, transM, scale): """ @@ -450,7 +455,7 @@ def handleGlobalDataChange(self, sn, global_coords, ts_img_captured, cam0, pt0, stage_info["Stage_X"] = local_coords[0] stage_info["Stage_Y"] = local_coords[1] stage_info["Stage_Z"] = local_coords[2] - self.stage_global_data = Stage(stage_info) + self.stage_global_data = Stage(stage_info) #TODO : check this if local_coords is not None: self.sn = sn @@ -526,3 +531,26 @@ def set_low_freq_default(self, interval=1000): self.worker.curr_interval = self.worker._low_freq_interval self.worker.start(interval=self.worker._low_freq_interval) # print("low_freq: 1000 ms") + + def _write_stage_info_to_json(self, stage): + """Write stage info to JSON file. + + Args: + stage (Stage): Stage object. + """ + print("---------- _write_stage_info_to_json") + print(" sn: ", stage.sn) + print(" name: ", stage.name) + print(" timestamp: ", stage.timestamp) + print(" local coords: ", stage.stage_x, stage.stage_y, stage.stage_z) + print(" global coords: ", stage.stage_x_global, stage.stage_y_global, stage.stage_z_global) + + def _snapshot_stage(self): + sn = self.stage_ui.get_selected_stage_sn() + stage = self.model.stages.get(sn) + print("---------- _snapshot_stage") + print(" sn: ", stage.sn) + print(" name: ", stage.name) + print(" timestamp: ", stage.timestamp) + print(" local coords: ", stage.stage_x, stage.stage_y, stage.stage_z) + print(" global coords: ", stage.stage_x_global, stage.stage_y_global, stage.stage_z_global) diff --git a/parallax/stage_server_ipconfig.py b/parallax/stage_server_ipconfig.py index f142c205..7bcceaf6 100644 --- a/parallax/stage_server_ipconfig.py +++ b/parallax/stage_server_ipconfig.py @@ -15,7 +15,7 @@ from PyQt5.QtCore import Qt logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) +logger.setLevel(logging.WARNING) package_dir = os.path.dirname(os.path.abspath(__file__)) debug_dir = os.path.join(os.path.dirname(package_dir), "debug") diff --git a/ui/stage_info.ui b/ui/stage_info.ui index 79a8b1b9..6f23649b 100644 --- a/ui/stage_info.ui +++ b/ui/stage_info.ui @@ -45,8 +45,8 @@ 2 - - + + 0 @@ -59,15 +59,13 @@ 30 - - - Global coords - - + + SN + - - + + 0 @@ -81,72 +79,69 @@ - Z: + X: - - - - QLabel { - color: yellow; -} - - - - + + + + + 0 + 25 + - - - - - 25 + 16777215 25 - - background-color : yellow + + X: + + + + - + μm - - - - - 50 - 0 - + + + + μm - - - 50 - 16777215 - + + + + + + QLabel { + color: yellow; +} - μm + - - - - - - 16777215 - 16777215 - + + + + QLabel { + color: yellow; +} - - - + + 0 @@ -160,50 +155,65 @@ - X: + Y: - - + + 0 - 30 + 25 16777215 - 30 + 25 - SN + Z: - - + + 0 - 25 + 30 16777215 - 25 + 30 + + + 75 + true + + + + + Global coords + + + + + + - Y: + μm - - + + QLabel { color: yellow; @@ -214,53 +224,85 @@ - - + + + + + 16777215 + 16777215 + + - - - - - μm + + + + + 0 + 25 + + + + + 16777215 + 25 + - - - - - μm + Y: - - + + - 0 - 30 + 40 + 40 - 16777215 - 30 + 40 + 16777215 + + MPM Http Server + - Local coords + + + + + resources/mpmServer.pngresources/mpmServer.png + + + + 36 + 36 + - - - - QLabel { - color: yellow; -} + + + + + 40 + 16777215 + + + μm + + + + + - @@ -279,22 +321,8 @@ - - - - μm - - - - - - - - - - - - - + + μm @@ -313,8 +341,14 @@ - - + + + + + 40 + 0 + + 40 @@ -326,27 +360,40 @@ - - + + 0 - 25 + 30 16777215 - 25 + 30 + + + 75 + true + + - X: + Local coords - - + + + + - + + + + + 0 @@ -364,22 +411,29 @@ - - + + - 0 - 25 + 40 + 40 - 16777215 - 25 + 40 + 16777215 + + Stage Snapshot + - Y: + + + + + resources/check.pngresources/check.png @@ -397,37 +451,12 @@ 40 - - - - - - - 0 - 40 - - - - - 45 - 16777215 - - - - MPM Http Server - - - - - - - resources/mpmServer.pngresources/mpmServer.png - - - - 36 - 36 - + + + 50 + false + true + From e0aafdd1305dd28902aa404cb94dcdeb1e83c66e Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Fri, 21 Feb 2025 08:44:13 -0800 Subject: [PATCH 02/12] Write the stage info to json file. This is for communicating with Pinpoint --- parallax/stage_listener.py | 40 +++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/parallax/stage_listener.py b/parallax/stage_listener.py index e97d5f7c..98ffbfe1 100644 --- a/parallax/stage_listener.py +++ b/parallax/stage_listener.py @@ -3,6 +3,8 @@ applications, using PyQt5 for threading and signals, and requests for HTTP requests. """ +import os +import json import logging import time from collections import deque @@ -15,10 +17,11 @@ # Set logger name logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) -# Set the logging level for PyQt5.uic.uiparser/properties to WARNING, to ignore DEBUG messages -logging.getLogger("PyQt5.uic.uiparser").setLevel(logging.WARNING) -logging.getLogger("PyQt5.uic.properties").setLevel(logging.WARNING) +# Directory and file path for storing stage coordinates +package_dir = os.path.dirname(os.path.abspath(__file__)) +data_dir = os.path.join(os.path.dirname(package_dir), "data", "stage_coords") +os.makedirs(data_dir, exist_ok=True) # Ensure the directory exists class StageInfo(QObject): """Retrieve and manage information about the stages.""" @@ -222,6 +225,9 @@ def __init__(self, model, stage_ui, probeCalibrationLabel): self.stage_global_data = None self.transM_dict = {} self.scale_dict = {} + self.file = None + + # Connect the snapshot button self.stage_ui.ui.snapshot_btn.clicked.connect(self._snapshot_stage) def start(self): @@ -538,12 +544,28 @@ def _write_stage_info_to_json(self, stage): Args: stage (Stage): Stage object. """ - print("---------- _write_stage_info_to_json") - print(" sn: ", stage.sn) - print(" name: ", stage.name) - print(" timestamp: ", stage.timestamp) - print(" local coords: ", stage.stage_x, stage.stage_y, stage.stage_z) - print(" global coords: ", stage.stage_x_global, stage.stage_y_global, stage.stage_z_global) + file_path = os.path.join(data_dir, f"{stage.sn}.json") # Store the file in the correct location + + # Create the stage data dictionary + stage_data = { + "sn": stage.sn, + "name": stage.name, + "timestamp": stage.timestamp, + "local_coords": { + "x": stage.stage_x, + "y": stage.stage_y, + "z": stage.stage_z + }, + "global_coords": { + "x": stage.stage_x_global, + "y": stage.stage_y_global, + "z": stage.stage_z_global + } + } + + # Write the stage data to the JSON file + with open(file_path, "w", encoding="utf-8") as f: + json.dump(stage_data, f, indent=4) def _snapshot_stage(self): sn = self.stage_ui.get_selected_stage_sn() From 91aa7c34fe94618a27cf932f14ff2e6c35d53f9a Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Fri, 21 Feb 2025 10:53:41 -0800 Subject: [PATCH 03/12] Export stage coordinates --- parallax/stage_listener.py | 86 +++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/parallax/stage_listener.py b/parallax/stage_listener.py index 98ffbfe1..fc5e67e2 100644 --- a/parallax/stage_listener.py +++ b/parallax/stage_listener.py @@ -13,6 +13,7 @@ import numpy as np import requests from PyQt5.QtCore import QObject, QThread, QTimer, pyqtSignal +from PyQt5.QtWidgets import QFileDialog # Set logger name logger = logging.getLogger(__name__) @@ -225,7 +226,7 @@ def __init__(self, model, stage_ui, probeCalibrationLabel): self.stage_global_data = None self.transM_dict = {} self.scale_dict = {} - self.file = None + self.snapshot_folder_path = None # Connect the snapshot button self.stage_ui.ui.snapshot_btn.clicked.connect(self._snapshot_stage) @@ -538,15 +539,22 @@ def set_low_freq_default(self, interval=1000): self.worker.start(interval=self.worker._low_freq_interval) # print("low_freq: 1000 ms") - def _write_stage_info_to_json(self, stage): - """Write stage info to JSON file. + def _get_stage_info_json(self, stage): + """Create a JSON file for the stage info. Args: stage (Stage): Stage object. """ - file_path = os.path.join(data_dir, f"{stage.sn}.json") # Store the file in the correct location + stage_data = None + + if stage is None: + logger.error("Error: Stage object is None. Cannot save JSON.") + return + + if not hasattr(stage, 'sn') or not stage.sn: + logger.error("Error: Invalid stage serial number (sn). Cannot save JSON.") + return - # Create the stage data dictionary stage_data = { "sn": stage.sn, "name": stage.name, @@ -563,16 +571,72 @@ def _write_stage_info_to_json(self, stage): } } + return stage_data + + def _write_stage_info_to_json(self, stage): + """Write stage info to JSON file. + + Args: + stage (Stage): Stage object. + """ + file_path = os.path.join(data_dir, f"{stage.sn}.json") # Store the file in the correct location + + # Create the stage data dictionary + stage_data = self._get_stage_info_json(stage) + if stage_data is None: + return + # Write the stage data to the JSON file with open(file_path, "w", encoding="utf-8") as f: json.dump(stage_data, f, indent=4) + def _save_into_file(self, sn, stage_data): + """Save stage info into file. + + Args: + sn (str): Serial number of the stage. + stage (Stage): Stage object. + """ + + # If no folder is set, default to the "Documents" directory + if self.snapshot_folder_path is None: + self.snapshot_folder_path = os.path.join(os.path.expanduser("~"), "Documents") + + + # Open save file dialog, defaulting to the last used folder + file_path, _ = QFileDialog.getSaveFileName( + None, + "Save Stage Info", + os.path.join(self.snapshot_folder_path, f"{sn}.json"), # Default to last used folder + "JSON Files (*.json)" + ) + + if not file_path: # User canceled the dialog + print("Save canceled by user.") + return + + # Update `snapshot_folder_path` to the selected folder + self.snapshot_folder_path = os.path.dirname(file_path) + + # Ensure the file has the correct `.json` extension + if not file_path.endswith(".json"): + file_path += ".json" + + # Write the JSON file + try: + with open(file_path, "w", encoding="utf-8") as f: + json.dump(stage_data, f, indent=4) + print(f"Stage info saved at {file_path}") + except Exception as e: + print(f"Error saving stage info: {e}") + def _snapshot_stage(self): + """Snapshot the current stage info. Handler for the stage snapshot button.""" sn = self.stage_ui.get_selected_stage_sn() stage = self.model.stages.get(sn) - print("---------- _snapshot_stage") - print(" sn: ", stage.sn) - print(" name: ", stage.name) - print(" timestamp: ", stage.timestamp) - print(" local coords: ", stage.stage_x, stage.stage_y, stage.stage_z) - print(" global coords: ", stage.stage_x_global, stage.stage_y_global, stage.stage_z_global) + + stage_data = self._get_stage_info_json(stage) + if stage_data is None: + return + + self._save_into_file(sn, stage_data) \ No newline at end of file From 5fe7cd1f95002ccf40966464a48a44e1c5451b0e Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Fri, 21 Feb 2025 11:19:47 -0800 Subject: [PATCH 04/12] Remove comments --- parallax/stage_listener.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parallax/stage_listener.py b/parallax/stage_listener.py index fc5e67e2..96762ae5 100644 --- a/parallax/stage_listener.py +++ b/parallax/stage_listener.py @@ -462,7 +462,7 @@ def handleGlobalDataChange(self, sn, global_coords, ts_img_captured, cam0, pt0, stage_info["Stage_X"] = local_coords[0] stage_info["Stage_Y"] = local_coords[1] stage_info["Stage_Z"] = local_coords[2] - self.stage_global_data = Stage(stage_info) #TODO : check this + self.stage_global_data = Stage(stage_info) if local_coords is not None: self.sn = sn From faadad9b309c5eaf2cf21707da47fbdba5a662cc Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Fri, 21 Feb 2025 13:55:14 -0800 Subject: [PATCH 05/12] Change the release version --- parallax/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parallax/__init__.py b/parallax/__init__.py index 61b442a8..73b61e42 100644 --- a/parallax/__init__.py +++ b/parallax/__init__.py @@ -4,7 +4,7 @@ import os -__version__ = "1.2.0" +__version__ = "1.2.2" # allow multiple OpenMP instances os.environ["KMP_DUPLICATE_LIB_OK"] = "True" From f576657bded202a1b8c55578196ef51f503f8f8d Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Fri, 21 Feb 2025 14:16:55 -0800 Subject: [PATCH 06/12] Update UI for saving stage info --- ui/resources/save.png | Bin 0 -> 905 bytes ui/stage_info.ui | 12 +++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 ui/resources/save.png diff --git a/ui/resources/save.png b/ui/resources/save.png new file mode 100644 index 0000000000000000000000000000000000000000..b5b21457684312c63147a7e33b8f5899d55d44b1 GIT binary patch literal 905 zcmV;419tq0P)MdVo&;|gpx{sk};v65BbF?M${4&LZ;zjx7y%ss$y{*S;qFexI}!zyqE zr~zXUomv8piO51k`$SRIGr%MKyWt(+vWV1!%2!l%7=d&{^uz*$TGzFXQ`p{hN=SKv4B3;5wVMtq8`8ZJ2o)Kt|gB+w8b zNV&9d`?n2YV^Ng=5gee$va-Pb)IyR-t5slnqkC=NR4Yrjy0_1AqROq`8WdxK0#A!@l%e~mjH(=0ke$nSQ+ei9MDysS!*l$s0 z9ylT*KjT)~N>p_ScuTySxe%w&0Ps>(AJABeTIb*7s7`wpOk55C6Ktbf#w|F!adbRo z8cf@Pv?&Eh+kvzx1xVY0w26`i?*g}gH5&Qe&GXF*BS|ijY-j3y4!D8gMu%Pe6>!P) z?`p=GQq{e{l^}z#F7UpC8}Bk_IvFOwS_>V?Bw+&NVFA?GhA;s-J^Vu{LIfBo{5+tH zfV3TG1IBTz6d=UlPiuY29$^9$J`LucQUD5&>P!?N0z#h!azD_SC;g^#`+V$IfDm<2>c=Ami#xQnWe0vBj(pU)GY ffN5Z+c^mCt4Wh$!1%RFH00000NkvXXu0mjf5L1p0 literal 0 HcmV?d00001 diff --git a/ui/stage_info.ui b/ui/stage_info.ui index 6f23649b..46a54c7f 100644 --- a/ui/stage_info.ui +++ b/ui/stage_info.ui @@ -282,8 +282,8 @@ - 36 - 36 + 30 + 30 @@ -433,7 +433,13 @@ - resources/check.pngresources/check.png + resources/save.pngresources/save.png + + + + 24 + 24 + From 5f561a2c069625e4d25996869229d9658355c8e6 Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Fri, 21 Feb 2025 14:18:12 -0800 Subject: [PATCH 07/12] Update the release version --- parallax/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parallax/__init__.py b/parallax/__init__.py index 73b61e42..0a2b8523 100644 --- a/parallax/__init__.py +++ b/parallax/__init__.py @@ -4,7 +4,7 @@ import os -__version__ = "1.2.2" +__version__ = "1.3.0" # allow multiple OpenMP instances os.environ["KMP_DUPLICATE_LIB_OK"] = "True" From e0204282f88a8814e691f389d133232a5c586388 Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Fri, 21 Feb 2025 15:12:39 -0800 Subject: [PATCH 08/12] linter --- parallax/stage_listener.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parallax/stage_listener.py b/parallax/stage_listener.py index 96762ae5..73a09625 100644 --- a/parallax/stage_listener.py +++ b/parallax/stage_listener.py @@ -24,6 +24,7 @@ data_dir = os.path.join(os.path.dirname(package_dir), "data", "stage_coords") os.makedirs(data_dir, exist_ok=True) # Ensure the directory exists + class StageInfo(QObject): """Retrieve and manage information about the stages.""" @@ -602,7 +603,6 @@ def _save_into_file(self, sn, stage_data): if self.snapshot_folder_path is None: self.snapshot_folder_path = os.path.join(os.path.expanduser("~"), "Documents") - # Open save file dialog, defaulting to the last used folder file_path, _ = QFileDialog.getSaveFileName( None, @@ -639,4 +639,4 @@ def _snapshot_stage(self): if stage_data is None: return - self._save_into_file(sn, stage_data) \ No newline at end of file + self._save_into_file(sn, stage_data) From c6eab2b869a00ac67f0c31f38d925321e8f31496 Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Mon, 3 Mar 2025 15:03:55 -0800 Subject: [PATCH 09/12] Export stages in one json files. Follow AIND file format standards for timestamp --- parallax/stage_listener.py | 106 ++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 59 deletions(-) diff --git a/parallax/stage_listener.py b/parallax/stage_listener.py index 73a09625..42b0f9f3 100644 --- a/parallax/stage_listener.py +++ b/parallax/stage_listener.py @@ -8,7 +8,7 @@ import logging import time from collections import deque -from datetime import datetime +from datetime import datetime, timezone import numpy as np import requests @@ -21,7 +21,8 @@ # Directory and file path for storing stage coordinates package_dir = os.path.dirname(os.path.abspath(__file__)) -data_dir = os.path.join(os.path.dirname(package_dir), "data", "stage_coords") +#data_dir = os.path.join(os.path.dirname(package_dir), "data", "stage_coords") +data_dir = os.path.join(os.path.expanduser("~"), "Documents", "Parallax_Stages") os.makedirs(data_dir, exist_ok=True) # Ensure the directory exists @@ -73,7 +74,10 @@ def __init__(self, stage_info=None): self.stage_x_global = None self.stage_y_global = None self.stage_z_global = None - self.timestamp = None + self.yaw = None + self.pitch = None + self.roll = None + # self.timestamp = None class Worker(QObject): @@ -143,7 +147,10 @@ def fetchData(self): selected_probe = data["SelectedProbe"] probe = data["ProbeArray"][selected_probe] - if self.last_stage_info is None: # Initial + # At init, update stage info for connected stages + if self.last_stage_info is None: # Initial setup + for stage in data["ProbeArray"]: + self.dataChanged.emit(stage) self.last_stage_info = probe self.last_bigmove_stage_info = probe self.dataChanged.emit(probe) @@ -228,6 +235,7 @@ def __init__(self, model, stage_ui, probeCalibrationLabel): self.transM_dict = {} self.scale_dict = {} self.snapshot_folder_path = None + self.stages_info = {} # Connect the snapshot button self.stage_ui.ui.snapshot_btn.clicked.connect(self._snapshot_stage) @@ -242,10 +250,8 @@ def update_url(self): """Update the URL for the worker.""" # Update URL self.worker.update_url(self.model.stage_listener_url) - # Restart worker - # If there is an timeout error, stop the worker. - def get_last_moved_time(self, millisecond=False): + def get_timestamp(self, millisecond=False): """Get the last moved time of the stage. Args: @@ -294,7 +300,7 @@ def handleDataChange(self, probe): probe (dict): Probe data. """ # Format the current timestamp - self.timestamp_local = self.get_last_moved_time(millisecond=True) + self.timestamp_local = self.get_timestamp(millisecond=True) # id = probe["Id"] sn = probe["SerialNumber"] @@ -310,7 +316,7 @@ def handleDataChange(self, probe): moving_stage.stage_x = local_coords_x moving_stage.stage_y = local_coords_y moving_stage.stage_z = local_coords_z - moving_stage.timestamp = self.timestamp_local + # moving_stage.timestamp = self.timestamp_local # Update to buffer self.append_to_buffer(self.timestamp_local, moving_stage) @@ -329,8 +335,19 @@ def handleDataChange(self, probe): else: logger.debug(f"Transformation matrix or scale not found for serial number: {sn}") - # Update stage info into JSON - self._write_stage_info_to_json(moving_stage) + # Update stage info + self._update_stages_info(moving_stage) + + def _update_stages_info(self, stage): + """Update stage info. + + Args: + stage (Stage): Stage object. + """ + if stage is None: + return + + self.stages_info[stage.sn] = self._get_stage_info_json(stage) def _updateGlobalDataTransformM(self, sn, moving_stage, transM, scale): """ @@ -559,55 +576,37 @@ def _get_stage_info_json(self, stage): stage_data = { "sn": stage.sn, "name": stage.name, - "timestamp": stage.timestamp, - "local_coords": { - "x": stage.stage_x, - "y": stage.stage_y, - "z": stage.stage_z - }, - "global_coords": { - "x": stage.stage_x_global, - "y": stage.stage_y_global, - "z": stage.stage_z_global - } + # "timestamp": stage.timestamp, + "Stage_X": stage.stage_x, + "Stage_Y": stage.stage_y, + "Stage_Z": stage.stage_z, + "global_X": stage.stage_x_global, + "global_Y": stage.stage_y_global, + "global_Z": stage.stage_z_global, + "yaw": stage.yaw, + "pitch": stage.pitch, + "roll": stage.roll } return stage_data - def _write_stage_info_to_json(self, stage): - """Write stage info to JSON file. - - Args: - stage (Stage): Stage object. - """ - file_path = os.path.join(data_dir, f"{stage.sn}.json") # Store the file in the correct location - - # Create the stage data dictionary - stage_data = self._get_stage_info_json(stage) - if stage_data is None: - return - - # Write the stage data to the JSON file - with open(file_path, "w", encoding="utf-8") as f: - json.dump(stage_data, f, indent=4) - - def _save_into_file(self, sn, stage_data): - """Save stage info into file. - - Args: - sn (str): Serial number of the stage. - stage (Stage): Stage object. - """ + def _snapshot_stage(self): + """Snapshot the current stage info. Handler for the stage snapshot button.""" + selected_sn = self.stage_ui.get_selected_stage_sn() + now = datetime.now().astimezone() + info = {"timestamp": now.isoformat(timespec='milliseconds'), + "selected_sn": selected_sn, "probes:": self.stages_info} # If no folder is set, default to the "Documents" directory if self.snapshot_folder_path is None: self.snapshot_folder_path = os.path.join(os.path.expanduser("~"), "Documents") # Open save file dialog, defaulting to the last used folder + now_fmt = now.strftime("%Y-%m-%dT%H%M%S%z") file_path, _ = QFileDialog.getSaveFileName( None, "Save Stage Info", - os.path.join(self.snapshot_folder_path, f"{sn}.json"), # Default to last used folder + os.path.join(self.snapshot_folder_path, f"{now_fmt}.json"), "JSON Files (*.json)" ) @@ -625,18 +624,7 @@ def _save_into_file(self, sn, stage_data): # Write the JSON file try: with open(file_path, "w", encoding="utf-8") as f: - json.dump(stage_data, f, indent=4) + json.dump(info, f, indent=4) print(f"Stage info saved at {file_path}") except Exception as e: print(f"Error saving stage info: {e}") - - def _snapshot_stage(self): - """Snapshot the current stage info. Handler for the stage snapshot button.""" - sn = self.stage_ui.get_selected_stage_sn() - stage = self.model.stages.get(sn) - - stage_data = self._get_stage_info_json(stage) - if stage_data is None: - return - - self._save_into_file(sn, stage_data) From 5115065e30190af146b59a333b399ed80129597c Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Mon, 3 Mar 2025 15:20:32 -0800 Subject: [PATCH 10/12] remove unused codes --- parallax/stage_listener.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/parallax/stage_listener.py b/parallax/stage_listener.py index 42b0f9f3..ff7da7f2 100644 --- a/parallax/stage_listener.py +++ b/parallax/stage_listener.py @@ -8,7 +8,7 @@ import logging import time from collections import deque -from datetime import datetime, timezone +from datetime import datetime import numpy as np import requests @@ -18,13 +18,7 @@ # Set logger name logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) - -# Directory and file path for storing stage coordinates package_dir = os.path.dirname(os.path.abspath(__file__)) -#data_dir = os.path.join(os.path.dirname(package_dir), "data", "stage_coords") -data_dir = os.path.join(os.path.expanduser("~"), "Documents", "Parallax_Stages") -os.makedirs(data_dir, exist_ok=True) # Ensure the directory exists - class StageInfo(QObject): """Retrieve and manage information about the stages.""" @@ -77,7 +71,6 @@ def __init__(self, stage_info=None): self.yaw = None self.pitch = None self.roll = None - # self.timestamp = None class Worker(QObject): @@ -316,7 +309,6 @@ def handleDataChange(self, probe): moving_stage.stage_x = local_coords_x moving_stage.stage_y = local_coords_y moving_stage.stage_z = local_coords_z - # moving_stage.timestamp = self.timestamp_local # Update to buffer self.append_to_buffer(self.timestamp_local, moving_stage) @@ -576,7 +568,6 @@ def _get_stage_info_json(self, stage): stage_data = { "sn": stage.sn, "name": stage.name, - # "timestamp": stage.timestamp, "Stage_X": stage.stage_x, "Stage_Y": stage.stage_y, "Stage_Z": stage.stage_z, From 89d3d2f7f7aeebae3ba0cc0b46f0a214d2ef3f30 Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Mon, 3 Mar 2025 15:24:28 -0800 Subject: [PATCH 11/12] Follow PEP8 --- parallax/stage_listener.py | 1 + 1 file changed, 1 insertion(+) diff --git a/parallax/stage_listener.py b/parallax/stage_listener.py index ff7da7f2..67f19ce4 100644 --- a/parallax/stage_listener.py +++ b/parallax/stage_listener.py @@ -20,6 +20,7 @@ logger.setLevel(logging.DEBUG) package_dir = os.path.dirname(os.path.abspath(__file__)) + class StageInfo(QObject): """Retrieve and manage information about the stages.""" From 7900627c2f8476c03e3f7fea37b9b18d06149216 Mon Sep 17 00:00:00 2001 From: hannalee2 Date: Mon, 3 Mar 2025 15:58:32 -0800 Subject: [PATCH 12/12] x,y,z coords are selectable --- ui/stage_info.ui | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ui/stage_info.ui b/ui/stage_info.ui index 46a54c7f..ce4c66fb 100644 --- a/ui/stage_info.ui +++ b/ui/stage_info.ui @@ -126,6 +126,9 @@ - + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + @@ -138,6 +141,9 @@ - + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + @@ -222,6 +228,9 @@ - + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + @@ -235,6 +244,9 @@ - + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + @@ -306,6 +318,9 @@ - + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + @@ -390,6 +405,9 @@ - + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse +