changeset 389:bba6672a0ef8

rfid reader service Ignore-this: 26df9a4be891c26722311cf89d1d7cf7
author drewp@bigasterisk.com
date Thu, 03 Jan 2019 21:52:27 -0800
parents c146fa2bc7d4
children e220d5c875b3
files service/rfid/Dockerfile service/rfid/Dockerfile.pi service/rfid/index.html service/rfid/makefile service/rfid/requirements.txt service/rfid/rfid.py
diffstat 6 files changed, 210 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid/Dockerfile	Thu Jan 03 21:52:27 2019 -0800
@@ -0,0 +1,15 @@
+FROM bang6:5000/base_x86
+
+WORKDIR /opt
+
+
+
+COPY requirements.txt .
+
+RUN pip install -r requirements.txt
+
+COPY *.py *.html *.js ./
+
+EXPOSE 10012
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid/Dockerfile.pi	Thu Jan 03 21:52:27 2019 -0800
@@ -0,0 +1,12 @@
+FROM bang6:5000/base_pi
+
+WORKDIR /opt
+
+COPY requirements.txt .
+
+RUN pip install -r requirements.txt
+
+COPY *.py *.html *.js ./
+
+EXPOSE 10012
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid/index.html	Thu Jan 03 21:52:27 2019 -0800
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+  <head>
+    <title></title>
+    <meta charset="utf-8" />
+  </head>
+  <body>
+
+    Current read: graph.sensor.card  graph.sensor.card.text
+
+    Write new key: [random id]   [Write]
+    
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid/makefile	Thu Jan 03 21:52:27 2019 -0800
@@ -0,0 +1,24 @@
+SERVICE=rfid
+
+build_image:
+	rm -rf tmp_ctx
+	mkdir -p tmp_ctx
+	cp -a Dockerfile ../../lib/*.py *.py *.txt *.html MFRC522-python tmp_ctx
+	docker build --network=host -t bang6:5000/$(SERVICE)_x86:latest tmp_ctx
+	docker push bang6:5000/$(SERVICE)_x86:latest
+	rm -rf tmp_ctx
+
+
+build_image_pi:
+	rm -rf tmp_ctx
+	mkdir -p tmp_ctx
+	cp -a Dockerfile.pi ../../lib/*.py *.py *.txt *.html MFRC522-python tmp_ctx
+	docker build --file Dockerfile.pi --network=host -t bang6:5000/$(SERVICE)_pi:latest tmp_ctx
+	docker push bang6:5000/$(SERVICE)_pi:latest
+	rm -rf tmp_ctx
+
+shell:
+	docker run --rm -it --cap-add SYS_PTRACE --net=host bang6:5000/$(SERVICE)_x86:latest  /bin/sh
+
+local_run:
+	docker run --rm -it --net=host bang6:5000/$(SERVICE)_x86:latest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/service/rfid/requirements.txt	Thu Jan 03 21:52:27 2019 -0800
@@ -0,0 +1,6 @@
+cyclone
+rdflib-jsonld==0.4.0
+rdflib==4.2.2
+https://projects.bigasterisk.com/rdfdb/rdfdb-0.6.0.tar.gz
+https://github.com/drewp/MFRC522-python/archive/drewp/m4.zip
+
--- /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()