From 95c79277974c37c82f7405b6240b7e568afd3f77 Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Sat, 3 Sep 2016 15:37:42 -0700 Subject: [PATCH] Improved Web control interface, LCD screen output --- src/jade/index.jade | 15 +- src/jade/templates/control-view.jade | 195 +++++++++++---------- src/jade/templates/estop.jade | 4 +- src/js/app.js | 7 + src/js/control-view.js | 24 +-- src/py/bbctrl/AVR.py | 25 +-- src/py/bbctrl/LCD.py | 26 ++- src/py/lcd/__init__.py | 7 +- src/resources/css/side-menu.css | 1 - src/stylus/style.styl | 242 ++++++++++++++++++++------- 10 files changed, 360 insertions(+), 186 deletions(-) diff --git a/src/jade/index.jade b/src/jade/index.jade index eb8a877..e5c8b9e 100644 --- a/src/jade/index.jade +++ b/src/jade/index.jade @@ -65,11 +65,16 @@ html(lang="en") #main .header - img(src="/images/buildbotics_logo.png") - .logo - span.left Build - span.right botics - h2 Machine Controller + .header-content + .estop(:class="{active: state.es}") + estop(@click="estop") + + .banner + img(src="/images/buildbotics_logo.png") + .title + span.left Build + span.right botics + .subtitle Machine Controller .content(class="{{currentView}}-view") component(:is="currentView + '-view'", :index="index", diff --git a/src/jade/templates/control-view.jade b/src/jade/templates/control-view.jade index 6e877d8..7c085e1 100644 --- a/src/jade/templates/control-view.jade +++ b/src/jade/templates/control-view.jade @@ -1,58 +1,12 @@ script#control-view-template(type="text/x-template") #control - .jog - axis-control(axes="XY", :colors="['red', 'green']", - :enabled="[enabled('x'), enabled('y')]", - v-if="enabled('x') || enabled('y')") - axis-control(axes="AZ", :colors="['orange', 'blue']", - :enabled="[enabled('a'), enabled('z')]", - v-if="enabled('a') || enabled('z')") - axis-control(axes="BC", :colors="['cyan', 'purple']", - :enabled="[enabled('b'), enabled('c')]", - v-if="enabled('b') || enabled('c')") - - - .estop(:class="{active: state.es}") - estop(@click="estop") - - - table.info - tr - th Tool - td {{state.t || 0}} - tr - th Velocity - td {{state.v || 0 | fixed 0}} mm/min - tr - th Feed - td {{state.f || 0}} mm/min - tr - th Speed - td {{state.s || 0}} RPM - tr - th Direction - td {{state.sd || 'Clockwise'}} - tr - th Mist - td {{state.mist || 'Off'}} - tr - th Coolant - td {{state.coolant || 'Off'}} - tr - th State - td {{state.x || ''}} - tr - th Cycle - td {{state.c || ''}} - - table.axes tr - th Axis - th Position - th Offset - th Errors - th Actions + th.name Axis + th.position Position + th.offset Offset + th.errors Errors + th.actions Actions each axis in 'xyzabc' tr.axis(class="axis-#{axis}", v-if="enabled('#{axis}')") @@ -63,35 +17,60 @@ script#control-view-template(type="text/x-template") .fa.fa-hot(v-if="state.#{axis}t", title="Driver overtemp.") .fa.fa-ban(v-if="state.#{axis}s", title="Motor stalled.") th.actions - button.pure-button(title="Zero #{axis} axis.", + button.pure-button(title="Zero {{'#{axis}' | upper}} axis.", @click="zero('#{axis}')") | ∅ - button.pure-button(title="Home #{axis} axis.", + button.pure-button(title="Home {{'#{axis}' | upper}} axis.", @click="home('#{axis}')") .fa.fa-home + table.info + tr + th State + td {{get_state()}} + td + tr + th Feed + td {{state.f || 0}} + td mm/min + tr + th Speed + td {{state.s || 0}} + td RPM + tr + th Direction + td {{state.sd || 'Clockwise'}} + td - .overrides - | Override: - .override - label Feed - input(title="Feed rate override.", type="range", min="0", max="2", - step="0.01", v-model="feed_override", @change="override_feed") - span.percent {{feed_override | percent 0}} - - .override - label Speed - input(title="Speed override.", type="range", min="0", max="2", - step="0.01", v-model="speed_override", @change="override_speed") - span.percent {{speed_override | percent 0}} + table.info + tr + th Velocity + td {{state.v || 0 | fixed 0}} + td mm/min + tr + th Tool + td {{state.t || 0}} + td + tr + th Mist + td {{state.mist || 'Off'}} + td + tr + th Coolant + td {{state.coolant || 'Off'}} + td + .override(title="Feed rate override.") + label Feed + input(type="range", min="0", max="2", step="0.01", + v-model="feed_override", @change="override_feed") + span.percent {{feed_override | percent 0}} - .mdi.pure-form - fieldset - button.pure-button.pure-button-primary( - title="Manually execute instructions.", @click="submit_mdi", - :disabled="state.x != 'ready'") MDI - input(v-model="mdi", @keyup.enter="submit_mdi") + .override(title="Spindle speed override.") + label Speed + input(type="range", min="0", max="2", step="0.01", + v-model="speed_override", @change="override_speed") + span.percent {{speed_override | percent 0}} .toolbar @@ -117,22 +96,56 @@ script#control-view-template(type="text/x-template") :disabled="(state.x != 'ready' && state.x != 'holding') || !file") .fa.fa-step-forward - .spacer - - button.pure-button(title="Upload a new program file.", @click="open", - :disabled="state.x != 'ready'") - .fa.fa-folder-open - - input.gcode-file-input(type="file", @change="upload", - style="display:none", accept=".nc,.gcode,.gc,.ngc") - - button.pure-button(title="Delete current program file.", @click="delete", - :disabled="!file") - .fa.fa-trash - - select(title="Select previously uploaded program files.", v-model="file", - @change="load", :disabled="state.x != 'ready'") - option(v-for="file in files", :value="file") {{file}} - - - .gcode {{gcode || 'GCode displays here.'}} + .tabs + input#tab1(type="radio", name="tabs" checked) + label(for="tab1") Auto + + input#tab2(type="radio", name="tabs") + label(for="tab2") MDI + + input#tab3(type="radio", name="tabs") + label(for="tab3") Manual + + section#content1.tab-content + .toolbar + button.pure-button(title="Upload a new program file.", @click="open", + :disabled="state.x == 'running' || state.x == 'stopping'") + .fa.fa-folder-open + + input.gcode-file-input(type="file", @change="upload", + style="display:none", accept=".nc,.gcode,.gc,.ngc") + + button.pure-button(title="Delete current program file.", + @click="delete", :disabled="!file") + .fa.fa-trash + + select(title="Select previously uploaded program files.", + v-model="file", @change="load", + :disabled="state.x == 'running' || state.x == 'stopping'") + option(v-for="file in files", :value="file") {{file}} + + .gcode(:class="{placeholder: !gcode}") + {{gcode || 'GCode displays here.'}} + + section#content2.tab-content + .mdi.pure-form + fieldset + button.pure-button.pure-button-primary( + title="Manually execute instructions.", @click="submit_mdi", + :disabled="state.x != 'ready'") MDI + input(v-model="mdi", @keyup.enter="submit_mdi") + + .history(:class="{placeholder: !history}") + {{history || 'MDI history displays here.'}} + + section#content3.tab-content + .jog + axis-control(axes="XY", :colors="['red', 'green']", + :enabled="[enabled('x'), enabled('y')]", + v-if="enabled('x') || enabled('y')") + axis-control(axes="AZ", :colors="['orange', 'blue']", + :enabled="[enabled('a'), enabled('z')]", + v-if="enabled('a') || enabled('z')") + axis-control(axes="BC", :colors="['cyan', 'purple']", + :enabled="[enabled('b'), enabled('c')]", + v-if="enabled('b') || enabled('c')") diff --git a/src/jade/templates/estop.jade b/src/jade/templates/estop.jade index 8f9a865..0fc0f45 100644 --- a/src/jade/templates/estop.jade +++ b/src/jade/templates/estop.jade @@ -2,7 +2,7 @@ script#estop-template(type="text/x-template") svg(version="1.1", xmlns:svg="http://www.w3.org/2000/svg", xmlns="http://www.w3.org/2000/svg", xmlns:xlink="http://www.w3.org/1999/xlink", - width="200", height="200") + width="130", height="130") defs path#text-path-1(style="fill:none;stroke:none", d="m 73.735867,673.1299 c 0,55.10749 44.673453,99.78094 99.780973,99.78094 55.10748,0 99.78093,-44.67345 99.78093,-99.78094 0,-55.10749 -44.67345,-99.78094 -99.78093,-99.78094 -55.10752,0 -99.780973,44.67345 -99.780973,99.78094 z") @@ -55,7 +55,7 @@ script#estop-template(type="text/x-template") fecomposite(in="offset", in2="SourceGraphic", operator="atop") - g(transform="scale(0.92, 0.92),translate(-65, -526)") + g(transform="scale(0.6, 0.6),translate(-65, -526)") // Yellow ring circle.ring(style="fill:#f5e138;filter:url(#filter5266)", cx="173", cy="633", r="100") diff --git a/src/js/app.js b/src/js/app.js index 5fae6ae..c92cd4b 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -22,6 +22,7 @@ module.exports = new Vue({ components: { + 'estop': {template: '#estop-template'}, 'loading-view': {template: '

Loading...

'}, 'control-view': require('./control-view'), 'axis-view': require('./axis-view'), @@ -56,6 +57,12 @@ module.exports = new Vue({ methods: { + estop: function () { + if (this.state.x == 'estopped') api.put('clear'); + else api.put('estop'); + }, + + update: function () { $.get('/config-template.json', {cache: false}) .success(function (data, status, xhr) { diff --git a/src/js/control-view.js b/src/js/control-view.js index d169c37..7479a17 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -21,6 +21,7 @@ module.exports = { files: [], axes: 'xyzabc', gcode: '', + history: '', speed_override: 1, feed_override: 1 } @@ -28,12 +29,12 @@ module.exports = { components: { - 'axis-control': require('./axis-control'), - 'estop': {template: '#estop-template'} + 'axis-control': require('./axis-control') }, events: { + // TODO These should all be implemented via the API jog: function (axis, move) {this.send('g91 g0' + axis + move)}, home: function (axis) {this.send('$home ' + axis)}, zero: function (axis) {this.send('$zero ' + axis)} @@ -46,6 +47,13 @@ module.exports = { methods: { + get_state: function () { + var state = this.state.x || ''; + if (state == 'running' && this.state.c) state = this.state.c; + return state.toUpperCase(); + }, + + send: function (msg) { this.$dispatch('send', msg); }, @@ -58,12 +66,6 @@ module.exports = { }, - estop: function () { - if (this.state.x == 'estopped') api.put('clear').done(this.update); - else api.put('estop').done(this.update); - }, - - update: function () { api.get('file') .done(function (files) { @@ -79,6 +81,7 @@ module.exports = { submit_mdi: function () { this.send(this.mdi); + this.history = this.mdi + '\n' + this.history; }, @@ -174,8 +177,7 @@ module.exports = { }, - fixed: function (value, precision) { - return value.toFixed(precision); - } + fixed: function (value, precision) {return value.toFixed(precision)}, + upper: function (value) {return value.toUpperCase()} } } diff --git a/src/py/bbctrl/AVR.py b/src/py/bbctrl/AVR.py index 23c2108..0f22bb3 100644 --- a/src/py/bbctrl/AVR.py +++ b/src/py/bbctrl/AVR.py @@ -154,20 +154,23 @@ class AVR(): try: msg = json.loads(line) - if 'firmware' in msg: - log.error('AVR rebooted') - self._stop_sending_gcode() - self.report() + except Exception as e: + log.error('%s, data: %s', e, line) - if 'x' in msg and msg['x'] == 'estopped': - self._stop_sending_gcode() + if 'firmware' in msg: + log.error('AVR rebooted') + self._stop_sending_gcode() + self.report() - self.vars.update(msg) - self.ctrl.web.broadcast(msg) - log.debug(line) + if 'x' in msg and msg['x'] == 'estopped': + self._stop_sending_gcode() + + self.vars.update(msg) + self.ctrl.lcd.update(msg) + self.ctrl.web.broadcast(msg) + + log.debug(line) - except Exception as e: - log.error('%s, data: %s', e, line) def queue_command(self, cmd): diff --git a/src/py/bbctrl/LCD.py b/src/py/bbctrl/LCD.py index 4e4ab31..bc4e80f 100644 --- a/src/py/bbctrl/LCD.py +++ b/src/py/bbctrl/LCD.py @@ -4,16 +4,30 @@ import atexit class LCD: def __init__(self, ctrl): + self.ctrl = ctrl + self.lcd = lcd.LCD(ctrl.args.lcd_port, ctrl.args.lcd_addr) - self.splash() atexit.register(self.goodbye) - def splash(self): - self.lcd.clear() - self.lcd.display(0, 'Buildbotics', lcd.JUSTIFY_CENTER) - self.lcd.display(1, 'Controller', lcd.JUSTIFY_CENTER) - self.lcd.display(3, '*Ready*', lcd.JUSTIFY_CENTER) + def update(self, msg, force = False): + def has(name): return force or name in msg + + if has('x') or has('c'): + v = self.ctrl.avr.vars + state = v.get('x', 'init') + if 'c' in v and state == 'running': state = v['c'] + + self.lcd.text('%-9s' % state.upper(), 0, 0) + + if has('xp'): self.lcd.text('% 10.4fX' % msg['xp'], 9, 0) + if has('yp'): self.lcd.text('% 10.4fY' % msg['yp'], 9, 1) + if has('zp'): self.lcd.text('% 10.4fZ' % msg['zp'], 9, 2) + if has('ap'): self.lcd.text('% 10.4fA' % msg['ap'], 9, 3) + if has('t'): self.lcd.text('%2uT' % msg['t'], 6, 1) + if has('u'): self.lcd.text('%s' % msg['u'].upper(), 0, 1) + if has('f'): self.lcd.text('%8uF' % msg['f'], 0, 2) + if has('s'): self.lcd.text('%8dS' % msg['s'], 0, 3) def goodbye(self): diff --git a/src/py/lcd/__init__.py b/src/py/lcd/__init__.py index b14f317..b7b44ac 100755 --- a/src/py/lcd/__init__.py +++ b/src/py/lcd/__init__.py @@ -141,7 +141,9 @@ class LCD: self.write(LCD_SET_DDRAM_ADDR | (0, 64, 20, 84)[y] + int(x)) - def text(self, msg): + def text(self, msg, x = None, y = None): + if x is not None and y is not None: self.goto(x, y) + for c in msg: self.write(ord(c), REG_SELECT_BIT) @@ -153,8 +155,7 @@ class LCD: if x < 0: x = 0 - self.goto(x, line) - self.text(msg) + self.text(msg, x, line) def shift(self, count = 1, right = True, display = True): diff --git a/src/resources/css/side-menu.css b/src/resources/css/side-menu.css index b5ce7ae..8999d61 100644 --- a/src/resources/css/side-menu.css +++ b/src/resources/css/side-menu.css @@ -245,4 +245,3 @@ Hides the menu at `48em`, but modify this based on your app's needs. left: 150px; } } - diff --git a/src/stylus/style.styl b/src/stylus/style.styl index efa621a..5c27db6 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -7,19 +7,44 @@ body .button-success:not([disabled]) background rgb(28, 184, 65) -.header img - vertical-align top +.header, .content + padding 0 -.logo - font-size 30pt - font-family Audiowide - display inline - margin-right 0.5em +.header + height 140px + padding 0 - .left - color #444 - .right - color #e5aa3d + .header-content + max-width 800px + margin auto + text-align left + + .estop + float right + margin 5px + + .banner + padding-top 40px + white-space nowrap + + img + vertical-align top + + .title + font-size 30pt + font-family Audiowide + display inline + margin-right 0.5em + + .left + color #444 + .right + color #e5aa3d + + .subtitle + font-size 18pt + font-weight 100 + color #aaa .error background red @@ -101,6 +126,19 @@ body 50% fill #ff9d00 +.estop + width 130px + transition 250ms + + &.active .ring + animation blink 2s step-start 0s infinite + + svg + cursor pointer + + .button:hover + filter brightness(120%) + .control-view table margin 0.5em 0 @@ -110,6 +148,8 @@ body border 1px solid #ddd .axes + width 100% + .axis-x .name color #f00 @@ -130,6 +170,7 @@ body td, th padding 2px + white-space nowrap th text-align center @@ -144,24 +185,17 @@ body .name, .position font-size 36pt - line-height 36pt + line-height 30pt - .estop - display inline-block - width 190px - transition 250ms - - &.active .ring - animation blink 2s step-start 0s infinite - - svg - cursor pointer + .position + width 99% - .button:hover - filter brightness(120%) + .offset + min-width 8em - .jog - float right + .errors + min-width 6em + white-space normal .jog svg text @@ -199,57 +233,153 @@ body text-anchor middle .info - float right - clear right + empty-cells show th, td padding 3px text-align right - td + th + width 5.25em + + td:nth-child(2) min-width 8em - .overrides - clear both + td:nth-child(3) + text-align left - .override - margin 0.5em - display inline-block + .info:nth-of-type(2) + float left + clear left - .percent - display inline-block - width 3em - - input - border-radius 0 - margin -0.4em 0.5em + .info:nth-of-type(3) + float right + clear right - .mdi - clear both - white-space nowrap + .override margin 0.5em 0 + white-space nowrap + + label + font-weight bold + min-width 3.5em + display inline-block + + .percent + display inline-block + width 3em input - width 90% + border-radius 0 + margin -0.4em 0.5em + + .override:nth-of-type(1) + clear left + float left + + .override:nth-of-type(2) + clear right + float right .toolbar clear both - margin 0.5em 0 - .spacer - display inline-block - width 1px - height 1px - margin 0 1em + > * + margin 0.5em 0 - .gcode + .gcode, .history clear both - border 2px inset #ccc - border-radius 5px overflow auto width 100% - max-width 100% - min-width 100% + max-width 99% + min-width 99% height 200px padding 2px white-space pre + + &.placeholder + color #aaa + + .mdi + clear both + white-space nowrap + + input + width 90% + + .jog + text-align center + + > svg + margin 1em + + +.tabs + clear both + + > input + display none + + > label + display block + float left + width 5em + font-weight bold + cursor pointer + text-decoration none + text-align center + background #f6f6f6 + border-top-left-radius 5px + border-top-right-radius 5px + border-top 2px solid #f6f6f6 + border-left 1px solid #f6f6f6 + border-right 1px solid #f6f6f6 + margin-right 2px + + > section + display none + clear both + + > #tab1:checked ~ #content1, + > #tab2:checked ~ #content2, + > #tab3:checked ~ #content3 + display block + + [id^="tab"]:checked + label + background #fff + border-top 2px solid #ddd + border-left 1px solid #ddd + border-right 1px solid #ddd + border-bottom 1px solid #fff + margin-bottom -1px + + .tab-content + border 1px solid #ddd + padding 0.5em + + +@media only screen and (max-width 48em) + .header + height auto + + .header-content > .banner + padding-top 0 + clear both + + .control-view #control + .axes + .axis + .name, .position + font-size 24pt + line-height 24pt + + .offset, .errors + display none + + > *:nth-of-type(n) + float none + clear both + width 99% + + .tab_container + width 98% -- 2.27.0