Atoring just updates on the SecopInteractor does not work properly, as these updates depend on what the clients have already. As updates are polled internall anyway, store onm each client the state of the parameters it has looked up already and emit changes values only.
165 lines
6.6 KiB
Python
165 lines
6.6 KiB
Python
import logging
|
|
from frappy.client import SecopClient
|
|
|
|
|
|
def convert_par(module, name, par):
|
|
result = dict(type='input', name=module+":"+name, title=name)
|
|
if par.get('readonly', True):
|
|
result['type'] = 'rdonly'
|
|
else:
|
|
result['command'] = 'change %s:%s' % (module, name)
|
|
if par['datainfo']['type'] == 'enum':
|
|
result['enum_names'] = [dict(title=k, value=v) for k, v in par['datainfo']['members'].items()]
|
|
result['type'] = 'enum'
|
|
elif par['datainfo']['type'] == 'bool':
|
|
result['type'] = 'checkbox'
|
|
if par['description']:
|
|
result['info'] = par['description']
|
|
return result
|
|
|
|
|
|
def convert_cmd(module, name, cmd):
|
|
result = dict(type='pushbutton', name=module+":"+name, title=name)
|
|
result['command'] = 'do %s:%s' % (module, name)
|
|
argument = cmd['datainfo'].get('argument')
|
|
if cmd['datainfo'].get('result'):
|
|
result['result'] = True
|
|
else:
|
|
if not argument: # simple command like stop
|
|
return result
|
|
result['button'] = not argument
|
|
# result['type'] = pushbutton will be replaced below
|
|
if argument:
|
|
if argument['type'] == 'enum':
|
|
result['enum_names'] = [dict(title=k, value=v) for k, v in argument['members'].items()]
|
|
result['type'] = 'enum'
|
|
elif argument['type'] == 'bool':
|
|
result['type'] = 'checkbox'
|
|
else:
|
|
result['type'] = 'input'
|
|
else:
|
|
result['type'] = 'rdonly'
|
|
if cmd['description']:
|
|
result['info'] = cmd['description']
|
|
return result
|
|
|
|
|
|
class SecopInteractor(SecopClient):
|
|
prio_par = ["value", "status", "target"]
|
|
hide_par = ["baseclass", "class", "pollinterval"]
|
|
skip_par = ["status2"]
|
|
main_params = {'value', 'status', 'param'}
|
|
|
|
def __init__(self, uri, node_map, change_callback=None):
|
|
super().__init__(uri)
|
|
self.change_callback = change_callback
|
|
try:
|
|
self.connect()
|
|
self.update_node_map(node_map)
|
|
self.register_callback(None, descriptiveDataChange=self.descChanged)
|
|
except Exception as e:
|
|
print(repr(e))
|
|
|
|
def update_node_map(self, node_map):
|
|
node_map.update({k: self for k in self.modules})
|
|
|
|
def descChanged(self, module, desc):
|
|
if module is None and self.change_callback:
|
|
self.change_callback()
|
|
|
|
def add_main_components(self, components):
|
|
for name, desc in self.modules.items():
|
|
parameters = desc['parameters']
|
|
component = {'type': 'rdonly' if 'value' in parameters else 'none'}
|
|
if 'status' in parameters:
|
|
component['statusname'] = f'{name}:status'
|
|
targetpar = parameters.get('target')
|
|
if targetpar:
|
|
component.update(convert_par(name, 'target', targetpar))
|
|
component['targetname'] = f'{name}:target'
|
|
info = desc['properties'].get('description')
|
|
if info:
|
|
component['info'] = info
|
|
component['name'] = f'{name}:value'
|
|
component['title'] = name
|
|
components.append(component)
|
|
|
|
def get_components(self, path):
|
|
module = self.modules[path]
|
|
parameters = dict(module["parameters"])
|
|
components = []
|
|
for name in SecopInteractor.skip_par:
|
|
if name in parameters:
|
|
parameters.pop(name)
|
|
for name in SecopInteractor.prio_par:
|
|
if name in parameters:
|
|
components.append(convert_par(path, name, parameters.pop(name)))
|
|
components1 = []
|
|
for name in SecopInteractor.hide_par:
|
|
if name in parameters:
|
|
components1.append(convert_par(path, name, parameters.pop(name)))
|
|
for name, par in parameters.items():
|
|
components.append(convert_par(path, name, par))
|
|
components.extend(components1)
|
|
for name, cmd in module.get("commands", {}).items():
|
|
components.append(convert_cmd(path, name, cmd))
|
|
return components
|
|
|
|
def buildUpdateEvent(self, module, parameter, entry):
|
|
name = f'{module}:{parameter}'
|
|
if entry.readerror:
|
|
item = {'name': name, 'error': str(entry.readerror)}
|
|
elif parameter == 'status':
|
|
# statuscode: 0: DISABLED, 1: IDLE, 2: WARN, 3: BUSY, 4: ERROR
|
|
statuscode, statustext = entry[0]
|
|
formatted = statuscode.name + (f', {statustext}' if statustext else '')
|
|
item = {'name': name, 'value': str(entry), 'statuscode': entry[0][0] // 100,
|
|
'formatted': formatted}
|
|
else:
|
|
item = {'name': name, 'value': str(entry), 'formatted': entry.formatted()}
|
|
return item
|
|
|
|
def handle_command(self, command):
|
|
"""handle command if we can, else return False"""
|
|
if not command.strip():
|
|
return dict(type='accept-command')
|
|
is_param = True
|
|
if command.startswith('change '):
|
|
command = command[7:]
|
|
elif command.startswith('do '):
|
|
is_param = False
|
|
command = command[3:]
|
|
modpar, _, strvalue = command.partition(' ')
|
|
module, _, parameter = modpar.partition(':')
|
|
if not parameter:
|
|
parameter = 'target'
|
|
if module not in self.modules:
|
|
return None
|
|
logging.info('SENDCOMMAND %r', command)
|
|
if is_param:
|
|
try:
|
|
entry = self.setParameterFromString(module, parameter, strvalue)
|
|
item = {'name': f'{module}:{parameter}', 'value': str(entry), 'formatted': entry.formatted()}
|
|
except Exception as e:
|
|
print(f"{e!r} converting {strvalue} to {self.modules[module]['parameters'][parameter]['datatype']}")
|
|
self.updates[module, parameter] = item
|
|
return True
|
|
# called a command
|
|
item = self.execCommandFromString(module, parameter, strvalue)[0] # ignore qualifiers
|
|
return {'name': f'{module}:{parameter}', 'value': str(item.value), 'formatted': item.formatted()}
|
|
|
|
def get_updates(self, client_state, update_filter):
|
|
updates = []
|
|
main_params = self.main_params if '' in update_filter else ()
|
|
for (module, param), item in self.cache.items():
|
|
if param in main_params or module in update_filter:
|
|
key = module, param
|
|
prev = client_state.get(key)
|
|
if prev is None or item.value != prev.value or item.readerror != prev.readerror:
|
|
updates.append(self.buildUpdateEvent(module, param, item))
|
|
client_state[key] = item
|
|
return updates
|
|
|
|
def info(self):
|
|
return ["na"]
|