diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index 86887cd..82b4a96 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -1055,7 +1055,7 @@ class FlomniAlignmentMixin: with open(os.path.join(dir_path, "ptychotomoalign_Ay3.txt"), "r") as file: tomo_alignment_fit[1][3] = file.readline() - with open(os.path.join(dir_path, "ptychotomoalign_Cy3.txt"), "r") as file: + with open(os.path.join(dir_path, "ptychotomoalign_By3.txt"), "r") as file: tomo_alignment_fit[1][4] = file.readline() print("New alignment parameters loaded from filesystem, meaning Matlab fit:") @@ -1618,7 +1618,7 @@ class Flomni( self.feye_out() tags = ["BEC_alignment_tomo", self.sample_name] - + self.write_alignment_scan_numbers(bec.queue.next_scan_number) start_angle = 0 alignment_scan_numbers = [] @@ -1667,6 +1667,32 @@ class Flomni( print(scilog_content) bec.messaging.scilog.new().add_text(scilog_content.replace("\n", "
")).add_tags("alignmentscan").send() + def write_alignment_scan_numbers(self, first_scan): + import os + + file = os.path.expanduser("~/data/raw/logs/ptychotomoalign_scannum.txt") + os.makedirs(os.path.dirname(file), exist_ok=True) + + scans = [first_scan + k for k in range(5)] + angles = [0, 45, 90, 135, 180] + + x_vals = [] + + for angle in angles: + x, y, z = self.get_alignment_offset(angle) + x_vals.append(x) + + zeros = [0] * len(angles) + + with open(file, "w") as f: + f.write(" ".join(map(str, scans)) + "\n") + f.write(" ".join(map(str, angles)) + "\n") + f.write(" ".join(f"{x:.2f}" for x in x_vals) + "\n") + f.write(" ".join(map(str, x_vals)) + "\n") + f.write(" ".join(map(str, zeros)) + "\n") + + + def sub_tomo_scan(self, subtomo_number, start_angle=None): """ Performs a sub tomogram scan. diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py index 82333d6..6ea3b10 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_webpage_generator.py @@ -158,7 +158,7 @@ def _safe_get(device_manager, dotpath: str): obj = device_manager.devices for part in dotpath.split("."): obj = getattr(obj, part) - return obj.get() + return obj.get(cached=True) except Exception: return None @@ -2230,16 +2230,23 @@ function render(d){{ handleAudioForStatus(s, prevStatus); }} +let _fetchFailCount=0; +const FETCH_FAIL_THRESHOLD=3; + async function poll(){{ try{{ const r=await fetch(STATUS_JSON, {{cache:'no-store'}}); if(!r.ok) throw new Error('HTTP '+r.status); + _fetchFailCount=0; render(await r.json()); }}catch(e){{ - console.warn('Fetch failed:',e); - document.getElementById('last-update').textContent='fetch failed - retrying...'; - document.getElementById('outdated-banner').classList.add('visible'); - handleStale(true); + _fetchFailCount++; + console.warn('Fetch failed:',e,'('+_fetchFailCount+')'); + document.getElementById('last-update').textContent='fetch failed - retrying\u2026 ('+_fetchFailCount+')'; + if(_fetchFailCount>=FETCH_FAIL_THRESHOLD){{ + document.getElementById('outdated-banner').classList.add('visible'); + handleStale(true); + }} }} }} diff --git a/csaxs_bec/devices/jungfraujoch/jungfraujoch_preview.py b/csaxs_bec/devices/jungfraujoch/jungfraujoch_preview.py index 5364c70..713338b 100644 --- a/csaxs_bec/devices/jungfraujoch/jungfraujoch_preview.py +++ b/csaxs_bec/devices/jungfraujoch/jungfraujoch_preview.py @@ -214,11 +214,13 @@ class JungfrauJochPreview: r = self._socket.recv_multipart(flags=zmq.NOBLOCK) self._parse_data(r) except zmq.error.Again: - # No message received, this is expected when the receive queue is empty + # No new message available on Preview socket within timeout that was set pass - except Exception as e: - # Consider moving this to debug level, but it can be useful to see why preview updates are not working - logger.info(f"Error while receiving ZMQ message from JFJ preview at {self.url}: {e}") + except Exception as exc: + logger.info( + f"Error parsing input data from JungfrauJoch host {self.url}. Exception: {exc}" + ) + finally: # Unsubscribe from the topic self._socket.setsockopt(zmq.UNSUBSCRIBE, ZMQ_TOPIC_FILTER) diff --git a/csaxs_bec/devices/omny/galil/fgalil_ophyd.py b/csaxs_bec/devices/omny/galil/fgalil_ophyd.py index f8d424b..7b79edd 100644 --- a/csaxs_bec/devices/omny/galil/fgalil_ophyd.py +++ b/csaxs_bec/devices/omny/galil/fgalil_ophyd.py @@ -37,6 +37,7 @@ class FlomniGalilController(GalilController): "get_motor_limit_switch", "fosaz_light_curtain_is_triggered", "is_motor_on", + "is_thread_active", "all_axes_referenced", "lights_off", "lights_on", diff --git a/csaxs_bec/devices/omny/galil/galil_ophyd.py b/csaxs_bec/devices/omny/galil/galil_ophyd.py index d6fcda9..550cc36 100644 --- a/csaxs_bec/devices/omny/galil/galil_ophyd.py +++ b/csaxs_bec/devices/omny/galil/galil_ophyd.py @@ -57,6 +57,7 @@ class GalilController(Controller): "find_reference", "get_motor_limit_switch", "is_motor_on", + "is_thread_active", "all_axes_referenced", ] @@ -85,7 +86,7 @@ class GalilController(Controller): return_val = self.socket_put_and_receive(val) if return_val != ":": raise GalilCommunicationError( - f"Expected return value of ':' but instead received {return_val}" + f"Sent {val} and expected return value of ':' but instead received {return_val}" ) def is_axis_moving(self, axis_Id, axis_Id_numeric) -> bool: diff --git a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py index 1626ebc..6038286 100644 --- a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py @@ -353,10 +353,13 @@ class RtFlomniController(Controller): def laser_tracker_galil_enable(self): ftrackz_con = self.device_manager.devices.ftrackz.obj.controller - ftrackz_con.socket_put_confirmed("tracken=1") - ftrackz_con.socket_put_confirmed("trackyct=0") - ftrackz_con.socket_put_confirmed("trackzct=0") - ftrackz_con.socket_put_confirmed("XQ#Tracker") + if not ftrackz_con.is_thread_active(5): + ftrackz_con.socket_put_confirmed("tracken=1") + ftrackz_con.socket_put_confirmed("trackyct=0") + ftrackz_con.socket_put_confirmed("trackzct=0") + ftrackz_con.socket_put_confirmed("XQ#Tracker") + while not ftrackz_con.is_thread_active(5): + time.sleep(0.1) def laser_tracker_on_target(self) -> bool: self.laser_update_tracker_info() @@ -369,8 +372,8 @@ class RtFlomniController(Controller): def laser_tracker_wait_on_target(self): max_repeat = 25 count = 0 + self.laser_tracker_galil_enable() while not self.laser_tracker_on_target(): - self.laser_tracker_galil_enable() logger.info("Waiting for laser tracker to reach target.") time.sleep(0.5) count += 1 @@ -442,7 +445,7 @@ class RtFlomniController(Controller): " interferometer error." ) # here exception - (mode, number_of_positions_planned, current_position_in_scan) = self.get_scan_status() + mode, number_of_positions_planned, current_position_in_scan = self.get_scan_status() if number_of_positions_planned == 0: logger.error("Cannot start scan because no target positions are planned.") @@ -517,14 +520,11 @@ class RtFlomniSetpointSignal(RtSetpointSignal): tracker_status = self.parent.controller.laser_tracker_check_signalstrength() if tracker_status == "toolow": - print( - "The interferometer signal is too low for movements. Realignment required." - ) + print("The interferometer signal is too low for movements. Realignment required.") raise RtError( "The interferometer signal is too low for movements. Realignment required." ) - self.set_with_feedback_disabled(val) def set_with_feedback_disabled(self, val):