mercury, ips, sea, triton, convergence
after gerrit Change-Id: Iff14047ecc476589aef10c96fae9970133b8bd14
This commit is contained in:
@ -40,9 +40,8 @@ from os.path import expanduser, join, exists
|
||||
from frappy.client import ProxyClient
|
||||
from frappy.datatypes import ArrayOf, BoolType, \
|
||||
EnumType, FloatRange, IntRange, StringType
|
||||
from frappy.errors import ConfigError, HardwareError, secop_error, NoSuchModuleError, \
|
||||
CommunicationFailedError
|
||||
from frappy.lib import generalConfig, mkthread, formatExtendedStack
|
||||
from frappy.errors import ConfigError, HardwareError, secop_error, CommunicationFailedError
|
||||
from frappy.lib import generalConfig, mkthread
|
||||
from frappy.lib.asynconn import AsynConn, ConnectionClosed
|
||||
from frappy.modules import Attached, Command, Done, Drivable, \
|
||||
Module, Parameter, Property, Readable, Writable
|
||||
@ -62,7 +61,7 @@ Mod(%(seaconn)r,
|
||||
"""
|
||||
|
||||
CFG_MODULE = """Mod(%(module)r,
|
||||
'frappy_psi.sea.%(modcls)s',
|
||||
'frappy_psi.sea.%(modcls)s', '',
|
||||
io = %(seaconn)r,
|
||||
sea_object = %(module)r,
|
||||
)
|
||||
@ -75,18 +74,18 @@ SERVICE_NAMES = {
|
||||
}
|
||||
|
||||
SEA_DIR = expanduser('~/sea')
|
||||
for confdir in generalConfig.confdir.split(os.pathsep):
|
||||
seaconfdir = join(confdir, 'sea')
|
||||
if exists(seaconfdir):
|
||||
break
|
||||
else:
|
||||
seaconfdir = os.environ.get('FRAPPY_SEA_DIR')
|
||||
seaconfdir = os.environ.get('FRAPPY_SEA_DIR')
|
||||
if not exists(seaconfdir):
|
||||
for confdir in generalConfig.confdir.split(os.pathsep):
|
||||
seaconfdir = join(confdir, 'sea')
|
||||
if exists(seaconfdir):
|
||||
break
|
||||
|
||||
|
||||
def get_sea_port(instance):
|
||||
for filename in ('sea_%s.tcl' % instance, 'sea.tcl'):
|
||||
try:
|
||||
with open(join(SEA_DIR, filename)) as f:
|
||||
with open(join(SEA_DIR, filename), encoding='utf-8') as f:
|
||||
for line in f:
|
||||
linesplit = line.split()
|
||||
if len(linesplit) == 3:
|
||||
@ -115,18 +114,21 @@ class SeaClient(ProxyClient, Module):
|
||||
_instance = None
|
||||
|
||||
def __init__(self, name, log, opts, srv):
|
||||
instance = srv.node_cfg['name'].rsplit('_', 1)[0]
|
||||
nodename = srv.node_cfg.get('name') or srv.node_cfg.get('equipment_id')
|
||||
instance = nodename.rsplit('_', 1)[0]
|
||||
if 'uri' not in opts:
|
||||
self._instance = instance
|
||||
port = get_sea_port(instance)
|
||||
if port is None:
|
||||
raise ConfigError('missing sea port for %s' % instance)
|
||||
opts['uri'] = 'tcp://localhost:%s' % port
|
||||
opts['uri'] = {'value': 'tcp://localhost:%s' % port}
|
||||
self.objects = set()
|
||||
self.shutdown = False
|
||||
self.path2param = {}
|
||||
self._write_lock = threading.Lock()
|
||||
config = opts.get('config')
|
||||
if isinstance(config, dict):
|
||||
config = config['value']
|
||||
if config:
|
||||
self.default_json_file[name] = config.split()[0] + '.json'
|
||||
self.syncio = None
|
||||
@ -147,14 +149,17 @@ class SeaClient(ProxyClient, Module):
|
||||
def _connect(self, started_callback):
|
||||
if self._instance:
|
||||
if not self._service_manager:
|
||||
from servicemanager import SeaManager
|
||||
self._service_manager = SeaManager()
|
||||
self._service_manager.do_start(self._instance)
|
||||
if self._service_manager is None:
|
||||
try:
|
||||
from servicemanager import SeaManager # pylint: disable=import-outside-toplevel
|
||||
self._service_manager = SeaManager()
|
||||
except ImportError:
|
||||
self._service_manager = False
|
||||
if self._service_manager:
|
||||
self._service_manager.do_start(self._instance)
|
||||
if '//' not in self.uri:
|
||||
self.uri = 'tcp://' + self.uri
|
||||
self.asynio = AsynConn(self.uri)
|
||||
# print('CONNECT', self.uri, self.asynio)
|
||||
# print(formatExtendedStack())
|
||||
reply = self.asynio.readline()
|
||||
if reply != b'OK':
|
||||
raise CommunicationFailedError('reply %r should be "OK"' % reply)
|
||||
@ -184,13 +189,11 @@ class SeaClient(ProxyClient, Module):
|
||||
pass
|
||||
self._connect(None)
|
||||
self.syncio = AsynConn(self.uri)
|
||||
# print('SYNCIO', self.uri)
|
||||
assert self.syncio.readline() == b'OK'
|
||||
self.syncio.writeline(b'seauser seaser')
|
||||
assert self.syncio.readline() == b'Login OK'
|
||||
print('connected to %s' % self.uri)
|
||||
self.log.info('connected to %s', self.uri)
|
||||
self.syncio.flush_recv()
|
||||
# print('> %s' % command)
|
||||
ft = 'fulltransAct' if quiet else 'fulltransact'
|
||||
self.syncio.writeline(('%s %s' % (ft, command)).encode())
|
||||
result = None
|
||||
@ -203,19 +206,18 @@ class SeaClient(ProxyClient, Module):
|
||||
except ConnectionClosed:
|
||||
break
|
||||
reply = reply.decode()
|
||||
# print('< %s' % reply)
|
||||
if reply.startswith('TRANSACTIONSTART'):
|
||||
result = []
|
||||
continue
|
||||
if reply == 'TRANSACTIONFINISHED':
|
||||
if result is None:
|
||||
print('missing TRANSACTIONSTART on: %s' % command)
|
||||
self.log.info('missing TRANSACTIONSTART on: %s', command)
|
||||
return ''
|
||||
if not result:
|
||||
return ''
|
||||
return '\n'.join(result)
|
||||
if result is None:
|
||||
print('swallow: %s' % reply)
|
||||
self.log.info('swallow: %s', reply)
|
||||
continue
|
||||
if not result:
|
||||
result = [reply.split('=', 1)[-1]]
|
||||
@ -234,7 +236,7 @@ class SeaClient(ProxyClient, Module):
|
||||
try:
|
||||
msg = json.loads(reply)
|
||||
except Exception as e:
|
||||
print(repr(e), reply)
|
||||
self.log.warn('bad reply %r %r', e, reply)
|
||||
continue
|
||||
if isinstance(msg, str):
|
||||
if msg.startswith('_E '):
|
||||
@ -259,12 +261,12 @@ class SeaClient(ProxyClient, Module):
|
||||
continue
|
||||
if flag != 'hdbevent':
|
||||
if obj not in ('frappy_async_client', 'get_all_param'):
|
||||
print('SKIP', msg)
|
||||
self.log.debug('skip %r', msg)
|
||||
continue
|
||||
if not data:
|
||||
continue
|
||||
if not isinstance(data, dict):
|
||||
print('what means %r' % msg)
|
||||
self.log.debug('what means %r', msg)
|
||||
continue
|
||||
now = time.time()
|
||||
for path, value in data.items():
|
||||
@ -318,7 +320,7 @@ class SeaClient(ProxyClient, Module):
|
||||
|
||||
|
||||
class SeaConfigCreator(SeaClient):
|
||||
def startModule(self, started_callback):
|
||||
def startModule(self, start_events):
|
||||
"""save objects (and sub-objects) description and exit"""
|
||||
self._connect(None)
|
||||
reply = self.request('describe_all')
|
||||
@ -343,20 +345,20 @@ class SeaConfigCreator(SeaClient):
|
||||
service = SERVICE_NAMES[ext]
|
||||
seaconn = 'sea_' + service
|
||||
cfgfile = join(seaconfdir, stripped + '_cfg.py')
|
||||
with open(cfgfile, 'w') as fp:
|
||||
fp.write(CFG_HEADER % dict(config=filename, seaconn=seaconn, service=service,
|
||||
nodedescr=description.get(filename, filename)))
|
||||
with open(cfgfile, 'w', encoding='utf-8') as fp:
|
||||
fp.write(CFG_HEADER % {'config': filename, 'seaconn': seaconn, 'service': service,
|
||||
'nodedescr': description.get(filename, filename)})
|
||||
for obj in descr:
|
||||
fp.write(CFG_MODULE % dict(modcls=modcls[obj], module=obj, seaconn=seaconn))
|
||||
fp.write(CFG_MODULE % {'modcls': modcls[obj], 'module': obj, 'seaconn': seaconn})
|
||||
content = json.dumps(descr).replace('}, {', '},\n{').replace('[{', '[\n{').replace('}]}, ', '}]},\n\n')
|
||||
result.append('%s\n' % cfgfile)
|
||||
with open(join(seaconfdir, filename + '.json'), 'w') as fp:
|
||||
with open(join(seaconfdir, filename + '.json'), 'w', encoding='utf-8') as fp:
|
||||
fp.write(content + '\n')
|
||||
result.append('%s: %s' % (filename, ','.join(n for n in descr)))
|
||||
raise SystemExit('; '.join(result))
|
||||
|
||||
@Command(StringType(), result=StringType())
|
||||
def query(self, cmd):
|
||||
def query(self, cmd, quiet=False):
|
||||
"""a request checking for errors and accepting 0 or 1 line as result"""
|
||||
errors = []
|
||||
reply = None
|
||||
@ -400,12 +402,18 @@ class SeaModule(Module):
|
||||
sea_object = None
|
||||
hdbpath = None # hdbpath for main writable
|
||||
|
||||
# pylint: disable=too-many-statements,arguments-differ,too-many-branches
|
||||
def __new__(cls, name, logger, cfgdict, srv):
|
||||
if hasattr(srv, 'extra_sea_modules'):
|
||||
extra_modules = srv.extra_sea_modules
|
||||
else:
|
||||
extra_modules = {}
|
||||
srv.extra_sea_modules = extra_modules
|
||||
for k, v in cfgdict.items():
|
||||
try:
|
||||
cfgdict[k] = v['value']
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
json_file = cfgdict.pop('json_file', None) or SeaClient.default_json_file[cfgdict['io']]
|
||||
visibility_level = cfgdict.pop('visibility_level', 2)
|
||||
|
||||
@ -416,25 +424,22 @@ class SeaModule(Module):
|
||||
paramdesc['key'] = 'value'
|
||||
if issubclass(cls, SeaWritable):
|
||||
if paramdesc.get('readonly', True):
|
||||
raise ConfigError('%s/%s is not writable' % (sea_object, paramdesc['path']))
|
||||
raise ConfigError(f"{sea_object}/{paramdesc['path']} is not writable")
|
||||
params.insert(0, paramdesc.copy()) # copy value
|
||||
paramdesc['key'] = 'target'
|
||||
paramdesc['readonly'] = False
|
||||
extra_module_set = ()
|
||||
if 'description' not in cfgdict:
|
||||
cfgdict['description'] = '%s@%s' % (single_module, json_file)
|
||||
cfgdict['description'] = f'{single_module}@{json_file}'
|
||||
else:
|
||||
sea_object = cfgdict.pop('sea_object')
|
||||
rel_paths = cfgdict.pop('rel_paths', '.')
|
||||
if 'description' not in cfgdict:
|
||||
cfgdict['description'] = '%s@%s%s' % (
|
||||
name, json_file, '' if rel_paths == '.' else ' (rel_paths=%s)' % rel_paths)
|
||||
name, json_file, '' if rel_paths == '.' else f' (rel_paths={rel_paths})')
|
||||
|
||||
# with open(join(seaconfdir, json_file + '.json')) as fp:
|
||||
# sea_object, descr = json.load(fp)
|
||||
with open(join(seaconfdir, json_file)) as fp:
|
||||
with open(join(seaconfdir, json_file), encoding='utf-8') as fp:
|
||||
content = json.load(fp)
|
||||
# print(json_file, content.keys())
|
||||
descr = content[sea_object]
|
||||
if rel_paths == '*' or not rel_paths:
|
||||
# take all
|
||||
@ -478,7 +483,6 @@ class SeaModule(Module):
|
||||
result.pop(0)
|
||||
else:
|
||||
logger.error('%s: no value found', name)
|
||||
# logger.info('PARAMS %s %r', name, result)
|
||||
base = descr['base']
|
||||
params = descr['params']
|
||||
if issubclass(cls, SeaWritable):
|
||||
@ -486,14 +490,14 @@ class SeaModule(Module):
|
||||
assert paramdesc['key'] == 'value'
|
||||
params.append(paramdesc.copy()) # copy value?
|
||||
if paramdesc.get('readonly', True):
|
||||
raise ConfigError('%s/%s is not writable' % (sea_object, paramdesc['path']))
|
||||
raise ConfigError(f"{sea_object}/{paramdesc['path']} is not writable")
|
||||
paramdesc['key'] = 'target'
|
||||
paramdesc['readonly'] = False
|
||||
extra_module_set = cfgdict.pop('extra_modules', ())
|
||||
if extra_module_set:
|
||||
extra_module_set = set(extra_module_set.replace(',', ' ').split())
|
||||
path2param = {}
|
||||
attributes = dict(sea_object=sea_object, path2param=path2param)
|
||||
attributes = {'sea_object': sea_object, 'path2param': path2param}
|
||||
|
||||
# some guesses about visibility (may be overriden in *_cfg.py):
|
||||
if sea_object in ('table', 'cc'):
|
||||
@ -504,12 +508,10 @@ class SeaModule(Module):
|
||||
path = paramdesc['path']
|
||||
readonly = paramdesc.get('readonly', True)
|
||||
dt = get_datatype(paramdesc)
|
||||
#print('----', sea_object)
|
||||
#print(dt, paramdesc)
|
||||
kwds = dict(description=paramdesc.get('description', path),
|
||||
datatype=dt,
|
||||
visibility=paramdesc.get('visibility', 1),
|
||||
needscfg=False, readonly=readonly)
|
||||
kwds = {'description': paramdesc.get('description', path),
|
||||
'datatype': dt,
|
||||
'visibility': paramdesc.get('visibility', 1),
|
||||
'needscfg': False, 'readonly': readonly}
|
||||
if kwds['datatype'] is None:
|
||||
kwds.update(visibility=3, default='', datatype=StringType())
|
||||
pathlist = path.split('/') if path else []
|
||||
@ -555,10 +557,8 @@ class SeaModule(Module):
|
||||
continue # skip this parameter
|
||||
path2param.setdefault(hdbpath, []).append((name, key))
|
||||
attributes[key] = pobj
|
||||
# if hasattr(cls, 'read_' + key):
|
||||
# print('override %s.read_%s' % (cls.__name__, key))
|
||||
|
||||
def rfunc(self, cmd='hval %s/%s' % (base, path)):
|
||||
def rfunc(self, cmd=f'hval {base}/{path}'):
|
||||
reply = self.io.query(cmd, True)
|
||||
try:
|
||||
reply = float(reply)
|
||||
@ -571,15 +571,13 @@ class SeaModule(Module):
|
||||
attributes['read_' + key] = rfunc
|
||||
|
||||
if not readonly:
|
||||
# if hasattr(cls, 'write_' + key):
|
||||
# print('override %s.write_%s' % (cls.__name__, key))
|
||||
|
||||
def wfunc(self, value, datatype=datatype, command=paramdesc['cmd']):
|
||||
value = datatype.export_value(value)
|
||||
if isinstance(value, bool):
|
||||
value = int(value)
|
||||
# TODO: check if more has to be done for valid tcl data (strings?)
|
||||
cmd = "%s %s" % (command, value)
|
||||
cmd = f'{command} {value}'
|
||||
self.io.query(cmd)
|
||||
return Done
|
||||
|
||||
@ -596,7 +594,7 @@ class SeaModule(Module):
|
||||
attributes[pname] = pobj
|
||||
pobj.__set_name__(cls, pname)
|
||||
|
||||
classname = '%s_%s' % (cls.__name__, name)
|
||||
classname = f'{cls.__name__}_{name}'
|
||||
newcls = type(classname, (cls,), attributes)
|
||||
result = Module.__new__(newcls)
|
||||
return result
|
||||
@ -609,11 +607,9 @@ class SeaModule(Module):
|
||||
try:
|
||||
pobj = self.parameters[parameter]
|
||||
except KeyError:
|
||||
print(self.name, parameter)
|
||||
self.log.error('do not know %s:%s', self.name, parameter)
|
||||
raise
|
||||
pobj.timestamp = timestamp
|
||||
#if not pobj.readonly and pobj.value != value:
|
||||
# print('UPDATE', module, parameter, value)
|
||||
# should be done here: deal with clock differences
|
||||
if not readerror:
|
||||
try:
|
||||
@ -668,7 +664,7 @@ class SeaDrivable(SeaModule, Drivable):
|
||||
# return self.target
|
||||
|
||||
def write_target(self, value):
|
||||
self.io.query('run %s %s' % (self.sea_object, value))
|
||||
self.io.query(f'run {self.sea_object} {value}')
|
||||
# self.status = [self.Status.BUSY, 'driving']
|
||||
return value
|
||||
|
||||
@ -701,4 +697,4 @@ class SeaDrivable(SeaModule, Drivable):
|
||||
- on stdsct drivables this will call the halt script
|
||||
- on EaseDriv this will set the stopped state
|
||||
"""
|
||||
self.io.query('%s is_running 0' % self.sea_object)
|
||||
self.io.query(f'{self.sea_object} is_running 0')
|
||||
|
Reference in New Issue
Block a user