# HG changeset patch # User drewp@bigasterisk.com # Date 1454164800 28800 # Node ID 4ebb5cc300022c653784a7391bb07a453cc585bc # Parent 4e91f3ec460b6ddaf34f799e0101fa85b72fd520 server/browser graph sync. cut dependency on the WS version. merge some changes between arduino/pi code. Ignore-this: cf7d20d54e134e8ff33a9ee405610846 diff -r 4e91f3ec460b -r 4ebb5cc30002 lib/patchablegraph.py --- a/lib/patchablegraph.py Thu Jan 28 02:48:54 2016 -0800 +++ b/lib/patchablegraph.py Sat Jan 30 06:40:00 2016 -0800 @@ -10,8 +10,17 @@ 5. Client queries its graph with low-level APIs or client-side sparql. 6. When the graph changes, the client knows and can update itself at low or high granularity. + + +See also: +* http://iswc2007.semanticweb.org/papers/533.pdf RDFSync: efficient remote synchronization of RDF +models +* https://www.w3.org/2009/12/rdf-ws/papers/ws07 Supporting Change Propagation in RDF +* https://www.w3.org/DesignIssues/lncs04/Diff.pdf Delta: an ontology for the distribution of +differences between RDF graphs + """ -import sys, json +import sys, json, logging import cyclone.sse sys.path.append("/my/proj/light9") from light9.rdfdb.grapheditapi import GraphEditApi @@ -20,6 +29,8 @@ from rdflib_jsonld.serializer import from_rdf from cycloneerr import PrettyErrorHandler +log = logging.getLogger('patchablegraph') + def writeGraphResponse(req, graph, acceptHeader): if acceptHeader == 'application/nquads': req.set_header('Content-type', 'application/nquads') @@ -46,6 +57,11 @@ 'deletes': from_rdf(_graphFromQuads2(p.delQuads)), }}) +def graphAsJson(g): + # This is not the same as g.serialize(format='json-ld')! That + # version omits literal datatypes. + return json.dumps(from_rdf(g)) + class PatchableGraph(GraphEditApi): """ Master graph that you modify with self.patch, and we get the @@ -68,6 +84,9 @@ for ob in self._observers: ob(patchAsJson(p)) + def asJsonLd(self): + return graphAsJson(self._graph) + def addObserver(self, onPatch): self._observers.append(onPatch) @@ -102,10 +121,9 @@ self.masterGraph = masterGraph def bind(self): - self.sendEvent( - message=self.masterGraph.serialize(None, format='json-ld', - indent=None), - event='fullGraph') + graphJson = self.masterGraph.asJsonLd() + log.debug("send fullGraph event: %s", graphJson) + self.sendEvent(message=graphJson, event='fullGraph') self.masterGraph.addObserver(self.onPatch) def onPatch(self, patchJson): diff -r 4e91f3ec460b -r 4ebb5cc30002 service/arduinoNode/arduinoNode.py --- a/service/arduinoNode/arduinoNode.py Thu Jan 28 02:48:54 2016 -0800 +++ b/service/arduinoNode/arduinoNode.py Sat Jan 30 06:40:00 2016 -0800 @@ -41,7 +41,8 @@ ACTION_BASE = 10 # higher than any of the fixed command numbers -CTX = ROOM['arduinosOn%s' % socket.gethostname()] +hostname = socket.gethostname() +CTX = ROOM['arduinosOn%s' % hostname] class Config(object): def __init__(self, masterGraph): @@ -74,10 +75,8 @@ self.masterGraph = masterGraph self.dev = dev - self.masterGraph.patch(Patch(addQuads=[ - (HOST[socket.gethostname()], ROOM['connectedTo'], self.uri, CTX), - ])) - + self.masterGraph.patch(Patch(addQuads=self.staticStmts())) + # The order of this list needs to be consistent between the # deployToArduino call and the poll call. self._devs = devices.makeDevices(configGraph, self.uri) @@ -98,7 +97,6 @@ 'dev': self.dev, 'baudrate': self.baudrate, 'devices': [d.description() for d in self._devs], - 'graph': 'http://%s6:9059/graph' % socket.gethostname(), #todo } def open(self): @@ -143,14 +141,21 @@ def _exportToGraphite(self): # note this is writing way too often- graphite is storing at a lower res now = time.time() + # 20 sec is not precise; just trying to reduce wifi traffic + if getattr(self, 'lastGraphiteExport', 0) + 20 > now: + return + self.lastGraphiteExport = now + log.debug('graphite export:') # objects of these statements are suitable as graphite values. - graphitePredicates = {ROOM['temperatureF']} + graphitePredicates = {ROOM['temperatureF']} + # bug: one sensor can have temp and humid- this will be ambiguous for s, graphiteName in self.configGraph.subject_objects(ROOM['graphiteName']): for group in self._statementsFromInputs.values(): for stmt in group: if stmt[0] == s and stmt[1] in graphitePredicates: + log.debug(' sending %s -> %s', stmt[0], graphiteName) self._carbon.send(graphiteName, stmt[2].toPython(), now) - + def outputStatements(self, stmts): unused = set(stmts) for dev in self._devs: @@ -176,19 +181,24 @@ # should be good enough. The right answer is to give # each dev the masterGraph for it to write to. self.syncMasterGraphToHostStatements(dev) - log.info("success") + log.info("output and masterGraph sync complete") if unused: log.info("Board %s doesn't care about these statements:", self.uri) for s in unused: - log.info("%r", s) + log.warn("%r", s) def syncMasterGraphToHostStatements(self, dev): hostStmtCtx = URIRef(dev.uri + '/host') newQuads = inContext(dev.hostStatements(), hostStmtCtx) - self.masterGraph.patchSubgraph(hostStmtCtx, newQuads) - + p = self.masterGraph.patchSubgraph(hostStmtCtx, newQuads) + log.debug("patch master with these host stmts %s", p) + + def staticStmts(self): + return [(HOST[hostname], ROOM['connectedTo'], self.uri, CTX)] + def generateArduinoCode(self): - code = write_arduino_code.writeCode(self.baudrate, self._devs, self._devCommandNum) + code = write_arduino_code.writeCode(self.baudrate, self._devs, + self._devCommandNum) code = write_arduino_code.indent(code) cksum = hashlib.sha1(code).hexdigest() code = code.replace('CODE_CHECKSUM', cksum) @@ -300,10 +310,9 @@ class Boards(cyclone.web.RequestHandler): def get(self): - self.set_header('Content-type', 'application/json') self.write(json.dumps({ - 'host': socket.gethostname(), + 'host': hostname, 'boards': [b.description() for b in self.settings.boards] }, indent=2)) @@ -344,7 +353,7 @@ b.startPolling() - app = cyclone.web.Application([ + reactor.listenTCP(9059, cyclone.web.Application([ (r"/()", cyclone.web.StaticFileHandler, { "path": "static", "default_filename": "index.html"}), (r'/static/(.*)', cyclone.web.StaticFileHandler, {"path": "static"}), @@ -354,8 +363,7 @@ (r'/output', OutputPage), (r'/arduinoCode', ArduinoCode), (r'/dot', Dot), - ], config=config, boards=boards) - reactor.listenTCP(9059, app, interface='::') + ], config=config, boards=boards), interface='::') reactor.run() main() diff -r 4e91f3ec460b -r 4ebb5cc30002 service/arduinoNode/devices.py --- a/service/arduinoNode/devices.py Thu Jan 28 02:48:54 2016 -0800 +++ b/service/arduinoNode/devices.py Sat Jan 30 06:40:00 2016 -0800 @@ -51,7 +51,7 @@ return { 'uri': self.uri, 'className': self.__class__.__name__, - 'pinNumber': self.pinNumber, + 'pinNumber': getattr(self, 'pinNumber', None), 'outputPatterns': self.outputPatterns(), 'watchPrefixes': self.watchPrefixes(), 'outputWidgets': self.outputWidgets(), diff -r 4e91f3ec460b -r 4ebb5cc30002 service/arduinoNode/static/index.html --- a/service/arduinoNode/static/index.html Thu Jan 28 02:48:54 2016 -0800 +++ b/service/arduinoNode/static/index.html Sat Jan 30 06:40:00 2016 -0800 @@ -1,7 +1,7 @@
-