1194
|
1 from docopt import docopt
|
|
2 from rdfdb.patch import Patch
|
|
3 from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler
|
|
4 from rdflib import Namespace, URIRef, Literal, Graph
|
|
5 from rdflib.parser import StringInputSource
|
|
6 from twisted.internet import reactor, task
|
|
7 import cyclone.web
|
|
8 from cyclone.httpclient import fetch
|
|
9 import logging, time
|
|
10 from MFRC522.SimpleMFRC522 import SimpleMFRC522
|
|
11 from logsetup import log, enableTwistedLog
|
|
12
|
|
13 ROOM = Namespace('http://projects.bigasterisk.com/room/')
|
|
14
|
|
15 ctx = ROOM['frontDoorWindowRfidCtx']
|
|
16
|
|
17 def rdfGraphBody(body, headers):
|
|
18 g = Graph()
|
|
19 g.parse(StringInputSource(body), format='nt')
|
|
20 return g
|
|
21
|
|
22 class OutputPage(cyclone.web.RequestHandler):
|
|
23 def put(self):
|
|
24 user = URIRef(self.request.headers['x-foaf-agent'])
|
|
25 arg = self.request.arguments
|
|
26 if arg.get('s') and arg.get('p'):
|
|
27 subj = URIRef(arg['s'][-1])
|
|
28 pred = URIRef(arg['p'][-1])
|
|
29 obj = URIRef(self.request.body)
|
|
30 stmt = (subj, pred, obj)
|
|
31 else:
|
|
32 g = rdfGraphBody(self.request.body, self.request.headers)
|
|
33 assert len(g) == 1, len(g)
|
|
34 stmt = g.triples((None, None, None)).next()
|
|
35 self._onStatement(user, stmt)
|
|
36 post = put
|
|
37
|
|
38 def _onStatement(self, user, stmt):
|
|
39 # write rfid to new key, etc.
|
|
40 if stmt[1] == ROOM['keyContents']:
|
|
41 return
|
|
42 log.warn("ignoring %s", stmt)
|
|
43
|
|
44 sensor = ROOM['frontDoorWindowRfid']
|
|
45
|
|
46 class ReadLoop(object):
|
|
47 def __init__(self, reader, masterGraph):
|
|
48 self.reader = reader
|
|
49 self.masterGraph = masterGraph
|
|
50 self.log = {} # cardIdUri : most recent seentime
|
|
51
|
|
52 self.pollPeriodSecs = .2
|
|
53 self.expireSecs = 2
|
|
54
|
|
55 task.LoopingCall(self.poll).start(self.pollPeriodSecs)
|
|
56
|
|
57 def poll(self):
|
|
58 now = time.time()
|
|
59
|
|
60 self.flushOldReads(now)
|
|
61
|
|
62 card_id, text = self.reader.read()
|
|
63 if card_id is None:
|
|
64 return
|
|
65
|
|
66 cardIdUri = URIRef('http://bigasterisk.com/rfidCard/%s' % card_id)
|
|
67 textLit = Literal(text.rstrip())
|
|
68
|
|
69 is_new = cardIdUri not in self.log
|
|
70 self.log[cardIdUri] = now
|
|
71 if is_new:
|
|
72 self.startCardRead(cardIdUri, textLit)
|
|
73
|
|
74 def flushOldReads(self, now):
|
|
75 for uri in self.log.keys():
|
|
76 if self.log[uri] < now - self.expireSecs:
|
|
77 self.endCardRead(uri)
|
|
78 del self.log[uri]
|
|
79
|
|
80 def startCardRead(self, cardUri, text):
|
|
81 p = Patch(addQuads=[(sensor, ROOM['reading'], cardUri, ctx),
|
|
82 (cardUri, ROOM['cardText'], text, ctx)], delQuads=[])
|
|
83 self.masterGraph.patch(p)
|
|
84 self._sendOneshot([(sensor, ROOM['startReading'], cardUri),
|
|
85 (cardUri, ROOM['cardText'], text)])
|
|
86
|
|
87 def endCardRead(self, cardUri):
|
|
88 delQuads = []
|
|
89 for spo in self.masterGraph._graph.triples((sensor, ROOM['reading'], cardUri)):
|
|
90 delQuads.append(spo + (ctx,))
|
|
91 for spo in self.masterGraph._graph.triples((cardUri, ROOM['cardText'], None)):
|
|
92 delQuads.append(spo + (ctx,))
|
|
93
|
|
94 self.masterGraph.patch(Patch(addQuads=[], delQuads=delQuads))
|
|
95
|
|
96 def _sendOneshot(self, oneshot):
|
|
97 body = (' '.join('%s %s %s .' % (s.n3(), p.n3(), o.n3())
|
|
98 for s,p,o in oneshot)).encode('utf8')
|
|
99 url = 'http://bang6:9071/oneShot'
|
|
100 d = fetch(method='POST',
|
|
101 url=url,
|
|
102 headers={'Content-Type': ['text/n3']},
|
|
103 postdata=body,
|
|
104 timeout=5)
|
|
105 def err(e):
|
|
106 log.info('oneshot post to %r failed: %s',
|
|
107 url, e.getErrorMessage())
|
|
108 d.addErrback(err)
|
|
109
|
|
110
|
|
111
|
|
112 if __name__ == '__main__':
|
|
113 arg = docopt("""
|
|
114 Usage: rfid.py [options]
|
|
115
|
|
116 -v Verbose
|
|
117 """)
|
|
118 log.setLevel(logging.INFO)
|
|
119 if arg['-v']:
|
|
120 enableTwistedLog()
|
|
121 log.setLevel(logging.DEBUG)
|
|
122
|
|
123 masterGraph = PatchableGraph()
|
|
124 reader = SimpleMFRC522()
|
|
125
|
|
126 loop = ReadLoop(reader, masterGraph)
|
|
127
|
|
128 port = 10012
|
|
129 reactor.listenTCP(port, cyclone.web.Application([
|
|
130 (r"/()", cyclone.web.StaticFileHandler,
|
|
131 {"path": ".", "default_filename": "index.html"}),
|
|
132 (r"/graph", CycloneGraphHandler, {'masterGraph': masterGraph}),
|
|
133 (r"/graph/events", CycloneGraphEventsHandler,
|
|
134 {'masterGraph': masterGraph}),
|
|
135 (r'/output', OutputPage),
|
|
136 ], masterGraph=masterGraph, debug=arg['-v']), interface='::')
|
|
137 log.warn('serving on %s', port)
|
|
138
|
|
139 reactor.run()
|