130
|
1 import sys, time
|
|
2 import cyclone.web, cyclone.httpclient, cyclone.websocket
|
|
3 sys.path.append("../../lib")
|
|
4 from logsetup import log
|
|
5 from cycloneerr import PrettyErrorHandler
|
|
6 from twisted.internet import reactor, threads
|
|
7 from shuttlepro import powermate
|
|
8 sys.path.append("/my/site/magma")
|
|
9 from stategraph import StateGraph
|
|
10
|
|
11 from rdflib import Namespace, Graph, Literal
|
|
12 SHUTTLEPRO = Namespace("http://bigasterisk.com/room/livingRoom/shuttlepro/")
|
|
13 ROOM = Namespace("http://projects.bigasterisk.com/room/")
|
|
14
|
|
15 def sendOneShotGraph(g):
|
|
16 if not g:
|
|
17 return
|
|
18
|
|
19 nt = g.serialize(format='nt')
|
|
20 cyclone.httpclient.fetch(
|
|
21 "http://bang:9071/oneShot",
|
|
22 method='POST',
|
|
23 postdata=nt,
|
|
24 timeout=1,
|
|
25 headers={'Content-Type': ['text/n3']},
|
|
26 ).addErrback(log.error)
|
|
27
|
|
28 class Index(PrettyErrorHandler, cyclone.web.StaticFileHandler):
|
|
29 def get(self, *args, **kw):
|
|
30 self.settings.poller.assertCurrent()
|
|
31 return cyclone.web.StaticFileHandler.get(self, *args, **kw)
|
|
32
|
|
33 class GraphResource(PrettyErrorHandler, cyclone.web.RequestHandler):
|
|
34 def get(self):
|
|
35 p = self.settings.poller
|
|
36 g = StateGraph(ROOM.environment)
|
|
37 g.add((SHUTTLEPRO['shuttle'], ROOM['angle'],
|
|
38 Literal(p.currentShuttleAngle)))
|
|
39 g.add((SHUTTLEPRO['dial'], ROOM['totalDialMovement'],
|
|
40 Literal(p.totalDialMovement)))
|
|
41 self.set_header('Content-type', 'application/x-trig')
|
|
42 self.write(g.asTrig())
|
|
43
|
|
44 class Poller(object):
|
|
45 def __init__(self, dev="/dev/input/by-id/usb-Contour_Design_ShuttlePRO-event-if00"):
|
|
46 self.lastUpdateTime = 0
|
|
47 self.p = powermate(dev, self.onEvent)
|
|
48 self.updateLoop()
|
|
49 self.currentShuttleAngle = 0
|
|
50 self.totalDialMovement = 0
|
|
51
|
|
52 def assertCurrent(self):
|
|
53 ago = time.time() - self.lastUpdateTime
|
|
54 if ago > 60: # this may have to go up depending on how read_next times out
|
|
55 raise ValueError("last usb update was %s sec ago" % ago)
|
|
56
|
|
57 def onEvent(self, what):
|
|
58 print 'onEvent', what
|
|
59 g = Graph()
|
|
60 if 'key' in what:
|
|
61 g.add((SHUTTLEPRO['button%s' % what['key']['button']],
|
|
62 ROOM['state'],
|
|
63 ROOM['press'] if what['key']['press'] else ROOM['release']))
|
|
64 elif 'shuttle' in what:
|
|
65 # this will send lots of repeats. It's really not a one-shot at all.
|
|
66 g.add((SHUTTLEPRO['shuttle'], ROOM['position'],
|
|
67 Literal(what['shuttle'])))
|
|
68 self.currentShuttleAngle = what['shuttle']
|
|
69 elif 'dial' in what:
|
|
70 g.add((SHUTTLEPRO['dial'], ROOM['change'],
|
|
71 ROOM['clockwise'] if what['dial'] == 1 else
|
|
72 ROOM['counterclockwise']))
|
|
73 self.totalDialMovement += what['dial']
|
|
74 sendOneShotGraph(g)
|
|
75
|
|
76 def updateLoop(self, *prevResults):
|
|
77 self.lastUpdateTime = time.time()
|
|
78 threads.deferToThread(
|
|
79 self.p.read_next
|
|
80 ).addCallback(self.updateLoop)
|
|
81
|
|
82 if __name__ == '__main__':
|
|
83 from twisted.python import log as twlog
|
|
84 twlog.startLogging(sys.stdout)
|
|
85 port = 9103
|
|
86 poller = Poller()
|
|
87 reactor.listenTCP(port, cyclone.web.Application(handlers=[
|
131
|
88 (r'/()', Index, {"path" : ".", "default_filename" : "index.html"}),
|
130
|
89 (r'/graph', GraphResource),
|
131
|
90 # serves this source code too
|
|
91 (r'/(.*)', cyclone.web.StaticFileHandler, {"path" : "."})
|
130
|
92 ], poller=poller))
|
|
93 log.info("serving on %s" % port)
|
|
94 reactor.run()
|