From 6bdee2a527e1132552bc230fdb263d317d2b9378 Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Tue, 3 Jan 2017 05:14:45 -0800 Subject: [PATCH] Fixed config, implemented configuration backup, restore, reset and firmware check & upgrade --- .gitignore | 3 + MANIFEST.in | 2 + Makefile | 23 +++-- package.json | 4 +- scripts/install.sh | 4 + scripts/setup_rpi.sh | 3 + scripts/upgrade-bbctrl | 29 ++++++ setup.cfg | 2 + setup.py | 13 ++- src/jade/index.jade | 8 +- src/jade/templates/admin-view.jade | 42 ++++++-- src/jade/templates/axis-view.jade | 10 -- src/jade/templates/control-view.jade | 2 +- src/jade/templates/message.jade | 11 +++ src/js/admin-view.js | 74 ++++++++++++-- src/js/api.js | 3 +- src/js/app.js | 14 +-- src/js/axis-view.js | 52 ---------- src/js/control-view.js | 10 +- src/js/main.js | 1 + src/js/message.js | 14 +++ src/py/bbctrl/APIHandler.py | 6 +- src/py/bbctrl/Config.py | 34 ++++--- src/py/bbctrl/Web.py | 40 +++++++- src/resources/config-template.json | 140 ++++++++++++--------------- src/resources/default-config.json | 64 ------------ src/stylus/style.styl | 39 ++++++++ 27 files changed, 377 insertions(+), 270 deletions(-) create mode 100644 MANIFEST.in create mode 100755 scripts/install.sh create mode 100755 scripts/upgrade-bbctrl create mode 100644 setup.cfg delete mode 100644 src/jade/templates/axis-view.jade create mode 100644 src/jade/templates/message.jade delete mode 100644 src/js/axis-view.js create mode 100644 src/js/message.js delete mode 100644 src/resources/default-config.json diff --git a/.gitignore b/.gitignore index 150b38a..745bae7 100644 --- a/.gitignore +++ b/.gitignore @@ -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 index 0000000..56d2aa1 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +recursive-include src/py/bbctrl/http * +include package.json README.md scripts/install.sh diff --git a/Makefile b/Makefile index 55b92a1..762fc56 100644 --- 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 diff --git a/package.json b/package.json index eff8596..162db98 100644 --- a/package.json +++ b/package.json @@ -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 index 0000000..d7084d0 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +sudo ./setup.py install +sudo service bbctrl restart diff --git a/scripts/setup_rpi.sh b/scripts/setup_rpi.sh index 2ee7118..8f02d90 100755 --- a/scripts/setup_rpi.sh +++ b/scripts/setup_rpi.sh @@ -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 index 0000000..44454f2 --- /dev/null +++ b/scripts/upgrade-bbctrl @@ -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 index 0000000..ba15267 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[sdist] +formats=bztar diff --git a/setup.py b/setup.py index b6105b3..6356450 100755 --- 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' diff --git a/src/jade/index.jade b/src/jade/index.jade index e5c8b9e..b664730 100644 --- a/src/jade/index.jade +++ b/src/jade/index.jade @@ -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") diff --git a/src/jade/templates/admin-view.jade b/src/jade/templates/admin-view.jade index f7cd7b7..5256b27 100644 --- a/src/jade/templates/admin-view.jade +++ b/src/jade/templates/admin-view.jade @@ -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 index 7b2fff8..0000000 --- a/src/jade/templates/axis-view.jade +++ /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") diff --git a/src/jade/templates/control-view.jade b/src/jade/templates/control-view.jade index 36d3e70..6844058 100644 --- a/src/jade/templates/control-view.jade +++ b/src/jade/templates/control-view.jade @@ -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 index 0000000..98b16e3 --- /dev/null +++ b/src/jade/templates/message.jade @@ -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 diff --git a/src/js/admin-view.js b/src/js/admin-view.js index bdf355a..4dca60e 100644 --- a/src/js/admin-view.js +++ b/src/js/admin-view.js @@ -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'); } } } diff --git a/src/js/api.js b/src/js/api.js index ce8424a..ebfcf7a 100644 --- a/src/js/api.js +++ b/src/js/api.js @@ -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') { diff --git a/src/js/app.js b/src/js/app.js index 55d45ed..ac701ec 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -25,7 +25,6 @@ module.exports = new Vue({ 'estop': {template: '#estop-template'}, 'loading-view': {template: '

Loading...

'}, '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 index e6fc680..0000000 --- a/src/js/axis-view.js +++ /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)); - } - } -} diff --git a/src/js/control-view.js b/src/js/control-view.js index 2524f78..43ac7d8 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -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; }, diff --git a/src/js/main.js b/src/js/main.js index 4e3ba2a..0336f43 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -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 index 0000000..a8cbfdd --- /dev/null +++ b/src/js/message.js @@ -0,0 +1,14 @@ +'use strict' + + +module.exports = { + template: '#message-template', + + props: { + show: { + type: Boolean, + required: true, + twoWay: true + } + } +} diff --git a/src/py/bbctrl/APIHandler.py b/src/py/bbctrl/APIHandler.py index 6317c17..4e6bf58 100644 --- a/src/py/bbctrl/APIHandler.py +++ b/src/py/bbctrl/APIHandler.py @@ -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) diff --git a/src/py/bbctrl/Config.py b/src/py/bbctrl/Config.py index 50e2e5b..d7d5f6c 100644 --- a/src/py/bbctrl/Config.py +++ b/src/py/bbctrl/Config.py @@ -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'] diff --git a/src/py/bbctrl/Web.py b/src/py/bbctrl/Web.py index 2fecf67..9fbf253 100644 --- a/src/py/bbctrl/Web.py +++ b/src/py/bbctrl/Web.py @@ -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"}), ] diff --git a/src/resources/config-template.json b/src/resources/config-template.json index e89be3c..07559ae 100644 --- a/src/resources/config-template.json +++ b/src/resources/config-template.json @@ -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, @@ -29,43 +59,10 @@ "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" }, @@ -87,17 +84,18 @@ "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", @@ -110,28 +108,6 @@ "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, @@ -170,18 +146,24 @@ "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 index 70c06b9..0000000 --- a/src/resources/default-config.json +++ /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": {} -} diff --git a/src/stylus/style.styl b/src/stylus/style.styl index a45dadd..404a53f 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -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 -- 2.27.0