changeset 2186:04612ba3fe45

refactor
author drewp@bigasterisk.com
date Fri, 19 May 2023 20:55:28 -0700
parents bf07831a5339
children ccdfdc8183ad
files light9/effect/sequencer/__init__.py light9/effect/sequencer/eval_faders.py light9/effect/sequencer/note.py light9/effect/sequencer/sequencer.py light9/effect/sequencer/service.py light9/effect/sequencer/service_test.py light9/newtypes.py light9/rdfdb/service_test.py
diffstat 8 files changed, 153 insertions(+), 128 deletions(-) [+]
line wrap: on
line diff
--- a/light9/effect/sequencer/__init__.py	Fri May 19 18:20:19 2023 -0700
+++ b/light9/effect/sequencer/__init__.py	Fri May 19 20:55:28 2023 -0700
@@ -0,0 +1,1 @@
+from .note import Note
--- a/light9/effect/sequencer/eval_faders.py	Fri May 19 18:20:19 2023 -0700
+++ b/light9/effect/sequencer/eval_faders.py	Fri May 19 20:55:28 2023 -0700
@@ -3,12 +3,12 @@
 import logging
 import time
 from typing import Callable, Coroutine, List, cast
-from light9.effect.sequencer.sequencer import Note
 
 from rdfdb.syncedgraph.syncedgraph import SyncedGraph
 from rdflib import URIRef
 
 from light9.effect import effecteval
+from light9.effect.sequencer import Note
 from light9.effect.settings import DeviceSettings
 from light9.effect.simple_outputs import SimpleOutputs
 from light9.metrics import metrics
@@ -16,6 +16,7 @@
 from light9.newtypes import NoteUri
 
 log = logging.getLogger('sequencer')
+
 class FaderEval:
     """peer to Sequencer, but this one takes the current :Fader settings -> sendToCollector
     
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/effect/sequencer/note.py	Fri May 19 20:55:28 2023 -0700
@@ -0,0 +1,116 @@
+import bisect
+import logging
+import time
+from decimal import Decimal
+from typing import Any, Dict, List, Optional, Tuple, Union, cast
+
+from rdfdb.syncedgraph.syncedgraph import SyncedGraph
+from rdflib import Literal, URIRef
+
+from light9.namespaces import L9
+from light9.newtypes import Curve, DeviceAttr, DeviceUri, NoteUri, typedValue
+
+log = logging.getLogger('sequencer')
+
+
+def pyType(n):
+    ret = n.toPython()
+    if isinstance(ret, Decimal):
+        return float(ret)
+    return ret
+
+
+class Note(object):
+
+    def __init__(self, graph: SyncedGraph, uri: NoteUri, effectevalModule, simpleOutputs, timed=True):
+        g = self.graph = graph
+        self.uri = uri
+        self.timed = timed
+        self.effectEval = effectevalModule.EffectEval(graph, g.value(uri, L9['effectClass']), simpleOutputs)
+        self.baseEffectSettings: Dict[URIRef, Any] = {}  # {effectAttr: value}
+        for s in g.objects(uri, L9['setting']):
+            settingValues = dict(g.predicate_objects(s))
+            ea = cast(URIRef, settingValues[L9['effectAttr']])
+            self.baseEffectSettings[ea] = pyType(settingValues[L9['value']])
+
+        if timed:
+
+            def floatVal(s, p):
+                return typedValue(float, g, s, p)
+
+            originTime = floatVal(uri, L9['originTime'])
+            self.points: List[Tuple[float, float]] = []
+            for curve in g.objects(uri, L9['curve']):
+                self.points.extend(self.getCurvePoints(cast(Curve, curve), L9['strength'], originTime))
+            self.points.sort()
+        else:
+            self.points = []
+
+    def getCurvePoints(self, curve: Curve, attr, originTime: float) -> List[Tuple[float, float]]:
+        points = []
+        po = list(self.graph.predicate_objects(curve))
+        if dict(po).get(L9['attr'], None) != attr:
+            return []
+        for point in [row[1] for row in po if row[0] == L9['point']]:
+            po2 = dict(self.graph.predicate_objects(point))
+            t = cast(Literal, po2[L9['time']]).toPython()
+            if not isinstance(t, float):
+                raise TypeError
+            
+            v = cast(Literal, po2[L9['value']]).toPython()
+            if not isinstance(v, float):
+                raise TypeError
+            points.append((originTime + t, v))
+        return points
+
+    def activeAt(self, t: float) -> bool:
+        return self.points[0][0] <= t <= self.points[-1][0]
+
+    def evalCurve(self, t: float) -> float:
+        i = bisect.bisect_left(self.points, (t, None)) - 1
+
+        if i == -1:
+            return self.points[0][1]
+        if self.points[i][0] > t:
+            return self.points[i][1]
+        if i >= len(self.points) - 1:
+            return self.points[i][1]
+
+        p1, p2 = self.points[i], self.points[i + 1]
+        frac = (t - p1[0]) / (p2[0] - p1[0])
+        y = p1[1] + (p2[1] - p1[1]) * frac
+        return y
+
+    def outputSettings(self, t: float, strength: Optional[float] = None) -> Tuple[List[Tuple[DeviceUri, DeviceAttr, float]], Dict]:
+        """
+        list of (device, attr, value), and a report for web
+        """
+        if t is None:
+            if self.timed:
+                raise TypeError()
+            t = time.time()  # so live effects will move
+        report = {
+            'note': str(self.uri),
+            'effectClass': self.effectEval.effect,
+        }
+
+        strengthAttr = cast(DeviceAttr, L9['strength'])
+
+        effectSettings: Dict[DeviceAttr, Union[float, str]] = dict((DeviceAttr(da), v) for da, v in self.baseEffectSettings.items())
+        effectSettings[strengthAttr] = self.evalCurve(t) if strength is None else strength
+
+        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()))
+        report['nonZero'] = cast(float, effectSettings[strengthAttr]) > 0
+        startTime = self.points[0][0] if self.timed else 0
+        out, evalReport = self.effectEval.outputFromEffect(
+            list(effectSettings.items()),
+            songTime=t,
+            # note: not using origin here since it's going away
+            noteTime=t - startTime)
+        report['devicesAffected'] = len(out.devices())
+        return out, report
--- a/light9/effect/sequencer/sequencer.py	Fri May 19 18:20:19 2023 -0700
+++ b/light9/effect/sequencer/sequencer.py	Fri May 19 20:55:28 2023 -0700
@@ -3,28 +3,27 @@
 '''
 
 import asyncio
-from louie import dispatcher,All
+import imp
+import logging
+import time
+import traceback
+from typing import Callable, Coroutine, Dict, List, cast
+
+from louie import All, dispatcher
+from rdfdb.syncedgraph.syncedgraph import SyncedGraph
 from rdflib import URIRef
 from twisted.internet import reactor
-from twisted.internet import defer
-from twisted.internet.defer import Deferred, inlineCallbacks
 from twisted.internet.inotify import INotify
 from twisted.python.filepath import FilePath
-import logging, bisect, time
-import traceback
-from decimal import Decimal
-from typing import Any, Callable, Coroutine, Dict, List, Optional, Tuple, cast, Union
 
 from light9.ascoltami.musictime_client import MusicTime
 from light9.effect import effecteval
+from light9.effect.sequencer import Note
 from light9.effect.settings import DeviceSettings
 from light9.effect.simple_outputs import SimpleOutputs
+from light9.metrics import metrics
 from light9.namespaces import L9, RDF
-from light9.newtypes import DeviceUri, DeviceAttr, NoteUri, Curve, Song
-from rdfdb.syncedgraph.syncedgraph import SyncedGraph
-from light9.metrics import metrics
-
-import imp
+from light9.newtypes import NoteUri, Song
 
 log = logging.getLogger('sequencer')
 
@@ -32,112 +31,6 @@
 class StateUpdate(All):
     pass
 
-def pyType(n):
-    ret = n.toPython()
-    if isinstance(ret, Decimal):
-        return float(ret)
-    return ret
-
-
-class Note(object):
-
-    def __init__(self, graph: SyncedGraph, uri: NoteUri, effectevalModule,
-                 simpleOutputs, timed=True):
-        g = self.graph = graph
-        self.uri = uri
-        self.timed= timed
-        self.effectEval = effectevalModule.EffectEval(
-            graph, g.value(uri, L9['effectClass']), simpleOutputs)
-        self.baseEffectSettings: Dict[URIRef, Any] = {}  # {effectAttr: value}
-        for s in g.objects(uri, L9['setting']):
-            settingValues = dict(g.predicate_objects(s))
-            ea = settingValues[L9['effectAttr']]
-            self.baseEffectSettings[ea] = pyType(settingValues[L9['value']])
-
-
-        if timed:
-            def floatVal(s, p):
-                return float(g.value(s, p).toPython())
-
-            originTime = floatVal(uri, L9['originTime'])
-            self.points: List[Tuple[float, float]] = []
-            for curve in g.objects(uri, L9['curve']):
-                self.points.extend(
-                    self.getCurvePoints(cast(Curve, curve), L9['strength'], originTime))
-            self.points.sort()
-        else:
-            self.points = []
-
-    def getCurvePoints(self, curve: Curve, attr,
-                       originTime: float) -> List[Tuple[float, float]]:
-        points = []
-        po = list(self.graph.predicate_objects(curve))
-        if dict(po).get(L9['attr'], None) != attr:
-            return []
-        for point in [row[1] for row in po if row[0] == L9['point']]:
-            po2 = dict(self.graph.predicate_objects(point))
-            points.append(
-                (originTime + float(po2[L9['time']]), float(po2[L9['value']])))
-        return points
-
-    def activeAt(self, t: float) -> bool:
-        return self.points[0][0] <= t <= self.points[-1][0]
-
-    def evalCurve(self, t: float) -> float:
-        i = bisect.bisect_left(self.points, (t, None)) - 1
-
-        if i == -1:
-            return self.points[0][1]
-        if self.points[i][0] > t:
-            return self.points[i][1]
-        if i >= len(self.points) - 1:
-            return self.points[i][1]
-
-        p1, p2 = self.points[i], self.points[i + 1]
-        frac = (t - p1[0]) / (p2[0] - p1[0])
-        y = p1[1] + (p2[1] - p1[1]) * frac
-        return y
-
-    def outputSettings(
-            self,
-            t: float, strength: Optional[float] = None
-            ) -> Tuple[List[Tuple[DeviceUri, DeviceAttr, float]], Dict]:
-        """
-        list of (device, attr, value), and a report for web
-        """
-        if t is None:
-            if self.timed:
-                raise TypeError()
-            t = time.time() # so live effects will move
-        report = {
-            'note': str(self.uri),
-            'effectClass': self.effectEval.effect,
-        }
-
-        strengthAttr=cast(DeviceAttr, L9['strength'])
-
-        effectSettings: Dict[DeviceAttr, Union[float, str]] = dict(
-            (DeviceAttr(da), v) for da, v in self.baseEffectSettings.items())
-        effectSettings[strengthAttr] = self.evalCurve(t) if strength is None else strength
-
-        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()))
-        report['nonZero'] = cast(float, effectSettings[strengthAttr]) > 0
-        startTime = self.points[0][0] if self.timed else 0
-        out, evalReport = self.effectEval.outputFromEffect(
-            list(effectSettings.items()),
-            songTime=t,
-            # note: not using origin here since it's going away
-            noteTime=t - startTime)
-        report['devicesAffected'] = len(out.devices())
-        return out, report
-
 
 class CodeWatcher(object):
 
@@ -258,7 +151,7 @@
 
         with metrics('update_s1_eval').time():
             settings = []
-            songNotes = sorted(self.notes.get(song, []), key=lambda n: n.uri)
+            songNotes = sorted(cast(List[Note], self.notes.get(song, [])), key=lambda n: n.uri)
             noteReports = []
             for note in songNotes:
                 try:
--- a/light9/effect/sequencer/service.py	Fri May 19 18:20:19 2023 -0700
+++ b/light9/effect/sequencer/service.py	Fri May 19 20:55:28 2023 -0700
@@ -7,6 +7,13 @@
 import logging
 import time
 
+from louie import dispatcher
+from rdfdb.syncedgraph.syncedgraph import SyncedGraph
+from sse_starlette.sse import EventSourceResponse
+from starlette.applications import Starlette
+from starlette.routing import Route
+from starlette_exporter import PrometheusMiddleware, handle_metrics
+
 from light9 import networking
 from light9.collector.collector_client_asyncio import sendToCollector
 from light9.effect.sequencer.eval_faders import FaderEval
@@ -14,13 +21,6 @@
 from light9.effect.settings import DeviceSettings
 from light9.metrics import metrics
 from light9.run_local import log
-from louie import dispatcher
-from rdfdb.syncedgraph.syncedgraph import SyncedGraph
-from sse_starlette.sse import EventSourceResponse
-from starlette.applications import Starlette
-from starlette.routing import Route
-from starlette.types import Receive, Scope, Send
-from starlette_exporter import PrometheusMiddleware, handle_metrics
 
 
 async def changes():
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/effect/sequencer/service_test.py	Fri May 19 20:55:28 2023 -0700
@@ -0,0 +1,13 @@
+
+import asyncio
+from light9.run_local import log
+
+
+def test_import():
+
+    async def go():
+        # this sets up some watcher tasks
+        from light9.effect.sequencer.service import app
+        print(app)
+
+    asyncio.run(go(), debug=True)
\ No newline at end of file
--- a/light9/newtypes.py	Fri May 19 18:20:19 2023 -0700
+++ b/light9/newtypes.py	Fri May 19 20:55:28 2023 -0700
@@ -45,4 +45,5 @@
     if obj is None:
         raise ValueError()
     conv = obj if _isSubclass2(objType, Node) else obj.toPython()
+    # may need to turn Decimal to float here
     return cast(objType, conv)
\ No newline at end of file
--- a/light9/rdfdb/service_test.py	Fri May 19 18:20:19 2023 -0700
+++ b/light9/rdfdb/service_test.py	Fri May 19 20:55:28 2023 -0700
@@ -6,6 +6,6 @@
 
     async def go():
         # this sets up some watcher tasks
-        from service import app
+        from light9.rdfdb.service import app
 
     asyncio.run(go(), debug=True)