diff service/rfid/rfid.py @ 1194:ee9cbe5817a4

rfid reader service Ignore-this: 26df9a4be891c26722311cf89d1d7cf7 darcs-hash:80ff40afce46a1429309421b1db52aefd5b675a8
author drewp <drewp@bigasterisk.com>
date Thu, 03 Jan 2019 21:52:27 -0800
parents
children cebc0134254a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid/rfid.py	Thu Jan 03 21:52:27 2019 -0800
@@ -0,0 +1,139 @@
+from docopt import docopt
+from rdfdb.patch import Patch
+from patchablegraph import PatchableGraph, CycloneGraphHandler, CycloneGraphEventsHandler
+from rdflib import Namespace, URIRef, Literal, Graph
+from rdflib.parser import StringInputSource
+from twisted.internet import reactor, task
+import cyclone.web
+from cyclone.httpclient import fetch
+import logging, time
+from MFRC522.SimpleMFRC522 import SimpleMFRC522
+from logsetup import log, enableTwistedLog
+
+ROOM = Namespace('http://projects.bigasterisk.com/room/')
+
+ctx = ROOM['frontDoorWindowRfidCtx']
+
+def rdfGraphBody(body, headers):
+    g = Graph()
+    g.parse(StringInputSource(body), format='nt')
+    return g
+
+class OutputPage(cyclone.web.RequestHandler):
+    def put(self):
+        user = URIRef(self.request.headers['x-foaf-agent'])
+        arg = self.request.arguments
+        if arg.get('s') and arg.get('p'):
+            subj = URIRef(arg['s'][-1])
+            pred = URIRef(arg['p'][-1])
+            obj = URIRef(self.request.body)
+            stmt = (subj, pred, obj)
+        else:
+            g = rdfGraphBody(self.request.body, self.request.headers)
+            assert len(g) == 1, len(g)
+            stmt = g.triples((None, None, None)).next()
+        self._onStatement(user, stmt)
+    post = put
+    
+    def _onStatement(self, user, stmt):
+        # write rfid to new key, etc.
+        if stmt[1] == ROOM['keyContents']:
+            return
+        log.warn("ignoring %s", stmt)
+
+sensor = ROOM['frontDoorWindowRfid']
+
+class ReadLoop(object):
+    def __init__(self, reader, masterGraph):
+        self.reader = reader
+        self.masterGraph = masterGraph
+        self.log = {} # cardIdUri : most recent seentime
+
+        self.pollPeriodSecs = .2
+        self.expireSecs = 2
+        
+        task.LoopingCall(self.poll).start(self.pollPeriodSecs)
+        
+    def poll(self):
+        now = time.time()
+
+        self.flushOldReads(now)
+
+        card_id, text = self.reader.read()
+        if card_id is None:
+            return
+
+        cardIdUri = URIRef('http://bigasterisk.com/rfidCard/%s' % card_id)
+        textLit = Literal(text.rstrip())
+
+        is_new = cardIdUri not in self.log
+        self.log[cardIdUri] = now
+        if is_new:
+            self.startCardRead(cardIdUri, textLit)
+        
+    def flushOldReads(self, now):
+        for uri in self.log.keys():
+            if self.log[uri] < now - self.expireSecs:
+                self.endCardRead(uri)
+                del self.log[uri]
+
+    def startCardRead(self, cardUri, text):
+        p = Patch(addQuads=[(sensor, ROOM['reading'], cardUri, ctx),
+                            (cardUri, ROOM['cardText'], text, ctx)], delQuads=[])
+        self.masterGraph.patch(p)
+        self._sendOneshot([(sensor, ROOM['startReading'], cardUri),
+                            (cardUri, ROOM['cardText'], text)])
+
+    def endCardRead(self, cardUri):
+        delQuads = []
+        for spo in self.masterGraph._graph.triples((sensor, ROOM['reading'], cardUri)):
+            delQuads.append(spo + (ctx,))
+        for spo in self.masterGraph._graph.triples((cardUri, ROOM['cardText'], None)):
+            delQuads.append(spo + (ctx,))
+            
+        self.masterGraph.patch(Patch(addQuads=[], delQuads=delQuads))
+        
+    def _sendOneshot(self, oneshot):
+        body = (' '.join('%s %s %s .' % (s.n3(), p.n3(), o.n3())
+                         for s,p,o in oneshot)).encode('utf8')
+        url = 'http://bang6:9071/oneShot'
+        d = fetch(method='POST',
+                  url=url,
+                  headers={'Content-Type': ['text/n3']},
+                  postdata=body,
+                  timeout=5)
+        def err(e):
+            log.info('oneshot post to %r failed:  %s',
+                     url, e.getErrorMessage())
+        d.addErrback(err)
+
+                                                              
+        
+if __name__ == '__main__':
+    arg = docopt("""
+    Usage: rfid.py [options]
+
+    -v   Verbose
+    """)
+    log.setLevel(logging.INFO)
+    if arg['-v']:
+        enableTwistedLog()
+        log.setLevel(logging.DEBUG)
+
+    masterGraph = PatchableGraph()
+    reader = SimpleMFRC522()
+
+    loop = ReadLoop(reader, masterGraph)
+
+    port = 10012
+    reactor.listenTCP(port, cyclone.web.Application([
+        (r"/()", cyclone.web.StaticFileHandler,
+         {"path": ".", "default_filename": "index.html"}),
+        (r"/graph", CycloneGraphHandler, {'masterGraph': masterGraph}),
+        (r"/graph/events", CycloneGraphEventsHandler,
+         {'masterGraph': masterGraph}),
+        (r'/output', OutputPage),
+        ], masterGraph=masterGraph, debug=arg['-v']), interface='::')
+    log.warn('serving on %s', port)
+
+    reactor.run()