diff --git a/bin/collector b/bin/collector
--- a/bin/collector
+++ b/bin/collector
@@ -61,8 +61,8 @@ def startZmq(port, collector):
def launch(graph):
# todo: drive outputs with config files
- outputs = [EnttecDmx(L9['output/dmx0/'], 100, '/dev/dmx0'),
- Udmx(L9['output/udmx/'], 100)]
+ outputs = [EnttecDmx(L9['output/dmx0/'], '/dev/dmx0'),
+ Udmx(L9['output/udmx/'])]
c = Collector(graph, outputs)
server = WebServer(c)
diff --git a/light9/collector/collector.py b/light9/collector/collector.py
--- a/light9/collector/collector.py
+++ b/light9/collector/collector.py
@@ -9,35 +9,25 @@ log = logging.getLogger('collector')
def outputMap(graph, outputs):
"""From rdf config graph, compute a map of
- (device, attr) : (output, index)
+ (device, outputattr) : (output, index)
that explains which output index to set for any device update.
"""
ret = {}
- outIndex = {} # port : (output, index)
+ outputByUri = {} # universeUri : output
for out in outputs:
- for index, uri in out.allConnections():
- outIndex[uri] = (out, index)
+ outputByUri[out.uri] = out
- for dev in graph.subjects(RDF.type, L9['Device']):
- for attr, connectedTo in graph.predicate_objects(dev):
- if attr == RDF.type:
- continue
- outputPorts = list(graph.subjects(L9['connectedTo'], connectedTo))
- if len(outputPorts) == 0:
- raise ValueError('no output port :connectedTo %r' % connectedTo)
- elif len(outputPorts) > 1:
- raise ValueError('multiple output ports (%r) :connectedTo %r' %
- (outputPorts, connectedTo))
- else:
- try:
- output, index = outIndex[outputPorts[0]]
- except KeyError:
- log.warn('skipping %r', outputPorts[0])
- continue
- ret[(dev, attr)] = output, index
- log.debug('outputMap (%r, %r) -> %r, %r', dev, attr, output, index)
-
+ for dc in graph.subjects(RDF.type, L9['DeviceClass']):
+ for dev in graph.subjects(RDF.type, dc):
+ output = outputByUri[graph.value(dev, L9['dmxUniverse'])]
+ dmxBase = int(graph.value(dev, L9['dmxBase']).toPython())
+ for row in graph.objects(dc, L9['attr']):
+ outputAttr = graph.value(row, L9['outputAttr'])
+ offset = int(graph.value(row, L9['dmxOffset']).toPython())
+ index = dmxBase + offset - 1
+ ret[(dev, outputAttr)] = (output, index)
+ log.info('map %s,%s to %s,%s', dev, outputAttr, output, index)
return ret
class Collector(object):
@@ -52,10 +42,9 @@ class Collector(object):
def rebuildOutputMap(self):
self.outputMap = outputMap(self.graph, self.outputs) # (device, attr) : (output, index)
self.deviceType = {} # uri: type that's a subclass of Device
- for dev in self.graph.subjects(RDF.type, L9['Device']):
- for t in self.graph.objects(dev, RDF.type):
- if t != L9['Device']:
- self.deviceType[dev] = t
+ for dc in self.graph.subjects(RDF.type, L9['DeviceClass']):
+ for dev in self.graph.subjects(RDF.type, dc):
+ self.deviceType[dev] = dc
def _forgetStaleClients(self, now):
staleClients = []
@@ -97,27 +86,33 @@ class Collector(object):
self.lastRequest[client] = (clientSession, now, prevClientSettings)
- deviceAttrs = {} # device: {attr: value}
+ deviceAttrs = {} # device: {deviceAttr: value}
for _, _, lastSettings in self.lastRequest.itervalues():
- for (device, attr), value in lastSettings.iteritems():
+ for (device, deviceAttr), value in lastSettings.iteritems():
attrs = deviceAttrs.setdefault(device, {})
- if attr in attrs:
- value = resolve(device, attr, [attrs[attr], value])
- attrs[attr] = value
+ if deviceAttr in attrs:
+ value = resolve(device, deviceAttr, [attrs[deviceAttr],
+ value])
+ attrs[deviceAttr] = value
- outputAttrs = {} # device: {attr: value}
+ outputAttrs = {} # device: {outputAttr: value}
for d in deviceAttrs:
- outputAttrs[d] = toOutputAttrs(self.deviceType[d], deviceAttrs[d])
+ try:
+ devType = self.deviceType[d]
+ except KeyError:
+ log.warn("request for output to unconfigured device %s" % d)
+ continue
+ outputAttrs[d] = toOutputAttrs(devType, deviceAttrs[d])
pendingOut = {} # output : values
for device, attrs in outputAttrs.iteritems():
- for attr, value in attrs.iteritems():
- self.setAttr(device, attr, value, pendingOut)
+ for outputAttr, value in attrs.iteritems():
+ self.setAttr(device, outputAttr, value, pendingOut)
self.flush(pendingOut)
- def setAttr(self, device, attr, value, pendingOut):
- output, index = self.outputMap[(device, attr)]
+ def setAttr(self, device, outputAttr, value, pendingOut):
+ output, index = self.outputMap[(device, outputAttr)]
outList = pendingOut.setdefault(output, [])
setListElem(outList, index, value, combine=max)
diff --git a/light9/collector/device.py b/light9/collector/device.py
--- a/light9/collector/device.py
+++ b/light9/collector/device.py
@@ -47,9 +47,9 @@ def resolve(deviceType, deviceAttr, valu
def toOutputAttrs(deviceType, deviceAttrSettings):
"""
- Given settings like {L9['color']: Literal('#ff0000')}, return a
- similar dict where the keys are output attrs and the values are
- suitable for Collector.setAttr
+ Given device attr settings like {L9['color']: Literal('#ff0000')},
+ return a similar dict where the keys are output attrs (like
+ L9['red']) and the values are suitable for Collector.setAttr
"""
if deviceType == L9['ChauvetColorStrip']:
color = deviceAttrSettings.get(L9['color'], '#000000')
diff --git a/light9/collector/output.py b/light9/collector/output.py
--- a/light9/collector/output.py
+++ b/light9/collector/output.py
@@ -48,13 +48,8 @@ class Output(object):
class DmxOutput(Output):
- def __init__(self, baseUri, channels):
- self.baseUri = baseUri
- self.channels = channels
-
- def allConnections(self):
- return ((i, URIRef('%sc%s' % (self.baseUri, i + 1)))
- for i in range(self.channels))
+ def __init__(self, uri):
+ self.uri = uri
def flush(self):
pass
@@ -65,8 +60,8 @@ class EnttecDmx(DmxOutput):
scales.PmfStat('write'),
scales.PmfStat('update'))
- def __init__(self, baseUri, channels, devicePath='/dev/dmx0'):
- DmxOutput.__init__(self, baseUri, channels)
+ def __init__(self, uri, devicePath='/dev/dmx0'):
+ DmxOutput.__init__(self, uri)
sys.path.append("dmx_usb_module")
from dmx import Dmx
@@ -95,8 +90,8 @@ class Udmx(DmxOutput):
scales.PmfStat('update'),
scales.PmfStat('write'),
scales.IntStat('usbErrors'))
- def __init__(self, baseUri, channels):
- DmxOutput.__init__(self, baseUri, channels)
+ def __init__(self, uri):
+ DmxOutput.__init__(self, uri)
from light9.io.udmx import Udmx
self.dev = Udmx()
diff --git a/show/dance2016/theaterLightConfig.n3 b/show/dance2016/theaterLightConfig.n3
--- a/show/dance2016/theaterLightConfig.n3
+++ b/show/dance2016/theaterLightConfig.n3
@@ -3,34 +3,30 @@
@prefix udmx: .
@prefix dmx0: .
-dmx0:c87 :connectedTo dev:colorStripMode .
-dmx0:c88 :connectedTo dev:colorStripRed .
-dmx0:c89 :connectedTo dev:colorStripGreen .
-dmx0:c90 :connectedTo dev:colorStripBlue .
-
-dev:colorStrip a :ChauvetColorStrip, :Device;
- :mode dev:colorStripMode;
- :red dev:colorStripRed;
- :green dev:colorStripGreen;
- :blue dev:colorStripBlue .
+:ChauvetColorStrip a :DeviceClass .
+:ChauvetColorStrip :attr :ccsa0 . :ccsa0 :outputAttr :mode; :dmxOffset 0 .
+:ChauvetColorStrip :attr :ccsa1 . :ccsa1 :outputAttr :red; :dmxOffset 1 .
+:ChauvetColorStrip :attr :ccsa2 . :ccsa2 :outputAttr :green; :dmxOffset 2 .
+:ChauvetColorStrip :attr :ccsa3 . :ccsa3 :outputAttr :blue; :dmxOffset 3 .
-# All these bnodes don't refresh well, but they need to be rewritten
-# as offsets from a single dmx start index, and they need to be
-# inherited with the device type
-dev:moving1 a :Mini15, :Device;
- :xRotation [ is :connectedTo of udmx:c5 ];
- :xFine [ is :connectedTo of udmx:c6 ];
- :yRotation [ is :connectedTo of udmx:c7 ];
- :yFine [ is :connectedTo of udmx:c8 ];
- :rotationSpeed [ is :connectedTo of udmx:c9 ];
- :dimmer [ is :connectedTo of udmx:c10 ];
- :red [ is :connectedTo of udmx:c11 ];
- :green [ is :connectedTo of udmx:c12 ];
- :blue [ is :connectedTo of udmx:c13 ];
- :colorChange [ is :connectedTo of udmx:c14 ];
- :colorSpeed [ is :connectedTo of udmx:c15 ];
- :goboShake [ is :connectedTo of udmx:c16 ];
- :goboChoose [ is :connectedTo of udmx:c17 ] .
+:Mini15 a :DeviceClass .
+:Mini15 :attr :Mini15a0 . :Mini15a0 :outputAttr :xRotation; :dmxOffset 0 .
+:Mini15 :attr :Mini15a1 . :Mini15a1 :outputAttr :xFine; :dmxOffset 1 .
+:Mini15 :attr :Mini15a2 . :Mini15a2 :outputAttr :yRotation; :dmxOffset 2 .
+:Mini15 :attr :Mini15a3 . :Mini15a3 :outputAttr :yFine; :dmxOffset 3 .
+:Mini15 :attr :Mini15a4 . :Mini15a4 :outputAttr :rotationSpeed; :dmxOffset 4 .
+:Mini15 :attr :Mini15a5 . :Mini15a5 :outputAttr :dimmer; :dmxOffset 5 .
+:Mini15 :attr :Mini15a6 . :Mini15a6 :outputAttr :red; :dmxOffset 6 .
+:Mini15 :attr :Mini15a7 . :Mini15a7 :outputAttr :green; :dmxOffset 7 .
+:Mini15 :attr :Mini15a8 . :Mini15a8 :outputAttr :blue; :dmxOffset 8 .
+:Mini15 :attr :Mini15a9 . :Mini15a9 :outputAttr :colorChange; :dmxOffset 9 .
+:Mini15 :attr :Mini15a10 . :Mini15a10 :outputAttr :colorSpeed; :dmxOffset 10 .
+:Mini15 :attr :Mini15a11 . :Mini15a11 :outputAttr :goboShake; :dmxOffset 11 .
+:Mini15 :attr :Mini15a12 . :Mini15a12 :outputAttr :goboChoose; :dmxOffset 12 .
+
+dev:colorStrip a :ChauvetColorStrip; :dmxUniverse dmx0:; :dmxBase 87 .
+
+dev:moving1 a :Mini15; :dmxUniverse udmx:; :dmxBase 1 .
# [ :name "cyc-right"; :output dmx:c42 ] .
# [ :name "cyc-mid"; :output dmx:c43 ] .