changeset 1336:7993c7bcd04a

make devices_shared into lib Ignore-this: 9c4c0c05413eb2660312862ecc5a743b darcs-hash:b6eb09e457a7e8c49a9399a463a3ef64e1bae123
author drewp <drewp@bigasterisk.com>
date Thu, 25 Apr 2019 17:22:39 -0700
parents 013af6808aca
children c813d8228a25
files lib/devices_shared.py lib/devices_shared/devices_shared.py lib/devices_shared/setup.py lib/devices_shared/tasks.py
diffstat 4 files changed, 224 insertions(+), 199 deletions(-) [+]
line wrap: on
line diff
--- a/lib/devices_shared.py	Tue Apr 23 03:57:55 2019 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-from __future__ import division
-import time
-import numpy
-import logging
-import imageio
-from rdflib import Namespace, RDF, URIRef, Literal
-
-ROOM = Namespace('http://projects.bigasterisk.com/room/')
-log = logging.getLogger()
-
-def _rgbFromHex(h):
-    rrggbb = h.lstrip('#')
-    return [int(x, 16) for x in [rrggbb[0:2], rrggbb[2:4], rrggbb[4:6]]]
-
-class PixelColumnsFromImages(object):
-    # could use this instead:
-    # https://github.com/OpenImageIO/oiio/blob/master/src/python/py_imagecache.cpp
-    def __init__(self):
-        self.lastImg = None, None
-
-    def get(self, path, x, y, h):
-        if self.lastImg[0] != path:
-            fp = 'config/' + path
-            self.lastImg = path, imageio.imread(fp) # or etcd or http
-            log.debug('read image from %r', fp)
-        img = self.lastImg[1]
-
-        y = numpy.clip(y, 0, img.shape[0] - 1)
-        h = numpy.clip(h, 1, img.shape[0] - y)
-        x = numpy.clip(x, 0, img.shape[1] - 1)
-        log.info('getcol y=%s h=%s img=%r ret=%r', y, h, img.shape, img[y:y + h,x,:].shape)
-        return img[y:y + h,x,:]
-
-getPixelColumn = PixelColumnsFromImages().get
-
-
-class AnimChannel(object):
-    def __init__(self, x):
-        self.x = self.x2 = x
-        self.start = self.end = 0
-
-    def animTo(self, x2, rate):
-        self.get() # bring self.x to current time
-        log.info('animTo %s -> %s', self.x, x2)
-        if x2 == self.x:
-            return
-        self.start = time.time()
-        self.end = self.start + abs(x2 - self.x) / rate
-        self.x0 = self.x
-        self.x2 = x2
-
-    def get(self):
-        now = time.time()
-        if now > self.end:
-            self.x = self.x2
-        else:
-            dur = self.end - self.start
-            self.x = (self.end - now) / dur * self.x0 + (now - self.start) / dur * self.x2
-        return self.x
-
-class ScanGroup(object):
-
-    def __init__(self, uri, numLeds):
-        self.uri = uri
-        self.current = numpy.zeros((numLeds, 3), dtype=numpy.uint8)
-
-        self.x = AnimChannel(0)
-        self.y = AnimChannel(0)
-        self.height = AnimChannel(numLeds)
-        self.src = ""
-
-    def animateTo(self, x, y, height, src, rate=30, interpolate=ROOM['slide']):
-        log.info('anim to %s x=%s y=%s h=%s', src, x, y, height)
-        self.x.animTo(x, rate)
-        self.y.animTo(y, rate) # need separate y rate?
-        self.height.animTo(height, rate)
-
-        self.src = src
-
-    def updateCurrent(self):
-        try:
-            self.current = getPixelColumn(self.src,
-                                          int(self.x.get()),
-                                          int(self.y.get()),
-                                          int(self.height.get()))
-        except IOError as e:
-            log.warn('getPixelColumn %r', e)
-        log.debug('current = %r', self.current)
-        
-    def currentStatements(self):
-        return [
-            (self.uri, RDF.type, ROOM['ScanGroup']),
-            (self.uri, ROOM['xValue'], Literal(self.x.get())),
-            (self.uri, ROOM['yValue'], Literal(self.y.get())),
-            (self.uri, ROOM['heightValue'], Literal(self.height.get())),
-            (self.uri, ROOM['src'], Literal(self.src)),
-        ]
-
-    def colorForIndex(self, i):
-        return list(self.current[i,:])
-
-args = {ROOM['src']: ('src', str),
-        ROOM['x']: ('x', int),
-        ROOM['y']: ('y', int),
-        ROOM['height']: ('height', int),
-        ROOM['interpolate']: ('interpolate', lambda x: x),
-        ROOM['rate']: ('rate', float),
-        }
-
-    
-class RgbPixelsAnimation(object):
-
-    def __init__(self, graph, uri, updateOutput):
-        """we call updateOutput after any changes"""
-        self.graph = graph
-        self.uri = uri
-        self.updateOutput = updateOutput
-        self.setupGroups()
-        
-    def setupGroups(self):
-        self.groups = {}
-        self.groupWithIndex = {}
-        attrStatements = set()
-        for grp in self.graph.objects(self.uri, ROOM['pixelGroup']):
-            s = int(self.graph.value(grp, ROOM['startIndex']))
-            e = int(self.graph.value(grp, ROOM['endIndex']))
-            log.info('ScanGroup %s from %s to %s', grp, s, e)
-            sg = ScanGroup(grp, e - s + 1)
-            self.groups[grp] = [s, e, sg]
-            for i in range(s, e + 1):
-                self.groupWithIndex[i] = sg, i - s
-            attrStatements.update(self.graph.triples((grp, None, None)))
-        self.onStatements(attrStatements, _groups=False)
-            
-    def maxIndex(self):
-        return max(v[1] for v in self.groups.itervalues())
-
-    def hostStatements(self):
-        return (
-            [(self.uri, ROOM['pixelGroup'], grp) for grp in self.groups.keys()] +
-            sum([v[2].currentStatements()
-                 for v in self.groups.itervalues()], []) +
-            [] # current
-        )
-
-    def getColorOrder(self, graph, uri):
-        colorOrder = graph.value(uri, ROOM['colorOrder'],
-                                 default=ROOM['ledColorOrder/RGB'])
-        head, tail = str(colorOrder).rsplit('/', 1)
-        if head != str(ROOM['ledColorOrder']):
-            raise NotImplementedError('%r colorOrder %r' % (uri, colorOrder))
-        stripType = None
-        return colorOrder, stripType
-        
-    def step(self):
-        # if animating...
-        self.updateOutput()
-        
-    def onStatements(self, statements, _groups=True):
-        needSetup = False
-        animateCalls = {} # group uri : kw for animateTo
-        for s, p, o in statements:
-            if s not in self.groups:
-                # missing the case that you just added a new group
-                continue
-            if p in args:
-                k, conv = args[p]
-                animateCalls.setdefault(s, {})[k] = conv(o.toPython())
-            else:
-                needSetup = True
-
-        if needSetup and _groups:
-            self.setupGroups()
-        for grp, kw in animateCalls.items():
-            for pred, (k, conv) in args.items():
-                if k not in kw:
-                    v = self.graph.value(grp, pred)
-                    if v is not None:
-                        kw[k] = conv(v.toPython())
-
-            self.groups[grp][2].animateTo(**kw)
-
-    def outputPatterns(self):
-        pats = []
-        for grp in self.groups:
-            for attr in args:
-                pats.append((grp, attr, None))
-        return pats
-
-    def outputWidgets(self):
-        return []
-
-    def currentColors(self):
-        for _, _, sg in self.groups.values():
-            sg.updateCurrent()
-        for idx in range(self.maxIndex() + 1):
-            sg, offset = self.groupWithIndex[idx]
-            r, g, b = sg.colorForIndex(offset)
-            yield idx, (r, g, b)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/devices_shared/devices_shared.py	Thu Apr 25 17:22:39 2019 -0700
@@ -0,0 +1,199 @@
+from __future__ import division
+import time
+import numpy
+import logging
+import imageio
+from rdflib import Namespace, RDF, URIRef, Literal
+
+ROOM = Namespace('http://projects.bigasterisk.com/room/')
+log = logging.getLogger()
+
+def _rgbFromHex(h):
+    rrggbb = h.lstrip('#')
+    return [int(x, 16) for x in [rrggbb[0:2], rrggbb[2:4], rrggbb[4:6]]]
+
+class PixelColumnsFromImages(object):
+    # could use this instead:
+    # https://github.com/OpenImageIO/oiio/blob/master/src/python/py_imagecache.cpp
+    def __init__(self):
+        self.lastImg = None, None
+
+    def get(self, path, x, y, h):
+        if self.lastImg[0] != path:
+            fp = 'config/' + path
+            self.lastImg = path, imageio.imread(fp) # or etcd or http
+            log.debug('read image from %r', fp)
+        img = self.lastImg[1]
+
+        y = numpy.clip(y, 0, img.shape[0] - 1)
+        h = numpy.clip(h, 1, img.shape[0] - y)
+        x = numpy.clip(x, 0, img.shape[1] - 1)
+        log.info('getcol y=%s h=%s img=%r ret=%r', y, h, img.shape, img[y:y + h,x,:].shape)
+        return img[y:y + h,x,:]
+
+getPixelColumn = PixelColumnsFromImages().get
+
+
+class AnimChannel(object):
+    def __init__(self, x):
+        self.x = self.x2 = x
+        self.start = self.end = 0
+
+    def animTo(self, x2, rate):
+        self.get() # bring self.x to current time
+        log.info('animTo %s -> %s', self.x, x2)
+        if x2 == self.x:
+            return
+        self.start = time.time()
+        self.end = self.start + abs(x2 - self.x) / rate
+        self.x0 = self.x
+        self.x2 = x2
+
+    def get(self):
+        now = time.time()
+        if now > self.end:
+            self.x = self.x2
+        else:
+            dur = self.end - self.start
+            self.x = (self.end - now) / dur * self.x0 + (now - self.start) / dur * self.x2
+        return self.x
+
+class ScanGroup(object):
+
+    def __init__(self, uri, numLeds):
+        self.uri = uri
+        self.current = numpy.zeros((numLeds, 3), dtype=numpy.uint8)
+
+        self.x = AnimChannel(0)
+        self.y = AnimChannel(0)
+        self.height = AnimChannel(numLeds)
+        self.src = ""
+
+    def animateTo(self, x, y, height, src, rate=30, interpolate=ROOM['slide']):
+        log.info('anim to %s x=%s y=%s h=%s', src, x, y, height)
+        self.x.animTo(x, rate)
+        self.y.animTo(y, rate) # need separate y rate?
+        self.height.animTo(height, rate)
+
+        self.src = src
+
+    def updateCurrent(self):
+        try:
+            self.current = getPixelColumn(self.src,
+                                          int(self.x.get()),
+                                          int(self.y.get()),
+                                          int(self.height.get()))
+        except IOError as e:
+            log.warn('getPixelColumn %r', e)
+        log.debug('current = %r', self.current)
+        
+    def currentStatements(self):
+        return [
+            (self.uri, RDF.type, ROOM['ScanGroup']),
+            (self.uri, ROOM['xValue'], Literal(self.x.get())),
+            (self.uri, ROOM['yValue'], Literal(self.y.get())),
+            (self.uri, ROOM['heightValue'], Literal(self.height.get())),
+            (self.uri, ROOM['src'], Literal(self.src)),
+        ]
+
+    def colorForIndex(self, i):
+        return list(self.current[i,:])
+
+args = {ROOM['src']: ('src', str),
+        ROOM['x']: ('x', int),
+        ROOM['y']: ('y', int),
+        ROOM['height']: ('height', int),
+        ROOM['interpolate']: ('interpolate', lambda x: x),
+        ROOM['rate']: ('rate', float),
+        }
+
+    
+class RgbPixelsAnimation(object):
+
+    def __init__(self, graph, uri, updateOutput):
+        """we call updateOutput after any changes"""
+        self.graph = graph
+        self.uri = uri
+        self.updateOutput = updateOutput
+        self.setupGroups()
+        
+    def setupGroups(self):
+        self.groups = {}
+        self.groupWithIndex = {}
+        attrStatements = set()
+        for grp in self.graph.objects(self.uri, ROOM['pixelGroup']):
+            s = int(self.graph.value(grp, ROOM['startIndex']))
+            e = int(self.graph.value(grp, ROOM['endIndex']))
+            log.info('ScanGroup %s from %s to %s', grp, s, e)
+            sg = ScanGroup(grp, e - s + 1)
+            self.groups[grp] = [s, e, sg]
+            for i in range(s, e + 1):
+                self.groupWithIndex[i] = sg, i - s
+            attrStatements.update(self.graph.triples((grp, None, None)))
+        self.onStatements(attrStatements, _groups=False)
+            
+    def maxIndex(self):
+        return max(v[1] for v in self.groups.itervalues())
+
+    def hostStatements(self):
+        return (
+            [(self.uri, ROOM['pixelGroup'], grp) for grp in self.groups.keys()] +
+            sum([v[2].currentStatements()
+                 for v in self.groups.itervalues()], []) +
+            [] # current
+        )
+
+    def getColorOrder(self, graph, uri):
+        colorOrder = graph.value(uri, ROOM['colorOrder'],
+                                 default=ROOM['ledColorOrder/RGB'])
+        head, tail = str(colorOrder).rsplit('/', 1)
+        if head != str(ROOM['ledColorOrder']):
+            raise NotImplementedError('%r colorOrder %r' % (uri, colorOrder))
+        stripType = None
+        return colorOrder, stripType
+        
+    def step(self):
+        # if animating...
+        self.updateOutput()
+        
+    def onStatements(self, statements, _groups=True):
+        needSetup = False
+        animateCalls = {} # group uri : kw for animateTo
+        for s, p, o in statements:
+            if s not in self.groups:
+                # missing the case that you just added a new group
+                continue
+            if p in args:
+                k, conv = args[p]
+                animateCalls.setdefault(s, {})[k] = conv(o.toPython())
+            else:
+                needSetup = True
+
+        if needSetup and _groups:
+            self.setupGroups()
+        for grp, kw in animateCalls.items():
+            for pred, (k, conv) in args.items():
+                if k not in kw:
+                    v = self.graph.value(grp, pred)
+                    if v is not None:
+                        kw[k] = conv(v.toPython())
+
+            self.groups[grp][2].animateTo(**kw)
+
+    def outputPatterns(self):
+        pats = []
+        for grp in self.groups:
+            for attr in args:
+                pats.append((grp, attr, None))
+        return pats
+
+    def outputWidgets(self):
+        return []
+
+    def currentColors(self):
+        for _, _, sg in self.groups.values():
+            sg.updateCurrent()
+        for idx in range(self.maxIndex() + 1):
+            sg, offset = self.groupWithIndex[idx]
+            r, g, b = sg.colorForIndex(offset)
+            yield idx, (r, g, b)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/devices_shared/setup.py	Thu Apr 25 17:22:39 2019 -0700
@@ -0,0 +1,16 @@
+from distutils.core import setup
+ 
+setup(
+    name='devices_shared',
+    version='0.0.0',
+    packages=['devices_shared'],
+    package_dir={'devices_shared': ''},
+    requires=[
+        'numpy',
+        'imageio',
+        'rdflib',
+    ],
+    url='https://projects.bigasterisk.com/devices_shared/devices_shared-0.0.0.tar.gz',
+    author='Drew Perttula',
+    author_email='drewp@bigasterisk.com',
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/devices_shared/tasks.py	Thu Apr 25 17:22:39 2019 -0700
@@ -0,0 +1,9 @@
+from invoke import task
+
+import sys
+sys.path.append('/my/proj/release')
+from release import local_release
+
+@task
+def release(ctx):
+    local_release(ctx)