Mercurial > code > home > repos > homeauto
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()