From 62823899bb20c74f39796db097dedbbe7e359645 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Fri, 20 Mar 2026 11:49:00 +0100 Subject: [PATCH] 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. --- base.py | 13 +++++----- secop.py | 73 ++++++++++++++++++++++---------------------------------- 2 files changed, 34 insertions(+), 52 deletions(-) diff --git a/base.py b/base.py index e5586e9..af93f43 100644 --- a/base.py +++ b/base.py @@ -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 diff --git a/secop.py b/secop.py index 6b697d0..539ab17 100644 --- a/secop.py +++ b/secop.py @@ -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"]