Fixed config, implemented configuration backup, restore, reset and firmware check...
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 3 Jan 2017 13:14:45 +0000 (05:14 -0800)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 3 Jan 2017 13:14:45 +0000 (05:14 -0800)
27 files changed:
.gitignore
MANIFEST.in [new file with mode: 0644]
Makefile
package.json
scripts/install.sh [new file with mode: 0755]
scripts/setup_rpi.sh
scripts/upgrade-bbctrl [new file with mode: 0755]
setup.cfg [new file with mode: 0644]
setup.py
src/jade/index.jade
src/jade/templates/admin-view.jade
src/jade/templates/axis-view.jade [deleted file]
src/jade/templates/control-view.jade
src/jade/templates/message.jade [new file with mode: 0644]
src/js/admin-view.js
src/js/api.js
src/js/app.js
src/js/axis-view.js [deleted file]
src/js/control-view.js
src/js/main.js
src/js/message.js [new file with mode: 0644]
src/py/bbctrl/APIHandler.py
src/py/bbctrl/Config.py
src/py/bbctrl/Web.py
src/resources/config-template.json
src/resources/default-config.json [deleted file]
src/stylus/style.styl

index 150b38aa48e697e49c25c5d26c0e45bc79d57309..745bae7e0486c8eb1c426c55baf0226ddd30a308 100644 (file)
@@ -1,6 +1,9 @@
 .sconf_temp/
 .sconsign.dblite
 /build
+/dist
+/pkg
+/mnt
 node_modules
 *~
 \#*
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644 (file)
index 0000000..56d2aa1
--- /dev/null
@@ -0,0 +1,2 @@
+recursive-include src/py/bbctrl/http *
+include package.json README.md scripts/install.sh
index 55b92a1045af671fe86052e2d548352113d854c4..762fc568b36157197f856ee3adc5067afade714c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,11 @@ TEMPLS    := $(wildcard src/jade/templates/*.jade)
 
 RSYNC_EXCLUDE := \*.pyc __pycache__ \*.egg-info \\\#* \*~ .\\\#\*
 RSYNC_EXCLUDE := $(patsubst %,--exclude %,$(RSYNC_EXCLUDE))
-RSYNC_OPTS := $(RSYNC_EXCLUDE) -rLv --no-g --delete --force
+RSYNC_OPTS := $(RSYNC_EXCLUDE) -rv --no-g --delete --force
+
+VERSION := $(shell sed -n 's/^.*"version": "\([^"]*\)",.*$$/\1/p' package.json)
+PKG_NAME := bbctrl-$(VERSION)
+PUB_PATH := root@buildbotics.com:/var/www/buildbotics.com/bbctrl
 
 ifndef DEST
 DEST=mnt
@@ -29,10 +33,15 @@ WATCH := src/jade src/jade/templates src/stylus src/js src/resources Makefile
 
 all: html css js static
 
-copy: all
-       mkdir -p $(DEST)/bbctrl/src/py $(DEST)/bbctrl/build
-       rsync $(RSYNC_OPTS) src/py $(DEST)/bbctrl/src/
-       rsync $(RSYNC_OPTS) setup.py README.md $(DEST)/bbctrl
+copy: pkg
+       rsync $(RSYNC_OPTS) pkg/$(PKG_NAME)/ $(DEST)/bbctrl/
+
+pkg: all
+       ./setup.py sdist
+
+publish: pkg
+       echo -n $(VERSION) > dist/latest.txt
+       rsync $(RSYNC_OPTS) dist/$(PKG_NAME).tar.bz2 dist/latest.txt $(PUB_PATH)/
 
 mount:
        mkdir -p $(DEST)
@@ -102,9 +111,9 @@ tidy:
        rm -f $(shell find "$(DIR)" -name \*~)
 
 clean: tidy
-       rm -rf build html
+       rm -rf build html pkg
 
 dist-clean: clean
        rm -rf node_modules
 
-.PHONY: all install html css static templates clean tidy copy mount umount
+.PHONY: all install html css static templates clean tidy copy mount umount pkg
index eff859656903f3c89312d089a69f34a57a3d9e2c..162db98d99c301483cfd0fe1c102892490e73a90 100644 (file)
@@ -1,6 +1,8 @@
 {
   "name": "bbctrl",
-  "private": true,
+  "version": "0.1.5",
+  "homepage": "https://github.com/buildbotics/rpi-firmware",
+  "license": "GPL 3+",
 
   "dependencies": {
     "autoprefixer": ">=3.0.0",
diff --git a/scripts/install.sh b/scripts/install.sh
new file mode 100755 (executable)
index 0000000..d7084d0
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+sudo ./setup.py install
+sudo service bbctrl restart
index 2ee7118c63a56468b03e04d2f7a8efc326b153d3..8f02d904b6fe395972d2ca47a658d10279946e8a 100755 (executable)
@@ -95,6 +95,9 @@ cp bbctrl.init.d /etc/init.d/bbctrl
 chmod +x /etc/init.d/bbctrl
 update-rc.d bbctrl defaults
 
+# Install upgrade script
+cp upgrade-bbctrl /usr/local/bin
+
 # Disable Pi 3 USART BlueTooth swap
 echo -e "\ndtoverlay=pi3-disable-bt" >> /boot/config.txt
 # sudo systemctl disable hciuart
diff --git a/scripts/upgrade-bbctrl b/scripts/upgrade-bbctrl
new file mode 100755 (executable)
index 0000000..44454f2
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash -e
+
+(
+    flock -n 9
+
+    VERSION=$(curl -s https://buildbotics.com/bbctrl/latest.txt)
+    PKG_NAME=bbctrl-$VERSION
+    PKG=$PKG_NAME.tar.bz2
+    PKG_URL=https://buildbotics.com/bbctrl/$PKG
+
+    logger Installing bbctrl firmware $VERSION
+
+    echo Downloading $PKG_URL
+    curl -s $PKG_URL > $PKG
+
+    echo Unpacking $PKG
+    tar xf $PKG
+
+    echo Installing $PKG
+    (cd $PKG_NAME; ./scripts/install.sh)
+
+    echo Cleaning up
+    rm -rf $PKG_NAME $PKG
+
+    echo Success
+
+    logger bbctrl firmware $VERSION installed
+
+) 9> /var/lock/bbctrl.upgrade.lock
diff --git a/setup.cfg b/setup.cfg
new file mode 100644 (file)
index 0000000..ba15267
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[sdist]
+formats=bztar
index b6105b37699e4be4d392cb882043bf9a0c153093..635645095c6c73b1d9742a5d3eb2e31aa870f616 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -1,21 +1,24 @@
 #!/usr/bin/env python3
 
 from setuptools import setup
+import json
+
+pkg = json.load(open('package.json', 'r'))
+
 
 setup(
-    name = 'bbctrl',
-    version = '0.0.2',
+    name = pkg['name'],
+    version = pkg['version'],
     description = 'Buildbotics Machine Controller',
     long_description = open('README.md', 'rt').read(),
     author = 'Joseph Coffland',
     author_email = 'joseph@buildbotics.org',
     platforms = ['any'],
-    license = 'GPL 3+',
-    url = 'https://github.com/buildbotics/rpi-firmware',
+    license = pkg['license'],
+    url = pkg['homepage'],
     package_dir = {'': 'src/py'},
     packages = ['bbctrl', 'inevent', 'lcd'],
     include_package_data = True,
-    eager_resources = ['bbctrl/http/*'],
     entry_points = {
         'console_scripts': [
             'bbctrl = bbctrl:run'
index e5c8b9eb92e01bd7182fb4623719d3be0b4136e5..b66473047fd560037a13ba3c3ad9f865d4c38a15 100644 (file)
@@ -45,12 +45,6 @@ html(lang="en")
             li.pure-menu-item(v-for="motor in config.motors")
               a.pure-menu-link(:href="'#motor:' + $index") Motor {{$index}}
 
-            li.pure-menu-heading
-              a.pure-menu-link(href="#axis:x") Axes
-
-            li.pure-menu-item(v-for="axis in 'xyzabc'")
-              a.pure-menu-link(href="#axis:{{axis}}") {{axis | capitalize}} Axis
-
             li.pure-menu-heading
               a.pure-menu-link(href="#spindle") Spindle
 
@@ -83,6 +77,8 @@ html(lang="en")
     #templates
       include ../../build/templates.jade
 
+    iframe#download-target(style="display:none")
+
     script(src="//code.jquery.com/jquery-1.11.3.min.js")
     script(src="//cdn.jsdelivr.net/vue/1.0.17/vue.js")
     script(src="js/sockjs.min.js")
index f7cd7b72b9d5bd322548fcb24021cc484273589a..5256b274c83a91022b9ea9b41c564e672db7d802 100644 (file)
@@ -1,16 +1,42 @@
 script#admin-view-template(type="text/x-template")
   #admin
-    h2 Backup configuration
+    h2 Configuration
     button.pure-button.pure-button-primary(@click="backup") Backup
 
-    h2 Restore configuration
-    button.pure-button.pure-button-primary(@click="restore") Restore
+    label.pure-button.pure-button-primary.file-upload
+      input(type="file", accept=".json", @change="restore")
+      | Restore
+    message(:show.sync="configRestored")
+      h3(slot="header") Success
+      p(slot="body") Configuration restored.
 
-    h2 Reset to default configuration
-    button.pure-button.pure-button-primary(@click="reset") Reset
+    button.pure-button.pure-button-primary(@click="confirmReset = true")
+      | Reset
+    message(:show.sync="confirmReset")
+      h3(slot="header") Reset to default configuration?
+      p(slot="body") All configuration changes will be lost.
+      div(slot="footer")
+        button.pure-button.button-error(@click="confirmReset = false") Cancel
+        button.pure-button.button-success(@click="reset") OK
 
-    h2 Check for new firmware
-    button.pure-button.pure-button-primary(@click="check") Check
+    message(:show.sync="configReset")
+      h3(slot="header") Success
+      p(slot="body") Configuration reset.
 
-    h2 Upgrade firmware
+    h2 Firmware
+    button.pure-button.pure-button-primary(@click="check") Check
     button.pure-button.pure-button-primary(@click="upgrade") Upgrade
+
+    p
+    table.pure-table
+      tr
+        th Current version
+        td {{config.version}}
+
+      tr(v-if="latest")
+        th Latest version
+        td {{latest}}
+
+    message(:show.sync="firmwareUpgrading")
+      h3(slot="header") Firmware upgrading
+      p(slot="body") Please wait. . .
diff --git a/src/jade/templates/axis-view.jade b/src/jade/templates/axis-view.jade
deleted file mode 100644 (file)
index 7b2fff8..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-script#axis-view-template(type="text/x-template")
-  #axis
-    h1 {{index | capitalize}} Axis Configuration
-
-    form.pure-form.pure-form-aligned
-      fieldset(v-for="category in template.axes", :class="$key")
-        h2 {{$key}}
-
-        templated-input(v-for="templ in category", :name="$key",
-          :model.sync="axis[$key]", :template="templ")
index 36d3e70df2706af5fcc4cb4473af45ea763c15de..6844058fad64030a89416ba0ce6526e2802b35ce 100644 (file)
@@ -136,7 +136,7 @@ script#control-view-template(type="text/x-template")
             option(v-for="file in files", :value="file") {{file}}
 
         .gcode(:class="{placeholder: !gcode}")
-          {{{gcode || 'GCode displays here.'}}}
+          {{{gcode || 'GCode displays here.'}}}
 
       section#content2.tab-content
         .mdi.pure-form
diff --git a/src/jade/templates/message.jade b/src/jade/templates/message.jade
new file mode 100644 (file)
index 0000000..98b16e3
--- /dev/null
@@ -0,0 +1,11 @@
+script#message-template(type="text/x-template")
+  .modal-mask(v-show="show", transition="modal")
+    .modal-wrapper
+      .modal-container
+        .modal-header
+          slot(name="header") default header
+        .modal-body
+          slot(name="body") default body
+        .modal-footer
+          slot(name="footer")
+            button.pure-button.button-success(@click="show = false") OK
index bdf355ae68fb727b5bbdbeb6903b1b39bd0a1aac..4dca60efe985c45d4a14291978158fa1693ff29e 100644 (file)
@@ -1,38 +1,98 @@
 'use strict'
 
 
+var api = require('./api');
+
+
 module.exports = {
   template: '#admin-view-template',
   props: ['config'],
 
 
-  ready: function () {
+  data: function () {
+    return {
+      configRestored: false,
+      confirmReset: false,
+      configReset: false,
+      firmwareUpgrading: false,
+      latest: '',
+    }
   },
 
 
+  events: {
+    connected: function () {
+      if (this.firmwareUpgrading) location.reload(true);
+    }
+  },
+
+
+  ready: function () {},
+
+
   methods: {
     backup: function () {
-      alert('Not yet implemented');
+      document.getElementById('download-target').src = '/api/config/download';
     },
 
 
-    restore: function () {
-      alert('Not yet implemented');
+    restore: function (e) {
+      var files = e.target.files || e.dataTransfer.files;
+      if (!files.length) return;
+
+      var fr = new FileReader();
+      fr.onload = function (e) {
+        var config;
+
+        try {
+          config = JSON.parse(e.target.result);
+        } catch (e) {
+          alert("Invalid config file");
+          return;
+        }
+
+        api.put('config/save', config).done(function (data) {
+          this.$dispatch('update');
+          this.configRestored = true;
+
+        }.bind(this)).fail(function (error) {
+          alert('Restore failed: ' + error);
+        })
+      }.bind(this);
+
+      fr.readAsText(files[0]);
     },
 
 
     reset: function () {
-      alert('Not yet implemented');
+      this.confirmReset = false;
+      api.put('config/reset').done(function () {
+        this.configReset = true;
+
+      }.bind(this)).fail(function (error) {
+        alert('Reset failed: ' + error);
+      });
     },
 
 
     check: function () {
-      alert('Not yet implemented');
+      $.ajax({
+        type: 'GET',
+        url: 'https://buildbotics.com/bbctrl/latest.txt',
+        cache: false
+
+      }).done(function (data) {
+        this.latest = data;
+
+      }.bind(this)).fail(function (error) {
+        alert('Failed to get latest version information');
+      });
     },
 
 
     upgrade: function () {
-      alert('Not yet implemented');
+      this.firmwareUpgrading = true;
+      api.put('upgrade');
     }
   }
 }
index ce8424a0cce347b6534004faf14f94372ca78d1a..ebfcf7aec2522c560afdfd75768f31c510592013 100644 (file)
@@ -5,7 +5,8 @@ function api_cb(method, url, data, config) {
   config = $.extend({
     type: method,
     url: '/api/' + url,
-    dataType: 'json'
+    dataType: 'json',
+    cache: false
   }, config);
 
   if (typeof data == 'object') {
index 55d45edd96cc22bee5a6ccb9ed39fd64147f131a..ac701ec65e3c63f2a569da26ca292efc690ff97d 100644 (file)
@@ -25,7 +25,6 @@ module.exports = new Vue({
     'estop': {template: '#estop-template'},
     'loading-view': {template: '<h1>Loading...</h1>'},
     'control-view': require('./control-view'),
-    'axis-view': require('./axis-view'),
     'motor-view': require('./motor-view'),
     'spindle-view': require('./spindle-view'),
     'switches-view': require('./switches-view'),
@@ -46,7 +45,8 @@ module.exports = new Vue({
     },
 
 
-    connected: function () {this.update()}
+    connected: function () {this.update()},
+    update: function () {this.update()}
   },
 
 
@@ -64,11 +64,11 @@ module.exports = new Vue({
 
 
     update: function () {
-      $.get('/config-template.json', {cache: false})
+      $.ajax({type: 'GET', url: '/config-template.json', cache: false})
         .success(function (data, status, xhr) {
           this.template = data;
 
-          api.get('load').done(function (data) {
+          api.get('config/load').done(function (data) {
             this.config = data;
             this.parse_hash();
           }.bind(this))
@@ -120,10 +120,10 @@ module.exports = new Vue({
 
 
     save: function () {
-      api.put('save', this.config).done(function (data) {
+      api.put('config/save', this.config).done(function (data) {
         this.modified = false;
-      }.bind(this)).fail(function (xhr, status) {
-        alert('Save failed: ' + status + ': ' + xhr.responseText);
+      }.bind(this)).fail(function (error) {
+        alert('Save failed: ' + error);
       });
     }
   }
diff --git a/src/js/axis-view.js b/src/js/axis-view.js
deleted file mode 100644 (file)
index e6fc680..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-'use strict'
-
-
-module.exports = {
-  template: '#axis-view-template',
-  props: ['index', 'config', 'template'],
-
-
-  data: function () {
-    return {
-      active: false,
-      axis: {}
-    }
-  },
-
-
-  watch: {
-    index: function() {this.update();}
-  },
-
-
-  events: {
-    'input-changed': function() {
-      this.$dispatch('config-changed');
-      return false;
-    }
-  },
-
-
-  attached: function () {this.active = true; this.update()},
-  detached: function () {this.active = false},
-
-
-  methods: {
-    update: function () {
-      if (!this.active) return;
-
-      Vue.nextTick(function () {
-        if (this.config.hasOwnProperty('axes'))
-          this.axis = this.config.axes[this.index];
-        else this.axes = {};
-
-        var template = this.template.axes;
-        for (var category in template)
-          for (var key in template[category])
-            if (!this.axis.hasOwnProperty(key))
-              this.$set('axis["' + key + '"]',
-                        template[category][key].default);
-      }.bind(this));
-    }
-  }
-}
index 2524f78fbff0f61e8a8f0627cbea99267f9c6a9a..43ac7d86183ab6aa7dc3dee55d0e08d756595e0c 100644 (file)
@@ -88,8 +88,14 @@ module.exports = {
 
     enabled: function (axis) {
       var axis = axis.toLowerCase();
-      return axis in this.config.axes &&
-        this.config.axes[axis].mode != 'disabled';
+
+      for (var i = 0; i < this.config.motors.length; i++) {
+        var motor = this.config.motors[i];
+        if (motor.axis.toLowerCase() == axis &&
+            (motor.enabled || typeof motor.enabled == 'undefined')) return true;
+      }
+
+      return false;
     },
 
 
index 4e3ba2ac440644ebefcf610cdc20a26f3cb1bbae..0336f4378d6fd2958e66f4209d135801bef530dd 100644 (file)
@@ -5,6 +5,7 @@ $(function() {
 
   // Register global components
   Vue.component('templated-input', require('./templated-input'));
+  Vue.component('message', require('./message'));
 
   // Vue app
   require('./app');
diff --git a/src/js/message.js b/src/js/message.js
new file mode 100644 (file)
index 0000000..a8cbfdd
--- /dev/null
@@ -0,0 +1,14 @@
+'use strict'
+
+
+module.exports = {
+  template: '#message-template',
+
+  props: {
+    show: {
+      type: Boolean,
+      required: true,
+      twoWay: true
+    }
+  }
+}
index 6317c17be0b1924f59c7ed5164d275dd10600d2f..4e6bf58316fd56635943fe5fb376341ed2619b2a 100644 (file)
@@ -47,5 +47,7 @@ class APIHandler(tornado.web.RequestHandler):
         self.write_json(e)
 
 
-    def write_json(self, data):
-        self.write(json.dumps(data))
+    def write_json(self, data, pretty = False):
+        if pretty: data = json.dumps(data, indent = 2, separators = (',', ': '))
+        else: data = json.dumps(data)
+        self.write(data)
index 50e2e5b59b122c46dbcb78233be1e86bb3841983..d7d5f6ced30779e38d3eaabf5acbd5ab7bf9731c 100644 (file)
@@ -1,15 +1,28 @@
 import json
 import logging
+import pkg_resources
 
 import bbctrl
 
-
 log = logging.getLogger('Config')
 
+default_config = {
+    "motors": [
+        {"axis": "X"},
+        {"axis": "Y"},
+        {"axis": "Z"},
+        {"axis": "A"},
+        ],
+    "switches": {},
+    "spindle": {},
+    }
+
 
 class Config(object):
     def __init__(self, ctrl):
         self.ctrl = ctrl
+        self.version = pkg_resources.require('bbctrl')[0].version
+        default_config['version'] = self.version
 
         # Load config template
         with open(bbctrl.get_resource('http/config-template.json'), 'r',
@@ -28,16 +41,17 @@ class Config(object):
 
         except Exception as e:
             log.warning('%s', e)
-            return self.load_path(
-                bbctrl.get_resource('http/default-config.json'))
+            return default_config
 
 
     def save(self, config):
+        self.update(config)
+
+        config['version'] = self.version
+
         with open('config.json', 'w') as f:
             json.dump(config, f)
 
-        self.update(config)
-
         log.info('Saved')
 
 
@@ -64,18 +78,12 @@ class Config(object):
         # Motors
         tmpl = self.template['motors']
         for index in range(len(config['motors'])):
-            self.encode(index + 1, config['motors'][index], tmpl)
-
-        # Axes
-        tmpl = self.template['axes']
-        for axis in 'xyzabc':
-            if not axis in config['axes']: continue
-            self.encode(axis, config['axes'][axis], tmpl)
+            self.encode(index, config['motors'][index], tmpl)
 
         # Switches
         tmpl = self.template['switches']
         for index in range(len(config['switches'])):
-            self.encode_category(index + 1, config['switches'][index], tmpl)
+            self.encode_category(index, config['switches'][index], tmpl)
 
         # Spindle
         tmpl = self.template['spindle']
index 2fecf6705ac9db258fe7e8b66d8036bf865b4e5b..9fbf2530ebe5e0bc78c0d05384f686c15d26de45 100644 (file)
@@ -4,6 +4,7 @@ import json
 import tornado
 import sockjs.tornado
 import logging
+import datetime
 
 import bbctrl
 
@@ -12,14 +13,35 @@ log = logging.getLogger('Web')
 
 
 
-class LoadHandler(bbctrl.APIHandler):
+class ConfigLoadHandler(bbctrl.APIHandler):
     def get(self): self.write_json(self.ctrl.config.load())
 
 
-class SaveHandler(bbctrl.APIHandler):
+class ConfigDownloadHandler(bbctrl.APIHandler):
+    def set_default_headers(self):
+        filename = datetime.date.today().strftime('bbctrl-%Y%m%d.json')
+        self.set_header('Content-Type', 'application/octet-stream')
+        self.set_header('Content-Disposition',
+                        'attachment; filename="%s"' % filename)
+
+    def get(self):
+        self.write_json(self.ctrl.config.load(), pretty = True)
+
+
+class ConfigSaveHandler(bbctrl.APIHandler):
     def put_ok(self): self.ctrl.config.save(self.json)
 
 
+class ConfigResetHandler(bbctrl.APIHandler):
+    def put_ok(self): self.ctrl.config.reset()
+
+
+class UpgradeHandler(bbctrl.APIHandler):
+    def put_ok(self):
+        import subprocess
+        ret = subprocess.Popen(['upgrade-bbctrl'])
+
+
 class HomeHandler(bbctrl.APIHandler):
     def put_ok(self): self.ctrl.avr.home()
 
@@ -97,6 +119,11 @@ class Connection(sockjs.tornado.SockJSConnection):
         self.ctrl.avr.mdi(data)
 
 
+class StaticFileHandler(tornado.web.StaticFileHandler):
+    def set_extra_headers(self, path):
+        self.set_header('Cache-Control',
+                        'no-store, no-cache, must-revalidate, max-age=0')
+
 
 class Web(tornado.web.Application):
     def __init__(self, ctrl):
@@ -104,8 +131,11 @@ class Web(tornado.web.Application):
         self.clients = []
 
         handlers = [
-            (r'/api/load', LoadHandler),
-            (r'/api/save', SaveHandler),
+            (r'/api/config/load', ConfigLoadHandler),
+            (r'/api/config/download', ConfigDownloadHandler),
+            (r'/api/config/save', ConfigSaveHandler),
+            (r'/api/config/reset', ConfigResetHandler),
+            (r'/api/upgrade', UpgradeHandler),
             (r'/api/file(/.+)?', bbctrl.FileHandler),
             (r'/api/home', HomeHandler),
             (r'/api/start(/.+)', StartHandler),
@@ -119,7 +149,7 @@ class Web(tornado.web.Application):
             (r'/api/zero(/[xyzabcXYZABC])?', ZeroHandler),
             (r'/api/override/feed/([\d.]+)', OverrideFeedHandler),
             (r'/api/override/speed/([\d.]+)', OverrideSpeedHandler),
-            (r'/(.*)', tornado.web.StaticFileHandler,
+            (r'/(.*)', StaticFileHandler,
              {'path': bbctrl.get_resource('http/'),
               "default_filename": "index.html"}),
             ]
index e89be3cff8045dce50da03bfb771dbed2ec43b7a..07559aefd98cf5268128bfc2a401789ba916a63e 100644 (file)
@@ -1,12 +1,42 @@
 {
   "motors": {
     "general": {
-      "motor-map": {
+      "axis": {
         "type": "enum",
-        "values": ["x", "y", "z", "a", "b", "c"],
-        "default": "x",
-        "code": "ma"
+        "values": ["X", "Y", "Z", "A", "B", "C"],
+        "default": "X",
+        "code": "an"
+      }
+    },
+
+    "power": {
+      "power-mode": {
+        "type": "enum",
+        "values": ["disabled", "always-on", "in-cycle", "when-moving"],
+        "default": "in-cycle",
+        "code": "pm"
+      },
+      "min-power": {
+        "type": "percent",
+        "unit": "%",
+        "default": 30,
+        "code": "pl"
+      },
+      "max-power": {
+        "type": "percent",
+        "unit": "%",
+        "default": 80,
+        "code": "th"
       },
+      "idle-power": {
+        "type": "percent",
+        "unit": "%",
+        "default": 10,
+        "code": "ip"
+      }
+    },
+
+    "motion": {
       "step-angle": {
         "type": "float",
         "min": 0,
         "default": 16,
         "code": "mi"
       },
-      "polarity": {
-        "type": "enum",
-        "values": ["normal", "reversed"],
-        "default": "normal",
-        "code": "po"
-      }
-    },
-
-    "power": {
-      "power-mode": {
-        "type": "enum",
-        "values": ["disabled", "always-on", "in-cycle", "when-moving"],
-        "default": "in-cycle",
-        "code": "pm"
-      },
-      "power-level": {
-        "type": "percent",
-        "unit": "%",
-        "default": 80,
-        "code": "pl"
-      },
-      "stallguard": {
-        "type": "percent",
-        "unit": "%",
-        "default": 70,
-        "code": "th"
-      }
-    }
-  },
-
-  "axes": {
-    "motion": {
-      "mode": {
-        "type": "enum",
-        "values": ["disabled", "standard", "inhibited", "radius"],
-        "default": "disabled",
-        "code": "am"
+      "reverse": {
+        "type": "bool",
+        "default": false,
+        "code": "rv"
       },
       "max-velocity": {
         "type": "float",
@@ -76,8 +73,8 @@
       },
       "max-feedrate": {
         "type": "float",
-        "min": 0, "unit":
-        "mm/min",
+        "min": 0,
+        "unit": "mm/min",
         "default": 16000,
         "code": "fr"
       },
         "unit": "mm/min³",
         "default": 20,
         "code": "jm"
-      },
-      "junction-deviation": {
-        "type": "float",
-        "min": 0,
-        "unit": "mm",
-        "default": 0.05,
-        "code": "jd"
       }
     },
 
-    "limits": {
+    "homing": {
+      "homing-mode": {
+        "type": "enum",
+        "values": [
+          "disabled", "stall", "min-normally-open", "min-normally-closed",
+          "max-normally-open", "max-normally-closed"],
+        "default": "disabled",
+        "code": "hm"
+      },
       "min-soft-limit": {
         "type": "float",
         "unit": "mm",
         "default": 150,
         "code": "tm"
       },
-      "min-switch": {
-        "type": "enum",
-        "values": ["disabled", "normally-open", "normally-closed"],
-        "default": "disabled",
-        "code": "sn"
-      },
-      "max-switch": {
-        "type": "enum",
-        "values": ["disabled", "normally-open", "normally-closed"],
-        "default": "disabled",
-        "code": "sx"
-      }
-    },
-
-    "homing": {
-      "max-homing-jerk": {
-        "type": "float",
-        "min": 0,
-        "unit": "mm/min³",
-        "default": 40,
-        "code": "jh"
-      },
       "search-velocity": {
         "type": "float",
         "min": 0,
       "default": "PWM",
       "code": "st"
     },
-    "spin-polarity": {
-      "type": "enum",
-      "values": ["normal", "reversed"],
-      "default": "normal",
-        "code": "sp"
+    "spin-reversed": {
+      "type": "bool",
+      "default": "false",
+      "code": "sr"
     },
     "max-spin": {
       "type": "float",
       "unit": "RPM",
       "min": 0,
       "default": 10000,
-      "code": "ss"
+      "code": "sx"
+    },
+    "min-spin": {
+      "type": "float",
+      "unit": "RPM",
+      "min": 0,
+      "default": 0,
+      "code": "sm"
     },
     "spin-min-pulse": {
       "type": "float",
diff --git a/src/resources/default-config.json b/src/resources/default-config.json
deleted file mode 100644 (file)
index 70c06b9..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-  "motors": [
-    {
-      "motor-map": "x",
-      "step-angle": 1.8,
-      "travel-per-rev": 3.175,
-      "microsteps": 16,
-      "polarity": "normal",
-      "power-mode": "always-on",
-      "power-level": 80,
-      "stallguard": 70
-    }, {
-      "motor-map": "y"
-    }, {
-      "motor-map": "z"
-    }, {
-      "motor-map": "a"
-    }
-  ],
-
-  "axes": {
-    "x": {
-      "mode": "standard",
-      "max-velocity": 16000,
-      "max-feedrate": 16000,
-      "max-jerk": 40,
-      "min-soft-limit": 0,
-      "max-soft-limit": 150,
-      "max-homing-jerk": 80,
-      "junction-deviation": 0.05,
-      "search-velocity": 500,
-      "latch-velocity": 100,
-      "latch-backoff": 5,
-      "zero-backoff": 1
-    },
-
-    "y": {
-      "mode": "standard"
-    },
-
-    "z": {
-      "mode": "standard"
-    },
-
-    "a": {
-      "mode": "radius",
-      "max-velocity": 1000000,
-      "max-feedrate": 1000000,
-      "min-soft-limit": 0,
-      "max-soft-limit": 0
-    },
-
-    "b": {
-      "mode": "disabled"
-    },
-
-    "c": {
-      "mode": "disabled"
-    }
-  },
-
-  "switches": {},
-  "spindle": {}
-}
index a45daddf11f1119a33b5b7c7e7e531f4407e2dd4..404a53faf6cd3986e3c08a20787fcfa471e82066 100644 (file)
@@ -400,6 +400,45 @@ body
     border 1px solid #ddd
     padding 0.5em
 
+.modal-mask
+  position fixed
+  z-index 9998
+  top 0
+  left 0
+  width 100%
+  height 100%
+  background-color rgba(0, 0, 0, .25)
+  display table
+  transition opacity .3s ease
+
+  .modal-wrapper
+    display table-cell
+    vertical-align middle
+
+    .modal-container
+      width 300px
+      margin 0px auto
+      padding 20px 30px
+      background-color #fff
+      border-radius 2px
+      box-shadow 0 2px 8px rgba(0, 0, 0, .33)
+      transition all .3s ease
+      font-family Helvetica, Arial, sans-serif
+
+
+.modal-enter, .modal-leave
+  opacity 0
+
+.modal-enter .modal-container, .modal-leave .modal-container
+  transform scale(1.1)
+
+
+label.file-upload
+  display inline-block
+
+  input[type="file"]
+    position fixed
+    top -1000px
 
 @media only screen and (max-width 48em)
   .header