diff --git a/examples/video_mode/example_video_mode_opx.py b/examples/video_mode/example_video_mode_opx.py index 2592a70..b1a7491 100644 --- a/examples/video_mode/example_video_mode_opx.py +++ b/examples/video_mode/example_video_mode_opx.py @@ -87,7 +87,7 @@ # --- QMM Connection --- # Replace with your actual OPX host and cluster name # Example: qmm = QuantumMachinesManager(host="your_opx_ip", cluster_name="your_cluster") -qmm = QuantumMachinesManager(host="`127.0.0.1", cluster_name="CS_1") +qmm = QuantumMachinesManager(host="172.16.33.101", cluster_name="CS_1") # Generate the QUA configuration from the QUAM machine object config = machine.generate_config() @@ -160,7 +160,7 @@ # Run the Dash server. # `host="0.0.0.0"` makes it accessible on your network. # `use_reloader=False` is often recommended for stability with background threads. -app.run(debug=True, host="0.0.0.0", port=8050, use_reloader=False) +app.run(debug=True, host="127.0.0.1", port=8050, use_reloader=False) # %% --- Debugging Sections (Optional) --- diff --git a/src/qua_dashboards/core/base_updatable_component.py b/src/qua_dashboards/core/base_updatable_component.py index 5c17c9f..47e9f19 100644 --- a/src/qua_dashboards/core/base_updatable_component.py +++ b/src/qua_dashboards/core/base_updatable_component.py @@ -40,7 +40,7 @@ def __init__(self, *args: Any, component_id: str, **kwargs: Any) -> None: # Pass through component_id and any other args/kwargs to BaseComponent super().__init__(component_id=component_id, *args, **kwargs) # type: ignore - def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFlags: + def update_parameters(self, parameters: Dict[str, Dict[str, Any]]): """ Update the component's attributes based on the input values from the UI. @@ -59,7 +59,7 @@ def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFl and might require further action (e.g., re-generating a program). Defaults to ModifiedFlags.NONE. """ - return ModifiedFlags.NONE + return None def get_dash_components(self, include_subcomponents: bool = True) -> List[html.Div]: """ diff --git a/src/qua_dashboards/video_mode/data_acquirers/base_data_acquirer.py b/src/qua_dashboards/video_mode/data_acquirers/base_data_acquirer.py index cb2b8de..9c234ce 100644 --- a/src/qua_dashboards/video_mode/data_acquirers/base_data_acquirer.py +++ b/src/qua_dashboards/video_mode/data_acquirers/base_data_acquirer.py @@ -220,8 +220,7 @@ def get_latest_data(self) -> Dict[str, Any]: "status": self._acquisition_status, } - def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFlags: - flags = super().update_parameters(parameters) # From BaseUpdatableComponent + def update_parameters(self, parameters: Dict[str, Dict[str, Any]]): if self.component_id in parameters: params = parameters[self.component_id] @@ -232,7 +231,6 @@ def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFl new_interval = float(params["acquisition_interval_s"]) if new_interval > 0: self.acquisition_interval_s = new_interval - flags |= ModifiedFlags.PARAMETERS_MODIFIED else: logger.warning( f"Invalid acquisition_interval_s: {new_interval}. Not updated." @@ -245,7 +243,6 @@ def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFl new_averages = int(params["num_software_averages"]) if new_averages >= 1: self.num_software_averages = new_averages - flags |= ModifiedFlags.PARAMETERS_MODIFIED with self._data_lock: # Clear history as averaging window changed self._data_history_raw.clear() self._latest_processed_data = None @@ -259,9 +256,9 @@ def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFl continue if component.component_id in parameters: - flags |= component.update_parameters(parameters) + component.update_parameters(parameters) + - return flags def get_dash_components(self, include_subcomponents: bool = True) -> List[Any]: components = super().get_dash_components(include_subcomponents) diff --git a/src/qua_dashboards/video_mode/data_acquirers/opx_data_acquirer.py b/src/qua_dashboards/video_mode/data_acquirers/opx_data_acquirer.py index 611cb5e..aab9694 100644 --- a/src/qua_dashboards/video_mode/data_acquirers/opx_data_acquirer.py +++ b/src/qua_dashboards/video_mode/data_acquirers/opx_data_acquirer.py @@ -20,7 +20,6 @@ from qua_dashboards.video_mode.data_acquirers.base_2d_data_acquirer import ( Base2DDataAcquirer, ) -from qua_dashboards.core import ModifiedFlags from qua_dashboards.video_mode.sweep_axis import SweepAxis from qua_dashboards.video_mode.scan_modes import ScanMode @@ -104,13 +103,15 @@ def __init__( self.initial_delay_s: Optional[float] = initial_delay_s self.qua_program: Optional[Program] = None self.qm_job: Optional[RunningQmJob] = None - self._compilation_flags: ModifiedFlags = ModifiedFlags.NONE self.result_type: str = result_type self._raw_qua_results: Dict[str, np.ndarray] = {} self.stream_vars: List[str] = stream_vars or self.stream_vars_default self.result_types: List[str] = self.result_types_default + #Initial parameters upon instantialisation of the data_acquirer + self.current_parameters = self._program_parameters() + def generate_qua_program(self) -> Program: """ Generates the QUA program for the 2D scan. @@ -183,12 +184,26 @@ def initialize_qm(self): if self.qm is None: self.qm = self.qmm.open_qm(self.qua_config) # type: ignore + + def _program_parameters(self): + inner_loop = self.qua_inner_loop_action + + params = ( + self.x_axis.span, self.x_axis.points, + self.y_axis.span, self.y_axis.points, + # self.scan_mode.__name__, + tuple(self.stream_vars), self.result_type, self.initial_delay_s, + inner_loop.readout_pulse.channel.intermediate_frequency, inner_loop.readout_pulse.length, inner_loop.readout_pulse.amplitude, + self.num_software_averages, self.acquisition_interval_s + ) + return params + def execute_program(self, validate_running: bool = True): - if self.qua_program is None: - logger.info(f"Generating QUA program for {self.component_id}.") - self.generate_qua_program() + self.qua_program = None + logger.info(f"Generating QUA program for {self.component_id}.") + self.generate_qua_program() logger.info(f"Executing QUA program for {self.component_id}.") self.qm_job = self.qm.execute(self.qua_program) # type: ignore @@ -202,6 +217,8 @@ def execute_program(self, validate_running: bool = True): f"QM job for {self.component_id} failed to start or produce initial values: {e}" ) raise + #Update the internal programme parameters, if anything has changed. Only done after a successful execution + self.current_parameters = self._program_parameters() def _process_fetched_results(self, fetched_qua_results: Tuple) -> np.ndarray: """ @@ -252,15 +269,8 @@ def _process_fetched_results(self, fetched_qua_results: Tuple) -> np.ndarray: return output_data_2d def perform_actual_acquisition(self) -> np.ndarray: - if self._compilation_flags & ModifiedFlags.CONFIG_MODIFIED: - logger.info(f"Config regeneration triggered for {self.component_id}.") + if self.current_parameters != self._program_parameters(): self._regenerate_config_and_reopen_qm() - self._compilation_flags = ModifiedFlags.NONE - elif self._compilation_flags & ModifiedFlags.PROGRAM_MODIFIED: - logger.info(f"Program recompile triggered for {self.component_id}.") - self.qua_program = None # Clear the program to force a re-generation - self.execute_program() - self._compilation_flags = ModifiedFlags.NONE if self.qm_job is None or self.qm_job.status != "running": logger.warning( @@ -293,20 +303,25 @@ def perform_actual_acquisition(self) -> np.ndarray: def _regenerate_config_and_reopen_qm(self) -> None: logger.info(f"Regenerating QUA config for {self.component_id} from machine.") self.qua_config = self.machine.generate_config() + + try: + if self.qm_job and self.qm_job.status == "running": + self.qm_job.halt() + except Exception: + logger.exception("Error halting job before reopen") + + self.qm_job = None + self.qm = None self.initialize_qm() self.execute_program() - def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFlags: - flags = super().update_parameters(parameters) - + def update_parameters(self, parameters: Dict[str, Dict[str, Any]]): + super().update_parameters(parameters) if self.component_id in parameters: params = parameters[self.component_id] if "result-type" in params and self.result_type != params["result-type"]: self.result_type = params["result-type"] - flags |= ModifiedFlags.PARAMETERS_MODIFIED - self._compilation_flags = flags - return flags def get_dash_components(self, include_subcomponents: bool = True) -> List[Any]: components = super().get_dash_components(include_subcomponents) diff --git a/src/qua_dashboards/video_mode/inner_loop_actions/basic_inner_loop_action.py b/src/qua_dashboards/video_mode/inner_loop_actions/basic_inner_loop_action.py index 74d88d2..22d8482 100644 --- a/src/qua_dashboards/video_mode/inner_loop_actions/basic_inner_loop_action.py +++ b/src/qua_dashboards/video_mode/inner_loop_actions/basic_inner_loop_action.py @@ -1,4 +1,3 @@ -from qua_dashboards.core.base_updatable_component import ModifiedFlags from qua_dashboards.utils.dash_utils import create_input_field from dash import html from qm.qua.lib import Cast, Math @@ -187,14 +186,13 @@ def get_dash_components(self, include_subcomponents: bool = True) -> List[html.D return components - def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFlags: + def update_parameters(self, parameters: Dict[str, Dict[str, Any]]): """Update the data acquirer's attributes based on the input values.""" if self.component_id not in parameters: - return ModifiedFlags.NONE - + return None + params = parameters[self.component_id] - flags = ModifiedFlags.NONE if ( self.readout_pulse.channel.intermediate_frequency != params["readout_frequency"] @@ -202,27 +200,14 @@ def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFl self.readout_pulse.channel.intermediate_frequency = params[ "readout_frequency" ] - flags |= ModifiedFlags.PARAMETERS_MODIFIED - flags |= ModifiedFlags.PROGRAM_MODIFIED - flags |= ModifiedFlags.CONFIG_MODIFIED if self.readout_pulse.length != params["readout_duration"]: self.readout_pulse.length = params["readout_duration"] - flags |= ModifiedFlags.PARAMETERS_MODIFIED - flags |= ModifiedFlags.PROGRAM_MODIFIED - flags |= ModifiedFlags.CONFIG_MODIFIED if self.use_dBm: if unit.volts2dBm(self.readout_pulse.amplitude) != params["readout_power"]: self.readout_pulse.amplitude = unit.dBm2volts(params["readout_power"]) - flags |= ModifiedFlags.PARAMETERS_MODIFIED - flags |= ModifiedFlags.PROGRAM_MODIFIED - flags |= ModifiedFlags.CONFIG_MODIFIED else: if self.readout_pulse.amplitude != params["readout_amplitude"]: self.readout_pulse.amplitude = params["readout_amplitude"] - flags |= ModifiedFlags.PARAMETERS_MODIFIED - flags |= ModifiedFlags.PROGRAM_MODIFIED - flags |= ModifiedFlags.CONFIG_MODIFIED - return flags diff --git a/src/qua_dashboards/video_mode/sweep_axis.py b/src/qua_dashboards/video_mode/sweep_axis.py index 25466c3..1d51094 100644 --- a/src/qua_dashboards/video_mode/sweep_axis.py +++ b/src/qua_dashboards/video_mode/sweep_axis.py @@ -5,7 +5,7 @@ import numpy as np import dash_bootstrap_components as dbc -from qua_dashboards.core import BaseUpdatableComponent, ModifiedFlags +from qua_dashboards.core import BaseUpdatableComponent from qua_dashboards.utils.basic_parameter import BasicParameter from qua_dashboards.utils.dash_utils import create_input_field @@ -126,21 +126,17 @@ def create_axis_layout( className="mb-3", ) - def update_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> ModifiedFlags: + def update_parameters(self, parameters: Dict[str, Dict[str, Any]]): """ Updates 2D data acquirer parameters (axes, averages). """ - flags = super().update_parameters(parameters) if self.component_id not in parameters: - return flags + return None params = parameters[self.component_id] if "span" in params and self.span != params["span"]: self.span = params["span"] - flags |= ModifiedFlags.PARAMETERS_MODIFIED | ModifiedFlags.PROGRAM_MODIFIED if "points" in params and self.points != params["points"]: self.points = params["points"] - flags |= ModifiedFlags.PARAMETERS_MODIFIED | ModifiedFlags.PROGRAM_MODIFIED - return flags