# HG changeset patch # User drewp@bigasterisk.com # Date 2023-05-22 08:04:46 # Node ID 3db40ecf9e61c821d4c4b5080bcfc5d337437158 # Parent 615046dfe45fd8d9a34d7d411c221403fa3e3d0d refactor setAttrs diff --git a/light9/collector/collector.py b/light9/collector/collector.py --- a/light9/collector/collector.py +++ b/light9/collector/collector.py @@ -105,6 +105,48 @@ class Collector: end = typedValue(float, self.graph, remap, L9['end']) self.remapOut[(dev, attr)] = OutputRange((start, end)) + def setAttrs(self, client: ClientType, clientSession: ClientSessionType, settings: DeviceSettings, sendTime: UnixTime): + """ + Given DeviceSettings, we resolve conflicting values, + process them into output attrs, and call Output.update + to send the new outputs. + + client is a string naming the type of client. + (client, clientSession) is a unique client instance. + clientSession is deprecated. + + Each client session's last settings will be forgotten + after clientTimeoutSec. + """ + # todo: cleanup session code if we really don't want to be able to run multiple sessions of one client + clientSession = ClientSessionType("no_longer_used") + + now = UnixTime(time.time()) + self._warnOnLateRequests(client, now, sendTime) + + self._forgetStaleClients(now) + + self.lastRequest[(client, clientSession)] = (now, self._resolvedSettingsDict(settings)) + + deviceAttrs = self._merge(iter(self.lastRequest.values())) + + outputAttrsByDevice = self._convertToOutputAttrsPerDevice(deviceAttrs) + pendingOut = self._flattenDmxOutput(outputAttrsByDevice) + + dt1 = time.time() - now + + self._updateOutputs(pendingOut) + + dt2 = time.time() - dt1 + if dt1 > .030 or dt2 > .030: + log.warning("slow setAttrs: prepare %.1fms -> updateOutputs %.1fms" % (dt1 * 1000, dt2 * 1000)) + + def _warnOnLateRequests(self, client, now, sendTime): + requestLag = now - sendTime + if requestLag > .1 and now > self._initTime + 10 and getattr(self, '_lastWarnTime', 0) < now - 3: + self._lastWarnTime = now + log.warning('collector.setAttrs from %s is running %.1fms after the request was made', client, requestLag * 1000) + def _forgetStaleClients(self, now): staleClientSessions = [] for clientSession, (reqTime, _) in self.lastRequest.items(): @@ -125,12 +167,6 @@ class Collector: out[(devUri, devAttr)] = val return out - def _warnOnLateRequests(self, client, now, sendTime): - requestLag = now - sendTime - if requestLag > .1 and now > self._initTime + 10 and getattr(self, '_lastWarnTime', 0) < now - 3: - self._lastWarnTime = now - log.warn('collector.setAttrs from %s is running %.1fms after the request was made', client, requestLag * 1000) - def _merge(self, lastRequests): deviceAttrs: Dict[DeviceUri, Dict[DeviceAttr, VTUnion]] = {} # device: {deviceAttr: value} for _, lastSettings in lastRequests: @@ -157,51 +193,15 @@ class Collector: return deviceAttrs - def setAttrs(self, client: ClientType, clientSession: ClientSessionType, settings: DeviceSettings, sendTime: UnixTime): - """ - settings is a list of (device, attr, value). These attrs are - device attrs. We resolve conflicting values, process them into - output attrs, and call Output.update to send the new outputs. - - client is a string naming the type of client. (client, - clientSession) is a unique client instance. - - Each client session's last settings will be forgotten after - clientTimeoutSec. - """ - # todo: cleanup session code if we really don't want to be able to run multiple sessions of one client - clientSession = ClientSessionType("no_longer_used") - - now = UnixTime(time.time()) - self._warnOnLateRequests(client, now, sendTime) - - self._forgetStaleClients(now) - - self._acceptNewClientSessionSettings(client, clientSession, settings, now) - - deviceAttrs = self._merge(iter(self.lastRequest.values())) - - outputAttrs = cast(Dict[DeviceUri, Dict[OutputAttr, OutputValue]], {}) + def _convertToOutputAttrsPerDevice(self, deviceAttrs): + ret: Dict[DeviceUri, Dict[OutputAttr, OutputValue]] = {} for d, devType in self._deviceType.items(): try: - outputAttrs[d] = toOutputAttrs(devType, deviceAttrs.get(d, {})) - self.listeners.outputAttrsSet(d, outputAttrs[d], self._outputMap) + ret[d] = toOutputAttrs(devType, deviceAttrs.get(d, {})) + self.listeners.outputAttrsSet(d, ret[d], self._outputMap) except Exception as e: log.error('failing toOutputAttrs on %s: %r', d, e) - - pendingOut = self._flattenDmxOutput(outputAttrs) - - dt1 = 1000 * (time.time() - now) - - self._updateOutputs(pendingOut) - - dt2 = 1000 * (time.time() - now) - dt1 - if dt1 > 30 or dt2 > 30: - log.warn("slow setAttrs: prepare %.1fms -> updateOutputs %.1fms" % (dt1, dt2 - dt1)) - - def _acceptNewClientSessionSettings(self, client, clientSession, settings, now): - uniqueSettings = self.resolvedSettingsDict(settings) - self.lastRequest[(client, clientSession)] = (now, uniqueSettings) + return ret def _flattenDmxOutput(self, outputAttrs: Dict[DeviceUri, Dict[OutputAttr, OutputValue]]) -> Dict[OutputUri, bytearray]: pendingOut = cast(Dict[OutputUri, bytearray], {})