view light9/effect/sequencer/eval_faders.py @ 2306:3b48a6e6a3c9

just skip a fader with no value (it's probably coming soon)
author drewp@bigasterisk.com
date Wed, 31 May 2023 02:16:22 -0700
parents 972a54f99b2c
children 54cf7034bee0
line wrap: on
line source

import logging
import time
from dataclasses import dataclass
from typing import List, Optional, cast

from prometheus_client import Summary
from rdfdb import SyncedGraph
from rdflib import URIRef
from rdflib.term import Node

from light9.effect.effect_function_library import EffectFunctionLibrary
from light9.effect.effecteval2 import EffectEval2
from light9.effect.settings import DeviceSettings, EffectSettings
from light9.namespaces import L9, RDF
from light9.newtypes import EffectAttr, EffectUri, UnixTime
from light9.typedgraph import typedValue

log = logging.getLogger('seq.fader')

COMPILE = Summary('compile_graph_fader', '')


@dataclass
class Fader:
    graph: SyncedGraph
    lib: EffectFunctionLibrary
    uri: URIRef
    effect: EffectUri
    setEffectAttr: EffectAttr

    value: Optional[float] = None  # mutable

    def __post_init__(self):
        self.ee = EffectEval2(self.graph, self.effect, self.lib)


class FaderEval:
    """peer to Sequencer, but this one takes the current :Fader settings -> sendToCollector

    """

    def __init__(self, graph: SyncedGraph, lib: EffectFunctionLibrary):
        self.graph = graph
        self.lib = lib
        self.faders: List[Fader] = []

        self.graph.addHandler(self._compile)
        self.lastLoopSucceeded = False

    @COMPILE.time()
    def _compile(self) -> None:
        """rebuild our data from the graph"""
        self.faders = []
        for fader in self.graph.subjects(RDF.type, L9['Fader']):
            effect = typedValue(EffectUri, self.graph, fader, L9['effect'])
            setting = typedValue(Node, self.graph, fader, L9['setting'])
            setAttr = typedValue(EffectAttr, self.graph, setting, L9['effectAttr'])
            self.faders.append(Fader(self.graph, self.lib, cast(URIRef, fader), effect, setAttr))

        # this could go in a second, smaller addHandler call to avoid rebuilding Fader objs constantly
        for f in self.faders:
            setting = typedValue(Node, self.graph, f.uri, L9['setting'])
            f.value = typedValue(float, self.graph, setting, L9['value'])

    def computeOutput(self) -> DeviceSettings:
        faderEffectOutputs: List[DeviceSettings] = []
        now = UnixTime(time.time())
        for f in self.faders:
            try:
                if f.value is None:
                    log.warning(f'{f.value=}; should be set during _compile. Skipping {f.uri}')
                    continue
                effectSettings = EffectSettings(self.graph, [(f.effect, f.setEffectAttr, f.value)])

                ds = f.ee.compute(now, effectSettings)
                faderEffectOutputs.append(ds)
            except Exception:
                log.warning(f'on fader {f}')
                raise

        return DeviceSettings.merge(self.graph, faderEffectOutputs)