# HG changeset patch # User Drew Perttula # Date 2019-06-02 00:07:42 # Node ID 4718ca6f812e5a2150ed3b0ada46f77eb31efc6c # Parent 1a7e5b07bf17f36f7ec6706ccefeef24c57b480d autoformat Ignore-this: ec2538b5b3195c7c04f95c011dc89ff diff --git a/bin/collector b/bin/collector --- a/bin/collector +++ b/bin/collector @@ -42,9 +42,10 @@ class Updates(cyclone.websocket.WebSocke json.loads(message) -stats = scales.collection('/webServer', - scales.PmfStat('setAttr'), - scales.RecentFpsStat('setAttrFps'), +stats = scales.collection( + '/webServer', + scales.PmfStat('setAttr'), + scales.RecentFpsStat('setAttrFps'), ) @@ -64,7 +65,8 @@ def launch(graph, doLoadTest=False): try: # todo: drive outputs with config files outputs = [ - Udmx(L9['output/dmxA/'], bus=None, address=None, lastDmxChannel=221), + Udmx(L9['output/dmxA/'], bus=None, address=None, + lastDmxChannel=221), DummyOutput(L9['output/dmxB/']), ] except Exception: diff --git a/bin/effectsequencer b/bin/effectsequencer --- a/bin/effectsequencer +++ b/bin/effectsequencer @@ -38,8 +38,11 @@ class App(object): def launch(self, *args): self.seq = Sequencer( - self.graph, lambda settings: sendToCollector( - 'effectSequencer', self.session, settings, + self.graph, + lambda settings: sendToCollector( + 'effectSequencer', + self.session, + settings, # This seems to be safe here (and lets us get from # 20fpx to 40fpx), even though it leads to big stalls # if I use it on KC. diff --git a/bin/load_test_rdfdb b/bin/load_test_rdfdb --- a/bin/load_test_rdfdb +++ b/bin/load_test_rdfdb @@ -9,29 +9,32 @@ import time, logging from light9 import networking, showconfig from light9.namespaces import L9 + class BusyClient: + def __init__(self, subj, rate): self.subj = subj self.rate = rate - + self.graph = SyncedGraph(networking.rdfdb.url, "collector") self.graph.initiallySynced.addCallback(self.go) + def go(self, _): task.LoopingCall(self.loop).start(1 / self.rate) - def loop(self): self.graph.patchObject(showconfig.showUri() + '/loadTestContext', subject=self.subj, - predicate=L9['time'], - newObject=Literal(str(time.time()))) + predicate=L9['time'], + newObject=Literal(str(time.time()))) + def main(): log.setLevel(logging.INFO) clients = [BusyClient(L9['loadTest_%d' % i], 20) for i in range(10)] reactor.run() - + + if __name__ == "__main__": main() - diff --git a/light9/collector/collector_client.py b/light9/collector/collector_client.py --- a/light9/collector/collector_client.py +++ b/light9/collector/collector_client.py @@ -13,8 +13,8 @@ log = logging.getLogger('coll_client') stats = scales.collection( '/collectorClient/', scales.PmfStat('send'), +) - ) class TwistedZmqClient(object): @@ -26,6 +26,7 @@ class TwistedZmqClient(object): def send(self, msg): self.conn.push(msg) + def toCollectorJson(client, session, settings) -> str: assert isinstance(settings, DeviceSettings) return json.dumps({ @@ -44,7 +45,8 @@ def sendToCollectorZmq(msg): return defer.succeed(0.0) -def sendToCollector(client, session, settings: DeviceSettings, useZmq=False) -> defer.Deferred: +def sendToCollector(client, session, settings: DeviceSettings, + useZmq=False) -> defer.Deferred: """deferred to the time in seconds it took to get a response from collector""" sendTime = time.time() msg = toCollectorJson(client, session, settings).encode('utf8') diff --git a/light9/collector/device.py b/light9/collector/device.py --- a/light9/collector/device.py +++ b/light9/collector/device.py @@ -61,7 +61,7 @@ def resolve( # incomplete. how-to-resolve should be on the DeviceAttr defs in the graph. if deviceAttr in map( DeviceAttr, - [L9['rx'], L9['ry'], L9['zoom'], L9['focus'], L9['iris']]): + [L9['rx'], L9['ry'], L9['zoom'], L9['focus'], L9['iris']]): floatVals = [] for v in values: if isinstance(v, Literal): @@ -125,20 +125,29 @@ def untype_toOutputAttrs(deviceType, dev if deviceType == L9['ChauvetColorStrip']: r, g, b = rgbAttr(L9['color']) - return {L9['mode']: 215, - L9['red']: r, L9['green']: g, L9['blue']: b} + return {L9['mode']: 215, L9['red']: r, L9['green']: g, L9['blue']: b} elif deviceType == L9['Bar612601']: r, g, b = rgbAttr(L9['color']) return {L9['red']: r, L9['green']: g, L9['blue']: b} elif deviceType == L9['LedPar90']: r, g, b = rgbAttr(L9['color']) - return {L9['master']: 255, - L9['red']: r, L9['green']: g, L9['blue']: b, L9['white']: 0} + return { + L9['master']: 255, + L9['red']: r, + L9['green']: g, + L9['blue']: b, + L9['white']: 0 + } elif deviceType == L9['LedPar54']: r, g, b = rgbAttr(L9['color']) - return {L9['master']: 255, - L9['red']: r, L9['green']: g, L9['blue']: b, L9['white']: 0, - L9['strobe']: 0} + return { + L9['master']: 255, + L9['red']: r, + L9['green']: g, + L9['blue']: b, + L9['white']: 0, + L9['strobe']: 0 + } elif deviceType == L9['SimpleDimmer']: return {L9['level']: _8bit(floatAttr(L9['brightness']))} elif deviceType == L9['Mini15']: diff --git a/light9/collector/output.py b/light9/collector/output.py --- a/light9/collector/output.py +++ b/light9/collector/output.py @@ -47,6 +47,7 @@ class Output(object): _writeFail = scales.IntStat('write/fail') _writeCall = scales.PmfStat('write/call') _writeFps = scales.RecentFpsStat('write/fps') + def _write(self, buf: bytes) -> None: """ write buffer to output hardware (may be throttled if updates are diff --git a/light9/effect/sequencer.py b/light9/effect/sequencer.py --- a/light9/effect/sequencer.py +++ b/light9/effect/sequencer.py @@ -25,32 +25,30 @@ from greplin import scales import imp log = logging.getLogger('sequencer') -stats = scales.collection( - '/sequencer/', -) +stats = scales.collection('/sequencer/',) updateStats = scales.collection( '/update/', scales.PmfStat('s0_getMusic'), - scales.PmfStat('s1_eval'), + scales.PmfStat('s1_eval'), scales.PmfStat('s2_sendToWeb'), scales.PmfStat('s3_send'), scales.PmfStat('sendPhase'), - scales.PmfStat('updateLoopLatency'), scales.DoubleStat('updateLoopLatencyGoal'), scales.RecentFpsStat('updateFps'), scales.DoubleStat('goalFps'), - - ) +) compileStats = scales.collection( '/compile/', scales.PmfStat('graph'), scales.PmfStat('song'), -) +) + class Note(object): - def __init__(self, graph: SyncedGraph, uri: NoteUri, effectevalModule, simpleOutputs): + def __init__(self, graph: SyncedGraph, uri: NoteUri, effectevalModule, + simpleOutputs): g = self.graph = graph self.uri = uri self.effectEval = effectevalModule.EffectEval( @@ -101,7 +99,9 @@ class Note(object): y = p1[1] + (p2[1] - p1[1]) * frac return y - def outputSettings(self, t: float) -> Tuple[List[Tuple[DeviceUri, DeviceAttr, float]], Dict]: + def outputSettings( + self, + t: float) -> Tuple[List[Tuple[DeviceUri, DeviceAttr, float]], Dict]: """ list of (device, attr, value), and a report for web """ @@ -110,14 +110,18 @@ class Note(object): 'effectClass': self.effectEval.effect, } effectSettings: Dict[DeviceAttr, Union[float, str]] = dict( - (DeviceAttr(da), v.toPython()) for da, v in self.baseEffectSettings.items()) + (DeviceAttr(da), v.toPython()) + for da, v in self.baseEffectSettings.items()) effectSettings[L9['strength']] = self.evalCurve(t) + def prettyFormat(x: Union[float, str]): if isinstance(x, float): return round(x, 4) return x + report['effectSettings'] = dict( - (str(k), prettyFormat(v)) for k, v in sorted(effectSettings.items())) + (str(k), prettyFormat(v)) + for k, v in sorted(effectSettings.items())) report['nonZero'] = cast(float, effectSettings[L9['strength']]) > 0 out, evalReport = self.effectEval.outputFromEffect( list(effectSettings.items()), @@ -152,10 +156,11 @@ class CodeWatcher(object): class Sequencer(object): - def __init__(self, - graph: SyncedGraph, - sendToCollector: Callable[[DeviceSettings], defer.Deferred[float]], - fps=40): + def __init__( + self, + graph: SyncedGraph, + sendToCollector: Callable[[DeviceSettings], defer.Deferred[float]], + fps=40): self.graph = graph self.fps = fps updateStats.goalFps = self.fps @@ -178,8 +183,10 @@ class Sequencer(object): def compileGraph(self) -> None: """rebuild our data from the graph""" for song in self.graph.subjects(RDF.type, L9['Song']): + def compileSong(song: Song = cast(Song, song)) -> None: self.compileSong(song) + self.graph.addHandler(compileSong) @compileStats.song.time() @@ -194,13 +201,16 @@ class Sequencer(object): d = self.update() sendStarted = time.time() + def done(sec: float): took = time.time() - frameStart delay = max(0, 1 / self.fps - took) updateStats.updateLoopLatency = took # time to send to collector, reported by collector_client - if isinstance(sec, float): # sometimes None, not sure why, and neither is mypy + if isinstance( + sec, + float): # sometimes None, not sure why, and neither is mypy updateStats.s3_send = sec # time to send to collector, measured in this function, @@ -212,7 +222,7 @@ class Sequencer(object): def err(e): log.warn('updateLoop: %r', e) reactor.callLater(2, self.updateLoop) - + d.addCallbacks(done, err) @updateStats.updateFps.rate() @@ -220,14 +230,20 @@ class Sequencer(object): try: with updateStats.s0_getMusic.time(): musicState = self.music.getLatest() - if not musicState.get('song') or not isinstance(musicState.get('t'), float): + if not musicState.get('song') or not isinstance( + musicState.get('t'), float): return defer.succeed(0.0) song = Song(URIRef(musicState['song'])) - dispatcher.send('state', update={'song': str(song), 't': musicState['t']}) + dispatcher.send('state', + update={ + 'song': str(song), + 't': musicState['t'] + }) with updateStats.s1_eval.time(): settings = [] - songNotes = sorted(self.notes.get(song, []), key=lambda n: n.uri) + songNotes = sorted(self.notes.get(song, []), + key=lambda n: n.uri) noteReports = [] for note in songNotes: s, report = note.outputSettings(musicState['t']) @@ -237,7 +253,7 @@ class Sequencer(object): with updateStats.s2_sendToWeb.time(): dispatcher.send('state', update={'songNotes': noteReports}) - + return self.sendToCollector(devSettings) except Exception: traceback.print_exc() diff --git a/light9/subclient.py b/light9/subclient.py --- a/light9/subclient.py +++ b/light9/subclient.py @@ -41,10 +41,13 @@ class SubClient: except Exception: traceback.print_exc() return - return sendToCollector('subclient', self.session, outputSettings, - # when KC uses zmq, we get message - # pileups and delays on collector (even - # at 20fps). When sequencer uses zmp, - # it runs great at 40fps. Not sure the - # difference- maybe Tk main loop? - useZmq=False) + return sendToCollector( + 'subclient', + self.session, + outputSettings, + # when KC uses zmq, we get message + # pileups and delays on collector (even + # at 20fps). When sequencer uses zmp, + # it runs great at 40fps. Not sure the + # difference- maybe Tk main loop? + useZmq=False) diff --git a/light9/zmqtransport.py b/light9/zmqtransport.py --- a/light9/zmqtransport.py +++ b/light9/zmqtransport.py @@ -20,9 +20,10 @@ def parseJsonMessage(msg): def startZmq(port, collector): - stats = scales.collection('/zmqServer', - scales.PmfStat('setAttr'), - scales.RecentFpsStat('setAttrFps'), + stats = scales.collection( + '/zmqServer', + scales.PmfStat('setAttr'), + scales.RecentFpsStat('setAttrFps'), ) zf = ZmqFactory()