diff service/shuttlepro/shuttleservice.py @ 130:57ae7dc0f417

new shuttlepro web service with /graph Ignore-this: 1c8e71aad6ab24fa24532f4e5e01d2d9
author drewp@bigasterisk.com
date Fri, 11 Oct 2013 22:36:41 -0700
parents
children 68a7a15b0eb5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/shuttlepro/shuttleservice.py	Fri Oct 11 22:36:41 2013 -0700
@@ -0,0 +1,96 @@
+import sys, time
+import cyclone.web, cyclone.httpclient, cyclone.websocket
+sys.path.append("../../lib")
+from logsetup import log
+from cycloneerr import PrettyErrorHandler
+from twisted.internet import reactor, threads
+from shuttlepro import powermate
+sys.path.append("/my/site/magma")
+from stategraph import StateGraph
+
+from rdflib import Namespace, Graph, Literal
+SHUTTLEPRO = Namespace("http://bigasterisk.com/room/livingRoom/shuttlepro/")
+ROOM = Namespace("http://projects.bigasterisk.com/room/")
+
+def sendOneShotGraph(g):
+    if not g:
+        return
+
+    nt = g.serialize(format='nt')
+    cyclone.httpclient.fetch(
+        "http://bang:9071/oneShot",
+        method='POST',
+        postdata=nt,
+        timeout=1,
+        headers={'Content-Type': ['text/n3']},
+    ).addErrback(log.error)
+
+class Index(PrettyErrorHandler, cyclone.web.StaticFileHandler):
+    def get(self, *args, **kw):
+        self.settings.poller.assertCurrent()
+        return cyclone.web.StaticFileHandler.get(self, *args, **kw)
+
+class GraphResource(PrettyErrorHandler, cyclone.web.RequestHandler):
+    def get(self):
+        p = self.settings.poller
+        g = StateGraph(ROOM.environment)
+        g.add((SHUTTLEPRO['shuttle'], ROOM['angle'],
+               Literal(p.currentShuttleAngle)))
+        g.add((SHUTTLEPRO['dial'], ROOM['totalDialMovement'],
+               Literal(p.totalDialMovement)))
+        self.set_header('Content-type', 'application/x-trig')
+        self.write(g.asTrig())
+
+class Poller(object):
+    def __init__(self, dev="/dev/input/by-id/usb-Contour_Design_ShuttlePRO-event-if00"):
+        self.lastUpdateTime = 0
+        self.p = powermate(dev, self.onEvent)
+        self.updateLoop()
+        self.currentShuttleAngle = 0
+        self.totalDialMovement = 0
+
+    def assertCurrent(self):
+        ago = time.time() - self.lastUpdateTime
+        if ago > 60: # this may have to go up depending on how read_next times out
+            raise ValueError("last usb update was %s sec ago" % ago)
+        
+    def onEvent(self, what):
+        print 'onEvent', what
+        g = Graph()
+        if 'key' in what:
+            g.add((SHUTTLEPRO['button%s' % what['key']['button']],
+                   ROOM['state'],
+                   ROOM['press'] if what['key']['press'] else ROOM['release']))
+        elif 'shuttle' in what:
+            # this will send lots of repeats. It's really not a one-shot at all.
+            g.add((SHUTTLEPRO['shuttle'], ROOM['position'],
+                   Literal(what['shuttle'])))
+            self.currentShuttleAngle = what['shuttle']
+        elif 'dial' in what:
+            g.add((SHUTTLEPRO['dial'], ROOM['change'],
+                   ROOM['clockwise'] if what['dial'] == 1 else
+                   ROOM['counterclockwise']))
+            self.totalDialMovement += what['dial']
+        sendOneShotGraph(g)
+
+    def updateLoop(self, *prevResults):
+        self.lastUpdateTime = time.time()
+        threads.deferToThread(
+            self.p.read_next
+        ).addCallback(self.updateLoop)
+        
+if __name__ == '__main__':
+    from twisted.python import log as twlog
+    twlog.startLogging(sys.stdout)
+    port = 9103
+    poller = Poller()
+    reactor.listenTCP(port, cyclone.web.Application(handlers=[
+        (r'/()', Index, {"path" : ".", # security hole- serves this dir too
+          "default_filename" : "index.html"}),
+        (r'/graph', GraphResource),
+        (r'/(.*)', cyclone.web.StaticFileHandler,
+         {"path" : ".", # security hole- serves this dir too
+          "default_filename" : "index.html"}),
+        ], poller=poller))
+    log.info("serving on %s" % port)
+    reactor.run()