Integrated eslint and reformatted all of the JS/TS
This commit is contained in:
82
.eslintrc.yml
Normal file
82
.eslintrc.yml
Normal 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
2914
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||||
|
|||||||
@@ -1,143 +1,123 @@
|
|||||||
/******************************************************************************\
|
"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");
|
||||||
|
|
||||||
const variant_defaults = {
|
const variant_defaults = {
|
||||||
machinist_x35: require("../resources/onefinity_machinist_x35_defaults.json"),
|
machinist_x35: require("../resources/onefinity_machinist_x35_defaults.json"),
|
||||||
woodworker_x35: require("../resources/onefinity_woodworker_x35_defaults.json"),
|
woodworker_x35: require("../resources/onefinity_woodworker_x35_defaults.json"),
|
||||||
woodworker_x50: require("../resources/onefinity_woodworker_x50_defaults.json"),
|
woodworker_x50: require("../resources/onefinity_woodworker_x50_defaults.json"),
|
||||||
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 () {
|
|
||||||
this.autoCheckUpgrade = this.config.admin['auto-check-upgrade']
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
backup: function () {
|
|
||||||
document.getElementById('download-target').src = '/api/config/download';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
restore_config: function () {
|
ready: function () {
|
||||||
// If we don't reset the form the browser may cache file if name is same
|
this.autoCheckUpgrade = this.config.admin["auto-check-upgrade"];
|
||||||
// even if contents have changed
|
|
||||||
$('.restore-config')[0].reset();
|
|
||||||
$('.restore-config input').click();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
restore: function (e) {
|
methods: {
|
||||||
const files = e.target.files || e.dataTransfer.files;
|
backup: function () {
|
||||||
if (!files.length) {
|
document.getElementById("download-target").src = "/api/config/download";
|
||||||
return;
|
},
|
||||||
}
|
|
||||||
|
|
||||||
const fileReader = new FileReader();
|
restore_config: function () {
|
||||||
fileReader.onload = async ({ target }) => {
|
// If we don't reset the form the browser may cache file if name is same
|
||||||
let config;
|
// even if contents have changed
|
||||||
try {
|
$(".restore-config")[0].reset();
|
||||||
config = JSON.parse(target.result);
|
$(".restore-config input").click();
|
||||||
} catch (ex) {
|
},
|
||||||
api.alert("Invalid config file");
|
|
||||||
return;
|
restore: function (e) {
|
||||||
|
const files = e.target.files || e.dataTransfer.files;
|
||||||
|
if (!files.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
fileReader.onload = async ({ target }) => {
|
||||||
|
let config;
|
||||||
|
try {
|
||||||
|
config = JSON.parse(target.result);
|
||||||
|
} catch (ex) {
|
||||||
|
api.alert("Invalid config file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put("config/save", config);
|
||||||
|
this.$dispatch("update");
|
||||||
|
SvelteComponents.showDialog("Message", {
|
||||||
|
title: "Success",
|
||||||
|
message: "Configuration restored"
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
api.alert("Restore failed", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.readAsText(files[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: async function () {
|
||||||
|
const config = merge(
|
||||||
|
{},
|
||||||
|
config_defaults,
|
||||||
|
variant_defaults[this.reset_variant]
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put("config/save", config);
|
||||||
|
this.confirmReset = false;
|
||||||
|
this.$dispatch("update");
|
||||||
|
SvelteComponents.showDialog("Message", {
|
||||||
|
title: "Success",
|
||||||
|
message: "Configuration restored"
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
api.alert("Restore failed");
|
||||||
|
console.error("Restore failed", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
check: function () {
|
||||||
|
this.$dispatch("check");
|
||||||
|
},
|
||||||
|
|
||||||
|
upgrade: function () {
|
||||||
|
this.$dispatch("upgrade");
|
||||||
|
},
|
||||||
|
|
||||||
|
upload_firmware: function () {
|
||||||
|
// If we don't reset the form the browser may cache file if name is same
|
||||||
|
// even if contents have changed
|
||||||
|
$(".upload-firmware")[0].reset();
|
||||||
|
$(".upload-firmware input").click();
|
||||||
|
},
|
||||||
|
|
||||||
|
upload: function (e) {
|
||||||
|
const files = e.target.files || e.dataTransfer.files;
|
||||||
|
if (!files.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$dispatch("upload", files[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
change_auto_check_upgrade: function () {
|
||||||
|
this.config.admin["auto-check-upgrade"] = this.autoCheckUpgrade;
|
||||||
|
this.$dispatch("config-changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
await api.put('config/save', config);
|
|
||||||
this.$dispatch('update');
|
|
||||||
SvelteComponents.showDialog("Message", { title: "Success", message: "Configuration restored" })
|
|
||||||
} catch (error) {
|
|
||||||
api.alert('Restore failed', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileReader.readAsText(files[0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
reset: async function () {
|
|
||||||
const config = merge(
|
|
||||||
{},
|
|
||||||
config_defaults,
|
|
||||||
variant_defaults[this.reset_variant]
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await api.put('config/save', config)
|
|
||||||
this.confirmReset = false;
|
|
||||||
this.$dispatch('update');
|
|
||||||
SvelteComponents.showDialog("Message", { title: "Success", message: "Configuration restored" })
|
|
||||||
} catch (err) {
|
|
||||||
api.alert('Restore failed');
|
|
||||||
console.error('Restore failed', err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
check: function () {
|
|
||||||
this.$dispatch('check')
|
|
||||||
},
|
|
||||||
|
|
||||||
upgrade: function () {
|
|
||||||
this.$dispatch('upgrade')
|
|
||||||
},
|
|
||||||
|
|
||||||
upload_firmware: function () {
|
|
||||||
// If we don't reset the form the browser may cache file if name is same
|
|
||||||
// even if contents have changed
|
|
||||||
$('.upload-firmware')[0].reset();
|
|
||||||
$('.upload-firmware input').click();
|
|
||||||
},
|
|
||||||
|
|
||||||
upload: function (e) {
|
|
||||||
var files = e.target.files || e.dataTransfer.files;
|
|
||||||
if (!files.length) return;
|
|
||||||
this.$dispatch('upload', files[0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
change_auto_check_upgrade: function () {
|
|
||||||
this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade;
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
template: "#admin-network-view-template",
|
template: "#admin-network-view-template",
|
||||||
|
|
||||||
attached: function () {
|
attached: function () {
|
||||||
this.svelteComponent = SvelteComponents.createComponent(
|
this.svelteComponent = SvelteComponents.createComponent(
|
||||||
"AdminNetworkView",
|
"AdminNetworkView",
|
||||||
document.getElementById("admin-network")
|
document.getElementById("admin-network")
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
detached: function() {
|
||||||
this.svelteComponent.$destroy();
|
this.svelteComponent.$destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,77 +1,37 @@
|
|||||||
'use strict'
|
"use strict";
|
||||||
|
|
||||||
|
async function callApi(method, url, body) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/${url}`, {
|
||||||
|
method,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body
|
||||||
|
});
|
||||||
|
|
||||||
function api_cb(method, url, data, config) {
|
if (response.ok) {
|
||||||
config = $.extend({
|
return response.json();
|
||||||
type: method,
|
}
|
||||||
url: '/api/' + url,
|
|
||||||
dataType: 'json',
|
|
||||||
cache: false
|
|
||||||
}, config);
|
|
||||||
|
|
||||||
if (typeof data == 'object') {
|
throw new Error(await response.text());
|
||||||
config.data = JSON.stringify(data);
|
} catch (error) {
|
||||||
config.contentType = 'application/json; charset=utf-8';
|
console.debug(`API Error: ${url}: ${error}`);
|
||||||
}
|
|
||||||
|
|
||||||
var d = $.Deferred();
|
throw error;
|
||||||
|
}
|
||||||
$.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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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) {
|
||||||
|
return callApi("PUT", url, body);
|
||||||
|
},
|
||||||
|
|
||||||
put: function(url, data, config) {
|
delete: function (url) {
|
||||||
return api_cb('PUT', url, data, config);
|
return callApi("DELETE", url);
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
post: function(url, data, config) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
724
src/js/app.js
724
src/js/app.js
@@ -5,411 +5,437 @@ const cookie = require("./cookie")("bbctrl-");
|
|||||||
const Sock = require("./sock");
|
const Sock = require("./sock");
|
||||||
|
|
||||||
SvelteComponents.createComponent("DialogHost",
|
SvelteComponents.createComponent("DialogHost",
|
||||||
document.getElementById("svelte-dialog-host")
|
document.getElementById("svelte-dialog-host")
|
||||||
);
|
);
|
||||||
|
|
||||||
function is_newer_version(current, latest) {
|
function is_newer_version(current, latest) {
|
||||||
const pattern = /(\d+)\.(\d+)\.(\d+)(.*)/;
|
const pattern = /(\d+)\.(\d+)\.(\d+)(.*)/;
|
||||||
const currentParts = current.match(pattern);
|
const currentParts = current.match(pattern);
|
||||||
const latestParts = latest.match(pattern);
|
const latestParts = latest.match(pattern);
|
||||||
|
|
||||||
if (!currentParts || !latestParts) {
|
if (!currentParts || !latestParts) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal version comparisons
|
// Normal version comparisons
|
||||||
const major = latestParts[1] - currentParts[1];
|
const major = latestParts[1] - currentParts[1];
|
||||||
const minor = latestParts[2] - currentParts[2];
|
const minor = latestParts[2] - currentParts[2];
|
||||||
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:
|
||||||
case major === 0 && minor > 0:
|
case major === 0 && minor > 0:
|
||||||
case major === 0 && minor === 0 && patch > 0:
|
case major === 0 && minor === 0 && patch > 0:
|
||||||
case major === 0 && minor === 0 && patch === 0 && betaToRelease:
|
case major === 0 && minor === 0 && patch === 0 && betaToRelease:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
props = Object.getOwnPropertyNames(src);
|
props = Object.getOwnPropertyNames(src);
|
||||||
for (index in props) {
|
for (index in props) {
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Vue({
|
module.exports = new Vue({
|
||||||
el: "body",
|
el: "body",
|
||||||
|
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
|
||||||
status: "connecting",
|
|
||||||
currentView: "loading",
|
|
||||||
display_units: localStorage.getItem("display_units") || "METRIC",
|
|
||||||
index: -1,
|
|
||||||
modified: false,
|
|
||||||
template: require("../resources/config-template.json"),
|
|
||||||
config: {
|
|
||||||
settings: { units: "METRIC" },
|
|
||||||
motors: [{}, {}, {}, {}],
|
|
||||||
version: "<loading>",
|
|
||||||
full_version: "<loading>",
|
|
||||||
},
|
|
||||||
state: {
|
|
||||||
messages: [],
|
|
||||||
},
|
|
||||||
video_size: cookie.get("video-size", "small"),
|
|
||||||
crosshair: cookie.get("crosshair", "false") != "false",
|
|
||||||
errorTimeout: 30,
|
|
||||||
errorTimeoutStart: 0,
|
|
||||||
errorShow: false,
|
|
||||||
errorMessage: "",
|
|
||||||
confirmUpgrade: false,
|
|
||||||
confirmUpload: false,
|
|
||||||
firmwareUpgrading: false,
|
|
||||||
checkedUpgrade: false,
|
|
||||||
firmwareName: "",
|
|
||||||
latestVersion: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
estop: { template: "#estop-template" },
|
|
||||||
"loading-view": { template: "<h1>Loading...</h1>" },
|
|
||||||
"control-view": require("./control-view"),
|
|
||||||
"settings-view": require("./settings-view"),
|
|
||||||
"motor-view": require("./motor-view"),
|
|
||||||
"tool-view": require("./tool-view"),
|
|
||||||
"io-view": require("./io-view"),
|
|
||||||
"admin-general-view": require("./admin-general-view"),
|
|
||||||
"admin-network-view": require("./admin-network-view"),
|
|
||||||
"help-view": { template: "#help-view-template" },
|
|
||||||
"cheat-sheet-view": {
|
|
||||||
template: "#cheat-sheet-view-template",
|
|
||||||
data: function () {
|
|
||||||
return {
|
return {
|
||||||
showUnimplemented: false
|
status: "connecting",
|
||||||
|
currentView: "loading",
|
||||||
|
display_units: localStorage.getItem("display_units") || "METRIC",
|
||||||
|
index: -1,
|
||||||
|
modified: false,
|
||||||
|
template: require("../resources/config-template.json"),
|
||||||
|
config: {
|
||||||
|
settings: { units: "METRIC" },
|
||||||
|
motors: [{}, {}, {}, {}],
|
||||||
|
version: "<loading>",
|
||||||
|
full_version: "<loading>",
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
messages: [],
|
||||||
|
},
|
||||||
|
video_size: cookie.get("video-size", "small"),
|
||||||
|
crosshair: cookie.get("crosshair", "false") != "false",
|
||||||
|
errorTimeout: 30,
|
||||||
|
errorTimeoutStart: 0,
|
||||||
|
errorShow: false,
|
||||||
|
errorMessage: "",
|
||||||
|
confirmUpgrade: false,
|
||||||
|
confirmUpload: false,
|
||||||
|
firmwareUpgrading: false,
|
||||||
|
checkedUpgrade: false,
|
||||||
|
firmwareName: "",
|
||||||
|
latestVersion: ""
|
||||||
};
|
};
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
display_units: function (value) {
|
|
||||||
localStorage.setItem("display_units", value);
|
|
||||||
SvelteComponents.setDisplayUnits(value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
events: {
|
|
||||||
"config-changed": function () {
|
|
||||||
this.modified = true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
send: function (msg) {
|
components: {
|
||||||
if (this.status == "connected") {
|
estop: { template: "#estop-template" },
|
||||||
this.sock.send(msg);
|
"loading-view": { template: "<h1>Loading...</h1>" },
|
||||||
}
|
"control-view": require("./control-view"),
|
||||||
|
"settings-view": require("./settings-view"),
|
||||||
|
"motor-view": require("./motor-view"),
|
||||||
|
"tool-view": require("./tool-view"),
|
||||||
|
"io-view": require("./io-view"),
|
||||||
|
"admin-general-view": require("./admin-general-view"),
|
||||||
|
"admin-network-view": require("./admin-network-view"),
|
||||||
|
"help-view": { template: "#help-view-template" },
|
||||||
|
"cheat-sheet-view": {
|
||||||
|
template: "#cheat-sheet-view-template",
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
showUnimplemented: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
connected: function () {
|
watch: {
|
||||||
this.update();
|
display_units: function (value) {
|
||||||
|
localStorage.setItem("display_units", value);
|
||||||
|
SvelteComponents.setDisplayUnits(value);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
update: function () {
|
events: {
|
||||||
this.update();
|
"config-changed": function () {
|
||||||
},
|
this.modified = true;
|
||||||
|
},
|
||||||
|
|
||||||
check: async function () {
|
send: function (msg) {
|
||||||
try {
|
if (this.status == "connected") {
|
||||||
const response = await fetch("https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt", {
|
this.sock.send(msg);
|
||||||
cache: "no-cache"
|
}
|
||||||
});
|
},
|
||||||
|
|
||||||
this.latestVersion = (await response.text()).trim();
|
connected: function () {
|
||||||
} catch (err) {
|
this.update();
|
||||||
this.latestVersion = "";
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
upgrade: function () {
|
update: function () {
|
||||||
this.confirmUpgrade = true;
|
this.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
upload: function (firmware) {
|
check: async function () {
|
||||||
this.firmware = firmware;
|
try {
|
||||||
this.firmwareName = firmware.name;
|
const response = await fetch("https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt", {
|
||||||
this.confirmUpload = true;
|
cache: "no-cache"
|
||||||
},
|
});
|
||||||
|
|
||||||
error: function (msg) {
|
this.latestVersion = (await response.text()).trim();
|
||||||
// Honor user error blocking
|
} catch (err) {
|
||||||
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000)
|
this.latestVersion = "";
|
||||||
return;
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Wait at least 1 sec to pop up repeated errors
|
upgrade: function () {
|
||||||
if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
|
this.confirmUpgrade = true;
|
||||||
return;
|
},
|
||||||
}
|
|
||||||
|
|
||||||
// Popup error dialog
|
upload: function (firmware) {
|
||||||
this.errorShow = true;
|
this.firmware = firmware;
|
||||||
this.errorMessage = msg.msg;
|
this.firmwareName = firmware.name;
|
||||||
},
|
this.confirmUpload = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
error: function (msg) {
|
||||||
popupMessages: function () {
|
// Honor user error blocking
|
||||||
const msgs = [];
|
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000) {
|
||||||
|
return;
|
||||||
for (let i = 0; i < this.state.messages.length; i++) {
|
|
||||||
const text = this.state.messages[i].text;
|
|
||||||
if (!/^#/.test(text)) {
|
|
||||||
msgs.push(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return msgs;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
$(window).on("hashchange", this.parse_hash);
|
|
||||||
this.connect();
|
|
||||||
|
|
||||||
SvelteComponents.registerControllerMethods({
|
|
||||||
dispatch: (...args) => this.$dispatch(...args)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
block_error_dialog: function () {
|
|
||||||
this.errorTimeoutStart = Date.now();
|
|
||||||
this.errorShow = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
toggle_video: function (e) {
|
|
||||||
if (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);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggle_crosshair: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.crosshair = !this.crosshair;
|
|
||||||
cookie.set("crosshair", this.crosshair);
|
|
||||||
},
|
|
||||||
|
|
||||||
estop: function () {
|
|
||||||
if (this.state.xx == "ESTOPPED") api.put("clear");
|
|
||||||
else api.put("estop");
|
|
||||||
},
|
|
||||||
|
|
||||||
upgrade_confirmed: async function () {
|
|
||||||
this.confirmUpgrade = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await api.put("upgrade");
|
|
||||||
this.firmwareUpgrading = true;
|
|
||||||
} catch (err) {
|
|
||||||
api.alert("Error during upgrade.");
|
|
||||||
console.error("Error during upgrade", err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
upload_confirmed: function () {
|
|
||||||
this.confirmUpload = false;
|
|
||||||
|
|
||||||
const form = new FormData();
|
|
||||||
form.append("firmware", this.firmware);
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: "/api/firmware/update",
|
|
||||||
type: "PUT",
|
|
||||||
data: form,
|
|
||||||
cache: false,
|
|
||||||
contentType: false,
|
|
||||||
processData: false,
|
|
||||||
})
|
|
||||||
.success(
|
|
||||||
function () {
|
|
||||||
this.firmwareUpgrading = true;
|
|
||||||
}.bind(this)
|
|
||||||
)
|
|
||||||
.error(
|
|
||||||
function (err) {
|
|
||||||
api.alert("Firmware update failed");
|
|
||||||
console.error("Firmware update failed", err);
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
show_upgrade: function () {
|
|
||||||
if (!this.latestVersion) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_newer_version(this.config.version, this.latestVersion);
|
|
||||||
},
|
|
||||||
|
|
||||||
showShutdownDialog: function () {
|
|
||||||
SvelteComponents.showDialog("Shutdown");
|
|
||||||
},
|
|
||||||
|
|
||||||
update: async function () {
|
|
||||||
const config = await api.get("config/load");
|
|
||||||
|
|
||||||
update_object(this.config, config, true);
|
|
||||||
this.parse_hash();
|
|
||||||
|
|
||||||
if (!this.checkedUpgrade) {
|
|
||||||
this.checkedUpgrade = true;
|
|
||||||
|
|
||||||
var check = this.config.admin["auto-check-upgrade"];
|
|
||||||
if (typeof check == "undefined" || check) this.$emit("check");
|
|
||||||
}
|
|
||||||
|
|
||||||
SvelteComponents.handleConfigUpdate(this.config);
|
|
||||||
},
|
|
||||||
|
|
||||||
connect: function () {
|
|
||||||
this.sock = new Sock(`//${location.host}/sockjs`);
|
|
||||||
|
|
||||||
this.sock.onmessage = (e) => {
|
|
||||||
if (typeof e.data != "object") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("log" in e.data) {
|
|
||||||
if (e.data.log.msg !== "Switch not found") {
|
|
||||||
this.$broadcast("log", e.data.log);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for session ID change on controller
|
|
||||||
if ("sid" in e.data) {
|
|
||||||
if (typeof this.sid == "undefined") {
|
|
||||||
this.sid = e.data.sid;
|
|
||||||
} else if (this.sid != e.data.sid) {
|
|
||||||
if (
|
|
||||||
typeof this.hostname !== "undefined" &&
|
|
||||||
location.hostname !== "localhost"
|
|
||||||
) {
|
|
||||||
location.hostname = this.hostname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
location.reload();
|
// Wait at least 1 sec to pop up repeated errors
|
||||||
}
|
if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
update_object(this.state, e.data, false);
|
// Popup error dialog
|
||||||
|
this.errorShow = true;
|
||||||
SvelteComponents.handleControllerStateUpdate(this.state);
|
this.errorMessage = msg.msg;
|
||||||
|
},
|
||||||
delete this.state.log;
|
|
||||||
|
|
||||||
this.$broadcast("update");
|
|
||||||
};
|
|
||||||
|
|
||||||
this.sock.onopen = () => {
|
|
||||||
this.status = "connected";
|
|
||||||
this.$emit(this.status);
|
|
||||||
this.$broadcast(this.status);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.sock.onclose = () => {
|
|
||||||
this.status = "disconnected";
|
|
||||||
this.$emit(this.status);
|
|
||||||
this.$broadcast(this.status);
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
parse_hash: function () {
|
computed: {
|
||||||
var hash = location.hash.substr(1);
|
popupMessages: function () {
|
||||||
|
const msgs = [];
|
||||||
|
|
||||||
if (!hash.trim().length) {
|
for (let i = 0; i < this.state.messages.length; i++) {
|
||||||
location.hash = "control";
|
const text = this.state.messages[i].text;
|
||||||
return;
|
if (!/^#/.test(text)) {
|
||||||
}
|
msgs.push(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var parts = hash.split(":");
|
return msgs;
|
||||||
|
},
|
||||||
if (parts.length == 2) this.index = parts[1];
|
|
||||||
|
|
||||||
this.currentView = parts[0];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
save: async function () {
|
ready: function () {
|
||||||
const selected_tool = this.config.tool["selected-tool"];
|
$(window).on("hashchange", this.parse_hash);
|
||||||
const saveModbus =
|
this.connect();
|
||||||
selected_tool !== "pwm" &&
|
|
||||||
selected_tool !== "laser" &&
|
|
||||||
selected_tool !== "router";
|
|
||||||
const settings = {
|
|
||||||
["tool"]: { ...this.config.tool },
|
|
||||||
["pwm-spindle"]: { ...this.config["pwm-spindle"] },
|
|
||||||
["modbus-spindle"]: saveModbus
|
|
||||||
? { ...this.config["modbus-spindle"] }
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
delete settings.tool["tool-type"];
|
|
||||||
|
|
||||||
this.config["selected-tool-settings"][selected_tool] = settings;
|
SvelteComponents.registerControllerMethods({
|
||||||
|
dispatch: (...args) => this.$dispatch(...args)
|
||||||
try {
|
});
|
||||||
await api.put("config/save", this.config);
|
|
||||||
this.modified = false;
|
|
||||||
} catch (error) {
|
|
||||||
api.alert("Save failed", error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
close_messages: function (action) {
|
methods: {
|
||||||
if (action == "stop") api.put("stop");
|
block_error_dialog: function () {
|
||||||
if (action == "continue") api.put("unpause");
|
this.errorTimeoutStart = Date.now();
|
||||||
|
this.errorShow = false;
|
||||||
|
},
|
||||||
|
|
||||||
// Acknowledge messages
|
toggle_video: function () {
|
||||||
if (this.state.messages.length) {
|
if (this.video_size == "small") {
|
||||||
var id = this.state.messages.slice(-1)[0].id;
|
this.video_size = "large";
|
||||||
api.put("message/" + id + "/ack");
|
} else if (this.video_size == "large") {
|
||||||
}
|
this.video_size = "small";
|
||||||
|
}
|
||||||
|
cookie.set("video-size", this.video_size);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggle_crosshair: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.crosshair = !this.crosshair;
|
||||||
|
cookie.set("crosshair", this.crosshair);
|
||||||
|
},
|
||||||
|
|
||||||
|
estop: function () {
|
||||||
|
if (this.state.xx == "ESTOPPED") {
|
||||||
|
api.put("clear");
|
||||||
|
} else {
|
||||||
|
api.put("estop");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
upgrade_confirmed: async function () {
|
||||||
|
this.confirmUpgrade = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put("upgrade");
|
||||||
|
this.firmwareUpgrading = true;
|
||||||
|
} catch (err) {
|
||||||
|
api.alert("Error during upgrade.");
|
||||||
|
console.error("Error during upgrade", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
upload_confirmed: function () {
|
||||||
|
this.confirmUpload = false;
|
||||||
|
|
||||||
|
const form = new FormData();
|
||||||
|
form.append("firmware", this.firmware);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/firmware/update",
|
||||||
|
type: "PUT",
|
||||||
|
data: form,
|
||||||
|
cache: false,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
})
|
||||||
|
.success(
|
||||||
|
function () {
|
||||||
|
this.firmwareUpgrading = true;
|
||||||
|
}.bind(this)
|
||||||
|
)
|
||||||
|
.error(
|
||||||
|
function (err) {
|
||||||
|
api.alert("Firmware update failed");
|
||||||
|
console.error("Firmware update failed", err);
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
show_upgrade: function () {
|
||||||
|
if (!this.latestVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_newer_version(this.config.version, this.latestVersion);
|
||||||
|
},
|
||||||
|
|
||||||
|
showShutdownDialog: function () {
|
||||||
|
SvelteComponents.showDialog("Shutdown");
|
||||||
|
},
|
||||||
|
|
||||||
|
update: async function () {
|
||||||
|
const config = await api.get("config/load");
|
||||||
|
|
||||||
|
update_object(this.config, config, true);
|
||||||
|
this.parse_hash();
|
||||||
|
|
||||||
|
if (!this.checkedUpgrade) {
|
||||||
|
this.checkedUpgrade = true;
|
||||||
|
|
||||||
|
const check = this.config.admin["auto-check-upgrade"];
|
||||||
|
if (typeof check == "undefined" || check) {
|
||||||
|
this.$emit("check");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SvelteComponents.handleConfigUpdate(this.config);
|
||||||
|
},
|
||||||
|
|
||||||
|
connect: function () {
|
||||||
|
this.sock = new Sock(`//${location.host}/sockjs`);
|
||||||
|
|
||||||
|
this.sock.onmessage = (e) => {
|
||||||
|
if (typeof e.data != "object") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("log" in e.data) {
|
||||||
|
if (e.data.log.msg !== "Switch not found") {
|
||||||
|
this.$broadcast("log", e.data.log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for session ID change on controller
|
||||||
|
if ("sid" in e.data) {
|
||||||
|
if (typeof this.sid == "undefined") {
|
||||||
|
this.sid = e.data.sid;
|
||||||
|
} else if (this.sid != e.data.sid) {
|
||||||
|
if (this.hostname && location.hostname !== "localhost") {
|
||||||
|
location.hostname = this.hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_object(this.state, e.data, false);
|
||||||
|
|
||||||
|
SvelteComponents.handleControllerStateUpdate(this.state);
|
||||||
|
|
||||||
|
delete this.state.log;
|
||||||
|
|
||||||
|
this.$broadcast("update");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sock.onopen = () => {
|
||||||
|
this.status = "connected";
|
||||||
|
this.$emit(this.status);
|
||||||
|
this.$broadcast(this.status);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sock.onclose = () => {
|
||||||
|
this.status = "disconnected";
|
||||||
|
this.$emit(this.status);
|
||||||
|
this.$broadcast(this.status);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
parse_hash: function () {
|
||||||
|
const hash = location.hash.substr(1);
|
||||||
|
|
||||||
|
if (!hash.trim().length) {
|
||||||
|
location.hash = "control";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = hash.split(":");
|
||||||
|
|
||||||
|
if (parts.length == 2) {
|
||||||
|
this.index = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentView = parts[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
save: async function () {
|
||||||
|
const selected_tool = this.config.tool["selected-tool"];
|
||||||
|
const saveModbus =
|
||||||
|
selected_tool !== "pwm" &&
|
||||||
|
selected_tool !== "laser" &&
|
||||||
|
selected_tool !== "router";
|
||||||
|
const settings = {
|
||||||
|
["tool"]: { ...this.config.tool },
|
||||||
|
["pwm-spindle"]: { ...this.config["pwm-spindle"] },
|
||||||
|
["modbus-spindle"]: saveModbus
|
||||||
|
? { ...this.config["modbus-spindle"] }
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
delete settings.tool["tool-type"];
|
||||||
|
|
||||||
|
this.config["selected-tool-settings"][selected_tool] = settings;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put("config/save", this.config);
|
||||||
|
this.modified = false;
|
||||||
|
} catch (error) {
|
||||||
|
api.alert("Save failed", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
close_messages: function (action) {
|
||||||
|
if (action == "stop") {
|
||||||
|
api.put("stop");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == "continue") {
|
||||||
|
api.put("unpause");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acknowledge messages
|
||||||
|
if (this.state.messages.length) {
|
||||||
|
const id = this.state.messages.slice(-1)[0].id;
|
||||||
|
api.put("message/" + id + "/ack");
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
jog: function (axis, ring, direction) {
|
||||||
|
const value = direction * this.value(ring);
|
||||||
|
this.$dispatch(this.step ? "step" : "jog", this.axes[axis], value);
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
back2zero: function(axis0,axis1) {
|
||||||
jog: function (axis, ring, direction) {
|
this.$dispatch("back2zero",this.axes[axis0],this.axes[axis1]);
|
||||||
var value = direction * this.value(ring);
|
},
|
||||||
this.$dispatch(this.step ? 'step' : 'jog', this.axes[axis], value);
|
|
||||||
},
|
|
||||||
|
|
||||||
back2zero: function(axis0,axis1) {
|
release: function (axis) {
|
||||||
this.$dispatch('back2zero',this.axes[axis0],this.axes[axis1])
|
if (!this.step) {
|
||||||
},
|
this.$dispatch("jog", this.axes[axis], 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
value: function (ring) {
|
||||||
|
const adjust = [0.01, 0.1, 1][this.adjust];
|
||||||
|
if (this.step) {
|
||||||
|
return adjust * [0.1, 1, 10, 100][ring];
|
||||||
|
}
|
||||||
|
return adjust * [0.1, 0.25, 0.5, 1][ring];
|
||||||
|
},
|
||||||
|
|
||||||
release: function (axis) {
|
text: function (ring) {
|
||||||
if (!this.step) this.$dispatch('jog', this.axes[axis], 0)
|
let value = this.value(ring) * (this.step ? 1 : 100);
|
||||||
},
|
value = parseFloat(value.toFixed(3));
|
||||||
|
return value + (this.step ? "" : "%");
|
||||||
|
}
|
||||||
value: function (ring) {
|
|
||||||
var adjust = [0.01, 0.1, 1][this.adjust];
|
|
||||||
if (this.step) return adjust * [0.1, 1, 10, 100][ring];
|
|
||||||
return adjust * [0.1, 0.25, 0.5, 1][ring];
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
text: function (ring) {
|
|
||||||
var value = this.value(ring) * (this.step ? 1 : 100);
|
|
||||||
value = parseFloat(value.toFixed(3));
|
|
||||||
return value + (this.step ? '' : '%');
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,194 +1,254 @@
|
|||||||
'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') },
|
|
||||||
b: function () { return this._compute_axis('b') },
|
|
||||||
c: function () { return this._compute_axis('c') },
|
|
||||||
axes: function () { return this._compute_axes() }
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
x: function () {
|
||||||
_convert_length: function (value) {
|
return this._compute_axis("x");
|
||||||
return this.metric
|
},
|
||||||
? value
|
|
||||||
: value / 25.4;
|
|
||||||
},
|
|
||||||
|
|
||||||
_length_str: function (value) {
|
y: function () {
|
||||||
return this._convert_length(value).toLocaleString() +
|
return this._compute_axis("y");
|
||||||
(this.metric ? ' mm' : ' in');
|
},
|
||||||
},
|
|
||||||
|
|
||||||
_compute_axis: function (axis) {
|
z: function () {
|
||||||
var abs = this.state[axis + 'p'] || 0;
|
return this._compute_axis("z");
|
||||||
var off = this.state['offset_' + axis];
|
},
|
||||||
var motor_id = this._get_motor_id(axis);
|
|
||||||
var motor = motor_id == -1 ? {} : this.config.motors[motor_id];
|
|
||||||
var enabled = typeof motor.enabled != 'undefined' && motor.enabled;
|
|
||||||
var homingMode = motor['homing-mode']
|
|
||||||
var homed = this.state[motor_id + 'homed'];
|
|
||||||
var min = this.state[motor_id + 'tn'];
|
|
||||||
var max = this.state[motor_id + 'tm'];
|
|
||||||
var dim = max - min;
|
|
||||||
var pathMin = this.state['path_min_' + axis];
|
|
||||||
var pathMax = this.state['path_max_' + axis];
|
|
||||||
var pathDim = pathMax - pathMin;
|
|
||||||
var under = pathMin + off < min;
|
|
||||||
var over = max < pathMax + off;
|
|
||||||
var klass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis;
|
|
||||||
var state = 'UNHOMED';
|
|
||||||
var icon = 'question-circle';
|
|
||||||
var fault = this.state[motor_id + 'df'] & 0x1f;
|
|
||||||
var shutdown = this.state.power_shutdown;
|
|
||||||
var title;
|
|
||||||
var ticon = 'question-circle';
|
|
||||||
var tstate = 'NO FILE';
|
|
||||||
var toolmsg;
|
|
||||||
var tklass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis;
|
|
||||||
|
|
||||||
if (fault || shutdown) {
|
a: function () {
|
||||||
state = shutdown ? 'SHUTDOWN' : 'FAULT';
|
return this._compute_axis("a");
|
||||||
klass += ' error';
|
},
|
||||||
icon = 'exclamation-circle';
|
|
||||||
} else if (homed) {
|
|
||||||
state = 'HOMED';
|
|
||||||
icon = 'check-circle';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 < dim && dim < pathDim) {
|
b: function () {
|
||||||
tstate = 'NO FIT';
|
return this._compute_axis("b");
|
||||||
tklass += ' error';
|
},
|
||||||
ticon = 'ban';
|
|
||||||
} else {
|
c: function () {
|
||||||
if (over || under) {
|
return this._compute_axis("c");
|
||||||
tstate = over ? 'OVER' : 'UNDER';
|
},
|
||||||
tklass += ' warn';
|
|
||||||
ticon = 'exclamation-circle';
|
axes: function () {
|
||||||
} else {
|
return this._compute_axes();
|
||||||
tstate = 'OK';
|
|
||||||
ticon = 'check-circle';
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case 'UNHOMED': title = 'Click the home button to home axis.'; break;
|
|
||||||
case 'HOMED': title = 'Axis successfuly homed.'; break;
|
|
||||||
case 'FAULT':
|
|
||||||
title = 'Motor driver fault. A potentially damaging electrical ' +
|
|
||||||
'condition was detected and the motor driver was shutdown. ' +
|
|
||||||
'Please power down the controller and check your motor cabling. ' +
|
|
||||||
'See the "Motor Faults" table on the "Indicators" tab for more ' +
|
|
||||||
'information.';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SHUTDOWN':
|
|
||||||
title = 'Motor power fault. All motors in shutdown. ' +
|
|
||||||
'See the "Power Faults" table on the "Indicators" tab for more ' +
|
|
||||||
'information. Reboot controller to reset.';
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (tstate) {
|
|
||||||
case 'OVER':
|
|
||||||
toolmsg = 'Caution: The current tool path file would move ' +
|
|
||||||
this._length_str(pathMax + off - max) + ' above axis limit with the current offset.';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'UNDER':
|
|
||||||
toolmsg = 'Caution: The current tool path file would move ' +
|
|
||||||
this._length_str(min - pathMin - off) + ' below limit with the current offset.';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'NO FIT':
|
|
||||||
toolmsg = 'Warning: The current tool path dimensions (' +
|
|
||||||
this._length_str(pathDim) + ') exceed axis dimensions (' +
|
|
||||||
this._length_str(dim) + ') by ' +
|
|
||||||
this._length_str(pathDim - dim) + '.';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
toolmsg = 'Tool path ' + axis + ' dimensions OK.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
pos: abs - off,
|
|
||||||
abs: abs,
|
|
||||||
off: off,
|
|
||||||
min: min,
|
|
||||||
max: max,
|
|
||||||
dim: dim,
|
|
||||||
pathMin: pathMin,
|
|
||||||
pathMax: pathMax,
|
|
||||||
pathDim: pathDim,
|
|
||||||
motor: motor_id,
|
|
||||||
enabled: enabled,
|
|
||||||
homingMode: homingMode,
|
|
||||||
homed: homed,
|
|
||||||
klass: klass,
|
|
||||||
state: state,
|
|
||||||
icon: icon,
|
|
||||||
title: title,
|
|
||||||
ticon: ticon,
|
|
||||||
tstate: tstate,
|
|
||||||
toolmsg: toolmsg,
|
|
||||||
tklass: tklass
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_get_motor_id: function (axis) {
|
methods: {
|
||||||
for (var i = 0; i < this.config.motors.length; i++) {
|
_convert_length: function (value) {
|
||||||
var motor = this.config.motors[i];
|
return this.metric
|
||||||
if (motor.axis.toLowerCase() == axis) return i;
|
? value
|
||||||
}
|
: value / 25.4;
|
||||||
|
},
|
||||||
|
|
||||||
return -1;
|
_length_str: function (value) {
|
||||||
},
|
return this._convert_length(value).toLocaleString() + (this.metric ? " mm" : " in");
|
||||||
|
},
|
||||||
|
|
||||||
_compute_axes: function () {
|
_compute_axis: function (axis) {
|
||||||
var homed = false;
|
const abs = this.state[`${axis}p`] || 0;
|
||||||
|
const off = this.state[`offset_${axis}`];
|
||||||
|
const motor_id = this._get_motor_id(axis);
|
||||||
|
const motor = motor_id == -1 ? {} : this.config.motors[motor_id];
|
||||||
|
const enabled = typeof motor.enabled != "undefined" && motor.enabled;
|
||||||
|
const homingMode = motor["homing-mode"];
|
||||||
|
const homed = this.state[`${motor_id}homed`];
|
||||||
|
const min = this.state[`${motor_id}tn`];
|
||||||
|
const max = this.state[`${motor_id}tm`];
|
||||||
|
const dim = max - min;
|
||||||
|
const pathMin = this.state[`path_min_${axis}`];
|
||||||
|
const pathMax = this.state[`path_max_${axis}`];
|
||||||
|
const pathDim = pathMax - pathMin;
|
||||||
|
const under = pathMin + off < min;
|
||||||
|
const over = max < pathMax + off;
|
||||||
|
let klass = `${homed ? "homed" : "unhomed"} axis-${axis}`;
|
||||||
|
let state = "UNHOMED";
|
||||||
|
let icon = "question-circle";
|
||||||
|
const fault = this.state[`${motor_id}df`] & 0x1f;
|
||||||
|
const shutdown = this.state.power_shutdown;
|
||||||
|
let title;
|
||||||
|
let ticon = "question-circle";
|
||||||
|
let tstate = "NO FILE";
|
||||||
|
let toolmsg;
|
||||||
|
let tklass = `${homed ? "homed" : "unhomed"} axis-${axis}`;
|
||||||
|
|
||||||
for (var name of 'xyzabc') {
|
if (fault || shutdown) {
|
||||||
var axis = this[name];
|
state = shutdown ? "SHUTDOWN" : "FAULT";
|
||||||
|
klass += " error";
|
||||||
|
icon = "exclamation-circle";
|
||||||
|
} else if (homed) {
|
||||||
|
state = "HOMED";
|
||||||
|
icon = "check-circle";
|
||||||
|
}
|
||||||
|
|
||||||
if (!axis.enabled) continue
|
if (0 < dim && dim < pathDim) {
|
||||||
if (!axis.homed) { homed = false; break }
|
tstate = "NO FIT";
|
||||||
homed = true;
|
tklass += " error";
|
||||||
}
|
ticon = "ban";
|
||||||
|
} else {
|
||||||
|
if (over || under) {
|
||||||
|
tstate = over ? "OVER" : "UNDER";
|
||||||
|
tklass += " warn";
|
||||||
|
ticon = "exclamation-circle";
|
||||||
|
} else {
|
||||||
|
tstate = "OK";
|
||||||
|
ticon = "check-circle";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var error = false;
|
switch (state) {
|
||||||
var warn = false;
|
case "UNHOMED":
|
||||||
|
title = "Click the home button to home axis.";
|
||||||
|
break;
|
||||||
|
|
||||||
if (homed)
|
case "HOMED":
|
||||||
for (name of 'xyzabc') {
|
title = "Axis successfuly homed.";
|
||||||
axis = this[name];
|
break;
|
||||||
|
|
||||||
if (!axis.enabled) continue;
|
case "FAULT":
|
||||||
if (axis.klass.indexOf('error') != -1) error = true;
|
title = [
|
||||||
if (axis.klass.indexOf('warn') != -1) warn = true;
|
`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) {
|
||||||
|
case "OVER":
|
||||||
|
toolmsg = [
|
||||||
|
`Caution: The current tool path file would move`,
|
||||||
|
`${this._length_str(pathMax + off - max)}`,
|
||||||
|
`above axis limit with the current offset.`
|
||||||
|
].join(" ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "UNDER":
|
||||||
|
toolmsg = [
|
||||||
|
`Caution: The current tool path file would move`,
|
||||||
|
`${this._length_str(min - pathMin - off)}`,
|
||||||
|
`below limit with the current offset.`
|
||||||
|
].join(" ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "NO FIT":
|
||||||
|
toolmsg = [
|
||||||
|
`Warning: The current tool path dimensions`,
|
||||||
|
`(${this._length_str(pathDim)}) exceed axis dimensions`,
|
||||||
|
`(${this._length_str(dim)}) by ${this._length_str(pathDim - dim)}.`
|
||||||
|
].join(" ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
toolmsg = `Tool path ${axis} dimensions OK.`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
pos: abs - off,
|
||||||
|
abs: abs,
|
||||||
|
off: off,
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
dim: dim,
|
||||||
|
pathMin: pathMin,
|
||||||
|
pathMax: pathMax,
|
||||||
|
pathDim: pathDim,
|
||||||
|
motor: motor_id,
|
||||||
|
enabled: enabled,
|
||||||
|
homingMode: homingMode,
|
||||||
|
homed: homed,
|
||||||
|
klass: klass,
|
||||||
|
state: state,
|
||||||
|
icon: icon,
|
||||||
|
title: title,
|
||||||
|
ticon: ticon,
|
||||||
|
tstate: tstate,
|
||||||
|
toolmsg: toolmsg,
|
||||||
|
tklass: tklass
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_get_motor_id: function (axis) {
|
||||||
|
for (let i = 0; i < this.config.motors.length; i++) {
|
||||||
|
const motor = this.config.motors[i];
|
||||||
|
if (motor.axis.toLowerCase() == axis) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
_compute_axes: function () {
|
||||||
|
let homed = false;
|
||||||
|
|
||||||
|
for (const name of "xyzabc") {
|
||||||
|
const axis = this[name];
|
||||||
|
|
||||||
|
if (!axis.enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!axis.homed) {
|
||||||
|
homed = false; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
homed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = false;
|
||||||
|
let warn = false;
|
||||||
|
|
||||||
|
if (homed) {
|
||||||
|
for (const name of "xyzabc") {
|
||||||
|
const axis = this[name];
|
||||||
|
|
||||||
|
if (!axis.enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (axis.klass.indexOf("error") != -1) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
this.ask_home = false;
|
||||||
|
SvelteComponents.showDialog("HomeMachine", {
|
||||||
|
home: () => this.home()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
homed: homed,
|
||||||
|
klass: klass
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var klass = homed ? 'homed' : 'unhomed';
|
|
||||||
if (error) klass += ' error';
|
|
||||||
else if (warn) klass += ' warn';
|
|
||||||
|
|
||||||
if (!homed && this.ask_home) {
|
|
||||||
this.ask_home = false;
|
|
||||||
SvelteComponents.showDialog("HomeMachine", {
|
|
||||||
home: () => this.home()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
homed: homed,
|
|
||||||
klass: klass
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -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 () {
|
||||||
|
return {
|
||||||
|
messages
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
data: function () {
|
events: {
|
||||||
return {messages: messages}
|
log: function (msg) {
|
||||||
},
|
// There may be multiple instances of this module so ignore messages
|
||||||
|
// that have already been processed.
|
||||||
|
if (msg.logged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.logged = true;
|
||||||
|
|
||||||
events: {
|
// Make sure we have a message level
|
||||||
log: function (msg) {
|
msg.level = msg.level || "info";
|
||||||
// There may be multiple instances of this module so ignore messages
|
|
||||||
// that have already been processed.
|
|
||||||
if (msg.logged) return;
|
|
||||||
msg.logged = true;
|
|
||||||
|
|
||||||
// Make sure we have a message level
|
// Add to message log and count and collapse repeats
|
||||||
msg.level = msg.level || 'info';
|
const repeat = messages.length && _msg_equal(msg, messages[0]);
|
||||||
|
if (repeat) {
|
||||||
|
messages[0].repeat++;
|
||||||
|
} else {
|
||||||
|
msg.repeat = msg.repeat || 1;
|
||||||
|
messages.unshift(msg);
|
||||||
|
while (256 < messages.length) {
|
||||||
|
messages.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.ts = Date.now();
|
||||||
|
|
||||||
// Add to message log and count and collapse repeats
|
// Write message to browser console for debugging
|
||||||
var repeat = messages.length && _msg_equal(msg, messages[0]);
|
const text = JSON.stringify(msg);
|
||||||
if (repeat) messages[0].repeat++;
|
if (msg.level == "error" || msg.level == "critical") {
|
||||||
else {
|
console.error(text);
|
||||||
msg.repeat = msg.repeat || 1;
|
} else if (msg.level == "warning") {
|
||||||
messages.unshift(msg);
|
console.warn(text);
|
||||||
while (256 < messages.length) messages.pop();
|
} else if (msg.level == "debug" && console.debug) {
|
||||||
}
|
console.debug(text);
|
||||||
msg.ts = Date.now();
|
} else {
|
||||||
|
console.log(text);
|
||||||
|
}
|
||||||
|
|
||||||
// Write message to browser console for debugging
|
// Event on errors
|
||||||
var text = JSON.stringify(msg);
|
if (msg.level == "error" || msg.level == "critical") {
|
||||||
if (msg.level == 'error' || msg.level == 'critical') console.error(text);
|
this.$dispatch("error", msg);
|
||||||
else if (msg.level == 'warning') console.warn(text);
|
}
|
||||||
else if (msg.level == 'debug' && console.debug) console.debug(text);
|
}
|
||||||
else console.log(text);
|
},
|
||||||
|
|
||||||
// Event on errors
|
methods: {
|
||||||
if (msg.level == 'error' || msg.level == 'critical')
|
clear: function () {
|
||||||
this.$dispatch('error', msg);
|
messages.splice(0, messages.length);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
clear: function () {messages.splice(0, messages.length);},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,503 +1,501 @@
|
|||||||
'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,
|
||||||
jog_incr_amounts: {
|
jog_incr_amounts: {
|
||||||
"METRIC": {
|
"METRIC": {
|
||||||
fine: 0.1,
|
fine: 0.1,
|
||||||
small: 1.0,
|
small: 1.0,
|
||||||
medium: 10,
|
medium: 10,
|
||||||
large: 100,
|
large: 100,
|
||||||
|
},
|
||||||
|
"IMPERIAL": {
|
||||||
|
fine: 0.005,
|
||||||
|
small: 0.05,
|
||||||
|
medium: 0.5,
|
||||||
|
large: 5,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
jog_incr: localStorage.getItem("jog_incr") || "small",
|
||||||
|
jog_step: cookie.get_bool("jog-step"),
|
||||||
|
jog_adjust: parseInt(cookie.get("jog-adjust", 2)),
|
||||||
|
deleteGCode: false,
|
||||||
|
tab: "auto",
|
||||||
|
ask_home: true,
|
||||||
|
showGcodeMessage: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
"axis-control": require("./axis-control"),
|
||||||
|
"path-viewer": require("./path-viewer"),
|
||||||
|
"gcode-viewer": require("./gcode-viewer")
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
jog_incr: function (value) {
|
||||||
|
localStorage.setItem("jog_incr", value);
|
||||||
},
|
},
|
||||||
"IMPERIAL": {
|
|
||||||
fine: 0.005,
|
|
||||||
small: 0.05,
|
|
||||||
medium: 0.5,
|
|
||||||
large: 5,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
jog_incr: localStorage.getItem("jog_incr") || 'small',
|
|
||||||
jog_step: cookie.get_bool('jog-step'),
|
|
||||||
jog_adjust: parseInt(cookie.get('jog-adjust', 2)),
|
|
||||||
deleteGCode: false,
|
|
||||||
tab: 'auto',
|
|
||||||
ask_home: true,
|
|
||||||
showGcodeMessage: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
"state.metric": {
|
||||||
'axis-control': require('./axis-control'),
|
handler: function (metric) {
|
||||||
'path-viewer': require('./path-viewer'),
|
this.mach_units = metric
|
||||||
'gcode-viewer': require('./gcode-viewer')
|
? "METRIC"
|
||||||
},
|
: "IMPERIAL";
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
|
||||||
watch: {
|
"state.line": function () {
|
||||||
jog_incr: function (value) {
|
if (this.mach_state != "HOMING") {
|
||||||
localStorage.setItem("jog_incr", value);
|
this.$broadcast("gcode-line", this.state.line);
|
||||||
},
|
|
||||||
|
|
||||||
'state.metric': {
|
|
||||||
handler: function (metric) {
|
|
||||||
this.mach_units = metric
|
|
||||||
? 'METRIC'
|
|
||||||
: 'IMPERIAL';
|
|
||||||
},
|
|
||||||
immediate: true
|
|
||||||
},
|
|
||||||
|
|
||||||
'state.line': function () {
|
|
||||||
if (this.mach_state != 'HOMING') {
|
|
||||||
this.$broadcast('gcode-line', this.state.line);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
'state.selected_time': function () {
|
|
||||||
this.load();
|
|
||||||
},
|
|
||||||
|
|
||||||
jog_step: function () {
|
|
||||||
cookie.set_bool('jog-step', this.jog_step);
|
|
||||||
},
|
|
||||||
|
|
||||||
jog_adjust: function () {
|
|
||||||
cookie.set('jog-adjust', this.jog_adjust);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
display_units: {
|
|
||||||
cache: false,
|
|
||||||
get: function () {
|
|
||||||
return this.$root.display_units;
|
|
||||||
},
|
|
||||||
set: function (value) {
|
|
||||||
this.$root.display_units = value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
metric: function () {
|
|
||||||
return this.display_units === "METRIC";
|
|
||||||
},
|
|
||||||
|
|
||||||
mach_state: function () {
|
|
||||||
var cycle = this.state.cycle;
|
|
||||||
var state = this.state.xx;
|
|
||||||
|
|
||||||
if (typeof cycle != 'undefined' && state != 'ESTOPPED' &&
|
|
||||||
(cycle == 'jogging' || cycle == 'homing')) {
|
|
||||||
return cycle.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
return state || ''
|
|
||||||
},
|
|
||||||
|
|
||||||
pause_reason: function () {
|
|
||||||
return this.state.pr
|
|
||||||
},
|
|
||||||
|
|
||||||
is_running: function () {
|
|
||||||
return this.mach_state == 'RUNNING' || this.mach_state == 'HOMING';
|
|
||||||
},
|
|
||||||
|
|
||||||
is_stopping: function () {
|
|
||||||
return this.mach_state == 'STOPPING'
|
|
||||||
},
|
|
||||||
|
|
||||||
is_holding: function () {
|
|
||||||
return this.mach_state == 'HOLDING'
|
|
||||||
},
|
|
||||||
|
|
||||||
is_ready: function () {
|
|
||||||
return this.mach_state == 'READY'
|
|
||||||
},
|
|
||||||
|
|
||||||
is_idle: function () {
|
|
||||||
return this.state.cycle == 'idle'
|
|
||||||
},
|
|
||||||
|
|
||||||
is_paused: function () {
|
|
||||||
return this.is_holding &&
|
|
||||||
(this.pause_reason == 'User pause' ||
|
|
||||||
this.pause_reason == 'Program pause')
|
|
||||||
},
|
|
||||||
|
|
||||||
can_mdi: function () {
|
|
||||||
return this.is_idle || this.state.cycle == 'mdi'
|
|
||||||
},
|
|
||||||
|
|
||||||
can_set_axis: function () {
|
|
||||||
return this.is_idle
|
|
||||||
// TODO allow setting axis position during pause
|
|
||||||
return this.is_idle || this.is_paused
|
|
||||||
},
|
|
||||||
|
|
||||||
message: function () {
|
|
||||||
if (this.mach_state == 'ESTOPPED') {
|
|
||||||
return this.state.er;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.mach_state == 'HOLDING') {
|
|
||||||
return this.state.pr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.messages.length) {
|
|
||||||
return this.state.messages.slice(-1)[0].text;
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
|
|
||||||
highlight_state: function () {
|
|
||||||
return this.mach_state == 'ESTOPPED' || this.mach_state == 'HOLDING';
|
|
||||||
},
|
|
||||||
|
|
||||||
plan_time: function () {
|
|
||||||
return this.state.plan_time
|
|
||||||
},
|
|
||||||
|
|
||||||
plan_time_remaining: function () {
|
|
||||||
if (!(this.is_stopping || this.is_running || this.is_holding)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.toolpath.time - this.plan_time
|
|
||||||
},
|
|
||||||
|
|
||||||
eta: function () {
|
|
||||||
if (this.mach_state != 'RUNNING') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
var remaining = this.plan_time_remaining;
|
|
||||||
var d = new Date();
|
|
||||||
d.setSeconds(d.getSeconds() + remaining);
|
|
||||||
return d.toLocaleString();
|
|
||||||
},
|
|
||||||
|
|
||||||
progress: function () {
|
|
||||||
if (!this.toolpath.time || this.is_ready) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var p = this.plan_time / this.toolpath.time;
|
|
||||||
return p < 1 ? p : 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
events: {
|
|
||||||
jog: function (axis, power) {
|
|
||||||
var data = { ts: new Date().getTime() };
|
|
||||||
data[axis] = power;
|
|
||||||
api.put('jog', data);
|
|
||||||
},
|
|
||||||
|
|
||||||
back2zero: function (axis0, axis1) {
|
|
||||||
this.send(`G0 ${axis0}0 ${axis1}0`);
|
|
||||||
},
|
|
||||||
|
|
||||||
step: function (axis, value) {
|
|
||||||
this.send(`
|
|
||||||
M70
|
|
||||||
G91
|
|
||||||
G0 ${axis}${value}
|
|
||||||
M72
|
|
||||||
`);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
this.load();
|
|
||||||
|
|
||||||
setInterval(() => {
|
|
||||||
this.current_time = new Date().toLocaleTimeString();
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
SvelteComponents.registerControllerMethods({
|
|
||||||
stop: (...args) => this.stop(...args),
|
|
||||||
send: (...args) => this.send(...args),
|
|
||||||
isAxisHomed: (axis) => this[axis].homed,
|
|
||||||
unhome: (...args) => this.unhome(...args),
|
|
||||||
set_position: (...args) => this.set_position(...args),
|
|
||||||
set_home: (...args) => this.set_home(...args)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
getJogIncrStyle(value) {
|
|
||||||
const weight = `font-weight:${this.jog_incr === value ? 'bold' : 'normal'}`;
|
|
||||||
const color = this.jog_incr === value ? "color:#0078e7" : "";
|
|
||||||
|
|
||||||
return [weight, color].join(';');
|
|
||||||
},
|
|
||||||
|
|
||||||
jog_fn: function (x_jog, y_jog, z_jog, a_jog) {
|
|
||||||
const amount = this.jog_incr_amounts[this.display_units][this.jog_incr];
|
|
||||||
|
|
||||||
var xcmd = "X" + x_jog * amount;
|
|
||||||
var ycmd = "Y" + y_jog * amount;
|
|
||||||
var zcmd = "Z" + z_jog * amount;
|
|
||||||
var acmd = "A" + a_jog * amount;
|
|
||||||
|
|
||||||
this.send(`
|
|
||||||
G91
|
|
||||||
${this.metric ? "G21" : "G20"}
|
|
||||||
G0 ${xcmd}${ycmd}${zcmd}${acmd}
|
|
||||||
`);
|
|
||||||
},
|
|
||||||
|
|
||||||
send: function (msg) {
|
|
||||||
this.$dispatch('send', msg)
|
|
||||||
},
|
|
||||||
|
|
||||||
load: function () {
|
|
||||||
var file_time = this.state.selected_time;
|
|
||||||
var file = this.state.selected;
|
|
||||||
if (this.last_file == file && this.last_file_time == file_time) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.last_file = file;
|
|
||||||
this.last_file_time = file_time;
|
|
||||||
|
|
||||||
this.$broadcast('gcode-load', file);
|
|
||||||
this.$broadcast('gcode-line', this.state.line);
|
|
||||||
this.toolpath_progress = 0;
|
|
||||||
this.load_toolpath(file, file_time);
|
|
||||||
},
|
|
||||||
|
|
||||||
load_toolpath: async function (file, file_time) {
|
|
||||||
this.toolpath = {};
|
|
||||||
|
|
||||||
if (!file || this.last_file_time != file_time) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showGcodeMessage = true;
|
|
||||||
|
|
||||||
while (this.showGcodeMessage) {
|
|
||||||
const toolpath = await api.get(`path/${file}`);
|
|
||||||
this.toolpath_progress = toolpath.progress;
|
|
||||||
|
|
||||||
if (toolpath.progress === 1 || typeof toolpath.progress == 'undefined') {
|
|
||||||
this.showGcodeMessage = false
|
|
||||||
|
|
||||||
if (toolpath.bounds) {
|
|
||||||
toolpath.filename = file;
|
|
||||||
this.toolpath_progress = 1;
|
|
||||||
this.toolpath = toolpath;
|
|
||||||
|
|
||||||
const state = this.$root.state;
|
|
||||||
for (let axis of 'xyzabc') {
|
|
||||||
Vue.set(state, 'path_min_' + axis, toolpath.bounds.min[axis]);
|
|
||||||
Vue.set(state, 'path_max_' + axis, toolpath.bounds.max[axis]);
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
"state.selected_time": function () {
|
||||||
|
this.load();
|
||||||
|
},
|
||||||
|
|
||||||
|
jog_step: function () {
|
||||||
|
cookie.set_bool("jog-step", this.jog_step);
|
||||||
|
},
|
||||||
|
|
||||||
|
jog_adjust: function () {
|
||||||
|
cookie.set("jog-adjust", this.jog_adjust);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
submit_mdi: function () {
|
computed: {
|
||||||
this.send(this.mdi);
|
display_units: {
|
||||||
|
cache: false,
|
||||||
|
get: function () {
|
||||||
|
return this.$root.display_units;
|
||||||
|
},
|
||||||
|
set: function (value) {
|
||||||
|
this.$root.display_units = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
if (!this.history.length || this.history[0] != this.mdi) {
|
metric: function () {
|
||||||
this.history.unshift(this.mdi);
|
return this.display_units === "METRIC";
|
||||||
}
|
},
|
||||||
|
|
||||||
this.mdi = '';
|
mach_state: function () {
|
||||||
},
|
const cycle = this.state.cycle;
|
||||||
|
const state = this.state.xx;
|
||||||
|
|
||||||
mdi_start_pause: function () {
|
if (state != "ESTOPPED" && (cycle == "jogging" || cycle == "homing")) {
|
||||||
if (this.state.xx == 'RUNNING') {
|
return cycle.toUpperCase();
|
||||||
this.pause();
|
}
|
||||||
} else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') {
|
|
||||||
this.unpause();
|
|
||||||
} else {
|
|
||||||
this.submit_mdi();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
load_history: function (index) {
|
return state || "";
|
||||||
this.mdi = this.history[index];
|
},
|
||||||
},
|
|
||||||
|
|
||||||
open: function (e) {
|
pause_reason: function () {
|
||||||
// If we don't reset the form the browser may cache file if name is same
|
return this.state.pr;
|
||||||
// even if contents have changed
|
},
|
||||||
$('.gcode-file-input')[0].reset();
|
|
||||||
$('.gcode-file-input input').click();
|
|
||||||
},
|
|
||||||
|
|
||||||
upload: async function (e) {
|
is_running: function () {
|
||||||
const files = e.target.files || e.dataTransfer.files;
|
return this.mach_state == "RUNNING" || this.mach_state == "HOMING";
|
||||||
if (!files.length) {
|
},
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const file = files[0];
|
is_stopping: function () {
|
||||||
|
return this.mach_state == "STOPPING";
|
||||||
|
},
|
||||||
|
|
||||||
const extension = file.name.split(".").pop();
|
is_holding: function () {
|
||||||
switch (extension.toLowerCase()) {
|
return this.mach_state == "HOLDING";
|
||||||
case "nc":
|
},
|
||||||
case "ngc":
|
|
||||||
case "gcode":
|
|
||||||
case "gc":
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
is_ready: function () {
|
||||||
alert(`Unsupported file type: ${extension}`);
|
return this.mach_state == "READY";
|
||||||
return;
|
},
|
||||||
}
|
|
||||||
|
|
||||||
SvelteComponents.showDialog("Upload", {
|
is_idle: function () {
|
||||||
file,
|
return this.state.cycle == "idle";
|
||||||
onComplete: () => {
|
},
|
||||||
this.last_file_time = undefined; // Force reload
|
|
||||||
this.$broadcast('gcode-reload', file.name);
|
is_paused: function () {
|
||||||
|
return this.is_holding && (this.pause_reason == "User pause" || this.pause_reason == "Program pause");
|
||||||
|
},
|
||||||
|
|
||||||
|
can_mdi: function () {
|
||||||
|
return this.is_idle || this.state.cycle == "mdi";
|
||||||
|
},
|
||||||
|
|
||||||
|
can_set_axis: function () {
|
||||||
|
return this.is_idle;
|
||||||
|
|
||||||
|
// TODO allow setting axis position during pause
|
||||||
|
// return this.is_idle || this.is_paused;
|
||||||
|
},
|
||||||
|
|
||||||
|
message: function () {
|
||||||
|
if (this.mach_state == "ESTOPPED") {
|
||||||
|
return this.state.er;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mach_state == "HOLDING") {
|
||||||
|
return this.state.pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.messages.length) {
|
||||||
|
return this.state.messages.slice(-1)[0].text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
|
||||||
|
highlight_state: function () {
|
||||||
|
return this.mach_state == "ESTOPPED" || this.mach_state == "HOLDING";
|
||||||
|
},
|
||||||
|
|
||||||
|
plan_time: function () {
|
||||||
|
return this.state.plan_time;
|
||||||
|
},
|
||||||
|
|
||||||
|
plan_time_remaining: function () {
|
||||||
|
if (!(this.is_stopping || this.is_running || this.is_holding)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.toolpath.time - this.plan_time;
|
||||||
|
},
|
||||||
|
|
||||||
|
eta: function () {
|
||||||
|
if (this.mach_state != "RUNNING") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const remaining = this.plan_time_remaining;
|
||||||
|
const d = new Date();
|
||||||
|
d.setSeconds(d.getSeconds() + remaining);
|
||||||
|
return d.toLocaleString();
|
||||||
|
},
|
||||||
|
|
||||||
|
progress: function () {
|
||||||
|
if (!this.toolpath.time || this.is_ready) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const p = this.plan_time / this.toolpath.time;
|
||||||
|
return Math.max(1, p);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
delete_current: function () {
|
events: {
|
||||||
if (this.state.selected) {
|
jog: function (axis, power) {
|
||||||
api.delete('file/' + this.state.selected);
|
const data = { ts: new Date().getTime() };
|
||||||
}
|
data[axis] = power;
|
||||||
|
api.put("jog", data);
|
||||||
|
},
|
||||||
|
|
||||||
this.deleteGCode = false;
|
back2zero: function (axis0, axis1) {
|
||||||
|
this.send(`G0 ${axis0}0 ${axis1}0`);
|
||||||
|
},
|
||||||
|
|
||||||
|
step: function (axis, value) {
|
||||||
|
this.send(`
|
||||||
|
M70
|
||||||
|
G91
|
||||||
|
G0 ${axis}${value}
|
||||||
|
M72
|
||||||
|
`);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
delete_all: function () {
|
ready: function () {
|
||||||
api.delete('file');
|
this.load();
|
||||||
this.deleteGCode = false;
|
|
||||||
|
setInterval(() => {
|
||||||
|
this.current_time = new Date().toLocaleTimeString();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
SvelteComponents.registerControllerMethods({
|
||||||
|
stop: (...args) => this.stop(...args),
|
||||||
|
send: (...args) => this.send(...args),
|
||||||
|
isAxisHomed: (axis) => this[axis].homed,
|
||||||
|
unhome: (...args) => this.unhome(...args),
|
||||||
|
set_position: (...args) => this.set_position(...args),
|
||||||
|
set_home: (...args) => this.set_home(...args)
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
home: function (axis) {
|
methods: {
|
||||||
this.ask_home = false;
|
getJogIncrStyle(value) {
|
||||||
|
const weight = `font-weight:${this.jog_incr === value ? "bold" : "normal"}`;
|
||||||
|
const color = this.jog_incr === value ? "color:#0078e7" : "";
|
||||||
|
|
||||||
if (typeof axis == 'undefined') {
|
return [weight, color].join(";");
|
||||||
api.put('home');
|
},
|
||||||
} else if (this[axis].homingMode != 'manual') {
|
|
||||||
api.put('home/' + axis);
|
|
||||||
} else {
|
|
||||||
SvelteComponents.showDialog("ManualHomeAxis", { axis });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
set_home: function (axis, position) {
|
jog_fn: function (x_jog, y_jog, z_jog, a_jog) {
|
||||||
api.put('home/' + axis + '/set', { position: parseFloat(position) });
|
const amount = this.jog_incr_amounts[this.display_units][this.jog_incr];
|
||||||
},
|
|
||||||
|
|
||||||
unhome: function (axis) {
|
const xcmd = `X${x_jog * amount}`;
|
||||||
api.put('home/' + axis + '/clear');
|
const ycmd = `Y${y_jog * amount}`;
|
||||||
},
|
const zcmd = `Z${z_jog * amount}`;
|
||||||
|
const acmd = `A${a_jog * amount}`;
|
||||||
|
|
||||||
show_set_position: function (axis) {
|
this.send(`
|
||||||
SvelteComponents.showDialog("SetAxisPosition", { axis });
|
G91
|
||||||
},
|
${this.metric ? "G21" : "G20"}
|
||||||
|
G0 ${xcmd}${ycmd}${zcmd}${acmd}
|
||||||
|
`);
|
||||||
|
},
|
||||||
|
|
||||||
showMoveToZeroDialog: function (axes) {
|
send: function (msg) {
|
||||||
SvelteComponents.showDialog("MoveToZero", { axes });
|
this.$dispatch("send", msg);
|
||||||
},
|
},
|
||||||
|
|
||||||
showToolpathMessageDialog: function (axis) {
|
load: function () {
|
||||||
SvelteComponents.showDialog("Message", { title: this[axis].toolmsg });
|
const file_time = this.state.selected_time;
|
||||||
},
|
const file = this.state.selected;
|
||||||
|
if (this.last_file == file && this.last_file_time == file_time) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
set_position: function (axis, position) {
|
this.last_file = file;
|
||||||
api.put('position/' + axis, { 'position': parseFloat(position) });
|
this.last_file_time = file_time;
|
||||||
},
|
|
||||||
|
|
||||||
zero_all: function () {
|
this.$broadcast("gcode-load", file);
|
||||||
for (var axis of 'xyzabc') {
|
this.$broadcast("gcode-line", this.state.line);
|
||||||
if (this[axis].enabled) {
|
this.toolpath_progress = 0;
|
||||||
this.zero(axis);
|
this.load_toolpath(file, file_time);
|
||||||
|
},
|
||||||
|
|
||||||
|
load_toolpath: async function (file, file_time) {
|
||||||
|
this.toolpath = {};
|
||||||
|
|
||||||
|
if (!file || this.last_file_time != file_time) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showGcodeMessage = true;
|
||||||
|
|
||||||
|
while (this.showGcodeMessage) {
|
||||||
|
const toolpath = await api.get(`path/${file}`);
|
||||||
|
this.toolpath_progress = toolpath.progress;
|
||||||
|
|
||||||
|
if (toolpath.progress === 1 || typeof toolpath.progress == "undefined") {
|
||||||
|
this.showGcodeMessage = false;
|
||||||
|
|
||||||
|
if (toolpath.bounds) {
|
||||||
|
toolpath.filename = file;
|
||||||
|
this.toolpath_progress = 1;
|
||||||
|
this.toolpath = toolpath;
|
||||||
|
|
||||||
|
const state = this.$root.state;
|
||||||
|
for (const axis of "xyzabc") {
|
||||||
|
Vue.set(state, `path_min_${axis}`, toolpath.bounds.min[axis]);
|
||||||
|
Vue.set(state, `path_max_${axis}`, toolpath.bounds.max[axis]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
submit_mdi: function () {
|
||||||
|
this.send(this.mdi);
|
||||||
|
|
||||||
|
if (!this.history.length || this.history[0] != this.mdi) {
|
||||||
|
this.history.unshift(this.mdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mdi = "";
|
||||||
|
},
|
||||||
|
|
||||||
|
mdi_start_pause: function () {
|
||||||
|
if (this.state.xx == "RUNNING") {
|
||||||
|
this.pause();
|
||||||
|
} else if (this.state.xx == "STOPPING" || this.state.xx == "HOLDING") {
|
||||||
|
this.unpause();
|
||||||
|
} else {
|
||||||
|
this.submit_mdi();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
load_history: function (index) {
|
||||||
|
this.mdi = this.history[index];
|
||||||
|
},
|
||||||
|
|
||||||
|
open: function () {
|
||||||
|
// If we don't reset the form the browser may cache file if name is same
|
||||||
|
// even if contents have changed
|
||||||
|
$(".gcode-file-input")[0].reset();
|
||||||
|
$(".gcode-file-input input").click();
|
||||||
|
},
|
||||||
|
|
||||||
|
upload: async function (e) {
|
||||||
|
const files = e.target.files || e.dataTransfer.files;
|
||||||
|
if (!files.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = files[0];
|
||||||
|
|
||||||
|
const extension = file.name.split(".").pop();
|
||||||
|
switch (extension.toLowerCase()) {
|
||||||
|
case "nc":
|
||||||
|
case "ngc":
|
||||||
|
case "gcode":
|
||||||
|
case "gc":
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
alert(`Unsupported file type: ${extension}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SvelteComponents.showDialog("Upload", {
|
||||||
|
file,
|
||||||
|
onComplete: () => {
|
||||||
|
this.last_file_time = undefined; // Force reload
|
||||||
|
this.$broadcast("gcode-reload", file.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
delete_current: function () {
|
||||||
|
if (this.state.selected) {
|
||||||
|
api.delete(`file/${this.state.selected}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deleteGCode = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
delete_all: function () {
|
||||||
|
api.delete("file");
|
||||||
|
this.deleteGCode = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
home: function (axis) {
|
||||||
|
this.ask_home = false;
|
||||||
|
|
||||||
|
if (typeof axis == "undefined") {
|
||||||
|
api.put("home");
|
||||||
|
} else if (this[axis].homingMode != "manual") {
|
||||||
|
api.put(`home/${axis}`);
|
||||||
|
} else {
|
||||||
|
SvelteComponents.showDialog("ManualHomeAxis", { axis });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
set_home: function (axis, position) {
|
||||||
|
api.put(`home/${axis}/set`, { position: parseFloat(position) });
|
||||||
|
},
|
||||||
|
|
||||||
|
unhome: function (axis) {
|
||||||
|
api.put(`home/${axis}/clear`);
|
||||||
|
},
|
||||||
|
|
||||||
|
show_set_position: function (axis) {
|
||||||
|
SvelteComponents.showDialog("SetAxisPosition", { axis });
|
||||||
|
},
|
||||||
|
|
||||||
|
showMoveToZeroDialog: function (axes) {
|
||||||
|
SvelteComponents.showDialog("MoveToZero", { axes });
|
||||||
|
},
|
||||||
|
|
||||||
|
showToolpathMessageDialog: function (axis) {
|
||||||
|
SvelteComponents.showDialog("Message", { title: this[axis].toolmsg });
|
||||||
|
},
|
||||||
|
|
||||||
|
set_position: function (axis, position) {
|
||||||
|
api.put(`position/${axis}`, { "position": parseFloat(position) });
|
||||||
|
},
|
||||||
|
|
||||||
|
zero_all: function () {
|
||||||
|
for (const axis of "xyzabc") {
|
||||||
|
if (this[axis].enabled) {
|
||||||
|
this.zero(axis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
zero: function (axis) {
|
||||||
|
if (typeof axis == "undefined") {
|
||||||
|
this.zero_all();
|
||||||
|
} else {
|
||||||
|
this.set_position(axis, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
start_pause: function () {
|
||||||
|
if (this.state.xx == "RUNNING") {
|
||||||
|
this.pause();
|
||||||
|
} else if (this.state.xx == "STOPPING" || this.state.xx == "HOLDING") {
|
||||||
|
this.unpause();
|
||||||
|
} else {
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
start: function () {
|
||||||
|
api.put("start");
|
||||||
|
},
|
||||||
|
|
||||||
|
pause: function () {
|
||||||
|
api.put("pause");
|
||||||
|
},
|
||||||
|
|
||||||
|
unpause: function () {
|
||||||
|
api.put("unpause");
|
||||||
|
},
|
||||||
|
|
||||||
|
optional_pause: function () {
|
||||||
|
api.put("pause/optional");
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: function () {
|
||||||
|
api.put("stop");
|
||||||
|
},
|
||||||
|
|
||||||
|
step: function () {
|
||||||
|
api.put("step");
|
||||||
|
},
|
||||||
|
|
||||||
|
override_feed: function () {
|
||||||
|
api.put(`override/feed/${this.feed_override}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
override_speed: function () {
|
||||||
|
api.put(`override/speed/${this.speed_override}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
current: function (axis, value) {
|
||||||
|
const x = value / 32.0;
|
||||||
|
if (this.state[`${axis}pl`] == x) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {};
|
||||||
|
data[`${axis}pl`] = x;
|
||||||
|
this.send(JSON.stringify(data));
|
||||||
|
},
|
||||||
|
|
||||||
|
showProbeDialog: function (probeType) {
|
||||||
|
SvelteComponents.showDialog("Probe", { probeType });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
zero: function (axis) {
|
mixins: [require("./axis-vars")]
|
||||||
if (typeof axis == 'undefined') {
|
};
|
||||||
this.zero_all();
|
|
||||||
} else {
|
|
||||||
this.set_position(axis, 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
start_pause: function () {
|
|
||||||
if (this.state.xx == 'RUNNING') {
|
|
||||||
this.pause();
|
|
||||||
} else if (this.state.xx == 'STOPPING' || this.state.xx == 'HOLDING') {
|
|
||||||
this.unpause();
|
|
||||||
} else {
|
|
||||||
this.start();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
start: function () {
|
|
||||||
api.put('start')
|
|
||||||
},
|
|
||||||
|
|
||||||
pause: function () {
|
|
||||||
api.put('pause')
|
|
||||||
},
|
|
||||||
|
|
||||||
unpause: function () {
|
|
||||||
api.put('unpause')
|
|
||||||
},
|
|
||||||
|
|
||||||
optional_pause: function () {
|
|
||||||
api.put('pause/optional')
|
|
||||||
},
|
|
||||||
|
|
||||||
stop: function () {
|
|
||||||
api.put('stop')
|
|
||||||
},
|
|
||||||
|
|
||||||
step: function () {
|
|
||||||
api.put('step')
|
|
||||||
},
|
|
||||||
|
|
||||||
override_feed: function () {
|
|
||||||
api.put('override/feed/' + this.feed_override)
|
|
||||||
},
|
|
||||||
|
|
||||||
override_speed: function () {
|
|
||||||
api.put('override/speed/' + this.speed_override)
|
|
||||||
},
|
|
||||||
|
|
||||||
current: function (axis, value) {
|
|
||||||
var x = value / 32.0;
|
|
||||||
if (this.state[axis + 'pl'] == x) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = {};
|
|
||||||
data[axis + 'pl'] = x;
|
|
||||||
this.send(JSON.stringify(data));
|
|
||||||
},
|
|
||||||
|
|
||||||
showProbeDialog: function (probeType) {
|
|
||||||
SvelteComponents.showDialog("Probe", { probeType });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mixins: [require('./axis-vars')]
|
|
||||||
}
|
|
||||||
|
|||||||
110
src/js/cookie.js
110
src/js/cookie.js
@@ -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 = {
|
|
||||||
get: function (name, defaultValue) {
|
|
||||||
var decodedCookie = decodeURIComponent(document.cookie);
|
|
||||||
var ca = decodedCookie.split(';');
|
|
||||||
name = prefix + name + '=';
|
|
||||||
|
|
||||||
for (var i = 0; i < ca.length; i++) {
|
|
||||||
var c = ca[i];
|
|
||||||
while (c.charAt(0) == ' ') c = c.substring(1);
|
|
||||||
if (!c.indexOf(name)) return c.substring(name.length, c.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
set: function (name, value, days) {
|
|
||||||
var offset = 2147483647; // Max value
|
|
||||||
if (typeof days != 'undefined') offset = days * 24 * 60 * 60 * 1000;
|
|
||||||
var d = new Date();
|
|
||||||
d.setTime(d.getTime() + offset);
|
|
||||||
var expires = 'expires=' + d.toUTCString();
|
|
||||||
document.cookie = prefix + name + '=' + value + ';' + expires + ';path=/';
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
set_bool: function (name, value) {
|
|
||||||
cookie.set(name, value ? 'true' : 'false');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_bool: function (name, defaultValue) {
|
|
||||||
return cookie.get(name, defaultValue ? 'true' : 'false') == 'true';
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return cookie;
|
const cookie = {
|
||||||
}
|
get: function (name, defaultValue) {
|
||||||
|
const decodedCookie = decodeURIComponent(document.cookie);
|
||||||
|
const ca = decodedCookie.split(";");
|
||||||
|
name = `${prefix + name}=`;
|
||||||
|
|
||||||
|
for (let i = 0; i < ca.length; i++) {
|
||||||
|
let c = ca[i];
|
||||||
|
while (c.charAt(0) == " ") {
|
||||||
|
c = c.substring(1);
|
||||||
|
}
|
||||||
|
if (!c.indexOf(name)) {
|
||||||
|
return c.substring(name.length, c.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
},
|
||||||
|
|
||||||
|
set: function (name, value, days) {
|
||||||
|
let offset = 2147483647; // Max value
|
||||||
|
if (typeof days != "undefined") {
|
||||||
|
offset = days * 24 * 60 * 60 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
cookie.set(name, value ? "true" : "false");
|
||||||
|
},
|
||||||
|
|
||||||
|
get_bool: function (name, defaultValue) {
|
||||||
|
return cookie.get(name, defaultValue ? "true" : "false") == "true";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return cookie;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,168 +1,160 @@
|
|||||||
/******************************************************************************\
|
"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 = {
|
|
||||||
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
|
||||||
'/': '/', '`': '`', '=': '='}
|
|
||||||
|
|
||||||
|
const entityMap = {
|
||||||
|
"&": "&", "<": "<", ">": ">", '"': """, "'": "'",
|
||||||
|
"/": "/", "`": "`", "=": "="};
|
||||||
|
|
||||||
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: {
|
|
||||||
'gcode-load': function (file) {this.load(file)},
|
|
||||||
'gcode-clear': function () {this.clear()},
|
|
||||||
'gcode-reload': function (file) {this.reload(file)},
|
|
||||||
'gcode-line': function (line) {this.update_line(line)}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
this.clusterize = new Clusterize({
|
|
||||||
rows: [],
|
|
||||||
scrollElem: $(this.$el).find('.clusterize-scroll')[0],
|
|
||||||
contentElem: $(this.$el).find('.clusterize-content')[0],
|
|
||||||
no_data_text: 'GCode view...',
|
|
||||||
callbacks: {clusterChanged: this.highlight}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
attached: function () {
|
|
||||||
if (typeof this.clusterize != 'undefined')
|
|
||||||
this.clusterize.refresh(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
load: async function(file) {
|
|
||||||
if (file == this.file) return;
|
|
||||||
this.clear();
|
|
||||||
this.file = file;
|
|
||||||
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
const response = await fetch(`/api/file/${file}?${Math.random()}`);
|
|
||||||
const text = await response.text();
|
|
||||||
|
|
||||||
if (text.length > 20e6) {
|
|
||||||
this.clusterize.update(['File is large - gcode view disabled']);
|
|
||||||
} else {
|
|
||||||
const lines = escapeHTML(text.trimRight())
|
|
||||||
.split(/[\r\n]/)
|
|
||||||
.map((line, i) => `<li class="ln${i + 1}"><b>${i + 1}</b>${line}</li>`);
|
|
||||||
|
|
||||||
this.clusterize.update(lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.empty = false;
|
|
||||||
|
|
||||||
Vue.nextTick(this.update_line);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
clear: function () {
|
"gcode-load": function (file) {
|
||||||
this.empty = true;
|
this.load(file);
|
||||||
this.file = '';
|
},
|
||||||
this.line = -1;
|
"gcode-clear": function () {
|
||||||
this.clusterize.clear();
|
this.clear();
|
||||||
},
|
},
|
||||||
|
"gcode-reload": function (file) {
|
||||||
|
this.reload(file);
|
||||||
reload: function (file) {
|
},
|
||||||
if (file != this.file) return;
|
"gcode-line": function (line) {
|
||||||
this.clear();
|
this.update_line(line);
|
||||||
this.load(file);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
highlight: function () {
|
|
||||||
var e = $(this.$el).find('.highlight');
|
|
||||||
if (e.length) e.removeClass('highlight');
|
|
||||||
|
|
||||||
e = $(this.$el).find('.ln' + this.line);
|
|
||||||
if (e.length) e.addClass('highlight');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
update_line: function(line) {
|
|
||||||
if (typeof line != 'undefined') {
|
|
||||||
if (this.line == line) return;
|
|
||||||
this.line = line;
|
|
||||||
|
|
||||||
} else line = this.line;
|
|
||||||
|
|
||||||
var totalLines = this.clusterize.getRowsAmount();
|
|
||||||
|
|
||||||
if (line <= 0) line = 1;
|
|
||||||
if (totalLines < line) line = totalLines;
|
|
||||||
|
|
||||||
var e = $(this.$el).find('.clusterize-scroll');
|
|
||||||
|
|
||||||
var lineHeight = e[0].scrollHeight / totalLines;
|
|
||||||
var linesPerPage = Math.floor(e[0].clientHeight / lineHeight);
|
|
||||||
var current = e[0].scrollTop / lineHeight;
|
|
||||||
var target = line - 1 - Math.floor(linesPerPage / 2);
|
|
||||||
|
|
||||||
// Update scroll position
|
|
||||||
if (!this.scrolling) {
|
|
||||||
if (target < current - 20 || current + 20 < target)
|
|
||||||
e[0].scrollTop = target * lineHeight;
|
|
||||||
|
|
||||||
else {
|
|
||||||
this.scrolling = true;
|
|
||||||
e.animate({scrollTop: target * lineHeight}, {
|
|
||||||
complete: function () {this.scrolling = false}.bind(this)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
Vue.nextTick(this.highlight);
|
ready: function () {
|
||||||
|
this.clusterize = new Clusterize({
|
||||||
|
rows: [],
|
||||||
|
scrollElem: $(this.$el).find(".clusterize-scroll")[0],
|
||||||
|
contentElem: $(this.$el).find(".clusterize-content")[0],
|
||||||
|
no_data_text: "GCode view...",
|
||||||
|
callbacks: {clusterChanged: this.highlight}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
attached: function () {
|
||||||
|
if (typeof this.clusterize != "undefined") {
|
||||||
|
this.clusterize.refresh(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
load: async function(file) {
|
||||||
|
if (file == this.file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
this.file = file;
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/api/file/${file}?${Math.random()}`);
|
||||||
|
const text = await response.text();
|
||||||
|
|
||||||
|
if (text.length > 20e6) {
|
||||||
|
this.clusterize.update(["File is large - gcode view disabled"]);
|
||||||
|
} else {
|
||||||
|
const lines = escapeHTML(text.trimRight())
|
||||||
|
.split(/[\r\n]/)
|
||||||
|
.map((line, i) => `<li class="ln${i + 1}"><b>${i + 1}</b>${line}</li>`);
|
||||||
|
|
||||||
|
this.clusterize.update(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.empty = false;
|
||||||
|
|
||||||
|
Vue.nextTick(this.update_line);
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function () {
|
||||||
|
this.empty = true;
|
||||||
|
this.file = "";
|
||||||
|
this.line = -1;
|
||||||
|
this.clusterize.clear();
|
||||||
|
},
|
||||||
|
|
||||||
|
reload: function (file) {
|
||||||
|
if (file != this.file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
this.load(file);
|
||||||
|
},
|
||||||
|
|
||||||
|
highlight: function () {
|
||||||
|
let e = $(this.$el).find(".highlight");
|
||||||
|
if (e.length) {
|
||||||
|
e.removeClass("highlight");
|
||||||
|
}
|
||||||
|
|
||||||
|
e = $(this.$el).find(`.ln${this.line}`);
|
||||||
|
if (e.length) {
|
||||||
|
e.addClass("highlight");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
update_line: function(line) {
|
||||||
|
if (typeof line != "undefined") {
|
||||||
|
if (this.line == line) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.line = line;
|
||||||
|
} else {
|
||||||
|
line = this.line;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalLines = this.clusterize.getRowsAmount();
|
||||||
|
|
||||||
|
if (line <= 0) {
|
||||||
|
line = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalLines < line) {
|
||||||
|
line = totalLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
const e = $(this.$el).find(".clusterize-scroll");
|
||||||
|
|
||||||
|
const lineHeight = e[0].scrollHeight / totalLines;
|
||||||
|
const linesPerPage = Math.floor(e[0].clientHeight / lineHeight);
|
||||||
|
const current = e[0].scrollTop / lineHeight;
|
||||||
|
const target = line - 1 - Math.floor(linesPerPage / 2);
|
||||||
|
|
||||||
|
// Update scroll position
|
||||||
|
if (!this.scrolling) {
|
||||||
|
if (target < current - 20 || current + 20 < target) {
|
||||||
|
e[0].scrollTop = target * lineHeight;
|
||||||
|
} else {
|
||||||
|
this.scrolling = true;
|
||||||
|
e.animate({scrollTop: target * lineHeight}, {
|
||||||
|
complete: function () {
|
||||||
|
this.scrolling = false;
|
||||||
|
}.bind(this)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.nextTick(this.highlight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,107 +1,94 @@
|
|||||||
/******************************************************************************\
|
"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: {
|
||||||
|
modbus_status: function () {
|
||||||
|
return modbus.status_to_string(this.state.mx);
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
sense_error: function () {
|
||||||
modbus_status: function () {return modbus.status_to_string(this.state.mx)},
|
let error = "";
|
||||||
|
|
||||||
|
if (this.state.motor_voltage_sense_error) {
|
||||||
|
error += "Motor voltage\n";
|
||||||
|
}
|
||||||
|
if (this.state.motor_current_sense_error) {
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
sense_error: function () {
|
return error;
|
||||||
var error = '';
|
}
|
||||||
|
},
|
||||||
|
|
||||||
if (this.state.motor_voltage_sense_error) error += 'Motor voltage\n';
|
methods: {
|
||||||
if (this.state.motor_current_sense_error) error += 'Motor current\n';
|
is_motor_enabled: function (motor) {
|
||||||
if (this.state.load1_sense_error) error += 'Load 1\n';
|
return typeof this.state[`${motor}me`] != "undefined" && this.state[`${motor}me`];
|
||||||
if (this.state.load2_sense_error) error += 'Load 2\n';
|
},
|
||||||
if (this.state.vdd_current_sense_error) error += 'Vdd current\n';
|
|
||||||
|
|
||||||
return error;
|
get_min_pin: function (motor) {
|
||||||
|
switch (motor) {
|
||||||
|
case 0: return 3;
|
||||||
|
case 1: return 5;
|
||||||
|
case 2: return 9;
|
||||||
|
case 3: return 11;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_max_pin: function (motor) {
|
||||||
|
switch (motor) {
|
||||||
|
case 0: return 4;
|
||||||
|
case 1: return 8;
|
||||||
|
case 2: return 10;
|
||||||
|
case 3: return 12;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
motor_fault_class: function (motor, bit) {
|
||||||
|
if (typeof motor == "undefined") {
|
||||||
|
const status = this.state["fa"];
|
||||||
|
|
||||||
|
if (typeof status == "undefined") {
|
||||||
|
return "fa-question";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `fa-thumbs-${status ? "down error" : "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) {
|
||||||
|
if (typeof motor == "undefined") {
|
||||||
|
let cmd = "";
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
cmd += `\\$${i}df=0\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$dispatch("send", cmd);
|
||||||
|
} else {
|
||||||
|
this.$dispatch("send", `\\$${motor}df=0`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
is_motor_enabled: function (motor) {
|
|
||||||
return typeof this.state[motor + 'me'] != 'undefined' &&
|
|
||||||
this.state[motor + 'me'];
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_min_pin: function (motor) {
|
|
||||||
switch (motor) {
|
|
||||||
case 0: return 3;
|
|
||||||
case 1: return 5;
|
|
||||||
case 2: return 9;
|
|
||||||
case 3: return 11;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_max_pin: function (motor) {
|
|
||||||
switch (motor) {
|
|
||||||
case 0: return 4;
|
|
||||||
case 1: return 8;
|
|
||||||
case 2: return 10;
|
|
||||||
case 3: return 12;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
motor_fault_class: function (motor, bit) {
|
|
||||||
if (typeof motor == 'undefined') {
|
|
||||||
var status = this.state['fa'];
|
|
||||||
if (typeof status == 'undefined') return 'fa-question';
|
|
||||||
return 'fa-thumbs-' + (status ? 'down error' : 'up success')
|
|
||||||
}
|
|
||||||
|
|
||||||
var 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) {
|
|
||||||
if (typeof motor == 'undefined') {
|
|
||||||
var cmd = '';
|
|
||||||
for (var i = 0; i < 4; i++)
|
|
||||||
cmd += '\\$' + i + 'df=0\n';
|
|
||||||
this.$dispatch('send', cmd);
|
|
||||||
|
|
||||||
} else this.$dispatch('send', '\\$' + motor + 'df=0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
klass: function () {
|
||||||
|
switch (this.name) {
|
||||||
|
case "min-switch-0": return this.get_motor_min_class(0);
|
||||||
|
case "min-switch-1": return this.get_motor_min_class(1);
|
||||||
|
case "min-switch-2": return this.get_motor_min_class(2);
|
||||||
|
case "min-switch-3": return this.get_motor_min_class(3);
|
||||||
|
case "max-switch-0": return this.get_motor_max_class(0);
|
||||||
|
case "max-switch-1": return this.get_motor_max_class(1);
|
||||||
|
case "max-switch-2": return this.get_motor_max_class(2);
|
||||||
|
case "max-switch-3": return this.get_motor_max_class(3);
|
||||||
|
case "estop": return this.get_input_class("ew", "et");
|
||||||
|
case "probe": return this.get_input_class("pw", "pt");
|
||||||
|
case "load-1": return this.get_output_class("1");
|
||||||
|
case "load-2": return this.get_output_class("2");
|
||||||
|
case "fault": return this.get_output_class("f");
|
||||||
|
case "tool-enable-mode": return this.get_output_class("e");
|
||||||
|
case "tool-direction-mode": return this.get_output_class("d");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
tooltip: function () {
|
||||||
klass: function () {
|
switch (this.name) {
|
||||||
if (this.name == 'min-switch-0') return this.get_motor_min_class(0);
|
case "min-switch-0": return this.get_motor_min_tooltip(0);
|
||||||
if (this.name == 'min-switch-1') return this.get_motor_min_class(1);
|
case "min-switch-1": return this.get_motor_min_tooltip(1);
|
||||||
if (this.name == 'min-switch-2') return this.get_motor_min_class(2);
|
case "min-switch-2": return this.get_motor_min_tooltip(2);
|
||||||
if (this.name == 'min-switch-3') return this.get_motor_min_class(3);
|
case "min-switch-3": return this.get_motor_min_tooltip(3);
|
||||||
if (this.name == 'max-switch-0') return this.get_motor_max_class(0);
|
case "max-switch-0": return this.get_motor_max_tooltip(0);
|
||||||
if (this.name == 'max-switch-1') return this.get_motor_max_class(1);
|
case "max-switch-1": return this.get_motor_max_tooltip(1);
|
||||||
if (this.name == 'max-switch-2') return this.get_motor_max_class(2);
|
case "max-switch-2": return this.get_motor_max_tooltip(2);
|
||||||
if (this.name == 'max-switch-3') return this.get_motor_max_class(3);
|
case "max-switch-3": return this.get_motor_max_tooltip(3);
|
||||||
if (this.name == 'estop') return this.get_input_class('ew', 'et');
|
case "estop": return this.get_input_tooltip("ew", "et");
|
||||||
if (this.name == 'probe') return this.get_input_class('pw', 'pt');
|
case "probe": return this.get_input_tooltip("pw", "pt");
|
||||||
if (this.name == 'load-1') return this.get_output_class('1');
|
case "load-1": return this.get_output_tooltip("1");
|
||||||
if (this.name == 'load-2') return this.get_output_class('2');
|
case "load-2": return this.get_output_tooltip("2");
|
||||||
if (this.name == 'fault') return this.get_output_class('f');
|
case "fault": return this.get_output_tooltip("f");
|
||||||
if (this.name == 'tool-enable-mode') return this.get_output_class('e');
|
case "tool-direction-mode": return this.get_output_tooltip("d");
|
||||||
if (this.name == 'tool-direction-mode') return this.get_output_class('d');
|
case "tool-enable-mode": return this.get_output_tooltip("e");
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
get_io_state_class: function (active, state) {
|
||||||
|
if (typeof active == "undefined" || typeof state == "undefined") {
|
||||||
|
return "fa-exclamation-triangle warn";
|
||||||
|
}
|
||||||
|
|
||||||
tooltip: function () {
|
if (state == 2) {
|
||||||
if (this.name == 'min-switch-0') return this.get_motor_min_tooltip(0);
|
return "fa-circle-o";
|
||||||
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);
|
const icon = state ? "fa-plus-circle" : "fa-minus-circle";
|
||||||
if (this.name == 'max-switch-0') return this.get_motor_max_tooltip(0);
|
return `${icon} ${active ? "active" : "inactive"}`;
|
||||||
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);
|
get_input_active: function (stateCode, typeCode) {
|
||||||
if (this.name == 'estop') return this.get_input_tooltip('ew', 'et');
|
const type = this.state[typeCode];
|
||||||
if (this.name == 'probe') return this.get_input_tooltip('pw', 'pt');
|
const state = this.state[stateCode];
|
||||||
if (this.name == 'load-1') return this.get_output_tooltip('1');
|
|
||||||
if (this.name == 'load-2') return this.get_output_tooltip('2');
|
if (type == 1) {
|
||||||
if (this.name == 'fault') return this.get_output_tooltip('f');
|
return !state; // Normally open
|
||||||
if (this.name == 'tool-direction-mode')
|
} else if (type == 2) {
|
||||||
return this.get_output_tooltip('d');
|
return state; // Normally closed
|
||||||
if (this.name == 'tool-enable-mode')
|
}
|
||||||
return this.get_output_tooltip('e');
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get_input_class: function (stateCode, typeCode) {
|
||||||
|
return this.get_io_state_class(this.get_input_active(stateCode, typeCode), this.state[stateCode]);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_output_class: function (output) {
|
||||||
|
return this.get_io_state_class(this.state[`${output}oa`], this.state[`${output}os`]);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_motor_min_class: function (motor) {
|
||||||
|
return this.get_input_class(`${motor}lw`, `${motor}ls`);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_motor_max_class: function (motor) {
|
||||||
|
return this.get_input_class(`${motor}xw`, `${motor}xs`);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_tooltip: function (mode, active, state) {
|
||||||
|
if (typeof mode == "undefined" || typeof active == "undefined" || typeof state == "undefined") {
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == 0) {
|
||||||
|
state = "Lo/Gnd";
|
||||||
|
} else if (state == 1) {
|
||||||
|
state = "Hi/+3.3v";
|
||||||
|
} else if (state == 2) {
|
||||||
|
state = "Tristated";
|
||||||
|
} else {
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Mode: ${mode}\nActive: ${active ? "True" : "False"}\nLevel: ${state}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
get_input_tooltip: function (stateCode, typeCode) {
|
||||||
|
let type = this.state[typeCode];
|
||||||
|
if (type == 0) {
|
||||||
|
return "Disabled";
|
||||||
|
} else if (type == 1) {
|
||||||
|
type = "Normally open";
|
||||||
|
} else if (type == 2) {
|
||||||
|
type = "Normally closed";
|
||||||
|
}
|
||||||
|
|
||||||
|
const active = this.get_input_active(stateCode, typeCode);
|
||||||
|
const state = this.state[stateCode];
|
||||||
|
|
||||||
|
return this.get_tooltip(type, active, state);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_output_tooltip: function (output) {
|
||||||
|
let mode = this.state[`${output}om`];
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_motor_min_tooltip: function (motor) {
|
||||||
|
return this.get_input_tooltip(`${motor}lw`, `${motor}ls`);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_motor_max_tooltip: function (motor) {
|
||||||
|
return this.get_input_tooltip(`${motor}xw`, `${motor}xs`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
get_io_state_class: function (active, state) {
|
|
||||||
if (typeof active == 'undefined' || typeof state == 'undefined')
|
|
||||||
return 'fa-exclamation-triangle warn';
|
|
||||||
|
|
||||||
if (state == 2) return 'fa-circle-o';
|
|
||||||
|
|
||||||
return (state ? 'fa-plus-circle' : 'fa-minus-circle') + ' ' +
|
|
||||||
(active ? 'active' : 'inactive');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_input_active: function (stateCode, typeCode) {
|
|
||||||
var type = this.state[typeCode];
|
|
||||||
var state = this.state[stateCode];
|
|
||||||
|
|
||||||
if (type == 1) return !state; // Normally open
|
|
||||||
else if (type == 2) return state; // Normally closed
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_input_class: function (stateCode, typeCode) {
|
|
||||||
return this.get_io_state_class(this.get_input_active(stateCode, typeCode),
|
|
||||||
this.state[stateCode]);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_output_class: function (output) {
|
|
||||||
return this.get_io_state_class(this.state[output + 'oa'],
|
|
||||||
this.state[output + 'os']);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_motor_min_class: function (motor) {
|
|
||||||
return this.get_input_class(motor + 'lw', motor + 'ls');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_motor_max_class: function (motor) {
|
|
||||||
return this.get_input_class(motor + 'xw', motor + 'xs');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_tooltip: function (mode, active, state) {
|
|
||||||
if (typeof mode == 'undefined' || typeof active == 'undefined' ||
|
|
||||||
typeof state == 'undefined') return 'Invalid';
|
|
||||||
|
|
||||||
if (state == 0) state = 'Lo/Gnd';
|
|
||||||
else if (state == 1) state = 'Hi/+3.3v';
|
|
||||||
else if (state == 2) state = 'Tristated';
|
|
||||||
else return 'Invalid';
|
|
||||||
|
|
||||||
return 'Mode: ' + mode + '\nActive: ' + (active ? 'True' : 'False') +
|
|
||||||
'\nLevel: ' + state;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_input_tooltip: function (stateCode, typeCode) {
|
|
||||||
var type = this.state[typeCode];
|
|
||||||
if (type == 0) return 'Disabled';
|
|
||||||
else if (type == 1) type = 'Normally open';
|
|
||||||
else if (type == 2) type = 'Normally closed';
|
|
||||||
|
|
||||||
var active = this.get_input_active(stateCode, typeCode);
|
|
||||||
var state = this.state[stateCode];
|
|
||||||
|
|
||||||
return this.get_tooltip(type, active, state);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_output_tooltip: function (output) {
|
|
||||||
var 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'];
|
|
||||||
var state = this.state[output + 'os'];
|
|
||||||
|
|
||||||
return this.get_tooltip(mode, active, state);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_motor_min_tooltip: function (motor) {
|
|
||||||
return this.get_input_tooltip(motor + 'lw', motor + 'ls');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_motor_max_tooltip: function (motor) {
|
|
||||||
return this.get_input_tooltip(motor + 'xw', motor + 'xs');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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;
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
216
src/js/main.js
216
src/js/main.js
@@ -1,148 +1,148 @@
|
|||||||
'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);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c.indexOf(name)) {
|
if (!c.indexOf(name)) {
|
||||||
return c.substring(name.length, c.length);
|
return c.substring(name.length, c.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
Vue.component('templated-input', require('./templated-input'));
|
|
||||||
Vue.component('message', require('./message'));
|
|
||||||
Vue.component('indicators', require('./indicators'));
|
|
||||||
Vue.component('io-indicator', require('./io-indicator'));
|
|
||||||
Vue.component('console', require('./console'));
|
|
||||||
Vue.component('unit-value', require('./unit-value'));
|
|
||||||
|
|
||||||
Vue.filter('number', function (value) {
|
|
||||||
if (isNaN(value)) {
|
|
||||||
return 'NaN';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.toLocaleString();
|
// Register global components
|
||||||
});
|
Vue.component("templated-input", require("./templated-input"));
|
||||||
|
Vue.component("message", require("./message"));
|
||||||
|
Vue.component("indicators", require("./indicators"));
|
||||||
|
Vue.component("io-indicator", require("./io-indicator"));
|
||||||
|
Vue.component("console", require("./console"));
|
||||||
|
Vue.component("unit-value", require("./unit-value"));
|
||||||
|
|
||||||
Vue.filter('percent', function (value, precision) {
|
Vue.filter("number", function (value) {
|
||||||
if (typeof value == 'undefined') {
|
if (isNaN(value)) {
|
||||||
return '';
|
return "NaN";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof precision == 'undefined') {
|
return value.toLocaleString();
|
||||||
precision = 2;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return (value * 100.0).toFixed(precision) + '%';
|
Vue.filter("percent", function (value, precision) {
|
||||||
});
|
if (typeof value == "undefined") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
Vue.filter('non_zero_percent', function (value, precision) {
|
if (typeof precision == "undefined") {
|
||||||
if (!value) {
|
precision = 2;
|
||||||
return '';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof precision == 'undefined') {
|
return `${(value * 100.0).toFixed(precision)}%`;
|
||||||
precision = 2;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return (value * 100.0).toFixed(precision) + '%';
|
Vue.filter("non_zero_percent", function (value, precision) {
|
||||||
});
|
if (!value) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
Vue.filter('fixed', function (value, precision) {
|
if (typeof precision == "undefined") {
|
||||||
if (typeof value == 'undefined') {
|
precision = 2;
|
||||||
return '0';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return parseFloat(value).toFixed(precision)
|
return `${(value * 100.0).toFixed(precision)}%`;
|
||||||
});
|
});
|
||||||
|
|
||||||
Vue.filter('upper', function (value) {
|
Vue.filter("fixed", function (value, precision) {
|
||||||
if (typeof value == 'undefined') {
|
if (typeof value == "undefined") {
|
||||||
return '';
|
return "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.toUpperCase()
|
return parseFloat(value).toFixed(precision);
|
||||||
});
|
});
|
||||||
|
|
||||||
Vue.filter('time', function (value, precision) {
|
Vue.filter("upper", function (value) {
|
||||||
if (isNaN(value)) {
|
if (typeof value == "undefined") {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(precision)) {
|
return value.toUpperCase();
|
||||||
precision = 0;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
var MIN = 60;
|
Vue.filter("time", function (value, precision) {
|
||||||
var HR = MIN * 60;
|
if (isNaN(value)) {
|
||||||
var DAY = HR * 24;
|
return "";
|
||||||
var parts = [];
|
}
|
||||||
|
|
||||||
if (DAY <= value) {
|
if (isNaN(precision)) {
|
||||||
parts.push(Math.floor(value / DAY));
|
precision = 0;
|
||||||
value %= DAY;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (HR <= value) {
|
const MIN = 60;
|
||||||
parts.push(Math.floor(value / HR));
|
const HR = MIN * 60;
|
||||||
value %= HR;
|
const DAY = HR * 24;
|
||||||
}
|
const parts = [];
|
||||||
|
|
||||||
if (MIN <= value) {
|
if (DAY <= value) {
|
||||||
parts.push(Math.floor(value / MIN));
|
parts.push(Math.floor(value / DAY));
|
||||||
value %= MIN;
|
value %= DAY;
|
||||||
} else {
|
}
|
||||||
parts.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
parts.push(value);
|
if (HR <= value) {
|
||||||
|
parts.push(Math.floor(value / HR));
|
||||||
|
value %= HR;
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < parts.length; i++) {
|
if (MIN <= value) {
|
||||||
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
|
parts.push(Math.floor(value / MIN));
|
||||||
if (i && parts[i] < 10) {
|
value %= MIN;
|
||||||
parts[i] = '0' + parts[i];
|
} else {
|
||||||
}
|
parts.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parts.join(':');
|
parts.push(value);
|
||||||
});
|
|
||||||
|
|
||||||
// Vue app
|
for (let i = 0; i < parts.length; i++) {
|
||||||
require('./app');
|
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
|
||||||
|
if (i && parts[i] < 10) {
|
||||||
|
parts[i] = `0${parts[i]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.join(":");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Vue app
|
||||||
|
require("./app");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,47 +1,19 @@
|
|||||||
/******************************************************************************\
|
"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: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
twoWay: true
|
twoWay: true
|
||||||
},
|
},
|
||||||
|
|
||||||
class: {
|
class: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
twoWay: false
|
twoWay: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
has_user_value: function () {
|
||||||
|
const type = this.model["reg-type"];
|
||||||
|
return type.indexOf("write") != -1 || type.indexOf("fixed") != -1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
methods: {
|
||||||
has_user_value: function () {
|
change: function () {
|
||||||
var type = this.model['reg-type'];
|
this.$dispatch("input-changed");
|
||||||
return type.indexOf('write') != -1 || type.indexOf('fixed') != -1;
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
change: function () {this.$dispatch('input-changed')}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,51 +1,23 @@
|
|||||||
/******************************************************************************\
|
"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,
|
||||||
INVALID: 3,
|
INVALID: 3,
|
||||||
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;
|
||||||
|
|||||||
@@ -1,120 +1,120 @@
|
|||||||
'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 () {
|
||||||
return this.$root.display_units === "METRIC";
|
return this.$root.display_units === "METRIC";
|
||||||
},
|
},
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
motor: function () {
|
||||||
|
return this.config.motors[this.index];
|
||||||
|
},
|
||||||
|
|
||||||
|
invalidMaxVelocity: function () {
|
||||||
|
return this.maxMaxVelocity < this.motor["max-velocity"];
|
||||||
|
},
|
||||||
|
|
||||||
|
maxMaxVelocity: function () {
|
||||||
|
return 1 * (15 * this.umPerStep / this.motor["microsteps"]).toFixed(3);
|
||||||
|
},
|
||||||
|
|
||||||
|
ustepPerSec: function () {
|
||||||
|
return this.rpm * this.stepsPerRev * this.motor["microsteps"] / 60;
|
||||||
|
},
|
||||||
|
|
||||||
|
rpm: function () {
|
||||||
|
return 1000 * this.motor["max-velocity"] / this.motor["travel-per-rev"];
|
||||||
|
},
|
||||||
|
|
||||||
|
gForce: function () {
|
||||||
|
return this.motor["max-accel"] * 0.0283254504;
|
||||||
|
},
|
||||||
|
|
||||||
|
gForcePerMin: function () {
|
||||||
|
return this.motor["max-jerk"] * 0.0283254504;
|
||||||
|
},
|
||||||
|
|
||||||
|
stepsPerRev: function () {
|
||||||
|
return 360 / this.motor["step-angle"];
|
||||||
|
},
|
||||||
|
|
||||||
|
umPerStep: function () {
|
||||||
|
return this.motor["travel-per-rev"] * this.motor["step-angle"] / 0.36;
|
||||||
|
},
|
||||||
|
|
||||||
|
milPerStep: function () {
|
||||||
|
return this.umPerStep / 25.4;
|
||||||
|
},
|
||||||
|
|
||||||
|
invalidStallVelocity: function () {
|
||||||
|
if (!this.motor["homing-mode"].startsWith("stall-")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.maxStallVelocity < this.motor["search-velocity"];
|
||||||
|
},
|
||||||
|
|
||||||
|
stallRPM: function () {
|
||||||
|
const v = this.motor["search-velocity"];
|
||||||
|
return 1000 * v / this.motor["travel-per-rev"];
|
||||||
|
},
|
||||||
|
|
||||||
|
maxStallVelocity: function () {
|
||||||
|
const maxRate = 900000 / this.motor["stall-sample-time"];
|
||||||
|
const ustep = this.motor["stall-microstep"];
|
||||||
|
const angle = this.motor["step-angle"];
|
||||||
|
const travel = this.motor["travel-per-rev"];
|
||||||
|
const maxStall = maxRate * 60 / 360 / 1000 * angle / ustep * travel;
|
||||||
|
|
||||||
|
return 1 * maxStall.toFixed(3);
|
||||||
|
},
|
||||||
|
|
||||||
|
stallUStepPerSec: function () {
|
||||||
|
const ustep = this.motor["stall-microstep"];
|
||||||
|
return this.stallRPM * this.stepsPerRev * ustep / 60;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
motor: function () {
|
events: {
|
||||||
return this.config.motors[this.index]
|
"input-changed": function () {
|
||||||
},
|
Vue.nextTick(function () {
|
||||||
|
// Limit max-velocity
|
||||||
|
if (this.invalidMaxVelocity) {
|
||||||
|
this.$set('motor["max-velocity"]', this.maxMaxVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
invalidMaxVelocity: function () {
|
//Limit stall-velocity
|
||||||
return this.maxMaxVelocity < this.motor['max-velocity'];
|
if (this.invalidStallVelocity) {
|
||||||
},
|
this.$set('motor["search-velocity"]', this.maxStallVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
maxMaxVelocity: function () {
|
this.$dispatch("config-changed");
|
||||||
return 1 * (15 * this.umPerStep / this.motor['microsteps']).toFixed(3);
|
}.bind(this));
|
||||||
},
|
|
||||||
|
|
||||||
ustepPerSec: function () {
|
return false;
|
||||||
return this.rpm * this.stepsPerRev * this.motor['microsteps'] / 60;
|
|
||||||
},
|
|
||||||
|
|
||||||
rpm: function () {
|
|
||||||
return 1000 * this.motor['max-velocity'] / this.motor['travel-per-rev'];
|
|
||||||
},
|
|
||||||
|
|
||||||
gForce: function () {
|
|
||||||
return this.motor['max-accel'] * 0.0283254504
|
|
||||||
},
|
|
||||||
|
|
||||||
gForcePerMin: function () {
|
|
||||||
return this.motor['max-jerk'] * 0.0283254504
|
|
||||||
},
|
|
||||||
|
|
||||||
stepsPerRev: function () {
|
|
||||||
return 360 / this.motor['step-angle']
|
|
||||||
},
|
|
||||||
|
|
||||||
umPerStep: function () {
|
|
||||||
return this.motor['travel-per-rev'] * this.motor['step-angle'] / 0.36
|
|
||||||
},
|
|
||||||
|
|
||||||
milPerStep: function () {
|
|
||||||
return this.umPerStep / 25.4
|
|
||||||
},
|
|
||||||
|
|
||||||
invalidStallVelocity: function () {
|
|
||||||
if (!this.motor['homing-mode'].startsWith('stall-')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.maxStallVelocity < this.motor['search-velocity'];
|
|
||||||
},
|
|
||||||
|
|
||||||
stallRPM: function () {
|
|
||||||
var v = this.motor['search-velocity'];
|
|
||||||
return 1000 * v / this.motor['travel-per-rev'];
|
|
||||||
},
|
|
||||||
|
|
||||||
maxStallVelocity: function () {
|
|
||||||
var maxRate = 900000 / this.motor['stall-sample-time'];
|
|
||||||
var ustep = this.motor['stall-microstep'];
|
|
||||||
var angle = this.motor['step-angle'];
|
|
||||||
var travel = this.motor['travel-per-rev'];
|
|
||||||
var maxStall = maxRate * 60 / 360 / 1000 * angle / ustep * travel;
|
|
||||||
|
|
||||||
return 1 * maxStall.toFixed(3);
|
|
||||||
},
|
|
||||||
|
|
||||||
stallUStepPerSec: function () {
|
|
||||||
var ustep = this.motor['stall-microstep'];
|
|
||||||
return this.stallRPM * this.stepsPerRev * ustep / 60;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
events: {
|
|
||||||
'input-changed': function () {
|
|
||||||
Vue.nextTick(function () {
|
|
||||||
// Limit max-velocity
|
|
||||||
if (this.invalidMaxVelocity) {
|
|
||||||
this.$set('motor["max-velocity"]', this.maxMaxVelocity);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
//Limit stall-velocity
|
methods: {
|
||||||
if (this.invalidStallVelocity) {
|
show: function (name, templ) {
|
||||||
this.$set('motor["search-velocity"]', this.maxStallVelocity);
|
if (templ.hmodes == undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return templ.hmodes.indexOf(this.motor["homing-mode"]) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
}.bind(this))
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
methods: {
|
|
||||||
show: function (name, templ) {
|
|
||||||
if (templ.hmodes == undefined) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return templ.hmodes.indexOf(this.motor['homing-mode']) != -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
1191
src/js/orbit.js
1191
src/js/orbit.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
template: "#settings-view-template",
|
template: "#settings-view-template",
|
||||||
|
|
||||||
attached: function () {
|
attached: function () {
|
||||||
this.svelteComponent = SvelteComponents.createComponent(
|
this.svelteComponent = SvelteComponents.createComponent(
|
||||||
"SettingsView",
|
"SettingsView",
|
||||||
document.getElementById("settings")
|
document.getElementById("settings")
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
detached: function() {
|
||||||
this.svelteComponent.$destroy();
|
this.svelteComponent.$destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
181
src/js/sock.js
181
src/js/sock.js
@@ -1,127 +1,106 @@
|
|||||||
/******************************************************************************\
|
"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;
|
||||||
|
}
|
||||||
|
if (typeof timeout == "undefined") {
|
||||||
|
timeout = 16000;
|
||||||
|
}
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
this.url = url;
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
this.retry = retry;
|
||||||
version 2 as published by the Free Software Foundation. You should
|
this.timeout = timeout;
|
||||||
have received a copy of the GNU General Public License, version 2
|
this.divisions = 4;
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
this.count = 0;
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
this.connect();
|
||||||
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
|
Sock.prototype.onmessage = function () {
|
||||||
License along with the software. If not, see
|
// Ignore
|
||||||
<http://www.gnu.org/licenses/>.
|
};
|
||||||
|
|
||||||
For information regarding this software email:
|
Sock.prototype.onopen = function () {
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
// Ignore
|
||||||
|
};
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'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.retry = retry;
|
|
||||||
this.timeout = timeout;
|
|
||||||
this.divisions = 4;
|
|
||||||
this.count = 0;
|
|
||||||
|
|
||||||
this.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Sock.prototype.onmessage = function () {}
|
|
||||||
Sock.prototype.onopen = function () {}
|
|
||||||
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 () {
|
||||||
|
console.debug("connected");
|
||||||
|
this.heartbeat("open");
|
||||||
|
this.onopen();
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
this._sock.onopen = function () {
|
this._sock.onclose = function () {
|
||||||
console.debug('connected');
|
console.debug("disconnected");
|
||||||
this.heartbeat('open');
|
this._cancel_timeout();
|
||||||
this.onopen();
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
|
|
||||||
this._sock.onclose = function () {
|
|
||||||
console.debug('disconnected');
|
|
||||||
this._cancel_timeout();
|
|
||||||
|
|
||||||
this.onclose();
|
|
||||||
if (typeof this._sock != 'undefined')
|
|
||||||
setTimeout(this.connect.bind(this), this.retry);
|
|
||||||
}.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.onclose();
|
||||||
|
if (typeof this._sock != "undefined") {
|
||||||
|
setTimeout(this.connect.bind(this), this.retry);
|
||||||
|
}
|
||||||
|
}.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 (msg) {
|
|
||||||
//console.debug('heartbeat ' + new Date().toLocaleTimeString() + ' ' + msg);
|
|
||||||
this._cancel_timeout();
|
|
||||||
this._set_timeout();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Sock.prototype.heartbeat = function () {
|
||||||
|
this._cancel_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
|
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
'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: {
|
|
||||||
metric: function () {
|
|
||||||
return this.$root.display_units === "METRIC";
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_view: function () {
|
computed: {
|
||||||
if (this.template.scale) {
|
metric: function () {
|
||||||
if (this.metric) {
|
return this.$root.display_units === "METRIC";
|
||||||
return 1 * this.model.toFixed(3);
|
},
|
||||||
|
|
||||||
|
_view: function () {
|
||||||
|
if (this.template.scale) {
|
||||||
|
if (this.metric) {
|
||||||
|
return 1 * this.model.toFixed(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1 * (this.model / this.template.scale).toFixed(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.model;
|
||||||
|
},
|
||||||
|
|
||||||
|
units: function () {
|
||||||
|
return (this.metric || !this.template.iunit)
|
||||||
|
? this.template.unit
|
||||||
|
: this.template.iunit;
|
||||||
|
},
|
||||||
|
|
||||||
|
title: function () {
|
||||||
|
let s = `Default :${this.template.default} ${(this.template.unit || "")}`;
|
||||||
|
|
||||||
|
if (typeof this.template.help != "undefined") {
|
||||||
|
s = `${this.template.help}\n${s}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1 * (this.model / this.template.scale).toFixed(4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.model;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
units: function () {
|
watch: {
|
||||||
return (this.metric || !this.template.iunit)
|
_view: function () {
|
||||||
? this.template.unit
|
this.view = this._view;
|
||||||
: this.template.iunit;
|
},
|
||||||
|
|
||||||
|
view: function () {
|
||||||
|
if (this.template.scale && !this.metric) {
|
||||||
|
this.model = this.view * this.template.scale;
|
||||||
|
} else {
|
||||||
|
this.model = this.view;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
title: function () {
|
ready: function () {
|
||||||
var s = `Default :${this.template.default} ${(this.template.unit || '')}`;
|
this.view = this._view;
|
||||||
|
|
||||||
if (typeof this.template.help != 'undefined') {
|
|
||||||
s = this.template.help + '\n' + s;
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
_view: function () {
|
|
||||||
this.view = this._view
|
|
||||||
},
|
},
|
||||||
|
|
||||||
view: function () {
|
methods: {
|
||||||
if (this.template.scale && !this.metric) {
|
change: function () {
|
||||||
this.model = this.view * this.template.scale;
|
this.$dispatch("input-changed");
|
||||||
} else {
|
}
|
||||||
this.model = this.view;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
this.view = this._view
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
change: function () {
|
|
||||||
this.$dispatch('input-changed')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,277 +1,252 @@
|
|||||||
/******************************************************************************\
|
"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 {
|
||||||
address: 0,
|
address: 0,
|
||||||
value: 0,
|
value: 0,
|
||||||
toolList: [
|
toolList: [
|
||||||
{
|
{
|
||||||
id: "disabled",
|
id: "disabled",
|
||||||
name: "Disabled"
|
name: "Disabled"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "router",
|
id: "router",
|
||||||
type: "PWM Spindle",
|
type: "PWM Spindle",
|
||||||
name: "Router (Makita, etc)"
|
name: "Router (Makita, etc)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "laser",
|
id: "laser",
|
||||||
type: "PWM Spindle",
|
type: "PWM Spindle",
|
||||||
name: "Laser (J Tech, etc)"
|
name: "Laser (J Tech, etc)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "pwm",
|
id: "pwm",
|
||||||
name: "PWM Spindle"
|
name: "PWM Spindle"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "unsupported-separator",
|
id: "unsupported-separator",
|
||||||
name: "Unsupported Tools",
|
name: "Unsupported Tools",
|
||||||
disabled: true,
|
disabled: true,
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "huanyang-vfd",
|
id: "huanyang-vfd",
|
||||||
name: "Huanyang VFD",
|
name: "Huanyang VFD",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "custom-modbus-vfd",
|
id: "custom-modbus-vfd",
|
||||||
name: "Custom Modbus VFD",
|
name: "Custom Modbus VFD",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "ac-tech-vfd",
|
id: "ac-tech-vfd",
|
||||||
name: "AC-Tech VFD",
|
name: "AC-Tech VFD",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "nowforever-vfd",
|
id: "nowforever-vfd",
|
||||||
name: "Nowforever VFD",
|
name: "Nowforever VFD",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "delta-vfd",
|
id: "delta-vfd",
|
||||||
name: "Delta VFD015M21A (Beta)",
|
name: "Delta VFD015M21A (Beta)",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "yl600-vfd",
|
id: "yl600-vfd",
|
||||||
name: "YL600, YL620, YL620-A VFD (Beta)",
|
name: "YL600, YL620, YL620-A VFD (Beta)",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "fr-d700-vfd",
|
id: "fr-d700-vfd",
|
||||||
name: "FR-D700 (Beta)",
|
name: "FR-D700 (Beta)",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "sunfar-e300-vfd",
|
id: "sunfar-e300-vfd",
|
||||||
name: "Sunfar E300 (Beta)",
|
name: "Sunfar E300 (Beta)",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "omron-mx2-vfd",
|
id: "omron-mx2-vfd",
|
||||||
name: "OMRON MX2",
|
name: "OMRON MX2",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
"modbus-reg": require("./modbus-reg.js")
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
"state.mr": function () {
|
||||||
|
this.value = this.state.mr;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
"input-changed": function () {
|
||||||
|
this.$dispatch("config-changed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ready: function () {
|
||||||
|
this.value = this.state.mr;
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
regs_tmpl: function () {
|
||||||
|
return this.template["modbus-spindle"].regs;
|
||||||
|
},
|
||||||
|
|
||||||
|
tool_type: function () {
|
||||||
|
return this.config.tool["tool-type"].toUpperCase();
|
||||||
|
},
|
||||||
|
|
||||||
|
selected_tool: function () {
|
||||||
|
return this.config.tool["selected-tool"];
|
||||||
|
},
|
||||||
|
|
||||||
|
is_pwm_spindle: function () {
|
||||||
|
return this.selected_tool == "pwm";
|
||||||
|
},
|
||||||
|
|
||||||
|
is_modbus: function () {
|
||||||
|
switch (this.selected_tool) {
|
||||||
|
case "disabled":
|
||||||
|
case "laser":
|
||||||
|
case "router":
|
||||||
|
case "pwm":
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
modbus_status: function () {
|
||||||
|
return modbus.status_to_string(this.state.mx);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
change_selected_tool: function () {
|
||||||
|
const selectedToolSettings = this.config["selected-tool-settings"] || {};
|
||||||
|
const settings = selectedToolSettings[this.selected_tool] || {};
|
||||||
|
this.config.tool = merge({}, this.config.tool, settings["tool"]);
|
||||||
|
this.config["pwm-spindle"] = merge({}, this.config["pwm-spindle"], settings["pwm-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"]);
|
||||||
|
this.config.tool["tool-type"] = tool.type || tool.name;
|
||||||
|
|
||||||
|
this.$dispatch("config-changed");
|
||||||
|
},
|
||||||
|
|
||||||
|
show_tool_settings: function (key) {
|
||||||
|
switch (true) {
|
||||||
|
case key === "tool-type":
|
||||||
|
case key === "selected-tool":
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case this.selected_tool === "disabled":
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case this.selected_tool === "laser":
|
||||||
|
case this.selected_tool === "router":
|
||||||
|
switch (key) {
|
||||||
|
case "tool-enable-mode":
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_reg_type: function (reg) {
|
||||||
|
return this.regs_tmpl.template["reg-type"].values[this.state[`${reg}vt`]];
|
||||||
|
},
|
||||||
|
|
||||||
|
get_reg_addr: function (reg) {
|
||||||
|
return this.state[`${reg}va`];
|
||||||
|
},
|
||||||
|
|
||||||
|
get_reg_value: function (reg) {
|
||||||
|
return this.state[`${reg}vv`];
|
||||||
|
},
|
||||||
|
|
||||||
|
get_reg_fails: function (reg) {
|
||||||
|
const fails = this.state[`${reg}vr`];
|
||||||
|
return fails == 255 ? "Max" : fails;
|
||||||
|
},
|
||||||
|
|
||||||
|
show_modbus_field: function (key) {
|
||||||
|
return key != "regs" && (key != "multi-write" || this.tool_type == "CUSTOM MODBUS VFD");
|
||||||
|
},
|
||||||
|
|
||||||
|
read: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
api.put("modbus/read", { address: this.address });
|
||||||
|
},
|
||||||
|
|
||||||
|
write: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
api.put("modbus/write", { address: this.address, value: this.value });
|
||||||
|
},
|
||||||
|
|
||||||
|
customize: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.config.tool["tool-type"] = "Custom Modbus VFD";
|
||||||
|
|
||||||
|
const regs = this.config["modbus-spindle"].regs;
|
||||||
|
for (let i = 0; i < regs.length; i++) {
|
||||||
|
const reg = this.regs_tmpl.index[i];
|
||||||
|
regs[i]["reg-type"] = this.get_reg_type(reg);
|
||||||
|
regs[i]["reg-addr"] = this.get_reg_addr(reg);
|
||||||
|
regs[i]["reg-value"] = this.get_reg_value(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$dispatch("config-changed");
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.config.tool["tool-type"] = "Custom Modbus VFD";
|
||||||
|
|
||||||
|
const regs = this.config["modbus-spindle"].regs;
|
||||||
|
for (let i = 0; i < regs.length; i++) {
|
||||||
|
regs[i]["reg-type"] = "disabled";
|
||||||
|
regs[i]["reg-addr"] = 0;
|
||||||
|
regs[i]["reg-value"] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$dispatch("config-changed");
|
||||||
|
},
|
||||||
|
|
||||||
|
reset_failures: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const regs = this.config["modbus-spindle"].regs;
|
||||||
|
for (let reg = 0; reg < regs.length; reg++) {
|
||||||
|
this.$dispatch("send", `$${reg}vr=0`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
components: {
|
|
||||||
'modbus-reg': require('./modbus-reg.js')
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
'state.mr': function () { this.value = this.state.mr }
|
|
||||||
},
|
|
||||||
|
|
||||||
events: {
|
|
||||||
'input-changed': function () {
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
this.value = this.state.mr;
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
regs_tmpl: function () {
|
|
||||||
return this.template['modbus-spindle'].regs;
|
|
||||||
},
|
|
||||||
|
|
||||||
tool_type: function () {
|
|
||||||
return this.config.tool['tool-type'].toUpperCase();
|
|
||||||
},
|
|
||||||
|
|
||||||
selected_tool: function () {
|
|
||||||
return this.config.tool['selected-tool'];
|
|
||||||
},
|
|
||||||
|
|
||||||
is_pwm_spindle: function () {
|
|
||||||
return this.selected_tool == 'pwm';
|
|
||||||
},
|
|
||||||
|
|
||||||
is_modbus: function () {
|
|
||||||
switch (this.selected_tool) {
|
|
||||||
case "disabled":
|
|
||||||
case "laser":
|
|
||||||
case "router":
|
|
||||||
case "pwm":
|
|
||||||
return false;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
modbus_status: function () {
|
|
||||||
return modbus.status_to_string(this.state.mx);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
change_selected_tool: function () {
|
|
||||||
const selectedToolSettings = this.config['selected-tool-settings'] || {};
|
|
||||||
const settings = selectedToolSettings[this.selected_tool] || {};
|
|
||||||
this.config.tool = merge({}, this.config.tool, settings['tool']);
|
|
||||||
this.config['pwm-spindle'] = merge({}, this.config['pwm-spindle'], settings['pwm-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']);
|
|
||||||
this.config.tool["tool-type"] = tool.type || tool.name;
|
|
||||||
|
|
||||||
this.$dispatch("config-changed");
|
|
||||||
},
|
|
||||||
|
|
||||||
show_tool_settings: function (key) {
|
|
||||||
switch (true) {
|
|
||||||
case key === "tool-type":
|
|
||||||
case key === "selected-tool":
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case this.selected_tool === "disabled":
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case this.selected_tool === "laser":
|
|
||||||
case this.selected_tool === "router":
|
|
||||||
switch (key) {
|
|
||||||
case "tool-enable-mode":
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get_reg_type: function (reg) {
|
|
||||||
return this.regs_tmpl.template['reg-type'].values[this.state[reg + 'vt']];
|
|
||||||
},
|
|
||||||
|
|
||||||
get_reg_addr: function (reg) {
|
|
||||||
return this.state[reg + 'va'];
|
|
||||||
},
|
|
||||||
|
|
||||||
get_reg_value: function (reg) {
|
|
||||||
return this.state[reg + 'vv'];
|
|
||||||
},
|
|
||||||
|
|
||||||
get_reg_fails: function (reg) {
|
|
||||||
const fails = this.state[reg + 'vr']
|
|
||||||
return fails == 255 ? 'Max' : fails;
|
|
||||||
},
|
|
||||||
|
|
||||||
show_modbus_field: function (key) {
|
|
||||||
return key != 'regs' &&
|
|
||||||
(key != 'multi-write' || this.tool_type == 'CUSTOM MODBUS VFD');
|
|
||||||
},
|
|
||||||
|
|
||||||
read: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
api.put('modbus/read', { address: this.address });
|
|
||||||
},
|
|
||||||
|
|
||||||
write: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
api.put('modbus/write', { address: this.address, value: this.value });
|
|
||||||
},
|
|
||||||
|
|
||||||
customize: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.config.tool['tool-type'] = 'Custom Modbus VFD';
|
|
||||||
|
|
||||||
const regs = this.config['modbus-spindle'].regs;
|
|
||||||
for (let i = 0; i < regs.length; i++) {
|
|
||||||
const reg = this.regs_tmpl.index[i];
|
|
||||||
regs[i]['reg-type'] = this.get_reg_type(reg);
|
|
||||||
regs[i]['reg-addr'] = this.get_reg_addr(reg);
|
|
||||||
regs[i]['reg-value'] = this.get_reg_value(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
},
|
|
||||||
|
|
||||||
clear: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.config.tool['tool-type'] = 'Custom Modbus VFD';
|
|
||||||
|
|
||||||
const regs = this.config['modbus-spindle'].regs;
|
|
||||||
for (let i = 0; i < regs.length; i++) {
|
|
||||||
regs[i]['reg-type'] = 'disabled';
|
|
||||||
regs[i]['reg-addr'] = 0;
|
|
||||||
regs[i]['reg-value'] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
},
|
|
||||||
|
|
||||||
reset_failures: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const regs = this.config['modbus-spindle'].regs;
|
|
||||||
for (let reg = 0; reg < regs.length; reg++)
|
|
||||||
this.$dispatch('send', '\$' + reg + 'vr=0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
'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: {
|
||||||
cache: false,
|
cache: false,
|
||||||
get: function () {
|
get: function () {
|
||||||
return this.$root.display_units === "METRIC";
|
return this.$root.display_units === "METRIC";
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
text: function () {
|
||||||
|
let value = this.value;
|
||||||
|
if (typeof value == "undefined") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.metric) {
|
||||||
|
value /= this.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (1 * value.toFixed(this.precision)).toLocaleString();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
text: function () {
|
ready: function () {
|
||||||
var value = this.value;
|
if (typeof this.precision == "undefined") {
|
||||||
if (typeof value == 'undefined') {
|
this.precision = 0;
|
||||||
return '';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.metric) {
|
if (typeof this.unit == "undefined") {
|
||||||
value /= this.scale;
|
this.unit = "mm";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (1 * value.toFixed(this.precision)).toLocaleString();
|
if (typeof this.iunit == "undefined") {
|
||||||
|
this.iunit = "in";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.scale == "undefined") {
|
||||||
|
this.scale = 25.4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
if (typeof this.precision == 'undefined') {
|
|
||||||
this.precision = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.unit == 'undefined') {
|
|
||||||
this.unit = 'mm';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.iunit == 'undefined') {
|
|
||||||
this.iunit = 'in';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.scale == 'undefined') {
|
|
||||||
this.scale = 25.4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
9
src/js/utils.js
Normal file
9
src/js/utils.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
function clickFileInput(formClass) {
|
||||||
|
const form = document.querySelector(`.${formClass}`);
|
||||||
|
form.reset();
|
||||||
|
form.querySelector("input").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
clickFileInput
|
||||||
|
};
|
||||||
@@ -1,204 +1,210 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import WifiConnectionDialog from "$dialogs/WifiConnectionDialog.svelte";
|
import WifiConnectionDialog from "$dialogs/WifiConnectionDialog.svelte";
|
||||||
import ChangeHostnameDialog from "$dialogs/ChangeHostnameDialog.svelte";
|
import ChangeHostnameDialog from "$dialogs/ChangeHostnameDialog.svelte";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
import List, { Item, Graphic, Text, Meta } from "@smui/list";
|
import List, { Item, Graphic, Text, Meta } from "@smui/list";
|
||||||
import Card from "@smui/card";
|
import Card from "@smui/card";
|
||||||
import { networkInfo, type WifiNetwork } from "$lib/NetworkInfo";
|
import { networkInfo, type WifiNetwork } from "$lib/NetworkInfo";
|
||||||
|
|
||||||
let changeHostnameDialog = {
|
let changeHostnameDialog = {
|
||||||
open: false,
|
open: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let wifiConnectionDialog = {
|
let wifiConnectionDialog = {
|
||||||
open: false,
|
open: false,
|
||||||
network: {} as WifiNetwork,
|
network: {} as WifiNetwork,
|
||||||
};
|
};
|
||||||
|
|
||||||
function getWifiStrengthStyle(network: WifiNetwork) {
|
function getWifiStrengthStyle(network: WifiNetwork) {
|
||||||
const strength = Math.ceil((Number(network.Quality) / 100) * 4);
|
const strength = Math.ceil((Number(network.Quality) / 100) * 4);
|
||||||
|
|
||||||
switch (strength) {
|
switch (strength) {
|
||||||
case 0:
|
case 0:
|
||||||
return "clip-path: circle(0px at 12.5px 19px);";
|
return "clip-path: circle(0px at 12.5px 19px);";
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return "clip-path: circle(4px at 12.5px 19px);";
|
return "clip-path: circle(4px at 12.5px 19px);";
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
return "clip-path: circle(8px at 12.5px 19px);";
|
return "clip-path: circle(8px at 12.5px 19px);";
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
return "clip-path: circle(14px at 12.5px 19px);";
|
return "clip-path: circle(14px at 12.5px 19px);";
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
return "";
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function onChangeHostname() {
|
function onChangeHostname() {
|
||||||
changeHostnameDialog = {
|
changeHostnameDialog = {
|
||||||
open: true,
|
open: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function onNetworkSelected(network: WifiNetwork) {
|
function onNetworkSelected(network: WifiNetwork) {
|
||||||
wifiConnectionDialog = {
|
wifiConnectionDialog = {
|
||||||
open: true,
|
open: true,
|
||||||
network,
|
network,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<WifiConnectionDialog {...wifiConnectionDialog} />
|
<WifiConnectionDialog {...wifiConnectionDialog} />
|
||||||
<ChangeHostnameDialog {...changeHostnameDialog} />
|
<ChangeHostnameDialog {...changeHostnameDialog} />
|
||||||
|
|
||||||
<div class="admin-network-view">
|
<div class="admin-network-view">
|
||||||
<h1>Network Info</h1>
|
<h1>Network Info</h1>
|
||||||
|
|
||||||
<div class="pure-form pure-form-aligned">
|
<div class="pure-form pure-form-aligned">
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="hostname">Hostname</label>
|
<label for="hostname">Hostname</label>
|
||||||
<Card id="hostname" variant="outlined">
|
<Card id="hostname" variant="outlined">
|
||||||
<Text id="hostname">
|
<Text id="hostname">
|
||||||
{$networkInfo.hostname}
|
{$networkInfo.hostname}
|
||||||
</Text>
|
</Text>
|
||||||
</Card>
|
</Card>
|
||||||
<Button on:click={onChangeHostname} touch variant="raised">
|
<Button on:click={onChangeHostname} touch variant="raised">
|
||||||
<Label>Change</Label>
|
<Label>Change</Label>
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pure-form pure-form-aligned">
|
<div class="pure-form pure-form-aligned">
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="ip-addresses">IP Addresses</label>
|
<label for="ip-addresses">IP Addresses</label>
|
||||||
<Card id="ip-addresses" variant="outlined">
|
<Card id="ip-addresses" variant="outlined">
|
||||||
{#each $networkInfo.ipAddresses as ipAddress}
|
{#each $networkInfo.ipAddresses as ipAddress}
|
||||||
<div>
|
<div>
|
||||||
<Text id="hostname">
|
<Text id="hostname">
|
||||||
{ipAddress}
|
{ipAddress}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</Card>
|
</Card>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pure-form pure-form-aligned">
|
<div class="pure-form pure-form-aligned">
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="wifi">Wi-Fi</label>
|
<label for="wifi">Wi-Fi</label>
|
||||||
<div class="wifi-networks">
|
<div class="wifi-networks">
|
||||||
<Card id="wifi" variant="outlined">
|
<Card id="wifi" variant="outlined">
|
||||||
<List>
|
<List>
|
||||||
{#if $networkInfo.wifi.networks.length === 0}
|
{#if $networkInfo.wifi.networks.length === 0}
|
||||||
<Item class="wifi-network">
|
<Item class="wifi-network">
|
||||||
<Text>Scanning...</Text>
|
<Text>Scanning...</Text>
|
||||||
</Item>
|
</Item>
|
||||||
{:else}
|
{:else}
|
||||||
{#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
|
>
|
||||||
class="strength {$networkInfo.wifi.ssid === network.Name
|
<Graphic
|
||||||
? 'active'
|
class="strength {$networkInfo.wifi
|
||||||
: ''}"
|
.ssid === network.Name
|
||||||
>
|
? 'active'
|
||||||
<span class="fa fa-wifi background" />
|
: ''}"
|
||||||
<span
|
>
|
||||||
class="fa fa-wifi"
|
<span class="fa fa-wifi background" />
|
||||||
style={getWifiStrengthStyle(network)}
|
<span
|
||||||
/>
|
class="fa fa-wifi"
|
||||||
</Graphic>
|
style={getWifiStrengthStyle(
|
||||||
<Text style="margin-right: 20px;">{network.Name}</Text>
|
network
|
||||||
{#if network.Encryption !== "Open"}
|
)}
|
||||||
<Meta>
|
/>
|
||||||
<span class="fa fa-lock" />
|
</Graphic>
|
||||||
</Meta>
|
<Text style="margin-right: 20px;"
|
||||||
{/if}
|
>{network.Name}</Text
|
||||||
</Item>
|
>
|
||||||
{/each}
|
{#if network.Encryption !== "Open"}
|
||||||
{/if}
|
<Meta>
|
||||||
</List>
|
<span class="fa fa-lock" />
|
||||||
</Card>
|
</Meta>
|
||||||
<em style="display: block;">
|
{/if}
|
||||||
Click on a Wi-Fi network to connect or disconnect.
|
</Item>
|
||||||
</em>
|
{/each}
|
||||||
</div>
|
{/if}
|
||||||
|
</List>
|
||||||
|
</Card>
|
||||||
|
<em style="display: block;">
|
||||||
|
Click on a Wi-Fi network to connect or disconnect.
|
||||||
|
</em>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
$primary: #0078e7;
|
$primary: #0078e7;
|
||||||
$very-dark: #555;
|
$very-dark: #555;
|
||||||
$text: #777;
|
$text: #777;
|
||||||
$grey: #bbb;
|
$grey: #bbb;
|
||||||
$light: #ddd;
|
$light: #ddd;
|
||||||
|
|
||||||
:global {
|
:global {
|
||||||
.admin-network-view {
|
.admin-network-view {
|
||||||
.pure-form-aligned .pure-control-group label {
|
.pure-form-aligned .pure-control-group label {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
font-size: 15pt;
|
font-size: 15pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-card {
|
.mdc-card {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
min-height: 38px;
|
min-height: 38px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
padding: 5px 15px;
|
padding: 5px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wifi-networks {
|
.wifi-networks {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
.mdc-card {
|
.mdc-card {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wifi-network {
|
.wifi-network {
|
||||||
.lock {
|
.lock {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.strength {
|
.strength {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background-color: $light;
|
background-color: $light;
|
||||||
color: $very-dark;
|
color: $very-dark;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: $primary;
|
background-color: $primary;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
|
|
||||||
&.background {
|
&.background {
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,131 +1,131 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import configTemplate from "../../../resources/config-template.json";
|
import configTemplate from "../../../resources/config-template.json";
|
||||||
import { Config, DisplayUnits } from "$lib/ConfigStore";
|
import { Config, DisplayUnits } from "$lib/ConfigStore";
|
||||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
type Template = {
|
type Template = {
|
||||||
type?: string;
|
type?: string;
|
||||||
values?: (string | number)[];
|
values?: (string | number)[];
|
||||||
unit?: "string";
|
unit?: "string";
|
||||||
iunit?: "string";
|
iunit?: "string";
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
help?: string;
|
help?: string;
|
||||||
default?: string | number;
|
default?: string | number;
|
||||||
scale?: number;
|
scale?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export let key: string;
|
export let key: string;
|
||||||
let keyParts: string[];
|
let keyParts: string[];
|
||||||
let template: Template;
|
let template: Template;
|
||||||
let name: string;
|
let name: string;
|
||||||
let title: string;
|
let title: string;
|
||||||
let units: string;
|
let units: string;
|
||||||
let value;
|
let value;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
keyParts = (key || "").split(".");
|
keyParts = (key || "").split(".");
|
||||||
template = getTemplate();
|
template = getTemplate();
|
||||||
name = keyParts[keyParts.length - 1];
|
name = keyParts[keyParts.length - 1];
|
||||||
title = getTitle();
|
title = getTitle();
|
||||||
value = getValue();
|
value = getValue();
|
||||||
});
|
|
||||||
|
|
||||||
$: metric = $DisplayUnits === "METRIC";
|
|
||||||
$: if (template) {
|
|
||||||
units = metric || !template.iunit ? template.unit : template.iunit;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTemplate(): Template {
|
|
||||||
let template = configTemplate;
|
|
||||||
for (const part of keyParts) {
|
|
||||||
template = template[part];
|
|
||||||
}
|
|
||||||
|
|
||||||
return template as Template;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTitle(): string {
|
|
||||||
const help = template.help ? `${template.help}\n` : "";
|
|
||||||
return `${help}Default: ${template.default} ${template.unit || ""}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getValue(): string | number {
|
|
||||||
let value: any = $Config;
|
|
||||||
for (const part of keyParts) {
|
|
||||||
value = value[part];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (template.scale) {
|
|
||||||
if (metric) {
|
|
||||||
return Number.parseFloat(value.toFixed(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Number.parseFloat((value / template.scale).toFixed(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChange() {
|
|
||||||
Config.update((config) => {
|
|
||||||
let target = config;
|
|
||||||
for (const part of keyParts.slice(0, -1)) {
|
|
||||||
target = target[part];
|
|
||||||
}
|
|
||||||
|
|
||||||
target[keyParts[keyParts.length - 1]] = value;
|
|
||||||
|
|
||||||
return config;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ControllerMethods.dispatch("config-changed");
|
$: metric = $DisplayUnits === "METRIC";
|
||||||
}
|
$: if (template) {
|
||||||
|
units = metric || !template.iunit ? template.unit : template.iunit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTemplate(): Template {
|
||||||
|
let template = configTemplate;
|
||||||
|
for (const part of keyParts) {
|
||||||
|
template = template[part];
|
||||||
|
}
|
||||||
|
|
||||||
|
return template as Template;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTitle(): string {
|
||||||
|
const help = template.help ? `${template.help}\n` : "";
|
||||||
|
return `${help}Default: ${template.default} ${template.unit || ""}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValue(): string | number {
|
||||||
|
let value: any = $Config;
|
||||||
|
for (const part of keyParts) {
|
||||||
|
value = value[part];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (template.scale) {
|
||||||
|
if (metric) {
|
||||||
|
return Number.parseFloat(value.toFixed(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Number.parseFloat((value / template.scale).toFixed(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange() {
|
||||||
|
Config.update((config) => {
|
||||||
|
let target = config;
|
||||||
|
for (const part of keyParts.slice(0, -1)) {
|
||||||
|
target = target[part];
|
||||||
|
}
|
||||||
|
|
||||||
|
target[keyParts[keyParts.length - 1]] = value;
|
||||||
|
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
ControllerMethods.dispatch("config-changed");
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if template}
|
{#if template}
|
||||||
<div class="pure-control-group" {title}>
|
<div class="pure-control-group" {title}>
|
||||||
<label for={name}>{name}</label>
|
<label for={name}>{name}</label>
|
||||||
|
|
||||||
{#if template.values}
|
{#if template.values}
|
||||||
<select {name} bind:value on:change={onChange}>
|
<select {name} bind:value on:change={onChange}>
|
||||||
{#each template.values as opt}
|
{#each template.values as opt}
|
||||||
<option value={opt} disabled={opt === "-----"}>
|
<option value={opt} disabled={opt === "-----"}>
|
||||||
{opt}
|
{opt}
|
||||||
</option>
|
</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
{:else if template.type === "bool"}
|
{:else if template.type === "bool"}
|
||||||
<input {name} type="checkbox" bind:value on:input={onChange} />
|
<input {name} type="checkbox" bind:value on:input={onChange} />
|
||||||
{:else if template.type === "float"}
|
{:else if template.type === "float"}
|
||||||
<input
|
<input
|
||||||
{name}
|
{name}
|
||||||
type="number"
|
type="number"
|
||||||
min={template.min}
|
min={template.min}
|
||||||
max={template.max}
|
max={template.max}
|
||||||
step={template.step || "any"}
|
step={template.step || "any"}
|
||||||
bind:value
|
bind:value
|
||||||
on:input={onChange}
|
on:input={onChange}
|
||||||
/>
|
/>
|
||||||
{:else if template.type === "int"}
|
{:else if template.type === "int"}
|
||||||
<input
|
<input
|
||||||
{name}
|
{name}
|
||||||
type="number"
|
type="number"
|
||||||
min={template.min}
|
min={template.min}
|
||||||
max={template.max}
|
max={template.max}
|
||||||
bind:value
|
bind:value
|
||||||
on:input={onChange}
|
on:input={onChange}
|
||||||
/>
|
/>
|
||||||
{:else if template.type === "string"}
|
{:else if template.type === "string"}
|
||||||
<input {name} type="text" bind:value on:input={onChange} />
|
<input {name} type="text" bind:value on:input={onChange} />
|
||||||
{:else if template.type == "text"}
|
{:else if template.type == "text"}
|
||||||
<textarea {name} bind:value on:input={onChange} />
|
<textarea {name} bind:value on:input={onChange} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<label for="" class="units">{units || ""}</label>
|
<label for="" class="units">{units || ""}</label>
|
||||||
|
|
||||||
<slot name="extra" />
|
<slot name="extra" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import configTemplate from "../../../resources/config-template.json";
|
import configTemplate from "../../../resources/config-template.json";
|
||||||
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
||||||
import ConfigTemplatedInput from "./ConfigTemplatedInput.svelte";
|
import ConfigTemplatedInput from "./ConfigTemplatedInput.svelte";
|
||||||
import SetTimeDialog from "$dialogs/SetTimeDialog.svelte";
|
import SetTimeDialog from "$dialogs/SetTimeDialog.svelte";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
|
|
||||||
const gcodeURL = "https://linuxcnc.org/docs/html/gcode/g-code.html";
|
const gcodeURL = "https://linuxcnc.org/docs/html/gcode/g-code.html";
|
||||||
|
|
||||||
let showScreenRotationDialog = false;
|
let showScreenRotationDialog = false;
|
||||||
let showSetTimeDialog = false;
|
let showSetTimeDialog = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ScreenRotationDialog bind:open={showScreenRotationDialog} />
|
<ScreenRotationDialog bind:open={showScreenRotationDialog} />
|
||||||
@@ -17,81 +17,81 @@
|
|||||||
<h1>Settings</h1>
|
<h1>Settings</h1>
|
||||||
|
|
||||||
<div class="pure-form pure-form-aligned">
|
<div class="pure-form pure-form-aligned">
|
||||||
<h2>User Interface</h2>
|
<h2>User Interface</h2>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="screen-rotation" />
|
<label for="screen-rotation" />
|
||||||
<Button
|
<Button
|
||||||
name="screen-rotation"
|
name="screen-rotation"
|
||||||
touch
|
touch
|
||||||
variant="raised"
|
variant="raised"
|
||||||
on:click={() => (showScreenRotationDialog = true)}
|
on:click={() => (showScreenRotationDialog = true)}
|
||||||
>
|
>
|
||||||
<Label>Change Screen Rotation</Label>
|
<Label>Change Screen Rotation</Label>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="set-time" />
|
<label for="set-time" />
|
||||||
<Button
|
<Button
|
||||||
name="set-time"
|
name="set-time"
|
||||||
touch
|
touch
|
||||||
variant="raised"
|
variant="raised"
|
||||||
on:click={() => (showSetTimeDialog = true)}
|
on:click={() => (showSetTimeDialog = true)}
|
||||||
>
|
>
|
||||||
<Label>Change Time & Timezone</Label>
|
<Label>Change Time & Timezone</Label>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<h2>Probe Dimensions</h2>
|
<h2>Probe Dimensions</h2>
|
||||||
{#each Object.keys(configTemplate.probe) as key}
|
{#each Object.keys(configTemplate.probe) as key}
|
||||||
{#if key !== "probe-diameter"}
|
{#if key !== "probe-diameter"}
|
||||||
<ConfigTemplatedInput key={`probe.${key}`} />
|
<ConfigTemplatedInput key={`probe.${key}`} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<h2>GCode</h2>
|
<h2>GCode</h2>
|
||||||
{#each Object.keys(configTemplate.gcode) as key}
|
{#each Object.keys(configTemplate.gcode) as key}
|
||||||
<ConfigTemplatedInput key={`gcode.${key}`} />
|
<ConfigTemplatedInput key={`gcode.${key}`} />
|
||||||
{/each}
|
{/each}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<h2>Path Accuracy</h2>
|
<h2>Path Accuracy</h2>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<ConfigTemplatedInput key={`settings.max-deviation`} />
|
<ConfigTemplatedInput key={`settings.max-deviation`} />
|
||||||
</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>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<ConfigTemplatedInput key={`settings.junction-accel`} />
|
<ConfigTemplatedInput key={`settings.junction-accel`} />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<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>
|
||||||
|
|||||||
@@ -1,90 +1,90 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import TextField from "@smui/textfield";
|
import TextField from "@smui/textfield";
|
||||||
import Icon from "@smui/textfield/icon";
|
import Icon from "@smui/textfield/icon";
|
||||||
import HelperText from "@smui/textfield/helper-text";
|
import HelperText from "@smui/textfield/helper-text";
|
||||||
import MenuSurface, {
|
import MenuSurface, {
|
||||||
type MenuSurfaceComponentDev,
|
type MenuSurfaceComponentDev,
|
||||||
} from "@smui/menu-surface";
|
} from "@smui/menu-surface";
|
||||||
import List, { Item, Text } from "@smui/list";
|
import List, { Item, Text } from "@smui/list";
|
||||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
import { onDestroy } from "svelte";
|
import { onDestroy } from "svelte";
|
||||||
|
|
||||||
let menuSurface: MenuSurfaceComponentDev;
|
let menuSurface: MenuSurfaceComponentDev;
|
||||||
let menuTimeout;
|
let menuTimeout;
|
||||||
let optionSelected: boolean = false;
|
let optionSelected: boolean = false;
|
||||||
|
|
||||||
export let value: string;
|
export let value: string;
|
||||||
export let options: string[][];
|
export let options: string[][];
|
||||||
export let valid: boolean;
|
export let valid: boolean;
|
||||||
export let helperText: string;
|
export let helperText: string;
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
if (menuTimeout) {
|
if (menuTimeout) {
|
||||||
clearTimeout(menuTimeout);
|
clearTimeout(menuTimeout);
|
||||||
menuTimeout = undefined;
|
menuTimeout = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function showMenu(show: boolean) {
|
||||||
|
if (show && optionSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
optionSelected = false;
|
||||||
|
|
||||||
|
if (menuTimeout) {
|
||||||
|
clearTimeout(menuTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a timeout to "debounce" the display of the menu.
|
||||||
|
menuTimeout = setTimeout(() => menuSurface.setOpen(show), 100);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
function showMenu(show: boolean) {
|
|
||||||
if (show && optionSelected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
optionSelected = false;
|
|
||||||
|
|
||||||
if (menuTimeout) {
|
|
||||||
clearTimeout(menuTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a timeout to "debounce" the display of the menu.
|
|
||||||
menuTimeout = setTimeout(() => menuSurface.setOpen(show), 100);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="textfield-with-options">
|
<div class="textfield-with-options">
|
||||||
<TextField
|
<TextField
|
||||||
bind:value
|
bind:value
|
||||||
on:focusin={() => showMenu(true)}
|
on:focusin={() => showMenu(true)}
|
||||||
on:focusout={() => showMenu(false)}
|
on:focusout={() => showMenu(false)}
|
||||||
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
||||||
{...$$restProps}
|
{...$$restProps}
|
||||||
>
|
>
|
||||||
<div slot="trailingIcon">
|
<div slot="trailingIcon">
|
||||||
{#if valid}
|
{#if valid}
|
||||||
<Icon class="fa fa-check-circle-o" style="color: green;" />
|
<Icon class="fa fa-check-circle-o" style="color: green;" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<HelperText persistent slot="helper">{helperText}</HelperText>
|
<HelperText persistent slot="helper">{helperText}</HelperText>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
|
||||||
<MenuSurface bind:this={menuSurface} anchorCorner="BOTTOM_LEFT">
|
<MenuSurface bind:this={menuSurface} anchorCorner="BOTTOM_LEFT">
|
||||||
<div style="display: flex; flex-direction: row;">
|
<div style="display: flex; flex-direction: row;">
|
||||||
{#each options as group}
|
{#each options as group}
|
||||||
<List>
|
<List>
|
||||||
{#each group as option}
|
{#each group as option}
|
||||||
<Item
|
<Item
|
||||||
on:SMUI:action={() => {
|
on:SMUI:action={() => {
|
||||||
value = option;
|
value = option;
|
||||||
showMenu(false);
|
showMenu(false);
|
||||||
|
|
||||||
optionSelected = true;
|
optionSelected = true;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text>{option}</Text>
|
<Text>{option}</Text>
|
||||||
</Item>
|
</Item>
|
||||||
{/each}
|
{/each}
|
||||||
</List>
|
</List>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</MenuSurface>
|
</MenuSurface>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
:global {
|
:global {
|
||||||
.textfield-with-options {
|
.textfield-with-options {
|
||||||
.mdc-deprecated-list-item {
|
.mdc-deprecated-list-item {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,95 +1,104 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import Button, { Label } from "@smui/button";
|
Title,
|
||||||
import TextField from "@smui/textfield";
|
Content,
|
||||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
Actions,
|
||||||
import * as api from "$lib/api";
|
InitialFocus,
|
||||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
} from "@smui/dialog";
|
||||||
|
import Button, { Label } from "@smui/button";
|
||||||
|
import TextField from "@smui/textfield";
|
||||||
|
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||||
|
import * as api from "$lib/api";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
// https://man7.org/linux/man-pages/man7/hostname.7.html
|
// https://man7.org/linux/man-pages/man7/hostname.7.html
|
||||||
//
|
//
|
||||||
// Each element of the hostname must be from 1 to 63 characters long
|
// Each element of the hostname must be from 1 to 63 characters long
|
||||||
// and the entire hostname, including the dots, can be at most 253
|
// and the entire hostname, including the dots, can be at most 253
|
||||||
// characters long. Valid characters for hostnames are ASCII(7)
|
// characters long. Valid characters for hostnames are ASCII(7)
|
||||||
// letters from a to z, the digits from 0 to 9, and the hyphen (-).
|
// letters from a to z, the digits from 0 to 9, and the hyphen (-).
|
||||||
// A hostname may not start with a hyphen.
|
// A hostname may not start with a hyphen.
|
||||||
|
|
||||||
const pattern = /[a-zA-Z0-9][a-zA-Z0-9-]{0,62}/;
|
const pattern = /[a-zA-Z0-9][a-zA-Z0-9-]{0,62}/;
|
||||||
|
|
||||||
export let open = false;
|
export let open = false;
|
||||||
|
|
||||||
let rebooting = false;
|
let rebooting = false;
|
||||||
let redirectTimeout = 45;
|
let redirectTimeout = 45;
|
||||||
let hostname = "";
|
let hostname = "";
|
||||||
|
|
||||||
$: setTimeout(() => {
|
$: setTimeout(() => {
|
||||||
hostname = (hostname.match(pattern) || [""])[0].toLowerCase();
|
hostname = (hostname.match(pattern) || [""])[0].toLowerCase();
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
$: if (open) {
|
$: if (open) {
|
||||||
hostname = "";
|
hostname = "";
|
||||||
}
|
|
||||||
|
|
||||||
async function onConfirm() {
|
|
||||||
rebooting = true;
|
|
||||||
await api.PUT("hostname", { hostname });
|
|
||||||
await api.PUT("reboot");
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
if (0 < redirectTimeout) {
|
|
||||||
redirectTimeout -= 1;
|
|
||||||
} else {
|
|
||||||
clearInterval(interval);
|
|
||||||
location.hostname = getRedirectTarget();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRedirectTarget() {
|
|
||||||
if (location.hostname.endsWith(".local")) {
|
|
||||||
return `${hostname}.local`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location.hostname.endsWith(".lan")) {
|
async function onConfirm() {
|
||||||
return `${hostname}.lan`;
|
rebooting = true;
|
||||||
|
await api.PUT("hostname", { hostname });
|
||||||
|
await api.PUT("reboot");
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (0 < redirectTimeout) {
|
||||||
|
redirectTimeout -= 1;
|
||||||
|
} else {
|
||||||
|
clearInterval(interval);
|
||||||
|
location.hostname = getRedirectTarget();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hostname;
|
function getRedirectTarget() {
|
||||||
}
|
if (location.hostname.endsWith(".local")) {
|
||||||
|
return `${hostname}.local`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location.hostname.endsWith(".lan")) {
|
||||||
|
return `${hostname}.lan`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||||
Rebooting to apply the hostname change...
|
Rebooting to apply the hostname change...
|
||||||
</MessageDialog>
|
</MessageDialog>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="change-hostname-dialog-title"
|
aria-labelledby="change-hostname-dialog-title"
|
||||||
aria-describedby="change-hostname-dialog-content"
|
aria-describedby="change-hostname-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="change-hostname-dialog-title">Change Hostname</Title>
|
<Title id="change-hostname-dialog-title">Change Hostname</Title>
|
||||||
|
|
||||||
<Content id="change-hostname-dialog-content">
|
<Content id="change-hostname-dialog-content">
|
||||||
<TextField
|
<TextField
|
||||||
bind:value={hostname}
|
bind:value={hostname}
|
||||||
use={[
|
use={[
|
||||||
InitialFocus,
|
InitialFocus,
|
||||||
[virtualKeyboardChange, (newValue) => (hostname = newValue)],
|
[virtualKeyboardChange, (newValue) => (hostname = newValue)],
|
||||||
]}
|
]}
|
||||||
label="New Hostname"
|
label="New Hostname"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
/>
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<Actions>
|
<Actions>
|
||||||
<Button>
|
<Button>
|
||||||
<Label>Cancel</Label>
|
<Label>Cancel</Label>
|
||||||
</Button>
|
</Button>
|
||||||
<Button defaultAction on:click={onConfirm} disabled={hostname.length === 0}>
|
<Button
|
||||||
<Label>Confirm & Reboot</Label>
|
defaultAction
|
||||||
</Button>
|
on:click={onConfirm}
|
||||||
</Actions>
|
disabled={hostname.length === 0}
|
||||||
|
>
|
||||||
|
<Label>Confirm & Reboot</Label>
|
||||||
|
</Button>
|
||||||
|
</Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,232 +1,235 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
import HomeMachineDialog from "$dialogs/HomeMachineDialog.svelte";
|
import HomeMachineDialog from "$dialogs/HomeMachineDialog.svelte";
|
||||||
import ProbeDialog from "$dialogs/ProbeDialog.svelte";
|
import ProbeDialog from "$dialogs/ProbeDialog.svelte";
|
||||||
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
||||||
import UploadDialog from "$dialogs/UploadDialog.svelte";
|
import UploadDialog from "$dialogs/UploadDialog.svelte";
|
||||||
import SetTimeDialog from "./SetTimeDialog.svelte";
|
import SetTimeDialog from "./SetTimeDialog.svelte";
|
||||||
import ManualHomeAxisDialog from "./ManualHomeAxisDialog.svelte";
|
import ManualHomeAxisDialog from "./ManualHomeAxisDialog.svelte";
|
||||||
import SetAxisPositionDialog from "./SetAxisPositionDialog.svelte";
|
import SetAxisPositionDialog from "./SetAxisPositionDialog.svelte";
|
||||||
import MoveToZeroDialog from "./MoveToZeroDialog.svelte";
|
import MoveToZeroDialog from "./MoveToZeroDialog.svelte";
|
||||||
import ShutdownDialog from "./ShutdownDialog.svelte";
|
import ShutdownDialog from "./ShutdownDialog.svelte";
|
||||||
import MessageDialog from "./MessageDialog.svelte";
|
import MessageDialog from "./MessageDialog.svelte";
|
||||||
|
|
||||||
const HomeMachineDialogProps = writable<HomeMachineDialogPropsType>();
|
const HomeMachineDialogProps = writable<HomeMachineDialogPropsType>();
|
||||||
type HomeMachineDialogPropsType = {
|
type HomeMachineDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
home: () => void;
|
home: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProbeDialogProps = writable<ProbeDialogPropsType>();
|
const ProbeDialogProps = writable<ProbeDialogPropsType>();
|
||||||
type ProbeDialogPropsType = {
|
type ProbeDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
probeType: "xyz" | "z";
|
probeType: "xyz" | "z";
|
||||||
};
|
};
|
||||||
|
|
||||||
const ScreenRotationDialogProps = writable<ScreenRotationDialogPropsType>();
|
const ScreenRotationDialogProps = writable<ScreenRotationDialogPropsType>();
|
||||||
type ScreenRotationDialogPropsType = {
|
type ScreenRotationDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const UploadDialogProps = writable<UploadDialogPropsType>();
|
const UploadDialogProps = writable<UploadDialogPropsType>();
|
||||||
type UploadDialogPropsType = {
|
type UploadDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
file: File;
|
file: File;
|
||||||
onComplete: () => void;
|
onComplete: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SetTimeDialogProps = writable<SetTimeDialogPropsType>();
|
const SetTimeDialogProps = writable<SetTimeDialogPropsType>();
|
||||||
type SetTimeDialogPropsType = {
|
type SetTimeDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ManualHomeAxisDialogProps = writable<ManualHomeAxisDialogPropsType>();
|
const ManualHomeAxisDialogProps = writable<ManualHomeAxisDialogPropsType>();
|
||||||
type ManualHomeAxisDialogPropsType = {
|
type ManualHomeAxisDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
axis: string;
|
axis: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SetAxisPositionDialogProps = writable<SetAxisPositionDialogPropsType>();
|
const SetAxisPositionDialogProps =
|
||||||
type SetAxisPositionDialogPropsType = {
|
writable<SetAxisPositionDialogPropsType>();
|
||||||
open: boolean;
|
type SetAxisPositionDialogPropsType = {
|
||||||
axis: string;
|
open: boolean;
|
||||||
};
|
axis: string;
|
||||||
|
};
|
||||||
|
|
||||||
const MoveToZeroDialogProps = writable<MoveToZeroDialogPropsType>();
|
const MoveToZeroDialogProps = writable<MoveToZeroDialogPropsType>();
|
||||||
type MoveToZeroDialogPropsType = {
|
type MoveToZeroDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
axes: "xy" | "z";
|
axes: "xy" | "z";
|
||||||
};
|
};
|
||||||
|
|
||||||
const ShutdownDialogProps = writable<ShutdownDialogPropsType>();
|
const ShutdownDialogProps = writable<ShutdownDialogPropsType>();
|
||||||
type ShutdownDialogPropsType = {
|
type ShutdownDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MessageDialogProps = writable<MessageDialogPropsType>();
|
const MessageDialogProps = writable<MessageDialogPropsType>();
|
||||||
type MessageDialogPropsType = {
|
type MessageDialogPropsType = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
message: string;
|
message: string;
|
||||||
noaction: boolean;
|
noaction: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "HomeMachine",
|
dialog: "HomeMachine",
|
||||||
props: Omit<HomeMachineDialogPropsType, "open">
|
props: Omit<HomeMachineDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "Probe",
|
dialog: "Probe",
|
||||||
props: Omit<ProbeDialogPropsType, "open">
|
props: Omit<ProbeDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "ScreenRotation",
|
dialog: "ScreenRotation",
|
||||||
props: Omit<ScreenRotationDialogPropsType, "open">
|
props: Omit<ScreenRotationDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "Upload",
|
dialog: "Upload",
|
||||||
props: Omit<UploadDialogPropsType, "open">
|
props: Omit<UploadDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "SetTime",
|
dialog: "SetTime",
|
||||||
props: Omit<SetTimeDialogPropsType, "open">
|
props: Omit<SetTimeDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "ManualHomeAxis",
|
dialog: "ManualHomeAxis",
|
||||||
props: Omit<ManualHomeAxisDialogPropsType, "open">
|
props: Omit<ManualHomeAxisDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "SetAxisPosition",
|
dialog: "SetAxisPosition",
|
||||||
props: Omit<SetAxisPositionDialogPropsType, "open">
|
props: Omit<SetAxisPositionDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "MoveToZero",
|
dialog: "MoveToZero",
|
||||||
props: Omit<MoveToZeroDialogPropsType, "open">
|
props: Omit<MoveToZeroDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "Shutdown",
|
dialog: "Shutdown",
|
||||||
props: Omit<ShutdownDialogPropsType, "open">
|
props: Omit<ShutdownDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(
|
export function showDialog(
|
||||||
dialog: "Message",
|
dialog: "Message",
|
||||||
props: Omit<MessageDialogPropsType, "open">
|
props: Omit<MessageDialogPropsType, "open">
|
||||||
);
|
);
|
||||||
|
|
||||||
export function showDialog(dialog: string, props: any) {
|
export function showDialog(dialog: string, props: any) {
|
||||||
switch (dialog) {
|
switch (dialog) {
|
||||||
case "HomeMachine":
|
case "HomeMachine":
|
||||||
HomeMachineDialogProps.set({ ...props, open: true });
|
HomeMachineDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Probe":
|
case "Probe":
|
||||||
ProbeDialogProps.set({ ...props, open: true });
|
ProbeDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "ScreenRotation":
|
case "ScreenRotation":
|
||||||
ScreenRotationDialogProps.set({ ...props, open: true });
|
ScreenRotationDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Upload":
|
case "Upload":
|
||||||
UploadDialogProps.set({ ...props, open: true });
|
UploadDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "SetTime":
|
case "SetTime":
|
||||||
SetTimeDialogProps.set({ ...props, open: true });
|
SetTimeDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "ManualHomeAxis":
|
case "ManualHomeAxis":
|
||||||
ManualHomeAxisDialogProps.set({ ...props, open: true });
|
ManualHomeAxisDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "SetAxisPosition":
|
case "SetAxisPosition":
|
||||||
SetAxisPositionDialogProps.set({ ...props, open: true });
|
SetAxisPositionDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "MoveToZero":
|
case "MoveToZero":
|
||||||
MoveToZeroDialogProps.set({ ...props, open: true });
|
MoveToZeroDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Shutdown":
|
case "Shutdown":
|
||||||
ShutdownDialogProps.set({ ...props, open: true });
|
ShutdownDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Message":
|
case "Message":
|
||||||
MessageDialogProps.set({ ...props, open: true });
|
MessageDialogProps.set({ ...props, open: true });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown dialog '${dialog}'`);
|
throw new Error(`Unknown dialog '${dialog}'`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, onDestroy } from "svelte";
|
import { onMount, onDestroy } from "svelte";
|
||||||
|
|
||||||
let bodyObserver: MutationObserver;
|
let bodyObserver: MutationObserver;
|
||||||
let keyboardObserver: MutationObserver;
|
let keyboardObserver: MutationObserver;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
bodyObserver = new MutationObserver(() => {
|
bodyObserver = new MutationObserver(() => {
|
||||||
const virtualKeyboard = document.getElementById(
|
const virtualKeyboard = document.getElementById(
|
||||||
"virtualKeyboardChromeExtension"
|
"virtualKeyboardChromeExtension"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (virtualKeyboard) {
|
if (virtualKeyboard) {
|
||||||
bodyObserver.disconnect();
|
bodyObserver.disconnect();
|
||||||
bodyObserver = undefined;
|
bodyObserver = undefined;
|
||||||
|
|
||||||
const virtualKeyboardOverlay = document.getElementById(
|
const virtualKeyboardOverlay = document.getElementById(
|
||||||
"virtualKeyboardChromeExtensionOverlayScrollExtend"
|
"virtualKeyboardChromeExtensionOverlayScrollExtend"
|
||||||
);
|
);
|
||||||
|
|
||||||
keyboardObserver = new MutationObserver(() => {
|
keyboardObserver = new MutationObserver(() => {
|
||||||
const open = virtualKeyboard.getAttribute("_state") === "open";
|
const open =
|
||||||
const keyboardHeight = Number.parseFloat(
|
virtualKeyboard.getAttribute("_state") === "open";
|
||||||
virtualKeyboardOverlay.style.height
|
const keyboardHeight = Number.parseFloat(
|
||||||
);
|
virtualKeyboardOverlay.style.height
|
||||||
|
);
|
||||||
|
|
||||||
const dialogContainers = document.querySelectorAll<HTMLDivElement>(
|
const dialogContainers =
|
||||||
".mdc-dialog .mdc-dialog__container"
|
document.querySelectorAll<HTMLDivElement>(
|
||||||
);
|
".mdc-dialog .mdc-dialog__container"
|
||||||
|
);
|
||||||
|
|
||||||
for (let dialogContainer of dialogContainers) {
|
for (let dialogContainer of dialogContainers) {
|
||||||
dialogContainer.style["marginBottom"] = open
|
dialogContainer.style["marginBottom"] = open
|
||||||
? `${keyboardHeight}px`
|
? `${keyboardHeight}px`
|
||||||
: "";
|
: "";
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboardObserver.observe(virtualKeyboard, { attributes: true });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keyboardObserver.observe(virtualKeyboard, { attributes: true });
|
bodyObserver.observe(document.querySelector("body"), {
|
||||||
}
|
subtree: false,
|
||||||
|
childList: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
bodyObserver.observe(document.querySelector("body"), {
|
onDestroy(() => {
|
||||||
subtree: false,
|
if (bodyObserver) {
|
||||||
childList: true,
|
bodyObserver.disconnect();
|
||||||
|
bodyObserver = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyboardObserver) {
|
||||||
|
keyboardObserver.disconnect();
|
||||||
|
keyboardObserver = undefined;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
if (bodyObserver) {
|
|
||||||
bodyObserver.disconnect();
|
|
||||||
bodyObserver = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyboardObserver) {
|
|
||||||
keyboardObserver.disconnect();
|
|
||||||
keyboardObserver = undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<HomeMachineDialog {...$HomeMachineDialogProps} />
|
<HomeMachineDialog {...$HomeMachineDialogProps} />
|
||||||
|
|||||||
@@ -1,28 +1,33 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import Button, { Label } from "@smui/button";
|
Title,
|
||||||
|
Content,
|
||||||
|
Actions,
|
||||||
|
InitialFocus,
|
||||||
|
} from "@smui/dialog";
|
||||||
|
import Button, { Label } from "@smui/button";
|
||||||
|
|
||||||
export let open;
|
export let open;
|
||||||
export let home: () => any;
|
export let home: () => any;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="home-machine-dialog-title"
|
aria-labelledby="home-machine-dialog-title"
|
||||||
aria-describedby="home-machine-dialog-content"
|
aria-describedby="home-machine-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="home-machine-dialog-title">Home Machine</Title>
|
<Title id="home-machine-dialog-title">Home Machine</Title>
|
||||||
|
|
||||||
<Content id="home-machine-dialog-content">Home the machine?</Content>
|
<Content id="home-machine-dialog-content">Home the machine?</Content>
|
||||||
|
|
||||||
<Actions>
|
<Actions>
|
||||||
<Button>
|
<Button>
|
||||||
<Label>Cancel</Label>
|
<Label>Cancel</Label>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button defaultAction use={[InitialFocus]} on:click={home}>
|
<Button defaultAction use={[InitialFocus]} on:click={home}>
|
||||||
<Label>OK</Label>
|
<Label>OK</Label>
|
||||||
</Button>
|
</Button>
|
||||||
</Actions>
|
</Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,51 +1,57 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import TextField from "@smui/textfield";
|
Title,
|
||||||
import Button, { Label } from "@smui/button";
|
Content,
|
||||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
Actions,
|
||||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
InitialFocus,
|
||||||
|
} from "@smui/dialog";
|
||||||
|
import TextField from "@smui/textfield";
|
||||||
|
import Button, { Label } from "@smui/button";
|
||||||
|
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open: boolean;
|
export let open: boolean;
|
||||||
export let axis = "";
|
export let axis = "";
|
||||||
|
|
||||||
let value = 0;
|
let value = 0;
|
||||||
|
|
||||||
function onConfirm() {
|
function onConfirm() {
|
||||||
ControllerMethods.set_home(axis, value);
|
ControllerMethods.set_home(axis, value);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
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">
|
|
||||||
<p>Set axis absolute position</p>
|
|
||||||
|
|
||||||
<TextField
|
<Content id="manual-home-axis-dialog-content">
|
||||||
label="Absolute"
|
<p>Set axis absolute position</p>
|
||||||
type="number"
|
|
||||||
bind:value
|
|
||||||
use={[
|
|
||||||
InitialFocus,
|
|
||||||
[virtualKeyboardChange, (newValue) => (value = newValue)],
|
|
||||||
]}
|
|
||||||
variant="filled"
|
|
||||||
style="width: 100%;"
|
|
||||||
/>
|
|
||||||
</Content>
|
|
||||||
|
|
||||||
<Actions>
|
<TextField
|
||||||
<Button>
|
label="Absolute"
|
||||||
<Label>Cancel</Label>
|
type="number"
|
||||||
</Button>
|
bind:value
|
||||||
<Button defaultAction on:click={onConfirm}>
|
use={[
|
||||||
<Label>Set</Label>
|
InitialFocus,
|
||||||
</Button>
|
[virtualKeyboardChange, (newValue) => (value = newValue)],
|
||||||
</Actions>
|
]}
|
||||||
|
variant="filled"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Actions>
|
||||||
|
<Button>
|
||||||
|
<Label>Cancel</Label>
|
||||||
|
</Button>
|
||||||
|
<Button defaultAction on:click={onConfirm}>
|
||||||
|
<Label>Set</Label>
|
||||||
|
</Button>
|
||||||
|
</Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,31 +1,36 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import Button, { Label } from "@smui/button";
|
Title,
|
||||||
|
Content,
|
||||||
|
Actions,
|
||||||
|
InitialFocus,
|
||||||
|
} from "@smui/dialog";
|
||||||
|
import Button, { Label } from "@smui/button";
|
||||||
|
|
||||||
export let open: boolean;
|
export let open: boolean;
|
||||||
export let title = "";
|
export let title = "";
|
||||||
export let message = "";
|
export let message = "";
|
||||||
export let noaction = false;
|
export let noaction = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
escapeKeyAction=""
|
escapeKeyAction=""
|
||||||
aria-labelledby="message-dialog-title"
|
aria-labelledby="message-dialog-title"
|
||||||
aria-describedby="message-dialog-content"
|
aria-describedby="message-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="message-dialog-title">{title}</Title>
|
<Title id="message-dialog-title">{title}</Title>
|
||||||
|
|
||||||
<Content id="message-dialog-content">
|
<Content id="message-dialog-content">
|
||||||
<slot>{message}</slot>
|
<slot>{message}</slot>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
{#if !noaction}
|
{#if !noaction}
|
||||||
<Actions>
|
<Actions>
|
||||||
<Button defaultAction use={[InitialFocus]}>
|
<Button defaultAction use={[InitialFocus]}>
|
||||||
<Label>OK</Label>
|
<Label>OK</Label>
|
||||||
</Button>
|
</Button>
|
||||||
</Actions>
|
</Actions>
|
||||||
{/if}
|
{/if}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,444 +1,455 @@
|
|||||||
<script type="ts">
|
<script type="ts">
|
||||||
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
import { waitForChange } from "$lib/StoreHelpers";
|
import { waitForChange } from "$lib/StoreHelpers";
|
||||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||||
import { Config } from "$lib/ConfigStore";
|
import { Config } from "$lib/ConfigStore";
|
||||||
import { writable, type Writable } from "svelte/store";
|
import { writable, type Writable } from "svelte/store";
|
||||||
import {
|
import {
|
||||||
probingActive,
|
probingActive,
|
||||||
probeContacted,
|
probeContacted,
|
||||||
probingComplete,
|
probingComplete,
|
||||||
probingFailed,
|
probingFailed,
|
||||||
probingStarted,
|
probingStarted,
|
||||||
} from "$lib/ControllerState";
|
} from "$lib/ControllerState";
|
||||||
import { numberWithUnit } from "$lib/RegexHelpers";
|
import { numberWithUnit } from "$lib/RegexHelpers";
|
||||||
import TextFieldWithOptions from "$components/TextFieldWithOptions.svelte";
|
import TextFieldWithOptions from "$components/TextFieldWithOptions.svelte";
|
||||||
|
|
||||||
const ValidSteps = [
|
const ValidSteps = [
|
||||||
"None",
|
"None",
|
||||||
"CheckProbe",
|
|
||||||
"BitDimensions",
|
|
||||||
"PlaceProbeBlock",
|
|
||||||
"Probe",
|
|
||||||
"Done",
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
type Step = typeof ValidSteps[number];
|
|
||||||
|
|
||||||
function isStep(str): str is Step {
|
|
||||||
return ValidSteps.includes(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stepLabels: Record<Step, string> = {
|
|
||||||
None: "",
|
|
||||||
CheckProbe: "Check probe",
|
|
||||||
BitDimensions: "Bit dimensions",
|
|
||||||
PlaceProbeBlock: "Place probe block",
|
|
||||||
Probe: "Probe",
|
|
||||||
Done: "Done",
|
|
||||||
};
|
|
||||||
|
|
||||||
const cancelled = writable(false);
|
|
||||||
const userAcknowledged = writable(false);
|
|
||||||
|
|
||||||
const imperialBits: `${number}/${number} in`[] = [
|
|
||||||
"1/2 in",
|
|
||||||
"3/8 in",
|
|
||||||
"1/4 in",
|
|
||||||
"1/8 in",
|
|
||||||
"1/16 in",
|
|
||||||
"1/32 in",
|
|
||||||
];
|
|
||||||
const metricBits: `${number} mm`[] = [
|
|
||||||
"12 mm",
|
|
||||||
"10 mm",
|
|
||||||
"8 mm",
|
|
||||||
"6 mm",
|
|
||||||
"4 mm",
|
|
||||||
"3 mm",
|
|
||||||
];
|
|
||||||
|
|
||||||
export let open;
|
|
||||||
export let probeType: "xyz" | "z";
|
|
||||||
let currentStep: Step = "None";
|
|
||||||
let cutterDiameterString: string = "";
|
|
||||||
let cutterDiameterMetric: number;
|
|
||||||
let showCancelButton = true;
|
|
||||||
let steps: Step[] = [];
|
|
||||||
let nextButton = {
|
|
||||||
label: "Next",
|
|
||||||
disabled: false,
|
|
||||||
allowClose: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
$: metric = $Config.settings?.units === "METRIC";
|
|
||||||
$: cutterDiameterMetric = numberWithUnit
|
|
||||||
.parse(cutterDiameterString)
|
|
||||||
?.toMetric();
|
|
||||||
|
|
||||||
$: if (open) {
|
|
||||||
cutterDiameterString = localStorage.getItem("cutterDiameter") ?? "";
|
|
||||||
|
|
||||||
// Svelte appears not to like it when you invoke
|
|
||||||
// an async function from a reactive statement, so we
|
|
||||||
// use requestAnimationFrame to call 'begin' at a later moment.
|
|
||||||
requestAnimationFrame(begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
$: if (cutterDiameterString) {
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function begin() {
|
|
||||||
try {
|
|
||||||
$probingActive = true;
|
|
||||||
assertValidProbeType();
|
|
||||||
|
|
||||||
steps = [
|
|
||||||
"CheckProbe",
|
"CheckProbe",
|
||||||
probeType === "xyz" ? "BitDimensions" : undefined,
|
"BitDimensions",
|
||||||
"PlaceProbeBlock",
|
"PlaceProbeBlock",
|
||||||
"Probe",
|
"Probe",
|
||||||
"Done",
|
"Done",
|
||||||
].filter<Step>(isStep);
|
] as const;
|
||||||
|
|
||||||
await stepCompleted("CheckProbe", probeContacted);
|
type Step = typeof ValidSteps[number];
|
||||||
|
|
||||||
if (probeType === "xyz") {
|
function isStep(str): str is Step {
|
||||||
await stepCompleted("BitDimensions", userAcknowledged);
|
return ValidSteps.includes(str);
|
||||||
localStorage.setItem(
|
|
||||||
"cutterDiameter",
|
|
||||||
numberWithUnit.normalize(cutterDiameterString)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await stepCompleted("PlaceProbeBlock", userAcknowledged);
|
|
||||||
await stepCompleted("Probe", probingComplete, probingFailed);
|
|
||||||
await stepCompleted("Done", userAcknowledged);
|
|
||||||
|
|
||||||
if (probeType === "xyz") {
|
|
||||||
ControllerMethods.gotoZero("xy");
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (err.message !== "cancelled") {
|
|
||||||
console.error("Error during probing:", err);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
$probingActive = false;
|
|
||||||
currentStep = "None";
|
|
||||||
|
|
||||||
if ($probingStarted) {
|
|
||||||
ControllerMethods.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
clearFlags();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertValidProbeType() {
|
|
||||||
switch (probeType) {
|
|
||||||
case "xyz":
|
|
||||||
case "z":
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Invalid probe type: ${probeType}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stepCompleted(
|
|
||||||
nextStep: Step,
|
|
||||||
...writables: Array<Writable<any>>
|
|
||||||
) {
|
|
||||||
currentStep = nextStep;
|
|
||||||
|
|
||||||
clearFlags();
|
|
||||||
updateButtons();
|
|
||||||
|
|
||||||
if (currentStep === "Probe") {
|
|
||||||
executeProbe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.race([
|
const stepLabels: Record<Step, string> = {
|
||||||
...writables.map((writable) => waitForChange(writable)),
|
None: "",
|
||||||
waitForChange(cancelled),
|
CheckProbe: "Check probe",
|
||||||
]);
|
BitDimensions: "Bit dimensions",
|
||||||
|
PlaceProbeBlock: "Place probe block",
|
||||||
if ($cancelled) {
|
Probe: "Probe",
|
||||||
throw new Error("cancelled");
|
Done: "Done",
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearFlags(foo: string = "") {
|
|
||||||
$cancelled = false;
|
|
||||||
$probeContacted = false;
|
|
||||||
$probingStarted = false;
|
|
||||||
$probingFailed = false;
|
|
||||||
$probingComplete = false;
|
|
||||||
$userAcknowledged = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateButtons() {
|
|
||||||
showCancelButton = true;
|
|
||||||
|
|
||||||
nextButton = {
|
|
||||||
label: "Next",
|
|
||||||
disabled: false,
|
|
||||||
allowClose: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (currentStep) {
|
const cancelled = writable(false);
|
||||||
case "CheckProbe":
|
const userAcknowledged = writable(false);
|
||||||
case "Probe":
|
|
||||||
nextButton.disabled = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "BitDimensions":
|
const imperialBits: `${number}/${number} in`[] = [
|
||||||
nextButton.disabled = !isFinite(cutterDiameterMetric);
|
"1/2 in",
|
||||||
break;
|
"3/8 in",
|
||||||
|
"1/4 in",
|
||||||
|
"1/8 in",
|
||||||
|
"1/16 in",
|
||||||
|
"1/32 in",
|
||||||
|
];
|
||||||
|
|
||||||
|
const metricBits: `${number} mm`[] = [
|
||||||
|
"12 mm",
|
||||||
|
"10 mm",
|
||||||
|
"8 mm",
|
||||||
|
"6 mm",
|
||||||
|
"4 mm",
|
||||||
|
"3 mm",
|
||||||
|
];
|
||||||
|
|
||||||
|
export let open;
|
||||||
|
export let probeType: "xyz" | "z";
|
||||||
|
let currentStep: Step = "None";
|
||||||
|
let cutterDiameterString: string = "";
|
||||||
|
let cutterDiameterMetric: number;
|
||||||
|
let showCancelButton = true;
|
||||||
|
let steps: Step[] = [];
|
||||||
|
let nextButton = {
|
||||||
|
label: "Next",
|
||||||
|
disabled: false,
|
||||||
|
allowClose: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
$: metric = $Config.settings?.units === "METRIC";
|
||||||
|
$: cutterDiameterMetric = numberWithUnit
|
||||||
|
.parse(cutterDiameterString)
|
||||||
|
?.toMetric();
|
||||||
|
|
||||||
|
$: if (open) {
|
||||||
|
cutterDiameterString = localStorage.getItem("cutterDiameter") ?? "";
|
||||||
|
|
||||||
|
// Svelte appears not to like it when you invoke
|
||||||
|
// an async function from a reactive statement, so we
|
||||||
|
// use requestAnimationFrame to call 'begin' at a later moment.
|
||||||
|
requestAnimationFrame(begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (cutterDiameterString) {
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function begin() {
|
||||||
|
try {
|
||||||
|
$probingActive = true;
|
||||||
|
assertValidProbeType();
|
||||||
|
|
||||||
|
steps = [
|
||||||
|
"CheckProbe",
|
||||||
|
probeType === "xyz" ? "BitDimensions" : undefined,
|
||||||
|
"PlaceProbeBlock",
|
||||||
|
"Probe",
|
||||||
|
"Done",
|
||||||
|
].filter<Step>(isStep);
|
||||||
|
|
||||||
|
await stepCompleted("CheckProbe", probeContacted);
|
||||||
|
|
||||||
|
if (probeType === "xyz") {
|
||||||
|
await stepCompleted("BitDimensions", userAcknowledged);
|
||||||
|
localStorage.setItem(
|
||||||
|
"cutterDiameter",
|
||||||
|
numberWithUnit.normalize(cutterDiameterString)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await stepCompleted("PlaceProbeBlock", userAcknowledged);
|
||||||
|
await stepCompleted("Probe", probingComplete, probingFailed);
|
||||||
|
await stepCompleted("Done", userAcknowledged);
|
||||||
|
|
||||||
|
if (probeType === "xyz") {
|
||||||
|
ControllerMethods.gotoZero("xy");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err.message !== "cancelled") {
|
||||||
|
console.error("Error during probing:", err);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$probingActive = false;
|
||||||
|
currentStep = "None";
|
||||||
|
|
||||||
|
if ($probingStarted) {
|
||||||
|
ControllerMethods.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearFlags();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertValidProbeType() {
|
||||||
|
switch (probeType) {
|
||||||
|
case "xyz":
|
||||||
|
case "z":
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Invalid probe type: ${probeType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stepCompleted(
|
||||||
|
nextStep: Step,
|
||||||
|
...writables: Array<Writable<any>>
|
||||||
|
) {
|
||||||
|
currentStep = nextStep;
|
||||||
|
|
||||||
|
clearFlags();
|
||||||
|
updateButtons();
|
||||||
|
|
||||||
|
if (currentStep === "Probe") {
|
||||||
|
executeProbe();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.race([
|
||||||
|
...writables.map((writable) => waitForChange(writable)),
|
||||||
|
waitForChange(cancelled),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($cancelled) {
|
||||||
|
throw new Error("cancelled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearFlags(foo: string = "") {
|
||||||
|
$cancelled = false;
|
||||||
|
$probeContacted = false;
|
||||||
|
$probingStarted = false;
|
||||||
|
$probingFailed = false;
|
||||||
|
$probingComplete = false;
|
||||||
|
$userAcknowledged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateButtons() {
|
||||||
|
showCancelButton = true;
|
||||||
|
|
||||||
case "Done":
|
|
||||||
showCancelButton = false;
|
|
||||||
nextButton = {
|
nextButton = {
|
||||||
disabled: false,
|
label: "Next",
|
||||||
label: "Done",
|
disabled: false,
|
||||||
allowClose: true,
|
allowClose: false,
|
||||||
};
|
};
|
||||||
break;
|
|
||||||
|
switch (currentStep) {
|
||||||
|
case "CheckProbe":
|
||||||
|
case "Probe":
|
||||||
|
nextButton.disabled = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "BitDimensions":
|
||||||
|
nextButton.disabled = !isFinite(cutterDiameterMetric);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Done":
|
||||||
|
showCancelButton = false;
|
||||||
|
nextButton = {
|
||||||
|
disabled: false,
|
||||||
|
label: "Done",
|
||||||
|
allowClose: true,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function executeProbe() {
|
function executeProbe() {
|
||||||
const probeBlockWidth = $Config.probe["probe-xdim"];
|
const probeBlockWidth = $Config.probe["probe-xdim"];
|
||||||
const probeBlockLength = $Config.probe["probe-ydim"];
|
const probeBlockLength = $Config.probe["probe-ydim"];
|
||||||
const probeBlockHeight = $Config.probe["probe-zdim"];
|
const probeBlockHeight = $Config.probe["probe-zdim"];
|
||||||
const slowSeek = $Config.probe["probe-slow-seek"];
|
const slowSeek = $Config.probe["probe-slow-seek"];
|
||||||
const fastSeek = $Config.probe["probe-fast-seek"];
|
const fastSeek = $Config.probe["probe-fast-seek"];
|
||||||
|
|
||||||
const cutterLength = 12.7;
|
const cutterLength = 12.7;
|
||||||
const zLift = 1;
|
const zLift = 1;
|
||||||
const xOffset = probeBlockWidth + cutterDiameterMetric / 2.0;
|
const xOffset = probeBlockWidth + cutterDiameterMetric / 2.0;
|
||||||
const yOffset = probeBlockLength + cutterDiameterMetric / 2.0;
|
const yOffset = probeBlockLength + cutterDiameterMetric / 2.0;
|
||||||
const zOffset = probeBlockHeight;
|
const zOffset = probeBlockHeight;
|
||||||
|
|
||||||
if (probeType === "z") {
|
if (probeType === "z") {
|
||||||
ControllerMethods.send(`
|
ControllerMethods.send(`
|
||||||
G21
|
G21
|
||||||
G92 Z0
|
G92 Z0
|
||||||
|
|
||||||
G38.2 Z -25.4 F${fastSeek}
|
G38.2 Z -25.4 F${fastSeek}
|
||||||
G91 G1 Z 1
|
G91 G1 Z 1
|
||||||
G38.2 Z -2 F${slowSeek}
|
G38.2 Z -2 F${slowSeek}
|
||||||
G92 Z ${zOffset}
|
G92 Z ${zOffset}
|
||||||
|
|
||||||
G91 G0 Z 3
|
G91 G0 Z 3
|
||||||
|
|
||||||
M2
|
M2
|
||||||
`);
|
`);
|
||||||
} else {
|
} else {
|
||||||
// After probing Z, we want to drop the bit down:
|
// After probing Z, we want to drop the bit down:
|
||||||
// Ideally, 12.7mm/0.5in
|
// Ideally, 12.7mm/0.5in
|
||||||
// And we don't want to be more than 90% down on the probe block
|
// And we don't want to be more than 90% down on the probe block
|
||||||
// Also, add zlift to compensate for the fact that we lift after probing Z
|
// Also, add zlift to compensate for the fact that we lift after probing Z
|
||||||
const plunge = Math.min(cutterLength, zOffset * 0.9) + zLift;
|
const plunge = Math.min(cutterLength, zOffset * 0.9) + zLift;
|
||||||
|
|
||||||
ControllerMethods.send(`
|
ControllerMethods.send(`
|
||||||
G21
|
G21
|
||||||
G92 X0 Y0 Z0
|
G92 X0 Y0 Z0
|
||||||
|
|
||||||
G38.2 Z -25 F${fastSeek}
|
G38.2 Z -25 F${fastSeek}
|
||||||
G91 G1 Z 1
|
G91 G1 Z 1
|
||||||
G38.2 Z -2 F${slowSeek}
|
G38.2 Z -2 F${slowSeek}
|
||||||
G92 Z ${zOffset}
|
G92 Z ${zOffset}
|
||||||
|
|
||||||
G91 G0 Z ${zLift}
|
G91 G0 Z ${zLift}
|
||||||
G91 G0 X 20
|
G91 G0 X 20
|
||||||
G91 G0 Z ${-plunge}
|
G91 G0 Z ${-plunge}
|
||||||
G38.2 X -20 F${fastSeek}
|
G38.2 X -20 F${fastSeek}
|
||||||
G91 G1 X 1
|
G91 G1 X 1
|
||||||
G38.2 X -2 F${slowSeek}
|
G38.2 X -2 F${slowSeek}
|
||||||
G92 X ${xOffset}
|
G92 X ${xOffset}
|
||||||
|
|
||||||
G91 G0 X 1
|
G91 G0 X 1
|
||||||
G91 G0 Y 20
|
G91 G0 Y 20
|
||||||
G91 G0 X -20
|
G91 G0 X -20
|
||||||
G38.2 Y -20 F${fastSeek}
|
G38.2 Y -20 F${fastSeek}
|
||||||
G91 G1 Y 1
|
G91 G1 Y 1
|
||||||
G38.2 Y -2 F${slowSeek}
|
G38.2 Y -2 F${slowSeek}
|
||||||
G92 Y ${yOffset}
|
G92 Y ${yOffset}
|
||||||
|
|
||||||
G91 G0 Y 3
|
G91 G0 Y 3
|
||||||
G91 G0 Z 25
|
G91 G0 Z 25
|
||||||
|
|
||||||
M2
|
M2
|
||||||
`);
|
`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
class="probe-dialog"
|
class="probe-dialog"
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="probe-dialog-title"
|
aria-labelledby="probe-dialog-title"
|
||||||
aria-describedby="probe-dialog-content"
|
aria-describedby="probe-dialog-content"
|
||||||
surface$style="width: 700px; max-width: calc(100vw - 32px);"
|
surface$style="width: 700px; max-width: calc(100vw - 32px);"
|
||||||
>
|
>
|
||||||
<Title id="probe-dialog-title">Probing {probeType?.toUpperCase()}</Title>
|
<Title id="probe-dialog-title">Probing {probeType?.toUpperCase()}</Title>
|
||||||
|
|
||||||
<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>
|
||||||
<ul>
|
<b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b>
|
||||||
{#each steps as step}
|
</p>
|
||||||
<li class:active={currentStep === step}>{stepLabels[step]}</li>
|
<ul>
|
||||||
{/each}
|
{#each steps as step}
|
||||||
</ul>
|
<li class:active={currentStep === step}>
|
||||||
</div>
|
{stepLabels[step]}
|
||||||
<p>
|
</li>
|
||||||
{#if currentStep === "CheckProbe"}
|
{/each}
|
||||||
Attach the probe magnet to the collet, then touch the probe block to the
|
</ul>
|
||||||
bit.
|
</div>
|
||||||
{:else if currentStep === "BitDimensions"}
|
<p>
|
||||||
<TextFieldWithOptions
|
{#if currentStep === "CheckProbe"}
|
||||||
label="Cutter diameter"
|
Attach the probe magnet to the collet, then touch the probe
|
||||||
variant="filled"
|
block to the bit.
|
||||||
spellcheck="false"
|
{:else if currentStep === "BitDimensions"}
|
||||||
style="width: 100%;"
|
<TextFieldWithOptions
|
||||||
bind:value={cutterDiameterString}
|
label="Cutter diameter"
|
||||||
options={[imperialBits, metricBits]}
|
variant="filled"
|
||||||
valid={isFinite(cutterDiameterMetric)}
|
spellcheck="false"
|
||||||
helperText={`Examples: 1/2", 10 mm, 0.25 in`}
|
style="width: 100%;"
|
||||||
/>
|
bind:value={cutterDiameterString}
|
||||||
{:else if currentStep === "PlaceProbeBlock"}
|
options={[imperialBits, metricBits]}
|
||||||
{#if probeType === "xyz"}
|
valid={isFinite(cutterDiameterMetric)}
|
||||||
Place the probe block face up, on the lower-left corner of your
|
helperText={`Examples: 1/2", 10 mm, 0.25 in`}
|
||||||
workpiece.
|
/>
|
||||||
{:else}
|
{:else if currentStep === "PlaceProbeBlock"}
|
||||||
Place the probe block face down, with the bit above the recess.
|
{#if probeType === "xyz"}
|
||||||
|
Place the probe block face up, on the lower-left corner of
|
||||||
|
your workpiece.
|
||||||
|
{:else}
|
||||||
|
Place the probe block face down, with the bit above the
|
||||||
|
recess.
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The probing procedure will begin as soon as you click
|
||||||
|
'Next'.
|
||||||
|
</p>
|
||||||
|
{:else if currentStep === "Probe"}
|
||||||
|
Probing in progress...
|
||||||
|
{:else if currentStep === "Done"}
|
||||||
|
{#if $probingFailed}
|
||||||
|
Could not find the probe block during probing!
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Make sure the tip of the bit is less than {metric
|
||||||
|
? "25mm"
|
||||||
|
: "1 in"}
|
||||||
|
above the probe block, and try again.
|
||||||
|
</p>
|
||||||
|
{:else}
|
||||||
|
Don't forget to put away the probe!
|
||||||
|
|
||||||
|
{#if probeType === "xyz"}
|
||||||
|
<p>The machine will now move to the XY origin.</p>
|
||||||
|
|
||||||
|
<p>Watch your hands!</p>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Actions>
|
||||||
|
{#if showCancelButton}
|
||||||
|
<Button on:click={() => ($cancelled = true)}>
|
||||||
|
<Label>Cancel</Label>
|
||||||
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
|
<Button
|
||||||
<p>The probing procedure will begin as soon as you click 'Next'.</p>
|
defaultAction
|
||||||
{:else if currentStep === "Probe"}
|
data-mdc-dialog-action={nextButton.allowClose ? "close" : ""}
|
||||||
Probing in progress...
|
disabled={nextButton.disabled}
|
||||||
{:else if currentStep === "Done"}
|
on:click={() => ($userAcknowledged = true)}
|
||||||
{#if $probingFailed}
|
>
|
||||||
Could not find the probe block during probing!
|
<Label>
|
||||||
|
{nextButton.label}
|
||||||
<p>
|
</Label>
|
||||||
Make sure the tip of the bit is less than {metric ? "25mm" : "1 in"}
|
</Button>
|
||||||
above the probe block, and try again.
|
</Actions>
|
||||||
</p>
|
|
||||||
{:else}
|
|
||||||
Don't forget to put away the probe!
|
|
||||||
|
|
||||||
{#if probeType === "xyz"}
|
|
||||||
<p>The machine will now move to the XY origin.</p>
|
|
||||||
|
|
||||||
<p>Watch your hands!</p>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</p>
|
|
||||||
</Content>
|
|
||||||
|
|
||||||
<Actions>
|
|
||||||
{#if showCancelButton}
|
|
||||||
<Button on:click={() => ($cancelled = true)}>
|
|
||||||
<Label>Cancel</Label>
|
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
<Button
|
|
||||||
defaultAction
|
|
||||||
data-mdc-dialog-action={nextButton.allowClose ? "close" : ""}
|
|
||||||
disabled={nextButton.disabled}
|
|
||||||
on:click={() => ($userAcknowledged = true)}
|
|
||||||
>
|
|
||||||
<Label>
|
|
||||||
{nextButton.label}
|
|
||||||
</Label>
|
|
||||||
</Button>
|
|
||||||
</Actions>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
$primary: #0078e7;
|
$primary: #0078e7;
|
||||||
$very-dark: #555;
|
$very-dark: #555;
|
||||||
$text: #777;
|
$text: #777;
|
||||||
$grey: #bbb;
|
$grey: #bbb;
|
||||||
$light: #ddd;
|
$light: #ddd;
|
||||||
|
|
||||||
:global {
|
:global {
|
||||||
#probe-dialog-content {
|
#probe-dialog-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bit-dimensions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps {
|
||||||
|
margin-right: 50px;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0 auto;
|
||||||
|
list-style-type: none;
|
||||||
|
counter-reset: steps;
|
||||||
|
margin: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
padding-inline-start: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
padding: 0 0 12px 30px;
|
||||||
|
position: relative;
|
||||||
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: $text;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
left: 0.5px;
|
||||||
|
content: "";
|
||||||
|
border: 2px solid $text;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
height: 11px;
|
||||||
|
width: 11px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 12px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
left: 7px;
|
||||||
|
top: 22px;
|
||||||
|
bottom: 0;
|
||||||
|
content: "";
|
||||||
|
width: 0;
|
||||||
|
border-left: 2px solid $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type:before {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $primary;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
border: 3px solid $primary;
|
||||||
|
top: 2.5px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bit-dimensions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.steps {
|
|
||||||
margin-right: 50px;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0 auto;
|
|
||||||
list-style-type: none;
|
|
||||||
counter-reset: steps;
|
|
||||||
margin: 0;
|
|
||||||
font-family: sans-serif;
|
|
||||||
padding-inline-start: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
padding: 0 0 12px 30px;
|
|
||||||
position: relative;
|
|
||||||
margin: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
color: $text;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
position: absolute;
|
|
||||||
top: 3px;
|
|
||||||
left: 0.5px;
|
|
||||||
content: "";
|
|
||||||
border: 2px solid $text;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: inline-block;
|
|
||||||
height: 11px;
|
|
||||||
width: 11px;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 12px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
position: absolute;
|
|
||||||
left: 7px;
|
|
||||||
top: 22px;
|
|
||||||
bottom: 0;
|
|
||||||
content: "";
|
|
||||||
width: 0;
|
|
||||||
border-left: 2px solid $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-of-type:before {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $primary;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
border: 3px solid $primary;
|
|
||||||
top: 2.5px;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,76 +1,81 @@
|
|||||||
<script type="ts">
|
<script type="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import Button, { Label } from "@smui/button";
|
Title,
|
||||||
import Radio from "@smui/radio";
|
Content,
|
||||||
import FormField from "@smui/form-field";
|
Actions,
|
||||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
InitialFocus,
|
||||||
import * as Api from "$lib/api";
|
} from "@smui/dialog";
|
||||||
import { onMount } from "svelte";
|
import Button, { Label } from "@smui/button";
|
||||||
|
import Radio from "@smui/radio";
|
||||||
|
import FormField from "@smui/form-field";
|
||||||
|
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||||
|
import * as Api from "$lib/api";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
{ value: 0, label: "Normal" },
|
{ value: 0, label: "Normal" },
|
||||||
{ value: 1, label: "Upside-down" },
|
{ value: 1, label: "Upside-down" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export let open;
|
export let open;
|
||||||
let currentValue;
|
let currentValue;
|
||||||
let value;
|
let value;
|
||||||
let rebooting;
|
let rebooting;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const result = await Api.GET("screen-rotation");
|
const result = await Api.GET("screen-rotation");
|
||||||
currentValue = value = result.rotated ? 1 : 0;
|
currentValue = value = result.rotated ? 1 : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
async function onConfirm() {
|
async function onConfirm() {
|
||||||
rebooting = true;
|
rebooting = true;
|
||||||
|
|
||||||
await Api.PUT("screen-rotation", { rotated: value === 1 });
|
await Api.PUT("screen-rotation", { rotated: value === 1 });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||||
Rebooting to apply the new screen rotation...
|
Rebooting to apply the new screen rotation...
|
||||||
</MessageDialog>
|
</MessageDialog>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="screen-rotation-dialog-title"
|
aria-labelledby="screen-rotation-dialog-title"
|
||||||
aria-describedby="screen-rotation-dialog-content"
|
aria-describedby="screen-rotation-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="screen-rotation-dialog-title">Screen Rotation</Title>
|
<Title id="screen-rotation-dialog-title">Screen Rotation</Title>
|
||||||
|
|
||||||
<Content id="screen-rotation-dialog-content">
|
<Content id="screen-rotation-dialog-content">
|
||||||
{#each options as option}
|
{#each options as option}
|
||||||
<FormField>
|
<FormField>
|
||||||
<Radio bind:group={value} value={option.value} />
|
<Radio bind:group={value} value={option.value} />
|
||||||
<span slot="label">
|
<span slot="label">
|
||||||
{option.label}
|
{option.label}
|
||||||
</span>
|
</span>
|
||||||
</FormField>
|
</FormField>
|
||||||
{/each}
|
{/each}
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<Actions>
|
<Actions>
|
||||||
<Button use={[InitialFocus]}>
|
<Button use={[InitialFocus]}>
|
||||||
<Label>Cancel</Label>
|
<Label>Cancel</Label>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
defaultAction
|
defaultAction
|
||||||
disabled={value === currentValue}
|
disabled={value === currentValue}
|
||||||
on:click={onConfirm}
|
on:click={onConfirm}
|
||||||
>
|
>
|
||||||
<Label>Confirm & Reboot</Label>
|
<Label>Confirm & Reboot</Label>
|
||||||
</Button>
|
</Button>
|
||||||
</Actions>
|
</Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
:global {
|
:global {
|
||||||
#screen-rotation-dialog-content {
|
#screen-rotation-dialog-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,69 +1,75 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import TextField from "@smui/textfield";
|
Title,
|
||||||
import Button, { Label } from "@smui/button";
|
Content,
|
||||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
Actions,
|
||||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
InitialFocus,
|
||||||
|
} from "@smui/dialog";
|
||||||
|
import TextField from "@smui/textfield";
|
||||||
|
import Button, { Label } from "@smui/button";
|
||||||
|
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open: boolean;
|
export let open: boolean;
|
||||||
export let axis = "";
|
export let axis = "";
|
||||||
|
|
||||||
let value = 0;
|
let value = 0;
|
||||||
let homed = false;
|
let homed = false;
|
||||||
let wasOpen = false;
|
let wasOpen = false;
|
||||||
|
|
||||||
$: if (open != wasOpen) {
|
$: if (open != wasOpen) {
|
||||||
if (open) {
|
if (open) {
|
||||||
homed = ControllerMethods.isAxisHomed(axis);
|
homed = ControllerMethods.isAxisHomed(axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
wasOpen = open;
|
||||||
}
|
}
|
||||||
|
|
||||||
wasOpen = open;
|
function onUnhome() {
|
||||||
}
|
ControllerMethods.unhome(axis);
|
||||||
|
}
|
||||||
|
|
||||||
function onUnhome() {
|
function onConfirm() {
|
||||||
ControllerMethods.unhome(axis);
|
ControllerMethods.set_position(axis, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConfirm() {
|
|
||||||
ControllerMethods.set_position(axis, value);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
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">
|
|
||||||
<TextField
|
|
||||||
label="Position"
|
|
||||||
type="number"
|
|
||||||
bind:value
|
|
||||||
use={[
|
|
||||||
InitialFocus,
|
|
||||||
[virtualKeyboardChange, (newValue) => (value = newValue)],
|
|
||||||
]}
|
|
||||||
spellcheck="false"
|
|
||||||
variant="filled"
|
|
||||||
style="width: 100%;"
|
|
||||||
/>
|
|
||||||
</Content>
|
|
||||||
|
|
||||||
<Actions>
|
<Content id="set-axis-position-dialog-content">
|
||||||
<Button>
|
<TextField
|
||||||
<Label>Cancel</Label>
|
label="Position"
|
||||||
</Button>
|
type="number"
|
||||||
{#if homed}
|
bind:value
|
||||||
<Button on:click={onUnhome}>
|
use={[
|
||||||
<Label>Unhome</Label>
|
InitialFocus,
|
||||||
</Button>
|
[virtualKeyboardChange, (newValue) => (value = newValue)],
|
||||||
{/if}
|
]}
|
||||||
<Button defaultAction on:click={onConfirm}>
|
spellcheck="false"
|
||||||
<Label>Set</Label>
|
variant="filled"
|
||||||
</Button>
|
style="width: 100%;"
|
||||||
</Actions>
|
/>
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Actions>
|
||||||
|
<Button>
|
||||||
|
<Label>Cancel</Label>
|
||||||
|
</Button>
|
||||||
|
{#if homed}
|
||||||
|
<Button on:click={onUnhome}>
|
||||||
|
<Label>Unhome</Label>
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
<Button defaultAction on:click={onConfirm}>
|
||||||
|
<Label>Set</Label>
|
||||||
|
</Button>
|
||||||
|
</Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,245 +1,256 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import Button, { Label } from "@smui/button";
|
Title,
|
||||||
import TextField from "@smui/textfield";
|
Content,
|
||||||
import CircularProgress from "@smui/circular-progress";
|
Actions,
|
||||||
import VirtualList from "svelte-tiny-virtual-list";
|
InitialFocus,
|
||||||
import * as api from "$lib/api";
|
} from "@smui/dialog";
|
||||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
import Button, { Label } from "@smui/button";
|
||||||
|
import TextField from "@smui/textfield";
|
||||||
|
import CircularProgress from "@smui/circular-progress";
|
||||||
|
import VirtualList from "svelte-tiny-virtual-list";
|
||||||
|
import * as api from "$lib/api";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
const itemHeight = 35;
|
const itemHeight = 35;
|
||||||
|
|
||||||
type Timezone = {
|
type Timezone = {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export let open = false;
|
export let open = false;
|
||||||
let value = "";
|
let value = "";
|
||||||
let wasOpen = false;
|
let wasOpen = false;
|
||||||
let loading = true;
|
let loading = true;
|
||||||
let timezones: Timezone[] = [];
|
let timezones: Timezone[] = [];
|
||||||
let currentTimezoneIndex: number;
|
let currentTimezoneIndex: number;
|
||||||
let selectedTimezoneIndex: number;
|
let selectedTimezoneIndex: number;
|
||||||
let networkTimeSynchronized: boolean;
|
let networkTimeSynchronized: boolean;
|
||||||
|
|
||||||
$: if (open != wasOpen) {
|
$: if (open != wasOpen) {
|
||||||
if (!wasOpen) {
|
if (!wasOpen) {
|
||||||
loadData();
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
wasOpen = open;
|
||||||
}
|
}
|
||||||
|
|
||||||
wasOpen = open;
|
async function loadData() {
|
||||||
}
|
loading = true;
|
||||||
|
|
||||||
async function loadData() {
|
const result = await api.GET("time");
|
||||||
loading = true;
|
|
||||||
|
|
||||||
const result = await api.GET("time");
|
parseTimezones(result.timezones);
|
||||||
|
parseTimeinfo(result.timeinfo);
|
||||||
|
value = getDateTimeValueString();
|
||||||
|
|
||||||
parseTimezones(result.timezones);
|
loading = false;
|
||||||
parseTimeinfo(result.timeinfo);
|
|
||||||
value = getDateTimeValueString();
|
|
||||||
|
|
||||||
loading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseTimeinfo(str: string) {
|
|
||||||
const matches = Array.from(str.matchAll(/\s*([^:]+):\s+(.+)/gm));
|
|
||||||
|
|
||||||
let currentTimezoneValue;
|
|
||||||
for (const match of matches) {
|
|
||||||
let [, label, value] = match;
|
|
||||||
|
|
||||||
switch (label) {
|
|
||||||
case "Time zone":
|
|
||||||
currentTimezoneValue = value.split(" ")[0];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "NTP synchronized":
|
|
||||||
networkTimeSynchronized = value === "yes";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentTimezoneIndex = timezones.findIndex(
|
function parseTimeinfo(str: string) {
|
||||||
(tz) => tz.value === currentTimezoneValue
|
const matches = Array.from(str.matchAll(/\s*([^:]+):\s+(.+)/gm));
|
||||||
);
|
|
||||||
selectedTimezoneIndex = currentTimezoneIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseTimezones(str: string) {
|
let currentTimezoneValue;
|
||||||
const matches = Array.from(str.matchAll(/\s*(\S+)\s*/gm));
|
for (const match of matches) {
|
||||||
|
let [, label, value] = match;
|
||||||
|
|
||||||
timezones = [];
|
switch (label) {
|
||||||
for (let [, value] of matches) {
|
case "Time zone":
|
||||||
timezones.push({
|
currentTimezoneValue = value.split(" ")[0];
|
||||||
label: value.replace(/_/g, " "),
|
break;
|
||||||
value,
|
|
||||||
});
|
case "NTP synchronized":
|
||||||
|
networkTimeSynchronized = value === "yes";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTimezoneIndex = timezones.findIndex(
|
||||||
|
(tz) => tz.value === currentTimezoneValue
|
||||||
|
);
|
||||||
|
selectedTimezoneIndex = currentTimezoneIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort alphabetically, but with the current timezone at the top of the list
|
function parseTimezones(str: string) {
|
||||||
timezones.sort((a, b) => {
|
const matches = Array.from(str.matchAll(/\s*(\S+)\s*/gm));
|
||||||
switch (true) {
|
|
||||||
case a.value === "UTC":
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
case b.value === "UTC":
|
timezones = [];
|
||||||
return 1;
|
for (let [, value] of matches) {
|
||||||
|
timezones.push({
|
||||||
|
label: value.replace(/_/g, " "),
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
// Sort alphabetically, but with the current timezone at the top of the list
|
||||||
return a.value.localeCompare(b.value);
|
timezones.sort((a, b) => {
|
||||||
}
|
switch (true) {
|
||||||
});
|
case a.value === "UTC":
|
||||||
}
|
return -1;
|
||||||
|
|
||||||
function getDateTimeValueString() {
|
case b.value === "UTC":
|
||||||
const date = new Date();
|
return 1;
|
||||||
|
|
||||||
const year = date.getFullYear().toString().padStart(2, "0");
|
default:
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
return a.value.localeCompare(b.value);
|
||||||
const day = date.getDate().toString().padStart(2, "0");
|
}
|
||||||
const hour = date.getHours().toString().padStart(2, "0");
|
});
|
||||||
const minute = date.getMinutes().toString().padStart(2, "0");
|
}
|
||||||
|
|
||||||
return `${year}-${month}-${day}T${hour}:${minute}:00`;
|
function getDateTimeValueString() {
|
||||||
}
|
const date = new Date();
|
||||||
|
|
||||||
async function onConfirm() {
|
const year = date.getFullYear().toString().padStart(2, "0");
|
||||||
const date = new Date(value);
|
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||||
const year = date.getFullYear().toString().padStart(2, "0");
|
const day = date.getDate().toString().padStart(2, "0");
|
||||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
const hour = date.getHours().toString().padStart(2, "0");
|
||||||
const day = date.getDate().toString().padStart(2, "0");
|
const minute = date.getMinutes().toString().padStart(2, "0");
|
||||||
const hour = date.getHours().toString().padStart(2, "0");
|
|
||||||
const minute = date.getMinutes().toString().padStart(2, "0");
|
|
||||||
|
|
||||||
await api.PUT("time", {
|
return `${year}-${month}-${day}T${hour}:${minute}:00`;
|
||||||
datetime: `${year}-${month}-${day} ${hour}:${minute}:00`,
|
}
|
||||||
timezone: timezones[selectedTimezoneIndex].value,
|
|
||||||
});
|
async function onConfirm() {
|
||||||
}
|
const date = new Date(value);
|
||||||
|
const year = date.getFullYear().toString().padStart(2, "0");
|
||||||
|
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||||
|
const day = date.getDate().toString().padStart(2, "0");
|
||||||
|
const hour = date.getHours().toString().padStart(2, "0");
|
||||||
|
const minute = date.getMinutes().toString().padStart(2, "0");
|
||||||
|
|
||||||
|
await api.PUT("time", {
|
||||||
|
datetime: `${year}-${month}-${day} ${hour}:${minute}:00`,
|
||||||
|
timezone: timezones[selectedTimezoneIndex].value,
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="set-time-dialog-title"
|
aria-labelledby="set-time-dialog-title"
|
||||||
aria-describedby="set-time-dialog-content"
|
aria-describedby="set-time-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="set-time-dialog-title">Adjust Clock & Timezone</Title>
|
<Title id="set-time-dialog-title">Adjust Clock & Timezone</Title>
|
||||||
|
|
||||||
<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
|
||||||
</div>
|
style="height: 32px; width: 32px;"
|
||||||
{:else}
|
indeterminate
|
||||||
{#if networkTimeSynchronized}
|
/>
|
||||||
<p>
|
</div>
|
||||||
Because this controller is connected to the internet, the time is
|
{:else}
|
||||||
managed automatically, and cannot be manually set.
|
{#if networkTimeSynchronized}
|
||||||
</p>
|
<p>
|
||||||
{:else}
|
Because this controller is connected to the internet, the
|
||||||
<p>
|
time is managed automatically, and cannot be manually set.
|
||||||
Because this controller is not connected to the internet, you can
|
</p>
|
||||||
manually set the time.
|
{:else}
|
||||||
</p>
|
<p>
|
||||||
|
Because this controller is not connected to the internet,
|
||||||
|
you can manually set the time.
|
||||||
|
</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>
|
||||||
<TextField
|
<TextField
|
||||||
bind:value
|
bind:value
|
||||||
use={[
|
use={[
|
||||||
InitialFocus,
|
InitialFocus,
|
||||||
[virtualKeyboardChange, (newValue) => (value = newValue)],
|
[
|
||||||
]}
|
virtualKeyboardChange,
|
||||||
label="Time"
|
(newValue) => (value = newValue),
|
||||||
type="datetime-local"
|
],
|
||||||
variant="filled"
|
]}
|
||||||
style="width: 100%;"
|
label="Time"
|
||||||
/>
|
type="datetime-local"
|
||||||
{/if}
|
variant="filled"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
{/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;">
|
||||||
<Label>Select your timezone</Label>
|
<Label>Select your timezone</Label>
|
||||||
<VirtualList
|
<VirtualList
|
||||||
width="100%"
|
width="100%"
|
||||||
height={itemHeight * 6}
|
height={itemHeight * 6}
|
||||||
itemCount={timezones.length}
|
itemCount={timezones.length}
|
||||||
itemSize={itemHeight}
|
itemSize={itemHeight}
|
||||||
scrollToIndex={currentTimezoneIndex}
|
scrollToIndex={currentTimezoneIndex}
|
||||||
scrollToAlignment="center"
|
scrollToAlignment="center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
slot="item"
|
||||||
|
let:index
|
||||||
|
let:style
|
||||||
|
{style}
|
||||||
|
class="timezone"
|
||||||
|
class:selected={index === selectedTimezoneIndex}
|
||||||
|
on:click={() => (selectedTimezoneIndex = index)}
|
||||||
|
>
|
||||||
|
{timezones[index].label}
|
||||||
|
</div>
|
||||||
|
</VirtualList>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Actions>
|
||||||
|
<Button>
|
||||||
|
<Label>Cancel</Label>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
defaultAction
|
||||||
|
disabled={selectedTimezoneIndex === -1}
|
||||||
|
on:click={onConfirm}
|
||||||
>
|
>
|
||||||
<div
|
<Label>Confirm</Label>
|
||||||
slot="item"
|
</Button>
|
||||||
let:index
|
</Actions>
|
||||||
let:style
|
|
||||||
{style}
|
|
||||||
class="timezone"
|
|
||||||
class:selected={index === selectedTimezoneIndex}
|
|
||||||
on:click={() => (selectedTimezoneIndex = index)}
|
|
||||||
>
|
|
||||||
{timezones[index].label}
|
|
||||||
</div>
|
|
||||||
</VirtualList>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</Content>
|
|
||||||
|
|
||||||
<Actions>
|
|
||||||
<Button>
|
|
||||||
<Label>Cancel</Label>
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
defaultAction
|
|
||||||
disabled={selectedTimezoneIndex === -1}
|
|
||||||
on:click={onConfirm}
|
|
||||||
>
|
|
||||||
<Label>Confirm</Label>
|
|
||||||
</Button>
|
|
||||||
</Actions>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use "sass:color";
|
@use "sass:color";
|
||||||
|
|
||||||
$primary: #0078e7;
|
$primary: #0078e7;
|
||||||
$very-dark: #555;
|
$very-dark: #555;
|
||||||
$text: #777;
|
$text: #777;
|
||||||
$grey: #bbb;
|
$grey: #bbb;
|
||||||
$light: #ddd;
|
$light: #ddd;
|
||||||
|
|
||||||
.timezones-container {
|
.timezones-container {
|
||||||
:global {
|
:global {
|
||||||
.virtual-list-wrapper {
|
.virtual-list-wrapper {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.timezone {
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
color: $primary;
|
||||||
|
background-color: color.adjust($primary, $lightness: 50%);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.timezone {
|
|
||||||
font-size: 14px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 10px;
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
color: $primary;
|
|
||||||
background-color: color.adjust($primary, $lightness: 50%);
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,78 +1,83 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import Button, { Label } from "@smui/button";
|
Title,
|
||||||
import LinearProgress from "@smui/linear-progress";
|
Content,
|
||||||
|
Actions,
|
||||||
|
InitialFocus,
|
||||||
|
} from "@smui/dialog";
|
||||||
|
import Button, { Label } from "@smui/button";
|
||||||
|
import LinearProgress from "@smui/linear-progress";
|
||||||
|
|
||||||
export let open = false;
|
export let open = false;
|
||||||
export let file: File;
|
export let file: File;
|
||||||
export let onComplete: () => void;
|
export let onComplete: () => void;
|
||||||
|
|
||||||
let wasOpen = false;
|
let wasOpen = false;
|
||||||
let xhr;
|
let xhr;
|
||||||
let progress;
|
let progress;
|
||||||
|
|
||||||
$: if (open != wasOpen) {
|
$: if (open != wasOpen) {
|
||||||
if (!wasOpen) {
|
if (!wasOpen) {
|
||||||
beginUpload();
|
beginUpload();
|
||||||
|
}
|
||||||
|
|
||||||
|
wasOpen = open;
|
||||||
}
|
}
|
||||||
|
|
||||||
wasOpen = open;
|
$: if (!open) {
|
||||||
}
|
xhr = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
$: if (!open) {
|
async function beginUpload() {
|
||||||
xhr = undefined;
|
progress = 0;
|
||||||
}
|
|
||||||
|
|
||||||
async function beginUpload() {
|
xhr = new XMLHttpRequest();
|
||||||
progress = 0;
|
xhr.upload.onload = () => {
|
||||||
|
open = false;
|
||||||
|
if (onComplete) {
|
||||||
|
onComplete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
xhr = new XMLHttpRequest();
|
xhr.upload.onerror = () => {
|
||||||
xhr.upload.onload = () => {
|
open = false;
|
||||||
open = false;
|
alert("Upload failed.");
|
||||||
if (onComplete) {
|
};
|
||||||
onComplete();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.upload.onerror = () => {
|
xhr.upload.onabort = () => {
|
||||||
open = false;
|
open = false;
|
||||||
alert("Upload failed.");
|
};
|
||||||
};
|
|
||||||
|
|
||||||
xhr.upload.onabort = () => {
|
xhr.upload.onprogress = (event) => {
|
||||||
open = false;
|
progress = event.loaded / event.total;
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.upload.onprogress = (event) => {
|
xhr.open("PUT", `/api/file/${encodeURIComponent(file.name)}`);
|
||||||
progress = event.loaded / event.total;
|
xhr.send(file);
|
||||||
};
|
}
|
||||||
|
|
||||||
xhr.open("PUT", `/api/file/${encodeURIComponent(file.name)}`);
|
function onCancel() {
|
||||||
xhr.send(file);
|
xhr.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCancel() {
|
|
||||||
xhr.abort();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="upload-dialog-title"
|
aria-labelledby="upload-dialog-title"
|
||||||
aria-describedby="upload-dialog-content"
|
aria-describedby="upload-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="upload-dialog-title">
|
<Title id="upload-dialog-title">
|
||||||
Uploading {#if file}{file.name}...{/if}
|
Uploading {#if file}{file.name}...{/if}
|
||||||
</Title>
|
</Title>
|
||||||
|
|
||||||
<Content id="upload-dialog-content">
|
<Content id="upload-dialog-content">
|
||||||
<LinearProgress {progress} />
|
<LinearProgress {progress} />
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<Actions>
|
<Actions>
|
||||||
<Button on:click={onCancel} use={[InitialFocus]}>
|
<Button on:click={onCancel} use={[InitialFocus]}>
|
||||||
<Label>Cancel</Label>
|
<Label>Cancel</Label>
|
||||||
</Button>
|
</Button>
|
||||||
</Actions>
|
</Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -1,103 +1,115 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content, Actions, InitialFocus } from "@smui/dialog";
|
import Dialog, {
|
||||||
import Button, { Label } from "@smui/button";
|
Title,
|
||||||
import TextField from "@smui/textfield";
|
Content,
|
||||||
import Icon from "@smui/textfield/icon";
|
Actions,
|
||||||
import HelperText from "@smui/textfield/helper-text";
|
InitialFocus,
|
||||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
} from "@smui/dialog";
|
||||||
import type { WifiNetwork } from "$lib/NetworkInfo";
|
import Button, { Label } from "@smui/button";
|
||||||
import * as api from "$lib/api";
|
import TextField from "@smui/textfield";
|
||||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
import Icon from "@smui/textfield/icon";
|
||||||
|
import HelperText from "@smui/textfield/helper-text";
|
||||||
|
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||||
|
import type { WifiNetwork } from "$lib/NetworkInfo";
|
||||||
|
import * as api from "$lib/api";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open = false;
|
export let open = false;
|
||||||
export let network: WifiNetwork;
|
export let network: WifiNetwork;
|
||||||
|
|
||||||
let rebooting = false;
|
let rebooting = false;
|
||||||
let password = "";
|
let password = "";
|
||||||
let showPassword = false;
|
let showPassword = false;
|
||||||
|
|
||||||
$: needPassword = !network?.active && network?.Encryption !== "Open";
|
$: needPassword = !network?.active && network?.Encryption !== "Open";
|
||||||
$: connectOrDisconnect = network?.active ? "Disconnect" : "Connect";
|
$: connectOrDisconnect = network?.active ? "Disconnect" : "Connect";
|
||||||
$: connectToOrDisconnectFrom = network?.active
|
$: connectToOrDisconnectFrom = network?.active
|
||||||
? "Disconnect from"
|
? "Disconnect from"
|
||||||
: "Connect to";
|
: "Connect to";
|
||||||
|
|
||||||
$: if (open) {
|
$: if (open) {
|
||||||
password = "";
|
password = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onConfirm() {
|
async function onConfirm() {
|
||||||
rebooting = true;
|
rebooting = true;
|
||||||
|
|
||||||
await api.PUT("network", {
|
await api.PUT("network", {
|
||||||
wifi: {
|
wifi: {
|
||||||
enabled: !network.active,
|
enabled: !network.active,
|
||||||
ssid: network.Name,
|
ssid: network.Name,
|
||||||
password,
|
password,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||||
Rebooting to apply Wifi changes...
|
Rebooting to apply Wifi changes...
|
||||||
</MessageDialog>
|
</MessageDialog>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
bind:open
|
bind:open
|
||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="wifi-connection-dialog-title"
|
aria-labelledby="wifi-connection-dialog-title"
|
||||||
aria-describedby="wifi-connection-dialog-content"
|
aria-describedby="wifi-connection-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="wifi-connection-dialog-title">
|
<Title id="wifi-connection-dialog-title">
|
||||||
{connectToOrDisconnectFrom}
|
{connectToOrDisconnectFrom}
|
||||||
{network.Name}
|
{network.Name}
|
||||||
</Title>
|
</Title>
|
||||||
|
|
||||||
<Content id="wifi-connection-dialog-content">
|
<Content id="wifi-connection-dialog-content">
|
||||||
{#if needPassword}
|
{#if needPassword}
|
||||||
<TextField
|
<TextField
|
||||||
bind:value={password}
|
bind:value={password}
|
||||||
use={[
|
use={[
|
||||||
InitialFocus,
|
InitialFocus,
|
||||||
[virtualKeyboardChange, (newValue) => (password = newValue)],
|
[
|
||||||
]}
|
virtualKeyboardChange,
|
||||||
label="Password"
|
(newValue) => (password = newValue),
|
||||||
spellcheck="false"
|
],
|
||||||
variant="filled"
|
]}
|
||||||
type={showPassword ? "text" : "password"}
|
label="Password"
|
||||||
style="width: 100%;"
|
spellcheck="false"
|
||||||
>
|
variant="filled"
|
||||||
<div
|
type={showPassword ? "text" : "password"}
|
||||||
slot="trailingIcon"
|
style="width: 100%;"
|
||||||
on:click={() => (showPassword = !showPassword)}
|
>
|
||||||
|
<div
|
||||||
|
slot="trailingIcon"
|
||||||
|
on:click={() => (showPassword = !showPassword)}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
class={`fa ${showPassword ? "fa-eye-slash" : "fa-eye"}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<HelperText persistent slot="helper">
|
||||||
|
Wifi passwords must be 8 to 128 characters
|
||||||
|
</HelperText>
|
||||||
|
</TextField>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<em>
|
||||||
|
Clicking {connectOrDisconnect} will reboot the controller to apply
|
||||||
|
the changes.
|
||||||
|
</em>
|
||||||
|
</p>
|
||||||
|
</Content>
|
||||||
|
|
||||||
|
<Actions>
|
||||||
|
<Button>
|
||||||
|
<Label>Cancel</Label>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
defaultAction
|
||||||
|
on:click={onConfirm}
|
||||||
|
disabled={needPassword &&
|
||||||
|
(password.length < 8 || password.length > 128)}
|
||||||
>
|
>
|
||||||
<Icon class={`fa ${showPassword ? "fa-eye-slash" : "fa-eye"}`} />
|
<Label>{connectOrDisconnect} & Reboot</Label>
|
||||||
</div>
|
</Button>
|
||||||
<HelperText persistent slot="helper">
|
</Actions>
|
||||||
Wifi passwords must be 8 to 128 characters
|
|
||||||
</HelperText>
|
|
||||||
</TextField>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<em>
|
|
||||||
Clicking {connectOrDisconnect} will reboot the controller to apply the changes.
|
|
||||||
</em>
|
|
||||||
</p>
|
|
||||||
</Content>
|
|
||||||
|
|
||||||
<Actions>
|
|
||||||
<Button>
|
|
||||||
<Label>Cancel</Label>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
defaultAction
|
|
||||||
on:click={onConfirm}
|
|
||||||
disabled={needPassword && (password.length < 8 || password.length > 128)}
|
|
||||||
>
|
|
||||||
<Label>{connectOrDisconnect} & Reboot</Label>
|
|
||||||
</Button>
|
|
||||||
</Actions>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
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";
|
||||||
|
|
||||||
export function createComponent(component: string, target: HTMLElement, props: Record<string, any>) {
|
export function createComponent(component: string, target: HTMLElement, props: Record<string, any>) {
|
||||||
switch (component) {
|
switch (component) {
|
||||||
case "AdminNetworkView":
|
case "AdminNetworkView":
|
||||||
return new AdminNetworkView({ target, props });
|
return new AdminNetworkView({ target, props });
|
||||||
|
|
||||||
case "SettingsView":
|
case "SettingsView":
|
||||||
return new SettingsView({ target, props });
|
return new SettingsView({ target, props });
|
||||||
|
|
||||||
case "DialogHost":
|
case "DialogHost":
|
||||||
return new DialogHost({ target, props });
|
return new DialogHost({ target, props });
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error("Unknown component");
|
throw new Error("Unknown component");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
showDialog,
|
showDialog,
|
||||||
handleControllerStateUpdate,
|
handleControllerStateUpdate,
|
||||||
handleConfigUpdate,
|
handleConfigUpdate,
|
||||||
registerControllerMethods,
|
registerControllerMethods,
|
||||||
setDisplayUnits
|
setDisplayUnits
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user