From: Joseph Coffland Date: Wed, 14 Feb 2018 00:39:15 +0000 (-0800) Subject: - Fixed dwell (G4) X-Git-Url: https://git.buildbotics.com/?a=commitdiff_plain;h=76292a10a09808d720948ada20c295ac0705ea63;p=bbctrl-firmware - Fixed dwell (G4) - Always show limit switch indicators regardless of motor enable - Fixed feed rate display - Added current GCode unit display - Fixed homed axis zeroing - Fixed probe pin input - Added reload button to video tab - Don't open error dialog on repeat messages - Handle large GCode files in browser - Added max lookahead limit to planner - Fixed GCode stopping/pausing where ramp down needs more than is in the queue - Added breakout box diagram to indicators - Initialize axes offsets to zero on startup - Fixed conflict between x state variable and x axis variable - Don't show ipv6 addresses on LCD. They don't fit. --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 745d8f5..00c7719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ Buildbotics CNC Controller Firmware Change Log ============================================== +## v0.3.5 + - Fixed dwell (G4) + - Always show limit switch indicators regardless of motor enable + - Fixed feed rate display + - Added current GCode unit display + - Fixed homed axis zeroing + - Fixed probe pin input + - Added reload button to video tab + - Don't open error dialog on repeat messages + - Handle large GCode files in browser + - Added max lookahead limit to planner + - Fixed GCode stopping/pausing where ramp down needs more than is in the queue + - Added breakout box diagram to indicators + - Initialize axes offsets to zero on startup + - Fixed conflict between ``x`` state variable and ``x`` axis variable + - Don't show ipv6 addresses on LCD. They don't fit. + ## v0.3.4 - Added alternate units for motor parameters - Automatic config file upgrading diff --git a/package.json b/package.json index 8cbb856..fc0d42f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bbctrl", - "version": "0.3.4", + "version": "0.3.5", "homepage": "http://buildbotics.com/", "repository": "https://github.com/buildbotics/bbctrl-firmware", "license": "GPL-3.0+", diff --git a/src/avr/src/command.c b/src/avr/src/command.c index 4fcea2c..ed446c4 100644 --- a/src/avr/src/command.c +++ b/src/avr/src/command.c @@ -244,6 +244,7 @@ void command_reset_position() { bool command_exec() { if (!cmd.count) { cmd.last_empty = rtc_get_time(); + exec_set_velocity(0); state_idle(); return false; } diff --git a/src/avr/src/exec.c b/src/avr/src/exec.c index 5315a24..3aec68b 100644 --- a/src/avr/src/exec.c +++ b/src/avr/src/exec.c @@ -47,7 +47,6 @@ static struct { float jerk; int tool; - int line; float feed_override; float spindle_override; @@ -75,8 +74,6 @@ float exec_get_velocity() {return ex.velocity;} void exec_set_acceleration(float a) {ex.accel = a;} float exec_get_acceleration() {return ex.accel;} void exec_set_jerk(float j) {ex.jerk = j;} -void exec_set_line(int32_t line) {ex.line = line;} -int32_t exec_get_line() {return ex.line;} void exec_set_cb(exec_cb_t cb) {ex.cb = cb;} @@ -107,7 +104,6 @@ stat_t exec_next() { // Variable callbacks -int32_t get_line() {return ex.line;} uint8_t get_tool() {return ex.tool;} float get_velocity() {return ex.velocity / VELOCITY_MULTIPLIER;} float get_acceleration() {return ex.accel / ACCEL_MULTIPLIER;} @@ -116,7 +112,6 @@ float get_feed_override() {return ex.feed_override;} float get_speed_override() {return ex.spindle_override;} float get_axis_position(int axis) {return ex.position[axis];} -void set_line(int32_t line) {ex.line = line;} void set_tool(uint8_t tool) {ex.tool = tool;} void set_feed_override(float value) {ex.feed_override = value;} void set_speed_override(float value) {ex.spindle_override = value;} diff --git a/src/avr/src/exec.h b/src/avr/src/exec.h index 03f7b7f..e512b14 100644 --- a/src/avr/src/exec.h +++ b/src/avr/src/exec.h @@ -47,8 +47,6 @@ float exec_get_velocity(); void exec_set_acceleration(float a); float exec_get_acceleration(); void exec_set_jerk(float j); -void exec_set_line(int32_t line); -int32_t exec_get_line(); void exec_set_cb(exec_cb_t cb); diff --git a/src/avr/src/jog.c b/src/avr/src/jog.c index a5faacd..bc5705b 100644 --- a/src/avr/src/jog.c +++ b/src/avr/src/jog.c @@ -60,9 +60,6 @@ typedef struct { bool writing; bool done; - float Vi; - float Vt; - jog_axis_t axes[AXES]; } jog_runtime_t; diff --git a/src/avr/src/state.c b/src/avr/src/state.c index 4d36495..68fafb8 100644 --- a/src/avr/src/state.c +++ b/src/avr/src/state.c @@ -85,7 +85,6 @@ state_t state_get() {return s.state;} static void _set_state(state_t state) { if (s.state == state) return; // No change if (s.state == STATE_ESTOPPED) return; // Can't leave EStop state - if (state == STATE_READY) exec_set_line(0); s.state = state; report_request(); } diff --git a/src/avr/src/stepper.c b/src/avr/src/stepper.c index 11b8677..abaa9c4 100644 --- a/src/avr/src/stepper.c +++ b/src/avr/src/stepper.c @@ -115,7 +115,7 @@ ISR(STEP_LOW_LEVEL_ISR) { case STAT_NOP: st.busy = false; break; // No command executed case STAT_AGAIN: continue; // No command executed, try again - case STAT_OK: // Move executed + case STAT_OK: // Move executed if (!st.move_queued) ALARM(STAT_EXPECTED_MOVE); // No move was queued st.move_queued = false; st.move_ready = true; diff --git a/src/avr/src/vars.def b/src/avr/src/vars.def index 2a12f8b..fe38672 100644 --- a/src/avr/src/vars.def +++ b/src/avr/src/vars.def @@ -99,7 +99,6 @@ VAR(hy_connected, he, bool, 0, 0, 1, "Huanyang connected") // Machine state VAR(id, id, u32, 0, 1, 1, "Last executed command ID") -VAR(line, ln, s32, 0, 1, 1, "Last line executed") VAR(speed, s, f32, 0, 1, 1, "Current spindle speed") VAR(tool, t, u8, 0, 1, 1, "Current tool") VAR(feed_override, fo, f32, 0, 1, 1, "Feed rate override") diff --git a/src/jade/templates/console.jade b/src/jade/templates/console.jade index ec86cba..e546e2e 100644 --- a/src/jade/templates/console.jade +++ b/src/jade/templates/console.jade @@ -12,7 +12,7 @@ script#console-template(type="text/x-template") th Repeat th Message - tr(v-for="msg in messages.reverse()", :class="msg.level || 'info'") + tr(v-for="msg in messages", :class="msg.level || 'info'") td {{msg.level || 'info'}} td {{msg.source || ''}} td {{msg.where || ''}} diff --git a/src/jade/templates/control-view.jade b/src/jade/templates/control-view.jade index 8d741ff..0e8ddcc 100644 --- a/src/jade/templates/control-view.jade +++ b/src/jade/templates/control-view.jade @@ -17,16 +17,16 @@ script#control-view-template(type="text/x-template") td.absolute {{state.#{axis}p || 0 | fixed 3}} td.offset {{get_offset('#{axis}') | fixed 3}} th.actions - button.pure-button(:disabled="state.x != 'READY'", + button.pure-button(:disabled="state.xx != 'READY'", title="Set {{'#{axis}' | upper}} axis position.", @click="show_set_position('#{axis}')") .fa.fa-cog - button.pure-button(:disabled="state.x != 'READY'", + button.pure-button(:disabled="state.xx != 'READY'", title="Zero {{'#{axis}' | upper}} axis offset.", @click="zero_axis('#{axis}')") ∅ - button.pure-button(:disabled="state.x != 'READY'", + button.pure-button(:disabled="state.xx != 'READY'", title="Home {{'#{axis}' | upper}} axis.", @click="home('#{axis}')") .fa.fa-home @@ -80,18 +80,18 @@ script#control-view-template(type="text/x-template") th Message td.reason(:class="{attention: highlight_reason()}") {{get_reason()}} td + tr + th Units + td {{state.imperial ? 'IMPERIAL' : 'METRIC'}} + td tr th Feed - td {{state.f || 0 | fixed 0}} + td {{state.feed || 0 | fixed 0}} td mm/min tr th Speed - td {{state.s || 0 | fixed 0}} + td {{state.speed || 0 | fixed 0}} td RPM - tr - th Direction - td {{state.ss || 'Off'}} - td table.info tr @@ -100,19 +100,19 @@ script#control-view-template(type="text/x-template") td m/min tr th Line - td {{0 <= state.ln ? state.ln : '-'}} + td {{0 <= state.line ? state.line : '-'}} td tr th Tool - td {{state.t || 0}} + td {{state.tool || 0}} td tr th Mist - td {{state.mist || 'Off'}} + td {{state.mist ? 'On' : 'Off'}} td tr th Coolant - td {{state.coolant || 'Off'}} + td {{state.coolant ? 'On' : 'Off'}} td .override(title="Feed rate override.") @@ -130,16 +130,16 @@ script#control-view-template(type="text/x-template") .toolbar button.pure-button(title="Home the machine.", @click="home()", - :disabled="state.x != 'READY'") + :disabled="state.xx != 'READY'") .fa.fa-home button.pure-button( - title="{{state.x == 'RUNNING' ? 'Pause' : 'Start'}} program.", + title="{{state.xx == 'RUNNING' ? 'Pause' : 'Start'}} program.", @click="start_pause", :disabled="!file") - .fa(:class="state.x == 'RUNNING' ? 'fa-pause' : 'fa-play'") + .fa(:class="state.xx == 'RUNNING' ? 'fa-pause' : 'fa-play'") button.pure-button(title="Stop program.", @click="stop", - :disabled="state.x == 'READY'") + :disabled="state.xx == 'READY'") .fa.fa-stop button.pure-button(title="Pause program at next optional stop (M1).", @@ -147,7 +147,7 @@ script#control-view-template(type="text/x-template") .fa.fa-stop-circle-o button.pure-button(title="Execute one program step.", @click="step", - :disabled="(state.x != 'READY' && state.x != 'HOLDING') || !file") + :disabled="(state.xx != 'READY' && state.xx != 'HOLDING') || !file") .fa.fa-step-forward .tabs @@ -172,7 +172,7 @@ script#control-view-template(type="text/x-template") section#content1.tab-content .toolbar button.pure-button(title="Upload a new GCode program.", @click="open", - :disabled="state.x == 'RUNNING' || state.x == 'STOPPING'") + :disabled="state.xx == 'RUNNING' || state.xx == 'STOPPING'") .fa.fa-folder-open input.gcode-file-input(type="file", @change="upload", @@ -196,15 +196,15 @@ script#control-view-template(type="text/x-template") select(title="Select previously uploaded GCode programs.", v-model="file", @change="load", - :disabled="state.x == 'RUNNING' || state.x == 'STOPPING'") + :disabled="state.xx == 'RUNNING' || state.xx == 'STOPPING'") option(v-for="file in files", :value="file") {{file}} - .gcode(:class="{placeholder: !gcode}") + .gcode(:class="{placeholder: !gcode}", @scroll="gcode_scroll") span(v-if="!gcode.length") GCode displays here. ul li(v-for="item in gcode", id="gcode-line-{{$index}}", track-by="$index") - span {{$index + 1}} + span {{$index + 1 + gcode_offset}} | {{item}} section#content2.tab-content @@ -248,4 +248,6 @@ script#control-view-template(type="text/x-template") section#content6.tab-content .video + img.reload(src="/images/reload.png", @click="load_video", + title="Reload video") img.mjpeg(:src="video_url") diff --git a/src/jade/templates/indicators.jade b/src/jade/templates/indicators.jade index 8d94da3..36f9ea7 100644 --- a/src/jade/templates/indicators.jade +++ b/src/jade/templates/indicators.jade @@ -2,33 +2,34 @@ script#indicators-template(type="text/x-template") .indicators table.inputs tr - th(colspan=6) Switch Inputs + th.header(colspan=7) Inputs tr th th Pin th Name + th.separator th th Pin th Name - each motor in '01234' - tr(v-if="is_motor_enabled(#{motor})") + each motor in '0123' + tr td .fa(:class="get_class('#{motor}lw')") th {{get_min_pin(#{motor})}} - th #{motor} Min - + th Motor #{motor} Min + th.separator td .fa(:class="get_class('#{motor}xw')") th {{get_max_pin(#{motor})}} - th #{motor} Max + th Motor #{motor} Max tr td .fa(:class="get_class('ew')") th 23 th EStop - + th.separator td .fa(:class="get_class('pw')") th 22 @@ -36,12 +37,13 @@ script#indicators-template(type="text/x-template") table.outputs tr - th(colspan=6) Outputs + th.header(colspan=7) Outputs tr th th Pin th Name + th.separator th th Pin th Name @@ -51,7 +53,7 @@ script#indicators-template(type="text/x-template") .fa(:class="get_class('eos')") th 15 th Tool Enable - + th.separator td .fa(:class="get_class('1os')") th 2 @@ -62,7 +64,7 @@ script#indicators-template(type="text/x-template") .fa(:class="get_class('dos')") th 16 th Tool Direction - + th.separator td .fa(:class="get_class('2os')") th 1 @@ -72,7 +74,7 @@ script#indicators-template(type="text/x-template") td {{state['pd'] | percent 0}} th 17 th Tool PWM - + th.separator td .fa(:class="get_class('fos')") th 21 @@ -80,68 +82,69 @@ script#indicators-template(type="text/x-template") table.measurements tr - th(colspan=4) Measurements + th.header(colspan=5) Measurements tr td {{state['vin'] | fixed 1}} V th Input - + th.separator td {{state['vout'] | fixed 1}} V th Output tr td {{state['motor'] | fixed 2}} A th Motor - + th.separator td {{state['temp'] | fixed 0}} ℃ th Temp tr td {{state['load1'] | fixed 2}} A th Load 1 - + th.separator td {{state['load2'] | fixed 2}} A th Load 2 table.rs485 tr - th(colspan=4) RS485 Spindle + th.header(colspan=5) RS485 Spindle tr - th(colspan=4) {{state['he'] ? "" : "Not "}}Connected + th(colspan=5) {{state['he'] ? "" : "Not "}}Connected tr td {{state['hz']}} Hz th Frequency - + th.separator td {{state['hc']}} A th Current tr td {{state['hr']}} RPM th Speed - + th.separator td {{state['ht']}} ℃ th Temp table.legend tr - th(colspan=10) Legend + th.header(colspan=10) Legend tr td .fa.fa-circle.logic-hi th Logic Hi - tr td .fa.fa-circle.logic-lo th Logic Lo - tr td .fa.fa-circle-o.logic-tri th Tristated / Disabled + h2 DB25 breakout box + img(width=700, src="/images/DB25_breakout_box.png") + h2 DB25-M2 breakout - center: img(width=400, src="/images/DB25-M2_breakout.png") + img(width=400, src="/images/DB25-M2_breakout.png") diff --git a/src/js/app.js b/src/js/app.js index 58947ad..5d39577 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -109,7 +109,7 @@ module.exports = new Vue({ methods: { estop: function () { - if (this.state.x == 'ESTOPPED') api.put('clear'); + if (this.state.xx == 'ESTOPPED') api.put('clear'); else api.put('estop'); }, diff --git a/src/js/console.js b/src/js/console.js index d8cb148..62b90eb 100644 --- a/src/js/console.js +++ b/src/js/console.js @@ -31,12 +31,11 @@ module.exports = { msg.level = msg.level || 'info'; // Add to message log and count and collapse repeats - if (messages.length && _msg_equal(msg, messages[messages.length - 1])) - messages[messages.length - 1].repeat++; - + var repeat = messages.length && _msg_equal(msg, messages[0]); + if (repeat) messages[0].repeat++; else { msg.repeat = 1; - messages.push(msg); + messages.unshift(msg); } // Write message to browser console for debugging @@ -47,7 +46,7 @@ module.exports = { else console.log(text); // Event on errors - if (msg.level == 'error' || msg.level == 'critical') + if (!repeat && (msg.level == 'error' || msg.level == 'critical')) this.$dispatch('error', msg); } }, diff --git a/src/js/control-view.js b/src/js/control-view.js index 32a647d..4e7005e 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -2,6 +2,9 @@ var api = require('./api'); +var maxLines = 1000; +var pageSize = Math.round(maxLines / 10); + function _is_array(x) { return Object.prototype.toString.call(x) === '[object Array]'; @@ -26,7 +29,9 @@ module.exports = { last_file: '', files: [], axes: 'xyzabc', + all_gcode: [], gcode: [], + gcode_offset: 0, history: [], speed_override: 1, feed_override: 1, @@ -46,7 +51,7 @@ module.exports = { watch: { - 'state.ln': function () {this.update_gcode_line();} + 'state.line': function () {this.update_gcode_line();} }, @@ -63,12 +68,12 @@ module.exports = { methods: { - get_state: function () {return this.state.x || ''}, + get_state: function () {return this.state.xx || ''}, get_reason: function () { - if (this.state.x == 'ESTOPPED') return this.state.er; - if (this.state.x == 'HOLDING') return this.state.pr; + if (this.state.xx == 'ESTOPPED') return this.state.er; + if (this.state.xx == 'HOLDING') return this.state.pr; return ''; }, @@ -119,20 +124,80 @@ module.exports = { }, + gcode_move_up: function (count) { + var lines = 0; + + for (var i = 0; i < count; i++) { + if (!this.gcode_offset) break; + + this.gcode.unshift(this.all_gcode[this.gcode_offset - 1]) + this.gcode.pop(); + this.gcode_offset--; + lines++; + } + + return lines; + }, + + + gcode_move_down: function (count) { + var lines = 0; + + for (var i = 0; i < count; i++) { + if (this.all_gcode.length <= this.gcode_offset + this.gcode.length) + break; + + this.gcode.push(this.all_gcode[this.gcode_offset + this.gcode.length]) + this.gcode.shift(); + this.gcode_offset++; + lines++ + } + + return lines; + }, + + + gcode_scroll: function (e) { + if (this.gcode.length == this.all_gcode.length) return; + + var t = e.target; + var percentScroll = t.scrollTop / (t.scrollHeight - t.clientHeight); + + var lines = 0; + if (percentScroll < 0.2) lines = this.gcode_move_up(pageSize); + else if (0.8 < percentScroll) lines = -this.gcode_move_down(pageSize); + else return; + + if (lines) t.scrollTop += t.scrollHeight * lines / maxLines; + }, + + update_gcode_line: function () { if (typeof this.last_line != 'undefined') { $('#gcode-line-' + this.last_line).removeClass('highlight'); this.last_line = undefined; } - if (0 <= this.state.ln) { - var line = this.state.ln - 1; - var e = $('#gcode-line-' + line); - if (e.length) - e.addClass('highlight')[0] - .scrollIntoView({behavior: 'smooth'}); + if (0 <= this.state.line) { + var line = this.state.line - 1; + + // Make sure the current GCode is loaded + if (line < this.gcode_offset || + this.gcode_offset + this.gcode.length <= line) { + this.gcode_offset = line - pageSize; + if (this.gcode_offset < 0) this.gcode_offset = 0; - this.last_line = line; + this.gcode = this.all_gcode.slice(this.gcode_offset, maxLines); + } + + Vue.nextTick(function () { + var e = $('#gcode-line-' + line); + if (e.length) + e.addClass('highlight')[0] + .scrollIntoView({behavior: 'smooth'}); + + this.last_line = line; + }.bind(this)); } }, @@ -190,6 +255,7 @@ module.exports = { if (!file || this.files.indexOf(file) == -1) { this.file = ''; + this.all_gcode = []; this.gcode = []; return; } @@ -198,7 +264,9 @@ module.exports = { api.get('file/' + file) .done(function (data) { - this.gcode = data.trimRight().split(/\r?\n/); + this.all_gcode = data.trimRight().split(/\r?\n/); + this.gcode = this.all_gcode.slice(0, maxLines); + this.gcode_offset = 0; this.last_file = file; Vue.nextTick(this.update_gcode_line); @@ -254,9 +322,9 @@ module.exports = { start_pause: function () { - if (this.state.x == 'RUNNING') this.pause(); + if (this.state.xx == 'RUNNING') this.pause(); - else if (this.state.x == 'STOPPING' || this.state.x == 'HOLDING') + else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') this.unpause(); else this.start(); diff --git a/src/js/gcode-view.js b/src/js/gcode-view.js index b12d5f8..bdd66f9 100644 --- a/src/js/gcode-view.js +++ b/src/js/gcode-view.js @@ -21,9 +21,7 @@ module.exports = { }, - ready: function () { - this.update(); - }, + ready: function () {this.update()}, methods: { diff --git a/src/py/bbctrl/AVR.py b/src/py/bbctrl/AVR.py index 478d9bf..34bc64e 100644 --- a/src/py/bbctrl/AVR.py +++ b/src/py/bbctrl/AVR.py @@ -39,6 +39,7 @@ class AVR(): self.queue = deque() self.in_buf = '' self.command = None + self.stopping = False self.lcd_page = ctrl.lcd.add_new_page() self.install_page = True @@ -195,6 +196,13 @@ class AVR(): log.warning('firmware rebooted') self.connect() + if self.stopping and 'xx' in update and update['xx'] == 'HOLDING': + self._stop_sending_gcode() + # Resume once current queue of GCode commands has flushed + self._i2c_command(Cmd.FLUSH) + self._queue_command(Cmd.RESUME) + self.stopping = False + self.ctrl.state.update(update) # Must be after AVR vars have loaded @@ -204,15 +212,15 @@ class AVR(): def _update_state(self, update): - if 'x' in update and update['x'] == 'ESTOPPED': + if 'xx' in update and update['xx'] == 'ESTOPPED': self._stop_sending_gcode() self._update_lcd(update) def _update_lcd(self, update): - if 'x' in update: - self.lcd_page.text('%-9s' % self.ctrl.state.get('x'), 0, 0) + if 'xx' in update: + self.lcd_page.text('%-9s' % self.ctrl.state.get('xx'), 0, 0) # Show enabled axes row = 0 @@ -236,7 +244,9 @@ class AVR(): def connect(self): try: # Reset AVR communication - self.stop(); + self._stop_sending_gcode() + # Resume once current queue of GCode commands has flushed + self._queue_command(Cmd.RESUME) self._queue_command('h') # Load AVR commands and variables except Exception as e: @@ -282,19 +292,19 @@ class AVR(): if self._is_busy(): raise Exception('Busy, cannot home') if position is not None: - self.ctrl.planner.mdi('G28.3 %c%f' % (axis, position)) + self.mdi('G28.3 %c%f' % (axis, position)) else: if axis is None: axes = 'zxyabc' # TODO This should be configurable else: axes = '%c' % axis for axis in axes: - if not self.ctrl.state.axis_can_home(axis): continue + if not self.ctrl.state.axis_can_home(axis): + log.info('Cannot home ' + axis) + continue log.info('Homing %s axis' % axis) - gcode = axis_homing_procedure % {'axis': axis} - self.ctrl.planner.mdi(gcode) - self._set_write(True) + self.mdi(axis_homing_procedure % {'axis': axis}) def estop(self): self._i2c_command(Cmd.ESTOP) @@ -308,22 +318,20 @@ class AVR(): def step(self, path): self._i2c_command(Cmd.STEP) if not self._is_busy() and path and \ - self.ctrl.state.get('x', '') == 'READY': + self.ctrl.state.get('xx', '') == 'READY': self._start_sending_gcode(path) def stop(self): - self._i2c_command(Cmd.FLUSH) - self._stop_sending_gcode() - # Resume processing once current queue of GCode commands has flushed - self._queue_command(Cmd.RESUME) + self.pause() + self.stopping = True def pause(self): self._i2c_command(Cmd.PAUSE, byte = 0) def unpause(self): - if self.ctrl.state.get('x', '') != 'HOLDING' or not self._is_busy(): + if self.ctrl.state.get('xx', '') != 'HOLDING' or not self._is_busy(): return self._i2c_command(Cmd.FLUSH) @@ -340,5 +348,5 @@ class AVR(): if self._is_busy(): raise Exception('Busy, cannot set position') if self.ctrl.state.is_axis_homed('%c' % axis): - self.ctrl.planner.mdi('G92 %c%f' % (axis, position)) + self.mdi('G92 %c%f' % (axis, position)) else: self._queue_command('$%cp=%f' % (axis, position)) diff --git a/src/py/bbctrl/Cmd.py b/src/py/bbctrl/Cmd.py index c35f740..43c87b9 100755 --- a/src/py/bbctrl/Cmd.py +++ b/src/py/bbctrl/Cmd.py @@ -45,11 +45,11 @@ def encode_axes(axes): return data -def line_number(line): return '#ln=%d' % line +def set(name, value): return '#%s=%s' % (name, value) -def line(id, target, exitVel, maxAccel, maxJerk, times): - cmd = '#id=%u\n%c' % (id, LINE) +def line(target, exitVel, maxAccel, maxJerk, times): + cmd = LINE cmd += encode_float(exitVel) cmd += encode_float(maxAccel) diff --git a/src/py/bbctrl/Config.py b/src/py/bbctrl/Config.py index 5ac4f8f..11e8239 100644 --- a/src/py/bbctrl/Config.py +++ b/src/py/bbctrl/Config.py @@ -19,6 +19,7 @@ default_config = { "outputs": {}, "tool": {}, "gcode": {}, + "planner": {}, "admin": {}, } diff --git a/src/py/bbctrl/Ctrl.py b/src/py/bbctrl/Ctrl.py index 7a22817..94bb9bb 100644 --- a/src/py/bbctrl/Ctrl.py +++ b/src/py/bbctrl/Ctrl.py @@ -20,7 +20,8 @@ class IPPage(bbctrl.LCDPage): self.text('Host: %s' % hostname[0:14], 0, 0) for i in range(min(3, len(ips))): - self.text('IP: %s' % ips[i], 0, i + 1) + if len(ips[i]) <= 16: + self.text('IP: %s' % ips[i], 0, i + 1) def activate(self): self.update() diff --git a/src/py/bbctrl/Jog.py b/src/py/bbctrl/Jog.py index 6740b4d..c381853 100644 --- a/src/py/bbctrl/Jog.py +++ b/src/py/bbctrl/Jog.py @@ -37,7 +37,7 @@ class Jog(inevent.JogHandler): self.callback() self.processor = inevent.InEvent(ctrl.ioloop, self, - types = "js kbd".split()) + types = 'js kbd'.split()) def up(self): self.ctrl.lcd.page_up() diff --git a/src/py/bbctrl/Planner.py b/src/py/bbctrl/Planner.py index d7bcd43..cd9a987 100644 --- a/src/py/bbctrl/Planner.py +++ b/src/py/bbctrl/Planner.py @@ -1,6 +1,7 @@ import json import re import logging +from collections import deque import camotics.gplan as gplan # pylint: disable=no-name-in-module,import-error import bbctrl.Cmd as Cmd @@ -15,6 +16,7 @@ class Planner(): self.ctrl = ctrl self.lastID = -1 self.mode = 'idle' + self.setq = deque() ctrl.state.add_listener(self.update) @@ -65,14 +67,36 @@ class Planner(): def update(self, update): - if 'id' in update: self.planner.set_active(update['id']) + if 'id' in update: + id = update['id'] + self.planner.set_active(id) - if self.ctrl.state.get('x', '') == 'HOLDING' and \ + # Syncronize planner variables with execution id + self.release_set_cmds(id) + + # Automatically unpause on seek hold + if self.ctrl.state.get('xx', '') == 'HOLDING' and \ self.ctrl.state.get('pr', '') == 'Switch found' and \ self.planner.is_synchronizing(): self.ctrl.avr.unpause() + def release_set_cmds(self, id): + self.lastID = id + + # Apply all set commands <= to ID and those that follow consecutively + while len(self.setq) and self.setq[0][0] - 1 <= self.lastID: + id, name, value = self.setq.popleft() + self.ctrl.state.set(name, value) + if id == self.lastID + 1: self.lastID = id + + + def queue_set_cmd(self, id, name, value): + log.info('Planner set(#%d, %s, %s)', id, name, value) + self.setq.append((id, name, value)) + self.release_set_cmds(self.lastID) + + def restart(self): state = self.ctrl.state id = state.get('id') @@ -136,20 +160,21 @@ class Planner(): self.planner = gplan.Planner() self.planner.set_resolver(self.get_var) self.planner.set_logger(self.log, 1, 'LinePlanner:3') + self.setq.clear() - def encode(self, block): + def _encode(self, block): type = block['type'] if type == 'line': - return Cmd.line(block['id'], block['target'], block['exit-vel'], + return Cmd.line(block['target'], block['exit-vel'], block['max-accel'], block['max-jerk'], block['times']) if type == 'set': name, value = block['name'], block['value'] - if name == 'line': return Cmd.line_number(value) + if name == 'line': self.queue_set_cmd(block['id'], name, value) if name == 'tool': return Cmd.tool(value) if name == 'speed': return Cmd.speed(value) if name[0:1] == '_' and name[1:2] in 'xyzabc' and \ @@ -157,7 +182,7 @@ class Planner(): return Cmd.set_position(name[1], value) if len(name) and name[0] == '_': - self.ctrl.state.set(name[1:], value) + self.queue_set_cmd(block['id'], name[1:], value) return @@ -172,13 +197,17 @@ class Planner(): raise Exception('Unknown planner type "%s"' % type) + def encode(self, block): + cmd = self._encode(block) + if cmd is not None: return Cmd.set('id', block['id']) + '\n' + cmd + + def has_move(self): return self.planner.has_more() def next(self): while self.planner.has_more(): cmd = self.planner.next() - self.lastID = cmd['id'] cmd = self.encode(cmd) if cmd is not None: return cmd diff --git a/src/py/bbctrl/State.py b/src/py/bbctrl/State.py index d98e0fd..523e488 100644 --- a/src/py/bbctrl/State.py +++ b/src/py/bbctrl/State.py @@ -6,7 +6,7 @@ import bbctrl log = logging.getLogger('State') -machine_state_vars = 'xp yp zp ap bp cp t fo so'.split() +machine_state_vars = '''xp yp zp ap bp cp t fo so'''.split() class State(object): @@ -33,6 +33,12 @@ class State(object): # Set not homed self.set('%dhomed' % i, False) + # Zero offsets + for axis in 'xyzabc': self.vars['offset_' + axis] = 0 + + # No line + self.vars['line'] = -1 + def _notify(self): if not self.changes: return diff --git a/src/py/inevent/EventStream.py b/src/py/inevent/EventStream.py index 404815e..90fb8b2 100644 --- a/src/py/inevent/EventStream.py +++ b/src/py/inevent/EventStream.py @@ -168,7 +168,7 @@ class EventStream(object): return event except Exception as e: - log.warning('Reading event: %s' % e) + log.info('Reading event: %s' % e) def __enter__(self): return self diff --git a/src/py/inevent/InEvent.py b/src/py/inevent/InEvent.py index 8f01355..df0822a 100644 --- a/src/py/inevent/InEvent.py +++ b/src/py/inevent/InEvent.py @@ -90,7 +90,7 @@ class InEvent(object): The keys are listed in inevent.Constants.py or /usr/include/linux/input.h Note that the key names refer to a US keyboard. """ - def __init__(self, ioloop, cb, types = ["kbd", "mouse", "js"]): + def __init__(self, ioloop, cb, types = 'kbd mouse js'.split()): self.ioloop = ioloop self.cb = cb self.streams = [] diff --git a/src/resources/images/DB25_breakout_box.png b/src/resources/images/DB25_breakout_box.png new file mode 100644 index 0000000..5cfa26f Binary files /dev/null and b/src/resources/images/DB25_breakout_box.png differ diff --git a/src/resources/images/reload.png b/src/resources/images/reload.png new file mode 100644 index 0000000..9f417cd Binary files /dev/null and b/src/resources/images/reload.png differ diff --git a/src/stylus/style.styl b/src/stylus/style.styl index 388add5..420befc 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -392,11 +392,15 @@ body color green .indicators + padding 1em + text-align center + table display inline-block vertical-align top margin 0.5em empty-cells show + border 3px solid #bbb td, th padding 4px @@ -420,6 +424,15 @@ body th:nth-child(3), th:nth-child(6) text-align left + th.header + height 2.5em + border-bottom 3px solid #ccc + + th.separator + background #ccc + border 1px solid #ccc + padding 1px + .logic-lo color black @@ -433,7 +446,14 @@ body .video text-align center line-height 0 + min-height 200px + + .reload + float left + margin-right -48px + &:hover + opacity 0.5 .tabs clear both