From e0bbc3fa0bbc06260cc8d02043f38688bccdd884 Mon Sep 17 00:00:00 2001 From: David Carley Date: Thu, 13 Jan 2022 17:01:06 -0800 Subject: [PATCH 01/95] Fixed long/slow movement bug --- src/avr/src/line.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/avr/src/line.c b/src/avr/src/line.c index 132c14f..a94d186 100644 --- a/src/avr/src/line.c +++ b/src/avr/src/line.c @@ -54,7 +54,7 @@ static struct { line_t line; int section; - int seg; + uint32_t seg; float iD; // Initial section distance float iV; // Initial section velocity From 87290cd992372954ed705d3a411acf3bdcf60252 Mon Sep 17 00:00:00 2001 From: David Carley Date: Mon, 4 Jul 2022 20:07:26 -0700 Subject: [PATCH 02/95] Rebuilt the "Home Machine" dialog with Svelte. --- src/js/admin-network-view.js | 2 +- src/js/app.js | 3 + src/js/axis-vars.js | 187 +++++++----------- src/js/control-view.js | 115 +++++------ src/pug/index.pug | 2 + src/pug/templates/control-view.pug | 16 -- .../src/components/AdminNetworkView.svelte | 1 - .../src/components/DialogHost.svelte | 6 + .../src/dialogs/HomeMachineDialog.svelte | 27 +++ .../src/dialogs/WifiConnectionDialog.svelte | 7 +- src/svelte-components/src/lib/DialogProps.ts | 16 ++ src/svelte-components/src/main.ts | 24 ++- .../src/theme/_smui-theme.scss | 19 +- 13 files changed, 227 insertions(+), 198 deletions(-) create mode 100644 src/svelte-components/src/components/DialogHost.svelte create mode 100644 src/svelte-components/src/dialogs/HomeMachineDialog.svelte create mode 100644 src/svelte-components/src/lib/DialogProps.ts diff --git a/src/js/admin-network-view.js b/src/js/admin-network-view.js index 004ef54..c37e6ff 100644 --- a/src/js/admin-network-view.js +++ b/src/js/admin-network-view.js @@ -2,7 +2,7 @@ module.exports = { template: "#admin-network-view-template", attached: function () { - this.svelteComponent = SvelteComponents.create( + this.svelteComponent = SvelteComponents.createComponent( "AdminNetworkView", document.getElementById("svelte-root") ); diff --git a/src/js/app.js b/src/js/app.js index bec006a..3871766 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -6,6 +6,9 @@ const Sock = require("./sock"); const omit = require("lodash.omit"); SvelteComponents.initNetworkInfo(); +SvelteComponents.createComponent("DialogHost", + document.getElementById("svelte-dialog-host") +); function is_newer_version(current, latest) { const pattern = /(\d+)\.(\d+)\.(\d+)(.*)/; diff --git a/src/js/axis-vars.js b/src/js/axis-vars.js index 99eff79..18c54e1 100644 --- a/src/js/axis-vars.js +++ b/src/js/axis-vars.js @@ -1,107 +1,69 @@ -/******************************************************************************\ - - This file is part of the Buildbotics firmware. - - Copyright (c) 2015 - 2018, Buildbotics LLC - All rights reserved. - - This file ("the software") is free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License, - version 2 as published by the Free Software Foundation. You should - have received a copy of the GNU General Public License, version 2 - along with the software. If not, see . - - The software is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the software. If not, see - . - - For information regarding this software email: - "Joseph Coffland" - -\******************************************************************************/ - 'use strict' - -function is_defined(x) {return typeof x != 'undefined'} - - module.exports = { props: ['state', 'config'], - computed: { - x: function () {return this._compute_axis('x')}, - y: function () {return this._compute_axis('y')}, - z: function () {return this._compute_axis('z')}, - a: function () {return this._compute_axis('a')}, - b: function () {return this._compute_axis('b')}, - c: function () {return this._compute_axis('c')}, - axes: function () {return this._compute_axes()} + x: function () { return this._compute_axis('x') }, + y: function () { return this._compute_axis('y') }, + z: function () { return this._compute_axis('z') }, + a: function () { return this._compute_axis('a') }, + b: function () { return this._compute_axis('b') }, + c: function () { return this._compute_axis('c') }, + axes: function () { return this._compute_axes() } }, - methods: { _convert_length: function (value) { return this.state.imperial ? value / 25.4 : value; }, - _length_str: function (value) { return this._convert_length(value).toLocaleString() + (this.state.imperial ? ' in' : ' mm'); }, - _compute_axis: function (axis) { - var abs = this.state[axis + 'p'] || 0; - var off = this.state['offset_' + axis]; - var motor_id = this._get_motor_id(axis); - var motor = motor_id == -1 ? {} : this.config.motors[motor_id]; - var enabled = typeof motor.enabled != 'undefined' && motor.enabled; + var abs = this.state[axis + 'p'] || 0; + var off = this.state['offset_' + axis]; + var motor_id = this._get_motor_id(axis); + var motor = motor_id == -1 ? {} : this.config.motors[motor_id]; + var enabled = typeof motor.enabled != 'undefined' && motor.enabled; var homingMode = motor['homing-mode'] - var homed = this.state[motor_id + 'homed']; - var min = this.state[motor_id + 'tn']; - var max = this.state[motor_id + 'tm']; - var dim = max - min; - var pathMin = this.state['path_min_' + axis]; - var pathMax = this.state['path_max_' + axis]; - var pathDim = pathMax - pathMin; - var under = pathMin + off < min; - var over = max < pathMax + off; - var klass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis; - var state = 'UNHOMED'; - var icon = 'question-circle'; - var fault = this.state[motor_id + 'df'] & 0x1f; - var shutdown = this.state.power_shutdown; + var homed = this.state[motor_id + 'homed']; + var min = this.state[motor_id + 'tn']; + var max = this.state[motor_id + 'tm']; + var dim = max - min; + var pathMin = this.state['path_min_' + axis]; + var pathMax = this.state['path_max_' + axis]; + var pathDim = pathMax - pathMin; + var under = pathMin + off < min; + var over = max < pathMax + off; + var klass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis; + var state = 'UNHOMED'; + var icon = 'question-circle'; + var fault = this.state[motor_id + 'df'] & 0x1f; + var shutdown = this.state.power_shutdown; var title; - var ticon = 'question-circle'; - var tstate = 'NO FILE'; + var ticon = 'question-circle'; + var tstate = 'NO FILE'; var toolmsg; - var tklass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis; + var tklass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis; if (fault || shutdown) { state = shutdown ? 'SHUTDOWN' : 'FAULT'; klass += ' error'; icon = 'exclamation-circle'; - - } else if(homed) { + } else if (homed) { state = 'HOMED'; icon = 'check-circle'; } - + if (0 < dim && dim < pathDim) { tstate = 'NO FIT'; tklass += ' error'; ticon = 'ban'; - } else { - if (over || under) { tstate = over ? 'OVER' : 'UNDER'; tklass += ' warn'; @@ -113,46 +75,44 @@ module.exports = { } switch (state) { - case 'UNHOMED': title = 'Click the home button to home axis.'; break; - case 'HOMED': title = 'Axis successfuly homed.'; break; - case 'FAULT': - title = 'Motor driver fault. A potentially damaging electrical ' + - 'condition was detected and the motor driver was shutdown. ' + - 'Please power down the controller and check your motor cabling. ' + - 'See the "Motor Faults" table on the "Indicators" tab for more ' + - 'information.'; - break; - case 'SHUTDOWN': - title = 'Motor power fault. All motors in shutdown. ' + - 'See the "Power Faults" table on the "Indicators" tab for more ' + - 'information. Reboot controller to reset.'; - } - - switch(tstate) { - - case 'OVER': - toolmsg = 'Caution: The current tool path file would move ' + - this._length_str(pathMax + off - max) + ' above axis limit with the current offset.'; - break; + case 'UNHOMED': title = 'Click the home button to home axis.'; break; + case 'HOMED': title = 'Axis successfuly homed.'; break; + case 'FAULT': + title = 'Motor driver fault. A potentially damaging electrical ' + + 'condition was detected and the motor driver was shutdown. ' + + 'Please power down the controller and check your motor cabling. ' + + 'See the "Motor Faults" table on the "Indicators" tab for more ' + + 'information.'; + break; - case 'UNDER': - toolmsg = 'Caution: The current tool path file would move ' + - this._length_str(min - pathMin - off) + ' below limit with the current offset.'; - break; - - case 'NO FIT': - toolmsg = 'Warning: The current tool path dimensions (' + - this._length_str(pathDim) + ') exceed axis dimensions (' + - this._length_str(dim) + ') by ' + - this._length_str(pathDim - dim) + '.'; - break; - - default: - toolmsg = 'Tool path ' + axis + ' dimensions OK.'; - break; - + case 'SHUTDOWN': + title = 'Motor power fault. All motors in shutdown. ' + + 'See the "Power Faults" table on the "Indicators" tab for more ' + + 'information. Reboot controller to reset.'; + } + + switch (tstate) { + case 'OVER': + toolmsg = 'Caution: The current tool path file would move ' + + this._length_str(pathMax + off - max) + ' above axis limit with the current offset.'; + break; + + case 'UNDER': + toolmsg = 'Caution: The current tool path file would move ' + + this._length_str(min - pathMin - off) + ' below limit with the current offset.'; + break; + + case 'NO FIT': + toolmsg = 'Warning: The current tool path dimensions (' + + this._length_str(pathDim) + ') exceed axis dimensions (' + + this._length_str(dim) + ') by ' + + this._length_str(pathDim - dim) + '.'; + break; + + default: + toolmsg = 'Tool path ' + axis + ' dimensions OK.'; + break; } - return { pos: abs - off, @@ -179,7 +139,6 @@ module.exports = { } }, - _get_motor_id: function (axis) { for (var i = 0; i < this.config.motors.length; i++) { var motor = this.config.motors[i]; @@ -189,7 +148,6 @@ module.exports = { return -1; }, - _compute_axes: function () { var homed = false; @@ -197,7 +155,7 @@ module.exports = { var axis = this[name]; if (!axis.enabled) continue - if (!axis.homed) {homed = false; break} + if (!axis.homed) { homed = false; break } homed = true; } @@ -217,10 +175,11 @@ module.exports = { if (error) klass += ' error'; else if (warn) klass += ' warn'; - if(!homed && this.ask_home) - { - this.ask_home_msg = true; - this.ask_home = false; + if (!homed && this.ask_home) { + this.ask_home = false; + SvelteComponents.showDialog("HomeMachine", { + home: () => this.home() + }); } return { diff --git a/src/js/control-view.js b/src/js/control-view.js index 802f3d5..53d7aee 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -27,7 +27,7 @@ 'use strict' -var api = require('./api'); +var api = require('./api'); var cookie = require('./cookie')('bbctrl-'); module.exports = { @@ -81,7 +81,6 @@ module.exports = { c: false }, ask_home: true, - ask_home_msg: false, ask_zero_xy_msg: false, ask_zero_z_msg: false, showGcodeMessage: false @@ -102,10 +101,10 @@ module.exports = { }, immediate: true }, - + 'state.bitDiameter': { handler: function (bitDiameter) { - this.tool_diameter = bitDiameter; + this.tool_diameter = bitDiameter; }, immediate: true }, @@ -115,7 +114,7 @@ module.exports = { if ((units == 'METRIC') != this.metric) this.send(units == 'METRIC' ? 'G21' : 'G20'); - this.units_changed(); + this.units_changed(); }, 'state.line': function () { @@ -129,7 +128,7 @@ module.exports = { jog_step: function () { cookie.set_bool('jog-step', this.jog_step); - }, + }, jog_adjust: function () { cookie.set('jog-adjust', this.jog_adjust); @@ -148,13 +147,13 @@ module.exports = { var state = this.state.xx; if (typeof cycle != 'undefined' && state != 'ESTOPPED' && - (cycle == 'jogging' || cycle == 'homing')) + (cycle == 'jogging' || cycle == 'homing')) return cycle.toUpperCase(); return state || '' }, - pause_reason: function () {return this.state.pr}, + pause_reason: function () { return this.state.pr }, is_running: function () { @@ -162,20 +161,20 @@ module.exports = { }, - is_stopping: function () {return this.mach_state == 'STOPPING'}, - is_holding: function () {return this.mach_state == 'HOLDING'}, - is_ready: function () {return this.mach_state == 'READY'}, - is_idle: function () {return this.state.cycle == 'idle'}, + is_stopping: function () { return this.mach_state == 'STOPPING' }, + is_holding: function () { return this.mach_state == 'HOLDING' }, + is_ready: function () { return this.mach_state == 'READY' }, + is_idle: function () { return this.state.cycle == 'idle' }, is_paused: function () { return this.is_holding && (this.pause_reason == 'User pause' || - this.pause_reason == 'Program pause') + this.pause_reason == 'Program pause') }, - can_mdi: function () {return this.is_idle || this.state.cycle == 'mdi'}, + can_mdi: function () { return this.is_idle || this.state.cycle == 'mdi' }, can_set_axis: function () { @@ -199,7 +198,7 @@ module.exports = { }, - plan_time: function () {return this.state.plan_time}, + plan_time: function () { return this.state.plan_time }, plan_time_remaining: function () { @@ -227,20 +226,20 @@ module.exports = { events: { jog: function (axis, power) { - var data = {ts: new Date().getTime()}; + var data = { ts: new Date().getTime() }; data[axis] = power; api.put('jog', data); }, - back2zero: function(axis0,axis1) { - this.send("G0"+axis0+"0"+axis1+"0"); + back2zero: function (axis0, axis1) { + this.send("G0" + axis0 + "0" + axis1 + "0"); }, step: function (axis, value) { this.send('M70\nG91\nG0' + axis + value + '\nM72'); }, - probing_failed: function() { + probing_failed: function () { Vue.set(this.state, "probing_active", false); Vue.set(this.state, "wait_for_probing_complete", false); Vue.set(this.state, "show_probe_complete_modal", false); @@ -249,7 +248,7 @@ module.exports = { Vue.set(this.state, "show_probe_failed_modal", true); }, - probing_complete: function() { + probing_complete: function () { Vue.set(this.state, "probing_active", false); if (this.config.settings['probing-prompts']) { @@ -259,7 +258,7 @@ module.exports = { } }, - finalize_probe: function() { + finalize_probe: function () { Vue.set(this.state, "show_probe_complete_modal", false); if (this.state.goto_xy_zero_after_probe) { @@ -277,8 +276,8 @@ module.exports = { methods: { - units_changed : function() { - if(this.mach_units == 'METRIC') { + units_changed: function () { + if (this.mach_units == 'METRIC') { document.getElementById("jog_button_fine").innerHTML = "0.1"; document.getElementById("jog_button_small").innerHTML = "1.0"; document.getElementById("jog_button_medium").innerHTML = "10"; @@ -293,7 +292,7 @@ module.exports = { this.set_jog_incr('small'); }, - start_probe_test: function(on_finish) { + start_probe_test: function (on_finish) { if (!this.config.settings['probing-prompts']) { on_finish(); return; @@ -304,7 +303,7 @@ module.exports = { Vue.set(this.state, "on_probe_finish", on_finish); }, - finish_probe_test: function() { + finish_probe_test: function () { this.show_probe_test_modal = false; Vue.set(this.state, "saw_probe_connected", false); @@ -314,14 +313,14 @@ module.exports = { on_finish(); }, - hide_probe_failed_modal: function() { + hide_probe_failed_modal: function () { Vue.set(this.state, "show_probe_failed_modal", false); }, prep_and_show_tool_diameter_modal() { this.tool_diameter_for_prompt = (this.mach_units == 'METRIC') - ? this.tool_diameter - : this.tool_diameter / 25.4; + ? this.tool_diameter + : this.tool_diameter / 25.4; this.tool_diameter_for_prompt = this.tool_diameter_for_prompt.toFixed(3).replace(/0+$/, ""); @@ -340,7 +339,7 @@ module.exports = { if (this.mach_units !== "METRIC") { this.tool_diameter *= 25.4; } - + this.probe_xyz(); }, @@ -429,7 +428,7 @@ module.exports = { this.probe(true); }, - set_jog_incr: function(newValue) { + set_jog_incr: function (newValue) { document.getElementById("jog_button_fine").style.fontWeight = 'normal'; document.getElementById("jog_button_small").style.fontWeight = 'normal'; document.getElementById("jog_button_medium").style.fontWeight = 'normal'; @@ -437,19 +436,19 @@ module.exports = { if (newValue == 'fine') { document.getElementById("jog_button_fine").style.fontWeight = 'bold'; - if(this.mach_units == 'METRIC') + if (this.mach_units == 'METRIC') this.jog_incr = 0.1; else this.jog_incr = 0.005; } else if (newValue == 'small') { document.getElementById("jog_button_small").style.fontWeight = 'bold'; - if(this.mach_units == 'METRIC') + if (this.mach_units == 'METRIC') this.jog_incr = 1.0; else this.jog_incr = 0.05; } else if (newValue == 'medium') { document.getElementById("jog_button_medium").style.fontWeight = 'bold'; - if(this.mach_units == 'METRIC') + if (this.mach_units == 'METRIC') this.jog_incr = 10; else this.jog_incr = 0.5; @@ -462,15 +461,15 @@ module.exports = { } }, - goto_zero(zero_x,zero_y,zero_z,zero_a) { + goto_zero(zero_x, zero_y, zero_z, zero_a) { var xcmd = ""; var ycmd = ""; var zcmd = ""; var acmd = ""; - if(zero_x) xcmd = "X0"; - if(zero_y) ycmd = "Y0"; - if(zero_z) zcmd = "Z0"; - if(zero_a) acmd = "A0"; + if (zero_x) xcmd = "X0"; + if (zero_y) ycmd = "Y0"; + if (zero_z) zcmd = "Z0"; + if (zero_a) acmd = "A0"; this.ask_zero_xy_msg = false; this.ask_zero_z_msg = false; @@ -478,7 +477,7 @@ module.exports = { this.send('G90\nG0' + xcmd + ycmd + zcmd + acmd + '\n'); }, - jog_fn: function (x_jog,y_jog,z_jog,a_jog) { + jog_fn: function (x_jog, y_jog, z_jog, a_jog) { var xcmd = "X" + x_jog * this.jog_incr; var ycmd = "Y" + y_jog * this.jog_incr; var zcmd = "Z" + z_jog * this.jog_incr; @@ -619,22 +618,24 @@ module.exports = { home: function (axis) { - this.ask_home = false; - this.ask_home_msg = false; - - if (typeof axis == 'undefined') api.put('home'); - else { - if (this[axis].homingMode != 'manual') api.put('home/' + axis); - else this.manual_home[axis] = true; + if (typeof axis == 'undefined') { + api.put('home'); + } else { + if (this[axis].homingMode != 'manual') { + api.put('home/' + axis); + } + else { + this.manual_home[axis] = true; + } } }, set_home: function (axis, position) { this.manual_home[axis] = false; - api.put('home/' + axis + '/set', {position: parseFloat(position)}); + api.put('home/' + axis + '/set', { position: parseFloat(position) }); }, @@ -648,15 +649,15 @@ module.exports = { this.axis_position = 0; this.position_msg[axis] = true; }, - - show_toolpath_msg : function(axis) { + + show_toolpath_msg: function (axis) { this.toolpath_msg[axis] = true; }, set_position: function (axis, position) { this.position_msg[axis] = false; - api.put('position/' + axis, {'position': parseFloat(position)}); + api.put('position/' + axis, { 'position': parseFloat(position) }); }, @@ -682,15 +683,15 @@ module.exports = { }, - 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')}, + 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') }, - override_feed: function () {api.put('override/feed/' + this.feed_override)}, + override_feed: function () { api.put('override/feed/' + this.feed_override) }, override_speed: function () { diff --git a/src/pug/index.pug b/src/pug/index.pug index 1eedc13..575e904 100644 --- a/src/pug/index.pug +++ b/src/pug/index.pug @@ -20,6 +20,8 @@ html(lang="en") style: include:stylus ../stylus/style.styl body(v-cloak) + #svelte-dialog-host + #overlay(v-if="status != 'connected'") span {{status}} diff --git a/src/pug/templates/control-view.pug b/src/pug/templates/control-view.pug index 7c08bf5..83c8dc8 100644 --- a/src/pug/templates/control-view.pug +++ b/src/pug/templates/control-view.pug @@ -36,20 +36,7 @@ script#control-view-template(type="text/x-template") div(slot="footer") label Simulating {{(toolpath_progress || 0) | percent}} - - message(:show.sync=`ask_home_msg`) - h3(slot="header") Home Machine - div(slot="body") - p Home the machine? - - div(slot="footer") - button.pure-button(@click="home()") - | OK - - button.pure-button(@click='ask_home_msg = false; ask_home = false') - | Cancel - message(:show.sync=`ask_zero_xy_msg`) h3(slot="header") XY Origin @@ -497,9 +484,6 @@ script#control-view-template(type="text/x-template") section#content4.tab-content indicators(:state="state", :template="template") - - - .override(title="Feed rate override.") label Feed input(type="range", min="0", max="2", step="0.01", diff --git a/src/svelte-components/src/components/AdminNetworkView.svelte b/src/svelte-components/src/components/AdminNetworkView.svelte index 285e2cb..0b7dad3 100644 --- a/src/svelte-components/src/components/AdminNetworkView.svelte +++ b/src/svelte-components/src/components/AdminNetworkView.svelte @@ -1,7 +1,6 @@ + + diff --git a/src/svelte-components/src/dialogs/HomeMachineDialog.svelte b/src/svelte-components/src/dialogs/HomeMachineDialog.svelte new file mode 100644 index 0000000..3dca1a6 --- /dev/null +++ b/src/svelte-components/src/dialogs/HomeMachineDialog.svelte @@ -0,0 +1,27 @@ + + + + Home Machine + + Home the machine? + + + + + + + diff --git a/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte b/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte index cc14505..d6f333c 100644 --- a/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte +++ b/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte @@ -1,5 +1,5 @@ - - diff --git a/src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte b/src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte index 18627f4..de8930b 100644 --- a/src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte +++ b/src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte @@ -2,8 +2,8 @@ import Dialog, { Title, Content, Actions } from "@smui/dialog"; import Button, { Label } from "@smui/button"; import TextField from "@smui/textfield"; - import MessageDialog from "./MessageDialog.svelte"; - import * as api from "../lib/api"; + import MessageDialog from "$dialogs/MessageDialog.svelte"; + import * as api from "$lib/api"; // https://man7.org/linux/man-pages/man7/hostname.7.html // diff --git a/src/svelte-components/src/dialogs/DialogHost.svelte b/src/svelte-components/src/dialogs/DialogHost.svelte new file mode 100644 index 0000000..c6319dd --- /dev/null +++ b/src/svelte-components/src/dialogs/DialogHost.svelte @@ -0,0 +1,38 @@ + + + + diff --git a/src/svelte-components/src/dialogs/DialogProps.ts b/src/svelte-components/src/dialogs/DialogProps.ts new file mode 100644 index 0000000..338c722 --- /dev/null +++ b/src/svelte-components/src/dialogs/DialogProps.ts @@ -0,0 +1,13 @@ +import { writable } from "svelte/store"; + +export const HomeMachineProps = writable(); +export type HomeMachinePropsType = { + open: boolean, + home: () => void +} + +export const ProbeProps = writable(); +export type ProbePropsType = { + open: boolean, + probeType: "xyz" | "z" +}; diff --git a/src/svelte-components/src/dialogs/ProbeDialog.svelte b/src/svelte-components/src/dialogs/ProbeDialog.svelte new file mode 100644 index 0000000..36775fa --- /dev/null +++ b/src/svelte-components/src/dialogs/ProbeDialog.svelte @@ -0,0 +1,293 @@ + + + + + + Probe {probeType} + + + {#if stage === "TestingProbe" || stage === "TestingProbeComplete"} +

Attach the probe magnet to the collet.

+

Touch the probe block to the bit.

+ + {#if stage === "TestingProbe"} +

Waiting for probe contact...

+ {:else} +

Probe contact detected!

+ {/if} + {:else if stage === "GetToolDiameter"} + + + {:else if stage === "Probing"} +

Probing in progress...

+ {:else if stage === "ProbingFailed"} +

Could not find the probe block during probing!

+

+ Make sure the tip of the bit is about 1/4" (6mm) above the probe block, + and try again. +

+ {:else if stage === "ProbingComplete"} +

Don't forget to put away the probe!

+

The machine will now move to the XY origin.

+

Watch your hands!

+ {/if} +
+ + + + + +
diff --git a/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte b/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte index d6f333c..b213330 100644 --- a/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte +++ b/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte @@ -4,28 +4,22 @@ import TextField from "@smui/textfield"; import Icon from "@smui/textfield/icon"; import HelperText from "@smui/textfield/helper-text"; - import MessageDialog from "./MessageDialog.svelte"; - import type { WifiNetwork } from "../lib/NetworkInfo"; - import * as api from "../lib/api"; + import MessageDialog from "$dialogs/MessageDialog.svelte"; + import type { WifiNetwork } from "$lib/NetworkInfo"; + import * as api from "$lib/api"; export let open = false; export let network: WifiNetwork; let rebooting = false; - let needPassword = false; let password = ""; let showPassword = false; - let connectOrDisconnect: string; - let connectToOrDisconnectFrom: string; $: needPassword = !network?.active && network?.Encryption !== "Open"; - - $: { - connectOrDisconnect = network?.active ? "Disconnect" : "Connect"; - connectToOrDisconnectFrom = network?.active + $: connectOrDisconnect = network?.active ? "Disconnect" : "Connect"; + $: connectToOrDisconnectFrom = network?.active ? "Disconnect from" : "Connect to"; - } $: if (open) { password = ""; diff --git a/src/svelte-components/src/lib/ConfigStore.ts b/src/svelte-components/src/lib/ConfigStore.ts new file mode 100644 index 0000000..55a2f49 --- /dev/null +++ b/src/svelte-components/src/lib/ConfigStore.ts @@ -0,0 +1,7 @@ +import { writable } from "svelte/store"; + +export const Config = writable>({}); + +export function handleConfigUpdate(config: Record) { + Config.set(config); +} diff --git a/src/svelte-components/src/lib/DialogProps.ts b/src/svelte-components/src/lib/DialogProps.ts deleted file mode 100644 index 290f4df..0000000 --- a/src/svelte-components/src/lib/DialogProps.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { writable } from "svelte/store"; - -type HomeMachine = { - open: boolean, - home: () => any -} - -export type DialogPropsTypes = { - HomeMachine: HomeMachine -} - -export const HomeMachine = writable(); - -export default { - HomeMachine -}; diff --git a/src/svelte-components/src/lib/NetworkInfo.ts b/src/svelte-components/src/lib/NetworkInfo.ts index 1d84f3b..bad3a07 100644 --- a/src/svelte-components/src/lib/NetworkInfo.ts +++ b/src/svelte-components/src/lib/NetworkInfo.ts @@ -1,5 +1,5 @@ import { readable } from "svelte/store"; -import * as api from "./api"; +import * as api from "$lib/api"; export type WifiNetwork = { Quality: string; diff --git a/src/svelte-components/src/lib/RegisterControllerMethods.ts b/src/svelte-components/src/lib/RegisterControllerMethods.ts new file mode 100644 index 0000000..03917cd --- /dev/null +++ b/src/svelte-components/src/lib/RegisterControllerMethods.ts @@ -0,0 +1,10 @@ +type ControllerMethods = { + send: (gcode: string) => void; + goto_zero: (x: number, y: number, z: number, a: number) => void; +} + +export let ControllerMethods: ControllerMethods; + +export function registerControllerMethods(methods: ControllerMethods) { + ControllerMethods = methods; +} diff --git a/src/svelte-components/src/lib/StoreHelpers.ts b/src/svelte-components/src/lib/StoreHelpers.ts new file mode 100644 index 0000000..78604a5 --- /dev/null +++ b/src/svelte-components/src/lib/StoreHelpers.ts @@ -0,0 +1,18 @@ +import { get, type Writable } from "svelte/store"; + +export function listenForChange(writable: Writable, cb: (value: T) => void) { + const priorValue = get(writable); + + const unsubscribe = writable.subscribe((value) => { + if (value !== priorValue) { + unsubscribe(); + cb(value); + } + }); +} + +export function waitForChange(writable: Writable): Promise { + return new Promise((resolve) => { + listenForChange(writable, (value) => resolve(value)); + }); +} diff --git a/src/svelte-components/src/main.ts b/src/svelte-components/src/main.ts index 9d07eb0..c51d33d 100644 --- a/src/svelte-components/src/main.ts +++ b/src/svelte-components/src/main.ts @@ -1,10 +1,11 @@ import 'polyfill-object.fromentries'; -import AdminNetworkView from './components/AdminNetworkView.svelte'; -import { init as initNetworkInfo } from './lib/NetworkInfo'; -import DialogHost from "./components/DialogHost.svelte"; -import DialogProps from "./lib/DialogProps"; -import type { DialogPropsTypes } from "./lib/DialogProps"; +import AdminNetworkView from '$components/AdminNetworkView.svelte'; +import DialogHost, { showDialog } from "$dialogs/DialogHost.svelte"; +import { handleConfigUpdate } from '$lib/ConfigStore'; +import { init as initNetworkInfo } from '$lib/NetworkInfo'; +import { handleControllerStateUpdate } from "$dialogs/ProbeDialog.svelte"; +import { registerControllerMethods } from "$lib/RegisterControllerMethods"; export function createComponent(component: string, target: HTMLElement, props: Record) { switch (component) { @@ -19,17 +20,10 @@ export function createComponent(component: string, target: HTMLElement, props: R } } -export function showDialog(dialog: T, props: DialogPropsTypes[T]) { - switch (dialog) { - case "HomeMachine": - DialogProps.HomeMachine.set({ ...props, open: true }); - break; - - default: - throw new Error(`Unknown dialog '${dialog}`); - } -} - export { - initNetworkInfo + initNetworkInfo, + showDialog, + handleControllerStateUpdate, + handleConfigUpdate, + registerControllerMethods, }; diff --git a/src/svelte-components/tsconfig.json b/src/svelte-components/tsconfig.json index 4878d84..5bcd848 100644 --- a/src/svelte-components/tsconfig.json +++ b/src/svelte-components/tsconfig.json @@ -6,6 +6,11 @@ "module": "esnext", "resolveJsonModule": true, "baseUrl": ".", + "paths": { + "$lib/*": ["src/lib/*"], + "$components/*": ["src/components/*"], + "$dialogs/*": ["src/dialogs/*"] + }, /** * Typecheck JS in `.svelte` and `.js` files by default. * Disable checkJs if you'd like to use dynamic types in JS. diff --git a/src/svelte-components/vite.config.ts b/src/svelte-components/vite.config.ts index 33ccea6..eeb6a0e 100644 --- a/src/svelte-components/vite.config.ts +++ b/src/svelte-components/vite.config.ts @@ -7,6 +7,13 @@ export default defineConfig({ plugins: [ svelte() ], + resolve: { + alias: { + $lib: resolve('./src/lib'), + $dialogs: resolve('./src/dialogs'), + $components: resolve('./src/components') + } + }, build: { target: "chrome60", lib: { From de665cac4d07f6c02790c3ebb23d9d58bd8ab387 Mon Sep 17 00:00:00 2001 From: David Carley Date: Sun, 10 Jul 2022 02:00:45 -0700 Subject: [PATCH 04/95] New probe dialog working --- src/js/app.js | 9 + src/js/control-view.js | 1 + src/pug/index.pug | 1 + src/py/bbctrl/Ctrl.py | 44 ++-- src/py/bbctrl/Mach.py | 133 +++++------ src/py/bbctrl/State.py | 203 +++++++--------- src/py/bbctrl/Web.py | 9 + .../src/components/Devmode.svelte | 23 ++ .../src/dialogs/ProbeDialog.svelte | 222 ++++++++++-------- .../src/lib/RegisterControllerMethods.ts | 1 + src/svelte-components/src/main.ts | 4 + 11 files changed, 320 insertions(+), 330 deletions(-) create mode 100644 src/svelte-components/src/components/Devmode.svelte diff --git a/src/js/app.js b/src/js/app.js index 50cacfc..8e2dd2d 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -289,6 +289,15 @@ module.exports = new Vue({ update_object(this.config, config, true); this.parse_hash(); + if (!this.devModChecked) { + this.devModChecked = true; + if (this.config.devmode) { + SvelteComponents.createComponent("Devmode", + document.getElementById("svelte-devmode-host") + ); + } + } + if (!this.checkedUpgrade) { this.checkedUpgrade = true; diff --git a/src/js/control-view.js b/src/js/control-view.js index 1c54a26..f2b41ac 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -254,6 +254,7 @@ module.exports = { this.load(); SvelteComponents.registerControllerMethods({ + stop: (...args) => this.stop(...args), send: (...args) => this.send(...args), goto_zero: (...args) => this.goto_zero(...args) }); diff --git a/src/pug/index.pug b/src/pug/index.pug index 575e904..643b9aa 100644 --- a/src/pug/index.pug +++ b/src/pug/index.pug @@ -21,6 +21,7 @@ html(lang="en") body(v-cloak) #svelte-dialog-host + #svelte-devmode-host #overlay(v-if="status != 'connected'") span {{status}} diff --git a/src/py/bbctrl/Ctrl.py b/src/py/bbctrl/Ctrl.py index a751aff..43fa61f 100644 --- a/src/py/bbctrl/Ctrl.py +++ b/src/py/bbctrl/Ctrl.py @@ -1,18 +1,22 @@ import os import bbctrl + class Ctrl(object): def __init__(self, args, ioloop, id): self.args = args self.ioloop = bbctrl.IOLoop(ioloop) self.id = id - self.timeout = None # Used in demo mode + self.timeout = None # Used in demo mode - if id and not os.path.exists(id): os.mkdir(id) + if id and not os.path.exists(id): + os.mkdir(id) # Start log - if args.demo: log_path = self.get_path(filename = 'bbctrl.log') - else: log_path = args.log + if args.demo: + log_path = self.get_path(filename='bbctrl.log') + else: + log_path = args.log self.log = bbctrl.log.Log(args, self.ioloop, log_path) self.state = bbctrl.State(self) @@ -21,14 +25,17 @@ class Ctrl(object): self.log.get('Ctrl').info('Starting %s' % self.id) try: - if args.demo: self.avr = bbctrl.AVREmu(self) - else: self.avr = bbctrl.AVR(self) + if args.demo: + self.avr = bbctrl.AVREmu(self) + else: + self.avr = bbctrl.AVR(self) self.i2c = bbctrl.I2C(args.i2c_port, args.demo) self.lcd = bbctrl.LCD(self) self.mach = bbctrl.Mach(self, self.avr) self.preplanner = bbctrl.Preplanner(self) - if not args.demo: self.jog = bbctrl.Jog(self) + if not args.demo: + self.jog = bbctrl.Jog(self) self.pwr = bbctrl.Pwr(self) self.mach.connect() @@ -38,48 +45,41 @@ class Ctrl(object): os.environ['GCODE_SCRIPT_PATH'] = self.get_upload() - except Exception: self.log.get('Ctrl').exception('Internal error: Control initialization failed') - + except Exception: + self.log.get('Ctrl').exception( + 'Internal error: Control initialization failed') def __del__(self): print('Ctrl deleted') - def clear_timeout(self): - if self.timeout is not None: self.ioloop.remove_timeout(self.timeout) + if self.timeout is not None: + self.ioloop.remove_timeout(self.timeout) self.timeout = None - def set_timeout(self, cb, *args, **kwargs): self.clear_timeout() t = self.args.client_timeout self.timeout = self.ioloop.call_later(t, cb, *args, **kwargs) - - def get_path(self, dir = None, filename = None): + def get_path(self, dir=None, filename=None): path = './' + self.id if self.id else '.' path = path if dir is None else (path + '/' + dir) return path if filename is None else (path + '/' + filename) - - def get_upload(self, filename = None): + def get_upload(self, filename=None): return self.get_path('upload', filename) - - def get_plan(self, filename = None): + def get_plan(self, filename=None): return self.get_path('plans', filename) - def configure(self): # Indirectly configures state via calls to config() and the AVR self.config.reload() - self.state.init() - def ready(self): # This is used to synchronize the start of the preplanner self.preplanner.start() - def close(self): self.log.get('Ctrl').info('Closing %s' % self.id) self.ioloop.close() diff --git a/src/py/bbctrl/Mach.py b/src/py/bbctrl/Mach.py index 677c160..6a4e464 100644 --- a/src/py/bbctrl/Mach.py +++ b/src/py/bbctrl/Mach.py @@ -1,30 +1,3 @@ -################################################################################ -# # -# This file is part of the Buildbotics firmware. # -# # -# Copyright (c) 2015 - 2018, Buildbotics LLC # -# All rights reserved. # -# # -# This file ("the software") is free software: you can redistribute it # -# and/or modify it under the terms of the GNU General Public License, # -# version 2 as published by the Free Software Foundation. You should # -# have received a copy of the GNU General Public License, version 2 # -# along with the software. If not, see . # -# # -# The software is distributed in the hope that it will be useful, but # -# WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # -# Lesser General Public License for more details. # -# # -# You should have received a copy of the GNU Lesser General Public # -# License along with the software. If not, see # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - import bbctrl from bbctrl.Comm import Comm import bbctrl.Cmd as Cmd @@ -74,6 +47,7 @@ and check your motor cabling. See the "Motor Faults" table on the "Indicators" \ for more information.\ ''' + def overrides(interface_class): def overrider(method): if not method.__name__ in dir(interface_class): @@ -102,7 +76,6 @@ class Mach(Comm): super().reboot() - def _get_state(self): return self.ctrl.state.get('xx', '') def _is_estopped(self): return self._get_state() == 'ESTOPPED' def _is_holding(self): return self._get_state() == 'HOLDING' @@ -110,19 +83,18 @@ class Mach(Comm): def _get_pause_reason(self): return self.ctrl.state.get('pr', '') def _get_cycle(self): return self.ctrl.state.get('cycle', 'idle') - def _is_paused(self): - if not self._is_holding() or self.unpausing: return False + if not self._is_holding() or self.unpausing: + return False return self._get_pause_reason() in ( 'User pause', 'Program pause', 'Optional pause') - def _set_cycle(self, cycle): self.ctrl.state.set('cycle', cycle) - def _begin_cycle(self, cycle): current = self._get_cycle() - if current == cycle: return # No change + if current == cycle: + return # No change if current != 'idle': raise Exception('Cannot enter %s cycle while in %s cycle' % @@ -132,14 +104,12 @@ class Mach(Comm): # if current == 'idle' or (cycle == 'jogging' and self._is_paused()): self._set_cycle(cycle) - def process_log(self, log): # When a probe has failed, we have to e-stop or things # end up in a bad state, where positions and offsets are incorrect if log['msg'] == 'Switch not found': self.estop() - def _update(self, update): # Detect motor faults for motor in range(4): @@ -153,12 +123,12 @@ class Mach(Comm): # Handle EStop if state_changed and state == 'ESTOPPED': - self.planner.reset(stop = False) + self.planner.reset(stop=False) # Exit cycle if state changed to READY if (state_changed and self._get_cycle() != 'idle' and self._is_ready() and not self.planner.is_busy() and - not super().is_active()): + not super().is_active()): self.planner.position_change() self._set_cycle('idle') @@ -187,7 +157,6 @@ class Mach(Comm): (pr == 'Optional pause' and not op))): self._unpause() - def _unpause(self): pause_reason = self._get_pause_reason() self.mlog.info('Unpause: ' + pause_reason) @@ -196,36 +165,31 @@ class Mach(Comm): self.planner.stop() self.ctrl.state.set('line', 0) - else: self.planner.restart() + else: + self.planner.restart() super().i2c_command(Cmd.UNPAUSE) self.unpausing = True - def _i2c_block(self, block): - super().i2c_command(block[0], block = block[1:]) - + super().i2c_command(block[0], block=block[1:]) def _i2c_set(self, name, value): self._i2c_block(Cmd.set(name, value)) - @overrides(Comm) def comm_next(self): if self.planner.is_running() and not self._is_holding(): return self.planner.next() - @overrides(Comm) def comm_error(self): self.planner.reset() - @overrides(Comm) def connect(self): self.planner.reset() super().connect() - def _query_var(self, cmd): equal = cmd.find('=') if equal == -1: @@ -234,21 +198,26 @@ class Mach(Comm): else: name, value = cmd[1:equal], cmd[equal + 1:] - if value.lower() == 'true': value = True - elif value.lower() == 'false': value = False + if value.lower() == 'true': + value = True + elif value.lower() == 'false': + value = False else: try: value = float(value) - except: pass + except: + pass self.ctrl.state.config(name, value) - - def mdi(self, cmd, with_limits = True): + def mdi(self, cmd, with_limits=True): try: - if not len(cmd): return - if cmd[0] == '$': self._query_var(cmd) - elif cmd[0] == '\\': super().queue_command(cmd[1:]) + if not len(cmd): + return + if cmd[0] == '$': + self._query_var(cmd) + elif cmd[0] == '\\': + super().queue_command(cmd[1:]) else: self._begin_cycle('mdi') self.planner.mdi(cmd, with_limits) @@ -260,18 +229,18 @@ class Mach(Comm): def set(self, code, value): super().queue_command('${}={}'.format(code, value)) - def jog(self, axes): self._begin_cycle('jogging') self.planner.position_change() super().queue_command(Cmd.jog(axes)) - - def home(self, axis, position = None): + def home(self, axis, position=None): state = self.ctrl.state - if axis is None: axes = 'zxyabc' # TODO This should be configurable - else: axes = '%c' % axis + if axis is None: + axes = 'zxyabc' # TODO This should be configurable + else: + axes = '%c' % axis for axis in axes: enabled = state.is_axis_enabled(axis) @@ -291,7 +260,8 @@ class Mach(Comm): continue if mode == 'manual': - if position is None: raise Exception('Position not set') + if position is None: + raise Exception('Position not set') self.mdi('G28.3 %c%f' % (axis, position)) continue @@ -299,56 +269,63 @@ class Mach(Comm): self.mlog.info('Homing %s axis' % axis) self._begin_cycle('homing') - if mode.startswith('stall-'): procedure = stall_homing_procedure - else: procedure = axis_homing_procedure + if mode.startswith('stall-'): + procedure = stall_homing_procedure + else: + procedure = axis_homing_procedure gcode = procedure % {'axis': axis} self.planner.mdi(gcode, False) super().resume() - def unhome(self, axis): self.mdi('G28.2 %c0' % axis) def estop(self): super().estop() - def clear(self): if self._is_estopped(): self.planner.reset() super().clear() + def fake_probe_contact(self): + self._i2c_set('pt', 2) + self.ctrl.state.set('pw', 0) + self.timer = self.ctrl.ioloop.call_later(0.5, self.clear_fake_probe_contact) + + def clear_fake_probe_contact(self): + self._i2c_set('pt', 1) + self.ctrl.state.set('pw', 1) def start(self): filename = self.ctrl.state.get('selected', '') - if not filename: return + if not filename: + return self._begin_cycle('running') self.planner.load(filename) super().resume() - def step(self): - raise Exception('NYI') # TODO - if self._get_cycle() != 'running': self.start() - else: super().i2c_command(Cmd.UNPAUSE) - + raise Exception('NYI') # TODO + if self._get_cycle() != 'running': + self.start() + else: + super().i2c_command(Cmd.UNPAUSE) def stop(self): - if self._get_state() != 'jogging': self.stopping = True + if self._get_state() != 'jogging': + self.stopping = True super().i2c_command(Cmd.STOP) - - def pause(self): super().pause() + def pause(self): super().pause() def unpause(self): if self._is_paused(): self.ctrl.state.set('optional_pause', False) self._unpause() - - def optional_pause(self, enable = True): + def optional_pause(self, enable=True): self.ctrl.state.set('optional_pause', enable) - def set_position(self, axis, position): axis = axis.lower() state = self.ctrl.state @@ -367,17 +344,13 @@ class Mach(Comm): state.set(axis + 'p', target) super().queue_command(Cmd.set_axis(axis, target)) - def override_feed(self, override): self._i2c_set('fo', int(1000 * override)) - def override_speed(self, override): self._i2c_set('so', int(1000 * override)) - def modbus_read(self, addr): self._i2c_block(Cmd.modbus_read(addr)) - def modbus_write(self, addr, value): self._i2c_block(Cmd.modbus_write(addr, value)) diff --git a/src/py/bbctrl/State.py b/src/py/bbctrl/State.py index d7c1e67..61b163b 100644 --- a/src/py/bbctrl/State.py +++ b/src/py/bbctrl/State.py @@ -1,30 +1,3 @@ -################################################################################ -# # -# This file is part of the Buildbotics firmware. # -# # -# Copyright (c) 2015 - 2018, Buildbotics LLC # -# All rights reserved. # -# # -# This file ("the software") is free software: you can redistribute it # -# and/or modify it under the terms of the GNU General Public License, # -# version 2 as published by the Free Software Foundation. You should # -# have received a copy of the GNU General Public License, version 2 # -# along with the software. If not, see . # -# # -# The software is distributed in the hope that it will be useful, but # -# WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # -# Lesser General Public License for more details. # -# # -# You should have received a copy of the GNU Lesser General Public # -# License along with the software. If not, see # -# . # -# # -# For information regarding this software email: # -# "Joseph Coffland" # -# # -################################################################################ - import traceback import copy import uuid @@ -33,11 +6,12 @@ import bbctrl from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler + class UploadChangeHandler(FileSystemEventHandler): def __init__(self, state): self.state = state - def on_any_event(self, event): + def on_any_event(self, event): self.state.load_files() @@ -70,44 +44,36 @@ class State(object): # the planner will scale returned values when in imperial mode. for i in range(4): self.set_callback(str(i) + 'home_position', - lambda name, i = i: self.motor_home_position(i)) + lambda name, i=i: self.motor_home_position(i)) self.set_callback(str(i) + 'home_travel', - lambda name, i = i: self.motor_home_travel(i)) + lambda name, i=i: self.motor_home_travel(i)) self.set_callback(str(i) + 'latch_backoff', - lambda name, i = i: self.motor_latch_backoff(i)) + lambda name, i=i: self.motor_latch_backoff(i)) self.set_callback(str(i) + 'zero_backoff', - lambda name, i = i: self.motor_zero_backoff(i)) + lambda name, i=i: self.motor_zero_backoff(i)) self.set_callback(str(i) + 'search_velocity', - lambda name, i = i: self.motor_search_velocity(i)) + lambda name, i=i: self.motor_search_velocity(i)) self.set_callback(str(i) + 'latch_velocity', - lambda name, i = i: self.motor_latch_velocity(i)) + lambda name, i=i: self.motor_latch_velocity(i)) self.reset() self.load_files() observer = Observer() - observer.schedule(UploadChangeHandler(self), self.ctrl.get_upload(), recursive=True) + observer.schedule(UploadChangeHandler( + self), self.ctrl.get_upload(), recursive=True) observer.start() - - def init(self): - # Init machine units - metric = self.ctrl.config.get('units', 'METRIC').upper() == 'METRIC' - self.log.info('INIT Metric %d' % metric) - if not 'metric' in self.vars: self.set('metric', metric) - if not 'imperial' in self.vars: self.set('imperial', not metric) - - def reset(self): # Unhome all motors - for i in range(4): self.set('%dhomed' % i, False) + for i in range(4): + self.set('%dhomed' % i, False) # Zero offsets and positions for axis in 'xyzabc': self.set(axis + 'p', 0) self.set('offset_' + axis, 0) - def load_files(self): files = [] @@ -125,15 +91,15 @@ class State(object): files.sort() self.set('files', files) - if len(files): self.select_file(files[0]) - else: self.select_file('') - + if len(files): + self.select_file(files[0]) + else: + self.select_file('') def clear_files(self): self.select_file('') self.set('files', []) - def add_file(self, filename): files = copy.deepcopy(self.get('files')) if not filename in files: @@ -143,7 +109,6 @@ class State(object): self.select_file(filename) - def remove_file(self, filename): files = copy.deepcopy(self.get('files')) if filename in files: @@ -151,16 +116,16 @@ class State(object): self.set('files', files) if self.get('selected', filename) == filename: - if len(files): self.select_file(files[0]) - else: self.select_file('') - + if len(files): + self.select_file(files[0]) + else: + self.select_file('') def select_file(self, filename): self.set('selected', filename) time = os.path.getmtime(self.ctrl.get_upload(filename)) self.set('selected_time', time) - def set_bounds(self, bounds): for axis in 'xyzabc': for name in ('min', 'max'): @@ -168,24 +133,22 @@ class State(object): value = bounds[name][axis] if axis in bounds[name] else 0 self.set(var, value) - def ack_message(self, id): self.log.info('Message %d acknowledged' % id) msgs = self.vars['messages'] msgs = list(filter(lambda m: id < m['id'], msgs)) self.set('messages', msgs) - def add_message(self, text): - msg = dict(text = text, id = self.message_id) + msg = dict(text=text, id=self.message_id) self.message_id += 1 msgs = self.vars['messages'] - msgs = msgs + [msg] # It's important we make a new list here + msgs = msgs + [msg] # It's important we make a new list here self.set('messages', msgs) - def _notify(self): - if not self.changes: return + if not self.changes: + return for listener in self.listeners: try: @@ -193,25 +156,23 @@ class State(object): except Exception as e: self.log.warning('Updating state listener: %s', - traceback.format_exc()) + traceback.format_exc()) self.changes = {} self.timeout = None - def resolve(self, name): # Resolve axis prefixes to motor numbers if 2 < len(name) and name[1] == '_' and name[0] in 'xyzabc': motor = self.find_motor(name[0]) - if motor is not None: return str(motor) + name[2:] + if motor is not None: + return str(motor) + name[2:] return name - def has(self, name): return self.resolve(name) in self.vars def set_callback(self, name, cb): self.callbacks[self.resolve(name)] = cb - def set(self, name, value): name = self.resolve(name) @@ -223,23 +184,22 @@ class State(object): if self.timeout is None: self.timeout = self.ctrl.ioloop.call_later(0.25, self._notify) - def update(self, update): for name, value in update.items(): self.set(name, value) - - def get(self, name, default = None): + def get(self, name, default=None): name = self.resolve(name) - if name in self.vars: return self.vars[name] - if name in self.callbacks: return self.callbacks[name](name) + if name in self.vars: + return self.vars[name] + if name in self.callbacks: + return self.callbacks[name](name) if default is None: self.log.warning('State variable "%s" not found' % name) return default - def snapshot(self): vars = copy.deepcopy(self.vars) @@ -262,21 +222,19 @@ class State(object): return vars - def config(self, code, value): # Set machine variables via mach, others directly - if code in self.machine_var_set: self.ctrl.mach.set(code, value) - else: self.set(code, value) - + if code in self.machine_var_set: + self.ctrl.mach.set(code, value) + else: + self.set(code, value) def add_listener(self, listener): self.listeners.append(listener) listener(self.vars) - def remove_listener(self, listener): self.listeners.remove(listener) - def set_machine_vars(self, vars): # Record all machine vars, indexed or otherwise self.machine_var_set = set() @@ -284,8 +242,8 @@ class State(object): if 'index' in spec: for index in spec['index']: self.machine_var_set.add(index + code) - else: self.machine_var_set.add(code) - + else: + self.machine_var_set.add(code) def get_position(self): position = {} @@ -296,8 +254,7 @@ class State(object): return position - - def get_axis_vector(self, name, scale = 1): + def get_axis_vector(self, name, scale=1): v = {} for axis in 'xyzabc': @@ -305,11 +262,11 @@ class State(object): if motor is not None and self.motor_enabled(motor): value = self.get(str(motor) + name, None) - if value is not None: v[axis] = value * scale + if value is not None: + v[axis] = value * scale return v - def get_soft_limit_vector(self, var, default): limit = self.get_axis_vector(var, 1) @@ -319,23 +276,20 @@ class State(object): return limit - def find_motor(self, axis): for motor in range(4): - if not ('%dan' % motor) in self.vars: continue + if not ('%dan' % motor) in self.vars: + continue motor_axis = 'xyzabc'[self.vars['%dan' % motor]] if motor_axis == axis.lower() and self.vars.get('%dme' % motor, 0): return motor - def is_axis_homed(self, axis): return self.get('%s_homed' % axis, False) - def is_axis_enabled(self, axis): motor = self.find_motor(axis) return motor is not None and self.motor_enabled(motor) - def get_enabled_axes(self): axes = [] @@ -345,26 +299,25 @@ class State(object): return axes - def is_motor_faulted(self, motor): return self.get('%ddf' % motor, 0) & 0x1f - def is_axis_faulted(self, axis): motor = self.find_motor(axis) return motor is not None and self.is_motor_faulted(motor) - def axis_homing_mode(self, axis): motor = self.find_motor(axis) - if motor is None: return 'disabled' + if motor is None: + return 'disabled' return self.motor_homing_mode(motor) - 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' + if motor is None: + return 'Not mapped to motor' + if not self.motor_enabled(motor): + return 'Motor disabled' mode = self.motor_homing_mode(motor) @@ -381,35 +334,39 @@ class State(object): return 'max-soft-limit must be at least 1mm greater ' \ 'than min-soft-limit' - def motor_enabled(self, motor): return bool(int(self.vars.get('%dme' % motor, 0))) - 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' - if mode == '3': return 'stall-min' - if mode == '4': return 'stall-max' + if mode == '0': + return 'manual' + if mode == '1': + return 'switch-min' + if mode == '2': + return 'switch-max' + if mode == '3': + return 'stall-min' + if mode == '4': + return 'stall-max' raise Exception('Unrecognized homing mode "%s"' % mode) - def motor_home_direction(self, motor): mode = self.motor_homing_mode(motor) - if mode.endswith('-min'): return -1 - if mode.endswith('-max'): return 1 - return 0 # Disabled - + if mode.endswith('-min'): + return -1 + if mode.endswith('-max'): + return 1 + return 0 # Disabled def motor_home_position(self, motor): mode = self.motor_homing_mode(motor) # Return soft limit positions - if mode.endswith('-min'): return self.vars['%dtn' % motor] - if mode.endswith('-max'): return self.vars['%dtm' % motor] - return 0 # Disabled - + if mode.endswith('-min'): + return self.vars['%dtn' % motor] + if mode.endswith('-max'): + return self.vars['%dtm' % motor] + return 0 # Disabled def motor_home_travel(self, motor): tmin = self.get(str(motor) + 'tm', 0) @@ -419,27 +376,22 @@ class State(object): # (travel_max - travel_min) * 1.5 * home_dir return (tmin - tmax) * 1.5 * hdir - def motor_latch_backoff(self, motor): lb = self.get(str(motor) + 'lb', 0) hdir = self.motor_home_direction(motor) - return -(lb * hdir) # -latch_backoff * home_dir - + return -(lb * hdir) # -latch_backoff * home_dir def motor_zero_backoff(self, motor): zb = self.get(str(motor) + 'zb', 0) hdir = self.motor_home_direction(motor) - return -(zb * hdir) # -zero_backoff * home_dir - + return -(zb * hdir) # -zero_backoff * home_dir def motor_search_velocity(self, motor): return 1000 * self.get(str(motor) + 'sv', 0) - def motor_latch_velocity(self, motor): return 1000 * self.get(str(motor) + 'lv', 0) - def get_axis_switch(self, axis, side): axis = axis.lower() @@ -453,14 +405,17 @@ class State(object): # This must match the switch ID enum in avr/src/switch.h hmode = self.motor_homing_mode(motor) - if hmode.startswith('stall-'): return motor + 10 + if hmode.startswith('stall-'): + return motor + 10 return 2 * motor + 2 + (0 if side.lower() == 'min' else 1) - def get_switch_id(self, switch): # TODO Support other input switches in CAMotics gcode/machine/PortType.h switch = switch.lower() - if switch == 'probe': return 1 - if switch[1:] == '-min': return self.get_axis_switch(switch[0], 'min') - if switch[1:] == '-max': return self.get_axis_switch(switch[0], 'max') + if switch == 'probe': + return 1 + if switch[1:] == '-min': + return self.get_axis_switch(switch[0], 'min') + if switch[1:] == '-max': + return self.get_axis_switch(switch[0], 'max') raise Exception('Unsupported switch "%s"' % switch) diff --git a/src/py/bbctrl/Web.py b/src/py/bbctrl/Web.py index cbfbd7c..148fcdd 100644 --- a/src/py/bbctrl/Web.py +++ b/src/py/bbctrl/Web.py @@ -269,6 +269,14 @@ class HomeHandler(bbctrl.APIHandler): self.get_ctrl().mach.home(axis) +class DevmodeHandler(bbctrl.APIHandler): + def put_ok(self, command, *args): + if command == "/probe": + self.get_ctrl().mach.fake_probe_contact() + else: + raise HTTPError(400, 'Not implemented') + + class StartHandler(bbctrl.APIHandler): def put_ok(self): self.get_ctrl().mach.start() @@ -463,6 +471,7 @@ class Web(tornado.web.Application): (r'/api/file(/[^/]+)?', bbctrl.FileHandler), (r'/api/path/([^/]+)((/positions)|(/speeds))?', PathHandler), (r'/api/home(/[xyzabcXYZABC]((/set)|(/clear))?)?', HomeHandler), + (r'/api/devmode((/probe))?', DevmodeHandler), (r'/api/start', StartHandler), (r'/api/estop', EStopHandler), (r'/api/clear', ClearHandler), diff --git a/src/svelte-components/src/components/Devmode.svelte b/src/svelte-components/src/components/Devmode.svelte new file mode 100644 index 0000000..43adbd5 --- /dev/null +++ b/src/svelte-components/src/components/Devmode.svelte @@ -0,0 +1,23 @@ + + +
+ +
+ + diff --git a/src/svelte-components/src/dialogs/ProbeDialog.svelte b/src/svelte-components/src/dialogs/ProbeDialog.svelte index 36775fa..c73613c 100644 --- a/src/svelte-components/src/dialogs/ProbeDialog.svelte +++ b/src/svelte-components/src/dialogs/ProbeDialog.svelte @@ -5,12 +5,10 @@ import { Config } from "$lib/ConfigStore"; import { waitForChange } from "$lib/StoreHelpers"; import { ControllerMethods } from "$lib/RegisterControllerMethods"; - import { tick } from "svelte"; - type Stage = + type Step = | "None" | "TestingProbe" - | "TestingProbeComplete" | "GetToolDiameter" | "Probing" | "ProbingFailed" @@ -55,77 +53,48 @@ @@ -248,27 +260,27 @@ Probe {probeType} - {#if stage === "TestingProbe" || stage === "TestingProbeComplete"} + {#if step === "TestingProbe"}

Attach the probe magnet to the collet.

Touch the probe block to the bit.

- {#if stage === "TestingProbe"} -

Waiting for probe contact...

- {:else} + {#if $probeContacted}

Probe contact detected!

+ {:else} +

Waiting for probe contact...

{/if} - {:else if stage === "GetToolDiameter"} + {:else if step === "GetToolDiameter"} - {:else if stage === "Probing"} + {:else if step === "Probing"}

Probing in progress...

- {:else if stage === "ProbingFailed"} + {:else if step === "ProbingFailed"}

Could not find the probe block during probing!

Make sure the tip of the bit is about 1/4" (6mm) above the probe block, and try again.

- {:else if stage === "ProbingComplete"} + {:else if step === "ProbingComplete"}

Don't forget to put away the probe!

The machine will now move to the XY origin.

Watch your hands!

@@ -276,17 +288,19 @@
- + {#if showCancelButton} + + {/if} diff --git a/src/svelte-components/src/lib/RegisterControllerMethods.ts b/src/svelte-components/src/lib/RegisterControllerMethods.ts index 03917cd..758da85 100644 --- a/src/svelte-components/src/lib/RegisterControllerMethods.ts +++ b/src/svelte-components/src/lib/RegisterControllerMethods.ts @@ -1,4 +1,5 @@ type ControllerMethods = { + stop: () => void; send: (gcode: string) => void; goto_zero: (x: number, y: number, z: number, a: number) => void; } diff --git a/src/svelte-components/src/main.ts b/src/svelte-components/src/main.ts index c51d33d..4d55376 100644 --- a/src/svelte-components/src/main.ts +++ b/src/svelte-components/src/main.ts @@ -2,6 +2,7 @@ import 'polyfill-object.fromentries'; import AdminNetworkView from '$components/AdminNetworkView.svelte'; import DialogHost, { showDialog } from "$dialogs/DialogHost.svelte"; +import Devmode from "$components/Devmode.svelte"; import { handleConfigUpdate } from '$lib/ConfigStore'; import { init as initNetworkInfo } from '$lib/NetworkInfo'; import { handleControllerStateUpdate } from "$dialogs/ProbeDialog.svelte"; @@ -15,6 +16,9 @@ export function createComponent(component: string, target: HTMLElement, props: R case "DialogHost": return new DialogHost({ target, props }); + case "Devmode": + return new Devmode({target, props}); + default: throw new Error("Unknown component"); } From dd34b1e98811882606bf52b308c5fad96d27a873 Mon Sep 17 00:00:00 2001 From: David Carley Date: Mon, 11 Jul 2022 17:26:18 -0700 Subject: [PATCH 05/95] Added color to the "distance" controls. --- .gitignore | 1 + src/js/control-view.js | 7 ++++--- src/pug/templates/control-view.pug | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 81f1565..e344105 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ __pycache__ .vscode *.elf *.hex +null.d diff --git a/src/js/control-view.js b/src/js/control-view.js index f2b41ac..856a263 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -273,10 +273,11 @@ module.exports = { this.send('G90\nG0' + xcmd + ycmd + zcmd + acmd + '\n'); }, - getJogIncrFontWeight(value) { - const weight = this.jog_incr === value ? 'bold' : 'normal'; + getJogIncrStyle(value) { + const weight = `font-weight:${this.jog_incr === value ? 'bold' : 'normal'}`; + const color = this.jog_incr === value ? "color:#0078e7" : ""; - return `font-weight:${weight}`; + return [weight, color].join(';'); }, jog_fn: function (x_jog, y_jog, z_jog, a_jog) { diff --git a/src/pug/templates/control-view.pug b/src/pug/templates/control-view.pug index 4184025..b19d700 100644 --- a/src/pug/templates/control-view.pug +++ b/src/pug/templates/control-view.pug @@ -79,16 +79,16 @@ script#control-view-template(type="text/x-template") button(@click="jog_fn(0,0,-1,0)") Z- tr td(style="height:100px",align="center") - button(:style="getJogIncrFontWeight('fine')", @click="jog_incr = 'fine'") + button(:style="getJogIncrStyle('fine')", @click="jog_incr = 'fine'") span {{jog_incr_amounts[display_units].fine}}#[span.jog-units {{metric ? 'mm' : 'in'}}] td(style="height:100px",align="center") - button(:style="getJogIncrFontWeight('small')", @click="jog_incr = 'small'") + button(:style="getJogIncrStyle('small')", @click="jog_incr = 'small'") span {{jog_incr_amounts[display_units].small}}#[span.jog-units {{metric ? 'mm' : 'in'}}] td(style="height:100px",align="center") - button(:style="getJogIncrFontWeight('medium')", @click="jog_incr = 'medium'") + button(:style="getJogIncrStyle('medium')", @click="jog_incr = 'medium'") span {{jog_incr_amounts[display_units].medium}}#[span.jog-units {{metric ? 'mm' : 'in'}}] td(style="height:100px",align="center") - button(:style="getJogIncrFontWeight('large')", @click="jog_incr = 'large'") + button(:style="getJogIncrStyle('large')", @click="jog_incr = 'large'") span {{jog_incr_amounts[display_units].large}}#[span.jog-units {{metric ? 'mm' : 'in'}}] tr td(style="height:100px", align="center", colspan="2") From c710d124a094ff5938ff92536241796fddc31ac4 Mon Sep 17 00:00:00 2001 From: David Carley Date: Tue, 12 Jul 2022 18:08:20 -0700 Subject: [PATCH 06/95] New probing experience complete. --- src/pug/templates/settings-view.pug | 7 - src/resources/config-template.json | 5 - .../src/components/DimensionInput.svelte | 93 ++++++ .../src/dialogs/ProbeDialog.svelte | 296 ++++++++++++++---- .../src/theme/_smui-theme.scss | 11 + 5 files changed, 336 insertions(+), 76 deletions(-) create mode 100644 src/svelte-components/src/components/DimensionInput.svelte diff --git a/src/pug/templates/settings-view.pug b/src/pug/templates/settings-view.pug index 7e0b0fa..afeaa52 100644 --- a/src/pug/templates/settings-view.pug +++ b/src/pug/templates/settings-view.pug @@ -11,13 +11,6 @@ script#settings-view-template(type="text/x-template") option(value="METRIC") METRIC option(value="IMPERIAL") IMPERIAL - - fieldset - h2 Probing safety prompts - templated-input(name="probing-prompts", - :model.sync="config.settings['probing-prompts']", - :template="template.settings['probing-prompts']") - fieldset h2 Probe Dimensions templated-input(v-for="templ in template.probe", :name="$key", diff --git a/src/resources/config-template.json b/src/resources/config-template.json index a39ddfd..5495a73 100644 --- a/src/resources/config-template.json +++ b/src/resources/config-template.json @@ -22,11 +22,6 @@ "max": 100000000, "unit": "mm/min²", "default": 200000 - }, - "probing-prompts": { - "help": "Enable or disable safety prompts during and after probing", - "type": "bool", - "default": true } }, diff --git a/src/svelte-components/src/components/DimensionInput.svelte b/src/svelte-components/src/components/DimensionInput.svelte new file mode 100644 index 0000000..b7f09d6 --- /dev/null +++ b/src/svelte-components/src/components/DimensionInput.svelte @@ -0,0 +1,93 @@ + + +
+
+ menu.setOpen(true)} + /> + +
+ + + {#each options as option} + onOptionSelected(option)}> + + {option.value} + {option.metric ? "mm" : "in"} + + + {/each} + + +
+ + diff --git a/src/svelte-components/src/dialogs/ProbeDialog.svelte b/src/svelte-components/src/dialogs/ProbeDialog.svelte index c73613c..7a5b32f 100644 --- a/src/svelte-components/src/dialogs/ProbeDialog.svelte +++ b/src/svelte-components/src/dialogs/ProbeDialog.svelte @@ -1,18 +1,22 @@ - - + + + diff --git a/src/svelte-components/src/dialogs/DialogProps.ts b/src/svelte-components/src/dialogs/DialogProps.ts deleted file mode 100644 index 338c722..0000000 --- a/src/svelte-components/src/dialogs/DialogProps.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { writable } from "svelte/store"; - -export const HomeMachineProps = writable(); -export type HomeMachinePropsType = { - open: boolean, - home: () => void -} - -export const ProbeProps = writable(); -export type ProbePropsType = { - open: boolean, - probeType: "xyz" | "z" -}; diff --git a/src/svelte-components/src/dialogs/ScreenRotationDialog.svelte b/src/svelte-components/src/dialogs/ScreenRotationDialog.svelte new file mode 100644 index 0000000..0fd78a0 --- /dev/null +++ b/src/svelte-components/src/dialogs/ScreenRotationDialog.svelte @@ -0,0 +1,76 @@ + + + + Rebooting to apply the new screen rotation... + + + + Screen Rotation + + + {#each options as option} + + + + {option.label} + + + {/each} + + + + + + + + + diff --git a/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte b/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte index b213330..447783a 100644 --- a/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte +++ b/src/svelte-components/src/dialogs/WifiConnectionDialog.svelte @@ -92,7 +92,7 @@ on:click={onConfirm} disabled={needPassword && (password.length < 8 || password.length > 128)} > - + From f34e1413b764dc62d24d2054079b711ca21adbad Mon Sep 17 00:00:00 2001 From: David Carley Date: Sat, 16 Jul 2022 19:07:28 -0700 Subject: [PATCH 12/95] Brought back the metric and imperial state variables. --- src/py/bbctrl/Ctrl.py | 1 + src/py/bbctrl/State.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/py/bbctrl/Ctrl.py b/src/py/bbctrl/Ctrl.py index fae382c..f66f39d 100644 --- a/src/py/bbctrl/Ctrl.py +++ b/src/py/bbctrl/Ctrl.py @@ -71,6 +71,7 @@ class Ctrl(object): def configure(self): # Indirectly configures state via calls to config() and the AVR self.config.reload() + self.state.init() def ready(self): # This is used to synchronize the start of the preplanner diff --git a/src/py/bbctrl/State.py b/src/py/bbctrl/State.py index 61b163b..d60de7b 100644 --- a/src/py/bbctrl/State.py +++ b/src/py/bbctrl/State.py @@ -419,3 +419,12 @@ class State(object): if switch[1:] == '-max': return self.get_axis_switch(switch[0], 'max') raise Exception('Unsupported switch "%s"' % switch) + + def init(self): + # Init machine units + metric = self.ctrl.config.get('units', 'METRIC').upper() == 'METRIC' + self.log.info('INIT Metric %d' % metric) + if not 'metric' in self.vars: + self.set('metric', metric) + if not 'imperial' in self.vars: + self.set('imperial', not metric) From 8a904da3f69e1cf5226b81df433e42e86dfec90a Mon Sep 17 00:00:00 2001 From: David Carley Date: Sat, 16 Jul 2022 19:09:16 -0700 Subject: [PATCH 13/95] Fixed wifi display when quality = 0 --- src/svelte-components/src/components/AdminNetworkView.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/svelte-components/src/components/AdminNetworkView.svelte b/src/svelte-components/src/components/AdminNetworkView.svelte index 8efe39e..453c59e 100644 --- a/src/svelte-components/src/components/AdminNetworkView.svelte +++ b/src/svelte-components/src/components/AdminNetworkView.svelte @@ -19,6 +19,7 @@ const strength = Math.ceil((Number(network.Quality) / 100) * 4); switch (strength) { + case 0: case 1: return ""; From 9fa83d30c09db5a0acd3c7c32e877ed468400fb4 Mon Sep 17 00:00:00 2001 From: David Carley Date: Sat, 16 Jul 2022 19:12:56 -0700 Subject: [PATCH 14/95] When in devmode, clip to 1280x720 --- src/svelte-components/src/components/Devmode.svelte | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/svelte-components/src/components/Devmode.svelte b/src/svelte-components/src/components/Devmode.svelte index 43adbd5..36cdb51 100644 --- a/src/svelte-components/src/components/Devmode.svelte +++ b/src/svelte-components/src/components/Devmode.svelte @@ -1,5 +1,15 @@
From f6e17c656fe50e807e60fad52e126442f1f48de7 Mon Sep 17 00:00:00 2001 From: David Carley Date: Sat, 16 Jul 2022 19:18:58 -0700 Subject: [PATCH 15/95] Fixed dimension labels in the probe dialog --- .../src/components/DimensionInput.svelte | 28 +++++++++---------- .../src/dialogs/ProbeDialog.svelte | 28 +++++++++---------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/svelte-components/src/components/DimensionInput.svelte b/src/svelte-components/src/components/DimensionInput.svelte index e700e73..bec8141 100644 --- a/src/svelte-components/src/components/DimensionInput.svelte +++ b/src/svelte-components/src/components/DimensionInput.svelte @@ -11,6 +11,7 @@ type Option = { value: number; + label: string; metric: boolean; }; @@ -65,10 +66,7 @@ {#each options as option} onOptionSelected(option)}> - - {option.value} - {option.metric ? "mm" : "in"} - + {option.label} {/each} @@ -76,18 +74,18 @@
+ \ No newline at end of file diff --git a/src/svelte-components/src/dialogs/ProbeDialog.svelte b/src/svelte-components/src/dialogs/ProbeDialog.svelte index 618bcd2..74de934 100644 --- a/src/svelte-components/src/dialogs/ProbeDialog.svelte +++ b/src/svelte-components/src/dialogs/ProbeDialog.svelte @@ -63,21 +63,21 @@ import { Config } from "$lib/ConfigStore"; const cutterDiameterOptions = [ - { value: 0.5, metric: false }, - { value: 10, metric: true }, - { value: 0.25, metric: false }, - { value: 6, metric: true }, - { value: 0.125, metric: false }, - { value: 3, metric: true }, + { value: 0.5, label: '1/2 "', metric: false }, + { value: 0.25, label: '1/4 "', metric: false }, + { value: 0.125, label: '1/8 "', metric: false }, + { value: 10, label: "10 mm", metric: true }, + { value: 6, label: "6 mm", metric: true }, + { value: 3, label: "10 mm", metric: true }, ]; const cutterLengthOptions = [ - { value: 1, metric: false }, - { value: 20, metric: true }, - { value: 0.5, metric: false }, - { value: 10, metric: true }, - { value: 0.25, metric: false }, - { value: 6, metric: true }, + { value: 1, label: '1 "', metric: false }, + { value: 0.5, label: '1/2 "', metric: false }, + { value: 0.25, label: '1/4 "', metric: false }, + { value: 20, label: "20 mm", metric: true }, + { value: 10, label: "10 mm", metric: true }, + { value: 6, label: "6 mm", metric: true }, ]; export let open; @@ -132,8 +132,8 @@ if (probeType === "xyz") { await stepCompleted("BitDimensions", userAcknowledged); - localStorage.setItem("cutterDiameter", cutterDiameter); - localStorage.setItem("cutterLength", cutterLength); + localStorage.setItem("cutterDiameter", cutterDiameter.toString()); + localStorage.setItem("cutterLength", cutterLength.toString()); } await stepCompleted("PlaceProbeBlock", userAcknowledged); From 735064ca5e3698e864bb41db97b20642dbe40e4e Mon Sep 17 00:00:00 2001 From: David Carley Date: Sat, 16 Jul 2022 19:36:52 -0700 Subject: [PATCH 16/95] Fixed some ui alignment issues. --- .../src/dialogs/ChangeHostnameDialog.svelte | 8 ++++---- .../src/dialogs/HomeMachineDialog.svelte | 8 ++++---- src/svelte-components/src/dialogs/MessageDialog.svelte | 8 ++++---- src/svelte-components/src/dialogs/ProbeDialog.svelte | 10 +++++----- .../src/dialogs/ScreenRotationDialog.svelte | 10 +++++----- .../src/dialogs/WifiConnectionDialog.svelte | 8 ++++---- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte b/src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte index 77e472e..51bbdde 100644 --- a/src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte +++ b/src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte @@ -64,12 +64,12 @@ - Change Hostname + Change Hostname - + - Home Machine + Home Machine - Home the machine? + Home the machine? diff --git a/src/svelte-components/src/dialogs/ProbeDialog.svelte b/src/svelte-components/src/dialogs/ProbeDialog.svelte index 74de934..3a36ac2 100644 --- a/src/svelte-components/src/dialogs/ProbeDialog.svelte +++ b/src/svelte-components/src/dialogs/ProbeDialog.svelte @@ -310,13 +310,13 @@ bind:open class="probe-dialog" scrimClickAction="" - aria-labelledby="simple-title" - aria-describedby="simple-content" + aria-labelledby="probe-dialog-title" + aria-describedby="probe-dialog-content" surface$style="width: 700px; height: 400px; max-width: calc(100vw - 32px); overflow: visible;" > - Probing {probeType?.toUpperCase()} + Probing {probeType?.toUpperCase()} - +

Step {steps.indexOf(currentStep) + 1} of {steps.length}

    @@ -402,7 +402,7 @@ $light: #ddd; :global { - .mdc-dialog__content { + #probe-dialog-content { display: flex; flex-direction: row; } diff --git a/src/svelte-components/src/dialogs/ScreenRotationDialog.svelte b/src/svelte-components/src/dialogs/ScreenRotationDialog.svelte index 0fd78a0..4f02231 100644 --- a/src/svelte-components/src/dialogs/ScreenRotationDialog.svelte +++ b/src/svelte-components/src/dialogs/ScreenRotationDialog.svelte @@ -36,12 +36,12 @@ - Screen Rotation + Screen Rotation - + {#each options as option} @@ -68,7 +68,7 @@ diff --git a/src/svelte-components/src/lib/ControllerState.ts b/src/svelte-components/src/lib/ControllerState.ts new file mode 100644 index 0000000..ede3089 --- /dev/null +++ b/src/svelte-components/src/lib/ControllerState.ts @@ -0,0 +1,36 @@ +import { get, writable } from "svelte/store"; +import { processNetworkInfo } from "./NetworkInfo"; + +export const networkInfo = writable({}); + +export const probingActive = writable(false); +export const probeContacted = writable(false); +export const probingStarted = writable(false); +export const probingFailed = writable(false); +export const probingComplete = writable(false); + +export function handleControllerStateUpdate(state: Record) { + if (state.networkInfo) { + processNetworkInfo(state.networkInfo); + delete state.networkInfo; + } + + if (get(probingActive)) { + if (state.pw === 0) { + probeContacted.set(true); + } + + if (state.log?.msg === "Switch not found") { + probingFailed.set(true); + } + + if (state.cycle !== "idle") { + probingStarted.set(true); + } + + if (state.cycle === "idle" && get(probingStarted)) { + probingStarted.set(false); + probingComplete.set(true); + } + } +} \ No newline at end of file diff --git a/src/svelte-components/src/lib/NetworkInfo.ts b/src/svelte-components/src/lib/NetworkInfo.ts index bad3a07..8e9b46c 100644 --- a/src/svelte-components/src/lib/NetworkInfo.ts +++ b/src/svelte-components/src/lib/NetworkInfo.ts @@ -1,5 +1,4 @@ -import { readable } from "svelte/store"; -import * as api from "$lib/api"; +import { writable } from "svelte/store"; export type WifiNetwork = { Quality: string; @@ -34,60 +33,43 @@ const empty: NetworkInfo = { } } -export const networkInfo = readable(empty, (set) => { - getNetworkInfo(); - const networkInfoIntervalId = setInterval(getNetworkInfo, 5000); +export const networkInfo = writable(empty); - async function getNetworkInfo() { - const networksByName: Record = {} +export function processNetworkInfo(rawNetworkInfo: NetworkInfo) { + const now = Date.now(); + const networksByName: Record = {} - try { - const networkInfo: NetworkInfo = await api.GET("network"); - - const now = Date.now(); - for (let network of networkInfo.wifi.networks) { - if (network.Name) { - network.lastSeen = now; - network.active = networkInfo.wifi.ssid === network.Name; - networksByName[network.Name] = network; - } - } - - for (let network of Object.values(networksByName)) { - if (network.lastSeen - now > 30000) { - delete networksByName[network.Name]; - } - } - - set({ - ipAddresses: networkInfo.ipAddresses, - hostname: networkInfo.hostname, - wifi: { - ssid: networkInfo.wifi.ssid, - networks: Object.values(networksByName).sort((a, b) => { - switch (true) { - case a.active: - return -1; - - case b.active: - return 1; - - default: - return a.Name.localeCompare(b.Name); - } - }) - } - }); - } catch (error) { - console.debug("Failed to fetch network info", error); + for (let network of rawNetworkInfo.wifi.networks) { + if (network.Name) { + network.lastSeen = now; + network.active = rawNetworkInfo.wifi.ssid === network.Name; + networksByName[network.Name] = network; } } - return () => { - clearInterval(networkInfoIntervalId); + for (let network of Object.values(networksByName)) { + if (network.lastSeen - now > 30000) { + delete networksByName[network.Name]; + } } -}) -export function init() { - return networkInfo.subscribe(() => ({})); + networkInfo.set({ + ipAddresses: rawNetworkInfo.ipAddresses, + hostname: rawNetworkInfo.hostname, + wifi: { + ssid: rawNetworkInfo.wifi.ssid, + networks: Object.values(networksByName).sort((a, b) => { + switch (true) { + case a.active: + return -1; + + case b.active: + return 1; + + default: + return a.Name.localeCompare(b.Name); + } + }) + } + }); } diff --git a/src/svelte-components/src/main.ts b/src/svelte-components/src/main.ts index 4d55376..c2f746d 100644 --- a/src/svelte-components/src/main.ts +++ b/src/svelte-components/src/main.ts @@ -1,11 +1,13 @@ import 'polyfill-object.fromentries'; +import matchAll from "string.prototype.matchall"; + +matchAll.shim(); import AdminNetworkView from '$components/AdminNetworkView.svelte'; import DialogHost, { showDialog } from "$dialogs/DialogHost.svelte"; import Devmode from "$components/Devmode.svelte"; import { handleConfigUpdate } from '$lib/ConfigStore'; -import { init as initNetworkInfo } from '$lib/NetworkInfo'; -import { handleControllerStateUpdate } from "$dialogs/ProbeDialog.svelte"; +import { handleControllerStateUpdate } from "$lib/ControllerState"; import { registerControllerMethods } from "$lib/RegisterControllerMethods"; export function createComponent(component: string, target: HTMLElement, props: Record) { @@ -17,7 +19,7 @@ export function createComponent(component: string, target: HTMLElement, props: R return new DialogHost({ target, props }); case "Devmode": - return new Devmode({target, props}); + return new Devmode({ target, props }); default: throw new Error("Unknown component"); @@ -25,7 +27,6 @@ export function createComponent(component: string, target: HTMLElement, props: R } export { - initNetworkInfo, showDialog, handleControllerStateUpdate, handleConfigUpdate, diff --git a/src/svelte-components/vite.config.ts b/src/svelte-components/vite.config.ts index eeb6a0e..15a9cd4 100644 --- a/src/svelte-components/vite.config.ts +++ b/src/svelte-components/vite.config.ts @@ -15,6 +15,7 @@ export default defineConfig({ } }, build: { + minify: false, target: "chrome60", lib: { entry: resolve(__dirname, 'src/main.ts'), From 2efd753d95343455c2a53611e67c28defd12acdb Mon Sep 17 00:00:00 2001 From: David Carley Date: Fri, 22 Jul 2022 22:36:19 -0700 Subject: [PATCH 26/95] Rebuilt the settings view in Svelte --- src/js/admin-network-view.js | 2 +- src/js/app.js | 5 + src/js/settings-view.js | 35 ++--- src/pug/templates/admin-network-view.pug | 3 +- src/pug/templates/settings-view.pug | 53 +------ src/resources/config-template.json | 4 +- .../components/ConfigTemplatedInput.svelte | 131 ++++++++++++++++++ .../src/components/SettingsView.svelte | 80 +++++++++++ src/svelte-components/src/lib/ConfigStore.ts | 7 + .../src/lib/RegisterControllerMethods.ts | 8 +- src/svelte-components/src/main.ts | 7 +- 11 files changed, 249 insertions(+), 86 deletions(-) create mode 100644 src/svelte-components/src/components/ConfigTemplatedInput.svelte create mode 100644 src/svelte-components/src/components/SettingsView.svelte diff --git a/src/js/admin-network-view.js b/src/js/admin-network-view.js index c37e6ff..12d20c5 100644 --- a/src/js/admin-network-view.js +++ b/src/js/admin-network-view.js @@ -4,7 +4,7 @@ module.exports = { attached: function () { this.svelteComponent = SvelteComponents.createComponent( "AdminNetworkView", - document.getElementById("svelte-root") + document.getElementById("admin-network") ); }, diff --git a/src/js/app.js b/src/js/app.js index 57f18a5..a15c343 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -135,6 +135,7 @@ module.exports = new Vue({ watch: { display_units: function (value) { localStorage.setItem("display_units", value); + SvelteComponents.setDisplayUnits(value); }, }, @@ -213,6 +214,10 @@ module.exports = new Vue({ ready: function () { $(window).on("hashchange", this.parse_hash); this.connect(); + + SvelteComponents.registerControllerMethods({ + dispatch: (...args) => this.$dispatch(...args) + }); }, methods: { diff --git a/src/js/settings-view.js b/src/js/settings-view.js index 6d26b98..ab87296 100644 --- a/src/js/settings-view.js +++ b/src/js/settings-view.js @@ -1,31 +1,14 @@ -'use strict' - module.exports = { - template: '#settings-view-template', - props: ['config', 'template'], + template: "#settings-view-template", - computed: { - display_units: { - cache: false, - get: function () { - return this.$root.display_units; - }, - set: function (value) { - this.$root.display_units = value; - } - }, + attached: function () { + this.svelteComponent = SvelteComponents.createComponent( + "SettingsView", + document.getElementById("settings") + ); }, - events: { - 'input-changed': function () { - this.$dispatch('config-changed'); - return false; - } - }, - - methods: { - showScreenRotationDialog: function () { - SvelteComponents.showDialog("ScreenRotation"); - } + detached: function() { + this.svelteComponent.$destroy(); } -} +}; diff --git a/src/pug/templates/admin-network-view.pug b/src/pug/templates/admin-network-view.pug index dd57264..dd7d4a9 100644 --- a/src/pug/templates/admin-network-view.pug +++ b/src/pug/templates/admin-network-view.pug @@ -1,3 +1,2 @@ script#admin-network-view-template(type="text/x-template") - #admin-network - #svelte-root + #admin-network \ No newline at end of file diff --git a/src/pug/templates/settings-view.pug b/src/pug/templates/settings-view.pug index 15ec257..8a0061f 100644 --- a/src/pug/templates/settings-view.pug +++ b/src/pug/templates/settings-view.pug @@ -1,53 +1,2 @@ script#settings-view-template(type="text/x-template") - #settings - h1 Settings - - .pure-form.pure-form-aligned - fieldset - h2 Screen - .pure-control-group - label(for="screen-rotation") - button.pure-button(name="screen-rotation", @click="showScreenRotationDialog") Change Screen Rotation - - fieldset - h2 Probe Dimensions - templated-input(v-for="templ in template.probe", v-if="$key !== 'probe-diameter'", :name="$key" - :model.sync="config.probe[$key]", :template="templ") - - fieldset - h2 GCode - templated-input(v-for="templ in template.gcode", :name="$key", - :model.sync="config.gcode[$key]", :template="templ") - - fieldset - h2 Path Accuracy - templated-input(name="max-deviation", - :model.sync="config.settings['max-deviation']", - :template="template.settings['max-deviation']") - - p. - Lower #[tt max-deviation] to follow the programmed path more precisely - but at a slower speed. - - p. - In order to improve traversal speed, the path planner may merge - consecutive moves or round off sharp corners if doing so would deviate - from the program path by less than #[tt max-deviation]. - - - var base = '//linuxcnc.org/docs/html/gcode/g-code.html' - p. - GCode commands - #[a(href=base + "#gcode:g61", target="_blank") G61, G61.1] and - #[a(href=base + "#gcode:g64", target="_blank") G64] also affect path - planning accuracy. - - h2 Cornering Speed (Advanced) - templated-input(name="junction-accel", - :model.sync="config.settings['junction-accel']", - :template="template.settings['junction-accel']") - - p. - Junction acceleration limits the cornering speed the planner will - allow. Increasing this value will allow for faster traversal of - corners but may cause the planner to violate axis jerk limits and - stall the motors. Use with caution. + #settings \ No newline at end of file diff --git a/src/resources/config-template.json b/src/resources/config-template.json index 5495a73..fad50fb 100644 --- a/src/resources/config-template.json +++ b/src/resources/config-template.json @@ -502,14 +502,14 @@ }, "probe-fast-seek": { "type": "float", - "unit": "mm/m", + "unit": "mm/min", "min": 0, "max": 1000, "default": 200 }, "probe-slow-seek": { "type": "float", - "unit": "mm/m", + "unit": "mm/min", "min": 0, "max": 1000, "default": 25 diff --git a/src/svelte-components/src/components/ConfigTemplatedInput.svelte b/src/svelte-components/src/components/ConfigTemplatedInput.svelte new file mode 100644 index 0000000..a5f2950 --- /dev/null +++ b/src/svelte-components/src/components/ConfigTemplatedInput.svelte @@ -0,0 +1,131 @@ + + +{#if template} +
    + + + {#if template.values} + + {:else if template.type === "bool"} + + {:else if template.type === "float"} + + {:else if template.type === "int"} + + {:else if template.type === "string"} + + {:else if template.type == "text"} +