Changeset - ba24eeb2853a
[Not reviewed]
default
0 4 0
drewp@bigasterisk.com - 9 years ago 2016-06-12 19:07:00
drewp@bigasterisk.com
effects and config
Ignore-this: dccc8e21c4def7776ed23e975f7b3373
4 files changed with 46 insertions and 14 deletions:
0 comments (0 inline, 0 general)
bin/collector
Show inline comments
 
#!bin/python
 
from __future__ import division
 
from rdflib import Graph, URIRef, Literal
 
from twisted.internet import reactor
 
from twisted.web.server import Site
 
from txzmq import ZmqEndpoint, ZmqFactory, ZmqPullConnection
 
import json
 
import logging
 
import klein
 
import optparse
 
from greplin import scales
 
from greplin.scales.twistedweb import StatsResource
 

	
 
from run_local import log
 
from light9.collector.output import EnttecDmx, Udmx
 
from light9.collector.collector import Collector
 
from light9.namespaces import L9
 
from light9 import networking
 
from light9.rdfdb.syncedgraph import SyncedGraph
 
from light9.rdfdb import clientsession
 

	
 
class WebServer(object):
 
    stats = scales.collection('/webServer',
 
                              scales.PmfStat('setAttr'))
 
    app = klein.Klein()
 
    def __init__(self, collector):
 
        self.collector = collector
 
        
 
    @app.route('/attrs', methods=['PUT'])
 
    def putAttrs(self, request):
 
        with WebServer.stats.setAttr.time():
 
            body = json.load(request.content)
 
            settings = []
 
            for device, attr, value in body['settings']:
 
                settings.append((URIRef(device), URIRef(attr), Literal(value)))
 
            self.collector.setAttrs(body['client'],
 
                                    body['clientSession'],
 
                                    settings)
 
            request.setResponseCode(202)
 

	
 
    @app.route('/stats', methods=['GET'])
 
    def getStats(self, request):
 
        return StatsResource('collector')
 
        
 
def startZmq(port, collector):
 
    stats = scales.collection('/zmqServer',
 
                              scales.PmfStat('setAttr'))
 
    
 
    zf = ZmqFactory()
 
    e = ZmqEndpoint('bind', 'tcp://*:%s' % port)
 
    s = ZmqPullConnection(zf, e)
 
    def onPull(message):
 
        with stats.setAttrZmq.time():
 
            # todo: new compressed protocol where you send all URIs up
 
            # front and then use small ints to refer to devices and
 
            # attributes in subsequent requests.
 
            message[0]
 
            collector.setAttrs()
 
    s.onPull = onPull
 

	
 
def launch(graph):
 

	
 
    # todo: drive outputs with config files
 
    outputs = [
 
        EnttecDmx(L9['output/dmx0/'], '/dev/dmx3', 80),
 
        EnttecDmx(L9['output/dmx0/'], '/dev/dmx0', 80),
 
        Udmx(L9['output/udmx/'], 510),
 
    ]
 
    c = Collector(graph, outputs)
 

	
 
    server = WebServer(c)
 
    startZmq(networking.collectorZmq.port, c)
 
    
 
    reactor.listenTCP(networking.collector.port,
 
                      Site(server.app.resource()),
 
                      interface='::')
 
    log.info('serving http on %s, zmq on %s', networking.collector.port,
 
             networking.collectorZmq.port)
 
    
 
    
 
def main():
 
    parser = optparse.OptionParser()
 
    parser.add_option("-v", "--verbose", action="store_true",
 
                      help="logging.DEBUG")
 
    (options, args) = parser.parse_args()
 
    log.setLevel(logging.DEBUG if options.verbose else logging.INFO)
 

	
 
    graph = SyncedGraph(networking.rdfdb.url, "collector")
 

	
 
    graph.initiallySynced.addCallback(lambda _: launch(graph))
 
    reactor.run()
 

	
 
if __name__ == '__main__':
 
    main()
light9/effect/effecteval.py
Show inline comments
 
@@ -20,134 +20,165 @@ def noise(t):
 

	
 
def scale(value, strength):
 
    if isinstance(value, Literal):
 
        value = value.toPython()
 

	
 
    if isinstance(value, Decimal):
 
        value = float(value)
 
        
 
    if isinstance(value, basestring):
 
        if value[0] == '#':
 
            r,g,b = hex_to_rgb(value)
 
            return rgb_to_hex([r * strength, g * strength, b * strength])
 
    elif isinstance(value, (int, float)):
 
        return value * strength
 
    else:
 
        raise NotImplementedError(repr(value))
 
    
 
class EffectEval(object):
 
    """
 
    runs one effect's code to turn effect attr settings into output
 
    device settings. No state; suitable for reload().
 
    """
 
    def __init__(self, graph, effect):
 
        self.graph = graph
 
        self.effect = effect 
 

	
 
        # effect : [(dev, attr, value, isScaled)]
 
        self.effectOutputs = {}
 
        
 
        self.graph.addHandler(self.updateEffectsFromGraph)
 

	
 
    def updateEffectsFromGraph(self):
 
        self.effectOutputs = {}
 
        for effect in self.graph.subjects(RDF.type, L9['Effect']):
 
            settings = []
 
            for setting in self.graph.objects(effect, L9['setting']):
 
                d = self.graph.value(setting, L9['device'])
 
                a = self.graph.value(setting, L9['deviceAttr'])
 
                v = self.graph.value(setting, L9['value'])
 
                sv = self.graph.value(setting, L9['scaledValue'])
 
                if not (bool(v) ^ bool(sv)):
 
                    raise NotImplementedError
 

	
 
                settings.append((d, a, v if v is not None else sv, bool(sv)))
 

	
 
            if settings:
 
                self.effectOutputs[effect] = settings
 
            # also have to read eff :effectAttr [ :tint x; :tintStrength y ]
 
        
 
    def outputFromEffect(self, effectSettings, songTime):
 
        """
 
        From effect attr settings, like strength=0.75, to output device
 
        settings like light1/bright=0.72;light2/bright=0.78. This runs
 
        the effect code.
 
        """
 
        # both callers need to apply note overrides
 
        effectSettings = dict(effectSettings) # we should make everything into nice float and Color objects too
 

	
 
        strength = float(effectSettings[L9['strength']])
 
        if strength <= 0:
 
            return []
 

	
 
        out = {} # (dev, attr): value
 

	
 
        out.update(self.simpleOutput(strength))
 

	
 
        if self.effect.startswith(L9['effect/']):
 
            tail = 'effect_' + self.effect[len(L9['effect/']):]
 
            try:
 
                func = globals()[tail]
 
            except KeyError:
 
                pass
 
            else:
 
                out.update(func(effectSettings, strength, songTime))
 

	
 
        # todo: callers should prefer the dict form too
 
        outList = [(d, a, v) for (d, a), v in out.iteritems()]
 
        outList.sort()
 
        #import pprint; pprint.pprint(outList, width=170)
 
        return outList
 
                            
 
    def simpleOutput(self, strength):
 
        out = {}
 
        for dev, attr, value, isScaled in self.effectOutputs.get(self.effect, []):
 
            if isScaled:
 
                value = scale(value, strength)
 
            out[(dev, attr)] = value
 
        return out
 
        
 

	
 

	
 
    
 

	
 
def effect_Curtain(effectSettings, strength, songTime):
 
    return {
 
        (L9['device/lowPattern%s' % n], L9['color']):
 
        literalColor(0*strength, strength, strength)
 
        literalColor(strength, strength, strength)
 
        for n in range(301,308+1)
 
        }
 
    
 
def effect_animRainbow(effectSettings, strength, songTime):
 
    out = {}
 
    tint = effectSettings.get(L9['tint'], '#ffffff')
 
    tintStrength = float(effectSettings.get(L9['tintStrength'], 0))
 
    print tint, tintStrength
 
    tr, tg, tb = hex_to_rgb(tint)
 
    for n in range(1, 5+1):
 
        scl = strength * nsin(songTime + n * .3)**3
 
        col = literalColor(
 
            scl * lerp(nsin(songTime + n * .2), tr/255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .2 + .3), tg/255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .3 + .6), tb/255, tintStrength))
 

	
 
        dev = L9['device/aura%s' % n]
 
        out.update({
 
            (dev, L9['color']): col,
 
            (dev, L9['zoom']): .9,
 
            })
 
        ang = songTime * 4
 
        out.update({
 
        (dev, L9['rx']): lerp(.27, .7, (n-1)/4) + .2 * math.sin(ang+n),
 
        (dev, L9['ry']): lerp(.46, .52, (n-1)/4) + .5 * math.cos(ang+n),
 
            })
 
    return out
 

	
 
def effect_orangeSearch(effectSettings, strength, songTime):
 
    dev = L9['device/auraStage']
 
    return {(dev, L9['color']): '#c1905d',
 
            (dev, L9['rx']): lerp(.31, .68, nsquare(songTime / 2.0)),
 
            (dev, L9['ry']): lerp(.32, .4,  nsin(songTime / 5)),
 
            (dev, L9['zoom']): .88,
 
            }
 
    
 
    tint = effectSettings.get(L9['tint'], '#ffffff')
 
    tintStrength = float(effectSettings.get(L9['tintStrength'], 0))
 
    print tint, tintStrength
 
    tr, tg, tb = hex_to_rgb(tint)
 
    for n in range(1, 5+1):
 
        scl = strength * nsin(songTime + n * .3)**3
 
        col = literalColor(
 
            scl * lerp(nsin(songTime + n * .2), tr/255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .2 + .3), tg/255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .3 + .6), tb/255, tintStrength))
 

	
 
        dev = L9['device/aura%s' % n]
 
        out.update({
 
            (dev, L9['color']): col,
 
            (dev, L9['zoom']): .9,
 
            })
 
        ang = songTime * 4
 
        out.update({
 
        (dev, L9['rx']): lerp(.27, .7, (n-1)/4) + .2 * math.sin(ang+n),
 
        (dev, L9['ry']): lerp(.46, .52, (n-1)/4) + .5 * math.cos(ang+n),
 
            })
 
    return out
 

	
 
def effect_Strobe(effectSettings, strength, songTime):
 
    rate = 2
 
    duty = .3
 
    offset = 0
 
    f = (((songTime + offset) * rate) % 1.0)
 
    c = (f < duty) * strength
 
    col = rgb_to_hex([c * 255, c * 255, c * 255])
 
    return {(L9['device/colorStrip'], L9['color']): Literal(col)}
 

	
show/dance2016/effect.n3
Show inline comments
 
@prefix : <http://light9.bigasterisk.com/> .
 
@prefix dev: <http://light9.bigasterisk.com/device/> .
 
@prefix effect: <http://light9.bigasterisk.com/effect/> .
 
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
 
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
 

	
 

	
 

	
 
#effect:usa a :mockupEffect; rdfs:label "USA"; :chaseOffset 0;
 
#     :chaseTempo 120; :code "chase()"; :devices dev:colorStrip, dev:moving1;
 
#     :fadeShape :fadeCurve1; :palette "#0000ff", "#ff0000", "#ffffff";
 
#     :publishAttr :strength .
 

	
 
effect:Strobe a :Effect; rdfs:label "strobe"; :publishAttr :duty, :offset, :rate, :strength .
 

	
 

	
 
effect:animRainbow a :Effect; rdfs:label "animRainbow"; :publishAttr :strength, :rate .
 

	
 

	
 
effect:animRainbow a :Effect; rdfs:label "animRainbow"; :publishAttr :strength, :rate ; :group "anim".
 

	
 
effect:usa a :mockupEffect; rdfs:label "USA"; :chaseOffset 0;
 
     :chaseTempo 120; :code "chase()"; :devices dev:colorStrip, dev:moving1;
 
     :fadeShape :fadeCurve1; :palette "#0000ff", "#ff0000", "#ffffff";
 
     :publishAttr :strength .
 
effect:orangeSearch a :Effect; :publishAttr :strength ; :group "anim".
 

	
 
effect:house :group "main" .
 
effect:cyc :group "main" .
 

	
 
effect:centerSpot a :Effect; rdfs:label "center spot";
 
     :group "main"; :order 3; :publishAttr :strength;
 
     :setting effect:centerSpots0, effect:centerSpots1, effect:centerSpots2, effect:centerSpots3, effect:centerSpots4 .
 
effect:centerSpots0 :device dev:q2; :deviceAttr :color;
 
     :scaledValue "#ffffff" .
 
effect:centerSpots1 :device dev:q2; :deviceAttr :focus;
 
     :value 0.31 .
 
effect:centerSpots2 :device dev:q2; :deviceAttr :rx;
 
     :value 0.50 .
 
effect:centerSpots3 :device dev:q2; :deviceAttr :ry;
 
     :value 0.26 .
 
effect:centerSpots4 :device dev:q2; :deviceAttr :zoom;
 
     :value 0.42 .
 

	
 

	
 
:fadeCurve1 a :Curve; :point :fc1p0, :fc1p1, :fc1p2, :fc1p3 .
 
:fc1p0 :time 0.00; :value 0 .
 
:fc1p1 :time 0.02; :value 1 .
 
:fc1p2 :time 0.10; :value 1 .
 
:fc1p3 :time 0.15; :value 0 .
 
:strength rdfs:label "strength" .
show/dance2016/song1.n3
Show inline comments
 
@prefix : <http://light9.bigasterisk.com/> .
 
@prefix dev: <http://light9.bigasterisk.com/device/> .
 
@prefix effect: <http://light9.bigasterisk.com/effect/> .
 
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
 
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
 
@prefix song: <http://light9.bigasterisk.com/show/dance2016/song1/> .
 
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
 
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
 

	
 
:a01 :effectAttr :chaseOffset; :value 0.12 .
 
:ao0 :effectAttr :chaseTempo; :value 100 .
 
<http://light9.bigasterisk.com/show/dance2016/song1> :note song:n0, song:n3 .
 

	
 
song:n0 a :Note; :curve song:n0c0; :effectClass effect:centerSpot;
 
     :originTime 30.398 .
 
     :originTime 30.836 .
 

	
 
song:n0c0 a :Curve; :attr :strength; :point song:n0c0p0, song:n0c0p1, song:n0c0p2, song:n0c0p3 .
 
song:n0c0p0 :time 0.000; :value 0.000 .
 
song:n0c0p1 :time 1.000; :value 1.000 .
 
song:n0c0p2 :time 2.000; :value 1.000 .
 
song:n0c0p3 :time 3.000; :value 0.000 .
 
song:n0c0p2 :time 4.170; :value 1.000 .
 
song:n0c0p3 :time 5.772; :value 0.000 .
 

	
 
song:n3 a :Note; :curve song:n3c0; :effectClass effect:Curtain;
 
     :originTime 32.297 .
 
     :originTime 32.932 .
 

	
 
song:n3c0 a :Curve; :attr :strength; :point song:n3c0p0, song:n3c0p1, song:n3c0p2, song:n3c0p3 .
 
song:n3c0p0 :time 0.153; :value 0.000 .
 
song:n3c0p1 :time 1.000; :value 1.000 .
 
song:n3c0p2 :time 2.000; :value 1.000 .
 
song:n3c0p3 :time 2.928; :value 0.000 .
 
song:n3c0p3 :time 3.236; :value 0.000 .
0 comments (0 inline, 0 general)