checkpoint

This commit is contained in:
David Carley
2022-07-07 12:48:20 -07:00
parent 87290cd992
commit bc161fcd3d
31 changed files with 834 additions and 928 deletions

11
package-lock.json generated
View File

@@ -15,7 +15,6 @@
"jstransformer-scss": "^2.0.0",
"jstransformer-stylus": "^1.5.0",
"lodash.merge": "4.6.2",
"lodash.omit": "^4.5.0",
"pug-cli": "^1.0.0-alpha6"
}
},
@@ -1915,11 +1914,6 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"node_modules/lodash.omit": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
"integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg=="
},
"node_modules/longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
@@ -4614,11 +4608,6 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"lodash.omit": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
"integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg=="
},
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",

View File

@@ -11,7 +11,6 @@
"jstransformer-scss": "^2.0.0",
"jstransformer-stylus": "^1.5.0",
"lodash.merge": "4.6.2",
"lodash.omit": "^4.5.0",
"pug-cli": "^1.0.0-alpha6"
}
}

View File

@@ -3,7 +3,6 @@
const api = require("./api");
const cookie = require("./cookie")("bbctrl-");
const Sock = require("./sock");
const omit = require("lodash.omit");
SvelteComponents.initNetworkInfo();
SvelteComponents.createComponent("DialogHost",
@@ -84,6 +83,7 @@ module.exports = new Vue({
return {
status: "connecting",
currentView: "loading",
display_units: localStorage.getItem("display_units") || "METRIC",
index: -1,
modified: false,
template: require("../resources/config-template.json"),
@@ -95,10 +95,6 @@ module.exports = new Vue({
},
state: {
messages: [],
probing_active: false,
wait_for_probing_complete: false,
show_probe_complete_modal: false,
show_probe_failed_modal: false,
},
video_size: cookie.get("video-size", "small"),
crosshair: cookie.get("crosshair", "false") != "false",
@@ -130,11 +126,19 @@ module.exports = new Vue({
"cheat-sheet-view": {
template: "#cheat-sheet-view-template",
data: function () {
return { showUnimplemented: false };
return {
showUnimplemented: false
};
},
},
},
watch: {
display_units: function (value) {
localStorage.setItem("display_units", value);
},
},
events: {
"config-changed": function () {
this.modified = true;
@@ -142,7 +146,6 @@ module.exports = new Vue({
send: function (msg) {
if (this.status == "connected") {
console.debug(">", msg);
this.sock.send(msg);
}
},
@@ -214,10 +217,6 @@ module.exports = new Vue({
},
methods: {
metric: function () {
return this.config.settings.units != "IMPERIAL";
},
block_error_dialog: function () {
this.errorTimeoutStart = Date.now();
this.errorShow = false;
@@ -297,6 +296,7 @@ module.exports = new Vue({
if (typeof check == "undefined" || check) this.$emit("check");
}
SvelteComponents.handleConfigUpdate(this.config);
},
shutdown: function () {
@@ -318,13 +318,9 @@ module.exports = new Vue({
}
if ("log" in e.data) {
if (e.data.log.msg === "Switch not found") {
this.$broadcast("probing_failed");
} else {
if (e.data.log.msg !== "Switch not found") {
this.$broadcast("log", e.data.log);
}
delete e.data.log;
}
// Check for session ID change on controller
@@ -343,37 +339,11 @@ module.exports = new Vue({
}
}
// Set this to true to get console output of changes to the state
const debugStateChanges = false;
if (debugStateChanges) {
const data = omit(e.data, [
"vdd",
"vin",
"vout",
"motor",
"temp",
"heartbeat",
"load1",
"load2",
"rpi_temp",
]);
if (Object.keys(data).length > 0) {
console.log(JSON.stringify(data, null, 4));
}
}
update_object(this.state, e.data, false);
if (this.state.pw === 0) {
Vue.set(this.state, "saw_probe_connected", true);
}
SvelteComponents.handleControllerStateUpdate(this.state);
if (this.state.cycle === "idle") {
if (this.state.wait_for_probing_complete) {
Vue.set(this.state, "wait_for_probing_complete", false);
this.$broadcast("probing_complete");
}
}
delete this.state.log;
this.$broadcast("update");
};
@@ -406,7 +376,7 @@ module.exports = new Vue({
this.currentView = parts[0];
},
save: function () {
save: async function () {
const selected_tool = this.config.tool["selected-tool"];
const saveModbus =
selected_tool !== "pwm" &&
@@ -423,16 +393,12 @@ module.exports = new Vue({
this.config["selected-tool-settings"][selected_tool] = settings;
api
.put("config/save", this.config)
.done(
function (data) {
try {
await api.put("config/save", this.config);
this.modified = false;
}.bind(this)
)
.fail(function (error) {
} catch (error) {
api.alert("Save failed", error);
});
}
},
close_messages: function (action) {

View File

@@ -4,6 +4,7 @@ module.exports = {
props: ['state', 'config'],
computed: {
metric: function () { this.$root.display_units === "METRIC" },
x: function () { return this._compute_axis('x') },
y: function () { return this._compute_axis('y') },
z: function () { return this._compute_axis('z') },
@@ -15,12 +16,14 @@ module.exports = {
methods: {
_convert_length: function (value) {
return this.state.imperial ? value / 25.4 : value;
return this.metric
? value
: value / 25.4;
},
_length_str: function (value) {
return this._convert_length(value).toLocaleString() +
(this.state.imperial ? ' in' : ' mm');
(this.metric ? ' mm' : ' in');
},
_compute_axis: function (axis) {

View File

@@ -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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
var api = require('./api');
@@ -36,7 +9,7 @@ module.exports = {
data: function () {
return {
mach_units: 'METRIC',
mach_units: this.$root.state.metric ? "METRIC" : "IMPERIAL",
mdi: '',
last_file: undefined,
last_file_time: undefined,
@@ -62,16 +35,26 @@ module.exports = {
b: false,
c: false
},
jog_incr_amounts: {
"METRIC": {
fine: 0.1,
small: 1.0,
medium: 10,
large: 100,
},
"IMPERIAL": {
fine: 0.005,
small: 0.05,
medium: 0.5,
large: 5,
}
},
axis_position: 0,
jog_incr: localStorage.getItem("jog_incr") || 'small',
jog_step: cookie.get_bool('jog-step'),
jog_adjust: parseInt(cookie.get('jog-adjust', 2)),
deleteGCode: false,
tab: 'auto',
jog_incr: 1.0,
tool_diameter: 6.35,
tool_diameter_for_prompt: 6.35,
show_probe_test_modal: false,
show_tool_diameter_modal: false,
toolpath_msg: {
x: false,
y: false,
@@ -93,33 +76,24 @@ module.exports = {
'gcode-viewer': require('./gcode-viewer')
},
watch: {
'state.imperial': {
handler: function (imperial) {
this.mach_units = imperial ? 'IMPERIAL' : 'METRIC';
jog_incr: function (value) {
localStorage.setItem("jog_incr", value);
},
'state.metric': {
handler: function (metric) {
this.mach_units = metric
? 'METRIC'
: 'IMPERIAL';
},
immediate: true
},
'state.bitDiameter': {
handler: function (bitDiameter) {
this.tool_diameter = bitDiameter;
},
immediate: true
},
mach_units: function (units) {
if ((units == 'METRIC') != this.metric)
this.send(units == 'METRIC' ? 'G21' : 'G20');
this.units_changed();
},
'state.line': function () {
if (this.mach_state != 'HOMING')
if (this.mach_state != 'HOMING') {
this.$broadcast('gcode-line', this.state.line);
}
},
'state.selected_time': function () {
@@ -135,37 +109,56 @@ module.exports = {
}
},
computed: {
metric: function () {
return !this.state.imperial;
display_units: {
cache: false,
get: function () {
return this.$root.display_units;
},
set: function (value) {
this.$root.display_units = value;
}
},
metric: function () {
return this.display_units === "METRIC";
},
mach_state: function () {
var cycle = this.state.cycle;
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 () {
return this.mach_state == 'RUNNING' || this.mach_state == 'HOMING';
},
is_stopping: function () {
return this.mach_state == 'STOPPING'
},
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_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 &&
@@ -173,9 +166,9 @@ module.exports = {
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 () {
return this.is_idle
@@ -183,47 +176,59 @@ module.exports = {
return this.is_idle || this.is_paused
},
message: function () {
if (this.mach_state == 'ESTOPPED') return this.state.er;
if (this.mach_state == 'HOLDING') return this.state.pr;
if (this.state.messages.length)
if (this.mach_state == 'ESTOPPED') {
return this.state.er;
}
if (this.mach_state == 'HOLDING') {
return this.state.pr;
}
if (this.state.messages.length) {
return this.state.messages.slice(-1)[0].text;
}
return '';
},
highlight_state: function () {
return this.mach_state == 'ESTOPPED' || this.mach_state == 'HOLDING';
},
plan_time: function () { return this.state.plan_time },
plan_time: function () {
return this.state.plan_time
},
plan_time_remaining: function () {
if (!(this.is_stopping || this.is_running || this.is_holding)) return 0;
if (!(this.is_stopping || this.is_running || this.is_holding)) {
return 0;
}
return this.toolpath.time - this.plan_time
},
eta: function () {
if (this.mach_state != 'RUNNING') return '';
if (this.mach_state != 'RUNNING') {
return '';
}
var remaining = this.plan_time_remaining;
var d = new Date();
d.setSeconds(d.getSeconds() + remaining);
return d.toLocaleString();
},
progress: function () {
if (!this.toolpath.time || this.is_ready) return 0;
if (!this.toolpath.time || this.is_ready) {
return 0;
}
var p = this.plan_time / this.toolpath.time;
return p < 1 ? p : 1;
}
},
events: {
jog: function (axis, power) {
var data = { ts: new Date().getTime() };
@@ -232,244 +237,34 @@ module.exports = {
},
back2zero: function (axis0, axis1) {
this.send("G0" + axis0 + "0" + axis1 + "0");
this.send(`G0 ${axis0}0 ${axis1}0`);
},
step: function (axis, value) {
this.send('M70\nG91\nG0' + axis + value + '\nM72');
this.send(`
M70
G91
G0 ${axis}${value}
M72
`);
},
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);
Vue.set(this.state, "goto_xy_zero_after_probe", false);
Vue.set(this.state, "show_probe_failed_modal", true);
},
probing_complete: function () {
Vue.set(this.state, "probing_active", false);
if (this.config.settings['probing-prompts']) {
Vue.set(this.state, "show_probe_complete_modal", true);
} else {
this.$emit("finalize_probe");
}
},
finalize_probe: function () {
Vue.set(this.state, "show_probe_complete_modal", false);
if (this.state.goto_xy_zero_after_probe) {
this.goto_zero(1, 1, 0, 0);
}
Vue.set(this.state, "goto_xy_zero_after_probe", false);
}
},
ready: function () {
this.load()
},
this.load();
SvelteComponents.registerControllerMethods({
send: (...args) => this.send(...args),
goto_zero: (...args) => this.goto_zero(...args)
});
},
methods: {
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";
document.getElementById("jog_button_large").innerHTML = "100";
} else {
document.getElementById("jog_button_fine").innerHTML = "0.005";
document.getElementById("jog_button_small").innerHTML = "0.05";
document.getElementById("jog_button_medium").innerHTML = "0.5";
document.getElementById("jog_button_large").innerHTML = "5";
}
this.set_jog_incr('small');
},
start_probe_test: function (on_finish) {
if (!this.config.settings['probing-prompts']) {
on_finish();
return;
}
this.show_probe_test_modal = true;
Vue.set(this.state, "saw_probe_connected", false);
Vue.set(this.state, "on_probe_finish", on_finish);
},
finish_probe_test: function () {
this.show_probe_test_modal = false;
Vue.set(this.state, "saw_probe_connected", false);
const on_finish = this.state.on_probe_finish;
Vue.set(this.state, "on_probe_finish", undefined);
on_finish();
},
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_for_prompt = this.tool_diameter_for_prompt.toFixed(3).replace(/0+$/, "");
this.show_tool_diameter_modal = true;
},
set_tool_diameter() {
this.tool_diameter = parseFloat(this.tool_diameter_for_prompt);
if (!isFinite(this.tool_diameter)) {
return;
}
this.show_tool_diameter_modal = false;
if (this.mach_units !== "METRIC") {
this.tool_diameter *= 25.4;
}
this.probe_xyz();
},
probe(zOnly = false) {
const xdim = this.config.probe["probe-xdim"];
const ydim = this.config.probe["probe-ydim"];
const zdim = this.config.probe["probe-zdim"];
const slowSeek = this.config.probe["probe-slow-seek"];
const fastSeek = this.config.probe["probe-fast-seek"];
const zlift = 1;
const xoffset = xdim + (this.tool_diameter / 2.0);
const yoffset = ydim + (this.tool_diameter / 2.0);
const zoffset = zdim;
const metric = this.mach_units == "METRIC";
const mm = n => (metric ? n : n / 25.4).toFixed(5);
const speed = s => `F${mm(s)}`;
// After probing Z, we want to drop the bit down:
// Ideally, 12.7mm/0.5in
// And we don't want to be more than 75% down on the probe block
// Also, add zlift to compensate for the fact that we lift after probing Z
const plunge = Math.min(12.7, zoffset * 0.75) + zlift;
Vue.set(this.state, "probing_active", true);
Vue.set(this.state, "goto_xy_zero_after_probe", !zOnly);
if (zOnly) {
this.send(`
${metric ? "G21" : "G20"}
G92 Z0
G38.2 Z ${mm(-25.4)} ${speed(fastSeek)}
G91 G1 Z ${mm(1)}
G38.2 Z ${mm(-2)} ${speed(slowSeek)}
G92 Z ${mm(zoffset)}
G91 G0 Z ${mm(3)}
M2
`);
} else {
this.send(`
${metric ? "G21" : "G20"}
G92 X0 Y0 Z0
G38.2 Z ${mm(-25.4)} ${speed(fastSeek)}
G91 G1 Z ${mm(1)}
G38.2 Z ${mm(-2)} ${speed(slowSeek)}
G92 Z ${mm(zoffset)}
G91 G0 Z ${mm(zlift)}
G91 G0 X ${mm(20)}
G91 G0 Z ${mm(-plunge)}
G38.2 X ${mm(-20)} ${speed(fastSeek)}
G91 G1 X ${mm(1)}
G38.2 X ${mm(-2)} ${speed(slowSeek)}
G92 X ${mm(xoffset)}
G91 G0 X ${mm(1)}
G91 G0 Y ${mm(20)}
G91 G0 X ${mm(-20)}
G38.2 Y ${mm(-20)} ${speed(fastSeek)}
G91 G1 Y ${mm(1)}
G38.2 Y ${mm(-2)} ${speed(slowSeek)}
G92 Y ${mm(yoffset)}
G91 G0 Y ${mm(3)}
G91 G0 Z ${mm(25.4)}
M2
`);
}
// Wait 1 second to let the probing sequence begin,
// then wait for probing to be complete
setTimeout(() => Vue.set(this.state, "wait_for_probing_complete", true), 1000);
},
probe_xyz() {
this.probe(false);
},
probe_z() {
this.probe(true);
},
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';
document.getElementById("jog_button_large").style.fontWeight = 'normal';
if (newValue == 'fine') {
document.getElementById("jog_button_fine").style.fontWeight = 'bold';
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')
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')
this.jog_incr = 10;
else
this.jog_incr = 0.5;
} else if (newValue == 'large') {
document.getElementById("jog_button_large").style.fontWeight = 'bold';
this.jog_incr = (this.mach_units == 'METRIC')
? 100
: 5;
}
},
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";
const xcmd = zero_x ? "X0" : "";
const ycmd = zero_y ? "Y0" : "";
const zcmd = zero_z ? "Z0" : "";
const acmd = zero_a ? "A0" : "";
this.ask_zero_xy_msg = false;
this.ask_zero_z_msg = false;
@@ -477,13 +272,25 @@ module.exports = {
this.send('G90\nG0' + xcmd + ycmd + zcmd + acmd + '\n');
},
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;
var acmd = "A" + a_jog * this.jog_incr;
getJogIncrFontWeight(value) {
const weight = this.jog_incr === value ? 'bold' : 'normal';
this.send('G91\nG0' + xcmd + ycmd + zcmd + acmd + '\n');
return `font-weight:${weight}`;
},
jog_fn: function (x_jog, y_jog, z_jog, a_jog) {
const amount = this.jog_incr_amounts[this.display_units][this.jog_incr];
var xcmd = "X" + x_jog * amount;
var ycmd = "Y" + y_jog * amount;
var zcmd = "Z" + z_jog * amount;
var acmd = "A" + a_jog * amount;
this.send(`
G91
${this.metric ? "G21" : "G20"}
G0 ${xcmd}${ycmd}${zcmd}${acmd}
`);
},
send: function (msg) {
@@ -493,7 +300,10 @@ module.exports = {
load: function () {
var file_time = this.state.selected_time;
var file = this.state.selected;
if (this.last_file == file && this.last_file_time == file_time) return;
if (this.last_file == file && this.last_file_time == file_time) {
return;
}
this.last_file = file;
this.last_file_time = file_time;
@@ -503,12 +313,12 @@ module.exports = {
this.load_toolpath(file, file_time);
},
load_toolpath: async function (file, file_time) {
this.toolpath = {};
if (!file) return;
if (this.last_file_time != file_time) return;
if (!file || this.last_file_time != file_time) {
return;
}
this.showGcodeMessage = true;
@@ -534,30 +344,30 @@ module.exports = {
}
},
submit_mdi: function () {
this.send(this.mdi);
if (!this.history.length || this.history[0] != this.mdi)
if (!this.history.length || this.history[0] != this.mdi) {
this.history.unshift(this.mdi);
}
this.mdi = '';
},
mdi_start_pause: function () {
if (this.state.xx == 'RUNNING') this.pause();
else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING')
if (this.state.xx == 'RUNNING') {
this.pause();
} else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') {
this.unpause();
else this.submit_mdi();
} else {
this.submit_mdi();
}
},
load_history: function (index) {
this.mdi = this.history[index];
},
open: function (e) {
// If we don't reset the form the browser may cache file if name is same
// even if contents have changed
@@ -565,7 +375,6 @@ module.exports = {
$('.gcode-file-input input').click();
},
upload: async function (e) {
const files = e.target.files || e.dataTransfer.files;
if (!files.length) {
@@ -601,7 +410,6 @@ module.exports = {
}
},
delete_current: function () {
if (this.state.selected) {
api.delete('file/' + this.state.selected);
@@ -610,41 +418,33 @@ module.exports = {
this.deleteGCode = false;
},
delete_all: function () {
api.delete('file');
this.deleteGCode = false;
},
home: function (axis) {
this.ask_home = false;
if (typeof axis == 'undefined') {
api.put('home');
} else {
if (this[axis].homingMode != 'manual') {
} else if (this[axis].homingMode != 'manual') {
api.put('home/' + axis);
}
else {
} else {
this.manual_home[axis] = true;
}
}
},
set_home: function (axis, position) {
this.manual_home[axis] = false;
api.put('home/' + axis + '/set', { position: parseFloat(position) });
},
unhome: function (axis) {
this.position_msg[axis] = false;
api.put('home/' + axis + '/clear');
},
show_set_position: function (axis) {
this.axis_position = 0;
this.position_msg[axis] = true;
@@ -654,61 +454,84 @@ module.exports = {
this.toolpath_msg[axis] = true;
},
set_position: function (axis, position) {
this.position_msg[axis] = false;
api.put('position/' + axis, { 'position': parseFloat(position) });
},
zero_all: function () {
for (var axis of 'xyzabc')
if (this[axis].enabled) this.zero(axis);
for (var axis of 'xyzabc') {
if (this[axis].enabled) {
this.zero(axis);
}
}
},
zero: function (axis) {
if (typeof axis == 'undefined') this.zero_all();
else this.set_position(axis, 0);
if (typeof axis == 'undefined') {
this.zero_all();
} else {
this.set_position(axis, 0);
}
},
start_pause: function () {
if (this.state.xx == 'RUNNING') this.pause();
else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING')
if (this.state.xx == 'RUNNING') {
this.pause();
} else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') {
this.unpause();
else this.start();
} else {
this.start();
}
},
start: function () {
api.put('start')
},
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') },
pause: function () {
api.put('pause')
},
unpause: function () {
api.put('unpause')
},
override_feed: function () { api.put('override/feed/' + this.feed_override) },
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_speed: function () {
api.put('override/speed/' + this.speed_override)
},
current: function (axis, value) {
var x = value / 32.0;
if (this.state[axis + 'pl'] == x) return;
if (this.state[axis + 'pl'] == x) {
return;
}
var data = {};
data[axis + 'pl'] = x;
this.send(JSON.stringify(data));
},
showProbeDialog: function(probeType) {
SvelteComponents.showDialog("Probe", { probeType });
}
},
mixins: [require('./axis-vars')]
}

View File

@@ -1,33 +1,5 @@
/******************************************************************************\
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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict';
function cookie_get(name) {
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
@@ -35,11 +7,15 @@ function cookie_get(name) {
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1);
if (!c.indexOf(name)) return c.substring(name.length, c.length);
}
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (!c.indexOf(name)) {
return c.substring(name.length, c.length);
}
}
}
function cookie_set(name, value, days) {
var d = new Date();
@@ -48,29 +24,26 @@ function cookie_set(name, value, days) {
document.cookie = name + '=' + value + ';' + expires + ';path=/';
}
var uuid_chars =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+';
var uuid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+';
function uuid(length) {
if (typeof length == 'undefined') length = 52;
if (typeof length == 'undefined') {
length = 52;
}
var s = '';
for (var i = 0; i < length; i++)
for (var i = 0; i < length; i++) {
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
}
return s
}
$(function () {
if (typeof cookie_get('client-id') == 'undefined')
if (typeof cookie_get('client-id') == 'undefined') {
cookie_set('client-id', uuid(), 10000);
// Vue debugging
Vue.config.debug = true;
//Vue.util.warn = function (msg) {console.debug('[Vue warn]: ' + msg)}
}
// Register global components
Vue.component('templated-input', require('./templated-input'));
@@ -81,35 +54,61 @@ $(function() {
Vue.component('unit-value', require('./unit-value'));
Vue.filter('number', function (value) {
if (isNaN(value)) return 'NaN';
if (isNaN(value)) {
return 'NaN';
}
return value.toLocaleString();
});
Vue.filter('percent', function (value, precision) {
if (typeof value == 'undefined') return '';
if (typeof precision == 'undefined') precision = 2;
if (typeof value == 'undefined') {
return '';
}
if (typeof precision == 'undefined') {
precision = 2;
}
return (value * 100.0).toFixed(precision) + '%';
});
Vue.filter('non_zero_percent', function (value, precision) {
if (!value) return '';
if (typeof precision == 'undefined') precision = 2;
if (!value) {
return '';
}
if (typeof precision == 'undefined') {
precision = 2;
}
return (value * 100.0).toFixed(precision) + '%';
});
Vue.filter('fixed', function (value, precision) {
if (typeof value == 'undefined') return '0';
if (typeof value == 'undefined') {
return '0';
}
return parseFloat(value).toFixed(precision)
});
Vue.filter('upper', function (value) {
if (typeof value == 'undefined') return '';
if (typeof value == 'undefined') {
return '';
}
return value.toUpperCase()
});
Vue.filter('time', function (value, precision) {
if (isNaN(value)) return '';
if (isNaN(precision)) precision = 0;
if (isNaN(value)) {
return '';
}
if (isNaN(precision)) {
precision = 0;
}
var MIN = 60;
var HR = MIN * 60;
@@ -129,14 +128,17 @@ $(function() {
if (MIN <= value) {
parts.push(Math.floor(value / MIN));
value %= MIN;
} else parts.push(0);
} else {
parts.push(0);
}
parts.push(value);
for (var i = 0; i < parts.length; i++) {
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
if (i && parts[i] < 10) parts[i] = '0' + parts[i];
if (i && parts[i] < 10) {
parts[i] = '0' + parts[i];
}
}
return parts.join(':');

View File

@@ -1,88 +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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
module.exports = {
template: '#motor-view-template',
props: ['index', 'config', 'template', 'state'],
computed: {
metric: function () {return this.$root.metric()},
metric: function () {
return this.$root.display_units === "METRIC";
},
is_slave: function () {
for (var i = 0; i < this.index; i++)
if (this.motor.axis == this.config.motors[i].axis)
for (var i = 0; i < this.index; i++) {
if (this.motor.axis == this.config.motors[i].axis) {
return true;
}
}
return false;
},
motor: function () {return this.config.motors[this.index]},
motor: function () {
return this.config.motors[this.index]
},
invalidMaxVelocity: function () {
return this.maxMaxVelocity < this.motor['max-velocity'];
},
maxMaxVelocity: function () {
return 1 * (15 * this.umPerStep / this.motor['microsteps']).toFixed(3);
},
ustepPerSec: function () {
return this.rpm * this.stepsPerRev * this.motor['microsteps'] / 60;
},
rpm: function () {
return 1000 * this.motor['max-velocity'] / this.motor['travel-per-rev'];
},
gForce: function () {
return this.motor['max-accel'] * 0.0283254504
},
gForce: function () {return this.motor['max-accel'] * 0.0283254504},
gForcePerMin: function () {return this.motor['max-jerk'] * 0.0283254504},
stepsPerRev: function () {return 360 / this.motor['step-angle']},
gForcePerMin: function () {
return this.motor['max-jerk'] * 0.0283254504
},
stepsPerRev: function () {
return 360 / this.motor['step-angle']
},
umPerStep: function () {
return this.motor['travel-per-rev'] * this.motor['step-angle'] / 0.36
},
milPerStep: function () {return this.umPerStep / 25.4},
milPerStep: function () {
return this.umPerStep / 25.4
},
invalidStallVelocity: function () {
if(!this.motor['homing-mode'].startsWith('stall-')) return false;
if (!this.motor['homing-mode'].startsWith('stall-')) {
return false;
}
return this.maxStallVelocity < this.motor['search-velocity'];
},
@@ -105,20 +86,20 @@ module.exports = {
var ustep = this.motor['stall-microstep'];
return this.stallRPM * this.stepsPerRev * ustep / 60;
}
},
events: {
'input-changed': function () {
Vue.nextTick(function () {
// Limit max-velocity
if (this.invalidMaxVelocity)
if (this.invalidMaxVelocity) {
this.$set('motor["max-velocity"]', this.maxMaxVelocity);
}
//Limit stall-velocity
if(this.invalidStallVelocity)
if (this.invalidStallVelocity) {
this.$set('motor["search-velocity"]', this.maxStallVelocity);
}
this.$dispatch('config-changed');
}.bind(this))
@@ -129,7 +110,10 @@ module.exports = {
methods: {
show: function (name, templ) {
if(templ.hmodes == undefined) return true;
if (templ.hmodes == undefined) {
return true;
}
return templ.hmodes.indexOf(this.motor['homing-mode']) != -1;
}
}

View File

@@ -1,37 +1,20 @@
/******************************************************************************\
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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
module.exports = {
template: '#settings-view-template',
props: ['config', 'template'],
computed: {
display_units: {
cache: false,
get: function () {
return this.$root.display_units;
},
set: function (value) {
this.$root.display_units = value;
}
},
},
events: {
'input-changed': function () {

View File

@@ -1,49 +1,24 @@
/******************************************************************************\
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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
module.exports = {
replace: true,
template: '#templated-input-template',
props: ['name', 'model', 'template'],
data: function () {return {view: ''}},
data: function () {
return { view: '' }
},
computed: {
metric: function () {return this.$root.metric()},
metric: function () {
return this.$root.display_units === "METRIC";
},
_view: function () {
if (this.template.scale) {
if (this.metric) return 1 * this.model.toFixed(3);
if (this.metric) {
return 1 * this.model.toFixed(3);
}
return 1 * (this.model / this.template.scale).toFixed(4);
}
@@ -51,39 +26,44 @@ module.exports = {
return this.model;
},
units: function () {
return (this.metric || !this.template.iunit) ?
this.template.unit : this.template.iunit;
return (this.metric || !this.template.iunit)
? this.template.unit
: this.template.iunit;
},
title: function () {
var s = 'Default ' + this.template.default + ' ' +
(this.template.unit || '');
if (typeof this.template.help != 'undefined')
var s = `Default :${this.template.default} ${(this.template.unit || '')}`;
if (typeof this.template.help != 'undefined') {
s = this.template.help + '\n' + s;
}
return s;
}
},
watch: {
_view: function () {this.view = this._view},
_view: function () {
this.view = this._view
},
view: function () {
if (this.template.scale && !this.metric)
if (this.template.scale && !this.metric) {
this.model = this.view * this.template.scale;
else this.model = this.view;
} else {
this.model = this.view;
}
}
},
ready: function () {this.view = this._view},
ready: function () {
this.view = this._view
},
methods: {
change: function () {this.$dispatch('input-changed')}
change: function () {
this.$dispatch('input-changed')
}
}
}

View File

@@ -1,58 +1,47 @@
/******************************************************************************\
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 <http://www.gnu.org/licenses/>.
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
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
module.exports = {
replace: true,
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
props: ['value', 'precision', 'unit', 'iunit', 'scale'],
computed: {
metric: function () {return !this.$root.state.imperial},
metric: {
cache: false,
get: function () {
return this.$root.display_units === "METRIC";
}
},
text: function () {
var value = this.value;
if (typeof value == 'undefined') return '';
if (typeof value == 'undefined') {
return '';
}
if (!this.metric) value /= this.scale;
if (!this.metric) {
value /= this.scale;
}
return (1 * value.toFixed(this.precision)).toLocaleString();
}
},
ready: function () {
if (typeof this.precision == 'undefined') this.precision = 0;
if (typeof this.unit == 'undefined') this.unit = 'mm';
if (typeof this.iunit == 'undefined') this.iunit = 'in';
if (typeof this.scale == 'undefined') this.scale = 25.4;
if (typeof this.precision == 'undefined') {
this.precision = 0;
}
if (typeof this.unit == 'undefined') {
this.unit = 'mm';
}
if (typeof this.iunit == 'undefined') {
this.iunit = 'in';
}
if (typeof this.scale == 'undefined') {
this.scale = 25.4;
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>. //
//- //
//- 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 //
//- <http://www.gnu.org/licenses/>. //
//- //
//- For information regarding this software email: //
//- "Joseph Coffland" <joseph@buildbotics.com> //
//- //
//-/////////////////////////////////////////////////////////////////////////////
script#control-view-template(type="text/x-template")
#control
message(:show.sync="showGcodeMessage")
@@ -63,70 +36,6 @@ script#control-view-template(type="text/x-template")
button.pure-button(@click='ask_zero_z_msg = false')
| Cancel
message(:show.sync=`show_probe_test_modal`)
h3(slot="header") Test probe connection
div(slot="body")
.pure-form
p Attach the probe magnet to the collet.
p Touch the probe block to the bit.
div(slot="footer")
button.pure-button(@click=`show_probe_test_modal = false`)
| Cancel
button.pure-button.button-success(
:disabled=`!state.saw_probe_connected`
@click=`finish_probe_test()`) Continue
message(:show.sync=`show_tool_diameter_modal`)
h3(slot="header") Enter probe tool information
div(slot="body")
.pure-form
.pure-control-group
label="{{metric ? 'Diameter (mm)' : 'Diameter (inches)'}}"
input(v-model="tool_diameter_for_prompt", size="8")
p
div(slot="footer")
button.pure-button(@click=`show_tool_diameter_modal = false`)
| Cancel
button.pure-button.button-success(
@click=`set_tool_diameter`)
| Set
message(:show.sync=`state.show_probe_complete_modal`)
h3(slot="header") Probing complete!
div(slot="body")
.pure-form
p Don't forget to put away the probe!
div(v-if="state.goto_xy_zero_after_probe")
p
| The machine will now move
br
| to the X-Y zero point.
p Watch your hands!
div(slot="footer")
button.pure-button.button-success(@click=`$emit("finalize_probe")`)
| Done
message(:show.sync=`state.show_probe_failed_modal`)
h3(slot="header") Probing failed!
div(slot="body")
.pure-form
p Could not find the probe block during probing!
p Make sure the tip of the bit is about 1/4" (~6mm) above the probe block, and try again.
div(slot="footer")
button.pure-button.button-success(@click=`hide_probe_failed_modal()`)
| OK
table(style="table-layout: fixed; width: 100%;")
tr(style="height: fit-content;")
td(style="white-space: nowrap; width: 410px;", rowspan="2")
@@ -138,55 +47,60 @@ script#control-view-template(type="text/x-template")
col(style="width:100px")
tr
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(-1,1,0,0)")
button(@click="jog_fn(-1,1,0,0)")
.fa.fa-arrow-right(style="transform: rotate(-135deg);")
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(0,1,0,0)") Y+
button(@click="jog_fn(0,1,0,0)") Y+
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(1,1,0,0)")
button(@click="jog_fn(1,1,0,0)")
.fa.fa-arrow-right(style="transform: rotate(-45deg);")
td(style="height:100px",align="center")
button(style="height:100px;width:100px",,@click="jog_fn(0,0,1,0)") Z+
button(,@click="jog_fn(0,0,1,0)") Z+
tr
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(-1,0,0,0)") X-
button(@click="jog_fn(-1,0,0,0)") X-
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="ask_zero_xy_msg = true")
button(@click="ask_zero_xy_msg = true")
.fa.fa-bullseye(style="font-size: 172%")
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(1,0,0,0)") X+
button(@click="jog_fn(1,0,0,0)") X+
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click='ask_zero_z_msg = true') Z0
button(@click='ask_zero_z_msg = true') Z0
tr
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(-1,-1,0,0)")
button(@click="jog_fn(-1,-1,0,0)")
.fa.fa-arrow-right(style="transform: rotate(135deg);")
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(0,-1,0,0)") Y-
button(@click="jog_fn(0,-1,0,0)") Y-
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(1,-1,0,0)")
button(@click="jog_fn(1,-1,0,0)")
.fa.fa-arrow-right(style="transform: rotate(45deg);")
td(style="height:100px",align="center")
button(style="height:100px;width:100px",@click="jog_fn(0,0,-1,0)") Z-
button(@click="jog_fn(0,0,-1,0)") Z-
tr
td(style="height:100px",align="center")
button#jog_button_fine(style="height:100px;width:100px", @click=`set_jog_incr('fine')`) 0.1
button(:style="getJogIncrFontWeight('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#jog_button_small(style="height:100px;width:100px", @click=`set_jog_incr('small')`) 1.0
button(:style="getJogIncrFontWeight('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#jog_button_medium(style="height:100px;width:100px", @click=`set_jog_incr('medium')`) 10
button(:style="getJogIncrFontWeight('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#jog_button_large(style="height:100px;width:100px", @click=`set_jog_incr('large')`) 100
button(:style="getJogIncrFontWeight('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")
button(:class="state['pw'] ? '' : 'load-on'",
style="height:100px;width:200px",
@click=`start_probe_test(prep_and_show_tool_diameter_modal)`)
@click="showProbeDialog('xyz')")
| Probe XYZ
td(style="height:100px", align="center", colspan="2")
button(:class="state['pw'] ? '' : 'load-on'",
style="height:100px;width:200px",
@click=`start_probe_test(probe_z)`)
@click="showProbeDialog('z')")
| Probe Z
td(style="vertical-align: top;")
@@ -198,8 +112,6 @@ script#control-view-template(type="text/x-template")
th.offset Offset
th.state State
th.tstate Toolpath
//th.tstate Min
//th.tstate Max
th.actions
button.pure-button(disabled, style="height:60px;width:60px;display:none;")
@@ -224,8 +136,6 @@ script#control-view-template(type="text/x-template")
td.tstate(:class=`${axis}.tklass`, :title=`${axis}.toolmsg`, @click=`show_toolpath_msg('${axis}')`)
.fa(:class=`'fa-' + ${axis}.ticon`)
| {{#{axis}.tstate}}
//td.tstate: unit-value(:value=`${axis}.pathMin`, precision=4)
//td.tstate: unit-value(:value=`${axis}.pathMax`, precision=4)
message(:show.sync=`toolpath_msg['${axis}']`)
h3(slot="header") Tool path info {{'#{axis}' | upper}} axis
@@ -310,10 +220,10 @@ script#control-view-template(type="text/x-template")
td.message(:class="{attention: highlight_state}")
| {{message.replace(/^#/, '')}}
tr(title="Active machine units")
tr
th Units
td.mach_units
select(v-model="mach_units", :disabled="!is_idle")
td.units
select(v-model="display_units")
option(value="METRIC") METRIC
option(value="IMPERIAL") IMPERIAL
@@ -471,6 +381,9 @@ script#control-view-template(type="text/x-template")
input(v-model="mdi", :disabled="!can_mdi", @keyup.enter="submit_mdi")
div
em The machine is currently operating in #[strong {{mach_units}}] units. Use G20/G21 to switch units.
.history(:class="{placeholder: !history}")
span(v-if="!history.length") MDI history displays here.
ul

View File

@@ -1,25 +1,3 @@
//- 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 <http://www.gnu.org/licenses/>. //
//- //
//- 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 //
//- <http://www.gnu.org/licenses/>. //
//- //
//- For information regarding this software email: //
//- "Joseph Coffland" <joseph@buildbotics.com> //
//- //
//-/////////////////////////////////////////////////////////////////////////////
script#settings-view-template(type="text/x-template")
#settings
h1 Settings
@@ -27,13 +5,12 @@ script#settings-view-template(type="text/x-template")
.pure-form.pure-form-aligned
fieldset
h2 Units
templated-input(name="units", :model.sync="config.settings.units",
:template="template.settings.units")
.pure-control-group
label(for="units") units
select(name="units", v-model="display_units")
option(value="METRIC") METRIC
option(value="IMPERIAL") IMPERIAL
p
| Note, #[tt units] sets both the machine default units and the
| units used in motor configuration. GCode #[tt program-start],
| set below, may also change the default machine units.
fieldset
h2 Probing safety prompts

View File

@@ -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 <http://www.gnu.org/licenses/>. //
//- //
//- 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 //
//- <http://www.gnu.org/licenses/>. //
//- //
//- For information regarding this software email: //
//- "Joseph Coffland" <joseph@buildbotics.com> //
//- //
//-/////////////////////////////////////////////////////////////////////////////
script#templated-input-template(type="text/x-template")
.pure-control-group(class="tmpl-input-{{name}}", :title="title")
label(:for="name") {{name}}

View File

@@ -1,35 +1,6 @@
################################################################################
# #
# 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 <http://www.gnu.org/licenses/>. #
# #
# 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 #
# <http://www.gnu.org/licenses/>. #
# #
# For information regarding this software email: #
# "Joseph Coffland" <joseph@buildbotics.com> #
# #
################################################################################
import os
import time
import bbctrl
class Ctrl(object):
def __init__(self, args, ioloop, id):
self.args = args

View File

@@ -82,9 +82,6 @@ class State(object):
self.set_callback(str(i) + 'latch_velocity',
lambda name, i = i: self.motor_latch_velocity(i))
#self.set_callback('metric', lambda name: 1 if self.is_metric() else 0)
#self.set_callback('imperial', lambda name: 0 if self.is_metric() else 1)
self.reset()
self.load_files()
@@ -100,11 +97,6 @@ class State(object):
if not 'metric' in self.vars: self.set('metric', metric)
if not 'imperial' in self.vars: self.set('imperial', not metric)
# Bit diameter for probing
diameter = self.ctrl.config.get('probe-diameter', 6.35)
self.log.info('INIT Diameter %f' % diameter)
self.set('bitDiameter',diameter)
def reset(self):
# Unhome all motors

View File

@@ -282,6 +282,12 @@ span.unit
// The jogging buttons, etc.
.control-buttons button
font-size 150%
width 100px
height 100px
.jog-units
font-size initial
margin-left 5px
&:first-child
margin 0.5em 0
@@ -436,7 +442,7 @@ span.unit
min-width 8em
width 100%
.mach_units
.units
padding 0
select

View File

@@ -1,11 +1,10 @@
<script lang="ts">
import WifiConnectionDialog from "../dialogs/WifiConnectionDialog.svelte";
import ChangeHostnameDialog from "../dialogs/ChangeHostnameDialog.svelte";
import WifiConnectionDialog from "$dialogs/WifiConnectionDialog.svelte";
import ChangeHostnameDialog from "$dialogs/ChangeHostnameDialog.svelte";
import Button, { Label } from "@smui/button";
import List, { Item, Graphic, Text, Meta } from "@smui/list";
import Card from "@smui/card";
import { networkInfo } from "../lib/NetworkInfo";
import type { WifiNetwork } from "../lib/NetworkInfo";
import { networkInfo, type WifiNetwork } from "$lib/NetworkInfo";
let changeHostnameDialog = {
open: false,

View File

@@ -1,6 +0,0 @@
<script lang="ts">
import HomeMachineDialog from "../dialogs/HomeMachineDialog.svelte";
import { HomeMachine } from "../lib/DialogProps";
</script>
<HomeMachineDialog {...$HomeMachine} />

View File

@@ -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
//

View File

@@ -0,0 +1,38 @@
<script lang="ts" context="module">
import HomeMachineDialog from "$dialogs/HomeMachineDialog.svelte";
import ProbeDialog from "$dialogs/ProbeDialog.svelte";
import {
HomeMachineProps,
ProbeProps,
type HomeMachinePropsType,
type ProbePropsType,
} from "$dialogs/DialogProps";
export function showDialog(
dialog: "HomeMachine",
props: Omit<HomeMachinePropsType, "open">
);
export function showDialog(
dialog: "Probe",
props: Omit<ProbePropsType, "open">
);
export function showDialog(dialog: string, props: any) {
switch (dialog) {
case "HomeMachine":
HomeMachineProps.set({ ...props, open: true });
break;
case "Probe":
ProbeProps.set({ ...props, open: true });
break;
default:
throw new Error(`Unknown dialog '${dialog}`);
}
}
</script>
<HomeMachineDialog {...$HomeMachineProps} />
<ProbeDialog {...$ProbeProps} />

View File

@@ -0,0 +1,13 @@
import { writable } from "svelte/store";
export const HomeMachineProps = writable<HomeMachinePropsType>();
export type HomeMachinePropsType = {
open: boolean,
home: () => void
}
export const ProbeProps = writable<ProbePropsType>();
export type ProbePropsType = {
open: boolean,
probeType: "xyz" | "z"
};

View File

@@ -0,0 +1,293 @@
<script type="ts" context="module">
import Dialog, { Title, Content, Actions } from "@smui/dialog";
import Button, { Label } from "@smui/button";
import { get, writable, type Writable } from "svelte/store";
import { Config } from "$lib/ConfigStore";
import { waitForChange } from "$lib/StoreHelpers";
import { ControllerMethods } from "$lib/RegisterControllerMethods";
import { tick } from "svelte";
type Stage =
| "None"
| "TestingProbe"
| "TestingProbeComplete"
| "GetToolDiameter"
| "Probing"
| "ProbingFailed"
| "ProbingComplete";
const cancelled = writable(false);
const probingActive = writable(false);
const probeContacted = writable(false);
const probingStarted = writable(false);
const probingFailed = writable(false);
const probingComplete = writable(false);
const userAcknowledged = writable(false);
export function handleControllerStateUpdate(state: Record<string, any>) {
if (!get(probingActive)) {
return;
}
switch (true) {
case state.pw === 0:
probeContacted.set(true);
break;
case state.log?.msg === "Switch not found":
probingFailed.set(true);
break;
case state.cycle !== "idle":
probingStarted.set(true);
break;
case state.cycle === "idle":
if (get(probingStarted)) {
probingStarted.set(false);
probingComplete.set(true);
}
break;
}
}
</script>
<script type="ts">
export let open;
export let probeType: "xyz" | "z";
let stage: Stage = "None";
let toolDiameter;
let confirmButton = {
label: "Continue",
disabled: false,
allowClose: false,
};
$: showPrompts = $Config.settings?.["probing-prompts"];
$: clearFlags(stage);
$: updateConfirmButton();
$: if (open) {
// Svelte appears not to like it when you invoke
// an async function from a reactive statement
requestAnimationFrame(begin);
}
function updateConfirmButton() {
confirmButton.label = "Continue";
confirmButton.disabled = false;
confirmButton.allowClose = false;
switch (stage) {
case "TestingProbe":
case "Probing":
confirmButton.disabled = true;
break;
case "ProbingComplete":
confirmButton.disabled = true;
confirmButton.label = "Done";
confirmButton.allowClose = true;
break;
}
}
async function begin() {
try {
$probingActive = true;
assertValidProbeType();
if (showPrompts) {
stage = "TestingProbe";
await cancellableSignal(probeContacted);
stage = "TestingProbeComplete";
await cancellableSignal(userAcknowledged);
}
if (probeType === "xyz") {
stage = "GetToolDiameter";
await cancellableSignal(userAcknowledged);
}
do {
stage = "Probing";
executeProbe(probeType, toolDiameter);
await cancellableSignal(probingComplete, probingFailed);
if ($probingFailed) {
stage = "ProbingFailed";
await cancellableSignal(userAcknowledged);
}
} while (!$probingComplete);
stage = "ProbingComplete";
await cancellableSignal(userAcknowledged);
if (probeType === "xyz") {
ControllerMethods.goto_zero(1, 1, 0, 0);
}
} catch (err) {
if (err.message !== "cancelled") {
console.error("Error during probing:", err);
}
} finally {
$probingActive = false;
stage = "None";
clearFlags();
}
}
async function cancellableSignal<T>(...writables: Array<Writable<T>>) {
await Promise.race([
...writables.map((writable) => waitForChange(writable)),
waitForChange(cancelled),
]);
if ($cancelled) {
throw new Error("cancelled");
}
}
function clearFlags(foo: string = "") {
$cancelled = false;
$probeContacted = false;
$probingStarted = false;
$probingFailed = false;
$probingComplete = false;
$userAcknowledged = false;
}
function assertValidProbeType() {
switch (probeType) {
case "xyz":
case "z":
break;
default:
throw new Error(`Invalid probe type: ${probeType}`);
}
}
function executeProbe(probeType: "xyz" | "z", toolDiameter: number) {
const probeBlockWidth = $Config.probe["probe-xdim"];
const probeBlockLength = $Config.probe["probe-ydim"];
const probeBlockHeight = $Config.probe["probe-zdim"];
const slowSeek = $Config.probe["probe-slow-seek"];
const fastSeek = $Config.probe["probe-fast-seek"];
const zLift = 1;
const xOffset = probeBlockWidth + toolDiameter / 2.0;
const yOffset = probeBlockLength + toolDiameter / 2.0;
const zOffset = probeBlockHeight;
if (probeType === "z") {
ControllerMethods.send(`
G21
G92 Z0
G38.2 Z -25.4 F${fastSeek}
G91 G1 Z 1
G38.2 Z -2 F${slowSeek}
G92 Z ${zOffset}
G91 G0 Z 3
M2
`);
} else {
// After probing Z, we want to drop the bit down:
// Ideally, 12.7mm/0.5in
// And we don't want to be more than 75% down on the probe block
// Also, add zlift to compensate for the fact that we lift after probing Z
const plunge = Math.min(12.7, zOffset * 0.75) + zLift;
ControllerMethods.send(`
G21
G92 X0 Y0 Z0
G38.2 Z -25 F${fastSeek}
G91 G1 Z 1
G38.2 Z -2 F${slowSeek}
G92 Z ${zOffset}
G91 G0 Z ${zLift}
G91 G0 X 20
G91 G0 Z ${-plunge}
G38.2 X -20 F${fastSeek}
G91 G1 X 1
G38.2 X -2 F${slowSeek}
G92 X ${xOffset}
G91 G0 X 1
G91 G0 Y 20
G91 G0 X -20
G38.2 Y -20 F${fastSeek}
G91 G1 Y 1
G38.2 Y -2 F${slowSeek}
G92 Y ${yOffset}
G91 G0 Y 3
G91 G0 Z 25
M2
`);
}
}
</script>
<Dialog
bind:open
scrimClickAction=""
aria-labelledby="simple-title"
aria-describedby="simple-content"
>
<Title id="simple-title">Probe {probeType}</Title>
<Content id="simple-content">
{#if stage === "TestingProbe" || stage === "TestingProbeComplete"}
<p>Attach the probe magnet to the collet.</p>
<p>Touch the probe block to the bit.</p>
{#if stage === "TestingProbe"}
<p>Waiting for probe contact...</p>
{:else}
<p>Probe contact detected!</p>
{/if}
{:else if stage === "GetToolDiameter"}
<label for="tool-diameter">Tool Diameter (mm)</label>
<input id="tool-diameter" bind:value={toolDiameter} />
{:else if stage === "Probing"}
<p>Probing in progress...</p>
{:else if stage === "ProbingFailed"}
<p>Could not find the probe block during probing!</p>
<p>
Make sure the tip of the bit is about 1/4" (6mm) above the probe block,
and try again.
</p>
{:else if stage === "ProbingComplete"}
<p>Don't forget to put away the probe!</p>
<p>The machine will now move to the XY origin.</p>
<p>Watch your hands!</p>
{/if}
</Content>
<Actions>
<Button on:click={() => ($cancelled = true)}>
<Label>Cancel</Label>
</Button>
<Button
defaultAction
data-mdc-dialog-action={confirmButton.allowClose ? "close" : ""}
disabled={confirmButton.disabled}
on:click={() => ($userAcknowledged = true)}
>
<Label>
{confirmButton.label}
</Label>
</Button>
</Actions>
</Dialog>

View File

@@ -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 = "";

View File

@@ -0,0 +1,7 @@
import { writable } from "svelte/store";
export const Config = writable<Record<string, any>>({});
export function handleConfigUpdate(config: Record<string, any>) {
Config.set(config);
}

View File

@@ -1,16 +0,0 @@
import { writable } from "svelte/store";
type HomeMachine = {
open: boolean,
home: () => any
}
export type DialogPropsTypes = {
HomeMachine: HomeMachine
}
export const HomeMachine = writable<HomeMachine>();
export default {
HomeMachine
};

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -0,0 +1,18 @@
import { get, type Writable } from "svelte/store";
export function listenForChange<T>(writable: Writable<T>, cb: (value: T) => void) {
const priorValue = get(writable);
const unsubscribe = writable.subscribe((value) => {
if (value !== priorValue) {
unsubscribe();
cb(value);
}
});
}
export function waitForChange<T>(writable: Writable<T>): Promise<T> {
return new Promise((resolve) => {
listenForChange(writable, (value) => resolve(value));
});
}

View File

@@ -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<string, any>) {
switch (component) {
@@ -19,17 +20,10 @@ export function createComponent(component: string, target: HTMLElement, props: R
}
}
export function showDialog<T extends keyof typeof DialogProps>(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,
};

View File

@@ -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.

View File

@@ -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: {