changeset 461:cebc0134254a

last version of rc522 reader; i moved onto pn532 now Ignore-this: 37d50691ffec46f984a66c185cc27445
author drewp@bigasterisk.com
date Sat, 20 Apr 2019 23:33:39 -0700
parents 7051b8b4766a
children 7ec5945752b7
files service/rfid/index.html service/rfid/requirements.txt service/rfid/rfid.py
diffstat 3 files changed, 183 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/service/rfid/index.html	Sat Apr 20 23:32:36 2019 -0700
+++ b/service/rfid/index.html	Sat Apr 20 23:33:39 2019 -0700
@@ -1,14 +1,128 @@
 <!doctype html>
 <html>
   <head>
-    <title></title>
+    <title>rfid</title>
     <meta charset="utf-8" />
+    <meta name="mobile-web-app-capable" content="yes">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <script src="/lib/polymer/1.0.9/webcomponentsjs/webcomponents.min.js"></script>
+    <script src="/lib/require/require-2.3.3.js"></script>
+    <script>
+     requirejs.config({
+       paths: {
+         "streamed-graph": "/rdf/streamed-graph",
+         "quadstore": "/rdf/quadstore",
+         "async-module": "/lib/async/80f1793/async",
+         "async": "/lib/async/80f1793/async",
+         "jsonld-module": "/lib/jsonld.js/0.4.11/js/jsonld",
+         "jsonld": "/lib/jsonld.js/0.4.11/js/jsonld",
+         "rdfstore": "/lib/rdf_store/0.9.7/dist/rdfstore",
+         "moment": "/lib/moment.min",
+         "underscore": "/lib/underscore-1.5.2.min",
+       }
+     });
+    </script>
+    <script>
+     window.NS = {
+       dev: 'http://projects.bigasterisk.com/device/',
+       room: 'http://projects.bigasterisk.com/room/',
+       rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
+       sensor: 'http://bigasterisk.com/homeauto/sensor/',
+     };
+    </script>
+    <link rel="import" href="/lib/polymer/1.0.9/iron-ajax/iron-ajax.html">
+    <link rel="import" href="/rdf/streamed-graph.html">
+    <link rel="import" href="/lib/polymer/1.0.9/polymer/polymer.html">
+    <link rel="import" href="/rdf/rdf-oneshot.html">
+    <link rel="import" href="/rdf/rdf-uri.html">
   </head>
   <body>
-
-    Current read: graph.sensor.card  graph.sensor.card.text
+    <dom-module id="rfid-control">
+      <style>
+       button {
+           min-width: 60px;
+           min-height: 40px;
+       }
+       table {
+           border-collapse: collapse;
+       }
+       
+       td, th {
+           border: 1px solid gray;
+       }
+      </style>
+      <template>
+        <div>
+          <streamed-graph url="graph/events" graph="{{graph}}"></streamed-graph>
+          <!-- also get a graph of users so we can look up cards -->
+        </div>
+        
+        <iron-ajax id="rewrite" url="rewrite" method="POST"></iron-ajax>
+        
+        Current reads: 
+        <table>
+          <tr><th>Card UID</th><th>Card text</th><th></th></tr>
+          <template is="dom-repeat" items="{{currentReads}}">
+            <tr>
+              <td>{{item.uidDisplay}}</td>
+              <td>{{item.text}}</td>
+              <td>
+                <div id="form">
+                  <button on-click="rewrite">Rewrite</button>
+                </div>
+              </td>
+            </tr>
+          </template>
+        </table>
+        
+      </template>
+      <script>
+       HTMLImports.whenReady(function () {
+         Polymer({
+           is: 'rfid-control',
+           properties: {
+             graph: { type: Object, notify: true, observer: "_onGraph" },
+             currentReads: { type: Array, value: [] },
+           },
+           behaviors: [BigastUri],
+           _onGraph: function(graph) {
+             if (!graph.graph) return;
+             const env = graph.graph.store.rdf;
 
-    Write new key: [random id]   [Write]
-    
+             this.splice('currentReads', 0, this.currentReads.length);
+             graph.graph.quadStore.quads(
+               {subject: env.createNamedNode('room:frontDoorWindowRfid'),
+                predicate: env.createNamedNode('room:reading'),
+               },
+               (q) => {
+                 graph.graph.quadStore.quads(
+                   {subject: q.object,
+                    predicate: env.createNamedNode('room:cardText'),
+                   },
+                   (q2) => {
+                     this.push(
+                       'currentReads', {
+                         'cardUid': q.object,
+                         'uidDisplay': q.object.toString().replace(/.*\//, ""),
+                         'text': q2.object.toString()
+                       });
+                   });
+               });
+           },
+           rewrite: function(ev) {
+             const cardUid = ev.model.item.cardUid;
+
+             // ask for user first
+
+             this.$.rewrite.contentType = "application/json";
+             this.$.rewrite.body = {'cardUid': cardUid.toString(),
+                                    'user': "some foaf"};
+             this.$.rewrite.generateRequest();
+           }
+         });
+       });
+      </script>
+    </dom-module>
+    <rfid-control></rfid-control>
   </body>
 </html>
--- a/service/rfid/requirements.txt	Sat Apr 20 23:32:36 2019 -0700
+++ b/service/rfid/requirements.txt	Sat Apr 20 23:33:39 2019 -0700
@@ -1,6 +1,7 @@
 cyclone
+docopt
 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
-
+https://projects.bigasterisk.com/rdfdb/rdfdb-0.7.0.tar.gz
+https://github.com/drewp/MFRC522-python/archive/877f5568627b5e67d622c08191aae0f4ffd6dd32.zip
+git+http://github.com/drewp/scales.git@448d59fb491b7631877528e7695a93553bfaaa93#egg=scales
--- a/service/rfid/rfid.py	Sat Apr 20 23:32:36 2019 -0700
+++ b/service/rfid/rfid.py	Sat Apr 20 23:33:39 2019 -0700
@@ -6,14 +6,25 @@
 from twisted.internet import reactor, task
 import cyclone.web
 from cyclone.httpclient import fetch
-import logging, time
+import logging, time, json, random, string
 from MFRC522.SimpleMFRC522 import SimpleMFRC522
 from logsetup import log, enableTwistedLog
+import private
+from greplin import scales
+from greplin.scales.cyclonehandler import StatsHandler
 
 ROOM = Namespace('http://projects.bigasterisk.com/room/')
 
 ctx = ROOM['frontDoorWindowRfidCtx']
 
+cardOwner = {
+    URIRef('http://bigasterisk.com/rfidCard/93a7591a77'):
+    URIRef('http://bigasterisk.com/foaf.rdf#drewp'),
+}
+
+STATS = scales.collection('/web',
+                          scales.PmfStat('cardReadPoll'),
+)
 def rdfGraphBody(body, headers):
     g = Graph()
     g.parse(StringInputSource(body), format='nt')
@@ -41,6 +52,36 @@
             return
         log.warn("ignoring %s", stmt)
 
+def uidUri(card_id):
+    return URIRef('http://bigasterisk.com/rfidCard/%010x' % card_id)
+        
+def uidArray(uri):
+    prefix, h = uri.rsplit('/', 1)
+    if prefix != 'http://bigasterisk.com/rfidCard':
+        raise ValueError(uri)
+    return [int(h[i * 2: i * 2 + 2], 16) for i in range(0, len(h), 2)]
+        
+class Rewrite(cyclone.web.RequestHandler):
+    def post(self):
+        agent = URIRef(self.request.headers['x-foaf-agent'])
+        body = json.loads(self.request.body)
+
+        _, uid = reader.read_id()
+        log.info('current card id: %r %r', _, uid)
+        if uid is None:
+            self.set_status(404, "no card present")
+            # maybe retry a few more times since the card might be nearby
+            return
+            
+        text = ''.join(random.choice(string.uppercase) for n in range(32))
+        log.info('%s rewrites %s to %s, to be owned by %s', 
+                 agent, uid, text, body['user'])
+        
+        #reader.KEY = private.rfid_key
+        reader.write(uid, text)
+        log.info('done with write')
+
+    
 sensor = ROOM['frontDoorWindowRfid']
 
 class ReadLoop(object):
@@ -49,22 +90,25 @@
         self.masterGraph = masterGraph
         self.log = {} # cardIdUri : most recent seentime
 
-        self.pollPeriodSecs = .2
+        self.pollPeriodSecs = .1
         self.expireSecs = 2
         
         task.LoopingCall(self.poll).start(self.pollPeriodSecs)
-        
+
+    @STATS.cardReadPoll.time()
     def poll(self):
         now = time.time()
 
         self.flushOldReads(now)
 
         card_id, text = self.reader.read()
-        if card_id is None:
+        if card_id is None or text == '':
+            # text=='' could be legit, but it's probably a card that's
+            # still being read.
             return
 
-        cardIdUri = URIRef('http://bigasterisk.com/rfidCard/%s' % card_id)
-        textLit = Literal(text.rstrip())
+        cardIdUri = uidUri(card_id)
+        textLit = Literal(text.rstrip().decode('ascii', 'replace'))
 
         is_new = cardIdUri not in self.log
         self.log[cardIdUri] = now
@@ -79,16 +123,20 @@
 
     def startCardRead(self, cardUri, text):
         p = Patch(addQuads=[(sensor, ROOM['reading'], cardUri, ctx),
-                            (cardUri, ROOM['cardText'], text, ctx)], delQuads=[])
+                            (cardUri, ROOM['cardText'], text, ctx)],
+                  delQuads=[])
         self.masterGraph.patch(p)
+        log.info('read card: id=%s %r', cardUri, str(text))
         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)):
+        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)):
+        for spo in self.masterGraph._graph.triples(
+                (cardUri, ROOM['cardText'], None)):
             delQuads.append(spo + (ctx,))
             
         self.masterGraph.patch(Patch(addQuads=[], delQuads=delQuads))
@@ -121,7 +169,7 @@
         log.setLevel(logging.DEBUG)
 
     masterGraph = PatchableGraph()
-    reader = SimpleMFRC522()
+    reader = SimpleMFRC522(gain=0x07)
 
     loop = ReadLoop(reader, masterGraph)
 
@@ -133,6 +181,8 @@
         (r"/graph/events", CycloneGraphEventsHandler,
          {'masterGraph': masterGraph}),
         (r'/output', OutputPage),
+        (r'/rewrite', Rewrite),
+        (r'/stats/(.*)', StatsHandler, {'serverName': 'rfid'}),
         ], masterGraph=masterGraph, debug=arg['-v']), interface='::')
     log.warn('serving on %s', port)