From a04e6d36e1fc784bf61c525322f9fe610d82228b Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Fri, 23 Feb 2018 03:00:18 -0800 Subject: [PATCH] - Fixed "Flood" display, changed to "Load 1" and "Load 2". #108 - Highlight loads when on. - Fixed axis zeroing. - Fixed bug in home position set after successful home. #109 - Fixed ugly Web error dumps. - Allow access to log file from Web. - Rotate log so it does not grow too big. - Keep same GCode file through browser reload. #20 --- CHANGELOG.md | 7 ++++ src/avr/src/command.c | 4 +- src/avr/src/command.def | 1 + src/avr/src/exec.c | 58 +++++++++++++++++++++++++--- src/avr/src/status.c | 7 +--- src/avr/src/status.h | 1 - src/avr/src/stepper.c | 6 --- src/avr/src/stepper.h | 1 - src/avr/src/vars.c | 4 -- src/avr/src/vars.def | 2 +- src/jade/templates/admin-view.jade | 4 ++ src/jade/templates/console.jade | 2 +- src/jade/templates/control-view.jade | 10 ++--- src/js/control-view.js | 42 ++++++++------------ src/py/bbctrl/APIHandler.py | 13 +++++++ src/py/bbctrl/Cmd.py | 3 +- src/py/bbctrl/FileHandler.py | 7 ++++ src/py/bbctrl/Jog.py | 4 +- src/py/bbctrl/Mach.py | 48 ++++++++++++++++------- src/py/bbctrl/Planner.py | 10 +++-- src/py/bbctrl/Web.py | 34 +++++++++++++--- src/py/bbctrl/__init__.py | 8 +++- src/stylus/style.styl | 3 ++ 23 files changed, 195 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebaa8c9..bff502f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ Buildbotics CNC Controller Firmware Change Log ## v0.3.10 - Fixed "Flood" display, changed to "Load 1" and "Load 2". #108 + - Highlight loads when on. + - Fixed axis zeroing. + - Fixed bug in home position set after successful home. #109 + - Fixed ugly Web error dumps. + - Allow access to log file from Web. + - Rotate log so it does not grow too big. + - Keep same GCode file through browser reload. #20 ## v0.3.9 - Fixed bug in move exec that was causing bumping between moves. diff --git a/src/avr/src/command.c b/src/avr/src/command.c index bf37fa8..8688abd 100644 --- a/src/avr/src/command.c +++ b/src/avr/src/command.c @@ -196,7 +196,7 @@ bool command_callback() { // Special processing for synchronous commands if (_is_synchronous(*block)) { if (estop_triggered()) status = STAT_MACHINE_ALARMED; - else if (state_is_flushing()) status = STAT_NOP; // Flush + else if (state_is_flushing()) status = STAT_NOP; // Flush command else if (state_is_resuming() || _space() < _size(*block)) return false; // Wait } @@ -208,7 +208,7 @@ bool command_callback() { // Reporting report_request(); - if (status && status != STAT_NOP) status_error(status); + if (status && status != STAT_NOP) STATUS_ERROR(status, ""); return true; } diff --git a/src/avr/src/command.def b/src/avr/src/command.def index e9de9b9..b18dbfd 100644 --- a/src/avr/src/command.def +++ b/src/avr/src/command.def @@ -28,6 +28,7 @@ CMD('$', var, 0, "Set or get variable") CMD('#', sync_var, 1, "Set variable synchronous") 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") diff --git a/src/avr/src/exec.c b/src/avr/src/exec.c index ac9227b..5c30f95 100644 --- a/src/avr/src/exec.c +++ b/src/avr/src/exec.c @@ -26,17 +26,15 @@ \******************************************************************************/ #include "exec.h" -#include "jog.h" + #include "stepper.h" +#include "motor.h" #include "axis.h" -#include "spindle.h" #include "util.h" #include "command.h" +#include "report.h" #include "config.h" -#include -#include - static struct { exec_cb_t cb; @@ -112,10 +110,58 @@ 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;} void set_speed_override(float value) {ex.spindle_override = value;} -void set_axis_position(int axis, float p) {ex.position[axis] = p;} // Command callbacks +typedef struct { + uint8_t axis; + float position; +} set_axis_t; + + +stat_t command_set_axis(char *cmd) { + cmd++; // Skip command name + + // Decode axis + int axis = axis_get_id(*cmd++); + if (axis < 0) return STAT_INVALID_ARGUMENTS; + + // Decode position + float position; + if (!decode_float(&cmd, &position)) return STAT_BAD_FLOAT; + + // Check for end of command + if (*cmd) return STAT_INVALID_ARGUMENTS; + + // Update command + command_set_axis_position(axis, position); + + // Queue + set_axis_t set_axis = {axis, position}; + command_push(COMMAND_set_axis, &set_axis); + + return STAT_OK; +} + + +unsigned command_set_axis_size() {return sizeof(set_axis_t);} + + +void command_set_axis_exec(void *data) { + set_axis_t *cmd = (set_axis_t *)data; + + // Update exec + ex.position[cmd->axis] = cmd->position; + + // Update motors + int motor = axis_get_motor(cmd->axis); + if (0 <= motor) motor_set_position(motor, cmd->position); + + // 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/status.c b/src/avr/src/status.c index a492209..4241eb5 100644 --- a/src/avr/src/status.c +++ b/src/avr/src/status.c @@ -63,11 +63,6 @@ const char *status_level_pgmstr(status_level_t level) { } -stat_t status_error(stat_t code) { - return status_message_P(0, STAT_LEVEL_ERROR, code, 0); -} - - stat_t status_message_P(const char *location, status_level_t level, stat_t code, const char *msg, ...) { va_list args; @@ -77,7 +72,7 @@ stat_t status_message_P(const char *location, status_level_t level, status_level_pgmstr(level)); // Message - if (msg) { + if (msg && pgm_read_byte(msg)) { va_start(args, msg); vfprintf_P(stdout, msg, args); va_end(args); diff --git a/src/avr/src/status.h b/src/avr/src/status.h index 4523804..5458168 100644 --- a/src/avr/src/status.h +++ b/src/avr/src/status.h @@ -56,7 +56,6 @@ extern stat_t status_code; const char *status_to_pgmstr(stat_t code); const char *status_level_pgmstr(status_level_t level); -stat_t status_error(stat_t code); stat_t status_message_P(const char *location, status_level_t level, stat_t code, const char *msg, ...); diff --git a/src/avr/src/stepper.c b/src/avr/src/stepper.c index 4603a14..0039649 100644 --- a/src/avr/src/stepper.c +++ b/src/avr/src/stepper.c @@ -82,12 +82,6 @@ void stepper_init() { } -void st_set_position(const float position[]) { - for (int motor = 0; motor < MOTORS; motor++) - motor_set_position(motor, position[motor_get_axis(motor)]); -} - - void st_shutdown() { OUTCLR_PIN(MOTOR_ENABLE_PIN); st.dwell = 0; diff --git a/src/avr/src/stepper.h b/src/avr/src/stepper.h index 424e69f..4ede34b 100644 --- a/src/avr/src/stepper.h +++ b/src/avr/src/stepper.h @@ -32,7 +32,6 @@ void stepper_init(); -void st_set_position(const float position[]); void st_shutdown(); void st_enable(); bool st_is_busy(); diff --git a/src/avr/src/vars.c b/src/avr/src/vars.c index f50ae78..1c8426a 100644 --- a/src/avr/src/vars.c +++ b/src/avr/src/vars.c @@ -398,10 +398,6 @@ stat_t command_sync_var(char *cmd) { command_push(*cmd, &buffer); - // Special case for synchronizing axis position in command queue - if (info.set.set_f32_index == set_axis_position) - command_set_axis_position(buffer.index, buffer.value._f32); - return STAT_OK; } diff --git a/src/avr/src/vars.def b/src/avr/src/vars.def index b56c6a4..d3c1247 100644 --- a/src/avr/src/vars.def +++ b/src/avr/src/vars.def @@ -64,7 +64,7 @@ VAR(estop_switch, ew, u8, 0, 0, 1, "Estop switch state") VAR(probe_switch, pw, u8, 0, 0, 1, "Probe switch state") // Axis -VAR(axis_position, p, f32, AXES, 1, 1, "Axis position") +VAR(axis_position, p, f32, AXES, 0, 1, "Axis position") // Outputs VAR(output_active, oa, bool, OUTS, 1, 1, "Output pin active") diff --git a/src/jade/templates/admin-view.jade b/src/jade/templates/admin-view.jade index 236e7a4..ff0a9ac 100644 --- a/src/jade/templates/admin-view.jade +++ b/src/jade/templates/admin-view.jade @@ -53,6 +53,10 @@ script#admin-view-template(type="text/x-template") input(name="pass2", v-model="password2", type="password") button.pure-button.pure-button-primary(@click="set_password") Set + h2 Debugging + a(href="/api/log", target="_blank") + button.pure-button.pure-button-primary View Log + h2 Configuration button.pure-button.pure-button-primary(@click="backup") Backup diff --git a/src/jade/templates/console.jade b/src/jade/templates/console.jade index 1e5747c..bc8df6c 100644 --- a/src/jade/templates/console.jade +++ b/src/jade/templates/console.jade @@ -46,4 +46,4 @@ script#console-template(type="text/x-template") td {{msg.source || ''}} td {{msg.where || ''}} td {{msg.repeat}} - td {{msg.msg}} + td.message {{msg.msg}} diff --git a/src/jade/templates/control-view.jade b/src/jade/templates/control-view.jade index bc0011a..ac9ff60 100644 --- a/src/jade/templates/control-view.jade +++ b/src/jade/templates/control-view.jade @@ -164,7 +164,7 @@ script#control-view-template(type="text/x-template") button.pure-button( title="{{state.xx == 'RUNNING' ? 'Pause' : 'Start'}} program.", - @click="start_pause", :disabled="!file") + @click="start_pause", :disabled="!state.selected") .fa(:class="state.xx == 'RUNNING' ? 'fa-pause' : 'fa-play'") button.pure-button(title="Stop program.", @click="stop", @@ -176,8 +176,8 @@ 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.xx != 'READY' && state.xx != 'HOLDING') || !file", - v-if="false") + :disabled="(state.xx != 'READY' && state.xx != 'HOLDING') || " + + "!state.selected", v-if="false") .fa.fa-step-forward .tabs @@ -209,7 +209,7 @@ script#control-view-template(type="text/x-template") style="display:none", accept=".nc,.gcode,.gc,.ngc") button.pure-button(title="Delete current GCode program.", - @click="deleteGCode = true", :disabled="!file") + @click="deleteGCode = true", :disabled="!state.selected") .fa.fa-trash message(:show.sync="deleteGCode") @@ -225,7 +225,7 @@ script#control-view-template(type="text/x-template") |  selected select(title="Select previously uploaded GCode programs.", - v-model="file", @change="load", + v-model="state.selected", @change="load", :disabled="state.xx == 'RUNNING' || state.xx == 'STOPPING'") option(v-for="file in files", :value="file") {{file}} diff --git a/src/js/control-view.js b/src/js/control-view.js index 5fadc45..ff7f878 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -52,7 +52,6 @@ module.exports = { data: function () { return { mdi: '', - file: '', last_file: '', files: [], axes: 'xyzabc', @@ -78,7 +77,8 @@ module.exports = { watch: { - 'state.line': function () {this.update_gcode_line();} + 'state.line': function () {this.update_gcode_line()}, + 'state.selected': function () {this.load()} }, @@ -87,11 +87,16 @@ module.exports = { var data = {}; data[axis] = power; api.put('jog', data); - } + }, + + connected: function () {this.update()} }, - ready: function () {this.update()}, + ready: function () { + this.update(); + this.load(); + }, methods: { @@ -236,15 +241,7 @@ module.exports = { update: function () { // Update file list - api.get('file') - .done(function (files) { - var index = files.indexOf(this.file); - if (index == -1 && files.length) this.file = files[0]; - - this.files = files; - - this.load() - }.bind(this)) + api.get('file').done(function (files) {this.files = files}.bind(this)) }, @@ -275,7 +272,7 @@ module.exports = { api.upload('file', fd) .done(function () { - this.file = file.name; + file.name; if (file.name == this.last_file) this.last_file = ''; this.update(); }.bind(this)); @@ -283,15 +280,7 @@ module.exports = { load: function () { - var file = this.file; - - if (!file || this.files.indexOf(file) == -1) { - this.file = ''; - this.all_gcode = []; - this.gcode = []; - return; - } - + var file = this.state.selected; if (file == this.last_file) return; api.get('file/' + file) @@ -307,7 +296,8 @@ module.exports = { deleteCurrent: function () { - if (this.file) api.delete('file/' + this.file).done(this.update); + if (this.state.selected) + api.delete('file/' + this.state.selected).done(this.update); this.deleteGCode = false; }, @@ -363,12 +353,12 @@ module.exports = { }, - start: function () {api.put('start/' + this.file).done(this.update)}, + start: function () {api.put('start')}, pause: function () {api.put('pause')}, unpause: function () {api.put('unpause')}, optional_pause: function () {api.put('pause/optional')}, stop: function () {api.put('stop')}, - step: function () {api.put('step/' + this.file).done(this.update)}, + step: function () {api.put('step')}, override_feed: function () {api.put('override/feed/' + this.feed_override)}, diff --git a/src/py/bbctrl/APIHandler.py b/src/py/bbctrl/APIHandler.py index 4f2f0b5..de25a0c 100644 --- a/src/py/bbctrl/APIHandler.py +++ b/src/py/bbctrl/APIHandler.py @@ -26,16 +26,29 @@ ################################################################################ import json +import traceback +import logging + from tornado.web import RequestHandler, HTTPError import tornado.httpclient +log = logging.getLogger('API') + + class APIHandler(RequestHandler): def __init__(self, app, request, **kwargs): super(APIHandler, self).__init__(app, request, **kwargs) self.ctrl = app.ctrl + # Override exception logging + def log_exception(self, typ, value, tb): + log.error(str(value)) + trace = ''.join(traceback.format_exception(typ, value, tb)) + log.debug(trace) + + def delete(self, *args, **kwargs): self.delete_ok(*args, **kwargs) self.write_json('ok') diff --git a/src/py/bbctrl/Cmd.py b/src/py/bbctrl/Cmd.py index 8f978bd..79e3f31 100644 --- a/src/py/bbctrl/Cmd.py +++ b/src/py/bbctrl/Cmd.py @@ -38,6 +38,7 @@ log = logging.getLogger('Cmd') SET = '$' SET_SYNC = '#' SEEK = 's' +SET_AXIS = 'a' LINE = 'l' DWELL = 'd' OUTPUT = 'o' @@ -80,6 +81,7 @@ def encode_axes(axes): def set(name, value): return '#%s=%s' % (name, value) +def set_axis(axis, position): return SET_AXIS + axis + encode_float(position) def line(target, exitVel, maxAccel, maxJerk, times): @@ -100,7 +102,6 @@ def line(target, exitVel, maxAccel, maxJerk, times): def tool(tool): return '#t=%d' % tool def speed(speed): return '#s=:' + encode_float(speed) -def set_position(axis, value): return '#%sp=:%s' % (axis, encode_float(value)) def output(port, value): diff --git a/src/py/bbctrl/FileHandler.py b/src/py/bbctrl/FileHandler.py index 9c96a6d..a854153 100644 --- a/src/py/bbctrl/FileHandler.py +++ b/src/py/bbctrl/FileHandler.py @@ -57,6 +57,9 @@ class FileHandler(bbctrl.APIHandler): 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()) return @@ -68,4 +71,8 @@ class FileHandler(bbctrl.APIHandler): if os.path.isfile('upload/' + path): files.append(path) + selected = self.ctrl.state.get('selected', '') + if not selected in files: + if len(files): self.ctrl.state.set('selected', files[0]) + self.write_json(files) diff --git a/src/py/bbctrl/Jog.py b/src/py/bbctrl/Jog.py index c16efa7..e8950a0 100644 --- a/src/py/bbctrl/Jog.py +++ b/src/py/bbctrl/Jog.py @@ -80,7 +80,9 @@ class Jog(inevent.JogHandler): axes = {} for i in range(len(self.v)): axes["xyzabc"[i]] = self.v[i] self.ctrl.mach.jog(axes) - except Exception as e: log.warning('Jog: %s', e) + + except Exception as e: + log.warning('Jog: %s', e) self.ctrl.ioloop.call_later(0.25, self.callback) diff --git a/src/py/bbctrl/Mach.py b/src/py/bbctrl/Mach.py index 418b375..722a477 100644 --- a/src/py/bbctrl/Mach.py +++ b/src/py/bbctrl/Mach.py @@ -77,7 +77,6 @@ class Mach(): self.planner.update_position() self.ctrl.state.set('cycle', cycle) - elif current == 'homing' and cycle == 'mdi': pass elif current != cycle: raise Exception('Cannot enter %s cycle during %s' % (cycle, current)) @@ -96,6 +95,7 @@ class Mach(): # 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 @@ -107,7 +107,7 @@ class Mach(): if (state == 'HOLDING' and self.ctrl.state.get('pr', '') == 'Switch found' and self.planner.is_synchronizing()): - self.ctrl.mach.unpause() + self.unpause() def _comm_next(self): @@ -118,7 +118,7 @@ class Mach(): def _start_sending_gcode(self, path): - self.planner.load(path) + self.planner.load('upload/' + path) self.comm.set_write(True) @@ -163,37 +163,47 @@ class Mach(): def home(self, axis, position = None): - self._begin_cycle('homing') - if position is not None: self.mdi('G28.3 %c%f' % (axis, position)) else: + self._begin_cycle('homing') + 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): - log.info('Cannot home ' + axis) + log.warning('Cannot home ' + axis) continue log.info('Homing %s axis' % axis) - self.mdi(axis_homing_procedure % {'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 start(self, path): + def select(self, path): + if self.ctrl.state.get('selected', '') == path: return + + if self._get_cycle() != 'idle': + raise Exception('Cannot select file during ' + self._get_cycle()) + + self.ctrl.state.set('selected', path) + + + def start(self): self._begin_cycle('running') - self._start_sending_gcode(path) + self._start_sending_gcode(self.ctrl.state.get('selected')) - def step(self, path): + def step(self): raise Exception('NYI') # TODO self.comm.i2c_command(Cmd.STEP) - if self._get_cycle() != 'running': self.start(path) + if self._get_cycle() != 'running': self.start() def stop(self): @@ -220,6 +230,16 @@ class Mach(): def set_position(self, axis, position): - if self.ctrl.state.is_axis_homed('%c' % axis): - self.mdi('G92 %c%f' % (axis, position)) - else: self.comm.queue_command('$%cp=%f' % (axis, position)) + axis = axis.lower() + + if self.ctrl.state.is_axis_homed(axis): + self.mdi('G92 %s%f' % (axis, position)) + + else: + if self._get_cycle() != 'idle': + raise Exception('Cannot zero position during ' + + self._get_cycle()) + + self._begin_cycle('mdi') + self.planner.set_position({axis: position}) + self.comm.queue_command(Cmd.set_axis(axis, position)) diff --git a/src/py/bbctrl/Planner.py b/src/py/bbctrl/Planner.py index 44bdbca..b336b08 100644 --- a/src/py/bbctrl/Planner.py +++ b/src/py/bbctrl/Planner.py @@ -54,6 +54,10 @@ class Planner(): def is_synchronizing(self): return self.planner.is_synchronizing() + def set_position(self, position): + self.planner.set_position(position) + + def update_position(self): position = {} @@ -62,7 +66,7 @@ class Planner(): value = self.ctrl.state.get(axis + 'p', None) if value is not None: position[axis] = value - self.planner.set_position(position) + self.set_position(position) def _get_config_vector(self, name, scale): @@ -166,7 +170,7 @@ class Planner(): 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_position(name[1], value) + return Cmd.set_axis(name[1], value) if len(name) and name[0] == '_': self._queue_set_cmd(block['id'], name[1:], value) @@ -216,7 +220,7 @@ class Planner(): def load(self, path): log.info('GCode:' + path) - self.planner.load('upload' + path, self._get_config()) + self.planner.load(path, self._get_config()) def has_move(self): return self.planner.has_more() diff --git a/src/py/bbctrl/Web.py b/src/py/bbctrl/Web.py index b9be542..4f0c1a8 100644 --- a/src/py/bbctrl/Web.py +++ b/src/py/bbctrl/Web.py @@ -82,6 +82,24 @@ class RebootHandler(bbctrl.APIHandler): def put_ok(self): subprocess.Popen('reboot') +class LogHandler(tornado.web.RequestHandler): + def __init__(self, app, request, **kwargs): + super(LogHandler, self).__init__(app, request, **kwargs) + self.filename = app.ctrl.args.log + + + def get(self): + with open(self.filename, 'r') as f: + self.write(f.read()) + + + def set_default_headers(self): + fmt = socket.gethostname() + '-%Y%m%d.log' + filename = datetime.date.today().strftime(fmt) + self.set_header('Content-Disposition', 'filename="%s"' % filename) + self.set_header('Content-Type', 'text/plain') + + class HostnameHandler(bbctrl.APIHandler): def get(self): self.write_json(socket.gethostname()) @@ -191,7 +209,7 @@ class HomeHandler(bbctrl.APIHandler): class StartHandler(bbctrl.APIHandler): - def put_ok(self, path): self.ctrl.mach.start(path) + def put_ok(self): self.ctrl.mach.start() class EStopHandler(bbctrl.APIHandler): @@ -219,12 +237,12 @@ class OptionalPauseHandler(bbctrl.APIHandler): class StepHandler(bbctrl.APIHandler): - def put_ok(self, path): self.ctrl.mach.step(path) + def put_ok(self): self.ctrl.mach.step() class PositionHandler(bbctrl.APIHandler): def put_ok(self, axis): - self.ctrl.mach.set_position(ord(axis.lower()), self.json['position']) + self.ctrl.mach.set_position(axis, float(self.json['position'])) class OverrideFeedHandler(bbctrl.APIHandler): @@ -310,6 +328,7 @@ class Web(tornado.web.Application): handlers = [ (r'/websocket', WSConnection), + (r'/api/log', LogHandler), (r'/api/reboot', RebootHandler), (r'/api/hostname', HostnameHandler), (r'/api/remote/username', UsernameHandler), @@ -322,14 +341,14 @@ class Web(tornado.web.Application): (r'/api/upgrade', UpgradeHandler), (r'/api/file(/.+)?', bbctrl.FileHandler), (r'/api/home(/[xyzabcXYZABC](/set)?)?', HomeHandler), - (r'/api/start(/.+)', StartHandler), + (r'/api/start', StartHandler), (r'/api/estop', EStopHandler), (r'/api/clear', ClearHandler), (r'/api/stop', StopHandler), (r'/api/pause', PauseHandler), (r'/api/unpause', UnpauseHandler), (r'/api/pause/optional', OptionalPauseHandler), - (r'/api/step(/.+)', StepHandler), + (r'/api/step', StepHandler), (r'/api/position/([xyzabcXYZABC])', PositionHandler), (r'/api/override/feed/([\d.]+)', OverrideFeedHandler), (r'/api/override/speed/([\d.]+)', OverrideSpeedHandler), @@ -354,3 +373,8 @@ class Web(tornado.web.Application): sys.exit(1) log.info('Listening on http://%s:%d/', ctrl.args.addr, ctrl.args.port) + + + # Override default logger + def log_request(self, handler): + log.info("%d %s", handler.get_status(), handler._request_summary()) diff --git a/src/py/bbctrl/__init__.py b/src/py/bbctrl/__init__.py index 65607a0..bd0fd5c 100644 --- a/src/py/bbctrl/__init__.py +++ b/src/py/bbctrl/__init__.py @@ -33,6 +33,7 @@ import signal import tornado import argparse import logging +import datetime from pkg_resources import Requirement, resource_filename @@ -107,11 +108,16 @@ def run(): root.addHandler(h) if args.log: - h = logging.FileHandler(args.log) + h = logging.handlers.RotatingFileHandler(args.log, maxBytes = 1000000, + backupCount = 5) h.setLevel(level) h.setFormatter(f) root.addHandler(h) + # Log header + now = datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S') + root.info('Log started ' + now) + # Set signal handler signal.signal(signal.SIGTERM, on_exit) diff --git a/src/stylus/style.styl b/src/stylus/style.styl index 8d93980..ff31f1d 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -377,6 +377,9 @@ body max-height 400px overflow-y auto + .message + white-space pre + table width 100% margin 0.5em 0 -- 2.27.0