improvements on interactive client

- fix handling of exceptions
- add selective logging
- improve formatting of values

Change-Id: I69c11e95aca1cdd222800fd3fd192a6b12b38411
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/29348
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 2022-09-22 07:47:43 +02:00
parent 0a28192c15
commit 71aaf7187a
2 changed files with 42 additions and 16 deletions

View File

@ -356,7 +356,7 @@ class SecopClient(ProxyClient):
except ConnectionClosed: except ConnectionClosed:
pass pass
except Exception as e: except Exception as e:
self.log.error('rxthread ended with %s' % e) self.log.error('rxthread ended with %r', e)
self._rxthread = None self._rxthread = None
self.disconnect(False) self.disconnect(False)
if self._shutdown: if self._shutdown:
@ -490,7 +490,7 @@ class SecopClient(ProxyClient):
def _unhandled_message(self, action, ident, data): def _unhandled_message(self, action, ident, data):
if not self.callback(None, 'unhandledMessage', action, ident, data): if not self.callback(None, 'unhandledMessage', action, ident, data):
self.log.warning('unhandled message: %s %s %r' % (action, ident, data)) self.log.warning('unhandled message: %s %s %r', action, ident, data)
def _set_state(self, online, state=None): def _set_state(self, online, state=None):
# remark: reconnecting is treated as online # remark: reconnecting is treated as online

View File

@ -23,10 +23,11 @@
import sys import sys
import time import time
import json import re
from queue import Queue from queue import Queue
from secop.client import SecopClient from secop.client import SecopClient
from secop.errors import SECoPError from secop.errors import SECoPError
from secop.datatypes import get_datatype
USAGE = """ USAGE = """
Usage: Usage:
@ -58,10 +59,15 @@ class Logger:
if lev == loglevel: if lev == loglevel:
func = self.emit func = self.emit
setattr(self, lev, func) setattr(self, lev, func)
self._minute = 0
@staticmethod def emit(self, fmt, *args, **kwds):
def emit(fmt, *args, **kwds): now = time.time()
print(str(fmt) % args) minute = now // 60
if minute != self._minute:
self._minute = minute
print(time.strftime('--- %H:%M:%S ---', time.localtime(now)))
print('%6.3f' % (now % 60.0), str(fmt) % args)
@staticmethod @staticmethod
def noop(fmt, *args, **kwds): def noop(fmt, *args, **kwds):
@ -77,6 +83,8 @@ class PrettyFloat(float):
class Module: class Module:
_log_pattern = re.compile('.*')
def __init__(self, name, secnode): def __init__(self, name, secnode):
self._name = name self._name = name
self._secnode = secnode self._secnode = secnode
@ -89,15 +97,12 @@ class Module:
def _one_line(self, pname, minwid=0): def _one_line(self, pname, minwid=0):
"""return <module>.<param> = <value> truncated to one line""" """return <module>.<param> = <value> truncated to one line"""
param = getattr(type(self), pname)
try: try:
value = getattr(self, pname) value = getattr(self, pname)
# make floats appear with 7 digits only r = param.format(value)
r = repr(json.loads(json.dumps(value), parse_float=PrettyFloat))
except Exception as e: except Exception as e:
r = repr(e) r = repr(e)
unit = getattr(type(self), pname).unit
if unit:
r += ' %s' % unit
pname = pname.ljust(minwid) pname = pname.ljust(minwid)
vallen = 113 - len(self._name) - len(pname) vallen = 113 - len(self._name) - len(pname)
if len(r) > vallen: if len(r) > vallen:
@ -174,13 +179,21 @@ class Module:
'\n'.join(self._one_line(k, wid) for k in self._parameters), '\n'.join(self._one_line(k, wid) for k in self._parameters),
', '.join(k + '()' for k in self._commands)) ', '.join(k + '()' for k in self._commands))
def logging(self, level='comlog', pattern='.*'):
self._log_pattern = re.compile(pattern)
self._secnode.request('logging', self._name, level)
def handle_log_message_(self, data):
if self._log_pattern.match(data):
self._secnode.log.info('%s: %r', self._name, data)
class Param: class Param:
def __init__(self, name, unit=None): def __init__(self, name, datainfo):
self.name = name self.name = name
self.prev = None self.prev = None
self.prev_time = 0 self.prev_time = 0
self.unit = unit self.datatype = get_datatype(datainfo)
def __get__(self, obj, owner): def __get__(self, obj, owner):
if obj is None: if obj is None:
@ -198,6 +211,9 @@ class Param:
except SECoPError as e: except SECoPError as e:
obj._secnode.log.error(repr(e)) obj._secnode.log.error(repr(e))
def format(self, value):
return self.datatype.format_value(value)
class Command: class Command:
def __init__(self, name, modname, secnode): def __init__(self, name, modname, secnode):
@ -250,14 +266,24 @@ class Client(SecopClient):
self.log.info('overwrite module %s', modname) self.log.info('overwrite module %s', modname)
attrs = {} attrs = {}
for pname, pinfo in moddesc['parameters'].items(): for pname, pinfo in moddesc['parameters'].items():
unit = pinfo['datainfo'].get('unit') attrs[pname] = Param(pname, pinfo['datainfo'])
attrs[pname] = Param(pname, unit)
for cname in moddesc['commands']: for cname in moddesc['commands']:
attrs[cname] = Command(cname, modname, self) attrs[cname] = Command(cname, modname, self)
mobj = type('M_%s' % modname, (Module,), attrs)(modname, self) mobj = type('M_%s' % modname, (Module,), attrs)(modname, self)
if 'status' in mobj._parameters: if 'status' in mobj._parameters:
self.register_callback((modname, 'status'), updateEvent=mobj._status_value_update) self.register_callback((modname, 'status'), updateEvent=mobj._status_value_update)
self.register_callback((modname, 'value'), updateEvent=mobj._status_value_update) self.register_callback((modname, 'value'), updateEvent=mobj._status_value_update)
setattr(main, modname, mobj) setattr(main, modname, mobj)
self.register_callback(None, self.unhandledMessage)
self.log.info('%s', USAGE) self.log.info('%s', USAGE)
def unhandledMessage(self, action, ident, data):
"""handle logging messages"""
if action == 'log':
modname = ident.split(':')[0]
modobj = getattr(main, modname, None)
if modobj:
modobj.handle_log_message_(data)
return
self.log.info('module %s not found', modname)
self.log.info('unhandled: %s %s %r', action, ident, data)