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 <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
zolliker 2023-07-10 18:36:02 +02:00
parent e866ae7034
commit a7cbb35455

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)