Merge branch 'wip' of gitlab.psi.ch:samenv/frappy into wip

This commit is contained in:
l_samenv 2023-07-11 13:27:57 +02:00
commit 9e2e6074c8
3 changed files with 38 additions and 18 deletions

View File

@ -74,7 +74,7 @@ Mod('om',
'frappy_psi.phytron.Motor', 'frappy_psi.phytron.Motor',
'stick rotation, typically used for omega', 'stick rotation, typically used for omega',
io='om_io', io='om_io',
encoder_mode='NO', encoder_mode='CHECK',
target_min=-180, target_min=-180,
target_max=360, target_max=360,
) )

View File

@ -28,8 +28,8 @@ import code
import signal import signal
import os import os
import traceback import traceback
import threading
from os.path import expanduser from os.path import expanduser
from queue import Queue
from frappy.client import SecopClient from frappy.client import SecopClient
from frappy.errors import SECoPError from frappy.errors import SECoPError
from frappy.datatypes import get_datatype, StatusType from frappy.datatypes import get_datatype, StatusType
@ -66,7 +66,7 @@ class Logger:
def __init__(self, loglevel='info'): def __init__(self, loglevel='info'):
func = self.noop func = self.noop
for lev in 'debug', 'info', 'warning', 'error': for lev in 'debug', 'info', 'warning', 'error', 'exception':
if lev == loglevel: if lev == loglevel:
func = self.emit func = self.emit
setattr(self, lev, func) setattr(self, lev, func)
@ -120,7 +120,8 @@ class Module:
else: else:
self._watched_params = {'value', 'status'} self._watched_params = {'value', 'status'}
self._log_level = 'info' self._log_level = 'info'
self._running = None self._is_driving = False
self._driving_event = threading.Event()
self._status = None self._status = None
props = secnode.modules[name]['properties'] props = secnode.modules[name]['properties']
self._title = f"# {props.get('implementation', '')} ({(props.get('interface_classes') or ['Module'])[0]})" 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 return self.status[0] // 100 == StatusType.BUSY // 100
def _status_value_update(self, m, p, status, t, e): def _status_value_update(self, m, p, status, t, e):
if self._running: if self._is_driving and not self._isBusy():
try: self._is_driving = False
self._running.put(True) self._driving_event.set()
if self._running and not self._isBusy():
self._running.put(False)
except TypeError: # may happen when _running is removed during above lines
pass
def _watch_parameter(self, m, pname, *args, forced=False, mininterval=0): def _watch_parameter(self, m, pname, *args, forced=False, mininterval=0):
"""show parameter update""" """show parameter update"""
@ -215,15 +212,17 @@ class Module:
def __call__(self, target=None): def __call__(self, target=None):
if target is None: if target is None:
return self.read() 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 type(self).value.prev = None # show at least one value
try: 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, 'value', mininterval=self._secnode.mininterval)
self._watch_parameter(self._name, 'status') self._watch_parameter(self._name, 'status')
self._driving_event.clear()
except KeyboardInterrupt: except KeyboardInterrupt:
self.stop()
self._secnode.log.info('-- interrupted --') self._secnode.log.info('-- interrupted --')
self._running = None
self._watch_parameter(self._name, 'status') self._watch_parameter(self._name, 'status')
self._secnode.readParameter(self._name, 'value') self._secnode.readParameter(self._name, 'value')
self._watch_parameter(self._name, 'value', forced=True) self._watch_parameter(self._name, 'value', forced=True)
@ -269,10 +268,10 @@ class Param:
return self.format(value) return self.format(value)
def __set__(self, obj, value): def __set__(self, obj, value):
if self.name == 'target':
obj._running = Queue()
try: try:
obj._secnode.setParameter(obj._name, self.name, value) obj._secnode.setParameter(obj._name, self.name, value)
if self.name == 'target':
obj._is_driving = obj._isBusy()
return return
except SECoPError as e: except SECoPError as e:
clientenv.raise_with_short_traceback(e) clientenv.raise_with_short_traceback(e)

View File

@ -142,6 +142,8 @@ class SeaClient(ProxyClient, Module):
# make sure no more connect thread is running # make sure no more connect thread is running
if self._connect_thread and self._connect_thread.isAlive(): if self._connect_thread and self._connect_thread.isAlive():
return return
if not self._last_connect:
self.log.info('reconnect to SEA %s', self.service)
self._connect_thread = mkthread(self._connect, None) self._connect_thread = mkthread(self._connect, None)
def register_obj(self, module, obj): def register_obj(self, module, obj):
@ -151,6 +153,14 @@ class SeaClient(ProxyClient, Module):
self.register_callback(module.name, module.updateEvent) self.register_callback(module.name, module.updateEvent)
def _connect(self, started_callback): 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() self._last_connect = time.time()
if self._instance: if self._instance:
if not self._service_manager: if not self._service_manager:
@ -192,7 +202,9 @@ class SeaClient(ProxyClient, Module):
self._connect_thread.join() self._connect_thread.join()
except AttributeError: except AttributeError:
pass pass
self._connect(None) # let doPoll do the reconnect
self.pollInfo.trigger(True)
raise ConnectionClosed('disconnected - reconnect later')
self.syncio = AsynConn(self.uri) self.syncio = AsynConn(self.uri)
assert self.syncio.readline() == b'OK' assert self.syncio.readline() == b'OK'
self.syncio.writeline(b'seauser seaser') self.syncio.writeline(b'seauser seaser')
@ -232,6 +244,7 @@ class SeaClient(ProxyClient, Module):
except Exception: except Exception:
pass pass
self.syncio = None self.syncio = None
raise
raise TimeoutError('no response within 10s') raise TimeoutError('no response within 10s')
def _rxthread(self, started_callback): def _rxthread(self, started_callback):
@ -681,6 +694,10 @@ class SeaDrivable(SeaModule, Drivable):
_sea_status = '' _sea_status = ''
_is_running = 0 _is_running = 0
def earlyInit(self):
super().earlyInit()
self._run_event = threading.Event()
def read_status(self): def read_status(self):
return self.status return self.status
@ -688,8 +705,10 @@ class SeaDrivable(SeaModule, Drivable):
# return self.target # return self.target
def write_target(self, value): def write_target(self, value):
self._run_event.clear()
self.io.query(f'run {self.sea_object} {value}') 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 return value
def update_status(self, value, timestamp, readerror): def update_status(self, value, timestamp, readerror):
@ -701,6 +720,8 @@ class SeaDrivable(SeaModule, Drivable):
if not readerror: if not readerror:
self._is_running = value self._is_running = value
self.updateStatus() self.updateStatus()
if value:
self._run_event.set()
def updateStatus(self): def updateStatus(self):
if self._sea_status: if self._sea_status: