Changeset - c8cffe82b537
[Not reviewed]
default
0 4 0
Drew Perttula - 8 years ago 2017-05-19 07:33:11
drewp@bigasterisk.com
collector gui updates
Ignore-this: d8ab8247884e674a2332684297920c6a
4 files changed with 9 insertions and 2 deletions:
0 comments (0 inline, 0 general)
bin/collector
Show inline comments
 
@@ -6,131 +6,134 @@ from txzmq import ZmqEndpoint, ZmqFactor
 
import json
 
import logging
 
import optparse
 
import time
 
import traceback
 
import cyclone.web, cyclone.websocket
 
from greplin import scales
 

	
 
from run_local import log
 
from lib.cycloneerr import PrettyErrorHandler
 
from light9.collector.output import EnttecDmx, Udmx
 
from light9.collector.collector import Collector
 
from light9.namespaces import L9
 
from light9 import networking
 
from light9.rdfdb.syncedgraph import SyncedGraph
 
from light9.greplin_cyclone import StatsForCyclone
 

	
 
def parseJsonMessage(msg):
 
    body = json.loads(msg)
 
    settings = []
 
    for device, attr, value in body['settings']:
 
        settings.append((URIRef(device), URIRef(attr), Literal(value)))
 
    return body['client'], body['clientSession'], settings, body['sendTime']
 

	
 
def startZmq(port, collector):
 
    stats = scales.collection('/zmqServer',
 
                              scales.PmfStat('setAttr'))
 
    
 
    zf = ZmqFactory()
 
    addr = 'tcp://*:%s' % port
 
    log.info('creating zmq endpoint at %r', addr)
 
    e = ZmqEndpoint('bind', addr)
 
    s = ZmqPullConnection(zf, e)
 
    def onPull(message):
 
        with stats.setAttr.time():
 
            # todo: new compressed protocol where you send all URIs up
 
            # front and then use small ints to refer to devices and
 
            # attributes in subsequent requests.
 
            client, clientSession, settings, sendTime = parseJsonMessage(message[0])
 
            collector.setAttrs(client, clientSession, settings, sendTime)
 
    s.onPull = onPull
 

	
 
class WebListeners(object):
 
    def __init__(self):
 
        self.clients = []
 

	
 
    def addClient(self, client):
 
        self.clients.append([client, {}])
 
        log.info('added client %s', client)
 

	
 
    def delClient(self, client):
 
        self.clients = [[c, t] for c, t in self.clients if c != client]
 
        log.info('delClient %s, %s left', client, len(self.clients))
 
        
 
    def outputAttrsSet(self, dev, attrs, outputMap):
 
        now = time.time()
 

	
 
        msg = self.makeMsg(dev, attrs, outputMap)
 
        
 
        for client, seen in self.clients:
 
            for m, t in seen.items():
 
                if t < now - 5:
 
                    del seen[m]
 
            if msg in seen:
 
                continue
 
            seen[msg] = now
 
            client.sendMessage(msg)
 

	
 
    def makeMsg(self, dev, attrs, outputMap):
 
        attrRows = []
 
        for attr, val in attrs.items():
 
            output, index = outputMap[(dev, attr)]
 
            attrRows.append({'attr': attr.rsplit('/')[-1],
 
                             'val': val,
 
                             'chan': (output.shortId(), index)})
 
                             'chan': (output.shortId(), index + 1)})
 
        attrRows.sort(key=lambda r: r['chan'])
 
        for row in attrRows:
 
            row['chan'] = '%s %s' % (row['chan'][0], row['chan'][1])
 

	
 
        msg = json.dumps({'outputAttrsSet': {'dev': dev, 'attrs': attrRows}}, sort_keys=True)
 
        return msg
 
    
 
class Updates(cyclone.websocket.WebSocketHandler):
 

	
 
    def connectionMade(self, *args, **kwargs):
 
        log.info('socket connect %s', self)
 
        self.settings.listeners.addClient(self)
 

	
 
    def connectionLost(self, reason):
 
        self.settings.listeners.delClient(self)
 

	
 
    def messageReceived(self, message):
 
        json.loads(message)
 

	
 
stats = scales.collection('/webServer', scales.PmfStat('setAttr'))
 

	
 
class Attrs(PrettyErrorHandler, cyclone.web.RequestHandler):
 
    def put(self):
 
        with stats.setAttr.time():
 
            client, clientSession, settings, sendTime = parseJsonMessage(self.request.body)
 
            self.settings.collector.setAttrs(client, clientSession, settings, sendTime)
 
            self.set_status(202)
 

	
 

	
 
            
 
def launch(graph, doLoadTest=False):
 
    try:
 
        # todo: drive outputs with config files
 
        outputs = [
 
            #EnttecDmx(L9['output/dmx0/'], '/dev/dmx0', 80),
 
            Udmx(L9['output/udmx/'], 510),
 
        ]
 
    except Exception as e:
 
        log.error("setting up outputs: %r", e)
 
        traceback.print_exc()
 
        raise
 
    listeners = WebListeners()
 
    c = Collector(graph, outputs, listeners)
 

	
 
    startZmq(networking.collectorZmq.port, c)
 
    
 
    reactor.listenTCP(networking.collector.port,
 
                      cyclone.web.Application(handlers=[
 
                          (r'/()', cyclone.web.StaticFileHandler,
 
                           {"path" : "light9/collector/web", "default_filename" : "index.html"}),
 
                          (r'/updates', Updates),
 
                          (r'/attrs', Attrs),
 
                          (r'/stats', StatsForCyclone),
 
                      ], collector=c, listeners=listeners),
 
                      interface='::')
 
    log.info('serving http on %s, zmq on %s', networking.collector.port,
 
             networking.collectorZmq.port)
 
    if doLoadTest:
 
        # in a subprocess since we don't want this client to be
bin/homepageConfig
Show inline comments
 
#!bin/python
 
from run_local import log
 
from rdflib import RDF, URIRef
 
from light9 import networking, showconfig
 
from light9.namespaces import L9
 
from urlparse import urlparse
 
from urllib import splitport
 

	
 
from light9.rdfdb.syncedgraph import SyncedGraph
 
from twisted.internet import reactor
 

	
 
graph = showconfig.getGraph()
 

	
 
netHome = graph.value(showconfig.showUri(), L9['networking'])
 
webServer = graph.value(netHome, L9['webServer'])
 
if not webServer:
 
    raise ValueError('no %r :webServer' % netHome)
 
print "listen %s;" % splitport(urlparse(webServer).netloc)[1]
 

	
 
def location(path, server):
 
    print """
 
    location /%(path)s/ {
 

	
 
      # for websocket
 
      proxy_http_version 1.1;
 
      proxy_set_header Upgrade $http_upgrade;
 
      proxy_set_header Connection "upgrade";
 
      proxy_set_header Host $host;
 

	
 
      proxy_pass %(server)s;
 
      proxy_buffering off;
 
      rewrite /[^/]+/(.*) /$1 break;
 
    }""" % vars()
 

	
 
for role, server in sorted(graph.predicate_objects(netHome)):
 
    if not server.startswith('http') or role == L9['webServer']:
 
        continue
 
    path = graph.value(role, L9['urlPath'])
 
    if not path:
 
        continue
 
    server = server.rstrip('/')
 
    location(path, server)
 

	
 

	
 

	
 
showPath = showconfig.showUri().split('/', 3)[-1]
 
print """
 
    location /%(path)s {
 
      root %(root)s;
 
    }""" % {'path': showPath,
 
            'root': showconfig.root()[:-len(showPath)]}
light9/collector/web/index.html
Show inline comments
 
<!doctype html>
 
<html>
 
  <head>
 
    <title>collector</title>
 
    <meta charset="utf-8" />
 
    <script src="/lib/webcomponentsjs/webcomponents-lite.min.js"></script>
 
    <link rel="import" href="/lib/polymer/polymer.html">
 
    <link rel="import" href="/lib/iron-ajax/iron-ajax.html">
 
    <link rel="import" href="../rdfdb-synced-graph.html">
 
    <script src="/lib/N3.js-pull61/browser/n3-browser.js"></script>
 
    <script src="/lib/async/dist/async.js"></script>
 
    <script src="/lib/underscore/underscore-min.js"></script>
 

	
 
    <link rel="stylesheet"  href="/style.css">
 
    <style>
 
     td { white-space: nowrap; }
 
    </style>
 
  </head>
 
  <body>
 

	
 
    <dom-module id="light9-collector-device">
 
      <template>
 
        <style>
 
         :host {
 
             display: block;
 
             break-inside: avoid-column;
 
         }
 
         td.nonzero {
 
             background: #310202;
 
             color: #e25757;
 
         }
 
         td.full {
 
             background: #2b0000;
 
             color: red;
 
             font-weight: bold;
 
         }
 
        </style>
 
        <h3>{{label}}</h3>
 
        <table class="borders">
 
          <tr>
 
            <th>output attr</th>
 
            <th>value</th>
 
            <th>output chan</th>
 
          </tr>
 
          <template is="dom-repeat" items="{{attrs}}">
 
            <tr>
 
              <td>{{item.attr}}</td>
 
              <td class$="{{item.valClass}}">{{item.val}} →</td>
 
              <td>{{item.chan}}</td>
 
            </tr>
 
          </template>
 

	
 
      </template>
 
      <script>
 
       HTMLImports.whenReady(function () {
 
           Polymer({
 
               is: "light9-collector-device",
 
               properties: {
 
                   graph: {type: Object, notify: true},
 
                   uri: {type: String, notify: true},
 
                   label: {type: String, notify: true},
 
                   attrs: {type: Array, notify: true},
 
               },
 
               observers: [
 
                   "setLabel(graph, uri)",
makefile
Show inline comments
 
NOSEARGS="--no-path-adjustment light9.rdfdb.rdflibpatch light9.rdfdb.patch light9.effecteval.test_effect light9.collector light9.rdfdb.graphfile_test light9.paint"
 
NOSEARGS="--no-path-adjustment light9.rdfdb.rdflibpatch light9.rdfdb.patch light9.effecteval.test_effect light9.collector light9.rdfdb.graphfile_test light9.paint light9.effect"
 

	
 
tests:
 
	eval env/bin/nosetests -x $(NOSEARGS)
 

	
 
tests_watch:
 
	eval env/bin/nosetests --with-watcher $(NOSEARGS)
 

	
 

	
 
tests_coverage:
 
	eval env/bin/nosetests --with-coverage --cover-erase --cover-html --cover-html-dir=/tmp/light9-cov/  --cover-package=light9 --cover-branches $(NOSEARGS)
 

	
 
test_js_init:
 
	npm install
 

	
 
test_js:
 
	coffee -c light9/web/*.coffee
 
	node_modules/mocha/bin/mocha --compilers coffee:coffee-script/register --globals window,N3 light9/web/graph_test.coffee
 

	
 
test_js_watch:
 
	# have coffee continuously running
 
	watch -c node_modules/mocha/bin/mocha --compilers coffee:coffee-script/register --globals window,N3 light9/web/graph_test.coffee --colors
 

	
 
# needed packages: python-gtk2 python-imaging
 

	
 
binexec:
 
	chmod a+x bin/*
 

	
 
install_python_deps: link_to_sys_packages
 
	env/bin/pip install twisted
 
	env/bin/pip install -U -r requirements.txt
 

	
 
DP=/usr/lib/python2.7/dist-packages
 
SP=env/lib/python2.7/site-packages
 

	
 
link_to_sys_packages:
 
	# http://stackoverflow.com/questions/249283/virtualenv-on-ubuntu-with-no-site-packages
 
	ln -sf $(DP)/glib $(SP)/
 
	ln -sf $(DP)/gi $(SP)/
 
	ln -sf $(DP)/gobject $(SP)/
 
	ln -sf $(DP)/cairo $(SP)/
 
	ln -sf $(DP)/gtk-2.0 $(SP)/
 
	ln -sf $(DP)/pygtk.py $(SP)/
 
	ln -sf $(DP)/pygtk.pth $(SP)/
 
	ln -sf $(DP)/pygst.pth $(SP)/
 
	ln -sf $(DP)/pygst.py $(SP)/
 
	ln -sf $(DP)/gst-0.10 $(SP)/
 
	ln -sf $(DP)/goocanvasmodule.so $(SP)/
 
	ln -sf $(DP)/cv2.x86_64-linux-gnu.so $(SP)/
0 comments (0 inline, 0 general)