From b84b7964e35363fef12fcbf3cf7578155138edff Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Mon, 10 Jul 2023 17:48:59 +0200 Subject: [PATCH 1/3] frappy_psi.sea: further bug fixes - in SEA, it is not guaranteed that the is_running state is set before the run command returns. as a consequence, we have to wait in SeaDrivable.write_target for is_running being set - syncio has always to be reconnected after asynio Change-Id: Ia46cff11de86868ce0627faaf6f776282bd7a8f4 Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/31631 Tested-by: Jenkins Automated Tests Reviewed-by: Enrico Faulhaber Reviewed-by: Markus Zolliker --- frappy_psi/sea.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/frappy_psi/sea.py b/frappy_psi/sea.py index fe907a4..7e17549 100644 --- a/frappy_psi/sea.py +++ b/frappy_psi/sea.py @@ -142,6 +142,8 @@ class SeaClient(ProxyClient, Module): # make sure no more connect thread is running if self._connect_thread and self._connect_thread.isAlive(): return + if not self._last_connect: + self.log.info('reconnect to SEA %s', self.service) self._connect_thread = mkthread(self._connect, None) def register_obj(self, module, obj): @@ -151,6 +153,14 @@ class SeaClient(ProxyClient, Module): self.register_callback(module.name, module.updateEvent) def _connect(self, started_callback): + self.asynio = None + if self.syncio: + # trigger syncio reconnect in self.request() + try: + self.syncio.disconnect() + except Exception: + pass + self.syncio = None self._last_connect = time.time() if self._instance: if not self._service_manager: @@ -192,7 +202,9 @@ class SeaClient(ProxyClient, Module): self._connect_thread.join() except AttributeError: pass - self._connect(None) + # let doPoll do the reconnect + self.pollInfo.trigger(True) + raise ConnectionClosed('disconnected - reconnect later') self.syncio = AsynConn(self.uri) assert self.syncio.readline() == b'OK' self.syncio.writeline(b'seauser seaser') @@ -232,6 +244,7 @@ class SeaClient(ProxyClient, Module): except Exception: pass self.syncio = None + raise raise TimeoutError('no response within 10s') def _rxthread(self, started_callback): @@ -681,6 +694,10 @@ class SeaDrivable(SeaModule, Drivable): _sea_status = '' _is_running = 0 + def earlyInit(self): + super().earlyInit() + self._run_event = threading.Event() + def read_status(self): return self.status @@ -688,8 +705,10 @@ class SeaDrivable(SeaModule, Drivable): # return self.target def write_target(self, value): + self._run_event.clear() self.io.query(f'run {self.sea_object} {value}') - # self.status = [self.Status.BUSY, 'driving'] + if not self._run_event.wait(5): + self.log.warn('target changed but is_running stays 0') return value def update_status(self, value, timestamp, readerror): @@ -701,6 +720,8 @@ class SeaDrivable(SeaModule, Drivable): if not readerror: self._is_running = value self.updateStatus() + if value: + self._run_event.set() def updateStatus(self): if self._sea_status: From 5a8a6b88ff0d5cc39c702b5a8c8edb05b81970df Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Mon, 10 Jul 2023 18:36:02 +0200 Subject: [PATCH 2/3] frappy.client.interactive: bug fixes - correct behaviour with the following untypical message sequence: - send change target - receive status idle - receive status busy - receive changed target - add 'exception' to Logger Change-Id: I614b2a2c2e09ef1b43544838ccb2fa43357dd50d Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/31632 Tested-by: Jenkins Automated Tests Reviewed-by: Enrico Faulhaber Reviewed-by: Markus Zolliker --- frappy/client/interactive.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/frappy/client/interactive.py b/frappy/client/interactive.py index c36a96b..ce02317 100644 --- a/frappy/client/interactive.py +++ b/frappy/client/interactive.py @@ -28,8 +28,8 @@ import code import signal import os import traceback +import threading from os.path import expanduser -from queue import Queue from frappy.client import SecopClient from frappy.errors import SECoPError from frappy.datatypes import get_datatype, StatusType @@ -66,7 +66,7 @@ class Logger: def __init__(self, loglevel='info'): func = self.noop - for lev in 'debug', 'info', 'warning', 'error': + for lev in 'debug', 'info', 'warning', 'error', 'exception': if lev == loglevel: func = self.emit setattr(self, lev, func) @@ -120,7 +120,8 @@ class Module: else: self._watched_params = {'value', 'status'} self._log_level = 'info' - self._running = None + self._is_driving = False + self._driving_event = threading.Event() self._status = None props = secnode.modules[name]['properties'] self._title = f"# {props.get('implementation', '')} ({(props.get('interface_classes') or ['Module'])[0]})" @@ -139,13 +140,9 @@ class Module: return self.status[0] // 100 == StatusType.BUSY // 100 def _status_value_update(self, m, p, status, t, e): - if self._running: - try: - self._running.put(True) - if self._running and not self._isBusy(): - self._running.put(False) - except TypeError: # may happen when _running is removed during above lines - pass + if self._is_driving and not self._isBusy(): + self._is_driving = False + self._driving_event.set() def _watch_parameter(self, m, pname, *args, forced=False, mininterval=0): """show parameter update""" @@ -215,15 +212,17 @@ class Module: def __call__(self, target=None): if target is None: return self.read() - self.target = target # this sets self._running + self.target = target # this sets self._is_driving type(self).value.prev = None # show at least one value try: - while self._running.get(): + while self._is_driving: + self._driving_event.wait() self._watch_parameter(self._name, 'value', mininterval=self._secnode.mininterval) self._watch_parameter(self._name, 'status') + self._driving_event.clear() except KeyboardInterrupt: + self.stop() self._secnode.log.info('-- interrupted --') - self._running = None self._watch_parameter(self._name, 'status') self._secnode.readParameter(self._name, 'value') self._watch_parameter(self._name, 'value', forced=True) @@ -269,10 +268,10 @@ class Param: return self.format(value) def __set__(self, obj, value): - if self.name == 'target': - obj._running = Queue() try: obj._secnode.setParameter(obj._name, self.name, value) + if self.name == 'target': + obj._is_driving = obj._isBusy() return except SECoPError as e: clientenv.raise_with_short_traceback(e) From 5a138884984d2636a0a55b59cdccb32879e75dce Mon Sep 17 00:00:00 2001 From: zebra Date: Tue, 11 Jul 2023 11:27:20 +0200 Subject: [PATCH 3/3] sMA6 encoder mode to CHECK after Oksana experienced that it works --- cfg/main/ma6_cfg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/main/ma6_cfg.py b/cfg/main/ma6_cfg.py index 984a445..1a2e984 100644 --- a/cfg/main/ma6_cfg.py +++ b/cfg/main/ma6_cfg.py @@ -74,7 +74,7 @@ Mod('om', 'frappy_psi.phytron.Motor', 'stick rotation, typically used for omega', io='om_io', - encoder_mode='NO', + encoder_mode='CHECK', target_min=-180, target_max=360, )