From 4dc103b330eb564b9722944c4f014e297f043efc Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Fri, 1 Apr 2016 23:46:42 -0700 Subject: [PATCH] Updates --- bbctrl.py | 113 +++++++++++- inevent/EventStream.py | 2 +- src/jade/index.jade | 14 +- src/jade/templates/admin-view.jade | 17 +- src/jade/templates/status-view.jade | 41 ++--- src/jade/templates/templated-input.jade | 17 +- src/js/admin-view.js | 17 ++ src/js/api.js | 51 ++++++ src/js/app.js | 16 +- src/js/status-view.js | 25 +-- src/resources/config-template.json | 234 ++++++++++++++---------- src/resources/css/pure-min.css | 11 ++ src/stylus/style.styl | 16 ++ 13 files changed, 403 insertions(+), 171 deletions(-) create mode 100644 src/js/api.js create mode 100644 src/resources/css/pure-min.css diff --git a/bbctrl.py b/bbctrl.py index daa3457..aa0da41 100755 --- a/bbctrl.py +++ b/bbctrl.py @@ -7,12 +7,13 @@ HTTP_PORT = 8080 HTTP_ADDR = '0.0.0.0' import os -from tornado import web, ioloop +from tornado import web, ioloop, escape from sockjs.tornado import SockJSRouter, SockJSConnection import json import serial import multiprocessing import time +import select import inevent from inevent.Constants import * @@ -29,6 +30,11 @@ config = { "verbose": False } + +with open('http/config-template.json', 'r') as f: + config_template = json.load(f) + + state = {} clients = [] @@ -36,6 +42,102 @@ input_queue = multiprocessing.Queue() output_queue = multiprocessing.Queue() +def encode_cmd(index, value, spec): + if spec['type'] == 'enum': value = spec['values'].index(value) + elif spec['type'] == 'bool': value = 1 if value else 0 + elif spec['type'] == 'percent': value /= 100.0 + + cmd = '${}{}={}'.format(index, spec['code'], value) + input_queue.put(cmd + '\n') + #print(cmd) + + +def encode_config_category(index, config, category): + for key, spec in category.items(): + if key in config: + encode_cmd(index, config[key], spec) + + +def encode_config(index, config, tmpl): + for category in tmpl.values(): + encode_config_category(index, config, category) + + +def update_config(config): + # Motors + tmpl = config_template['motors'] + for index in range(len(config['motors'])): + encode_config(index + 1, config['motors'][index], tmpl) + + # Axes + tmpl = config_template['axes'] + axes = 'xyzabc' + for axis in axes: + if not axis in config['axes']: continue + encode_config(axis, config['axes'][axis], tmpl) + + # Switches + tmpl = config_template['switches'] + for index in range(len(config['switches'])): + encode_config_category(index + 1, config['switches'][index], tmpl) + + # Spindle + tmpl = config_template['spindle'] + encode_config_category('', config['spindle'], tmpl) + + + +class APIHandler(web.RequestHandler): + def prepare(self): + self.json = {} + + if self.request.body: + try: + self.json = escape.json_decode(self.request.body) + except ValueError: + self.send_error(400, message = 'Unable to parse JSON.') + + + def set_default_headers(self): + self.set_header('Content-Type', 'application/json') + + + def write_error(self, status_code, **kwargs): + e = {} + e['message'] = str(kwargs['exc_info'][1]) + e['code'] = status_code + + self.write_json(e) + + + def write_json(self, data): + self.write(json.dumps(data)) + + +class LoadHandler(APIHandler): + def send_file(self, path): + with open(path, 'r') as f: + self.write_json(json.load(f)) + + def get(self): + try: + self.send_file('config.json') + except Exception as e: + print(e) + self.send_file('http/default-config.json') + + +class SaveHandler(APIHandler): + def post(self): + with open('config.json', 'w') as f: + json.dump(self.json, f) + + update_config(self.json) + print('Saved config') + self.write_json('ok') + + + class SerialProcess(multiprocessing.Process): def __init__(self, input_queue, output_queue): multiprocessing.Process.__init__(self) @@ -61,6 +163,9 @@ class SerialProcess(multiprocessing.Process): self.sp.flushInput() while True: + fds = [self.input_queue._reader.fileno(), self.sp] + ready = select.select(fds, [], [], 0.25)[0] + # look for incoming tornado request if not self.input_queue.empty(): data = self.input_queue.get() @@ -69,7 +174,7 @@ class SerialProcess(multiprocessing.Process): self.writeSerial(data) # look for incoming serial data - if (self.sp.inWaiting() > 0): + if self.sp.inWaiting() > 0: data = self.readSerial() # send it back to tornado self.output_queue.put(data) @@ -95,7 +200,7 @@ class Connection(SockJSConnection): -# check the queue for pending messages, and rely that to all connected clients +# check the queue for pending messages, and relay them to all connected clients def checkQueue(): while not output_queue.empty(): try: @@ -106,6 +211,8 @@ def checkQueue(): handlers = [ + (r'/api/load', LoadHandler), + (r'/api/save', SaveHandler), (r'/(.*)', web.StaticFileHandler, {'path': os.path.join(DIR, 'http/'), "default_filename": "index.html"}), diff --git a/inevent/EventStream.py b/inevent/EventStream.py index ab4b7e1..408e5ec 100644 --- a/inevent/EventStream.py +++ b/inevent/EventStream.py @@ -134,7 +134,7 @@ class EventStream(object): def __iter__(self): - """ + """s Required to make this class an iterator """ return self diff --git a/src/jade/index.jade b/src/jade/index.jade index 6431dd6..294e2f8 100644 --- a/src/jade/index.jade +++ b/src/jade/index.jade @@ -9,8 +9,7 @@ html(lang="en") title Buildbotics Controller - Web interface - link(rel="stylesheet", - href="http://yui.yahooapis.com/pure/0.6.0/pure-min.css") + link(rel="stylesheet", href="/css/pure-min.css") //if lte IE 8 link(rel="stylesheet", href="css/side-menu-old-ie.css") // [if gt IE 8] .pure-menu-children,.pure-menu-active>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;padding:.5em 0}.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar{display:none}.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-link,.pure-menu-disabled,.pure-menu-heading{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent}.pure-menu-active>.pure-menu-link,.pure-menu-link:hover,.pure-menu-link:focus{background-color:#eee}.pure-menu-selected .pure-menu-link,.pure-menu-selected .pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table td:first-child,.pure-table th:first-child{border-left-width:0}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} \ No newline at end of file diff --git a/src/stylus/style.styl b/src/stylus/style.styl index e684416..70d9fdf 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -1,3 +1,9 @@ +body + overflow-y scroll + +[v-cloak] + display none + .button-success:not([disabled]) background rgb(28, 184, 65) @@ -16,6 +22,11 @@ color #e5aa3d +.jog + button + width 3.5em + margin 0.25em + #menu .save display block @@ -42,11 +53,16 @@ label.units text-align left + textarea + width 24em + height 12em + .switch h3, .pure-control-group display inline-block + table.axes border-collapse collapse -- 2.27.0