Integrated eslint and reformatted all of the JS/TS

This commit is contained in:
David Carley
2022-09-02 00:04:31 +00:00
parent 868258cfa7
commit c6a3732750
49 changed files with 8535 additions and 5675 deletions

82
.eslintrc.yml Normal file
View File

@@ -0,0 +1,82 @@
env:
es2021: true
node: true
browser: true
extends:
- eslint:recommended
- plugin:@typescript-eslint/recommended
overrides: []
parser: "@typescript-eslint/parser"
parserOptions:
ecmaVersion: latest
sourceType: module
plugins:
- "@typescript-eslint"
globals:
Vue: readonly
THREE: readonly
SvelteComponents: readonly
$: readonly
Clusterize: readonly
SockJS: readonly
rules:
indent:
- off
"@typescript-eslint/indent":
- error
- 4
linebreak-style:
- error
- unix
quotes:
- error
- double
- allowTemplateLiterals: true
avoidEscape: true
semi:
- error
- always
"@typescript-eslint/no-explicit-any":
- off
"@typescript-eslint/no-unused-vars":
- error
- argsIgnorePattern: _.*
no-unused-vars:
- error
no-trailing-spaces:
- error
key-spacing:
- error
space-before-blocks:
- error
block-spacing:
- error
brace-style:
- error
curly:
- error
keyword-spacing:
- error
"@typescript-eslint/no-var-requires":
- off
no-multiple-empty-lines:
- error
- max: 1
func-call-spacing:
- error
- never
padding-line-between-statements:
- error
- blankLine: always
prev: function
next: function
no-var:
- error
no-unused-expressions:
- error
prefer-const:
- error
prefer-template:
- error
template-curly-spacing:
- error

2914
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,14 @@
"postinstall": "cd src/svelte-components && npm i" "postinstall": "cd src/svelte-components && npm i"
}, },
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"browserify": "^17.0.0", "browserify": "^17.0.0",
"eslint": "^8.23.0",
"eslint-config-standard-with-typescript": "^22.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.2.5",
"eslint-plugin-promise": "^6.0.1",
"jshint": "^2.13.4", "jshint": "^2.13.4",
"jstransformer-escape-html": "^1.1.0", "jstransformer-escape-html": "^1.1.0",
"jstransformer-scss": "^2.0.0", "jstransformer-scss": "^2.0.0",

View File

@@ -1,32 +1,6 @@
/******************************************************************************\ "use strict";
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'
const api = require("./api");
const merge = require("lodash.merge"); const merge = require("lodash.merge");
const config_defaults = require("../resources/onefinity_defaults.json"); const config_defaults = require("../resources/onefinity_defaults.json");
@@ -38,34 +12,32 @@ const variant_defaults = {
journeyman_x50: require("../resources/onefinity_journeyman_x50_defaults.json") journeyman_x50: require("../resources/onefinity_journeyman_x50_defaults.json")
}; };
const api = require('./api');
module.exports = { module.exports = {
template: '#admin-general-view-template', template: "#admin-general-view-template",
props: ['config', 'state'], props: ["config", "state"],
data: function () { data: function () {
return { return {
confirmReset: false, confirmReset: false,
autoCheckUpgrade: true, autoCheckUpgrade: true,
reset_variant: '' reset_variant: ""
} };
}, },
ready: function () { ready: function () {
this.autoCheckUpgrade = this.config.admin['auto-check-upgrade'] this.autoCheckUpgrade = this.config.admin["auto-check-upgrade"];
}, },
methods: { methods: {
backup: function () { backup: function () {
document.getElementById('download-target').src = '/api/config/download'; document.getElementById("download-target").src = "/api/config/download";
}, },
restore_config: function () { restore_config: function () {
// If we don't reset the form the browser may cache file if name is same // If we don't reset the form the browser may cache file if name is same
// even if contents have changed // even if contents have changed
$('.restore-config')[0].reset(); $(".restore-config")[0].reset();
$('.restore-config input').click(); $(".restore-config input").click();
}, },
restore: function (e) { restore: function (e) {
@@ -85,13 +57,16 @@ module.exports = {
} }
try { try {
await api.put('config/save', config); await api.put("config/save", config);
this.$dispatch('update'); this.$dispatch("update");
SvelteComponents.showDialog("Message", { title: "Success", message: "Configuration restored" }) SvelteComponents.showDialog("Message", {
title: "Success",
message: "Configuration restored"
});
} catch (error) { } catch (error) {
api.alert('Restore failed', error); api.alert("Restore failed", error);
}
} }
};
fileReader.readAsText(files[0]); fileReader.readAsText(files[0]);
}, },
@@ -104,40 +79,45 @@ module.exports = {
); );
try { try {
await api.put('config/save', config) await api.put("config/save", config);
this.confirmReset = false; this.confirmReset = false;
this.$dispatch('update'); this.$dispatch("update");
SvelteComponents.showDialog("Message", { title: "Success", message: "Configuration restored" }) SvelteComponents.showDialog("Message", {
title: "Success",
message: "Configuration restored"
});
} catch (err) { } catch (err) {
api.alert('Restore failed'); api.alert("Restore failed");
console.error('Restore failed', err); console.error("Restore failed", err);
} }
}, },
check: function () { check: function () {
this.$dispatch('check') this.$dispatch("check");
}, },
upgrade: function () { upgrade: function () {
this.$dispatch('upgrade') this.$dispatch("upgrade");
}, },
upload_firmware: function () { upload_firmware: function () {
// If we don't reset the form the browser may cache file if name is same // If we don't reset the form the browser may cache file if name is same
// even if contents have changed // even if contents have changed
$('.upload-firmware')[0].reset(); $(".upload-firmware")[0].reset();
$('.upload-firmware input').click(); $(".upload-firmware input").click();
}, },
upload: function (e) { upload: function (e) {
var files = e.target.files || e.dataTransfer.files; const files = e.target.files || e.dataTransfer.files;
if (!files.length) return; if (!files.length) {
this.$dispatch('upload', files[0]); return;
}
this.$dispatch("upload", files[0]);
}, },
change_auto_check_upgrade: function () { change_auto_check_upgrade: function () {
this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade; this.config.admin["auto-check-upgrade"] = this.autoCheckUpgrade;
this.$dispatch('config-changed'); this.$dispatch("config-changed");
} }
} }
} };

View File

@@ -1,77 +1,37 @@
'use strict' "use strict";
async function callApi(method, url, body) {
function api_cb(method, url, data, config) { try {
config = $.extend({ const response = await fetch(`/api/${url}`, {
type: method, method,
url: '/api/' + url, headers: {
dataType: 'json', "Content-Type": "application/json"
cache: false },
}, config); body
if (typeof data == 'object') {
config.data = JSON.stringify(data);
config.contentType = 'application/json; charset=utf-8';
}
var d = $.Deferred();
$.ajax(config).success(function (data, status, xhr) {
d.resolve(data, status, xhr);
}).error(function (xhr, status, error) {
var text = xhr.responseText;
try {text = $.parseJSON(xhr.responseText)} catch(e) {}
if (!text) text = error;
d.reject(text, xhr, status, error);
console.debug('API Error: ' + url + ': ' + text);
}); });
return d.promise(); if (response.ok) {
} return response.json();
}
throw new Error(await response.text());
} catch (error) {
console.debug(`API Error: ${url}: ${error}`);
throw error;
}
}
module.exports = { module.exports = {
get: function (url, config) { get: function (url) {
return api_cb('GET', url, undefined, config); return callApi("GET", url);
}, },
put: function(url, body = undefined) {
put: function(url, data, config) { return callApi("PUT", url, body);
return api_cb('PUT', url, data, config);
}, },
delete: function (url) {
post: function(url, data, config) { return callApi("DELETE", url);
return api_cb('POST', url, data, config);
},
upload: function(url, data, config) {
config = $.extend({
processData: false,
contentType: false,
cache: false,
data: data
}, config);
return api_cb('PUT', url, undefined, config);
},
'delete': function (url, config) {
return api_cb('DELETE', url, undefined, config);
},
alert: function (msg, error) {
if (typeof error != 'undefined') {
if (typeof error.message != 'undefined')
msg += '\n' + error.message;
else msg += '\n' + JSON.stringify(error);
} }
};
alert(msg);
}
}

View File

@@ -23,8 +23,7 @@ function is_newer_version(current, latest) {
const patch = latestParts[3] - currentParts[3]; const patch = latestParts[3] - currentParts[3];
// If current is a pre-release, and latest is a release // If current is a pre-release, and latest is a release
const betaToRelease = const betaToRelease = latestParts[4].length === 0 && currentParts[4].length > 0;
latestParts[4].length === 0 && currentParts[4].length > 0;
switch (true) { switch (true) {
case major > 0: case major > 0:
@@ -41,24 +40,36 @@ function is_newer_version(current, latest) {
function is_object(o) { function is_object(o) {
return o !== null && typeof o == "object"; return o !== null && typeof o == "object";
} }
function is_array(o) { function is_array(o) {
return Array.isArray(o); return Array.isArray(o);
} }
function update_array(dst, src) { function update_array(dst, src) {
while (dst.length) dst.pop(); while (dst.length) {
for (var i = 0; i < src.length; i++) Vue.set(dst, i, src[i]); dst.pop();
}
for (let i = 0; i < src.length; i++) {
Vue.set(dst, i, src[i]);
}
}
function hasOwnProperty(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
} }
function update_object(dst, src, remove) { function update_object(dst, src, remove) {
var props, index, key, value; let props, index, key, value;
if (remove) { if (remove) {
props = Object.getOwnPropertyNames(dst); props = Object.getOwnPropertyNames(dst);
for (index in props) { for (index in props) {
key = props[index]; key = props[index];
if (!src.hasOwnProperty(key)) Vue.delete(dst, key); if (!hasOwnProperty(src, key)) {
Vue.delete(dst, key);
}
} }
} }
@@ -67,11 +78,13 @@ function update_object(dst, src, remove) {
key = props[index]; key = props[index];
value = src[key]; value = src[key];
if (is_array(value) && dst.hasOwnProperty(key) && is_array(dst[key])) if (is_array(value) && hasOwnProperty(dst, key) && is_array(dst[key])) {
update_array(dst[key], value); update_array(dst[key], value);
else if (is_object(value) && dst.hasOwnProperty(key) && is_object(dst[key])) } else if (is_object(value) && hasOwnProperty(dst, key) && is_object(dst[key])) {
update_object(dst[key], value, remove); update_object(dst[key], value, remove);
else Vue.set(dst, key, value); } else {
Vue.set(dst, key, value);
}
} }
} }
@@ -181,8 +194,9 @@ module.exports = new Vue({
error: function (msg) { error: function (msg) {
// Honor user error blocking // Honor user error blocking
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000) if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000) {
return; return;
}
// Wait at least 1 sec to pop up repeated errors // Wait at least 1 sec to pop up repeated errors
if (1 < msg.repeat && Date.now() - msg.ts < 1000) { if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
@@ -225,9 +239,12 @@ module.exports = new Vue({
this.errorShow = false; this.errorShow = false;
}, },
toggle_video: function (e) { toggle_video: function () {
if (this.video_size == "small") this.video_size = "large"; if (this.video_size == "small") {
else if (this.video_size == "large") this.video_size = "small"; this.video_size = "large";
} else if (this.video_size == "large") {
this.video_size = "small";
}
cookie.set("video-size", this.video_size); cookie.set("video-size", this.video_size);
}, },
@@ -238,8 +255,11 @@ module.exports = new Vue({
}, },
estop: function () { estop: function () {
if (this.state.xx == "ESTOPPED") api.put("clear"); if (this.state.xx == "ESTOPPED") {
else api.put("estop"); api.put("clear");
} else {
api.put("estop");
}
}, },
upgrade_confirmed: async function () { upgrade_confirmed: async function () {
@@ -302,8 +322,10 @@ module.exports = new Vue({
if (!this.checkedUpgrade) { if (!this.checkedUpgrade) {
this.checkedUpgrade = true; this.checkedUpgrade = true;
var check = this.config.admin["auto-check-upgrade"]; const check = this.config.admin["auto-check-upgrade"];
if (typeof check == "undefined" || check) this.$emit("check"); if (typeof check == "undefined" || check) {
this.$emit("check");
}
} }
SvelteComponents.handleConfigUpdate(this.config); SvelteComponents.handleConfigUpdate(this.config);
@@ -328,10 +350,7 @@ module.exports = new Vue({
if (typeof this.sid == "undefined") { if (typeof this.sid == "undefined") {
this.sid = e.data.sid; this.sid = e.data.sid;
} else if (this.sid != e.data.sid) { } else if (this.sid != e.data.sid) {
if ( if (this.hostname && location.hostname !== "localhost") {
typeof this.hostname !== "undefined" &&
location.hostname !== "localhost"
) {
location.hostname = this.hostname; location.hostname = this.hostname;
} }
@@ -362,16 +381,18 @@ module.exports = new Vue({
}, },
parse_hash: function () { parse_hash: function () {
var hash = location.hash.substr(1); const hash = location.hash.substr(1);
if (!hash.trim().length) { if (!hash.trim().length) {
location.hash = "control"; location.hash = "control";
return; return;
} }
var parts = hash.split(":"); const parts = hash.split(":");
if (parts.length == 2) this.index = parts[1]; if (parts.length == 2) {
this.index = parts[1];
}
this.currentView = parts[0]; this.currentView = parts[0];
}, },
@@ -402,12 +423,17 @@ module.exports = new Vue({
}, },
close_messages: function (action) { close_messages: function (action) {
if (action == "stop") api.put("stop"); if (action == "stop") {
if (action == "continue") api.put("unpause"); api.put("stop");
}
if (action == "continue") {
api.put("unpause");
}
// Acknowledge messages // Acknowledge messages
if (this.state.messages.length) { if (this.state.messages.length) {
var id = this.state.messages.slice(-1)[0].id; const id = this.state.messages.slice(-1)[0].id;
api.put("message/" + id + "/ack"); api.put("message/" + id + "/ack");
} }
}, },

View File

@@ -1,65 +1,37 @@
/******************************************************************************\ "use strict";
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 = { module.exports = {
template: '#axis-control-template', template: "#axis-control-template",
props: ['axes', 'colors', 'enabled', 'adjust', 'step'], props: ["axes", "colors", "enabled", "adjust", "step"],
methods: { methods: {
jog: function (axis, ring, direction) { jog: function (axis, ring, direction) {
var value = direction * this.value(ring); const value = direction * this.value(ring);
this.$dispatch(this.step ? 'step' : 'jog', this.axes[axis], value); this.$dispatch(this.step ? "step" : "jog", this.axes[axis], value);
}, },
back2zero: function(axis0,axis1) { back2zero: function(axis0,axis1) {
this.$dispatch('back2zero',this.axes[axis0],this.axes[axis1]) this.$dispatch("back2zero",this.axes[axis0],this.axes[axis1]);
}, },
release: function (axis) { release: function (axis) {
if (!this.step) this.$dispatch('jog', this.axes[axis], 0) if (!this.step) {
this.$dispatch("jog", this.axes[axis], 0);
}
}, },
value: function (ring) { value: function (ring) {
var adjust = [0.01, 0.1, 1][this.adjust]; const adjust = [0.01, 0.1, 1][this.adjust];
if (this.step) return adjust * [0.1, 1, 10, 100][ring]; if (this.step) {
return adjust * [0.1, 1, 10, 100][ring];
}
return adjust * [0.1, 0.25, 0.5, 1][ring]; return adjust * [0.1, 0.25, 0.5, 1][ring];
}, },
text: function (ring) { text: function (ring) {
var value = this.value(ring) * (this.step ? 1 : 100); let value = this.value(ring) * (this.step ? 1 : 100);
value = parseFloat(value.toFixed(3)); value = parseFloat(value.toFixed(3));
return value + (this.step ? '' : '%'); return value + (this.step ? "" : "%");
} }
} }
} };

View File

@@ -1,17 +1,40 @@
'use strict' "use strict";
module.exports = { module.exports = {
props: ['state', 'config'], props: ["state", "config"],
computed: { computed: {
metric: function () { this.$root.display_units === "METRIC" }, metric: function () {
x: function () { return this._compute_axis('x') }, return this.$root.display_units === "METRIC";
y: function () { return this._compute_axis('y') }, },
z: function () { return this._compute_axis('z') },
a: function () { return this._compute_axis('a') }, x: function () {
b: function () { return this._compute_axis('b') }, return this._compute_axis("x");
c: function () { return this._compute_axis('c') }, },
axes: function () { return this._compute_axes() }
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: { methods: {
@@ -22,98 +45,115 @@ module.exports = {
}, },
_length_str: function (value) { _length_str: function (value) {
return this._convert_length(value).toLocaleString() + return this._convert_length(value).toLocaleString() + (this.metric ? " mm" : " in");
(this.metric ? ' mm' : ' in');
}, },
_compute_axis: function (axis) { _compute_axis: function (axis) {
var abs = this.state[axis + 'p'] || 0; const abs = this.state[`${axis}p`] || 0;
var off = this.state['offset_' + axis]; const off = this.state[`offset_${axis}`];
var motor_id = this._get_motor_id(axis); const motor_id = this._get_motor_id(axis);
var motor = motor_id == -1 ? {} : this.config.motors[motor_id]; const motor = motor_id == -1 ? {} : this.config.motors[motor_id];
var enabled = typeof motor.enabled != 'undefined' && motor.enabled; const enabled = typeof motor.enabled != "undefined" && motor.enabled;
var homingMode = motor['homing-mode'] const homingMode = motor["homing-mode"];
var homed = this.state[motor_id + 'homed']; const homed = this.state[`${motor_id}homed`];
var min = this.state[motor_id + 'tn']; const min = this.state[`${motor_id}tn`];
var max = this.state[motor_id + 'tm']; const max = this.state[`${motor_id}tm`];
var dim = max - min; const dim = max - min;
var pathMin = this.state['path_min_' + axis]; const pathMin = this.state[`path_min_${axis}`];
var pathMax = this.state['path_max_' + axis]; const pathMax = this.state[`path_max_${axis}`];
var pathDim = pathMax - pathMin; const pathDim = pathMax - pathMin;
var under = pathMin + off < min; const under = pathMin + off < min;
var over = max < pathMax + off; const over = max < pathMax + off;
var klass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis; let klass = `${homed ? "homed" : "unhomed"} axis-${axis}`;
var state = 'UNHOMED'; let state = "UNHOMED";
var icon = 'question-circle'; let icon = "question-circle";
var fault = this.state[motor_id + 'df'] & 0x1f; const fault = this.state[`${motor_id}df`] & 0x1f;
var shutdown = this.state.power_shutdown; const shutdown = this.state.power_shutdown;
var title; let title;
var ticon = 'question-circle'; let ticon = "question-circle";
var tstate = 'NO FILE'; let tstate = "NO FILE";
var toolmsg; let toolmsg;
var tklass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis; let tklass = `${homed ? "homed" : "unhomed"} axis-${axis}`;
if (fault || shutdown) { if (fault || shutdown) {
state = shutdown ? 'SHUTDOWN' : 'FAULT'; state = shutdown ? "SHUTDOWN" : "FAULT";
klass += ' error'; klass += " error";
icon = 'exclamation-circle'; icon = "exclamation-circle";
} else if (homed) { } else if (homed) {
state = 'HOMED'; state = "HOMED";
icon = 'check-circle'; icon = "check-circle";
} }
if (0 < dim && dim < pathDim) { if (0 < dim && dim < pathDim) {
tstate = 'NO FIT'; tstate = "NO FIT";
tklass += ' error'; tklass += " error";
ticon = 'ban'; ticon = "ban";
} else { } else {
if (over || under) { if (over || under) {
tstate = over ? 'OVER' : 'UNDER'; tstate = over ? "OVER" : "UNDER";
tklass += ' warn'; tklass += " warn";
ticon = 'exclamation-circle'; ticon = "exclamation-circle";
} else { } else {
tstate = 'OK'; tstate = "OK";
ticon = 'check-circle'; ticon = "check-circle";
} }
} }
switch (state) { switch (state) {
case 'UNHOMED': title = 'Click the home button to home axis.'; break; case "UNHOMED":
case 'HOMED': title = 'Axis successfuly homed.'; break; title = "Click the home button to home axis.";
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; break;
case 'SHUTDOWN': case "HOMED":
title = 'Motor power fault. All motors in shutdown. ' + title = "Axis successfuly homed.";
'See the "Power Faults" table on the "Indicators" tab for more ' + break;
'information. Reboot controller to reset.';
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.`,
].join(" ");
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.`
].join(" ");
break;
} }
switch (tstate) { switch (tstate) {
case 'OVER': case "OVER":
toolmsg = 'Caution: The current tool path file would move ' + toolmsg = [
this._length_str(pathMax + off - max) + ' above axis limit with the current offset.'; `Caution: The current tool path file would move`,
`${this._length_str(pathMax + off - max)}`,
`above axis limit with the current offset.`
].join(" ");
break; break;
case 'UNDER': case "UNDER":
toolmsg = 'Caution: The current tool path file would move ' + toolmsg = [
this._length_str(min - pathMin - off) + ' below limit with the current offset.'; `Caution: The current tool path file would move`,
`${this._length_str(min - pathMin - off)}`,
`below limit with the current offset.`
].join(" ");
break; break;
case 'NO FIT': case "NO FIT":
toolmsg = 'Warning: The current tool path dimensions (' + toolmsg = [
this._length_str(pathDim) + ') exceed axis dimensions (' + `Warning: The current tool path dimensions`,
this._length_str(dim) + ') by ' + `(${this._length_str(pathDim)}) exceed axis dimensions`,
this._length_str(pathDim - dim) + '.'; `(${this._length_str(dim)}) by ${this._length_str(pathDim - dim)}.`
].join(" ");
break; break;
default: default:
toolmsg = 'Tool path ' + axis + ' dimensions OK.'; toolmsg = `Tool path ${axis} dimensions OK.`;
break; break;
} }
@@ -139,44 +179,64 @@ module.exports = {
tstate: tstate, tstate: tstate,
toolmsg: toolmsg, toolmsg: toolmsg,
tklass: tklass tklass: tklass
} };
}, },
_get_motor_id: function (axis) { _get_motor_id: function (axis) {
for (var i = 0; i < this.config.motors.length; i++) { for (let i = 0; i < this.config.motors.length; i++) {
var motor = this.config.motors[i]; const motor = this.config.motors[i];
if (motor.axis.toLowerCase() == axis) return i; if (motor.axis.toLowerCase() == axis) {
return i;
}
} }
return -1; return -1;
}, },
_compute_axes: function () { _compute_axes: function () {
var homed = false; let homed = false;
for (var name of 'xyzabc') { for (const name of "xyzabc") {
var axis = this[name]; const axis = this[name];
if (!axis.enabled) {
continue;
}
if (!axis.homed) {
homed = false; break;
}
if (!axis.enabled) continue
if (!axis.homed) { homed = false; break }
homed = true; homed = true;
} }
var error = false; let error = false;
var warn = false; let warn = false;
if (homed) if (homed) {
for (name of 'xyzabc') { for (const name of "xyzabc") {
axis = this[name]; const axis = this[name];
if (!axis.enabled) continue; if (!axis.enabled) {
if (axis.klass.indexOf('error') != -1) error = true; continue;
if (axis.klass.indexOf('warn') != -1) warn = true;
} }
var klass = homed ? 'homed' : 'unhomed'; if (axis.klass.indexOf("error") != -1) {
if (error) klass += ' error'; error = true;
else if (warn) klass += ' warn'; }
if (axis.klass.indexOf("warn") != -1) {
warn = true;
}
}
}
let klass = homed ? "homed" : "unhomed";
if (error) {
klass += " error";
} else if (warn) {
klass += " warn";
}
if (!homed && this.ask_home) { if (!homed && this.ask_home) {
this.ask_home = false; this.ask_home = false;
@@ -188,7 +248,7 @@ module.exports = {
return { return {
homed: homed, homed: homed,
klass: klass klass: klass
};
} }
} }
} };
}

View File

@@ -1,87 +1,72 @@
/******************************************************************************\ "use strict";
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 _msg_equal(a, b) { function _msg_equal(a, b) {
return a.level == b.level && a.source == b.source && a.where == b.where && return a.level == b.level
a.msg == b.msg; && a.source == b.source
&& a.where == b.where
&&a.msg == b.msg;
} }
// Shared among all instances // Shared among all instances
var messages = []; const messages = [];
module.exports = { module.exports = {
template: '#console-template', template: "#console-template",
data: function () { data: function () {
return {messages: messages} return {
messages
};
}, },
events: { events: {
log: function (msg) { log: function (msg) {
// There may be multiple instances of this module so ignore messages // There may be multiple instances of this module so ignore messages
// that have already been processed. // that have already been processed.
if (msg.logged) return; if (msg.logged) {
return;
}
msg.logged = true; msg.logged = true;
// Make sure we have a message level // Make sure we have a message level
msg.level = msg.level || 'info'; msg.level = msg.level || "info";
// Add to message log and count and collapse repeats // Add to message log and count and collapse repeats
var repeat = messages.length && _msg_equal(msg, messages[0]); const repeat = messages.length && _msg_equal(msg, messages[0]);
if (repeat) messages[0].repeat++; if (repeat) {
else { messages[0].repeat++;
} else {
msg.repeat = msg.repeat || 1; msg.repeat = msg.repeat || 1;
messages.unshift(msg); messages.unshift(msg);
while (256 < messages.length) messages.pop(); while (256 < messages.length) {
messages.pop();
}
} }
msg.ts = Date.now(); msg.ts = Date.now();
// Write message to browser console for debugging // Write message to browser console for debugging
var text = JSON.stringify(msg); const text = JSON.stringify(msg);
if (msg.level == 'error' || msg.level == 'critical') console.error(text); if (msg.level == "error" || msg.level == "critical") {
else if (msg.level == 'warning') console.warn(text); console.error(text);
else if (msg.level == 'debug' && console.debug) console.debug(text); } else if (msg.level == "warning") {
else console.log(text); console.warn(text);
} else if (msg.level == "debug" && console.debug) {
console.debug(text);
} else {
console.log(text);
}
// Event on errors // Event on errors
if (msg.level == 'error' || msg.level == 'critical') if (msg.level == "error" || msg.level == "critical") {
this.$dispatch('error', msg); this.$dispatch("error", msg);
}
} }
}, },
methods: { methods: {
clear: function () {messages.splice(0, messages.length);}, clear: function () {
messages.splice(0, messages.length);
},
} }
} };

View File

@@ -1,22 +1,22 @@
'use strict' "use strict";
var api = require('./api'); const api = require("./api");
var cookie = require('./cookie')('bbctrl-'); const cookie = require("./cookie")("bbctrl-");
module.exports = { module.exports = {
template: '#control-view-template', template: "#control-view-template",
props: ['config', 'template', 'state'], props: ["config", "template", "state"],
data: function () { data: function () {
return { return {
current_time: "", current_time: "",
mach_units: this.$root.state.metric ? "METRIC" : "IMPERIAL", mach_units: this.$root.state.metric ? "METRIC" : "IMPERIAL",
mdi: '', mdi: "",
last_file: undefined, last_file: undefined,
last_file_time: undefined, last_file_time: undefined,
toolpath: {}, toolpath: {},
toolpath_progress: 0, toolpath_progress: 0,
axes: 'xyzabc', axes: "xyzabc",
history: [], history: [],
speed_override: 1, speed_override: 1,
feed_override: 1, feed_override: 1,
@@ -34,20 +34,20 @@ module.exports = {
large: 5, large: 5,
} }
}, },
jog_incr: localStorage.getItem("jog_incr") || 'small', jog_incr: localStorage.getItem("jog_incr") || "small",
jog_step: cookie.get_bool('jog-step'), jog_step: cookie.get_bool("jog-step"),
jog_adjust: parseInt(cookie.get('jog-adjust', 2)), jog_adjust: parseInt(cookie.get("jog-adjust", 2)),
deleteGCode: false, deleteGCode: false,
tab: 'auto', tab: "auto",
ask_home: true, ask_home: true,
showGcodeMessage: false showGcodeMessage: false
} };
}, },
components: { components: {
'axis-control': require('./axis-control'), "axis-control": require("./axis-control"),
'path-viewer': require('./path-viewer'), "path-viewer": require("./path-viewer"),
'gcode-viewer': require('./gcode-viewer') "gcode-viewer": require("./gcode-viewer")
}, },
watch: { watch: {
@@ -55,31 +55,31 @@ module.exports = {
localStorage.setItem("jog_incr", value); localStorage.setItem("jog_incr", value);
}, },
'state.metric': { "state.metric": {
handler: function (metric) { handler: function (metric) {
this.mach_units = metric this.mach_units = metric
? 'METRIC' ? "METRIC"
: 'IMPERIAL'; : "IMPERIAL";
}, },
immediate: true immediate: true
}, },
'state.line': function () { "state.line": function () {
if (this.mach_state != 'HOMING') { if (this.mach_state != "HOMING") {
this.$broadcast('gcode-line', this.state.line); this.$broadcast("gcode-line", this.state.line);
} }
}, },
'state.selected_time': function () { "state.selected_time": function () {
this.load(); this.load();
}, },
jog_step: function () { jog_step: function () {
cookie.set_bool('jog-step', this.jog_step); cookie.set_bool("jog-step", this.jog_step);
}, },
jog_adjust: function () { jog_adjust: function () {
cookie.set('jog-adjust', this.jog_adjust); cookie.set("jog-adjust", this.jog_adjust);
} }
}, },
@@ -99,63 +99,61 @@ module.exports = {
}, },
mach_state: function () { mach_state: function () {
var cycle = this.state.cycle; const cycle = this.state.cycle;
var state = this.state.xx; const state = this.state.xx;
if (typeof cycle != 'undefined' && state != 'ESTOPPED' && if (state != "ESTOPPED" && (cycle == "jogging" || cycle == "homing")) {
(cycle == 'jogging' || cycle == 'homing')) {
return cycle.toUpperCase(); return cycle.toUpperCase();
} }
return state || '' return state || "";
}, },
pause_reason: function () { pause_reason: function () {
return this.state.pr return this.state.pr;
}, },
is_running: function () { is_running: function () {
return this.mach_state == 'RUNNING' || this.mach_state == 'HOMING'; return this.mach_state == "RUNNING" || this.mach_state == "HOMING";
}, },
is_stopping: function () { is_stopping: function () {
return this.mach_state == 'STOPPING' return this.mach_state == "STOPPING";
}, },
is_holding: function () { is_holding: function () {
return this.mach_state == 'HOLDING' return this.mach_state == "HOLDING";
}, },
is_ready: function () { is_ready: function () {
return this.mach_state == 'READY' return this.mach_state == "READY";
}, },
is_idle: function () { is_idle: function () {
return this.state.cycle == 'idle' return this.state.cycle == "idle";
}, },
is_paused: function () { is_paused: function () {
return this.is_holding && return this.is_holding && (this.pause_reason == "User pause" || this.pause_reason == "Program pause");
(this.pause_reason == 'User pause' ||
this.pause_reason == 'Program pause')
}, },
can_mdi: function () { can_mdi: function () {
return this.is_idle || this.state.cycle == 'mdi' return this.is_idle || this.state.cycle == "mdi";
}, },
can_set_axis: function () { can_set_axis: function () {
return this.is_idle return this.is_idle;
// TODO allow setting axis position during pause // TODO allow setting axis position during pause
return this.is_idle || this.is_paused // return this.is_idle || this.is_paused;
}, },
message: function () { message: function () {
if (this.mach_state == 'ESTOPPED') { if (this.mach_state == "ESTOPPED") {
return this.state.er; return this.state.er;
} }
if (this.mach_state == 'HOLDING') { if (this.mach_state == "HOLDING") {
return this.state.pr; return this.state.pr;
} }
@@ -163,15 +161,15 @@ module.exports = {
return this.state.messages.slice(-1)[0].text; return this.state.messages.slice(-1)[0].text;
} }
return ''; return "";
}, },
highlight_state: function () { highlight_state: function () {
return this.mach_state == 'ESTOPPED' || this.mach_state == 'HOLDING'; return this.mach_state == "ESTOPPED" || this.mach_state == "HOLDING";
}, },
plan_time: function () { plan_time: function () {
return this.state.plan_time return this.state.plan_time;
}, },
plan_time_remaining: function () { plan_time_remaining: function () {
@@ -179,16 +177,16 @@ module.exports = {
return 0; return 0;
} }
return this.toolpath.time - this.plan_time return this.toolpath.time - this.plan_time;
}, },
eta: function () { eta: function () {
if (this.mach_state != 'RUNNING') { if (this.mach_state != "RUNNING") {
return ''; return "";
} }
var remaining = this.plan_time_remaining; const remaining = this.plan_time_remaining;
var d = new Date(); const d = new Date();
d.setSeconds(d.getSeconds() + remaining); d.setSeconds(d.getSeconds() + remaining);
return d.toLocaleString(); return d.toLocaleString();
}, },
@@ -198,16 +196,16 @@ module.exports = {
return 0; return 0;
} }
var p = this.plan_time / this.toolpath.time; const p = this.plan_time / this.toolpath.time;
return p < 1 ? p : 1; return Math.max(1, p);
} }
}, },
events: { events: {
jog: function (axis, power) { jog: function (axis, power) {
var data = { ts: new Date().getTime() }; const data = { ts: new Date().getTime() };
data[axis] = power; data[axis] = power;
api.put('jog', data); api.put("jog", data);
}, },
back2zero: function (axis0, axis1) { back2zero: function (axis0, axis1) {
@@ -243,19 +241,19 @@ module.exports = {
methods: { methods: {
getJogIncrStyle(value) { getJogIncrStyle(value) {
const weight = `font-weight:${this.jog_incr === value ? 'bold' : 'normal'}`; const weight = `font-weight:${this.jog_incr === value ? "bold" : "normal"}`;
const color = this.jog_incr === value ? "color:#0078e7" : ""; const color = this.jog_incr === value ? "color:#0078e7" : "";
return [weight, color].join(';'); return [weight, color].join(";");
}, },
jog_fn: function (x_jog, y_jog, z_jog, a_jog) { jog_fn: function (x_jog, y_jog, z_jog, a_jog) {
const amount = this.jog_incr_amounts[this.display_units][this.jog_incr]; const amount = this.jog_incr_amounts[this.display_units][this.jog_incr];
var xcmd = "X" + x_jog * amount; const xcmd = `X${x_jog * amount}`;
var ycmd = "Y" + y_jog * amount; const ycmd = `Y${y_jog * amount}`;
var zcmd = "Z" + z_jog * amount; const zcmd = `Z${z_jog * amount}`;
var acmd = "A" + a_jog * amount; const acmd = `A${a_jog * amount}`;
this.send(` this.send(`
G91 G91
@@ -265,12 +263,12 @@ module.exports = {
}, },
send: function (msg) { send: function (msg) {
this.$dispatch('send', msg) this.$dispatch("send", msg);
}, },
load: function () { load: function () {
var file_time = this.state.selected_time; const file_time = this.state.selected_time;
var file = this.state.selected; const file = this.state.selected;
if (this.last_file == file && this.last_file_time == file_time) { if (this.last_file == file && this.last_file_time == file_time) {
return; return;
} }
@@ -278,8 +276,8 @@ module.exports = {
this.last_file = file; this.last_file = file;
this.last_file_time = file_time; this.last_file_time = file_time;
this.$broadcast('gcode-load', file); this.$broadcast("gcode-load", file);
this.$broadcast('gcode-line', this.state.line); this.$broadcast("gcode-line", this.state.line);
this.toolpath_progress = 0; this.toolpath_progress = 0;
this.load_toolpath(file, file_time); this.load_toolpath(file, file_time);
}, },
@@ -297,8 +295,8 @@ module.exports = {
const toolpath = await api.get(`path/${file}`); const toolpath = await api.get(`path/${file}`);
this.toolpath_progress = toolpath.progress; this.toolpath_progress = toolpath.progress;
if (toolpath.progress === 1 || typeof toolpath.progress == 'undefined') { if (toolpath.progress === 1 || typeof toolpath.progress == "undefined") {
this.showGcodeMessage = false this.showGcodeMessage = false;
if (toolpath.bounds) { if (toolpath.bounds) {
toolpath.filename = file; toolpath.filename = file;
@@ -306,9 +304,9 @@ module.exports = {
this.toolpath = toolpath; this.toolpath = toolpath;
const state = this.$root.state; const state = this.$root.state;
for (let axis of 'xyzabc') { for (const axis of "xyzabc") {
Vue.set(state, 'path_min_' + axis, toolpath.bounds.min[axis]); Vue.set(state, `path_min_${axis}`, toolpath.bounds.min[axis]);
Vue.set(state, 'path_max_' + axis, toolpath.bounds.max[axis]); Vue.set(state, `path_max_${axis}`, toolpath.bounds.max[axis]);
} }
} }
} }
@@ -322,13 +320,13 @@ module.exports = {
this.history.unshift(this.mdi); this.history.unshift(this.mdi);
} }
this.mdi = ''; this.mdi = "";
}, },
mdi_start_pause: function () { mdi_start_pause: function () {
if (this.state.xx == 'RUNNING') { if (this.state.xx == "RUNNING") {
this.pause(); this.pause();
} else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') { } else if (this.state.xx == "STOPPING" || this.state.xx == "HOLDING") {
this.unpause(); this.unpause();
} else { } else {
this.submit_mdi(); this.submit_mdi();
@@ -339,11 +337,11 @@ module.exports = {
this.mdi = this.history[index]; this.mdi = this.history[index];
}, },
open: function (e) { open: function () {
// If we don't reset the form the browser may cache file if name is same // If we don't reset the form the browser may cache file if name is same
// even if contents have changed // even if contents have changed
$('.gcode-file-input')[0].reset(); $(".gcode-file-input")[0].reset();
$('.gcode-file-input input').click(); $(".gcode-file-input input").click();
}, },
upload: async function (e) { upload: async function (e) {
@@ -371,42 +369,42 @@ module.exports = {
file, file,
onComplete: () => { onComplete: () => {
this.last_file_time = undefined; // Force reload this.last_file_time = undefined; // Force reload
this.$broadcast('gcode-reload', file.name); this.$broadcast("gcode-reload", file.name);
} }
}); });
}, },
delete_current: function () { delete_current: function () {
if (this.state.selected) { if (this.state.selected) {
api.delete('file/' + this.state.selected); api.delete(`file/${this.state.selected}`);
} }
this.deleteGCode = false; this.deleteGCode = false;
}, },
delete_all: function () { delete_all: function () {
api.delete('file'); api.delete("file");
this.deleteGCode = false; this.deleteGCode = false;
}, },
home: function (axis) { home: function (axis) {
this.ask_home = false; this.ask_home = false;
if (typeof axis == 'undefined') { if (typeof axis == "undefined") {
api.put('home'); api.put("home");
} else if (this[axis].homingMode != 'manual') { } else if (this[axis].homingMode != "manual") {
api.put('home/' + axis); api.put(`home/${axis}`);
} else { } else {
SvelteComponents.showDialog("ManualHomeAxis", { axis }); SvelteComponents.showDialog("ManualHomeAxis", { axis });
} }
}, },
set_home: function (axis, position) { set_home: function (axis, position) {
api.put('home/' + axis + '/set', { position: parseFloat(position) }); api.put(`home/${axis}/set`, { position: parseFloat(position) });
}, },
unhome: function (axis) { unhome: function (axis) {
api.put('home/' + axis + '/clear'); api.put(`home/${axis}/clear`);
}, },
show_set_position: function (axis) { show_set_position: function (axis) {
@@ -422,11 +420,11 @@ module.exports = {
}, },
set_position: function (axis, position) { set_position: function (axis, position) {
api.put('position/' + axis, { 'position': parseFloat(position) }); api.put(`position/${axis}`, { "position": parseFloat(position) });
}, },
zero_all: function () { zero_all: function () {
for (var axis of 'xyzabc') { for (const axis of "xyzabc") {
if (this[axis].enabled) { if (this[axis].enabled) {
this.zero(axis); this.zero(axis);
} }
@@ -434,7 +432,7 @@ module.exports = {
}, },
zero: function (axis) { zero: function (axis) {
if (typeof axis == 'undefined') { if (typeof axis == "undefined") {
this.zero_all(); this.zero_all();
} else { } else {
this.set_position(axis, 0); this.set_position(axis, 0);
@@ -442,9 +440,9 @@ module.exports = {
}, },
start_pause: function () { start_pause: function () {
if (this.state.xx == 'RUNNING') { if (this.state.xx == "RUNNING") {
this.pause(); this.pause();
} else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') { } else if (this.state.xx == "STOPPING" || this.state.xx == "HOLDING") {
this.unpause(); this.unpause();
} else { } else {
this.start(); this.start();
@@ -452,45 +450,45 @@ module.exports = {
}, },
start: function () { start: function () {
api.put('start') api.put("start");
}, },
pause: function () { pause: function () {
api.put('pause') api.put("pause");
}, },
unpause: function () { unpause: function () {
api.put('unpause') api.put("unpause");
}, },
optional_pause: function () { optional_pause: function () {
api.put('pause/optional') api.put("pause/optional");
}, },
stop: function () { stop: function () {
api.put('stop') api.put("stop");
}, },
step: function () { step: function () {
api.put('step') api.put("step");
}, },
override_feed: function () { override_feed: function () {
api.put('override/feed/' + this.feed_override) api.put(`override/feed/${this.feed_override}`);
}, },
override_speed: function () { override_speed: function () {
api.put('override/speed/' + this.speed_override) api.put(`override/speed/${this.speed_override}`);
}, },
current: function (axis, value) { current: function (axis, value) {
var x = value / 32.0; const x = value / 32.0;
if (this.state[axis + 'pl'] == x) { if (this.state[`${axis}pl`] == x) {
return; return;
} }
var data = {}; const data = {};
data[axis + 'pl'] = x; data[`${axis}pl`] = x;
this.send(JSON.stringify(data)); this.send(JSON.stringify(data));
}, },
@@ -499,5 +497,5 @@ module.exports = {
} }
}, },
mixins: [require('./axis-vars')] mixins: [require("./axis-vars")]
} };

View File

@@ -1,69 +1,49 @@
/******************************************************************************\ "use strict";
Copyright 2018. Buildbotics LLC
All Rights Reserved.
For information regarding this software email:
Joseph Coffland
joseph@buildbotics.com
This software is free software: you clan redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 2.1 of
the License, or (at your option) any later version.
This 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 C! library. If not, see
<http://www.gnu.org/licenses/>.
\******************************************************************************/
'use strict'
module.exports = function (prefix) { module.exports = function (prefix) {
if (typeof prefix == 'undefined') prefix = ''; if (typeof prefix == "undefined") {
prefix = "";
}
var cookie = { const cookie = {
get: function (name, defaultValue) { get: function (name, defaultValue) {
var decodedCookie = decodeURIComponent(document.cookie); const decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';'); const ca = decodedCookie.split(";");
name = prefix + name + '='; name = `${prefix + name}=`;
for (var i = 0; i < ca.length; i++) { for (let i = 0; i < ca.length; i++) {
var c = ca[i]; let c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1); while (c.charAt(0) == " ") {
if (!c.indexOf(name)) return c.substring(name.length, c.length); c = c.substring(1);
}
if (!c.indexOf(name)) {
return c.substring(name.length, c.length);
}
} }
return defaultValue; return defaultValue;
}, },
set: function (name, value, days) { set: function (name, value, days) {
var offset = 2147483647; // Max value let offset = 2147483647; // Max value
if (typeof days != 'undefined') offset = days * 24 * 60 * 60 * 1000; if (typeof days != "undefined") {
var d = new Date(); offset = days * 24 * 60 * 60 * 1000;
d.setTime(d.getTime() + offset); }
var expires = 'expires=' + d.toUTCString();
document.cookie = prefix + name + '=' + value + ';' + expires + ';path=/';
},
const d = new Date();
d.setTime(d.getTime() + offset);
const expires = `expires=${d.toUTCString()}`;
document.cookie = `${prefix}${name}=${value};${expires};path=/`;
},
set_bool: function (name, value) { set_bool: function (name, value) {
cookie.set(name, value ? 'true' : 'false'); cookie.set(name, value ? "true" : "false");
}, },
get_bool: function (name, defaultValue) { get_bool: function (name, defaultValue) {
return cookie.get(name, defaultValue ? 'true' : 'false') == 'true'; return cookie.get(name, defaultValue ? "true" : "false") == "true";
}
} }
};
return cookie; return cookie;
} };

View File

@@ -1,97 +1,76 @@
/******************************************************************************\ "use strict";
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');
var entityMap = {
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
'/': '&#x2F;', '`': '&#x60;', '=': '&#x3D;'}
const entityMap = {
"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;",
"/": "&#x2F;", "`": "&#x60;", "=": "&#x3D;"};
function escapeHTML(s) { function escapeHTML(s) {
return s.replace(/[&<>"'`=\/]/g, function (c) {return entityMap[c]}) return s.replace(/[&<>"'`=\\/]/g, function (c) {
return entityMap[c];
});
} }
module.exports = { module.exports = {
template: '#gcode-viewer-template', template: "#gcode-viewer-template",
data: function () { data: function () {
return { return {
empty: true, empty: true,
file: '', file: "",
line: -1, line: -1,
scrolling: false scrolling: false
} };
}, },
events: { events: {
'gcode-load': function (file) {this.load(file)}, "gcode-load": function (file) {
'gcode-clear': function () {this.clear()}, this.load(file);
'gcode-reload': function (file) {this.reload(file)}, },
'gcode-line': function (line) {this.update_line(line)} "gcode-clear": function () {
this.clear();
},
"gcode-reload": function (file) {
this.reload(file);
},
"gcode-line": function (line) {
this.update_line(line);
}
}, },
ready: function () { ready: function () {
this.clusterize = new Clusterize({ this.clusterize = new Clusterize({
rows: [], rows: [],
scrollElem: $(this.$el).find('.clusterize-scroll')[0], scrollElem: $(this.$el).find(".clusterize-scroll")[0],
contentElem: $(this.$el).find('.clusterize-content')[0], contentElem: $(this.$el).find(".clusterize-content")[0],
no_data_text: 'GCode view...', no_data_text: "GCode view...",
callbacks: {clusterChanged: this.highlight} callbacks: {clusterChanged: this.highlight}
}); });
}, },
attached: function () { attached: function () {
if (typeof this.clusterize != 'undefined') if (typeof this.clusterize != "undefined") {
this.clusterize.refresh(true); this.clusterize.refresh(true);
}
}, },
methods: { methods: {
load: async function(file) { load: async function(file) {
if (file == this.file) return; if (file == this.file) {
return;
}
this.clear(); this.clear();
this.file = file; this.file = file;
if (!file) return; if (!file) {
return;
}
const response = await fetch(`/api/file/${file}?${Math.random()}`); const response = await fetch(`/api/file/${file}?${Math.random()}`);
const text = await response.text(); const text = await response.text();
if (text.length > 20e6) { if (text.length > 20e6) {
this.clusterize.update(['File is large - gcode view disabled']); this.clusterize.update(["File is large - gcode view disabled"]);
} else { } else {
const lines = escapeHTML(text.trimRight()) const lines = escapeHTML(text.trimRight())
.split(/[\r\n]/) .split(/[\r\n]/)
@@ -105,64 +84,77 @@ module.exports = {
Vue.nextTick(this.update_line); Vue.nextTick(this.update_line);
}, },
clear: function () { clear: function () {
this.empty = true; this.empty = true;
this.file = ''; this.file = "";
this.line = -1; this.line = -1;
this.clusterize.clear(); this.clusterize.clear();
}, },
reload: function (file) { reload: function (file) {
if (file != this.file) return; if (file != this.file) {
return;
}
this.clear(); this.clear();
this.load(file); this.load(file);
}, },
highlight: function () { highlight: function () {
var e = $(this.$el).find('.highlight'); let e = $(this.$el).find(".highlight");
if (e.length) e.removeClass('highlight'); if (e.length) {
e.removeClass("highlight");
}
e = $(this.$el).find('.ln' + this.line); e = $(this.$el).find(`.ln${this.line}`);
if (e.length) e.addClass('highlight'); if (e.length) {
e.addClass("highlight");
}
}, },
update_line: function(line) { update_line: function(line) {
if (typeof line != 'undefined') { if (typeof line != "undefined") {
if (this.line == line) return; if (this.line == line) {
return;
}
this.line = line; this.line = line;
} else {
line = this.line;
}
} else line = this.line; const totalLines = this.clusterize.getRowsAmount();
var totalLines = this.clusterize.getRowsAmount(); if (line <= 0) {
line = 1;
}
if (line <= 0) line = 1; if (totalLines < line) {
if (totalLines < line) line = totalLines; line = totalLines;
}
var e = $(this.$el).find('.clusterize-scroll'); const e = $(this.$el).find(".clusterize-scroll");
var lineHeight = e[0].scrollHeight / totalLines; const lineHeight = e[0].scrollHeight / totalLines;
var linesPerPage = Math.floor(e[0].clientHeight / lineHeight); const linesPerPage = Math.floor(e[0].clientHeight / lineHeight);
var current = e[0].scrollTop / lineHeight; const current = e[0].scrollTop / lineHeight;
var target = line - 1 - Math.floor(linesPerPage / 2); const target = line - 1 - Math.floor(linesPerPage / 2);
// Update scroll position // Update scroll position
if (!this.scrolling) { if (!this.scrolling) {
if (target < current - 20 || current + 20 < target) if (target < current - 20 || current + 20 < target) {
e[0].scrollTop = target * lineHeight; e[0].scrollTop = target * lineHeight;
} else {
else {
this.scrolling = true; this.scrolling = true;
e.animate({scrollTop: target * lineHeight}, { e.animate({scrollTop: target * lineHeight}, {
complete: function () {this.scrolling = false}.bind(this) complete: function () {
}) this.scrolling = false;
}.bind(this)
});
} }
} }
Vue.nextTick(this.highlight); Vue.nextTick(this.highlight);
} }
} }
} };

View File

@@ -1,65 +1,44 @@
/******************************************************************************\ "use strict";
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 modbus = require('./modbus.js');
const modbus = require("./modbus.js");
module.exports = { module.exports = {
template: '#indicators-template', template: "#indicators-template",
props: ['state'], props: ["state"],
computed: { computed: {
modbus_status: function () {return modbus.status_to_string(this.state.mx)}, modbus_status: function () {
return modbus.status_to_string(this.state.mx);
},
sense_error: function () { sense_error: function () {
var error = ''; let error = "";
if (this.state.motor_voltage_sense_error) error += 'Motor voltage\n'; if (this.state.motor_voltage_sense_error) {
if (this.state.motor_current_sense_error) error += 'Motor current\n'; error += "Motor voltage\n";
if (this.state.load1_sense_error) error += 'Load 1\n'; }
if (this.state.load2_sense_error) error += 'Load 2\n'; if (this.state.motor_current_sense_error) {
if (this.state.vdd_current_sense_error) error += 'Vdd current\n'; error += "Motor current\n";
}
if (this.state.load1_sense_error) {
error += "Load 1\n";
}
if (this.state.load2_sense_error) {
error += "Load 2\n";
}
if (this.state.vdd_current_sense_error) {
error += "Vdd current\n";
}
return error; return error;
} }
}, },
methods: { methods: {
is_motor_enabled: function (motor) { is_motor_enabled: function (motor) {
return typeof this.state[motor + 'me'] != 'undefined' && return typeof this.state[`${motor}me`] != "undefined" && this.state[`${motor}me`];
this.state[motor + 'me'];
}, },
get_min_pin: function (motor) { get_min_pin: function (motor) {
switch (motor) { switch (motor) {
case 0: return 3; case 0: return 3;
@@ -69,7 +48,6 @@ module.exports = {
} }
}, },
get_max_pin: function (motor) { get_max_pin: function (motor) {
switch (motor) { switch (motor) {
case 0: return 4; case 0: return 4;
@@ -79,29 +57,38 @@ module.exports = {
} }
}, },
motor_fault_class: function (motor, bit) { motor_fault_class: function (motor, bit) {
if (typeof motor == 'undefined') { if (typeof motor == "undefined") {
var status = this.state['fa']; const status = this.state["fa"];
if (typeof status == 'undefined') return 'fa-question';
return 'fa-thumbs-' + (status ? 'down error' : 'up success') if (typeof status == "undefined") {
return "fa-question";
} }
var flags = this.state[motor + 'df']; return `fa-thumbs-${status ? "down error" : "up success"}`;
if (typeof flags == 'undefined') return 'fa-question'; }
return (flags & (1 << bit)) ? 'fa-thumbs-down error' :
'fa-thumbs-up success'; const flags = this.state[`${motor}df`];
if (typeof flags == "undefined") {
return "fa-question";
}
return (flags & (1 << bit)) ? "fa-thumbs-down error" :
"fa-thumbs-up success";
}, },
motor_reset: function (motor) { motor_reset: function (motor) {
if (typeof motor == 'undefined') { if (typeof motor == "undefined") {
var cmd = ''; let cmd = "";
for (var i = 0; i < 4; i++) for (let i = 0; i < 4; i++) {
cmd += '\\$' + i + 'df=0\n'; cmd += `\\$${i}df=0\n`;
this.$dispatch('send', cmd); }
} else this.$dispatch('send', '\\$' + motor + 'df=0'); this.$dispatch("send", cmd);
} else {
this.$dispatch("send", `\\$${motor}df=0`);
} }
} }
} }
};

View File

@@ -1,177 +1,155 @@
/******************************************************************************\ "use strict";
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 = { module.exports = {
template: "#io-indicator-template", template: "#io-indicator-template",
props: ['name', 'state'], props: ["name", "state"],
computed: { computed: {
klass: function () { klass: function () {
if (this.name == 'min-switch-0') return this.get_motor_min_class(0); switch (this.name) {
if (this.name == 'min-switch-1') return this.get_motor_min_class(1); case "min-switch-0": return this.get_motor_min_class(0);
if (this.name == 'min-switch-2') return this.get_motor_min_class(2); case "min-switch-1": return this.get_motor_min_class(1);
if (this.name == 'min-switch-3') return this.get_motor_min_class(3); case "min-switch-2": return this.get_motor_min_class(2);
if (this.name == 'max-switch-0') return this.get_motor_max_class(0); case "min-switch-3": return this.get_motor_min_class(3);
if (this.name == 'max-switch-1') return this.get_motor_max_class(1); case "max-switch-0": return this.get_motor_max_class(0);
if (this.name == 'max-switch-2') return this.get_motor_max_class(2); case "max-switch-1": return this.get_motor_max_class(1);
if (this.name == 'max-switch-3') return this.get_motor_max_class(3); case "max-switch-2": return this.get_motor_max_class(2);
if (this.name == 'estop') return this.get_input_class('ew', 'et'); case "max-switch-3": return this.get_motor_max_class(3);
if (this.name == 'probe') return this.get_input_class('pw', 'pt'); case "estop": return this.get_input_class("ew", "et");
if (this.name == 'load-1') return this.get_output_class('1'); case "probe": return this.get_input_class("pw", "pt");
if (this.name == 'load-2') return this.get_output_class('2'); case "load-1": return this.get_output_class("1");
if (this.name == 'fault') return this.get_output_class('f'); case "load-2": return this.get_output_class("2");
if (this.name == 'tool-enable-mode') return this.get_output_class('e'); case "fault": return this.get_output_class("f");
if (this.name == 'tool-direction-mode') return this.get_output_class('d'); case "tool-enable-mode": return this.get_output_class("e");
}, case "tool-direction-mode": return this.get_output_class("d");
tooltip: function () {
if (this.name == 'min-switch-0') return this.get_motor_min_tooltip(0);
if (this.name == 'min-switch-1') return this.get_motor_min_tooltip(1);
if (this.name == 'min-switch-2') return this.get_motor_min_tooltip(2);
if (this.name == 'min-switch-3') return this.get_motor_min_tooltip(3);
if (this.name == 'max-switch-0') return this.get_motor_max_tooltip(0);
if (this.name == 'max-switch-1') return this.get_motor_max_tooltip(1);
if (this.name == 'max-switch-2') return this.get_motor_max_tooltip(2);
if (this.name == 'max-switch-3') return this.get_motor_max_tooltip(3);
if (this.name == 'estop') return this.get_input_tooltip('ew', 'et');
if (this.name == 'probe') return this.get_input_tooltip('pw', 'pt');
if (this.name == 'load-1') return this.get_output_tooltip('1');
if (this.name == 'load-2') return this.get_output_tooltip('2');
if (this.name == 'fault') return this.get_output_tooltip('f');
if (this.name == 'tool-direction-mode')
return this.get_output_tooltip('d');
if (this.name == 'tool-enable-mode')
return this.get_output_tooltip('e');
} }
}, },
tooltip: function () {
switch (this.name) {
case "min-switch-0": return this.get_motor_min_tooltip(0);
case "min-switch-1": return this.get_motor_min_tooltip(1);
case "min-switch-2": return this.get_motor_min_tooltip(2);
case "min-switch-3": return this.get_motor_min_tooltip(3);
case "max-switch-0": return this.get_motor_max_tooltip(0);
case "max-switch-1": return this.get_motor_max_tooltip(1);
case "max-switch-2": return this.get_motor_max_tooltip(2);
case "max-switch-3": return this.get_motor_max_tooltip(3);
case "estop": return this.get_input_tooltip("ew", "et");
case "probe": return this.get_input_tooltip("pw", "pt");
case "load-1": return this.get_output_tooltip("1");
case "load-2": return this.get_output_tooltip("2");
case "fault": return this.get_output_tooltip("f");
case "tool-direction-mode": return this.get_output_tooltip("d");
case "tool-enable-mode": return this.get_output_tooltip("e");
}
}
},
methods: { methods: {
get_io_state_class: function (active, state) { get_io_state_class: function (active, state) {
if (typeof active == 'undefined' || typeof state == 'undefined') if (typeof active == "undefined" || typeof state == "undefined") {
return 'fa-exclamation-triangle warn'; return "fa-exclamation-triangle warn";
}
if (state == 2) return 'fa-circle-o'; if (state == 2) {
return "fa-circle-o";
}
return (state ? 'fa-plus-circle' : 'fa-minus-circle') + ' ' + const icon = state ? "fa-plus-circle" : "fa-minus-circle";
(active ? 'active' : 'inactive'); return `${icon} ${active ? "active" : "inactive"}`;
}, },
get_input_active: function (stateCode, typeCode) { get_input_active: function (stateCode, typeCode) {
var type = this.state[typeCode]; const type = this.state[typeCode];
var state = this.state[stateCode]; const state = this.state[stateCode];
if (type == 1) return !state; // Normally open if (type == 1) {
else if (type == 2) return state; // Normally closed return !state; // Normally open
} else if (type == 2) {
return state; // Normally closed
}
return false return false;
}, },
get_input_class: function (stateCode, typeCode) { get_input_class: function (stateCode, typeCode) {
return this.get_io_state_class(this.get_input_active(stateCode, typeCode), return this.get_io_state_class(this.get_input_active(stateCode, typeCode), this.state[stateCode]);
this.state[stateCode]);
}, },
get_output_class: function (output) { get_output_class: function (output) {
return this.get_io_state_class(this.state[output + 'oa'], return this.get_io_state_class(this.state[`${output}oa`], this.state[`${output}os`]);
this.state[output + 'os']);
}, },
get_motor_min_class: function (motor) { get_motor_min_class: function (motor) {
return this.get_input_class(motor + 'lw', motor + 'ls'); return this.get_input_class(`${motor}lw`, `${motor}ls`);
}, },
get_motor_max_class: function (motor) { get_motor_max_class: function (motor) {
return this.get_input_class(motor + 'xw', motor + 'xs'); return this.get_input_class(`${motor}xw`, `${motor}xs`);
}, },
get_tooltip: function (mode, active, state) { get_tooltip: function (mode, active, state) {
if (typeof mode == 'undefined' || typeof active == 'undefined' || if (typeof mode == "undefined" || typeof active == "undefined" || typeof state == "undefined") {
typeof state == 'undefined') return 'Invalid'; return "Invalid";
}
if (state == 0) state = 'Lo/Gnd'; if (state == 0) {
else if (state == 1) state = 'Hi/+3.3v'; state = "Lo/Gnd";
else if (state == 2) state = 'Tristated'; } else if (state == 1) {
else return 'Invalid'; state = "Hi/+3.3v";
} else if (state == 2) {
state = "Tristated";
} else {
return "Invalid";
}
return 'Mode: ' + mode + '\nActive: ' + (active ? 'True' : 'False') + return `Mode: ${mode}\nActive: ${active ? "True" : "False"}\nLevel: ${state}`;
'\nLevel: ' + state;
}, },
get_input_tooltip: function (stateCode, typeCode) { get_input_tooltip: function (stateCode, typeCode) {
var type = this.state[typeCode]; let type = this.state[typeCode];
if (type == 0) return 'Disabled'; if (type == 0) {
else if (type == 1) type = 'Normally open'; return "Disabled";
else if (type == 2) type = 'Normally closed'; } else if (type == 1) {
type = "Normally open";
} else if (type == 2) {
type = "Normally closed";
}
var active = this.get_input_active(stateCode, typeCode); const active = this.get_input_active(stateCode, typeCode);
var state = this.state[stateCode]; const state = this.state[stateCode];
return this.get_tooltip(type, active, state); return this.get_tooltip(type, active, state);
}, },
get_output_tooltip: function (output) { get_output_tooltip: function (output) {
var mode = this.state[output + 'om']; let mode = this.state[`${output}om`];
if (mode == 0) return 'Disabled';
else if (mode == 1) mode = 'Lo/Hi';
else if (mode == 2) mode = 'Hi/Lo';
else if (mode == 3) mode = 'Tri/Lo';
else if (mode == 4) mode = 'Tri/Hi';
else if (mode == 5) mode = 'Lo/Tri';
else if (mode == 6) mode = 'Hi/Tri';
else mode = undefined;
var active = this.state[output + 'oa']; switch (mode) {
var state = this.state[output + 'os']; case 0: return "Disabled";
case 1: mode = "Lo/Hi"; break;
case 2: mode = "Hi/Lo"; break;
case 3: mode = "Tri/Lo"; break;
case 4: mode = "Tri/Hi"; break;
case 5: mode = "Lo/Tri"; break;
case 6: mode = "Hi/Tri"; break;
default:
mode = undefined;
}
const active = this.state[`${output}oa`];
const state = this.state[`${output}os`];
return this.get_tooltip(mode, active, state); return this.get_tooltip(mode, active, state);
}, },
get_motor_min_tooltip: function (motor) { get_motor_min_tooltip: function (motor) {
return this.get_input_tooltip(motor + 'lw', motor + 'ls'); return this.get_input_tooltip(`${motor}lw`, `${motor}ls`);
}, },
get_motor_max_tooltip: function (motor) { get_motor_max_tooltip: function (motor) {
return this.get_input_tooltip(motor + 'xw', motor + 'xs'); return this.get_input_tooltip(`${motor}xw`, `${motor}xs`);
} }
} }
} };

View File

@@ -1,42 +1,13 @@
/******************************************************************************\ "use strict";
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 = { module.exports = {
template: '#io-view-template', template: "#io-view-template",
props: ['config', 'template', 'state'], props: ["config", "template", "state"],
events: { events: {
'input-changed': function() { "input-changed": function() {
this.$dispatch('config-changed'); this.$dispatch("config-changed");
return false; return false;
} }
} }
} };

View File

@@ -1,13 +1,13 @@
'use strict'; "use strict";
function cookie_get(name) { function cookie_get(name) {
var decodedCookie = decodeURIComponent(document.cookie); const decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';'); const ca = decodedCookie.split(";");
name = name + '='; name = `${name}=`;
for (var i = 0; i < ca.length; i++) { for (let i = 0; i < ca.length; i++) {
var c = ca[i]; let c = ca[i];
while (c.charAt(0) == ' ') { while (c.charAt(0) == " ") {
c = c.substring(1); c = c.substring(1);
} }
@@ -18,101 +18,101 @@ function cookie_get(name) {
} }
function cookie_set(name, value, days) { function cookie_set(name, value, days) {
var d = new Date(); const d = new Date();
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000); d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
var expires = 'expires=' + d.toUTCString(); const expires = `expires=${d.toUTCString()}`;
document.cookie = name + '=' + value + ';' + expires + ';path=/'; document.cookie = `${name}=${value};${expires};path=/`;
} }
var uuid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+'; const uuid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+";
function uuid(length) { function uuid(length) {
if (typeof length == 'undefined') { if (typeof length == "undefined") {
length = 52; length = 52;
} }
var s = ''; let s = "";
for (var i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)]; s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
} }
return s return s;
} }
$(function () { $(function () {
if (typeof cookie_get('client-id') == 'undefined') { if (typeof cookie_get("client-id") == "undefined") {
cookie_set('client-id', uuid(), 10000); cookie_set("client-id", uuid(), 10000);
} }
// Register global components // Register global components
Vue.component('templated-input', require('./templated-input')); Vue.component("templated-input", require("./templated-input"));
Vue.component('message', require('./message')); Vue.component("message", require("./message"));
Vue.component('indicators', require('./indicators')); Vue.component("indicators", require("./indicators"));
Vue.component('io-indicator', require('./io-indicator')); Vue.component("io-indicator", require("./io-indicator"));
Vue.component('console', require('./console')); Vue.component("console", require("./console"));
Vue.component('unit-value', require('./unit-value')); Vue.component("unit-value", require("./unit-value"));
Vue.filter('number', function (value) { Vue.filter("number", function (value) {
if (isNaN(value)) { if (isNaN(value)) {
return 'NaN'; return "NaN";
} }
return value.toLocaleString(); return value.toLocaleString();
}); });
Vue.filter('percent', function (value, precision) { Vue.filter("percent", function (value, precision) {
if (typeof value == 'undefined') { if (typeof value == "undefined") {
return ''; return "";
} }
if (typeof precision == 'undefined') { if (typeof precision == "undefined") {
precision = 2; precision = 2;
} }
return (value * 100.0).toFixed(precision) + '%'; return `${(value * 100.0).toFixed(precision)}%`;
}); });
Vue.filter('non_zero_percent', function (value, precision) { Vue.filter("non_zero_percent", function (value, precision) {
if (!value) { if (!value) {
return ''; return "";
} }
if (typeof precision == 'undefined') { if (typeof precision == "undefined") {
precision = 2; precision = 2;
} }
return (value * 100.0).toFixed(precision) + '%'; return `${(value * 100.0).toFixed(precision)}%`;
}); });
Vue.filter('fixed', function (value, precision) { Vue.filter("fixed", function (value, precision) {
if (typeof value == 'undefined') { if (typeof value == "undefined") {
return '0'; return "0";
} }
return parseFloat(value).toFixed(precision) return parseFloat(value).toFixed(precision);
}); });
Vue.filter('upper', function (value) { Vue.filter("upper", function (value) {
if (typeof value == 'undefined') { if (typeof value == "undefined") {
return ''; return "";
} }
return value.toUpperCase() return value.toUpperCase();
}); });
Vue.filter('time', function (value, precision) { Vue.filter("time", function (value, precision) {
if (isNaN(value)) { if (isNaN(value)) {
return ''; return "";
} }
if (isNaN(precision)) { if (isNaN(precision)) {
precision = 0; precision = 0;
} }
var MIN = 60; const MIN = 60;
var HR = MIN * 60; const HR = MIN * 60;
var DAY = HR * 24; const DAY = HR * 24;
var parts = []; const parts = [];
if (DAY <= value) { if (DAY <= value) {
parts.push(Math.floor(value / DAY)); parts.push(Math.floor(value / DAY));
@@ -133,16 +133,16 @@ $(function () {
parts.push(value); parts.push(value);
for (var i = 0; i < parts.length; i++) { for (let i = 0; i < parts.length; i++) {
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0); parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
if (i && parts[i] < 10) { if (i && parts[i] < 10) {
parts[i] = '0' + parts[i]; parts[i] = `0${parts[i]}`;
} }
} }
return parts.join(':'); return parts.join(":");
}); });
// Vue app // Vue app
require('./app'); require("./app");
}); });

View File

@@ -1,35 +1,7 @@
/******************************************************************************\ "use strict";
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 = { module.exports = {
template: '#message-template', template: "#message-template",
props: { props: {
show: { show: {
@@ -44,4 +16,4 @@ module.exports = {
twoWay: false twoWay: false
} }
} }
} };

View File

@@ -1,48 +1,20 @@
/******************************************************************************\ "use strict";
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 = { module.exports = {
replace: true, replace: true,
template: '#modbus-reg-view-template', template: "#modbus-reg-view-template",
props: ['index', 'model', 'template', 'enable'], props: ["index", "model", "template", "enable"],
computed: { computed: {
has_user_value: function () { has_user_value: function () {
var type = this.model['reg-type']; const type = this.model["reg-type"];
return type.indexOf('write') != -1 || type.indexOf('fixed') != -1; return type.indexOf("write") != -1 || type.indexOf("fixed") != -1;
} }
}, },
methods: { methods: {
change: function () {this.$dispatch('input-changed')} change: function () {
this.$dispatch("input-changed");
} }
} }
};

View File

@@ -1,35 +1,7 @@
/******************************************************************************\ "use strict";
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'
// Must match modbus.c // Must match modbus.c
var exports = { const exports = {
DISCONNECTED: 0, DISCONNECTED: 0,
OK: 1, OK: 1,
CRC: 2, CRC: 2,
@@ -37,15 +9,15 @@ var exports = {
TIMEDOUT: 4 TIMEDOUT: 4
}; };
exports.status_to_string = exports.status_to_string =
function (status) { function (status) {
if (status == exports.OK) return 'Ok'; switch (status) {
if (status == exports.CRC) return 'CRC error'; case exports.OK: return "Ok";
if (status == exports.INVALID) return 'Invalid response'; case exports.CRC: return "CRC error";
if (status == exports.TIMEDOUT) return 'Timedout'; case exports.INVALID: return "Invalid response";
return 'Disconnected'; case exports.TIMEDOUT: return "Timedout";
default: return "Disconnected";
} }
};
module.exports = exports; module.exports = exports;

View File

@@ -1,8 +1,8 @@
'use strict' "use strict";
module.exports = { module.exports = {
template: '#motor-view-template', template: "#motor-view-template",
props: ['index', 'config', 'template', 'state'], props: ["index", "config", "template", "state"],
computed: { computed: {
metric: function () { metric: function () {
@@ -10,7 +10,7 @@ module.exports = {
}, },
is_slave: function () { is_slave: function () {
for (var i = 0; i < this.index; i++) { for (let i = 0; i < this.index; i++) {
if (this.motor.axis == this.config.motors[i].axis) { if (this.motor.axis == this.config.motors[i].axis) {
return true; return true;
} }
@@ -20,76 +20,76 @@ module.exports = {
}, },
motor: function () { motor: function () {
return this.config.motors[this.index] return this.config.motors[this.index];
}, },
invalidMaxVelocity: function () { invalidMaxVelocity: function () {
return this.maxMaxVelocity < this.motor['max-velocity']; return this.maxMaxVelocity < this.motor["max-velocity"];
}, },
maxMaxVelocity: function () { maxMaxVelocity: function () {
return 1 * (15 * this.umPerStep / this.motor['microsteps']).toFixed(3); return 1 * (15 * this.umPerStep / this.motor["microsteps"]).toFixed(3);
}, },
ustepPerSec: function () { ustepPerSec: function () {
return this.rpm * this.stepsPerRev * this.motor['microsteps'] / 60; return this.rpm * this.stepsPerRev * this.motor["microsteps"] / 60;
}, },
rpm: function () { rpm: function () {
return 1000 * this.motor['max-velocity'] / this.motor['travel-per-rev']; return 1000 * this.motor["max-velocity"] / this.motor["travel-per-rev"];
}, },
gForce: function () { gForce: function () {
return this.motor['max-accel'] * 0.0283254504 return this.motor["max-accel"] * 0.0283254504;
}, },
gForcePerMin: function () { gForcePerMin: function () {
return this.motor['max-jerk'] * 0.0283254504 return this.motor["max-jerk"] * 0.0283254504;
}, },
stepsPerRev: function () { stepsPerRev: function () {
return 360 / this.motor['step-angle'] return 360 / this.motor["step-angle"];
}, },
umPerStep: function () { umPerStep: function () {
return this.motor['travel-per-rev'] * this.motor['step-angle'] / 0.36 return this.motor["travel-per-rev"] * this.motor["step-angle"] / 0.36;
}, },
milPerStep: function () { milPerStep: function () {
return this.umPerStep / 25.4 return this.umPerStep / 25.4;
}, },
invalidStallVelocity: function () { invalidStallVelocity: function () {
if (!this.motor['homing-mode'].startsWith('stall-')) { if (!this.motor["homing-mode"].startsWith("stall-")) {
return false; return false;
} }
return this.maxStallVelocity < this.motor['search-velocity']; return this.maxStallVelocity < this.motor["search-velocity"];
}, },
stallRPM: function () { stallRPM: function () {
var v = this.motor['search-velocity']; const v = this.motor["search-velocity"];
return 1000 * v / this.motor['travel-per-rev']; return 1000 * v / this.motor["travel-per-rev"];
}, },
maxStallVelocity: function () { maxStallVelocity: function () {
var maxRate = 900000 / this.motor['stall-sample-time']; const maxRate = 900000 / this.motor["stall-sample-time"];
var ustep = this.motor['stall-microstep']; const ustep = this.motor["stall-microstep"];
var angle = this.motor['step-angle']; const angle = this.motor["step-angle"];
var travel = this.motor['travel-per-rev']; const travel = this.motor["travel-per-rev"];
var maxStall = maxRate * 60 / 360 / 1000 * angle / ustep * travel; const maxStall = maxRate * 60 / 360 / 1000 * angle / ustep * travel;
return 1 * maxStall.toFixed(3); return 1 * maxStall.toFixed(3);
}, },
stallUStepPerSec: function () { stallUStepPerSec: function () {
var ustep = this.motor['stall-microstep']; const ustep = this.motor["stall-microstep"];
return this.stallRPM * this.stepsPerRev * ustep / 60; return this.stallRPM * this.stepsPerRev * ustep / 60;
} }
}, },
events: { events: {
'input-changed': function () { "input-changed": function () {
Vue.nextTick(function () { Vue.nextTick(function () {
// Limit max-velocity // Limit max-velocity
if (this.invalidMaxVelocity) { if (this.invalidMaxVelocity) {
@@ -101,8 +101,8 @@ module.exports = {
this.$set('motor["search-velocity"]', this.maxStallVelocity); this.$set('motor["search-velocity"]', this.maxStallVelocity);
} }
this.$dispatch('config-changed'); this.$dispatch("config-changed");
}.bind(this)) }.bind(this));
return false; return false;
} }
@@ -114,7 +114,7 @@ module.exports = {
return true; return true;
} }
return templ.hmodes.indexOf(this.motor['homing-mode']) != -1; return templ.hmodes.indexOf(this.motor["homing-mode"]) != -1;
} }
} }
} };

View File

@@ -7,7 +7,7 @@
* @author jcoffland / https://buildbotics.com/ * @author jcoffland / https://buildbotics.com/
*/ */
'use strict' "use strict";
// This set of controls performs orbiting, dollying (zooming), and panning. // This set of controls performs orbiting, dollying (zooming), and panning.
// Unlike TrackballControls, it maintains the "up" direction object.up // Unlike TrackballControls, it maintains the "up" direction object.up
@@ -17,8 +17,42 @@
// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
// Pan - right mouse, or arrow keys / touch: two-finger move // Pan - right mouse, or arrow keys / touch: two-finger move
const OrbitControls = function (object, domElement) {
// internals
// eslint-disable-next-line @typescript-eslint/no-this-alias
const scope = this;
const changeEvent = {type: "change"};
const startEvent = {type: "start"};
const endEvent = {type: "end"};
const STATE = {
NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4
};
let state = STATE.NONE;
const EPS = 0.000001;
// current position in spherical coordinates
const spherical = new THREE.Spherical();
const sphericalDelta = new THREE.Spherical();
let scale = 1;
const panOffset = new THREE.Vector3();
let zoomChanged = false;
const rotateStart = new THREE.Vector2();
const rotateEnd = new THREE.Vector2();
const rotateDelta = new THREE.Vector2();
const panStart = new THREE.Vector2();
const panEnd = new THREE.Vector2();
const panDelta = new THREE.Vector2();
const dollyStart = new THREE.Vector2();
const dollyEnd = new THREE.Vector2();
const dollyDelta = new THREE.Vector2();
var OrbitControls = function (object, domElement) {
this.object = object; this.object = object;
this.domElement = domElement != undefined ? domElement : document; this.domElement = domElement != undefined ? domElement : document;
@@ -85,16 +119,19 @@ var OrbitControls = function (object, domElement) {
this.zoom0 = this.object.zoom; this.zoom0 = this.object.zoom;
// public methods // public methods
this.getPolarAngle = function () {return spherical.phi} this.getPolarAngle = function () {
this.getAzimuthalAngle = function () {return spherical.theta} return spherical.phi;
};
this.getAzimuthalAngle = function () {
return spherical.theta;
};
this.saveState = function () { this.saveState = function () {
scope.target0.copy(scope.target); scope.target0.copy(scope.target);
scope.position0.copy(scope.object.position); scope.position0.copy(scope.object.position);
scope.zoom0 = scope.object.zoom; scope.zoom0 = scope.object.zoom;
} };
this.reset = function () { this.reset = function () {
scope.target.copy(scope.target0); scope.target.copy(scope.target0);
@@ -106,22 +143,20 @@ var OrbitControls = function (object, domElement) {
scope.update(); scope.update();
state = STATE.NONE; state = STATE.NONE;
} };
this.update = function () { this.update = function () {
var offset = new THREE.Vector3(); const offset = new THREE.Vector3();
// so camera.up is the orbit axis // so camera.up is the orbit axis
var quat = new THREE.Quaternion() const quat = new THREE.Quaternion().setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0));
.setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0)); const quatInverse = quat.clone().inverse();
var quatInverse = quat.clone().inverse();
var lastPosition = new THREE.Vector3(); const lastPosition = new THREE.Vector3();
var lastQuaternion = new THREE.Quaternion(); const lastQuaternion = new THREE.Quaternion();
return function update() { return function update() {
var position = scope.object.position; const position = scope.object.position;
offset.copy(position).sub(scope.target); offset.copy(position).sub(scope.target);
@@ -131,28 +166,24 @@ var OrbitControls = function (object, domElement) {
// angle from z-axis around y-axis // angle from z-axis around y-axis
spherical.setFromVector3(offset); spherical.setFromVector3(offset);
if (scope.autoRotate && state == STATE.NONE) if (scope.autoRotate && state == STATE.NONE) {
rotateLeft(getAutoRotationAngle()); rotateLeft(getAutoRotationAngle());
}
spherical.theta += sphericalDelta.theta; spherical.theta += sphericalDelta.theta;
spherical.phi += sphericalDelta.phi; spherical.phi += sphericalDelta.phi;
// restrict theta to be between desired limits // restrict theta to be between desired limits
spherical.theta = spherical.theta = Math.max(scope.minAzimuthAngle, Math.min(scope.maxAzimuthAngle, spherical.theta));
Math.max(scope.minAzimuthAngle,
Math.min(scope.maxAzimuthAngle, spherical.theta));
// restrict phi to be between desired limits // restrict phi to be between desired limits
spherical.phi = spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi));
Math.max(scope.minPolarAngle,
Math.min(scope.maxPolarAngle, spherical.phi));
spherical.makeSafe(); spherical.makeSafe();
spherical.radius *= scale; spherical.radius *= scale;
// restrict radius to be between desired limits // restrict radius to be between desired limits
spherical.radius = spherical.radius = Math.max(10, Math.min(scope.object.far * 0.8, spherical.radius));
Math.max(10, Math.min(scope.object.far * 0.8, spherical.radius));
// move target to panned location // move target to panned location
scope.target.add(panOffset); scope.target.add(panOffset);
@@ -178,9 +209,10 @@ var OrbitControls = function (object, domElement) {
// update condition is: // update condition is:
// min(camera displacement, camera rotation in radians)^2 > EPS // min(camera displacement, camera rotation in radians)^2 > EPS
// using small-angle approximation cos(x/2) = 1 - x^2 / 8 // using small-angle approximation cos(x/2) = 1 - x^2 / 8
if (zoomChanged || scale != 1 || if (zoomChanged
lastPosition.distanceToSquared(scope.object.position) > EPS || || scale != 1
8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) { || lastPosition.distanceToSquared(scope.object.position) > EPS
|| 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) {
scope.dispatchEvent(changeEvent); scope.dispatchEvent(changeEvent);
@@ -193,187 +225,150 @@ var OrbitControls = function (object, domElement) {
} }
return false; return false;
} };
}() }();
this.dispose = function () { this.dispose = function () {
scope.domElement.removeEventListener('contextmenu', onContextMenu, false); scope.domElement.removeEventListener("contextmenu", onContextMenu, false);
scope.domElement.removeEventListener('mousedown', onMouseDown, false); scope.domElement.removeEventListener("mousedown", onMouseDown, false);
scope.domElement.removeEventListener('wheel', onMouseWheel, false); scope.domElement.removeEventListener("wheel", onMouseWheel, false);
scope.domElement.removeEventListener('touchstart', onTouchStart, false); scope.domElement.removeEventListener("touchstart", onTouchStart, false);
scope.domElement.removeEventListener('touchend', onTouchEnd, false); scope.domElement.removeEventListener("touchend", onTouchEnd, false);
scope.domElement.removeEventListener('touchmove', onTouchMove, false); scope.domElement.removeEventListener("touchmove", onTouchMove, false);
document.removeEventListener('mousemove', onMouseMove, false); document.removeEventListener("mousemove", onMouseMove, false);
document.removeEventListener('mouseup', onMouseUp, false); document.removeEventListener("mouseup", onMouseUp, false);
window.removeEventListener('keydown', onKeyDown, false); window.removeEventListener("keydown", onKeyDown, false);
}
// internals
var scope = this;
var changeEvent = {type: 'change'};
var startEvent = {type: 'start'};
var endEvent = {type: 'end'};
var STATE = {
NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4
}; };
var state = STATE.NONE;
var EPS = 0.000001;
// current position in spherical coordinates
var spherical = new THREE.Spherical();
var sphericalDelta = new THREE.Spherical();
var scale = 1;
var panOffset = new THREE.Vector3();
var zoomChanged = false;
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var panStart = new THREE.Vector2();
var panEnd = new THREE.Vector2();
var panDelta = new THREE.Vector2();
var dollyStart = new THREE.Vector2();
var dollyEnd = new THREE.Vector2();
var dollyDelta = new THREE.Vector2();
function getAutoRotationAngle() { function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
} }
function getZoomScale() {
return Math.pow(0.95, scope.zoomSpeed);
}
function getZoomScale() {return Math.pow(0.95, scope.zoomSpeed)} function rotateLeft(angle) {
function rotateLeft(angle) {sphericalDelta.theta -= angle} sphericalDelta.theta -= angle;
function rotateUp(angle) {sphericalDelta.phi -= angle} }
function rotateUp(angle) {
sphericalDelta.phi -= angle;
}
var panLeft = function () { const panLeft = function () {
var v = new THREE.Vector3(); const v = new THREE.Vector3();
return function panLeft(distance, objectMatrix) { return function panLeft(distance, objectMatrix) {
v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix
v.multiplyScalar(-distance); v.multiplyScalar(-distance);
panOffset.add(v); panOffset.add(v);
} };
}() }();
const panUp = function () {
var panUp = function () { const v = new THREE.Vector3();
var v = new THREE.Vector3();
return function panUp(distance, objectMatrix) { return function panUp(distance, objectMatrix) {
if (scope.screenSpacePanning) v.setFromMatrixColumn(objectMatrix, 1); if (scope.screenSpacePanning) {
else { v.setFromMatrixColumn(objectMatrix, 1);
} else {
v.setFromMatrixColumn(objectMatrix, 0); v.setFromMatrixColumn(objectMatrix, 0);
v.crossVectors(scope.object.up, v); v.crossVectors(scope.object.up, v);
} }
v.multiplyScalar(distance); v.multiplyScalar(distance);
panOffset.add(v); panOffset.add(v);
} };
}() }();
function unknownCamera() { function unknownCamera() {
console.warn('WARNING: OrbitControls.js encountered an unknown camera ' + console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan & zoom disabled.");
'type - pan & zoom disabled.');
scope.enablePan = false; scope.enablePan = false;
scope.enableZoom = false; scope.enableZoom = false;
} }
// deltaX and deltaY are in pixels; right and down are positive // deltaX and deltaY are in pixels; right and down are positive
var pan = function () { const pan = function () {
var offset = new THREE.Vector3(); const offset = new THREE.Vector3();
return function pan(deltaX, deltaY) { return function pan(deltaX, deltaY) {
var element = scope.domElement === document ? const element = scope.domElement === document
scope.domElement.body : scope.domElement; ? scope.domElement.body
: scope.domElement;
if (scope.object.isPerspectiveCamera) { if (scope.object.isPerspectiveCamera) {
// perspective // perspective
offset.copy(scope.object.position).sub(scope.target); offset.copy(scope.object.position).sub(scope.target);
var targetDistance = offset.length(); let targetDistance = offset.length();
// half of the fov is center to top of screen // half of the fov is center to top of screen
targetDistance *= Math.tan((scope.object.fov / 2) * Math.PI / 180.0); targetDistance *= Math.tan((scope.object.fov / 2) * Math.PI / 180.0);
// we use only clientHeight here so aspect ratio does not distort speed // we use only clientHeight here so aspect ratio does not distort speed
panLeft(2 * deltaX * targetDistance / element.clientHeight, panLeft(2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix);
scope.object.matrix); panUp(2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix);
panUp(2 * deltaY * targetDistance / element.clientHeight,
scope.object.matrix);
} else if (scope.object.isOrthographicCamera) { } else if (scope.object.isOrthographicCamera) {
// orthographic // orthographic
panLeft(deltaX * (scope.object.right - scope.object.left) / panLeft(deltaX * (scope.object.right - scope.object.left) / scope.object.zoom / element.clientWidth, scope.object.matrix);
scope.object.zoom / element.clientWidth, scope.object.matrix); panUp(deltaY * (scope.object.top - scope.object.bottom) / scope.object.zoom / element.clientHeight, scope.object.matrix);
panUp(deltaY * (scope.object.top - scope.object.bottom) /
scope.object.zoom / element.clientHeight, scope.object.matrix);
} else unknownCamera(); } else {
unknownCamera();
} }
}() };
}();
function dollyIn(dollyScale) { function dollyIn(dollyScale) {
if (scope.object.isPerspectiveCamera) scale /= dollyScale; if (scope.object.isPerspectiveCamera) {
scale /= dollyScale;
else if (scope.object.isOrthographicCamera) { } else if (scope.object.isOrthographicCamera) {
scope.object.zoom = scope.object.zoom =
Math.max(scope.minZoom, Math.max(scope.minZoom,
Math.min(scope.maxZoom, scope.object.zoom * dollyScale)); Math.min(scope.maxZoom, scope.object.zoom * dollyScale));
scope.object.updateProjectionMatrix(); scope.object.updateProjectionMatrix();
zoomChanged = true; zoomChanged = true;
} else unknownCamera(); } else {
unknownCamera();
}
} }
function dollyOut(dollyScale) { function dollyOut(dollyScale) {
if (scope.object.isPerspectiveCamera) scale *= dollyScale; if (scope.object.isPerspectiveCamera) {
scale *= dollyScale;
else if (scope.object.isOrthographicCamera) { } else if (scope.object.isOrthographicCamera) {
scope.object.zoom = scope.object.zoom =
Math.max(scope.minZoom, Math.max(scope.minZoom,
Math.min(scope.maxZoom, scope.object.zoom / dollyScale)); Math.min(scope.maxZoom, scope.object.zoom / dollyScale));
scope.object.updateProjectionMatrix(); scope.object.updateProjectionMatrix();
zoomChanged = true; zoomChanged = true;
} else unknownCamera(); } else {
unknownCamera();
}
} }
// event callbacks - update the object state // event callbacks - update the object state
function handleMouseDownRotate(event) { function handleMouseDownRotate(event) {
rotateStart.set(event.clientX, event.clientY); rotateStart.set(event.clientX, event.clientY);
} }
function handleMouseDownDolly(event) { function handleMouseDownDolly(event) {
dollyStart.set(event.clientX, event.clientY); dollyStart.set(event.clientX, event.clientY);
} }
function handleMouseDownPan(event) { function handleMouseDownPan(event) {
panStart.set(event.clientX, event.clientY); panStart.set(event.clientX, event.clientY);
} }
function handleMouseMoveRotate(event) { function handleMouseMoveRotate(event) {
rotateEnd.set(event.clientX, event.clientY); rotateEnd.set(event.clientX, event.clientY);
rotateDelta.subVectors(rotateEnd, rotateStart) rotateDelta.subVectors(rotateEnd, rotateStart)
.multiplyScalar(scope.rotateSpeed); .multiplyScalar(scope.rotateSpeed);
var element = scope.domElement === document ? const element = scope.domElement === document ?
scope.domElement.body : scope.domElement; scope.domElement.body : scope.domElement;
// yes, height // yes, height
@@ -385,19 +380,20 @@ var OrbitControls = function (object, domElement) {
scope.update(); scope.update();
} }
function handleMouseMoveDolly(event) { function handleMouseMoveDolly(event) {
dollyEnd.set(event.clientX, event.clientY); dollyEnd.set(event.clientX, event.clientY);
dollyDelta.subVectors(dollyEnd, dollyStart); dollyDelta.subVectors(dollyEnd, dollyStart);
if (dollyDelta.y > 0) dollyIn(getZoomScale()); if (dollyDelta.y > 0) {
else if (dollyDelta.y < 0) dollyOut(getZoomScale()); dollyIn(getZoomScale());
} else if (dollyDelta.y < 0) {
dollyOut(getZoomScale());
}
dollyStart.copy(dollyEnd); dollyStart.copy(dollyEnd);
scope.update(); scope.update();
} }
function handleMouseMovePan(event) { function handleMouseMovePan(event) {
panEnd.set(event.clientX, event.clientY); panEnd.set(event.clientX, event.clientY);
panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed);
@@ -406,18 +402,16 @@ var OrbitControls = function (object, domElement) {
scope.update(); scope.update();
} }
function handleMouseUp(event) {}
function handleMouseWheel(event) { function handleMouseWheel(event) {
if (event.deltaY < 0) dollyOut(getZoomScale()); if (event.deltaY < 0) {
else if (event.deltaY > 0) dollyIn(getZoomScale()); dollyOut(getZoomScale());
} else if (event.deltaY > 0) {
dollyIn(getZoomScale());
}
scope.update(); scope.update();
} }
function handleKeyDown(event) { function handleKeyDown(event) {
switch (event.keyCode) { switch (event.keyCode) {
case scope.keys.UP: case scope.keys.UP:
@@ -442,35 +436,32 @@ var OrbitControls = function (object, domElement) {
} }
} }
function handleTouchStartRotate(event) { function handleTouchStartRotate(event) {
rotateStart.set(event.touches[0].pageX, event.touches[0].pageY); rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
} }
function handleTouchStartDollyPan(event) { function handleTouchStartDollyPan(event) {
if (scope.enableZoom) { if (scope.enableZoom) {
var dx = event.touches[0].pageX - event.touches[1].pageX; const dx = event.touches[0].pageX - event.touches[1].pageX;
var dy = event.touches[0].pageY - event.touches[1].pageY; const dy = event.touches[0].pageY - event.touches[1].pageY;
var distance = Math.sqrt(dx * dx + dy * dy); const distance = Math.sqrt(dx * dx + dy * dy);
dollyStart.set(0, distance); dollyStart.set(0, distance);
} }
if (scope.enablePan) { if (scope.enablePan) {
var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); const x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); const y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
panStart.set(x, y); panStart.set(x, y);
} }
} }
function handleTouchMoveRotate(event) { function handleTouchMoveRotate(event) {
rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY); rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
rotateDelta.subVectors(rotateEnd, rotateStart) rotateDelta.subVectors(rotateEnd, rotateStart)
.multiplyScalar(scope.rotateSpeed); .multiplyScalar(scope.rotateSpeed);
var element = scope.domElement === document ? const element = scope.domElement === document ?
scope.domElement.body : scope.domElement; scope.domElement.body : scope.domElement;
// yes, height // yes, height
@@ -480,12 +471,11 @@ var OrbitControls = function (object, domElement) {
scope.update(); scope.update();
} }
function handleTouchMoveDollyPan(event) { function handleTouchMoveDollyPan(event) {
if (scope.enableZoom) { if (scope.enableZoom) {
var dx = event.touches[0].pageX - event.touches[1].pageX; const dx = event.touches[0].pageX - event.touches[1].pageX;
var dy = event.touches[0].pageY - event.touches[1].pageY; const dy = event.touches[0].pageY - event.touches[1].pageY;
var distance = Math.sqrt(dx * dx + dy * dy); const distance = Math.sqrt(dx * dx + dy * dy);
dollyEnd.set(0, distance); dollyEnd.set(0, distance);
dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed)); dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed));
@@ -493,10 +483,9 @@ var OrbitControls = function (object, domElement) {
dollyStart.copy(dollyEnd); dollyStart.copy(dollyEnd);
} }
if (scope.enablePan) { if (scope.enablePan) {
var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX); const x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY); const y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
panEnd.set(x, y); panEnd.set(x, y);
panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed);
@@ -507,82 +496,94 @@ var OrbitControls = function (object, domElement) {
scope.update(); scope.update();
} }
function handleTouchEnd(event) {}
// event handlers - listen for events and reset state // event handlers - listen for events and reset state
function onMouseDown(event) { function onMouseDown(event) {
if (!scope.enabled) return; if (!scope.enabled) {
return;
}
event.preventDefault(); event.preventDefault();
switch (event.button) { switch (event.button) {
case scope.mouseButtons.ORBIT: case scope.mouseButtons.ORBIT:
if (!scope.enableRotate) return; if (!scope.enableRotate) {
return;
}
handleMouseDownRotate(event); handleMouseDownRotate(event);
state = STATE.ROTATE; state = STATE.ROTATE;
break; break;
case scope.mouseButtons.ZOOM: case scope.mouseButtons.ZOOM:
if (!scope.enableZoom) return; if (!scope.enableZoom) {
return;
}
handleMouseDownDolly(event); handleMouseDownDolly(event);
state = STATE.DOLLY; state = STATE.DOLLY;
break; break;
case scope.mouseButtons.PAN: case scope.mouseButtons.PAN:
if (!scope.enablePan) return; if (!scope.enablePan) {
return;
}
handleMouseDownPan(event); handleMouseDownPan(event);
state = STATE.PAN; state = STATE.PAN;
break; break;
} }
if (state != STATE.NONE) { if (state != STATE.NONE) {
document.addEventListener('mousemove', onMouseMove, false); document.addEventListener("mousemove", onMouseMove, false);
document.addEventListener('mouseup', onMouseUp, false); document.addEventListener("mouseup", onMouseUp, false);
scope.dispatchEvent(startEvent); scope.dispatchEvent(startEvent);
} }
} }
function onMouseMove(event) { function onMouseMove(event) {
if (!scope.enabled) return; if (!scope.enabled) {
return;
}
event.preventDefault(); event.preventDefault();
switch (state) { switch (state) {
case STATE.ROTATE: case STATE.ROTATE:
if (!scope.enableRotate) return; if (!scope.enableRotate) {
return;
}
handleMouseMoveRotate(event); handleMouseMoveRotate(event);
break; break;
case STATE.DOLLY: case STATE.DOLLY:
if (!scope.enableZoom) return; if (!scope.enableZoom) {
return;
}
handleMouseMoveDolly(event); handleMouseMoveDolly(event);
break; break;
case STATE.PAN: case STATE.PAN:
if (!scope.enablePan) return; if (!scope.enablePan) {
return;
}
handleMouseMovePan(event); handleMouseMovePan(event);
break; break;
} }
} }
function onMouseUp() {
if (!scope.enabled) {
return;
}
function onMouseUp(event) { document.removeEventListener("mousemove", onMouseMove, false);
if (!scope.enabled) return; document.removeEventListener("mouseup", onMouseUp, false);
handleMouseUp(event);
document.removeEventListener('mousemove', onMouseMove, false);
document.removeEventListener('mouseup', onMouseUp, false);
scope.dispatchEvent(endEvent); scope.dispatchEvent(endEvent);
state = STATE.NONE; state = STATE.NONE;
} }
function onMouseWheel(event) { function onMouseWheel(event) {
if (!scope.enabled || !scope.enableZoom || if (!scope.enabled || !scope.enableZoom ||
(state != STATE.NONE && state != STATE.ROTATE)) return; (state != STATE.NONE && state != STATE.ROTATE)) {
return;
}
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@@ -591,28 +592,34 @@ var OrbitControls = function (object, domElement) {
scope.dispatchEvent(endEvent); scope.dispatchEvent(endEvent);
} }
function onKeyDown(event) { function onKeyDown(event) {
if (!scope.enabled || !scope.enableKeys || !scope.enablePan) return; if (!scope.enabled || !scope.enableKeys || !scope.enablePan) {
return;
}
handleKeyDown(event); handleKeyDown(event);
} }
function onTouchStart(event) { function onTouchStart(event) {
if (!scope.enabled) return; if (!scope.enabled) {
return;
}
event.preventDefault(); event.preventDefault();
switch (event.touches.length) { switch (event.touches.length) {
case 1: // one-fingered touch: rotate case 1: // one-fingered touch: rotate
if (!scope.enableRotate) return; if (!scope.enableRotate) {
return;
}
handleTouchStartRotate(event); handleTouchStartRotate(event);
state = STATE.TOUCH_ROTATE; state = STATE.TOUCH_ROTATE;
break; break;
case 2: // two-fingered touch: dolly-pan case 2: // two-fingered touch: dolly-pan
if (!scope.enableZoom && !scope.enablePan) return; if (!scope.enableZoom && !scope.enablePan) {
return;
}
handleTouchStartDollyPan(event); handleTouchStartDollyPan(event);
state = STATE.TOUCH_DOLLY_PAN; state = STATE.TOUCH_DOLLY_PAN;
break; break;
@@ -620,27 +627,38 @@ var OrbitControls = function (object, domElement) {
default: state = STATE.NONE; default: state = STATE.NONE;
} }
if (state != STATE.NONE) scope.dispatchEvent(startEvent); if (state != STATE.NONE) {
scope.dispatchEvent(startEvent);
}
} }
function onTouchMove(event) { function onTouchMove(event) {
if (!scope.enabled) return; if (!scope.enabled) {
return;
}
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
switch (event.touches.length) { switch (event.touches.length) {
case 1: // one-fingered touch: rotate case 1: // one-fingered touch: rotate
if (!scope.enableRotate) return; if (!scope.enableRotate) {
if (state != STATE.TOUCH_ROTATE) return; // is this needed? return;
}
if (state != STATE.TOUCH_ROTATE) {
return;
} // is this needed?
handleTouchMoveRotate(event); handleTouchMoveRotate(event);
break; break;
case 2: // two-fingered touch: dolly-pan case 2: // two-fingered touch: dolly-pan
if (!scope.enableZoom && !scope.enablePan) return; if (!scope.enableZoom && !scope.enablePan) {
if (state != STATE.TOUCH_DOLLY_PAN) return; // is this needed? return;
}
if (state != STATE.TOUCH_DOLLY_PAN) {
return;
} // is this needed?
handleTouchMoveDollyPan(event); handleTouchMoveDollyPan(event);
break; break;
@@ -649,33 +667,32 @@ var OrbitControls = function (object, domElement) {
} }
} }
function onTouchEnd() {
if (!scope.enabled) {
return;
}
function onTouchEnd(event) {
if (!scope.enabled) return;
handleTouchEnd(event);
scope.dispatchEvent(endEvent); scope.dispatchEvent(endEvent);
state = STATE.NONE; state = STATE.NONE;
} }
function onContextMenu(event) { function onContextMenu(event) {
if (!scope.enabled) return; if (!scope.enabled) {
return;
}
event.preventDefault(); event.preventDefault();
} }
scope.domElement.addEventListener("contextmenu", onContextMenu, false);
scope.domElement.addEventListener('contextmenu', onContextMenu, false); scope.domElement.addEventListener("mousedown", onMouseDown, false);
scope.domElement.addEventListener('mousedown', onMouseDown, false); scope.domElement.addEventListener("wheel", onMouseWheel, false);
scope.domElement.addEventListener('wheel', onMouseWheel, false); scope.domElement.addEventListener("touchstart", onTouchStart, false);
scope.domElement.addEventListener('touchstart', onTouchStart, false); scope.domElement.addEventListener("touchend", onTouchEnd, false);
scope.domElement.addEventListener('touchend', onTouchEnd, false); scope.domElement.addEventListener("touchmove", onTouchMove, false);
scope.domElement.addEventListener('touchmove', onTouchMove, false); window.addEventListener("keydown", onKeyDown, false);
window .addEventListener('keydown', onKeyDown, false);
this.update(); // force an update at start this.update(); // force an update at start
} };
OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype); OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype);
OrbitControls.prototype.constructor = OrbitControls; OrbitControls.prototype.constructor = OrbitControls;

View File

@@ -1,58 +1,32 @@
/******************************************************************************\ "use strict";
Copyright 2018. Buildbotics LLC const orbit = require("./orbit");
All Rights Reserved. const cookie = require("./cookie")("bbctrl-");
const font = require("./helvetiker_regular.typeface.json");
For information regarding this software email:
Joseph Coffland
joseph@buildbotics.com
This software is free software: you clan redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 2.1 of
the License, or (at your option) any later version.
This 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 C! library. If not, see
<http://www.gnu.org/licenses/>.
\******************************************************************************/
'use strict'
var orbit = require('./orbit');
var cookie = require('./cookie')('bbctrl-');
var font = require('./helvetiker_regular.typeface.json')
module.exports = { module.exports = {
template: '#path-viewer-template', template: "#path-viewer-template",
props: ['toolpath'], props: ["toolpath"],
data: function () { data: function () {
return { return {
enabled: false, enabled: false,
loading: false, loading: false,
dirty: true, dirty: true,
snapView: cookie.get('snap-view', 'isometric'), snapView: cookie.get("snap-view", "isometric"),
small: cookie.get_bool('small-path-view', true), small: cookie.get_bool("small-path-view", true),
surfaceMode: 'cut', surfaceMode: "cut",
showPath: cookie.get_bool('show-path', true), showPath: cookie.get_bool("show-path", true),
showTool: cookie.get_bool('show-tool', true), showTool: cookie.get_bool("show-tool", true),
showBBox: cookie.get_bool('show-bbox', true), showBBox: cookie.get_bool("show-bbox", true),
showAxes: cookie.get_bool('show-axes', true), showAxes: cookie.get_bool("show-axes", true),
showIntensity: cookie.get_bool('show-intensity', false) showIntensity: cookie.get_bool("show-intensity", false)
} };
}, },
computed: { computed: {
target: function () { target: function () {
return $(this.$el).find('.path-viewer-content')[0] return $(this.$el).find(".path-viewer-content")[0];
}, },
webglAvailable: function() { webglAvailable: function() {
@@ -69,61 +43,64 @@ module.exports = {
} }
}, },
watch: { watch: {
toolpath: function () {Vue.nextTick(this.update)}, toolpath: function () {
surfaceMode: function (mode) {this.update_surface_mode(mode)}, Vue.nextTick(this.update);
},
surfaceMode: function (mode) {
this.update_surface_mode(mode);
},
small: function (enable) { small: function (enable) {
cookie.set_bool('small-path-view', enable); cookie.set_bool("small-path-view", enable);
Vue.nextTick(this.update_view) Vue.nextTick(this.update_view);
}, },
showPath: function (enable) { showPath: function (enable) {
cookie.set_bool('show-path', enable); cookie.set_bool("show-path", enable);
this.set_visible(this.pathView, enable) this.set_visible(this.pathView, enable);
}, },
showTool: function (enable) { showTool: function (enable) {
cookie.set_bool('show-tool', enable); cookie.set_bool("show-tool", enable);
this.set_visible(this.toolView, enable) this.set_visible(this.toolView, enable);
}, },
showAxes: function (enable) { showAxes: function (enable) {
cookie.set_bool('show-axes', enable); cookie.set_bool("show-axes", enable);
this.set_visible(this.axesView, enable) this.set_visible(this.axesView, enable);
}, },
showIntensity: function (enable) { showIntensity: function (enable) {
cookie.set_bool('show-intensity', enable); cookie.set_bool("show-intensity", enable);
Vue.nextTick(this.update) Vue.nextTick(this.update);
}, },
showBBox: function (enable) { showBBox: function (enable) {
cookie.set_bool('show-bbox', enable); cookie.set_bool("show-bbox", enable);
this.set_visible(this.bboxView, enable); this.set_visible(this.bboxView, enable);
this.set_visible(this.envelopeView, enable); this.set_visible(this.envelopeView, enable);
}, },
x: function () {
x: function () {this.axis_changed()}, this.axis_changed();
y: function () {this.axis_changed()},
z: function () {this.axis_changed()}
}, },
y: function () {
this.axis_changed();
},
z: function () {
this.axis_changed();
}
},
ready: function () { ready: function () {
this.graphics(); this.graphics();
Vue.nextTick(this.update); Vue.nextTick(this.update);
}, },
methods: { methods: {
update: async function () { update: async function () {
if (!this.webglAvailable) { if (!this.webglAvailable) {
@@ -140,7 +117,9 @@ module.exports = {
this.draw_loading(); this.draw_loading();
} }
if (!this.enabled || !this.toolpath.filename) return; if (!this.enabled || !this.toolpath.filename) {
return;
}
async function get(url) { async function get(url) {
const response = await fetch(`${url}?${Math.random()}`); const response = await fetch(`${url}?${Math.random()}`);
@@ -150,11 +129,11 @@ module.exports = {
} }
const [positions, speeds] = await Promise.all([ const [positions, speeds] = await Promise.all([
get('/api/path/' + this.toolpath.filename + '/positions'), get(`/api/path/${this.toolpath.filename}/positions`),
get('/api/path/' + this.toolpath.filename + '/speeds') get(`/api/path/${this.toolpath.filename}/speeds`)
]); ]);
this.positions = positions this.positions = positions;
this.speeds = speeds; this.speeds = speeds;
this.loading = false; this.loading = false;
@@ -166,22 +145,22 @@ module.exports = {
this.update_view(); this.update_view();
}, },
update_surface_mode: function (mode) { update_surface_mode: function (mode) {
if (!this.enabled) return; if (!this.enabled) {
return;
}
if (typeof this.surfaceMaterial != 'undefined') { if (typeof this.surfaceMaterial != "undefined") {
this.surfaceMaterial.wireframe = mode == 'wire'; this.surfaceMaterial.wireframe = mode == "wire";
this.surfaceMaterial.needsUpdate = true; this.surfaceMaterial.needsUpdate = true;
} }
this.set_visible(this.surfaceMesh, mode == 'cut' || mode == 'wire'); this.set_visible(this.surfaceMesh, mode == "cut" || mode == "wire");
this.set_visible(this.workpieceMesh, mode == 'solid'); this.set_visible(this.workpieceMesh, mode == "solid");
}, },
load_surface: function (surface) { load_surface: function (surface) {
if (typeof surface == 'undefined') { if (typeof surface == "undefined") {
this.vertices = undefined; this.vertices = undefined;
this.normals = undefined; this.normals = undefined;
return; return;
@@ -191,30 +170,35 @@ module.exports = {
// Expand normals // Expand normals
this.normals = []; this.normals = [];
for (var i = 0; i < surface.normals.length / 3; i++) for (let i = 0; i < surface.normals.length / 3; i++) {
for (var j = 0; j < 3; j++) for (let j = 0; j < 3; j++) {
for (var k = 0; k < 3; k++) for (let k = 0; k < 3; k++) {
this.normals.push(surface.normals[i * 3 + k]); this.normals.push(surface.normals[i * 3 + k]);
}
}
}
}, },
set_visible: function (target, visible) { set_visible: function (target, visible) {
if (typeof target != 'undefined') target.visible = visible; if (typeof target != "undefined") {
target.visible = visible;
}
this.dirty = true; this.dirty = true;
}, },
get_dims: function () { get_dims: function () {
var t = $(this.target); const t = $(this.target);
var width = t.innerWidth(); const width = t.innerWidth();
var height = t.innerHeight(); const height = t.innerHeight();
return {width: width, height: height}; return {width: width, height: height};
}, },
update_view: function () { update_view: function () {
if (!this.enabled) return; if (!this.enabled) {
var dims = this.get_dims(); return;
}
const dims = this.get_dims();
this.camera.aspect = dims.width / dims.height; this.camera.aspect = dims.width / dims.height;
this.camera.updateProjectionMatrix(); this.camera.updateProjectionMatrix();
@@ -229,47 +213,59 @@ module.exports = {
this.dirty = true; this.dirty = true;
}, },
update_tool: function (tool) { update_tool: function (tool) {
if (!this.enabled) return; if (!this.enabled) {
if (typeof tool == 'undefined') tool = this.toolView; return;
if (typeof tool == 'undefined') return; }
if (typeof tool == "undefined") {
tool = this.toolView;
}
if (typeof tool == "undefined") {
return;
}
tool.position.x = this.x.pos; tool.position.x = this.x.pos;
tool.position.y = this.y.pos; tool.position.y = this.y.pos;
tool.position.z = this.z.pos; tool.position.z = this.z.pos;
}, },
update_envelope: function (envelope) { update_envelope: function (envelope) {
if (!this.enabled || !this.axes.homed) return; if (!this.enabled || !this.axes.homed) {
if (typeof envelope == 'undefined') envelope = this.envelopeView; return;
if (typeof envelope == 'undefined') return; }
var min = new THREE.Vector3(); if (typeof envelope == "undefined") {
var max = new THREE.Vector3(); envelope = this.envelopeView;
}
for (var axis of 'xyz') { if (typeof envelope == "undefined") {
return;
}
const min = new THREE.Vector3();
const max = new THREE.Vector3();
for (const axis of "xyz") {
min[axis] = this[axis].min - this[axis].off; min[axis] = this[axis].min - this[axis].off;
max[axis] = this[axis].max - this[axis].off; max[axis] = this[axis].max - this[axis].off;
} }
var bounds = new THREE.Box3(min, max); const bounds = new THREE.Box3(min, max);
if (bounds.isEmpty()) { if (bounds.isEmpty()) {
envelope.geometry = this.create_empty_geom(); envelope.geometry = this.create_empty_geom();
} } else {
else {
envelope.geometry = this.create_bbox_geom(bounds); envelope.geometry = this.create_bbox_geom(bounds);
} }
}, },
axis_changed: function () { axis_changed: function () {
this.update_tool(); this.update_tool();
this.update_envelope(); this.update_envelope();
this.dirty = true; this.dirty = true;
}, },
graphics: function () { graphics: function () {
if (!this.webglAvailable) { if (!this.webglAvailable) {
return; return;
@@ -283,7 +279,7 @@ module.exports = {
this.target.appendChild(this.renderer.domElement); this.target.appendChild(this.renderer.domElement);
} catch (e) { } catch (e) {
console.log('WebGL not supported: ', e); console.log("WebGL not supported: ", e);
return; return;
} }
@@ -295,15 +291,13 @@ module.exports = {
// Lighting // Lighting
this.ambient = new THREE.AmbientLight(0xffffff, 0.5); this.ambient = new THREE.AmbientLight(0xffffff, 0.5);
var keyLight = new THREE.DirectionalLight const keyLight = new THREE.DirectionalLight(new THREE.Color("hsl(30, 100%, 75%)"), 0.75);
(new THREE.Color('hsl(30, 100%, 75%)'), 0.75);
keyLight.position.set(-100, 0, 100); keyLight.position.set(-100, 0, 100);
var fillLight = new THREE.DirectionalLight const fillLight = new THREE.DirectionalLight(new THREE.Color("hsl(240, 100%, 75%)"), 0.25);
(new THREE.Color('hsl(240, 100%, 75%)'), 0.25);
fillLight.position.set(100, 0, 100); fillLight.position.set(100, 0, 100);
var backLight = new THREE.DirectionalLight(0xffffff, 0.5); const backLight = new THREE.DirectionalLight(0xffffff, 0.5);
backLight.position.set(100, 0, -100).normalize(); backLight.position.set(100, 0, -100).normalize();
this.lights = new THREE.Group(); this.lights = new THREE.Group();
@@ -322,7 +316,7 @@ module.exports = {
this.controls.enableZoom = true; this.controls.enableZoom = true;
// Move lights with scene // Move lights with scene
this.controls.addEventListener('change', function (scope) { this.controls.addEventListener("change", function (scope) {
return function () { return function () {
keyLight.position.copy(scope.camera.position); keyLight.position.copy(scope.camera.position);
fillLight.position.copy(scope.camera.position); fillLight.position.copy(scope.camera.position);
@@ -331,17 +325,16 @@ module.exports = {
fillLight.lookAt(scope.controls.target); fillLight.lookAt(scope.controls.target);
backLight.lookAt(scope.controls.target); backLight.lookAt(scope.controls.target);
scope.dirty = true; scope.dirty = true;
} };
}(this)) }(this));
// Events // Events
window.addEventListener('resize', this.update_view, false); window.addEventListener("resize", this.update_view, false);
// Start it // Start it
this.render(); this.render();
}, },
create_surface_material: function () { create_surface_material: function () {
return new THREE.MeshPhongMaterial({ return new THREE.MeshPhongMaterial({
specular: 0x111111, specular: 0x111111,
@@ -351,11 +344,10 @@ module.exports = {
}); });
}, },
draw_loading: function () { draw_loading: function () {
this.scene = new THREE.Scene(); this.scene = new THREE.Scene();
var geometry = new THREE.TextGeometry('Loading 3D View...', { const geometry = new THREE.TextGeometry("Loading 3D View...", {
font: new THREE.Font(font), font: new THREE.Font(font),
size: 40, size: 40,
height: 5, height: 5,
@@ -367,8 +359,7 @@ module.exports = {
}); });
geometry.computeBoundingBox(); geometry.computeBoundingBox();
var center = geometry.center(); const mesh = new THREE.Mesh(geometry, this.surfaceMaterial);
var mesh = new THREE.Mesh(geometry, this.surfaceMaterial);
this.scene.add(mesh); this.scene.add(mesh);
this.scene.add(this.ambient); this.scene.add(this.ambient);
@@ -376,21 +367,22 @@ module.exports = {
this.update_view(); this.update_view();
}, },
draw_workpiece: function (scene, material) { draw_workpiece: function (scene, material) {
if (typeof this.workpiece == 'undefined') return; if (typeof this.workpiece == "undefined") {
return;
}
var min = this.workpiece.min; let min = this.workpiece.min;
var max = this.workpiece.max; let max = this.workpiece.max;
min = new THREE.Vector3(min[0], min[1], min[2]); min = new THREE.Vector3(min[0], min[1], min[2]);
max = new THREE.Vector3(max[0], max[1], max[2]); max = new THREE.Vector3(max[0], max[1], max[2]);
var dims = max.clone().sub(min); const dims = max.clone().sub(min);
var geometry = new THREE.BoxGeometry(dims.x, dims.y, dims.z) const geometry = new THREE.BoxGeometry(dims.x, dims.y, dims.z);
var mesh = new THREE.Mesh(geometry, material); const mesh = new THREE.Mesh(geometry, material);
var offset = dims.clone(); const offset = dims.clone();
offset.divideScalar(2); offset.divideScalar(2);
offset.add(min); offset.add(min);
@@ -403,16 +395,15 @@ module.exports = {
return mesh; return mesh;
}, },
draw_surface: function (scene, material) { draw_surface: function (scene, material) {
if (typeof this.vertices == 'undefined') return; if (typeof this.vertices == "undefined") {
return;
}
var geometry = new THREE.BufferGeometry(); const geometry = new THREE.BufferGeometry();
geometry.addAttribute geometry.addAttribute("position", new THREE.Float32BufferAttribute(this.vertices, 3));
('position', new THREE.Float32BufferAttribute(this.vertices, 3)); geometry.addAttribute("normal", new THREE.Float32BufferAttribute(this.normals, 3));
geometry.addAttribute
('normal', new THREE.Float32BufferAttribute(this.normals, 3));
geometry.computeBoundingSphere(); geometry.computeBoundingSphere();
geometry.computeBoundingBox(); geometry.computeBoundingBox();
@@ -420,15 +411,16 @@ module.exports = {
return new THREE.Mesh(geometry, material); return new THREE.Mesh(geometry, material);
}, },
draw_tool: function (scene, bbox) { draw_tool: function (scene, bbox) {
// Tool size is relative to bounds // Tool size is relative to bounds
var size = bbox.getSize(new THREE.Vector3()); const size = bbox.getSize(new THREE.Vector3());
var length = (size.x + size.y + size.z) / 24; let length = (size.x + size.y + size.z) / 24;
if (length < 1) length = 1; if (length < 1) {
length = 1;
}
var material = new THREE.MeshPhongMaterial({ const material = new THREE.MeshPhongMaterial({
transparent: true, transparent: true,
opacity: 0.75, opacity: 0.75,
specular: 0x161616, specular: 0x161616,
@@ -436,30 +428,33 @@ module.exports = {
color: 0xffa500 // Orange color: 0xffa500 // Orange
}); });
var geometry = new THREE.CylinderGeometry(length / 2, 0, length, 128); const geometry = new THREE.CylinderGeometry(length / 2, 0, length, 128);
geometry.translate(0, length / 2, 0); geometry.translate(0, length / 2, 0);
geometry.rotateX(0.5 * Math.PI); geometry.rotateX(0.5 * Math.PI);
var mesh = new THREE.Mesh(geometry, material); const mesh = new THREE.Mesh(geometry, material);
this.update_tool(mesh); this.update_tool(mesh);
mesh.visible = this.showTool; mesh.visible = this.showTool;
scene.add(mesh); scene.add(mesh);
return mesh; return mesh;
}, },
draw_axis: function (axis, up, length, radius) { draw_axis: function (axis, up, length, radius) {
var color; let color;
if (axis == 0) color = 0xff0000; // Red if (axis == 0) {
else if (axis == 1) color = 0x00ff00; // Green color = 0xff0000;
else if (axis == 2) color = 0x0000ff; // Blue } else if (axis == 1) {
color = 0x00ff00;
} else if (axis == 2) {
color = 0x0000ff;
}
var group = new THREE.Group(); const group = new THREE.Group();
var material = new THREE.MeshPhongMaterial({ const material = new THREE.MeshPhongMaterial({
specular: 0x161616, shininess: 10, color: color specular: 0x161616, shininess: 10, color: color
}); });
var geometry = new THREE.CylinderGeometry(radius, radius, length, 128); let geometry = new THREE.CylinderGeometry(radius, radius, length, 128);
geometry.translate(0, -length / 2, 0); geometry.translate(0, -length / 2, 0);
group.add(new THREE.Mesh(geometry, material)); group.add(new THREE.Mesh(geometry, material));
@@ -467,28 +462,35 @@ module.exports = {
geometry.translate(0, -length - radius, 0); geometry.translate(0, -length - radius, 0);
group.add(new THREE.Mesh(geometry, material)); group.add(new THREE.Mesh(geometry, material));
if (axis == 0) group.rotateZ((up ? 0.5 : 1.5) * Math.PI); if (axis == 0) {
else if (axis == 1) group.rotateX((up ? 0 : 1 ) * Math.PI); group.rotateZ((up ? 0.5 : 1.5) * Math.PI);
else if (axis == 2) group.rotateX((up ? 1.5 : 0.5) * Math.PI); } else if (axis == 1) {
group.rotateX((up ? 0 : 1 ) * Math.PI);
} else if (axis == 2) {
group.rotateX((up ? 1.5 : 0.5) * Math.PI);
}
return group; return group;
}, },
draw_axes: function (scene, bbox) { draw_axes: function (scene, bbox) {
var size = bbox.getSize(new THREE.Vector3()); const size = bbox.getSize(new THREE.Vector3());
var length = (size.x + size.y + size.z) / 3; let length = (size.x + size.y + size.z) / 3;
length /= 10; length /= 10;
if (length < 1) length = 1; if (length < 1) {
length = 1;
}
var radius = length / 20; const radius = length / 20;
var group = new THREE.Group(); const group = new THREE.Group();
for (var axis = 0; axis < 3; axis++) for (let axis = 0; axis < 3; axis++) {
for (var up = 0; up < 2; up++) for (let up = 0; up < 2; up++) {
group.add(this.draw_axis(axis, up, length, radius)); group.add(this.draw_axis(axis, up, length, radius));
}
}
group.visible = this.showAxes; group.visible = this.showAxes;
scene.add(group); scene.add(group);
@@ -496,40 +498,42 @@ module.exports = {
return group; return group;
}, },
get_color: function (speed) { get_color: function (speed) {
if (isNaN(speed)) return [255, 0, 0]; // Rapid if (isNaN(speed)) {
return [255, 0, 0];
} // Rapid
let intensity = speed / this.toolpath.maxSpeed;
if (typeof speed == "undefined" || !this.showIntensity) {
intensity = 1;
}
var intensity = speed / this.toolpath.maxSpeed;
if (typeof speed == 'undefined' || !this.showIntensity) intensity = 1;
return [0, 255 * intensity, 127 * (1 - intensity)]; return [0, 255 * intensity, 127 * (1 - intensity)];
}, },
draw_path: function (scene) { draw_path: function (scene) {
var geometry = new THREE.BufferGeometry(); const geometry = new THREE.BufferGeometry();
var material = const material = new THREE.LineBasicMaterial({
new THREE.LineBasicMaterial({
vertexColors: THREE.VertexColors, vertexColors: THREE.VertexColors,
linewidth: 1.5 linewidth: 1.5
}); });
var positions = new THREE.Float32BufferAttribute(this.positions, 3); const positions = new THREE.Float32BufferAttribute(this.positions, 3);
geometry.addAttribute('position', positions); geometry.addAttribute("position", positions);
var colors = []; let colors = [];
for (var i = 0; i < this.speeds.length; i++) { for (let i = 0; i < this.speeds.length; i++) {
var color = this.get_color(this.speeds[i]); const color = this.get_color(this.speeds[i]);
Array.prototype.push.apply(colors, color); Array.prototype.push.apply(colors, color);
} }
colors = new THREE.Uint8BufferAttribute(colors, 3, true); colors = new THREE.Uint8BufferAttribute(colors, 3, true);
geometry.addAttribute('color', colors); geometry.addAttribute("color", colors);
geometry.computeBoundingSphere(); geometry.computeBoundingSphere();
geometry.computeBoundingBox(); geometry.computeBoundingBox();
var line = new THREE.Line(geometry, material); const line = new THREE.Line(geometry, material);
line.visible = this.showPath; line.visible = this.showPath;
scene.add(line); scene.add(line);
@@ -537,17 +541,15 @@ module.exports = {
return line; return line;
}, },
create_empty_geom: function () { create_empty_geom: function () {
var geometry = new THREE.BufferGeometry(); const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', geometry.addAttribute("position",
new THREE.Float32BufferAttribute([], 3)); new THREE.Float32BufferAttribute([], 3));
return geometry; return geometry;
}, },
create_bbox_geom: function (bbox) { create_bbox_geom: function (bbox) {
var vertices = []; const vertices = [];
if (!bbox.isEmpty()) { if (!bbox.isEmpty()) {
// Top // Top
@@ -581,19 +583,18 @@ module.exports = {
vertices.push(bbox.min.x, bbox.max.y, bbox.max.z); vertices.push(bbox.min.x, bbox.max.y, bbox.max.z);
} }
var geometry = new THREE.BufferGeometry(); const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', geometry.addAttribute("position",
new THREE.Float32BufferAttribute(vertices, 3)); new THREE.Float32BufferAttribute(vertices, 3));
return geometry; return geometry;
}, },
draw_bbox: function (scene, bbox) { draw_bbox: function (scene, bbox) {
var geometry = this.create_bbox_geom(bbox); const geometry = this.create_bbox_geom(bbox);
var material = new THREE.LineBasicMaterial({color: 0xffffff}); const material = new THREE.LineBasicMaterial({color: 0xffffff});
var line = new THREE.LineSegments(geometry, material); const line = new THREE.LineSegments(geometry, material);
line.visible = this.showBBox; line.visible = this.showBBox;
@@ -602,11 +603,10 @@ module.exports = {
return line; return line;
}, },
draw_envelope: function (scene) { draw_envelope: function (scene) {
var geometry = this.create_empty_geom(); const geometry = this.create_empty_geom();
var material = new THREE.LineBasicMaterial({color: 0x00f7ff}); const material = new THREE.LineBasicMaterial({color: 0x00f7ff});
var line = new THREE.LineSegments(geometry, material); const line = new THREE.LineSegments(geometry, material);
line.visible = this.showBBox; line.visible = this.showBBox;
@@ -616,7 +616,6 @@ module.exports = {
return line; return line;
}, },
draw: function (scene) { draw: function (scene) {
// Lights // Lights
scene.add(this.ambient); scene.add(this.ambient);
@@ -629,7 +628,7 @@ module.exports = {
this.update_surface_mode(this.surfaceMode); this.update_surface_mode(this.surfaceMode);
// Compute bounding box // Compute bounding box
var bbox = this.get_model_bounds(); const bbox = this.get_model_bounds();
// Tool, axes & bounds // Tool, axes & bounds
this.toolView = this.draw_tool(scene, bbox); this.toolView = this.draw_tool(scene, bbox);
@@ -638,10 +637,12 @@ module.exports = {
this.envelopeView = this.draw_envelope(scene); this.envelopeView = this.draw_envelope(scene);
}, },
render: function () { render: function () {
window.requestAnimationFrame(this.render); window.requestAnimationFrame(this.render);
if (typeof this.scene == 'undefined') return;
if (typeof this.scene == "undefined") {
return;
}
if (this.controls.update() || this.dirty) { if (this.controls.update() || this.dirty) {
this.dirty = false; this.dirty = false;
@@ -649,14 +650,13 @@ module.exports = {
} }
}, },
get_model_bounds: function () { get_model_bounds: function () {
var bbox = new THREE.Box3(new THREE.Vector3(0, 0, 0), const bbox = new THREE.Box3(new THREE.Vector3(0, 0, 0),
new THREE.Vector3(0.00001, 0.00001, 0.00001)); new THREE.Vector3(0.00001, 0.00001, 0.00001));
function add(o) { function add(o) {
if (typeof o != 'undefined') { if (typeof o != "undefined") {
var oBBox = new THREE.Box3(); const oBBox = new THREE.Box3();
oBBox.setFromObject(o); oBBox.setFromObject(o);
bbox.union(oBBox); bbox.union(oBBox);
} }
@@ -669,45 +669,53 @@ module.exports = {
return bbox; return bbox;
}, },
snap: function (view) { snap: function (view) {
if (this.loading) return; if (this.loading) {
if (view != this.snapView) { return;
this.snapView = view;
cookie.set('snap-view', view);
} }
var bbox = this.get_model_bounds(); if (view != this.snapView) {
this.snapView = view;
cookie.set("snap-view", view);
}
const bbox = this.get_model_bounds();
this.controls.reset(); this.controls.reset();
bbox.getCenter(this.controls.target); bbox.getCenter(this.controls.target);
this.update_view(); this.update_view();
// Compute new camera position // Compute new camera position
var center = bbox.getCenter(new THREE.Vector3()); const center = bbox.getCenter(new THREE.Vector3());
var offset = new THREE.Vector3(); const offset = new THREE.Vector3();
switch (view) {
case "isometric": offset.y -= 1; offset.z += 1; break;
case "front": offset.y -= 1; break;
case "back": offset.y += 1; break;
case "left": offset.x -= 1; break;
case "right": offset.x += 1; break;
case "top": offset.z += 1; break;
case "bottom": offset.z -= 1; break;
}
if (view == 'isometric') {offset.y -= 1; offset.z += 1;}
if (view == 'front') offset.y -= 1;
if (view == 'back') offset.y += 1;
if (view == 'left') offset.x -= 1;
if (view == 'right') offset.x += 1;
if (view == 'top') offset.z += 1;
if (view == 'bottom') offset.z -= 1;
offset.normalize(); offset.normalize();
// Initial camera position // Initial camera position
var position = new THREE.Vector3().copy(center).add(offset); const position = new THREE.Vector3().copy(center).add(offset);
this.camera.position.copy(position); this.camera.position.copy(position);
this.camera.lookAt(center); // Get correct camera orientation this.camera.lookAt(center); // Get correct camera orientation
var theta = this.camera.fov / 180 * Math.PI; // View angle const theta = this.camera.fov / 180 * Math.PI; // View angle
var cameraLine = new THREE.Line3(center, position); const cameraLine = new THREE.Line3(center, position);
var cameraUp = new THREE.Vector3().copy(this.camera.up) const cameraUp = new THREE.Vector3()
.copy(this.camera.up)
.applyQuaternion(this.camera.quaternion); .applyQuaternion(this.camera.quaternion);
var cameraLeft = const cameraLeft = new THREE.Vector3()
new THREE.Vector3().copy(offset).cross(cameraUp).normalize(); .copy(offset)
.cross(cameraUp)
.normalize();
var corners = [ const corners = [
new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z), new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z),
new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.max.z), new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.max.z),
new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.min.z), new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.min.z),
@@ -716,39 +724,37 @@ module.exports = {
new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z), new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z),
new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.min.z), new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.min.z),
new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.max.z), new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.max.z),
] ];
var dist = this.camera.near; // Min camera dist let dist = this.camera.near; // Min camera dist
for (var i = 0; i < corners.length; i++) { for (let i = 0; i < corners.length; i++) {
// Project on to camera line // Project on to camera line
var p1 = cameraLine const p1 = cameraLine.closestPointToPoint(corners[i], false, new THREE.Vector3());
.closestPointToPoint(corners[i], false, new THREE.Vector3());
// Compute distance from projection to center // Compute distance from projection to center
var d = p1.distanceTo(center); let d = p1.distanceTo(center);
if (cameraLine.closestPointToPointParameter(p1, false) < 0) d = -d; if (cameraLine.closestPointToPointParameter(p1, false) < 0) {
d = -d;
}
// Compute up line // Compute up line
var up = const up = new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraUp));
new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraUp));
// Project on to up line // Project on to up line
var p2 = up.closestPointToPoint(corners[i], false, new THREE.Vector3()); const p2 = up.closestPointToPoint(corners[i], false, new THREE.Vector3());
// Compute length // Compute length
var l = p1.distanceTo(p2); let l = p1.distanceTo(p2);
// Update min camera distance // Update min camera distance
dist = Math.max(dist, d + l / Math.tan(theta / 2)); dist = Math.max(dist, d + l / Math.tan(theta / 2));
// Compute left line // Compute left line
var left = const left = new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraLeft));
new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraLeft));
// Project on to left line // Project on to left line
var p3 = const p3 = left.closestPointToPoint(corners[i], false, new THREE.Vector3());
left.closestPointToPoint(corners[i], false, new THREE.Vector3());
// Compute length // Compute length
l = p1.distanceTo(p3); l = p1.distanceTo(p3);
@@ -761,6 +767,5 @@ module.exports = {
} }
}, },
mixins: [require("./axis-vars")]
mixins: [require('./axis-vars')] };
}

View File

@@ -1,38 +1,16 @@
/******************************************************************************\ "use strict";
This file is part of the Buildbotics firmware. const Sock = function (url, retry, timeout) {
if (!(this instanceof Sock)) {
return new Sock(url, retry);
}
Copyright (c) 2015 - 2018, Buildbotics LLC if (typeof retry == "undefined") {
All rights reserved. retry = 2000;
}
This file ("the software") is free software: you can redistribute it if (typeof timeout == "undefined") {
and/or modify it under the terms of the GNU General Public License, timeout = 16000;
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 Sock = function (url, retry, timeout) {
if (!(this instanceof Sock)) return new Sock(url, retry);
if (typeof retry == 'undefined') retry = 2000;
if (typeof timeout == 'undefined') timeout = 16000;
this.url = url; this.url = url;
this.retry = retry; this.retry = retry;
@@ -41,87 +19,88 @@ var Sock = function (url, retry, timeout) {
this.count = 0; this.count = 0;
this.connect(); this.connect();
} };
Sock.prototype.onmessage = function () {
// Ignore
};
Sock.prototype.onmessage = function () {} Sock.prototype.onopen = function () {
Sock.prototype.onopen = function () {} // Ignore
Sock.prototype.onclose = function () {} };
Sock.prototype.onclose = function () {
// Ignore
};
Sock.prototype.connect = function () { Sock.prototype.connect = function () {
console.debug('connecting to', this.url); console.debug("connecting to", this.url);
this.close(); this.close();
this._sock = new SockJS(this.url); this._sock = new SockJS(this.url);
this._sock.onmessage = function (e) { this._sock.onmessage = function (e) {
console.debug('msg:', e.data); console.debug("msg:", e.data);
this.heartbeat('msg'); this.heartbeat("msg");
this.onmessage(e); this.onmessage(e);
}.bind(this); }.bind(this);
this._sock.onopen = function () { this._sock.onopen = function () {
console.debug('connected'); console.debug("connected");
this.heartbeat('open'); this.heartbeat("open");
this.onopen(); this.onopen();
}.bind(this); }.bind(this);
this._sock.onclose = function () { this._sock.onclose = function () {
console.debug('disconnected'); console.debug("disconnected");
this._cancel_timeout(); this._cancel_timeout();
this.onclose(); this.onclose();
if (typeof this._sock != 'undefined') if (typeof this._sock != "undefined") {
setTimeout(this.connect.bind(this), this.retry); setTimeout(this.connect.bind(this), this.retry);
}
}.bind(this); }.bind(this);
} };
Sock.prototype._timedout = function () { Sock.prototype._timedout = function () {
// Divide timeout so slow browser doesn't trigger timeouts when the // Divide timeout so slow browser doesn't trigger timeouts when the
// connection is good. // connection is good.
if (this.divisions <= ++this.count) { if (this.divisions <= ++this.count) {
console.debug('connection timedout'); console.debug("connection timedout");
this._timeout = undefined; this._timeout = undefined;
this._sock.close(); this._sock.close();
} else this._set_timeout(); } else {
} this._set_timeout();
}
};
Sock.prototype._cancel_timeout = function () { Sock.prototype._cancel_timeout = function () {
clearTimeout(this._timeout); clearTimeout(this._timeout);
this._timeout = undefined; this._timeout = undefined;
this.count = 0; this.count = 0;
} };
Sock.prototype._set_timeout = function () { Sock.prototype._set_timeout = function () {
this._timeout = setTimeout(this._timedout.bind(this), this._timeout = setTimeout(this._timedout.bind(this),
this.timeout / this.divisions); this.timeout / this.divisions);
} };
Sock.prototype.heartbeat = function () {
Sock.prototype.heartbeat = function (msg) {
//console.debug('heartbeat ' + new Date().toLocaleTimeString() + ' ' + msg);
this._cancel_timeout(); this._cancel_timeout();
this._set_timeout(); this._set_timeout();
} };
Sock.prototype.close = function () { Sock.prototype.close = function () {
if (typeof this._sock != 'undefined') { if (typeof this._sock != "undefined") {
var sock = this._sock; const sock = this._sock;
this._sock = undefined; this._sock = undefined;
sock.close(); sock.close();
} }
} };
Sock.prototype.send = function (msg) {
this._sock.send(msg);
};
Sock.prototype.send = function (msg) {this._sock.send(msg)} module.exports = Sock;
module.exports = Sock

View File

@@ -1,12 +1,12 @@
'use strict' "use strict";
module.exports = { module.exports = {
replace: true, replace: true,
template: '#templated-input-template', template: "#templated-input-template",
props: ['name', 'model', 'template'], props: ["name", "model", "template"],
data: function () { data: function () {
return { view: '' } return { view: "" };
}, },
computed: { computed: {
@@ -33,10 +33,10 @@ module.exports = {
}, },
title: function () { title: function () {
var s = `Default :${this.template.default} ${(this.template.unit || '')}`; let s = `Default :${this.template.default} ${(this.template.unit || "")}`;
if (typeof this.template.help != 'undefined') { if (typeof this.template.help != "undefined") {
s = this.template.help + '\n' + s; s = `${this.template.help}\n${s}`;
} }
return s; return s;
@@ -45,7 +45,7 @@ module.exports = {
watch: { watch: {
_view: function () { _view: function () {
this.view = this._view this.view = this._view;
}, },
view: function () { view: function () {
@@ -58,12 +58,12 @@ module.exports = {
}, },
ready: function () { ready: function () {
this.view = this._view this.view = this._view;
}, },
methods: { methods: {
change: function () { change: function () {
this.$dispatch('input-changed') this.$dispatch("input-changed");
} }
} }
} };

View File

@@ -1,39 +1,12 @@
/******************************************************************************\ "use strict";
This file is part of the Buildbotics firmware. const api = require("./api");
const modbus = require("./modbus.js");
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';
const api = require('./api');
const modbus = require('./modbus.js');
const merge = require("lodash.merge"); const merge = require("lodash.merge");
module.exports = { module.exports = {
template: '#tool-view-template', template: "#tool-view-template",
props: ['config', 'template', 'state'], props: ["config", "template", "state"],
data: function () { data: function () {
return { return {
@@ -110,20 +83,22 @@ module.exports = {
unsupported: true unsupported: true
} }
] ]
} };
}, },
components: { components: {
'modbus-reg': require('./modbus-reg.js') "modbus-reg": require("./modbus-reg.js")
}, },
watch: { watch: {
'state.mr': function () { this.value = this.state.mr } "state.mr": function () {
this.value = this.state.mr;
}
}, },
events: { events: {
'input-changed': function () { "input-changed": function () {
this.$dispatch('config-changed'); this.$dispatch("config-changed");
return false; return false;
}, },
@@ -135,19 +110,19 @@ module.exports = {
computed: { computed: {
regs_tmpl: function () { regs_tmpl: function () {
return this.template['modbus-spindle'].regs; return this.template["modbus-spindle"].regs;
}, },
tool_type: function () { tool_type: function () {
return this.config.tool['tool-type'].toUpperCase(); return this.config.tool["tool-type"].toUpperCase();
}, },
selected_tool: function () { selected_tool: function () {
return this.config.tool['selected-tool']; return this.config.tool["selected-tool"];
}, },
is_pwm_spindle: function () { is_pwm_spindle: function () {
return this.selected_tool == 'pwm'; return this.selected_tool == "pwm";
}, },
is_modbus: function () { is_modbus: function () {
@@ -170,13 +145,13 @@ module.exports = {
methods: { methods: {
change_selected_tool: function () { change_selected_tool: function () {
const selectedToolSettings = this.config['selected-tool-settings'] || {}; const selectedToolSettings = this.config["selected-tool-settings"] || {};
const settings = selectedToolSettings[this.selected_tool] || {}; const settings = selectedToolSettings[this.selected_tool] || {};
this.config.tool = merge({}, this.config.tool, settings['tool']); this.config.tool = merge({}, this.config.tool, settings["tool"]);
this.config['pwm-spindle'] = merge({}, this.config['pwm-spindle'], settings['pwm-spindle']); this.config["pwm-spindle"] = merge({}, this.config["pwm-spindle"], settings["pwm-spindle"]);
this.config['modbus-spindle'] = merge({}, this.config['modbus-spindle'], settings['modbus-spindle']); this.config["modbus-spindle"] = merge({}, this.config["modbus-spindle"], settings["modbus-spindle"]);
const tool = this.toolList.find(tool => tool.id == this.config.tool['selected-tool']); const tool = this.toolList.find(tool => tool.id == this.config.tool["selected-tool"]);
this.config.tool["tool-type"] = tool.type || tool.name; this.config.tool["tool-type"] = tool.type || tool.name;
this.$dispatch("config-changed"); this.$dispatch("config-changed");
@@ -207,71 +182,71 @@ module.exports = {
}, },
get_reg_type: function (reg) { get_reg_type: function (reg) {
return this.regs_tmpl.template['reg-type'].values[this.state[reg + 'vt']]; return this.regs_tmpl.template["reg-type"].values[this.state[`${reg}vt`]];
}, },
get_reg_addr: function (reg) { get_reg_addr: function (reg) {
return this.state[reg + 'va']; return this.state[`${reg}va`];
}, },
get_reg_value: function (reg) { get_reg_value: function (reg) {
return this.state[reg + 'vv']; return this.state[`${reg}vv`];
}, },
get_reg_fails: function (reg) { get_reg_fails: function (reg) {
const fails = this.state[reg + 'vr'] const fails = this.state[`${reg}vr`];
return fails == 255 ? 'Max' : fails; return fails == 255 ? "Max" : fails;
}, },
show_modbus_field: function (key) { show_modbus_field: function (key) {
return key != 'regs' && return key != "regs" && (key != "multi-write" || this.tool_type == "CUSTOM MODBUS VFD");
(key != 'multi-write' || this.tool_type == 'CUSTOM MODBUS VFD');
}, },
read: function (e) { read: function (e) {
e.preventDefault(); e.preventDefault();
api.put('modbus/read', { address: this.address }); api.put("modbus/read", { address: this.address });
}, },
write: function (e) { write: function (e) {
e.preventDefault(); e.preventDefault();
api.put('modbus/write', { address: this.address, value: this.value }); api.put("modbus/write", { address: this.address, value: this.value });
}, },
customize: function (e) { customize: function (e) {
e.preventDefault(); e.preventDefault();
this.config.tool['tool-type'] = 'Custom Modbus VFD'; this.config.tool["tool-type"] = "Custom Modbus VFD";
const regs = this.config['modbus-spindle'].regs; const regs = this.config["modbus-spindle"].regs;
for (let i = 0; i < regs.length; i++) { for (let i = 0; i < regs.length; i++) {
const reg = this.regs_tmpl.index[i]; const reg = this.regs_tmpl.index[i];
regs[i]['reg-type'] = this.get_reg_type(reg); regs[i]["reg-type"] = this.get_reg_type(reg);
regs[i]['reg-addr'] = this.get_reg_addr(reg); regs[i]["reg-addr"] = this.get_reg_addr(reg);
regs[i]['reg-value'] = this.get_reg_value(reg); regs[i]["reg-value"] = this.get_reg_value(reg);
} }
this.$dispatch('config-changed'); this.$dispatch("config-changed");
}, },
clear: function (e) { clear: function (e) {
e.preventDefault(); e.preventDefault();
this.config.tool['tool-type'] = 'Custom Modbus VFD'; this.config.tool["tool-type"] = "Custom Modbus VFD";
const regs = this.config['modbus-spindle'].regs; const regs = this.config["modbus-spindle"].regs;
for (let i = 0; i < regs.length; i++) { for (let i = 0; i < regs.length; i++) {
regs[i]['reg-type'] = 'disabled'; regs[i]["reg-type"] = "disabled";
regs[i]['reg-addr'] = 0; regs[i]["reg-addr"] = 0;
regs[i]['reg-value'] = 0; regs[i]["reg-value"] = 0;
} }
this.$dispatch('config-changed'); this.$dispatch("config-changed");
}, },
reset_failures: function (e) { reset_failures: function (e) {
e.preventDefault(); e.preventDefault();
const regs = this.config['modbus-spindle'].regs; const regs = this.config["modbus-spindle"].regs;
for (let reg = 0; reg < regs.length; reg++) for (let reg = 0; reg < regs.length; reg++) {
this.$dispatch('send', '\$' + reg + 'vr=0'); this.$dispatch("send", `$${reg}vr=0`);
} }
} }
} }
};

View File

@@ -1,9 +1,9 @@
'use strict' "use strict";
module.exports = { module.exports = {
replace: true, replace: true,
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>', template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
props: ['value', 'precision', 'unit', 'iunit', 'scale'], props: ["value", "precision", "unit", "iunit", "scale"],
computed: { computed: {
metric: { metric: {
@@ -14,9 +14,9 @@ module.exports = {
}, },
text: function () { text: function () {
var value = this.value; let value = this.value;
if (typeof value == 'undefined') { if (typeof value == "undefined") {
return ''; return "";
} }
if (!this.metric) { if (!this.metric) {
@@ -28,20 +28,20 @@ module.exports = {
}, },
ready: function () { ready: function () {
if (typeof this.precision == 'undefined') { if (typeof this.precision == "undefined") {
this.precision = 0; this.precision = 0;
} }
if (typeof this.unit == 'undefined') { if (typeof this.unit == "undefined") {
this.unit = 'mm'; this.unit = "mm";
} }
if (typeof this.iunit == 'undefined') { if (typeof this.iunit == "undefined") {
this.iunit = 'in'; this.iunit = "in";
} }
if (typeof this.scale == 'undefined') { if (typeof this.scale == "undefined") {
this.scale = 25.4; this.scale = 25.4;
} }
} }
} };

9
src/js/utils.js Normal file
View File

@@ -0,0 +1,9 @@
function clickFileInput(formClass) {
const form = document.querySelector(`.${formClass}`);
form.reset();
form.querySelector("input").click();
}
module.exports = {
clickFileInput
};

View File

@@ -99,20 +99,26 @@
{#each $networkInfo.wifi.networks as network} {#each $networkInfo.wifi.networks as network}
<Item <Item
class="wifi-network" class="wifi-network"
on:SMUI:action={() => onNetworkSelected(network)} on:SMUI:action={() =>
onNetworkSelected(network)}
> >
<Graphic <Graphic
class="strength {$networkInfo.wifi.ssid === network.Name class="strength {$networkInfo.wifi
.ssid === network.Name
? 'active' ? 'active'
: ''}" : ''}"
> >
<span class="fa fa-wifi background" /> <span class="fa fa-wifi background" />
<span <span
class="fa fa-wifi" class="fa fa-wifi"
style={getWifiStrengthStyle(network)} style={getWifiStrengthStyle(
network
)}
/> />
</Graphic> </Graphic>
<Text style="margin-right: 20px;">{network.Name}</Text> <Text style="margin-right: 20px;"
>{network.Name}</Text
>
{#if network.Encryption !== "Open"} {#if network.Encryption !== "Open"}
<Meta> <Meta>
<span class="fa fa-lock" /> <span class="fa fa-lock" />

View File

@@ -66,21 +66,21 @@
</fieldset> </fieldset>
<p> <p>
Lower <tt>max-deviation</tt> to follow the programmed path more precisely but Lower <tt>max-deviation</tt> to follow the programmed path more precisely
at a slower speed. but at a slower speed.
</p> </p>
<p> <p>
In order to improve traversal speed, the path planner may merge consecutive In order to improve traversal speed, the path planner may merge
moves or round off sharp corners if doing so would deviate from the program consecutive moves or round off sharp corners if doing so would deviate
path by less than <tt>max-deviation</tt>. from the program path by less than <tt>max-deviation</tt>.
</p> </p>
<p> <p>
GCode commands GCode commands
<a href={`${gcodeURL}#gcode:g61`} target="_blank">G61, G61.1</a> <a href={`${gcodeURL}#gcode:g61`} target="_blank">G61, G61.1</a>
and <a href={`${gcodeURL}#gcode:g64`} target="_blank"> G64</a> also affect path and <a href={`${gcodeURL}#gcode:g64`} target="_blank"> G64</a> also affect
planning accuracy. path planning accuracy.
</p> </p>
<h2>Cornering Speed (Advanced)</h2> <h2>Cornering Speed (Advanced)</h2>
@@ -91,7 +91,7 @@
<p> <p>
Junction acceleration limits the cornering speed the planner will allow. Junction acceleration limits the cornering speed the planner will allow.
Increasing this value will allow for faster traversal of corners but may 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 cause the planner to violate axis jerk limits and stall the motors. Use
caution. with caution.
</p> </p>
</div> </div>

View File

@@ -1,5 +1,10 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
import TextField from "@smui/textfield"; import TextField from "@smui/textfield";
import MessageDialog from "$dialogs/MessageDialog.svelte"; import MessageDialog from "$dialogs/MessageDialog.svelte";
@@ -88,7 +93,11 @@
<Button> <Button>
<Label>Cancel</Label> <Label>Cancel</Label>
</Button> </Button>
<Button defaultAction on:click={onConfirm} disabled={hostname.length === 0}> <Button
defaultAction
on:click={onConfirm}
disabled={hostname.length === 0}
>
<Label>Confirm & Reboot</Label> <Label>Confirm & Reboot</Label>
</Button> </Button>
</Actions> </Actions>

View File

@@ -46,7 +46,8 @@
axis: string; axis: string;
}; };
const SetAxisPositionDialogProps = writable<SetAxisPositionDialogPropsType>(); const SetAxisPositionDialogProps =
writable<SetAxisPositionDialogPropsType>();
type SetAxisPositionDialogPropsType = { type SetAxisPositionDialogPropsType = {
open: boolean; open: boolean;
axis: string; axis: string;
@@ -190,12 +191,14 @@
); );
keyboardObserver = new MutationObserver(() => { keyboardObserver = new MutationObserver(() => {
const open = virtualKeyboard.getAttribute("_state") === "open"; const open =
virtualKeyboard.getAttribute("_state") === "open";
const keyboardHeight = Number.parseFloat( const keyboardHeight = Number.parseFloat(
virtualKeyboardOverlay.style.height virtualKeyboardOverlay.style.height
); );
const dialogContainers = document.querySelectorAll<HTMLDivElement>( const dialogContainers =
document.querySelectorAll<HTMLDivElement>(
".mdc-dialog .mdc-dialog__container" ".mdc-dialog .mdc-dialog__container"
); );

View File

@@ -1,5 +1,10 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
export let open; export let open;

View File

@@ -1,5 +1,10 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import TextField from "@smui/textfield"; import TextField from "@smui/textfield";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
import { ControllerMethods } from "$lib/RegisterControllerMethods"; import { ControllerMethods } from "$lib/RegisterControllerMethods";
@@ -21,9 +26,10 @@
aria-labelledby="manual-home-axis-dialog-title" aria-labelledby="manual-home-axis-dialog-title"
aria-describedby="manual-home-axis-dialog-content" aria-describedby="manual-home-axis-dialog-content"
> >
<Title id="manual-home-axis-dialog-title" <Title id="manual-home-axis-dialog-title">
>Manually Home {axis.toUpperCase()} Axis</Title Manually Home {axis.toUpperCase()} Axis
> </Title>
<Content id="manual-home-axis-dialog-content"> <Content id="manual-home-axis-dialog-content">
<p>Set axis absolute position</p> <p>Set axis absolute position</p>

View File

@@ -1,5 +1,10 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
export let open: boolean; export let open: boolean;

View File

@@ -50,6 +50,7 @@
"1/16 in", "1/16 in",
"1/32 in", "1/32 in",
]; ];
const metricBits: `${number} mm`[] = [ const metricBits: `${number} mm`[] = [
"12 mm", "12 mm",
"10 mm", "10 mm",
@@ -289,17 +290,21 @@
<Content id="probe-dialog-content" style="overflow: visible;"> <Content id="probe-dialog-content" style="overflow: visible;">
<div class="steps"> <div class="steps">
<p><b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b></p> <p>
<b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b>
</p>
<ul> <ul>
{#each steps as step} {#each steps as step}
<li class:active={currentStep === step}>{stepLabels[step]}</li> <li class:active={currentStep === step}>
{stepLabels[step]}
</li>
{/each} {/each}
</ul> </ul>
</div> </div>
<p> <p>
{#if currentStep === "CheckProbe"} {#if currentStep === "CheckProbe"}
Attach the probe magnet to the collet, then touch the probe block to the Attach the probe magnet to the collet, then touch the probe
bit. block to the bit.
{:else if currentStep === "BitDimensions"} {:else if currentStep === "BitDimensions"}
<TextFieldWithOptions <TextFieldWithOptions
label="Cutter diameter" label="Cutter diameter"
@@ -313,13 +318,17 @@
/> />
{:else if currentStep === "PlaceProbeBlock"} {:else if currentStep === "PlaceProbeBlock"}
{#if probeType === "xyz"} {#if probeType === "xyz"}
Place the probe block face up, on the lower-left corner of your Place the probe block face up, on the lower-left corner of
workpiece. your workpiece.
{:else} {:else}
Place the probe block face down, with the bit above the recess. Place the probe block face down, with the bit above the
recess.
{/if} {/if}
<p>The probing procedure will begin as soon as you click 'Next'.</p> <p>
The probing procedure will begin as soon as you click
'Next'.
</p>
{:else if currentStep === "Probe"} {:else if currentStep === "Probe"}
Probing in progress... Probing in progress...
{:else if currentStep === "Done"} {:else if currentStep === "Done"}
@@ -327,7 +336,9 @@
Could not find the probe block during probing! Could not find the probe block during probing!
<p> <p>
Make sure the tip of the bit is less than {metric ? "25mm" : "1 in"} Make sure the tip of the bit is less than {metric
? "25mm"
: "1 in"}
above the probe block, and try again. above the probe block, and try again.
</p> </p>
{:else} {:else}

View File

@@ -1,5 +1,10 @@
<script type="ts"> <script type="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
import Radio from "@smui/radio"; import Radio from "@smui/radio";
import FormField from "@smui/form-field"; import FormField from "@smui/form-field";

View File

@@ -1,5 +1,10 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import TextField from "@smui/textfield"; import TextField from "@smui/textfield";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
import { ControllerMethods } from "$lib/RegisterControllerMethods"; import { ControllerMethods } from "$lib/RegisterControllerMethods";
@@ -35,9 +40,10 @@
aria-labelledby="set-axis-position-dialog-title" aria-labelledby="set-axis-position-dialog-title"
aria-describedby="set-axis-position-dialog-content" aria-describedby="set-axis-position-dialog-content"
> >
<Title id="set-axis-position-dialog-title" <Title id="set-axis-position-dialog-title">
>Set {axis.toUpperCase()} Axis Position</Title Set {axis.toUpperCase()} Axis Position
> </Title>
<Content id="set-axis-position-dialog-content"> <Content id="set-axis-position-dialog-content">
<TextField <TextField
label="Position" label="Position"

View File

@@ -1,5 +1,10 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
import TextField from "@smui/textfield"; import TextField from "@smui/textfield";
import CircularProgress from "@smui/circular-progress"; import CircularProgress from "@smui/circular-progress";
@@ -131,24 +136,27 @@
<Content id="set-time-dialog-content"> <Content id="set-time-dialog-content">
{#if loading} {#if loading}
<div style="display: flex; justify-content: center"> <div style="display: flex; justify-content: center">
<CircularProgress style="height: 32px; width: 32px;" indeterminate /> <CircularProgress
style="height: 32px; width: 32px;"
indeterminate
/>
</div> </div>
{:else} {:else}
{#if networkTimeSynchronized} {#if networkTimeSynchronized}
<p> <p>
Because this controller is connected to the internet, the time is Because this controller is connected to the internet, the
managed automatically, and cannot be manually set. time is managed automatically, and cannot be manually set.
</p> </p>
{:else} {:else}
<p> <p>
Because this controller is not connected to the internet, you can Because this controller is not connected to the internet,
manually set the time. you can manually set the time.
</p> </p>
<p> <p>
Note: any time the controller is turned off, the time will need to be Note: any time the controller is turned off, the time will
reset. If you connect the controller to the internet, the time will be need to be reset. If you connect the controller to the
managed automatically. internet, the time will be managed automatically.
</p> </p>
<Label>Date & Time</Label> <Label>Date & Time</Label>
@@ -156,7 +164,10 @@
bind:value bind:value
use={[ use={[
InitialFocus, InitialFocus,
[virtualKeyboardChange, (newValue) => (value = newValue)], [
virtualKeyboardChange,
(newValue) => (value = newValue),
],
]} ]}
label="Time" label="Time"
type="datetime-local" type="datetime-local"
@@ -166,8 +177,8 @@
{/if} {/if}
<p> <p>
To display your local time correctly, the controller must know what To display your local time correctly, the controller must know
timezone it is in. what timezone it is in.
</p> </p>
<div class="timezones-container" style="margin-top: 20px;"> <div class="timezones-container" style="margin-top: 20px;">

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Actions, InitialFocus } from "@smui/dialog"; import Dialog, { Title, Actions, InitialFocus } from "@smui/dialog";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
import * as Api from "$lib/api" import * as Api from "$lib/api";
export let open; export let open;

View File

@@ -1,5 +1,10 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
import LinearProgress from "@smui/linear-progress"; import LinearProgress from "@smui/linear-progress";

View File

@@ -1,5 +1,10 @@
<script lang="ts"> <script lang="ts">
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog"; import Dialog, {
Title,
Content,
Actions,
InitialFocus,
} from "@smui/dialog";
import Button, { Label } from "@smui/button"; import Button, { Label } from "@smui/button";
import TextField from "@smui/textfield"; import TextField from "@smui/textfield";
import Icon from "@smui/textfield/icon"; import Icon from "@smui/textfield/icon";
@@ -60,7 +65,10 @@
bind:value={password} bind:value={password}
use={[ use={[
InitialFocus, InitialFocus,
[virtualKeyboardChange, (newValue) => (password = newValue)], [
virtualKeyboardChange,
(newValue) => (password = newValue),
],
]} ]}
label="Password" label="Password"
spellcheck="false" spellcheck="false"
@@ -72,7 +80,9 @@
slot="trailingIcon" slot="trailingIcon"
on:click={() => (showPassword = !showPassword)} on:click={() => (showPassword = !showPassword)}
> >
<Icon class={`fa ${showPassword ? "fa-eye-slash" : "fa-eye"}`} /> <Icon
class={`fa ${showPassword ? "fa-eye-slash" : "fa-eye"}`}
/>
</div> </div>
<HelperText persistent slot="helper"> <HelperText persistent slot="helper">
Wifi passwords must be 8 to 128 characters Wifi passwords must be 8 to 128 characters
@@ -82,7 +92,8 @@
<p> <p>
<em> <em>
Clicking {connectOrDisconnect} will reboot the controller to apply the changes. Clicking {connectOrDisconnect} will reboot the controller to apply
the changes.
</em> </em>
</p> </p>
</Content> </Content>
@@ -95,7 +106,8 @@
<Button <Button
defaultAction defaultAction
on:click={onConfirm} on:click={onConfirm}
disabled={needPassword && (password.length < 8 || password.length > 128)} disabled={needPassword &&
(password.length < 8 || password.length > 128)}
> >
<Label>{connectOrDisconnect} & Reboot</Label> <Label>{connectOrDisconnect} & Reboot</Label>
</Button> </Button>

View File

@@ -31,15 +31,15 @@ const empty: NetworkInfo = {
ssid: "", ssid: "",
networks: [] networks: []
} }
} };
export const networkInfo = writable<NetworkInfo>(empty); export const networkInfo = writable<NetworkInfo>(empty);
export function processNetworkInfo(rawNetworkInfo: NetworkInfo) { export function processNetworkInfo(rawNetworkInfo: NetworkInfo) {
const now = Date.now(); const now = Date.now();
const networksByName: Record<string, WifiNetwork> = {} const networksByName: Record<string, WifiNetwork> = {};
for (let network of rawNetworkInfo.wifi.networks) { for (const network of rawNetworkInfo.wifi.networks) {
if (network.Name) { if (network.Name) {
network.lastSeen = now; network.lastSeen = now;
network.active = rawNetworkInfo.wifi.ssid === network.Name; network.active = rawNetworkInfo.wifi.ssid === network.Name;
@@ -53,7 +53,7 @@ export function processNetworkInfo(rawNetworkInfo: NetworkInfo) {
} }
} }
for (let network of Object.values(networksByName)) { for (const network of Object.values(networksByName)) {
if (network.lastSeen - now > 30000) { if (network.lastSeen - now > 30000) {
delete networksByName[network.Name]; delete networksByName[network.Name];
} }

View File

@@ -1,9 +1,3 @@
type NumberWithUnit = {
value: number,
metric: boolean,
toMetric: () => number;
}
function numberWithUnitToMetric() { function numberWithUnitToMetric() {
return this.metric ? this.value : this.value * 25.4; return this.metric ? this.value : this.value * 25.4;
} }
@@ -38,12 +32,13 @@ function formatFraction(value: number) {
export const numberWithUnit = { export const numberWithUnit = {
regex: /^\s*(?:(\d+)\s*\/\s*(\d+)|(\d*\.\d+)|(\d+(?:\.\d+)?))\s*("|in|inch|inches|mm|millimeters)\s*$/, regex: /^\s*(?:(\d+)\s*\/\s*(\d+)|(\d*\.\d+)|(\d+(?:\.\d+)?))\s*("|in|inch|inches|mm|millimeters)\s*$/,
parse: function (str: string) { parse: function (str: string) {
// eslint-disable-next-line prefer-const
let [, numerator, denominator, decimal1, decimal2, unit]: any = str?.match(numberWithUnit.regex) ?? []; let [, numerator, denominator, decimal1, decimal2, unit]: any = str?.match(numberWithUnit.regex) ?? [];
numerator = Number.parseFloat(numerator) numerator = Number.parseFloat(numerator);
denominator = Number.parseFloat(denominator) denominator = Number.parseFloat(denominator);
decimal1 = Number.parseFloat(decimal1) decimal1 = Number.parseFloat(decimal1);
decimal2 = Number.parseFloat(decimal2) decimal2 = Number.parseFloat(decimal2);
const metric = (unit ?? "").includes("m"); const metric = (unit ?? "").includes("m");
@@ -81,10 +76,10 @@ export const numberWithUnit = {
return ""; return "";
case value.metric: case value.metric:
return `${value.value} mm` return `${value.value} mm`;
default: default:
return `${formatFraction(value.value)} in` return `${formatFraction(value.value)} in`;
} }
} }
} };

View File

@@ -6,36 +6,36 @@ async function doFetch(method: HttpMethod, url: string, data: any, config: Reque
...config, ...config,
method, method,
cache: "no-cache", cache: "no-cache",
body: (typeof data === 'object') body: (typeof data === "object")
? JSON.stringify(data) ? JSON.stringify(data)
: undefined, : undefined,
headers: (typeof data === 'object') headers: (typeof data === "object")
? { ? {
"Content-Type": 'application/json; charset=utf-8' "Content-Type": "application/json; charset=utf-8"
} }
: {} : {}
}); });
return await response.json(); return await response.json();
} catch (error) { } catch (error) {
console.debug('API Error: ' + url + ': ' + error); console.debug(`API Error: ${url}: ${error}`);
throw error; throw error;
} }
} }
export async function GET(url: string, config: RequestInit = {}) { export async function GET(url: string, config: RequestInit = {}) {
return doFetch('GET', url, undefined, config); return doFetch("GET", url, undefined, config);
} }
export async function PUT(url: string, data: any = undefined, config: RequestInit = {}) { export async function PUT(url: string, data: any = undefined, config: RequestInit = {}) {
return doFetch('PUT', url, data, config); return doFetch("PUT", url, data, config);
} }
export async function POST(url: string, data: any = undefined, config: RequestInit = {}) { export async function POST(url: string, data: any = undefined, config: RequestInit = {}) {
return doFetch('POST', url, data, config); return doFetch("POST", url, data, config);
} }
export async function DELETE(url: string, config = {}) { export async function DELETE(url: string, config = {}) {
return doFetch('DELETE', url, undefined, config); return doFetch("DELETE", url, undefined, config);
} }

View File

@@ -1,12 +1,12 @@
import 'polyfill-object.fromentries'; import "polyfill-object.fromentries";
import matchAll from "string.prototype.matchall"; import matchAll from "string.prototype.matchall";
matchAll.shim(); matchAll.shim();
import AdminNetworkView from '$components/AdminNetworkView.svelte'; import AdminNetworkView from "$components/AdminNetworkView.svelte";
import SettingsView from '$components/SettingsView.svelte'; import SettingsView from "$components/SettingsView.svelte";
import DialogHost, { showDialog } from "$dialogs/DialogHost.svelte"; import DialogHost, { showDialog } from "$dialogs/DialogHost.svelte";
import { handleConfigUpdate, setDisplayUnits } from '$lib/ConfigStore'; import { handleConfigUpdate, setDisplayUnits } from "$lib/ConfigStore";
import { handleControllerStateUpdate } from "$lib/ControllerState"; import { handleControllerStateUpdate } from "$lib/ControllerState";
import { registerControllerMethods } from "$lib/RegisterControllerMethods"; import { registerControllerMethods } from "$lib/RegisterControllerMethods";