Changeset - 60cc3a504377
[Not reviewed]
default
0 6 0
Drew Perttula - 8 years ago 2017-05-22 04:37:28
drewp@bigasterisk.com
more settings.py coverage. Zeros are now dropped from Settings lists.
Ignore-this: cc61e2f12ad9b41daa126ccfb58b4f09
6 files changed with 135 insertions and 30 deletions:
0 comments (0 inline, 0 general)
light9/collector/collector.py
Show inline comments
 
@@ -40,25 +40,25 @@ def outputMap(graph, outputs):
 
                raise
 
            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.debug('    map %s to %s,%s', outputAttr, output, index)
 
    return ret
 
        
 
class Collector(Generic[ClientType, ClientSessionType]):
 
    def __init__(self, graph, outputs, listeners=None, clientTimeoutSec=10):
 
        # type: (Graph, List[Output], float) -> None
 
        # type: (Graph, List[Output], List[Listener], float) -> None
 
        self.graph = graph
 
        self.outputs = outputs
 
        self.listeners = listeners
 
        self.clientTimeoutSec = clientTimeoutSec
 
        self.initTime = time.time()
 
        self.allDevices = set()
 

	
 
        self.graph.addHandler(self.rebuildOutputMap)
 

	
 
        # client : (session, time, {(dev,devattr): latestValue})
 
        self.lastRequest = {} # type: Dict[ClientType, Tuple[ClientSessionType, float, Dict[Tuple[URIRef, URIRef], float]]]
 

	
light9/effect/settings.py
Show inline comments
 
@@ -8,31 +8,69 @@ from rdflib import URIRef, Literal
 
from light9.namespaces import RDF, L9, DEV
 
from light9.rdfdb.patch import Patch
 

	
 

	
 
def getVal(graph, subj):
 
    lit = graph.value(subj, L9['value']) or graph.value(subj, L9['scaledValue'])
 
    ret = lit.toPython()
 
    if isinstance(ret, decimal.Decimal):
 
        ret = float(ret)
 
    return ret
 

	
 
class _Settings(object):
 
    """
 
    default values are 0. Internal rep must not store zeros or some
 
    comparisons will break.
 
    """
 
    def __init__(self, graph, settingsList):
 
        self.graph = graph # for looking up all possible attrs
 
        self._compiled = {} # dev: { attr: val }
 
        for row in settingsList:
 
            self._compiled.setdefault(row[0], {})[row[1]] = row[2]
 
        # self._compiled may not be final yet- see _fromCompiled
 
        self._delZeros()
 
        
 
    @classmethod
 
    def _fromCompiled(cls, graph, compiled):
 
        obj = cls(graph, [])
 
        obj._compiled = compiled
 
        obj._delZeros()
 
        return obj
 
            
 
    @classmethod
 
    def fromResource(cls, graph, subj):
 
        settingsList = []
 
        with graph.currentState() as g:
 
            for s in g.objects(subj, L9['setting']):
 
                d = g.value(s, L9['device'])
 
                da = g.value(s, L9['deviceAttr'])
 
                v = getVal(g, s)
 
                settingsList.append((d, da, v))
 
        return cls(graph, settingsList)
 

	
 
    @classmethod
 
    def fromVector(cls, graph, vector):
 
        compiled = {}
 
        for (d, a), v in zip(cls(graph, [])._vectorKeys(), vector):
 
            compiled.setdefault(d, {})[a] = v
 
        return cls._fromCompiled(graph, compiled)
 

	
 
    def _delZeros(self):
 
        for dev, av in self._compiled.items():
 
            for attr, val in av.items():
 
                if val == 0:
 
                    del av[attr]
 
            if not av:
 
                del self._compiled[dev]
 
        
 
    def __hash__(self):
 
        itemed = tuple([(d, tuple([(a, v) for a, v in sorted(av.items())]))
 
                        for d, av in sorted(self._compiled.items())])
 
        return hash(itemed)
 

	
 
    def __eq__(self, other):
 
        if not issubclass(other.__class__, self.__class__):
 
            raise TypeError("can't compare %r to %r" % (self.__class__, other.__class__))
 
        return self._compiled == other._compiled
 

	
 
    def __ne__(self, other):
 
        return not self == other
 
@@ -45,48 +83,24 @@ class _Settings(object):
 
                for attr, val in av.iteritems():
 
                    words.append('%s.%s=%g' % (dev.rsplit('/')[-1],
 
                                               attr.rsplit('/')[-1],
 
                                               val))
 
                    if len(words) > 5:
 
                        words.append('...')
 
                        return
 
        accum()
 
        return '<%s %s>' % (self.__class__.__name__, ' '.join(words))
 
        
 
    def getValue(self, dev, attr):
 
        return self._compiled.get(dev, {}).get(attr, 0)
 
        
 
    @classmethod
 
    def _fromCompiled(cls, graph, compiled):
 
        obj = cls(graph, [])
 
        obj._compiled = compiled
 
        return obj
 
            
 
    @classmethod
 
    def fromResource(cls, graph, subj):
 
        settingsList = []
 
        with graph.currentState() as g:
 
            for s in g.objects(subj, L9['setting']):
 
                d = g.value(s, L9['device'])
 
                da = g.value(s, L9['deviceAttr'])
 
                v = getVal(g, s)
 
                settingsList.append((d, da, v))
 
        return cls(graph, settingsList)
 

	
 
    @classmethod
 
    def fromVector(cls, graph, vector):
 
        compiled = {}
 
        for (d, a), v in zip(cls(graph, [])._vectorKeys(), vector):
 
            compiled.setdefault(d, {})[a] = v
 
        return cls._fromCompiled(graph, compiled)
 

	
 
    def _vectorKeys(self):
 
        """stable order of all the dev,attr pairs for this type of settings"""
 
        raise NotImplementedError
 

	
 
    def asList(self):
 
        """old style list of (dev, attr, val) tuples"""
 
        out = []
 
        for dev, av in self._compiled.iteritems():
 
            for attr, val in av.iteritems():
 
                out.append((dev, attr, val))
 
        return out
 
@@ -109,48 +123,48 @@ class _Settings(object):
 
                                            {dev: self._compiled.get(dev, {})})
 
        
 
    def distanceTo(self, other):
 
        raise NotImplementedError
 
        dist = 0
 
        for key in set(attrs1).union(set(attrs2)):
 
            if key not in attrs1 or key not in attrs2:
 
                dist += 999
 
            else:
 
                dist += abs(attrs1[key] - attrs2[key])
 
        return dist
 

	
 
    def addStatements(self, subj, ctx, settingRoot, settingsSubgraphCache):
 
    def statements(self, subj, ctx, settingRoot, settingsSubgraphCache):
 
        """
 
        settingRoot can be shared across images (or even wider if you want)
 
        """
 
        # ported from live.coffee
 
        add = []
 
        for i, (dev, attr, val) in enumerate(self.asList()):
 
            # hopefully a unique number for the setting so repeated settings converge
 
            settingHash = hash((dev, attr, val)) % 9999999
 
            setting = URIRef('%sset%s' % (settingRoot, settingHash))
 
            add.append((subj, L9['setting'], setting, ctx))
 
            if setting in settingsSubgraphCache:              
 
                continue
 
                
 
            scaledAttributeTypes = [L9['color'], L9['brightness'], L9['uv']]
 
            settingType = L9['scaledValue'] if attr in scaledAttributeTypes else L9['value']
 
            add.extend([
 
                (setting, L9['device'], dev, ctx),
 
                (setting, L9['deviceAttr'], attr, ctx),
 
                (setting, settingType, Literal(val), ctx),
 
                ])
 
            settingsSubgraphCache.add(setting)
 
            
 
        self.graph.patch(Patch(addQuads=add))
 
        return add
 

	
 

	
 
class DeviceSettings(_Settings):
 
    def _vectorKeys(self):
 
        with self.graph.currentState() as g:
 
            devs = set() # devclass, dev
 
            for dc in g.subjects(RDF.type, L9['DeviceClass']):
 
                for dev in g.subjects(RDF.type, dc):
 
                    devs.add((dc, dev))
 

	
 
            keys = []
 
            for dc, dev in sorted(devs):
light9/effect/settings_test.py
Show inline comments
 
import unittest
 
from rdflib import Literal
 
from light9.rdfdb.patch import Patch
 
from light9.rdfdb.localsyncedgraph import LocalSyncedGraph
 
from light9.namespaces import RDF, L9, DEV
 
from light9.effect.settings import DeviceSettings
 

	
 
class TestDeviceSettings(unittest.TestCase):
 
    def setUp(self):
 
        self.graph = LocalSyncedGraph(files=['show/dance2017/cam/test/lightConfig.n3',
 
                                             'show/dance2017/cam/test/bg.n3'])
 

	
 
    def testToVectorZero(self):
 
        ds = DeviceSettings(self.graph, [])
 
        self.assertEqual([0] * 20, ds.toVector())
 

	
 
    def testEq(self):
 
        s1 = DeviceSettings(self.graph, [
 
            (L9['light1'], L9['attr1'], 0.5),
 
            (L9['light1'], L9['attr2'], 0.3),
 
        ])
 
        s2 = DeviceSettings(self.graph, [
 
            (L9['light1'], L9['attr2'], 0.3),
 
            (L9['light1'], L9['attr1'], 0.5),
 
        ])
 
        self.assertTrue(s1 == s2)
 
        self.assertFalse(s1 != s2)
 

	
 
    def testMissingFieldsEqZero(self):
 
        self.assertEqual(
 
            DeviceSettings(self.graph, [(L9['aura1'], L9['rx'], 0),]),
 
            DeviceSettings(self.graph, []))
 
        
 
    def testFromResource(self):
 
        ctx = L9['']
 
        self.graph.patch(Patch(addQuads=[
 
            (L9['foo'], L9['setting'], L9['foo_set0'], ctx),
 
            (L9['foo_set0'], L9['device'], L9['light1'], ctx),
 
            (L9['foo_set0'], L9['deviceAttr'], L9['brightness'], ctx),
 
            (L9['foo_set0'], L9['value'], Literal(0.1), ctx),
 
            (L9['foo'], L9['setting'], L9['foo_set1'], ctx),
 
            (L9['foo_set1'], L9['device'], L9['light1'], ctx),
 
            (L9['foo_set1'], L9['deviceAttr'], L9['speed'], ctx),
 
            (L9['foo_set1'], L9['scaledValue'], Literal(0.2), ctx),
 
        ]))
 
        s = DeviceSettings.fromResource(self.graph, L9['foo'])
 

	
 
        self.assertEqual(DeviceSettings(self.graph, [
 
            (L9['light1'], L9['brightness'], 0.1),
 
            (L9['light1'], L9['speed'], 0.2),
 
        ]), s)
 

	
 
    def testToVector(self):
 
        v = DeviceSettings(self.graph, [
 
            (DEV['aura1'], L9['rx'], 0.5),
 
        ]).toVector()
 
        self.assertEqual(
 
            [0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], v)
 
        
 
    def testFromVector(self):
 
        s = DeviceSettings.fromVector(
 
            self.graph,
 
            [0, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
 
        
 
        self.assertEqual(DeviceSettings(self.graph, [
 
            (DEV['aura1'], L9['rx'], 0.5),
 
        ]), s)                            
 

	
 
    def testAsList(self):
 
        sets = [
 
            (L9['light1'], L9['attr2'], 0.3),
 
            (L9['light1'], L9['attr1'], 0.5),
 
        ]
 
        self.assertItemsEqual(sets, DeviceSettings(self.graph, sets).asList())
 

	
 
    def testDevices(self):
 
        s = DeviceSettings(self.graph, [
 
            (L9['aura1'], L9['rx'], 0),
 
            (L9['aura2'], L9['rx'], 0.1),
 
            ])
 
        # aura1 is all defaults (zeros), so it doesn't get listed
 
        self.assertItemsEqual([L9['aura2']], s.devices())
 

	
 
    def testAddStatements(self):
 
        s = DeviceSettings(self.graph, [
 
            (L9['aura2'], L9['rx'], 0.1),
 
            ])
 
        stmts = s.statements(L9['foo'], L9['ctx1'], L9['s_'], set())
 
        self.maxDiff=None
 
        self.assertItemsEqual([
 
            (L9['foo'], L9['setting'], L9['s_set8011962'], L9['ctx1']),
 
            (L9['s_set8011962'], L9['device'], L9['aura2'], L9['ctx1']),
 
            (L9['s_set8011962'], L9['deviceAttr'], L9['rx'], L9['ctx1']),
 
            (L9['s_set8011962'], L9['value'], Literal(0.1), L9['ctx1']),
 
        ], stmts)
 
        
light9/paint/capture.py
Show inline comments
 
import os
 
from rdflib import URIRef
 
from light9 import showconfig
 
from light9.rdfdb.patch import Patch
 
from light9.namespaces import L9
 
from light9.paint.solve import loadNumPy
 

	
 
def writeCaptureDescription(graph, ctx, uri, dev, relOutPath, settingsSubgraphCache, settings):
 
    settings.addStatements(
 
    graph.patch(Patch(addQuads=settings.statements(
 
        uri, ctx=ctx,
 
        settingRoot=URIRef('/'.join([showconfig.showUri(), 'capture', dev.rsplit('/')[1]])),
 
        settingsSubgraphCache=settingsSubgraphCache)
 
        settingsSubgraphCache=settingsSubgraphCache)))
 
    graph.patch(Patch(addQuads=[
 
        (dev, L9['capture'], uri, ctx),
 
        (uri, L9['imagePath'], URIRef('/'.join([showconfig.showUri(), relOutPath])), ctx),
 
        ]))
 
    
 
class CaptureLoader(object):
 
    def __init__(self, graph):
 
        self.graph = graph
 
        
 
    def loadImage(self, pic, thumb=(100, 100)):
 
        ip = self.graph.value(pic, L9['imagePath'])
 
        if not ip.startswith(showconfig.show()):
light9/paint/solve.py
Show inline comments
 
@@ -140,25 +140,25 @@ class Solver(object):
 
        ]
 

	
 
        def settingsFromVector(x):
 
            settings = []
 

	
 
            xLeft = x.tolist()
 
            for dev, attr, _ in dims:
 
                if attr == L9['color']:
 
                    rgb = (xLeft.pop(), xLeft.pop(), xLeft.pop())
 
                    settings.append((dev, attr, toHex(rgb)))
 
                else:
 
                    settings.append((dev, attr, xLeft.pop()))
 
            return settings
 
            return DeviceSettings(self.graph, settings)
 

	
 
        
 
        def drawError(x):
 
            settings = DeviceSettings.fromVector(self.graph, x)
 
            preview = self.combineImages(self.simulationLayers(settings))
 
            saveNumpy('/tmp/x_%s.png' % abs(hash(settings)), preview)
 
            
 
            diff = preview.astype(numpy.float) - pic0
 
            out = scipy.sum(abs(diff))
 
            
 
            #print 'measure at', x, 'drawError=', out
 
            return out
light9/rdfdb/localsyncedgraph.py
Show inline comments
 
from rdflib import ConjunctiveGraph
 

	
 
from light9.rdfdb.currentstategraphapi import CurrentStateGraphApi
 
from light9.rdfdb.autodepgraphapi import AutoDepGraphApi
 
from light9.rdfdb.grapheditapi import GraphEditApi
 
from light9.rdfdb.rdflibpatch import patchQuads
 

	
 
class LocalSyncedGraph(CurrentStateGraphApi, AutoDepGraphApi, GraphEditApi):
 
    """for tests"""
 
    def __init__(self, files=None):
 
        self._graph = ConjunctiveGraph()
 
        for f in files or []:
 
            self._graph.parse(f, format='n3')
 
            
 

	
 
    def patch(self, p):
 
        patchQuads(self._graph,
 
                   deleteQuads=p.delQuads,
 
                   addQuads=p.addQuads,
 
                   perfect=True)
 
        # no deps
0 comments (0 inline, 0 general)