change update mechanism

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.
This commit is contained in:
2026-03-20 11:49:00 +01:00
parent 9b16483e45
commit 62823899bb
2 changed files with 34 additions and 52 deletions

13
base.py
View File

@@ -48,6 +48,8 @@ class Client(HandlerBase):
self.id = uuid.uuid4().hex[0:15]
self.nodes = {}
self.node_map = {}
self.update_filter = set() # modules to be updated
self.cache = {} # a dict to store the current state of all values
if streams:
for uri in streams:
urisplit = uri.rsplit('://')
@@ -69,12 +71,11 @@ class Client(HandlerBase):
self.server = server
self.instrument_name = instrument_name
self.device_name = device_name # do not know if this is needed
self.updates = {}
def poll(self):
updates = sum((n.get_updates() for n in self.nodes.values()), start=[])
updates = sum((n.get_updates(self.cache, self.update_filter) for n in self.nodes.values()), start=[])
result = [dict(type='update', updates=updates)] if updates else []
graph_updates = self.handlers.get('graphpoll', object)()
graph_updates = self.handlers.get('graphpoll', type(None))()
if graph_updates:
result.append(graph_updates)
return result
@@ -91,11 +92,9 @@ class Client(HandlerBase):
def w_updateblock(self, path):
if path == 'main': # TODO: change to "-main-"?
for node in self.nodes.values():
node.update_main()
self.update_filter.add('') # ready to accept updates for main block
else:
node = self.node_map[path]
node.update_params(path)
self.update_filter.add(path)
return dict(type='accept-block')
def w_console(self): # TODO: check if still used

View File

@@ -48,17 +48,15 @@ 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.module_updates = set()
self.param_updates = set()
self.updates = {}
self.change_callback = change_callback
try:
self.connect()
self.update_node_map(node_map)
self.register_callback(None, updateItem=self.updateItem, descriptiveDataChange=self.descChanged)
self.register_callback(None, descriptiveDataChange=self.descChanged)
except Exception as e:
print(repr(e))
@@ -85,13 +83,9 @@ class SecopInteractor(SecopClient):
component['name'] = f'{name}:value'
component['title'] = name
components.append(component)
self.param_updates.add('value')
self.param_updates.add('status')
self.param_updates.add('target')
def get_components(self, path):
module = self.modules[path]
self.module_updates.add(path) # TODO: remove others?
parameters = dict(module["parameters"])
components = []
for name in SecopInteractor.skip_par:
@@ -111,38 +105,19 @@ class SecopInteractor(SecopClient):
components.append(convert_cmd(path, name, cmd))
return components
def updateItem(self, module, parameter, entry):
key = module, parameter
# print(key, entry)
if module in self.module_updates or parameter in self.param_updates:
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()}
# print(item)
self.updates[key] = item
def update_main(self):
cache = self.cache
for modname in self.modules:
for param in 'value', 'status', 'target':
key = modname, param
if key in cache:
self.updateItem(*key, cache[key])
def update_params(self, path):
cache = self.cache
for param in self.modules[path]['parameters']:
key = path, param
if key in cache:
self.updateItem(*key, cache[key])
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"""
@@ -170,12 +145,20 @@ class SecopInteractor(SecopClient):
self.updates[module, parameter] = item
return True
# called a command
formatted = self.execCommandFromString(module, parameter, strvalue)[0] # ignore qualifiers
return {'name': f'{module}:{parameter}', 'value': str(result), 'formatted': formatted}
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):
updates, self.updates = self.updates, {}
return list(updates.values())
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"]