From 182fd3cd1cd57b478d23da38f62f485debd8c40a Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Sat, 24 Feb 2018 14:12:57 -0800 Subject: [PATCH] - Fixed problem with selecting newly uploaded file. - More thorough shutdown of stepper driver in estop. - Fixed spindle type specific options. - No more Unexpected AVR firmware reboot errors on estop clear. - Downgraded Machine alarmed - Command not processed errors to warnings. - Suppress unnecessary axis homing warnings. - More details for axis homing errors.a - Support GCode messages e.g. (MSG, Hello World!) - Support programmed pauses. i.e. M0 --- CHANGELOG.md | 9 ++++++ src/avr/src/command.c | 8 ++++- src/avr/src/command.def | 4 +-- src/avr/src/commands.c | 10 ------ src/avr/src/drv8711.c | 5 +++ src/avr/src/drv8711.h | 1 + src/avr/src/exec.c | 14 ++------- src/avr/src/huanyang.c | 5 +-- src/avr/src/line.c | 4 +++ src/avr/src/state.c | 31 ++++++++++++++----- src/avr/src/state.h | 12 +++++--- src/avr/src/status.h | 4 +-- src/avr/src/stepper.c | 28 ++++++++--------- src/avr/src/usart.c | 5 --- src/avr/src/vars.def | 2 -- src/jade/index.jade | 10 ++++++ src/js/app.js | 21 +++++++++++-- src/js/console.js | 2 +- src/js/control-view.js | 7 +++-- src/py/bbctrl/Cmd.py | 14 +++++++-- src/py/bbctrl/Comm.py | 16 ++++++++++ src/py/bbctrl/Config.py | 13 ++++---- src/py/bbctrl/FileHandler.py | 5 ++- src/py/bbctrl/Mach.py | 59 ++++++++++++++++++++++++------------ src/py/bbctrl/Messages.py | 8 +++-- src/py/bbctrl/Planner.py | 20 +++++++----- src/py/bbctrl/State.py | 48 +++++++++++++++++++---------- src/py/bbctrl/Web.py | 5 ++- 28 files changed, 241 insertions(+), 129 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fe4ecc..0e1b772 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ Buildbotics CNC Controller Firmware Change Log ## v0.3.12 - Updated DB25 M2 breakout diagram. - Enabled AVR watchdog. + - Fixed problem with selecting newly uploaded file. + - More thorough shutdown of stepper driver in estop. + - Fixed spindle type specific options. + - No more ``Unexpected AVR firmware reboot`` errors on estop clear. + - Downgraded ``Machine alarmed - Command not processed`` errors to warnings. + - Suppress unnecessary axis homing warnings. + - More details for axis homing errors. + - Support GCode messages e.g. (MSG, Hello World!) + - Support programmed pauses. i.e. M0 ## v0.3.11 - Supressed ``firmware rebooted`` warning. diff --git a/src/avr/src/command.c b/src/avr/src/command.c index d11beab..4041f1c 100644 --- a/src/avr/src/command.c +++ b/src/avr/src/command.c @@ -207,7 +207,13 @@ bool command_callback() { // Reporting report_request(); - if (status && status != STAT_NOP) STATUS_ERROR(status, ""); + + switch (status) { + case STAT_OK: break; + case STAT_NOP: break; + case STAT_MACHINE_ALARMED: STATUS_WARNING(status, ""); break; + default: STATUS_ERROR(status, ""); break; + } return true; } diff --git a/src/avr/src/command.def b/src/avr/src/command.def index b18dbfd..53eca13 100644 --- a/src/avr/src/command.def +++ b/src/avr/src/command.def @@ -31,9 +31,7 @@ CMD('s', seek, 1, "[switch][flags:active|error]") CMD('a', set_axis, 1, "[axis][position] Set axis position") CMD('l', line, 1, "[targetVel][maxJerk][axes][times]") CMD('d', dwell, 1, "[seconds]") -CMD('o', out, 1, "Output") -CMD('p', opt_pause, 1, "Set an optional pause") -CMD('P', pause, 0, "[optional]") +CMD('P', pause, 1, "[type] Pause control") CMD('U', unpause, 0, "Unpause") CMD('j', jog, 0, "[axes]") CMD('r', report, 0, "<0|1>[var] Enable or disable var reporting") diff --git a/src/avr/src/commands.c b/src/avr/src/commands.c index 3d5f092..59ad978 100644 --- a/src/avr/src/commands.c +++ b/src/avr/src/commands.c @@ -26,18 +26,14 @@ \******************************************************************************/ #include "config.h" -#include "rtc.h" #include "stepper.h" #include "command.h" #include "vars.h" #include "base64.h" #include "hardware.h" #include "report.h" -#include "state.h" #include "exec.h" -#include "util.h" -#include #include @@ -64,12 +60,6 @@ void command_dwell_exec(float *seconds) { } -// TODO -stat_t command_out(char *cmd) {return STAT_OK;} -unsigned command_out_size() {return 0;} -void command_out_exec(void *data) {} - - stat_t command_help(char *cmd) { printf_P(PSTR("\n{\"commands\":{")); command_print_json(); diff --git a/src/avr/src/drv8711.c b/src/avr/src/drv8711.c index e9f77e6..9deaf5b 100644 --- a/src/avr/src/drv8711.c +++ b/src/avr/src/drv8711.c @@ -366,6 +366,11 @@ void drv8711_init() { } +void drv8711_shutdown() { + SPIC.INTCTRL = 0; // Disable SPI interrupts +} + + drv8711_state_t drv8711_get_state(int driver) { if (driver < 0 || DRIVERS <= driver) return DRV8711_DISABLED; return drivers[driver].state; diff --git a/src/avr/src/drv8711.h b/src/avr/src/drv8711.h index 3119eb3..bbcb4ed 100644 --- a/src/avr/src/drv8711.h +++ b/src/avr/src/drv8711.h @@ -173,6 +173,7 @@ typedef void (*stall_callback_t)(int driver); void drv8711_init(); +void drv8711_shutdown(); drv8711_state_t drv8711_get_state(int driver); void drv8711_set_state(int driver, drv8711_state_t state); void drv8711_set_microsteps(int driver, uint16_t msteps); diff --git a/src/avr/src/exec.c b/src/avr/src/exec.c index 5c30f95..71be146 100644 --- a/src/avr/src/exec.c +++ b/src/avr/src/exec.c @@ -44,8 +44,6 @@ static struct { float accel; float jerk; - int tool; - float feed_override; float spindle_override; } ex; @@ -99,16 +97,13 @@ stat_t exec_next() { // Variable callbacks -uint8_t get_tool() {return ex.tool;} +float get_axis_position(int axis) {return ex.position[axis];} float get_velocity() {return ex.velocity / VELOCITY_MULTIPLIER;} float get_acceleration() {return ex.accel / ACCEL_MULTIPLIER;} float get_jerk() {return ex.jerk / JERK_MULTIPLIER;} 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_tool(uint8_t tool) {ex.tool = tool;} void set_feed_override(float value) {ex.feed_override = value;} +float get_speed_override() {return ex.spindle_override;} void set_speed_override(float value) {ex.spindle_override = value;} @@ -160,8 +155,3 @@ void command_set_axis_exec(void *data) { // Report report_request(); } - - -stat_t command_opt_pause(char *cmd) {command_push(*cmd, 0); return STAT_OK;} -unsigned command_opt_pause_size() {return 0;} -void command_opt_pause_exec(void *data) {} // TODO pause if requested diff --git a/src/avr/src/huanyang.c b/src/avr/src/huanyang.c index 9e8c88b..207f736 100644 --- a/src/avr/src/huanyang.c +++ b/src/avr/src/huanyang.c @@ -325,14 +325,15 @@ static bool _check_response() { ha.response[ha.response_length - 2]; if (computed != expected) { - STATUS_WARNING("huanyang: invalid CRC, expected=0x%04u got=0x%04u", + STATUS_WARNING(STAT_OK, "huanyang: invalid CRC, expected=0x%04u got=0x%04u", expected, computed); return false; } // Check if response code matches the code we sent if (ha.command[1] != ha.response[1]) { - STATUS_WARNING("huanyang: invalid function code, expected=%u got=%u", + STATUS_WARNING(STAT_OK, + "huanyang: invalid function code, expected=%u got=%u", ha.command[2], ha.response[2]); return false; } diff --git a/src/avr/src/line.c b/src/avr/src/line.c index 5f656a4..c73fae0 100644 --- a/src/avr/src/line.c +++ b/src/avr/src/line.c @@ -190,6 +190,10 @@ static stat_t _pause() { exec_set_jerk(0); _done(); + // TODO it's possible to exit here and have no more moves + // Apparently this pause method can take longer to pause than the + // actual move. FIX ME!!! + return STAT_AGAIN; } diff --git a/src/avr/src/state.c b/src/avr/src/state.c index 7a0b545..37dda17 100644 --- a/src/avr/src/state.c +++ b/src/avr/src/state.c @@ -70,9 +70,6 @@ PGM_P state_get_hold_reason_pgmstr(hold_reason_t reason) { switch (reason) { case HOLD_REASON_USER_PAUSE: return PSTR("User paused"); case HOLD_REASON_PROGRAM_PAUSE: return PSTR("Program paused"); - case HOLD_REASON_PROGRAM_END: return PSTR("Program end"); - case HOLD_REASON_PALLET_CHANGE: return PSTR("Pallet change"); - case HOLD_REASON_TOOL_CHANGE: return PSTR("Tool change"); case HOLD_REASON_STEPPING: return PSTR("Stepping"); case HOLD_REASON_SEEK: return PSTR("Switch found"); } @@ -120,9 +117,9 @@ void state_seek_hold() { void state_holding() {_set_state(STATE_HOLDING);} -void state_optional_pause() { - if (s.optional_pause_requested) { - _set_hold_reason(HOLD_REASON_USER_PAUSE); +void state_pause(bool optional) { + if (!optional || s.optional_pause_requested) { + _set_hold_reason(HOLD_REASON_PROGRAM_PAUSE); state_holding(); } } @@ -215,12 +212,30 @@ PGM_P get_hold_reason() {return state_get_hold_reason_pgmstr(s.hold_reason);} // Command callbacks stat_t command_pause(char *cmd) { - if (cmd[1] == '1') s.optional_pause_requested = true; - else s.pause_requested = true; + pause_t type = (pause_t)(cmd[1] - '0'); + + switch (type) { + case PAUSE_USER: s.pause_requested = true; break; + case PAUSE_OPTIONAL: s.optional_pause_requested = true; break; + default: command_push(cmd[0], &type); break; + } + return STAT_OK; } +unsigned command_pause_size() {return sizeof(pause_t);} + + +void command_pause_exec(void *data) { + switch (*(pause_t *)data) { + case PAUSE_PROGRAM: state_pause(false); break; + case PAUSE_PROGRAM_OPTIONAL: state_pause(true); break; + default: break; + } +} + + stat_t command_unpause(char *cmd) { s.start_requested = true; return STAT_OK; diff --git a/src/avr/src/state.h b/src/avr/src/state.h index 3a254ff..3d4059e 100644 --- a/src/avr/src/state.h +++ b/src/avr/src/state.h @@ -45,14 +45,19 @@ typedef enum { typedef enum { HOLD_REASON_USER_PAUSE, HOLD_REASON_PROGRAM_PAUSE, - HOLD_REASON_PROGRAM_END, - HOLD_REASON_PALLET_CHANGE, - HOLD_REASON_TOOL_CHANGE, HOLD_REASON_STEPPING, HOLD_REASON_SEEK, } hold_reason_t; +typedef enum { + PAUSE_USER, + PAUSE_OPTIONAL, + PAUSE_PROGRAM, + PAUSE_PROGRAM_OPTIONAL, +} pause_t; + + PGM_P state_get_pgmstr(state_t state); PGM_P state_get_hold_reason_pgmstr(hold_reason_t reason); @@ -64,7 +69,6 @@ bool state_is_quiescent(); void state_seek_hold(); void state_holding(); -void state_optional_pause(); void state_running(); void state_jogging(); void state_idle(); diff --git a/src/avr/src/status.h b/src/avr/src/status.h index 5458168..d1cab7b 100644 --- a/src/avr/src/status.h +++ b/src/avr/src/status.h @@ -76,8 +76,8 @@ stat_t status_alarm(const char *location, stat_t status, const char *msg); #define STATUS_DEBUG(MSG, ...) \ STATUS_MESSAGE(STAT_LEVEL_DEBUG, STAT_OK, MSG, ##__VA_ARGS__) -#define STATUS_WARNING(MSG, ...) \ - STATUS_MESSAGE(STAT_LEVEL_WARNING, STAT_OK, MSG, ##__VA_ARGS__) +#define STATUS_WARNING(CODE, MSG, ...) \ + STATUS_MESSAGE(STAT_LEVEL_WARNING, CODE, MSG, ##__VA_ARGS__) #define STATUS_ERROR(CODE, MSG, ...) \ STATUS_MESSAGE(STAT_LEVEL_ERROR, CODE, MSG, ##__VA_ARGS__) diff --git a/src/avr/src/stepper.c b/src/avr/src/stepper.c index 0039649..984245d 100644 --- a/src/avr/src/stepper.c +++ b/src/avr/src/stepper.c @@ -35,6 +35,7 @@ #include "cpp_magic.h" #include "report.h" #include "exec.h" +#include "drv8711.h" #include #include @@ -82,10 +83,18 @@ void stepper_init() { } +static void _end_move() { + for (int motor = 0; motor < MOTORS; motor++) + motor_end_move(motor); +} + + void st_shutdown() { - OUTCLR_PIN(MOTOR_ENABLE_PIN); - st.dwell = 0; - st.move_type = MOVE_TYPE_NULL; + OUTCLR_PIN(MOTOR_ENABLE_PIN); // Disable motors + TIMER_STEP.CTRLA = 0; // Stop stepper clock + _end_move(); // Stop motor clocks + ADCB_CH0_INTCTRL = 0; // Disable next move interrupt + drv8711_shutdown(); // Disable drivers } @@ -136,21 +145,8 @@ static void _request_exec_move() { } -static void _end_move() { - for (int motor = 0; motor < MOTORS; motor++) - motor_end_move(motor); -} - - /// Dwell or dequeue and load next move. static void _load_move() { - // Check EStop - if (estop_triggered()) { - st.move_type = MOVE_TYPE_NULL; - _end_move(); - return; - } - // New clock period TIMER_STEP.PER = st.clock_period; diff --git a/src/avr/src/usart.c b/src/avr/src/usart.c index 12aa002..06a8e1a 100644 --- a/src/avr/src/usart.c +++ b/src/avr/src/usart.c @@ -264,8 +264,3 @@ int16_t usart_rx_space() {return rx_buf_space();} int16_t usart_rx_fill() {return rx_buf_fill();} int16_t usart_tx_space() {return tx_buf_space();} int16_t usart_tx_fill() {return tx_buf_fill();} - - -// Var callbacks -bool get_echo() {return usart_is_set(USART_ECHO);} -void set_echo(bool value) {return usart_set(USART_ECHO, value);} diff --git a/src/avr/src/vars.def b/src/avr/src/vars.def index 3193d9f..fb85983 100644 --- a/src/avr/src/vars.def +++ b/src/avr/src/vars.def @@ -101,7 +101,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(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") VAR(speed_override, so, f32, 0, 1, 1, "Spindle speed override") @@ -110,7 +109,6 @@ VAR(velocity, v, f32, 0, 0, 1, "Current velocity") VAR(acceleration, ax, f32, 0, 0, 1, "Current acceleration") VAR(jerk, j, f32, 0, 0, 1, "Current jerk") VAR(hw_id, hid, str, 0, 0, 1, "Hardware ID") -VAR(echo, ec, bool, 0, 1, 1, "Enable or disable echo") VAR(estop, es, bool, 0, 1, 1, "Emergency stop") VAR(estop_reason, er, pstr, 0, 0, 1, "Emergency stop reason") VAR(state, xx, pstr, 0, 0, 1, "Machine state") diff --git a/src/jade/index.jade b/src/jade/index.jade index 9aa0f08..a23d5ed 100644 --- a/src/jade/index.jade +++ b/src/jade/index.jade @@ -159,6 +159,16 @@ html(lang="en") h3(slot="header") Firmware upgrading p(slot="body") Please wait... + message(:show.sync="showMessages") + h3(slot="header") GCode message + + div(slot="body") + ul + li(v-for="msg in messages") {{msg}} + + div(slot="footer") + button.pure-button.button-success(@click="close_messages") OK + #templates include ../../build/templates.jade diff --git a/src/js/app.js b/src/js/app.js index ac15dc6..ef8ef55 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -70,7 +70,8 @@ module.exports = new Vue({ checkedUpgrade: false, firmwareName: '', latestVersion: '', - password: '' + password: '', + showMessages: false } }, @@ -237,11 +238,13 @@ module.exports = new Vue({ this.sock.onmessage = function (e) { var msg = e.data; - if (typeof msg == 'object') + if (typeof msg == 'object') { for (var key in msg) { - if (key == 'msg') this.$broadcast('message', msg.msg); + if (key == 'log') this.$broadcast('log', msg.log); + else if (key == 'message') this.add_message(msg.message); else Vue.set(this.state, key, msg[key]); } + } }.bind(this) this.sock.onopen = function (e) { @@ -279,6 +282,18 @@ module.exports = new Vue({ }.bind(this)).fail(function (error) { alert('Save failed: ' + error); }); + }, + + + add_message: function (msg) { + this.messages.unshift(msg); + this.showMessages = true; + }, + + + close_messages: function () { + this.showMessages = false; + this.messages.splice(0, this.messages.length); } } }) diff --git a/src/js/console.js b/src/js/console.js index f10b442..80659b7 100644 --- a/src/js/console.js +++ b/src/js/console.js @@ -48,7 +48,7 @@ module.exports = { events: { - message: function (msg) { + log: function (msg) { // There may be multiple instances of this module so ignore messages // that have already been processed. if (msg.logged) return; diff --git a/src/js/control-view.js b/src/js/control-view.js index ff7f878..5edd318 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -241,7 +241,10 @@ module.exports = { update: function () { // Update file list - api.get('file').done(function (files) {this.files = files}.bind(this)) + api.get('file').done(function (files) { + this.files = files; + this.load(); + }.bind(this)) }, @@ -281,7 +284,7 @@ module.exports = { load: function () { var file = this.state.selected; - if (file == this.last_file) return; + if (typeof file == 'undefined' || file == this.last_file) return; api.get('file/' + file) .done(function (data) { diff --git a/src/py/bbctrl/Cmd.py b/src/py/bbctrl/Cmd.py index 79e3f31..6a4e1b4 100644 --- a/src/py/bbctrl/Cmd.py +++ b/src/py/bbctrl/Cmd.py @@ -41,7 +41,6 @@ SEEK = 's' SET_AXIS = 'a' LINE = 'l' DWELL = 'd' -OUTPUT = 'o' OPT_PAUSE = 'p' PAUSE = 'P' UNPAUSE = 'U' @@ -100,7 +99,6 @@ def line(target, exitVel, maxAccel, maxJerk, times): return cmd -def tool(tool): return '#t=%d' % tool def speed(speed): return '#s=:' + encode_float(speed) @@ -111,7 +109,17 @@ def output(port, value): def dwell(seconds): return 'd' + encode_float(seconds) -def pause(optional = False): 'P' + ('1' if optional else '0') + + +def pause(type): + if type == 'program': type = 2 + elif type == 'optional': type = 3 + elif type == 'pallet-change': type = 2 + else: raise Exception('Unknown pause type "%s"' % type) + + return 'P%d' % type + + def jog(axes): return 'j' + encode_axes(axes) diff --git a/src/py/bbctrl/Comm.py b/src/py/bbctrl/Comm.py index 7183cc2..2646a45 100644 --- a/src/py/bbctrl/Comm.py +++ b/src/py/bbctrl/Comm.py @@ -210,6 +210,22 @@ class Comm(): log.warning('Serial handler error: %s', traceback.format_exc()) + def estop(self): + if self.ctrl.state.get('xx', '') != 'ESTOPPED': + self.i2c_command(Cmd.ESTOP) + + + def clear(self): + if self.ctrl.state.get('xx', '') == 'ESTOPPED': + self.i2c_command(Cmd.CLEAR) + self.reboot_expected = True + + + def pause(self, optional = False): + data = ord('1' if optional else '0') + self.i2c_command(Cmd.PAUSE, byte = data) + + def reboot(self): self.queue_command(Cmd.REBOOT) self.reboot_expected = True diff --git a/src/py/bbctrl/Config.py b/src/py/bbctrl/Config.py index 71c605a..5362774 100644 --- a/src/py/bbctrl/Config.py +++ b/src/py/bbctrl/Config.py @@ -41,13 +41,7 @@ default_config = { {"axis": "Y"}, {"axis": "Z"}, {"axis": "A", "power-mode" : "disabled"}, - ], - "switches": {}, - "outputs": {}, - "tool": {}, - "gcode": {}, - "planner": {}, - "admin": {}, + ] } @@ -64,6 +58,11 @@ class Config(object): encoding = 'utf-8') as f: self.template = json.load(f) + # Add all sections from template to default config + for section in self.template: + if not section in default_config: + default_config[section] = {} + except Exception as e: log.exception(e) diff --git a/src/py/bbctrl/FileHandler.py b/src/py/bbctrl/FileHandler.py index a854153..6bff760 100644 --- a/src/py/bbctrl/FileHandler.py +++ b/src/py/bbctrl/FileHandler.py @@ -54,14 +54,17 @@ class FileHandler(bbctrl.APIHandler): with open(path, 'wb') as f: f.write(gcode['body']) + self.ctrl.state.set('selected', gcode['filename']) + def get(self, path): if path: path = path[1:] - self.ctrl.mach.select(path) with open('upload/' + path, 'r') as f: self.write_json(f.read()) + + self.ctrl.mach.select(path) return files = [] diff --git a/src/py/bbctrl/Mach.py b/src/py/bbctrl/Mach.py index 69a261a..2695d9a 100644 --- a/src/py/bbctrl/Mach.py +++ b/src/py/bbctrl/Mach.py @@ -88,22 +88,27 @@ class Mach(): # Handle EStop if 'xx' in update and state == 'ESTOPPED': self._stop_sending_gcode() + self.stopping = False # Handle stop - if self.stopping and 'xx' in update and state == 'HOLDING': - self._stop_sending_gcode() - # Resume once current queue of GCode commands has flushed - self.comm.i2c_command(Cmd.FLUSH) - self.comm.queue_command(Cmd.RESUME) - self.ctrl.state.set('line', 0) - self.stopping = False + if self.stopping: + if state == 'READY' and not self.planner.is_running(): + self.stopping = False + + if state == 'HOLDING': + self._stop_sending_gcode() + # Resume once current queue of GCode commands has flushed + self.comm.i2c_command(Cmd.FLUSH) + self.comm.queue_command(Cmd.RESUME) + self.ctrl.state.set('line', 0) + self.stopping = False # Update cycle if (self._get_cycle() != 'idle' and not self.planner.is_busy() and not self.comm.is_active() and state == 'READY'): self.ctrl.state.set('cycle', 'idle') - # Automatically unpause on seek hold + # Continue after seek hold if (state == 'HOLDING' and self.ctrl.state.get('pr', '') == 'Switch found' and self.planner.is_synchronizing()): @@ -163,6 +168,8 @@ class Mach(): def home(self, axis, position = None): + state = self.ctrl.state + if position is not None: self.mdi('G28.3 %c%f' % (axis, position)) @@ -173,17 +180,29 @@ class Mach(): else: axes = '%c' % axis for axis in axes: - if not self.ctrl.state.axis_can_home(axis): - log.warning('Cannot home ' + axis) + # If this is not a request to home a specific axis and the + # axis is disabled or in manual homing mode, don't show any + # warnings + if 1 < len(axes) and ( + not state.is_axis_enabled(axis) or + state.axis_homing_mode(axis) == 'manual'): continue + # Error when axes cannot be homed + reason = state.axis_home_fail_reason(axis) + if reason is not None: + log.error('Cannot home %s axis: %s' % ( + axis.upper(), reason)) + continue + + # Home axis log.info('Homing %s axis' % axis) self.planner.mdi(axis_homing_procedure % {'axis': axis}) self.comm.set_write(True) - def estop(self): self.comm.i2c_command(Cmd.ESTOP) - def clear(self): self.comm.i2c_command(Cmd.CLEAR) + def estop(self): self.comm.estop() + def clear(self): self.comm.clear() def select(self, path): @@ -211,22 +230,24 @@ class Mach(): self.stopping = True - def pause(self): self.comm.i2c_command(Cmd.PAUSE, byte = 0) + def pause(self): self.comm.pause() def unpause(self): if self.ctrl.state.get('xx', '') != 'HOLDING': return - self.comm.i2c_command(Cmd.FLUSH) - self.comm.queue_command(Cmd.RESUME) - self.planner.restart() - self.comm.set_write(True) + pause_reason = self.ctrl.state.get('pr', '') + if pause_reason in ['User paused', 'Switch found']: + self.comm.i2c_command(Cmd.FLUSH) + self.comm.queue_command(Cmd.RESUME) + self.planner.restart() + self.comm.set_write(True) + self.comm.i2c_command(Cmd.UNPAUSE) def optional_pause(self): - if self._get_cycle() == 'running': - self.comm.i2c_command(Cmd.PAUSE, byte = 1) + if self._get_cycle() == 'running': self.comm.pause(True) def set_position(self, axis, position): diff --git a/src/py/bbctrl/Messages.py b/src/py/bbctrl/Messages.py index 930b628..b6ae595 100644 --- a/src/py/bbctrl/Messages.py +++ b/src/py/bbctrl/Messages.py @@ -50,6 +50,11 @@ class Messages(logging.Handler): def remove_listener(self, listener): self.listeners.remove(listener) + def broadcast(self, msg): + for listener in self.listeners: + listener(msg) + + # From logging.Handler def emit(self, record): if record.levelno == logging.INFO: return @@ -61,5 +66,4 @@ class Messages(logging.Handler): if hasattr(record, 'where'): msg['where'] = record.where else: msg['where'] = '%s:%d' % (record.filename, record.lineno) - for listener in self.listeners: - listener(msg) + self.broadcast({'log': msg}) diff --git a/src/py/bbctrl/Planner.py b/src/py/bbctrl/Planner.py index b336b08..bb08e56 100644 --- a/src/py/bbctrl/Planner.py +++ b/src/py/bbctrl/Planner.py @@ -111,7 +111,10 @@ class Planner(): # 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 name == 'message': self.ctrl.msgs.broadcast({'message': value}) + else: self.ctrl.state.set(name, value) + if id == self.lastID + 1: self.lastID = id @@ -161,16 +164,19 @@ class Planner(): if type == 'set': name, value = block['name'], block['value'] - if name == 'line': self._queue_set_cmd(block['id'], name, value) - if name == 'tool': return Cmd.tool(value) + if name in ['message', 'line', 'tool']: + self._queue_set_cmd(block['id'], name, value) + if name == 'speed': return Cmd.speed(value) + if name == '_mist': self._queue_set_cmd(block['id'], 'load1state', value) + if name == '_flood': self._queue_set_cmd(block['id'], 'load2state', value) - if name[0:1] == '_' and name[1:2] in 'xyzabc' and \ - name[2:] == '_home': - return Cmd.set_axis(name[1], value) + + if (name[0:1] == '_' and name[1:2] in 'xyzabc' and + name[2:] == '_home'): return Cmd.set_axis(name[1], value) if len(name) and name[0] == '_': self._queue_set_cmd(block['id'], name[1:], value) @@ -181,7 +187,7 @@ class Planner(): return Cmd.output(block['port'], int(float(block['value']))) if type == 'dwell': return Cmd.dwell(block['seconds']) - if type == 'pause': return Cmd.pause(block['optional']) + if type == 'pause': return Cmd.pause(block['pause-type']) if type == 'seek': return Cmd.seek(block['switch'], block['active'], block['error']) diff --git a/src/py/bbctrl/State.py b/src/py/bbctrl/State.py index 5229767..ef79496 100644 --- a/src/py/bbctrl/State.py +++ b/src/py/bbctrl/State.py @@ -159,8 +159,7 @@ class State(object): return motor - def is_axis_homed(self, axis): - return self.get('%s_homed' % axis, False) + def is_axis_homed(self, axis): return self.get('%s_homed' % axis, False) def is_axis_enabled(self, axis): @@ -168,33 +167,50 @@ class State(object): return False if motor is None else self.motor_enabled(motor) - def axis_can_home(self, axis): + def axis_homing_mode(self, axis): motor = self.find_motor(axis) - if motor is None: return False - if not self.motor_enabled(motor): return False + if motor is None: return 'disabled' + return self.motor_homing_mode(motor) - homing_mode = self.motor_homing_mode(motor) - if homing_mode == 1: return bool(int(self.get(axis + '_ls'))) # min sw - if homing_mode == 2: return bool(int(self.get(axis + '_xs'))) # max sw - return False + + def axis_home_fail_reason(self, axis): + motor = self.find_motor(axis) + if motor is None: return 'Not mapped to motor' + if not self.motor_enabled(motor): return 'Motor disabled' + + mode = self.motor_homing_mode(motor) + + if mode == 'manual': return 'Configured for manual homing' + + if mode == 'switch-min' and not int(self.get(axis + '_ls')): + return 'Configured for min switch but switch is disabled' + + if mode == 'switch-max' and not int(self.get(axis + '_xs')): + return 'Configured for max switch but switch is disabled' def motor_enabled(self, motor): return bool(int(self.vars.get('%dpm' % motor, 0))) - def motor_homing_mode(self, motor): return int(self.vars['%dho' % motor]) + def motor_homing_mode(self, motor): + mode = str(self.vars.get('%dho' % motor, 0)) + if mode == '0': return 'manual' + if mode == '1': return 'switch-min' + if mode == '2': return 'switch-max' + raise Exception('Unrecognized homing mode "%s"' % mode) def motor_home_direction(self, motor): - homing_mode = self.motor_homing_mode(motor) - if homing_mode == 1: return -1 # Switch min - if homing_mode == 2: return 1 # Switch max + mode = self.motor_homing_mode(motor) + if mode == 'switch-min': return -1 + if mode == 'switch-max': return 1 return 0 # Disabled def motor_home_position(self, motor): - homing_mode = self.motor_homing_mode(motor) - if homing_mode == 1: return self.vars['%dtn' % motor] # Min soft limit - if homing_mode == 2: return self.vars['%dtm' % motor] # Max soft limit + mode = self.motor_homing_mode(motor) + # Return soft limit positions + if mode == 'switch-min': return self.vars['%dtn' % motor] + if mode == 'switch-max': return self.vars['%dtm' % motor] return 0 # Disabled diff --git a/src/py/bbctrl/Web.py b/src/py/bbctrl/Web.py index 4f0c1a8..29796a3 100644 --- a/src/py/bbctrl/Web.py +++ b/src/py/bbctrl/Web.py @@ -274,21 +274,20 @@ class ClientConnection(object): self.count += 1 - def notify(self, msg): self.send(dict(msg = msg)) def send(self, msg): raise HTTPError(400, 'Not implemented') def on_open(self, *args, **kwargs): self.timer = self.ctrl.ioloop.call_later(3, self.heartbeat) self.ctrl.state.add_listener(self.send) - self.ctrl.msgs.add_listener(self.notify) + self.ctrl.msgs.add_listener(self.send) self.is_open = True def on_close(self): self.ctrl.ioloop.remove_timeout(self.timer) self.ctrl.state.remove_listener(self.send) - self.ctrl.msgs.remove_listener(self.notify) + self.ctrl.msgs.remove_listener(self.send) def on_message(self, data): self.ctrl.mach.mdi(data) -- 2.27.0