v1.0.9 changes from 1.1.1
14
Makefile
@@ -100,6 +100,9 @@ node_modules: package.json
|
||||
$(TARGET_DIR)/%: src/resources/%
|
||||
install -D $< $@
|
||||
|
||||
src/svelte-components/dist/%:
|
||||
cd src/svelte-components && rm -rf dist && npm run build
|
||||
|
||||
$(TARGET_DIR)/index.html: build/templates.pug
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/static/js/*)
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/static/css/*)
|
||||
@@ -108,9 +111,16 @@ $(TARGET_DIR)/index.html: $(wildcard src/js/*)
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/stylus/*)
|
||||
$(TARGET_DIR)/index.html: src/resources/config-template.json
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/resources/onefinity*defaults.json)
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/svelte-components/dist/*)
|
||||
|
||||
$(TARGET_DIR)/%.html: src/pug/%.pug node_modules
|
||||
@mkdir -p $(shell dirname $@)
|
||||
FORCE:
|
||||
|
||||
$(TARGET_DIR)/%.html: src/pug/%.pug node_modules FORCE
|
||||
cd src/svelte-components && rm -rf dist && npm run build
|
||||
@mkdir -p $(TARGET_DIR)/svelte-components
|
||||
cp src/svelte-components/dist/* $(TARGET_DIR)/svelte-components/
|
||||
|
||||
@mkdir -p $(TARGET_DIR)
|
||||
$(PUG) -O pug-opts.js -P $< -o $(TARGET_DIR) || (rm -f $@; exit 1)
|
||||
|
||||
pylint:
|
||||
|
||||
@@ -54,7 +54,7 @@ static struct {
|
||||
line_t line;
|
||||
|
||||
int section;
|
||||
int seg;
|
||||
uint32_t seg;
|
||||
|
||||
float iD; // Initial section distance
|
||||
float iV; // Initial section velocity
|
||||
|
||||
@@ -1,150 +1,120 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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'
|
||||
"use strict";
|
||||
|
||||
const api = require("./api");
|
||||
const utils = require("./utils");
|
||||
const merge = require("lodash.merge");
|
||||
|
||||
const config_defaults = require("../resources/onefinity_defaults.json");
|
||||
|
||||
const variant_defaults = {
|
||||
machinist_x35: require("../resources/onefinity_machinist_x35_defaults.json"),
|
||||
woodworker_x35: require("../resources/onefinity_woodworker_x35_defaults.json"),
|
||||
woodworker_x50: require("../resources/onefinity_woodworker_x50_defaults.json"),
|
||||
journeyman_x50: require("../resources/onefinity_journeyman_x50_defaults.json")
|
||||
machinist_x35: require("../resources/onefinity_machinist_x35_defaults.json"),
|
||||
woodworker_x35: require("../resources/onefinity_woodworker_x35_defaults.json"),
|
||||
woodworker_x50: require("../resources/onefinity_woodworker_x50_defaults.json"),
|
||||
journeyman_x50: require("../resources/onefinity_journeyman_x50_defaults.json")
|
||||
};
|
||||
|
||||
const api = require('./api');
|
||||
|
||||
module.exports = {
|
||||
template: '#admin-general-view-template',
|
||||
props: ['config', 'state'],
|
||||
template: "#admin-general-view-template",
|
||||
props: [ "config", "state" ],
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
configRestored: false,
|
||||
confirmReset: false,
|
||||
configReset: false,
|
||||
latest: '',
|
||||
autoCheckUpgrade: true,
|
||||
reset_variant: ''
|
||||
}
|
||||
},
|
||||
|
||||
events: {
|
||||
latest_version: function (version) {
|
||||
this.latest = version
|
||||
}
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
this.autoCheckUpgrade = this.config.admin['auto-check-upgrade']
|
||||
},
|
||||
|
||||
methods: {
|
||||
backup: function () {
|
||||
document.getElementById('download-target').src = '/api/config/download';
|
||||
data: function() {
|
||||
return {
|
||||
confirmReset: false,
|
||||
autoCheckUpgrade: true,
|
||||
reset_variant: ""
|
||||
};
|
||||
},
|
||||
|
||||
restore_config: function () {
|
||||
// If we don't reset the form the browser may cache file if name is same
|
||||
// even if contents have changed
|
||||
$('.restore-config')[0].reset();
|
||||
$('.restore-config input').click();
|
||||
ready: function() {
|
||||
this.autoCheckUpgrade = this.config.admin["auto-check-upgrade"];
|
||||
},
|
||||
|
||||
restore: function (e) {
|
||||
var files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length) return;
|
||||
methods: {
|
||||
backup: function() {
|
||||
document.getElementById("download-target").src = "/api/config/download";
|
||||
},
|
||||
|
||||
var fr = new FileReader();
|
||||
fr.onload = function (e) {
|
||||
var config;
|
||||
try {
|
||||
config = JSON.parse(e.target.result);
|
||||
} catch (ex) {
|
||||
api.alert("Invalid config file");
|
||||
return;
|
||||
restore_config: function() {
|
||||
utils.clickFileInput("restore-config");
|
||||
},
|
||||
|
||||
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 (error) {
|
||||
console.error("Invalid config file:", error);
|
||||
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) {
|
||||
console.error("Restore failed:", error);
|
||||
alert("Restore failed");
|
||||
}
|
||||
};
|
||||
|
||||
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 (error) {
|
||||
console.error("Restore failed:", error);
|
||||
alert("Restore failed");
|
||||
}
|
||||
},
|
||||
|
||||
check: function() {
|
||||
this.$dispatch("check");
|
||||
},
|
||||
|
||||
upgrade: function() {
|
||||
this.$dispatch("upgrade");
|
||||
},
|
||||
|
||||
upload_firmware: function() {
|
||||
utils.clickFileInput("upload-firmware");
|
||||
},
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
api.put('config/save', config).done(function (data) {
|
||||
this.$dispatch('update');
|
||||
this.configRestored = true;
|
||||
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Restore failed', error);
|
||||
})
|
||||
}.bind(this);
|
||||
|
||||
fr.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');
|
||||
this.configRestored = true;
|
||||
} 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,177 +1,14 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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');
|
||||
|
||||
|
||||
module.exports = {
|
||||
template: '#admin-network-view-template',
|
||||
props: ['config', 'state'],
|
||||
template: "#admin-network-view-template",
|
||||
|
||||
attached: function() {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"AdminNetworkView",
|
||||
document.getElementById("admin-network")
|
||||
);
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
hostnameSet: false,
|
||||
usernameSet: false,
|
||||
passwordSet: false,
|
||||
redirectTimeout: 0,
|
||||
hostname: '',
|
||||
username: '',
|
||||
current: '',
|
||||
password: '',
|
||||
password2: '',
|
||||
wifi_mode: 'client',
|
||||
wifi_ssid: '',
|
||||
wifi_ch: undefined,
|
||||
wifi_pass: '',
|
||||
wifiConfirm: false,
|
||||
rebooting: false
|
||||
detached: function() {
|
||||
this.svelteComponent.$destroy();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
ready: function () {
|
||||
api.get('hostname').done(function (hostname) {
|
||||
this.hostname = hostname;
|
||||
}.bind(this));
|
||||
|
||||
api.get('remote/username').done(function (username) {
|
||||
this.username = username;
|
||||
}.bind(this));
|
||||
|
||||
api.get('wifi').done(function (config) {
|
||||
this.wifi_mode = config.mode;
|
||||
this.wifi_ssid = config.ssid;
|
||||
this.wifi_ch = config.channel;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
redirect: function (hostname) {
|
||||
if (0 < this.redirectTimeout) {
|
||||
this.redirectTimeout -= 1;
|
||||
setTimeout(function () {this.redirect(hostname)}.bind(this), 1000);
|
||||
|
||||
} else location.hostname = hostname;
|
||||
},
|
||||
|
||||
|
||||
set_hostname: function () {
|
||||
api.put('hostname', {hostname: this.hostname}).done(function () {
|
||||
this.redirectTimeout = 45;
|
||||
this.hostnameSet = true;
|
||||
|
||||
api.put('reboot').always(function () {
|
||||
if (String(location.hostname) == 'localhost') return;
|
||||
|
||||
var hostname = this.hostname;
|
||||
if (String(location.hostname).endsWith('.local'))
|
||||
hostname += '.local'
|
||||
this.$dispatch('hostname-changed', hostname);
|
||||
this.redirect(hostname);
|
||||
}.bind(this));
|
||||
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Set hostname failed', error);
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
set_username: function () {
|
||||
api.put('remote/username', {username: this.username}).done(function () {
|
||||
this.usernameSet = true;
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Set username failed', error);
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
set_password: function () {
|
||||
if (this.password != this.password2) {
|
||||
alert('Passwords to not match');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.password.length < 6) {
|
||||
alert('Password too short');
|
||||
return;
|
||||
}
|
||||
|
||||
api.put('remote/password', {
|
||||
current: this.current,
|
||||
password: this.password
|
||||
}).done(function () {
|
||||
this.passwordSet = true;
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Set password failed', error);
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
config_wifi: function () {
|
||||
this.wifiConfirm = false;
|
||||
|
||||
if (!this.wifi_ssid.length) {
|
||||
alert('SSID not set');
|
||||
return;
|
||||
}
|
||||
|
||||
if (32 < this.wifi_ssid.length) {
|
||||
alert('SSID longer than 32 characters');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.wifi_pass.length && this.wifi_pass.length < 8) {
|
||||
alert('WiFi password shorter than 8 characters');
|
||||
return;
|
||||
}
|
||||
|
||||
if (128 < this.wifi_pass.length) {
|
||||
alert('WiFi password longer than 128 characters');
|
||||
return;
|
||||
}
|
||||
|
||||
this.rebooting = true;
|
||||
|
||||
var config = {
|
||||
mode: this.wifi_mode,
|
||||
channel: this.wifi_ch,
|
||||
ssid: this.wifi_ssid,
|
||||
pass: this.wifi_pass
|
||||
}
|
||||
|
||||
api.put('wifi', config).fail(function (error) {
|
||||
api.alert('Failed to configure WiFi', error);
|
||||
this.rebooting = false;
|
||||
}.bind(this))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
128
src/js/api.js
@@ -1,104 +1,48 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
async function callApi(method, url, data) {
|
||||
try {
|
||||
const headers = {};
|
||||
let body = undefined;
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
if (data) {
|
||||
if (data instanceof FormData) {
|
||||
body = data;
|
||||
} else {
|
||||
headers["Content-Type"] = "application/json; charset=utf-8";
|
||||
body = JSON.stringify(data);
|
||||
}
|
||||
}
|
||||
|
||||
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/>.
|
||||
const response = await fetch(`/api/${url}`, {
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
cache: "no-cache",
|
||||
});
|
||||
|
||||
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.
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
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/>.
|
||||
throw new Error(await response.text());
|
||||
} catch (error) {
|
||||
console.debug(`API Error: ${url}: ${error}`);
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
function api_cb(method, url, data, config) {
|
||||
config = $.extend({
|
||||
type: method,
|
||||
url: '/api/' + url,
|
||||
dataType: 'json',
|
||||
cache: false
|
||||
}, config);
|
||||
|
||||
if (typeof data == 'object') {
|
||||
config.data = JSON.stringify(data);
|
||||
config.contentType = 'application/json; charset=utf-8';
|
||||
}
|
||||
|
||||
var d = $.Deferred();
|
||||
|
||||
$.ajax(config).success(function (data, status, xhr) {
|
||||
d.resolve(data, status, xhr);
|
||||
|
||||
}).error(function (xhr, status, error) {
|
||||
var text = xhr.responseText;
|
||||
try {text = $.parseJSON(xhr.responseText)} catch(e) {}
|
||||
if (!text) text = error;
|
||||
|
||||
d.reject(text, xhr, status, error);
|
||||
console.debug('API Error: ' + url + ': ' + text);
|
||||
});
|
||||
|
||||
return d.promise();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
get: function (url, config) {
|
||||
return api_cb('GET', url, undefined, config);
|
||||
},
|
||||
get: function(url) {
|
||||
return callApi("GET", url);
|
||||
},
|
||||
|
||||
put: function(url, body = undefined) {
|
||||
return callApi("PUT", url, body);
|
||||
},
|
||||
|
||||
put: function(url, data, config) {
|
||||
return api_cb('PUT', url, data, config);
|
||||
},
|
||||
|
||||
|
||||
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);
|
||||
delete: function(url) {
|
||||
return callApi("DELETE", url);
|
||||
}
|
||||
|
||||
alert(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
850
src/js/app.js
@@ -1,506 +1,440 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
const api = require("./api");
|
||||
const cookie = require("./cookie")("bbctrl-");
|
||||
const Sock = require("./sock");
|
||||
const semverLt = require("semver/functions/lt");
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
SvelteComponents.createComponent("DialogHost",
|
||||
document.getElementById("svelte-dialog-host")
|
||||
);
|
||||
|
||||
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/>.
|
||||
function parse_version(v) {
|
||||
const pattern = /^(\d+)\.(\d+)\.(\d+)(?:[-.]?(.*))?$/;
|
||||
const [ version, major, minor, patch, pre ] = v.trim().match(pattern) || [];
|
||||
|
||||
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 cookie = require('./cookie')('bbctrl-');
|
||||
const Sock = require('./sock');
|
||||
const omit = require('lodash.omit');
|
||||
|
||||
function is_newer_version(current, latest) {
|
||||
const pattern = /(\d+)\.(\d+)\.(\d+)(.*)/;
|
||||
const currentParts = current.match(pattern);
|
||||
const latestParts = latest.match(pattern);
|
||||
|
||||
if (!currentParts || !latestParts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Normal version comparisons
|
||||
const major = latestParts[1] - currentParts[1];
|
||||
const minor = latestParts[2] - currentParts[2];
|
||||
const patch = latestParts[3] - currentParts[3];
|
||||
|
||||
// If current is a pre-release, and latest is a release
|
||||
const betaToRelease = latestParts[4].length === 0 && currentParts[4].length > 0;
|
||||
|
||||
switch (true) {
|
||||
case major > 0:
|
||||
case major === 0 && minor > 0:
|
||||
case major === 0 && minor === 0 && patch > 0:
|
||||
case major === 0 && minor === 0 && patch === 0 && betaToRelease:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
version,
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
pre
|
||||
};
|
||||
}
|
||||
|
||||
function is_object(o) { return o !== null && typeof o == 'object' }
|
||||
function is_array(o) { return Array.isArray(o) }
|
||||
function fixup_version_number(version) {
|
||||
const v = parse_version(version);
|
||||
|
||||
version = `${v.major}.${v.minor}.${v.patch}`;
|
||||
if (v.pre) {
|
||||
const [ , prefix, num ] = v.pre.match(/([a-zA-Z])(\d+)/);
|
||||
|
||||
const suffix = prefix === "b"
|
||||
? `beta.${num}`
|
||||
: v.pre;
|
||||
|
||||
version = `${version}-${suffix}`;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
function is_object(o) {
|
||||
return o !== null && typeof o == "object";
|
||||
}
|
||||
|
||||
function is_array(o) {
|
||||
return Array.isArray(o);
|
||||
}
|
||||
|
||||
function update_array(dst, src) {
|
||||
while (dst.length) dst.pop()
|
||||
for (var i = 0; i < src.length; i++)
|
||||
Vue.set(dst, i, src[i]);
|
||||
while (dst.length) {
|
||||
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) {
|
||||
var props, index, key, value;
|
||||
let props, index, key, value;
|
||||
|
||||
if (remove) {
|
||||
props = Object.getOwnPropertyNames(dst);
|
||||
if (remove) {
|
||||
props = Object.getOwnPropertyNames(dst);
|
||||
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
if (!src.hasOwnProperty(key))
|
||||
Vue.delete(dst, key);
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
if (!hasOwnProperty(src, key)) {
|
||||
Vue.delete(dst, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
props = Object.getOwnPropertyNames(src);
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
value = src[key];
|
||||
props = Object.getOwnPropertyNames(src);
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
value = src[key];
|
||||
|
||||
if (is_array(value) && dst.hasOwnProperty(key) && is_array(dst[key]))
|
||||
update_array(dst[key], value);
|
||||
|
||||
else if (is_object(value) && dst.hasOwnProperty(key) && is_object(dst[key]))
|
||||
update_object(dst[key], value, remove);
|
||||
|
||||
else Vue.set(dst, key, value);
|
||||
}
|
||||
if (is_array(value) && hasOwnProperty(dst, key) && is_array(dst[key])) {
|
||||
update_array(dst[key], value);
|
||||
} else if (is_object(value) && hasOwnProperty(dst, key) && is_object(dst[key])) {
|
||||
update_object(dst[key], value, remove);
|
||||
} else {
|
||||
Vue.set(dst, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Vue({
|
||||
el: 'body',
|
||||
el: "body",
|
||||
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
status: 'connecting',
|
||||
currentView: 'loading',
|
||||
index: -1,
|
||||
modified: false,
|
||||
template: require('../resources/config-template.json'),
|
||||
config: {
|
||||
settings: { units: 'METRIC' },
|
||||
motors: [{}, {}, {}, {}],
|
||||
version: '<loading>',
|
||||
full_version: '<loading>'
|
||||
},
|
||||
state: {
|
||||
messages: [],
|
||||
probing_active: false,
|
||||
wait_for_probing_complete: false,
|
||||
show_probe_complete_modal: false,
|
||||
show_probe_failed_modal: false
|
||||
},
|
||||
video_size: cookie.get('video-size', 'small'),
|
||||
crosshair: cookie.get('crosshair', 'false') != 'false',
|
||||
errorTimeout: 30,
|
||||
errorTimeoutStart: 0,
|
||||
errorShow: false,
|
||||
errorMessage: '',
|
||||
confirmUpgrade: false,
|
||||
confirmUpload: false,
|
||||
firmwareUpgrading: false,
|
||||
checkedUpgrade: false,
|
||||
firmwareName: '',
|
||||
latestVersion: '',
|
||||
ipAddress: '0.0.0.0',
|
||||
wifiSSID: '',
|
||||
confirmShutdown: false,
|
||||
diskSpace: ''
|
||||
}
|
||||
},
|
||||
|
||||
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 { showUnimplemented: false } }
|
||||
}
|
||||
},
|
||||
|
||||
events: {
|
||||
'config-changed': function () {
|
||||
this.modified = true;
|
||||
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>",
|
||||
ip: "<>",
|
||||
wifiName: "not connected",
|
||||
},
|
||||
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: "",
|
||||
};
|
||||
},
|
||||
|
||||
'hostname-changed': function (hostname) {
|
||||
this.hostname = hostname
|
||||
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": require("./help-view"),
|
||||
"cheat-sheet-view": {
|
||||
template: "#cheat-sheet-view-template",
|
||||
data: function() {
|
||||
return {
|
||||
showUnimplemented: false
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
send: function (msg) {
|
||||
if (this.status == 'connected') {
|
||||
console.debug('>', msg);
|
||||
this.sock.send(msg);
|
||||
}
|
||||
watch: {
|
||||
display_units: function(value) {
|
||||
localStorage.setItem("display_units", value);
|
||||
SvelteComponents.setDisplayUnits(value);
|
||||
},
|
||||
},
|
||||
|
||||
connected: function () {
|
||||
this.update()
|
||||
},
|
||||
events: {
|
||||
"config-changed": function() {
|
||||
this.modified = true;
|
||||
},
|
||||
|
||||
update: function () {
|
||||
this.update()
|
||||
},
|
||||
send: function(msg) {
|
||||
if (this.status == "connected") {
|
||||
this.sock.send(msg);
|
||||
}
|
||||
},
|
||||
|
||||
check: function () {
|
||||
this.latestVersion = '';
|
||||
connected: function() {
|
||||
this.update();
|
||||
},
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt',
|
||||
data: { hid: this.state.hid },
|
||||
cache: false
|
||||
update: function() {
|
||||
this.update();
|
||||
},
|
||||
|
||||
}).done(function (data) {
|
||||
this.latestVersion = data;
|
||||
this.$broadcast('latest_version', data);
|
||||
}.bind(this))
|
||||
},
|
||||
check: async function() {
|
||||
try {
|
||||
const response = await fetch("https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt", {
|
||||
cache: "no-cache"
|
||||
});
|
||||
|
||||
upgrade: function () {
|
||||
this.confirmUpgrade = true;
|
||||
},
|
||||
this.latestVersion = (await response.text()).trim();
|
||||
} catch (err) {
|
||||
this.latestVersion = "";
|
||||
}
|
||||
},
|
||||
|
||||
upload: function (firmware) {
|
||||
this.firmware = firmware;
|
||||
this.firmwareName = firmware.name;
|
||||
this.confirmUpload = true;
|
||||
},
|
||||
upgrade: function() {
|
||||
this.confirmUpgrade = true;
|
||||
},
|
||||
|
||||
error: function (msg) {
|
||||
// Honor user error blocking
|
||||
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000)
|
||||
return;
|
||||
upload: function(firmware) {
|
||||
this.firmware = firmware;
|
||||
this.firmwareName = firmware.name;
|
||||
this.confirmUpload = true;
|
||||
},
|
||||
|
||||
// Wait at least 1 sec to pop up repeated errors
|
||||
if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Popup error dialog
|
||||
this.errorShow = true;
|
||||
this.errorMessage = msg.msg;
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
popupMessages: function () {
|
||||
const msgs = [];
|
||||
|
||||
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();
|
||||
},
|
||||
|
||||
methods: {
|
||||
metric: function () {
|
||||
return this.config.settings.units != 'IMPERIAL'
|
||||
},
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
update: function () {
|
||||
api.get('config/load').done(function (config) {
|
||||
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');
|
||||
}
|
||||
|
||||
this.check_ip_address();
|
||||
this.check_ssid();
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
check_ip_address: function () {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'hostinfo.txt',
|
||||
data: { hid: this.state.hid },
|
||||
cache: false
|
||||
|
||||
}).done(function (data) {
|
||||
console.debug('>', data);
|
||||
this.ipAddress = 'IP:' + data;
|
||||
this.$broadcast('ipAddress', data);
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
check_ssid: function () {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'ssidinfo.txt',
|
||||
data: { hid: this.state.hid },
|
||||
cache: false
|
||||
|
||||
}).done(function (data) {
|
||||
console.debug('>', data);
|
||||
this.wifiSSID = 'SSID:' + data;
|
||||
this.$broadcast('wifiSSID', data);
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
get_ip_address: function () {
|
||||
console.debug('get_ip>', this.ipAddress);
|
||||
return this.ipAddress;
|
||||
},
|
||||
|
||||
get_ssid: function () {
|
||||
console.debug('get_ssid>', this.wifiSSID);
|
||||
return this.wifiSSID;
|
||||
},
|
||||
|
||||
shutdown: function () {
|
||||
this.confirmShutdown = false;
|
||||
api.put('shutdown');
|
||||
|
||||
},
|
||||
|
||||
reboot: function () {
|
||||
this.confirmShutdown = false;
|
||||
api.put('reboot');
|
||||
},
|
||||
|
||||
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('probing_failed');
|
||||
} else {
|
||||
this.$broadcast('log', e.data.log);
|
||||
}
|
||||
|
||||
delete 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;
|
||||
error: function(msg) {
|
||||
// Honor user error blocking
|
||||
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
// Wait at least 1 sec to pop up repeated errors
|
||||
if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set this to true to get console output of changes to the state
|
||||
const debugStateChanges = false;
|
||||
if (debugStateChanges) {
|
||||
const data = omit(e.data, [
|
||||
'vdd',
|
||||
'vin',
|
||||
'vout',
|
||||
'motor',
|
||||
'temp',
|
||||
'heartbeat',
|
||||
'load1',
|
||||
'load2',
|
||||
'rpi_temp'
|
||||
]);
|
||||
if (Object.keys(data).length > 0) {
|
||||
console.log(JSON.stringify(data, null, 4));
|
||||
}
|
||||
}
|
||||
|
||||
update_object(this.state, e.data, false);
|
||||
|
||||
if (this.state.pw === 0) {
|
||||
Vue.set(this.state, "saw_probe_connected", true);
|
||||
}
|
||||
|
||||
if (this.state.cycle === 'idle') {
|
||||
if (this.state.wait_for_probing_complete) {
|
||||
Vue.set(this.state, "wait_for_probing_complete", false);
|
||||
this.$broadcast("probing_complete");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
// Popup error dialog
|
||||
this.errorShow = true;
|
||||
this.errorMessage = msg.msg;
|
||||
},
|
||||
},
|
||||
|
||||
parse_hash: function () {
|
||||
var hash = location.hash.substr(1);
|
||||
computed: {
|
||||
popupMessages: function() {
|
||||
const msgs = [];
|
||||
|
||||
if (!hash.trim().length) {
|
||||
location.hash = 'control';
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < this.state.messages.length; i++) {
|
||||
const text = this.state.messages[i].text;
|
||||
if (!/^#/.test(text)) {
|
||||
msgs.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
var parts = hash.split(':');
|
||||
|
||||
if (parts.length == 2) this.index = parts[1];
|
||||
|
||||
this.currentView = parts[0];
|
||||
return msgs;
|
||||
},
|
||||
},
|
||||
|
||||
save: 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'];
|
||||
ready: function() {
|
||||
window.onhashchange = () => this.parse_hash();
|
||||
this.connect();
|
||||
|
||||
this.config['selected-tool-settings'][selected_tool] = settings;
|
||||
|
||||
api.put('config/save', this.config).done(function (data) {
|
||||
this.modified = false;
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Save failed', error);
|
||||
});
|
||||
SvelteComponents.registerControllerMethods({
|
||||
dispatch: (...args) => this.$dispatch(...args)
|
||||
});
|
||||
},
|
||||
|
||||
close_messages: function (action) {
|
||||
if (action == 'stop') api.put('stop');
|
||||
if (action == 'continue') api.put('unpause');
|
||||
methods: {
|
||||
block_error_dialog: function() {
|
||||
this.errorTimeoutStart = Date.now();
|
||||
this.errorShow = false;
|
||||
},
|
||||
|
||||
// Acknowledge messages
|
||||
if (this.state.messages.length) {
|
||||
var id = this.state.messages.slice(-1)[0].id
|
||||
api.put('message/' + id + '/ack');
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
toggle_video: function() {
|
||||
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 (error) {
|
||||
console.error("Error during upgrade:", error);
|
||||
alert("Error during upgrade");
|
||||
}
|
||||
},
|
||||
|
||||
upload_confirmed: async function() {
|
||||
this.confirmUpload = false;
|
||||
|
||||
const form = new FormData();
|
||||
form.append("firmware", this.firmware);
|
||||
|
||||
try {
|
||||
await api.put("firmware/update", form);
|
||||
this.firmwareUpgrading = true;
|
||||
} catch (error) {
|
||||
console.error("Firmware update failed:", error);
|
||||
alert("Firmware update failed");
|
||||
}
|
||||
},
|
||||
|
||||
show_upgrade: function() {
|
||||
if (!this.latestVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return semverLt(this.config.full_version, this.latestVersion);
|
||||
},
|
||||
|
||||
showShutdownDialog: function() {
|
||||
SvelteComponents.showDialog("Shutdown");
|
||||
},
|
||||
|
||||
update: async function() {
|
||||
const config = await api.get("config/load");
|
||||
const wifi = await api.get("wifi");
|
||||
update_object(this.config, config, true);
|
||||
this.config.full_version = fixup_version_number(this.config.full_version);
|
||||
this.config.ip = wifi.ipAddresses;
|
||||
this.config.wifiName = wifi.wifi;
|
||||
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 (e.data.log && e.data.log.msg !== "Switch not found") {
|
||||
this.$broadcast("log", e.data.log);
|
||||
|
||||
if (Object.keys(e.data).length === 1) {
|
||||
// If there's only log data, we're done
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
console.error("Save failed:", error);
|
||||
alert("Save failed");
|
||||
}
|
||||
},
|
||||
|
||||
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 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#axis-control-template',
|
||||
props: ['axes', 'colors', 'enabled', 'adjust', 'step'],
|
||||
template: "#axis-control-template",
|
||||
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: {
|
||||
jog: function (axis, ring, direction) {
|
||||
var value = direction * this.value(ring);
|
||||
this.$dispatch(this.step ? 'step' : 'jog', this.axes[axis], value);
|
||||
},
|
||||
back2zero: function(axis0,axis1) {
|
||||
this.$dispatch("back2zero",this.axes[axis0],this.axes[axis1]);
|
||||
},
|
||||
|
||||
back2zero: function(axis0,axis1) {
|
||||
this.$dispatch('back2zero',this.axes[axis0],this.axes[axis1])
|
||||
},
|
||||
release: function(axis) {
|
||||
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) {
|
||||
if (!this.step) this.$dispatch('jog', this.axes[axis], 0)
|
||||
},
|
||||
|
||||
|
||||
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 ? '' : '%');
|
||||
text: function(ring) {
|
||||
let value = this.value(ring) * (this.step ? 1 : 100);
|
||||
value = parseFloat(value.toFixed(3));
|
||||
return value + (this.step ? "" : "%");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,232 +1,254 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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 is_defined(x) {return typeof x != 'undefined'}
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
props: ['state', 'config'],
|
||||
props: [ "state", "config" ],
|
||||
|
||||
computed: {
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
computed: {
|
||||
x: function () {return this._compute_axis('x')},
|
||||
y: function () {return this._compute_axis('y')},
|
||||
z: function () {return this._compute_axis('z')},
|
||||
a: function () {return this._compute_axis('a')},
|
||||
b: function () {return this._compute_axis('b')},
|
||||
c: function () {return this._compute_axis('c')},
|
||||
axes: function () {return this._compute_axes()}
|
||||
},
|
||||
x: function() {
|
||||
return this._compute_axis("x");
|
||||
},
|
||||
|
||||
y: function() {
|
||||
return this._compute_axis("y");
|
||||
},
|
||||
|
||||
methods: {
|
||||
_convert_length: function (value) {
|
||||
return this.state.imperial ? value / 25.4 : value;
|
||||
},
|
||||
z: function() {
|
||||
return this._compute_axis("z");
|
||||
},
|
||||
|
||||
a: function() {
|
||||
return this._compute_axis("a");
|
||||
},
|
||||
|
||||
_length_str: function (value) {
|
||||
return this._convert_length(value).toLocaleString() +
|
||||
(this.state.imperial ? ' in' : ' mm');
|
||||
},
|
||||
b: function() {
|
||||
return this._compute_axis("b");
|
||||
},
|
||||
|
||||
c: function() {
|
||||
return this._compute_axis("c");
|
||||
},
|
||||
|
||||
_compute_axis: function (axis) {
|
||||
var abs = this.state[axis + 'p'] || 0;
|
||||
var off = this.state['offset_' + axis];
|
||||
var motor_id = this._get_motor_id(axis);
|
||||
var motor = motor_id == -1 ? {} : this.config.motors[motor_id];
|
||||
var enabled = typeof motor.enabled != 'undefined' && motor.enabled;
|
||||
var 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) {
|
||||
state = shutdown ? 'SHUTDOWN' : 'FAULT';
|
||||
klass += ' error';
|
||||
icon = 'exclamation-circle';
|
||||
|
||||
} else if(homed) {
|
||||
state = 'HOMED';
|
||||
icon = 'check-circle';
|
||||
}
|
||||
|
||||
if (0 < dim && dim < pathDim) {
|
||||
tstate = 'NO FIT';
|
||||
tklass += ' error';
|
||||
ticon = 'ban';
|
||||
|
||||
} else {
|
||||
|
||||
if (over || under) {
|
||||
tstate = over ? 'OVER' : 'UNDER';
|
||||
tklass += ' warn';
|
||||
ticon = 'exclamation-circle';
|
||||
} else {
|
||||
tstate = 'OK';
|
||||
ticon = 'check-circle';
|
||||
axes: function() {
|
||||
return this._compute_axes();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
_convert_length: function(value) {
|
||||
return this.metric
|
||||
? value
|
||||
: value / 25.4;
|
||||
},
|
||||
|
||||
_get_motor_id: function (axis) {
|
||||
for (var i = 0; i < this.config.motors.length; i++) {
|
||||
var motor = this.config.motors[i];
|
||||
if (motor.axis.toLowerCase() == axis) return i;
|
||||
}
|
||||
_length_str: function(value) {
|
||||
return this._convert_length(value).toLocaleString() + (this.metric ? " mm" : " in");
|
||||
},
|
||||
|
||||
return -1;
|
||||
},
|
||||
_compute_axis: function(axis) {
|
||||
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}`;
|
||||
|
||||
if (fault || shutdown) {
|
||||
state = shutdown ? "SHUTDOWN" : "FAULT";
|
||||
klass += " error";
|
||||
icon = "exclamation-circle";
|
||||
} else if (homed) {
|
||||
state = "HOMED";
|
||||
icon = "check-circle";
|
||||
}
|
||||
|
||||
_compute_axes: function () {
|
||||
var homed = false;
|
||||
if (0 < dim && dim < pathDim) {
|
||||
tstate = "NO FIT";
|
||||
tklass += " error";
|
||||
ticon = "ban";
|
||||
} else {
|
||||
if (over || under) {
|
||||
tstate = over ? "OVER" : "UNDER";
|
||||
tklass += " warn";
|
||||
ticon = "exclamation-circle";
|
||||
} else {
|
||||
tstate = "OK";
|
||||
ticon = "check-circle";
|
||||
}
|
||||
}
|
||||
|
||||
for (var name of 'xyzabc') {
|
||||
var axis = this[name];
|
||||
switch (state) {
|
||||
case "UNHOMED":
|
||||
title = "Click the home button to home axis.";
|
||||
break;
|
||||
|
||||
if (!axis.enabled) continue
|
||||
if (!axis.homed) {homed = false; break}
|
||||
homed = true;
|
||||
}
|
||||
case "HOMED":
|
||||
title = "Axis successfuly homed.";
|
||||
break;
|
||||
|
||||
var error = false;
|
||||
var warn = false;
|
||||
case "FAULT":
|
||||
title = [
|
||||
`Motor driver fault. A potentially damaging electrical`,
|
||||
`condition was detected and the motor driver was shutdown.`,
|
||||
`Please power down the controller and check your motor cabling.`,
|
||||
`See the "Motor Faults" table on the "Indicators" tab for more`,
|
||||
`information.`,
|
||||
].join(" ");
|
||||
break;
|
||||
|
||||
if (homed)
|
||||
for (name of 'xyzabc') {
|
||||
axis = this[name];
|
||||
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;
|
||||
}
|
||||
|
||||
if (!axis.enabled) continue;
|
||||
if (axis.klass.indexOf('error') != -1) error = true;
|
||||
if (axis.klass.indexOf('warn') != -1) warn = true;
|
||||
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_msg = true;
|
||||
this.ask_home = false;
|
||||
}
|
||||
|
||||
return {
|
||||
homed: homed,
|
||||
klass: klass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,87 +1,77 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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'
|
||||
|
||||
"use strict";
|
||||
|
||||
function _msg_equal(a, b) {
|
||||
return a.level == b.level && a.source == b.source && a.where == b.where &&
|
||||
a.msg == b.msg;
|
||||
return a.level == b.level
|
||||
&& a.source == b.source
|
||||
&& a.where == b.where
|
||||
&& a.msg == b.msg;
|
||||
}
|
||||
|
||||
|
||||
// Shared among all instances
|
||||
var messages = [];
|
||||
|
||||
const messages = [];
|
||||
|
||||
module.exports = {
|
||||
template: '#console-template',
|
||||
template: "#console-template",
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
messages
|
||||
};
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {messages: messages}
|
||||
},
|
||||
events: {
|
||||
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: {
|
||||
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;
|
||||
// Make sure we have a message level
|
||||
msg.level = msg.level || "info";
|
||||
|
||||
// Make sure we have a message level
|
||||
msg.level = msg.level || 'info';
|
||||
// Add to message log and count and collapse repeats
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// Add to message log and count and collapse repeats
|
||||
var 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();
|
||||
if (messages[0].repeat > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write message to browser console for debugging
|
||||
var text = JSON.stringify(msg);
|
||||
if (msg.level == 'error' || msg.level == 'critical') console.error(text);
|
||||
else if (msg.level == 'warning') console.warn(text);
|
||||
else if (msg.level == 'debug' && console.debug) console.debug(text);
|
||||
else console.log(text);
|
||||
msg.ts = Date.now();
|
||||
|
||||
// Event on errors
|
||||
if (msg.level == 'error' || msg.level == 'critical')
|
||||
this.$dispatch('error', msg);
|
||||
// Write message to browser console for debugging
|
||||
const text = JSON.stringify(msg);
|
||||
if (msg.level == "error" || msg.level == "critical") {
|
||||
console.error(text);
|
||||
} 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
|
||||
if (msg.level == "error" || msg.level == "critical") {
|
||||
this.$dispatch("error", msg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
clear: function() {
|
||||
messages.splice(0, messages.length);
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
clear: function () {messages.splice(0, messages.length);},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
112
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) {
|
||||
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';
|
||||
module.exports = function(prefix) {
|
||||
if (typeof prefix == "undefined") {
|
||||
prefix = "";
|
||||
}
|
||||
}
|
||||
|
||||
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,154 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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 = {
|
||||
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
||||
'/': '/', '`': '`', '=': '='}
|
||||
"use strict";
|
||||
|
||||
const entityMap = {
|
||||
"&": "&", "<": "<", ">": ">", '"': """, "'": "'",
|
||||
"/": "/", "`": "`", "=": "=" };
|
||||
|
||||
function escapeHTML(s) {
|
||||
return s.replace(/[&<>"'`=\/]/g, function (c) {return entityMap[c]})
|
||||
return s.replace(/[&<>"'`=\\/]/g, function(c) {
|
||||
return entityMap[c];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
template: '#gcode-viewer-template',
|
||||
template: "#gcode-viewer-template",
|
||||
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
empty: true,
|
||||
file: '',
|
||||
line: -1,
|
||||
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);
|
||||
data: function() {
|
||||
return {
|
||||
empty: true,
|
||||
file: "",
|
||||
line: -1
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
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 () {
|
||||
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)
|
||||
})
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Vue.nextTick(this.highlight);
|
||||
ready: function() {
|
||||
this.clusterize = new Clusterize({
|
||||
rows: [],
|
||||
scrollElem: this.$el.querySelector(".clusterize-scroll"),
|
||||
contentElem: this.$el.querySelector(".clusterize-content"),
|
||||
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}`, { cache: "no-cache" });
|
||||
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() {
|
||||
const highlights = this.$el.querySelectorAll(".highlight");
|
||||
for (const highlight of highlights) {
|
||||
highlight.className = (highlight.className || "")
|
||||
.split(" ")
|
||||
.filter(c => c !== "highlight")
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
const lines = this.$el.querySelectorAll(`.ln${this.line}`);
|
||||
for (const line of lines) {
|
||||
line.className = (line.className || "")
|
||||
.split(" ")
|
||||
.filter(c => c !== "highlight")
|
||||
.concat([ "highlight" ])
|
||||
.join(" ");
|
||||
}
|
||||
},
|
||||
|
||||
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 scroll = this.$el.querySelector(".clusterize-scroll");
|
||||
|
||||
const lineHeight = scroll.scrollHeight / totalLines;
|
||||
const linesPerPage = Math.floor(scroll.clientHeight / lineHeight);
|
||||
const target = line - 1 - Math.floor(linesPerPage / 2);
|
||||
|
||||
// Update scroll position
|
||||
scroll.scrollTop = target * lineHeight;
|
||||
|
||||
Vue.nextTick(this.highlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
14
src/js/help-view.js
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
template: "#help-view-template",
|
||||
|
||||
attached: function() {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"HelpView",
|
||||
document.getElementById("help")
|
||||
);
|
||||
},
|
||||
|
||||
detached: function() {
|
||||
this.svelteComponent.$destroy();
|
||||
}
|
||||
};
|
||||
@@ -1,107 +1,94 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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');
|
||||
"use strict";
|
||||
|
||||
const modbus = require("./modbus.js");
|
||||
|
||||
module.exports = {
|
||||
template: '#indicators-template',
|
||||
props: ['state'],
|
||||
template: "#indicators-template",
|
||||
props: [ "state" ],
|
||||
|
||||
computed: {
|
||||
modbus_status: function() {
|
||||
return modbus.status_to_string(this.state.mx);
|
||||
},
|
||||
|
||||
computed: {
|
||||
modbus_status: function () {return modbus.status_to_string(this.state.mx)},
|
||||
sense_error: function() {
|
||||
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 () {
|
||||
var error = '';
|
||||
return 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';
|
||||
methods: {
|
||||
is_motor_enabled: function(motor) {
|
||||
return typeof this.state[`${motor}me`] != "undefined" && this.state[`${motor}me`];
|
||||
},
|
||||
|
||||
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 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: "#io-indicator-template",
|
||||
props: ['name', 'state'],
|
||||
template: "#io-indicator-template",
|
||||
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: {
|
||||
klass: function () {
|
||||
if (this.name == 'min-switch-0') return this.get_motor_min_class(0);
|
||||
if (this.name == 'min-switch-1') return this.get_motor_min_class(1);
|
||||
if (this.name == 'min-switch-2') return this.get_motor_min_class(2);
|
||||
if (this.name == 'min-switch-3') return this.get_motor_min_class(3);
|
||||
if (this.name == 'max-switch-0') return this.get_motor_max_class(0);
|
||||
if (this.name == 'max-switch-1') return this.get_motor_max_class(1);
|
||||
if (this.name == 'max-switch-2') return this.get_motor_max_class(2);
|
||||
if (this.name == 'max-switch-3') return this.get_motor_max_class(3);
|
||||
if (this.name == 'estop') return this.get_input_class('ew', 'et');
|
||||
if (this.name == 'probe') return this.get_input_class('pw', 'pt');
|
||||
if (this.name == 'load-1') return this.get_output_class('1');
|
||||
if (this.name == 'load-2') return this.get_output_class('2');
|
||||
if (this.name == 'fault') return this.get_output_class('f');
|
||||
if (this.name == 'tool-enable-mode') return this.get_output_class('e');
|
||||
if (this.name == 'tool-direction-mode') return this.get_output_class('d');
|
||||
tooltip: function() {
|
||||
switch (this.name) {
|
||||
case "min-switch-0": return this.get_motor_min_tooltip(0);
|
||||
case "min-switch-1": return this.get_motor_min_tooltip(1);
|
||||
case "min-switch-2": return this.get_motor_min_tooltip(2);
|
||||
case "min-switch-3": return this.get_motor_min_tooltip(3);
|
||||
case "max-switch-0": return this.get_motor_max_tooltip(0);
|
||||
case "max-switch-1": return this.get_motor_max_tooltip(1);
|
||||
case "max-switch-2": return this.get_motor_max_tooltip(2);
|
||||
case "max-switch-3": return this.get_motor_max_tooltip(3);
|
||||
case "estop": return this.get_input_tooltip("ew", "et");
|
||||
case "probe": return this.get_input_tooltip("pw", "pt");
|
||||
case "load-1": return this.get_output_tooltip("1");
|
||||
case "load-2": return this.get_output_tooltip("2");
|
||||
case "fault": return this.get_output_tooltip("f");
|
||||
case "tool-direction-mode": return this.get_output_tooltip("d");
|
||||
case "tool-enable-mode": return this.get_output_tooltip("e");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
get_io_state_class: function(active, state) {
|
||||
if (typeof active == "undefined" || typeof state == "undefined") {
|
||||
return "fa-exclamation-triangle warn";
|
||||
}
|
||||
|
||||
tooltip: function () {
|
||||
if (this.name == 'min-switch-0') return this.get_motor_min_tooltip(0);
|
||||
if (this.name == 'min-switch-1') return this.get_motor_min_tooltip(1);
|
||||
if (this.name == 'min-switch-2') return this.get_motor_min_tooltip(2);
|
||||
if (this.name == 'min-switch-3') return this.get_motor_min_tooltip(3);
|
||||
if (this.name == 'max-switch-0') return this.get_motor_max_tooltip(0);
|
||||
if (this.name == 'max-switch-1') return this.get_motor_max_tooltip(1);
|
||||
if (this.name == 'max-switch-2') return this.get_motor_max_tooltip(2);
|
||||
if (this.name == 'max-switch-3') return this.get_motor_max_tooltip(3);
|
||||
if (this.name == 'estop') return this.get_input_tooltip('ew', 'et');
|
||||
if (this.name == 'probe') return this.get_input_tooltip('pw', 'pt');
|
||||
if (this.name == 'load-1') return this.get_output_tooltip('1');
|
||||
if (this.name == 'load-2') return this.get_output_tooltip('2');
|
||||
if (this.name == 'fault') return this.get_output_tooltip('f');
|
||||
if (this.name == 'tool-direction-mode')
|
||||
return this.get_output_tooltip('d');
|
||||
if (this.name == 'tool-enable-mode')
|
||||
return this.get_output_tooltip('e');
|
||||
if (state == 2) {
|
||||
return "fa-circle-o";
|
||||
}
|
||||
|
||||
const icon = state ? "fa-plus-circle" : "fa-minus-circle";
|
||||
return `${icon} ${active ? "active" : "inactive"}`;
|
||||
},
|
||||
|
||||
get_input_active: function(stateCode, typeCode) {
|
||||
const type = this.state[typeCode];
|
||||
const 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) {
|
||||
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 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#io-view-template',
|
||||
props: ['config', 'template', 'state'],
|
||||
template: "#io-view-template",
|
||||
props: [ "config", "template", "state" ],
|
||||
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
this.$dispatch('config-changed');
|
||||
return false;
|
||||
events: {
|
||||
"input-changed": function() {
|
||||
this.$dispatch("config-changed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
251
src/js/main.js
@@ -1,147 +1,148 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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';
|
||||
|
||||
"use strict";
|
||||
|
||||
function cookie_get(name) {
|
||||
var decodedCookie = decodeURIComponent(document.cookie);
|
||||
var ca = decodedCookie.split(';');
|
||||
name = name + '=';
|
||||
const decodedCookie = decodeURIComponent(document.cookie);
|
||||
const ca = decodedCookie.split(";");
|
||||
name = `${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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cookie_set(name, value, days) {
|
||||
var d = new Date();
|
||||
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
|
||||
var expires = 'expires=' + d.toUTCString();
|
||||
document.cookie = name + '=' + value + ';' + expires + ';path=/';
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
|
||||
const expires = `expires=${d.toUTCString()}`;
|
||||
document.cookie = `${name}=${value};${expires};path=/`;
|
||||
}
|
||||
|
||||
|
||||
var uuid_chars =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+';
|
||||
|
||||
const uuid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+";
|
||||
|
||||
function uuid(length) {
|
||||
if (typeof length == 'undefined') length = 52;
|
||||
if (typeof length == "undefined") {
|
||||
length = 52;
|
||||
}
|
||||
|
||||
var s = '';
|
||||
for (var i = 0; i < length; i++)
|
||||
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
|
||||
let s = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
|
||||
}
|
||||
|
||||
return s
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
$(function() {
|
||||
if (typeof cookie_get('client-id') == 'undefined')
|
||||
cookie_set('client-id', uuid(), 10000);
|
||||
|
||||
// Vue debugging
|
||||
Vue.config.debug = true;
|
||||
//Vue.util.warn = function (msg) {console.debug('[Vue warn]: ' + msg)}
|
||||
|
||||
// Register global components
|
||||
Vue.component('templated-input', require('./templated-input'));
|
||||
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();
|
||||
});
|
||||
|
||||
Vue.filter('percent', function (value, precision) {
|
||||
if (typeof value == 'undefined') return '';
|
||||
if (typeof precision == 'undefined') precision = 2;
|
||||
return (value * 100.0).toFixed(precision) + '%';
|
||||
});
|
||||
|
||||
Vue.filter('non_zero_percent', function (value, precision) {
|
||||
if (!value) return '';
|
||||
if (typeof precision == 'undefined') precision = 2;
|
||||
return (value * 100.0).toFixed(precision) + '%';
|
||||
});
|
||||
|
||||
Vue.filter('fixed', function (value, precision) {
|
||||
if (typeof value == 'undefined') return '0';
|
||||
return parseFloat(value).toFixed(precision)
|
||||
});
|
||||
|
||||
Vue.filter('upper', function (value) {
|
||||
if (typeof value == 'undefined') return '';
|
||||
return value.toUpperCase()
|
||||
});
|
||||
|
||||
Vue.filter('time', function (value, precision) {
|
||||
if (isNaN(value)) return '';
|
||||
if (isNaN(precision)) precision = 0;
|
||||
|
||||
var MIN = 60;
|
||||
var HR = MIN * 60;
|
||||
var DAY = HR * 24;
|
||||
var parts = [];
|
||||
|
||||
if (DAY <= value) {
|
||||
parts.push(Math.floor(value / DAY));
|
||||
value %= DAY;
|
||||
window.onload = function() {
|
||||
if (typeof cookie_get("client-id") == "undefined") {
|
||||
cookie_set("client-id", uuid(), 10000);
|
||||
}
|
||||
|
||||
if (HR <= value) {
|
||||
parts.push(Math.floor(value / HR));
|
||||
value %= HR;
|
||||
}
|
||||
// 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"));
|
||||
|
||||
if (MIN <= value) {
|
||||
parts.push(Math.floor(value / MIN));
|
||||
value %= MIN;
|
||||
Vue.filter("number", function(value) {
|
||||
if (isNaN(value)) {
|
||||
return "NaN";
|
||||
}
|
||||
|
||||
} else parts.push(0);
|
||||
return value.toLocaleString();
|
||||
});
|
||||
|
||||
parts.push(value);
|
||||
Vue.filter("percent", function(value, precision) {
|
||||
if (typeof value == "undefined") {
|
||||
return "";
|
||||
}
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
|
||||
if (i && parts[i] < 10) parts[i] = '0' + parts[i];
|
||||
}
|
||||
if (typeof precision == "undefined") {
|
||||
precision = 2;
|
||||
}
|
||||
|
||||
return parts.join(':');
|
||||
});
|
||||
return `${(value * 100.0).toFixed(precision)}%`;
|
||||
});
|
||||
|
||||
// Vue app
|
||||
require('./app');
|
||||
});
|
||||
Vue.filter("non_zero_percent", function(value, precision) {
|
||||
if (!value) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (typeof precision == "undefined") {
|
||||
precision = 2;
|
||||
}
|
||||
|
||||
return `${(value * 100.0).toFixed(precision)}%`;
|
||||
});
|
||||
|
||||
Vue.filter("fixed", function(value, precision) {
|
||||
if (typeof value == "undefined") {
|
||||
return "0";
|
||||
}
|
||||
|
||||
return parseFloat(value).toFixed(precision);
|
||||
});
|
||||
|
||||
Vue.filter("upper", function(value) {
|
||||
if (typeof value == "undefined") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return value.toUpperCase();
|
||||
});
|
||||
|
||||
Vue.filter("time", function(value, precision) {
|
||||
if (isNaN(value)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (isNaN(precision)) {
|
||||
precision = 0;
|
||||
}
|
||||
|
||||
const MIN = 60;
|
||||
const HR = MIN * 60;
|
||||
const DAY = HR * 24;
|
||||
const parts = [];
|
||||
|
||||
if (DAY <= value) {
|
||||
parts.push(Math.floor(value / DAY));
|
||||
value %= DAY;
|
||||
}
|
||||
|
||||
if (HR <= value) {
|
||||
parts.push(Math.floor(value / HR));
|
||||
value %= HR;
|
||||
}
|
||||
|
||||
if (MIN <= value) {
|
||||
parts.push(Math.floor(value / MIN));
|
||||
value %= MIN;
|
||||
} else {
|
||||
parts.push(0);
|
||||
}
|
||||
|
||||
parts.push(value);
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
|
||||
if (i && parts[i] < 10) {
|
||||
parts[i] = `0${parts[i]}`;
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join(":");
|
||||
});
|
||||
|
||||
// Vue app
|
||||
require("./app");
|
||||
};
|
||||
|
||||
@@ -1,47 +1,19 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#message-template',
|
||||
template: "#message-template",
|
||||
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
twoWay: true
|
||||
},
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
twoWay: true
|
||||
},
|
||||
|
||||
class: {
|
||||
type: String,
|
||||
required: false,
|
||||
twoWay: false
|
||||
class: {
|
||||
type: String,
|
||||
required: false,
|
||||
twoWay: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,48 +1,20 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '#modbus-reg-view-template',
|
||||
props: ['index', 'model', 'template', 'enable'],
|
||||
replace: true,
|
||||
template: "#modbus-reg-view-template",
|
||||
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: {
|
||||
has_user_value: function () {
|
||||
var type = this.model['reg-type'];
|
||||
return type.indexOf('write') != -1 || type.indexOf('fixed') != -1;
|
||||
methods: {
|
||||
change: function() {
|
||||
this.$dispatch("input-changed");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
change: function () {this.$dispatch('input-changed')}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,51 +1,23 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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'
|
||||
|
||||
"use strict";
|
||||
|
||||
// Must match modbus.c
|
||||
var exports = {
|
||||
DISCONNECTED: 0,
|
||||
OK: 1,
|
||||
CRC: 2,
|
||||
INVALID: 3,
|
||||
TIMEDOUT: 4
|
||||
const constants = {
|
||||
DISCONNECTED: 0,
|
||||
OK: 1,
|
||||
CRC: 2,
|
||||
INVALID: 3,
|
||||
TIMEDOUT: 4
|
||||
};
|
||||
|
||||
|
||||
exports.status_to_string =
|
||||
function (status) {
|
||||
if (status == exports.OK) return 'Ok';
|
||||
if (status == exports.CRC) return 'CRC error';
|
||||
if (status == exports.INVALID) return 'Invalid response';
|
||||
if (status == exports.TIMEDOUT) return 'Timedout';
|
||||
return 'Disconnected';
|
||||
}
|
||||
|
||||
|
||||
module.exports = exports;
|
||||
module.exports = {
|
||||
...constants,
|
||||
status_to_string: function(status) {
|
||||
switch (status) {
|
||||
case constants.OK: return "Ok";
|
||||
case constants.CRC: return "CRC error";
|
||||
case constants.INVALID: return "Invalid response";
|
||||
case constants.TIMEDOUT: return "Timedout";
|
||||
default: return "Disconnected";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,136 +1,120 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#motor-view-template',
|
||||
props: ['index', 'config', 'template', 'state'],
|
||||
template: "#motor-view-template",
|
||||
props: [ "index", "config", "template", "state" ],
|
||||
|
||||
computed: {
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
computed: {
|
||||
metric: function () {return this.$root.metric()},
|
||||
is_slave: function() {
|
||||
for (let i = 0; i < this.index; i++) {
|
||||
if (this.motor.axis == this.config.motors[i].axis) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
is_slave: function () {
|
||||
for (var i = 0; i < this.index; i++)
|
||||
if (this.motor.axis == this.config.motors[i].axis)
|
||||
return true;
|
||||
motor: function() {
|
||||
return this.config.motors[this.index];
|
||||
},
|
||||
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
events: {
|
||||
"input-changed": function() {
|
||||
Vue.nextTick(function() {
|
||||
// Limit max-velocity
|
||||
if (this.invalidMaxVelocity) {
|
||||
this.$set('motor["max-velocity"]', this.maxMaxVelocity);
|
||||
}
|
||||
|
||||
motor: function () {return this.config.motors[this.index]},
|
||||
//Limit stall-velocity
|
||||
if (this.invalidStallVelocity) {
|
||||
this.$set('motor["search-velocity"]', this.maxStallVelocity);
|
||||
}
|
||||
|
||||
this.$dispatch("config-changed");
|
||||
}.bind(this));
|
||||
|
||||
invalidMaxVelocity: function () {
|
||||
return this.maxMaxVelocity < this.motor['max-velocity'];
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
show: function(name, templ) {
|
||||
if (templ.hmodes == undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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() {
|
||||
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;
|
||||
return templ.hmodes.indexOf(this.motor["homing-mode"]) != -1;
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
Vue.nextTick(function () {
|
||||
// Limit max-velocity
|
||||
if (this.invalidMaxVelocity)
|
||||
this.$set('motor["max-velocity"]', this.maxMaxVelocity);
|
||||
|
||||
//Limit stall-velocity
|
||||
if(this.invalidStallVelocity)
|
||||
this.$set('motor["search-velocity"]', this.maxStallVelocity);
|
||||
|
||||
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
@@ -1,42 +1,14 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
module.exports = {
|
||||
template: '#settings-view-template',
|
||||
props: ['config', 'template'],
|
||||
template: "#settings-view-template",
|
||||
|
||||
attached: function() {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"SettingsView",
|
||||
document.getElementById("settings")
|
||||
);
|
||||
},
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
this.$dispatch('config-changed');
|
||||
return false;
|
||||
detached: function() {
|
||||
this.svelteComponent.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
189
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
|
||||
All rights reserved.
|
||||
if (typeof retry == "undefined") {
|
||||
retry = 2000;
|
||||
}
|
||||
if (typeof timeout == "undefined") {
|
||||
timeout = 16000;
|
||||
}
|
||||
|
||||
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/>.
|
||||
this.url = url;
|
||||
this.retry = retry;
|
||||
this.timeout = timeout;
|
||||
this.divisions = 4;
|
||||
this.count = 0;
|
||||
|
||||
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.
|
||||
this.connect();
|
||||
};
|
||||
|
||||
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/>.
|
||||
Sock.prototype.onmessage = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
Sock.prototype.onopen = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
\******************************************************************************/
|
||||
Sock.prototype.onclose = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
'use strict'
|
||||
Sock.prototype.connect = function() {
|
||||
console.debug("connecting to", this.url);
|
||||
this.close();
|
||||
|
||||
this._sock = new SockJS(this.url);
|
||||
|
||||
var Sock = function (url, retry, timeout) {
|
||||
if (!(this instanceof Sock)) return new Sock(url, retry);
|
||||
this._sock.onmessage = function(e) {
|
||||
console.debug("msg:", e.data);
|
||||
this.heartbeat("msg");
|
||||
this.onmessage(e);
|
||||
}.bind(this);
|
||||
|
||||
if (typeof retry == 'undefined') retry = 2000;
|
||||
if (typeof timeout == 'undefined') timeout = 16000;
|
||||
this._sock.onopen = function() {
|
||||
console.debug("connected");
|
||||
this.heartbeat("open");
|
||||
this.onopen();
|
||||
}.bind(this);
|
||||
|
||||
this.url = url;
|
||||
this.retry = retry;
|
||||
this.timeout = timeout;
|
||||
this.divisions = 4;
|
||||
this.count = 0;
|
||||
this._sock.onclose = function() {
|
||||
console.debug("disconnected");
|
||||
this._cancel_timeout();
|
||||
|
||||
this.connect();
|
||||
}
|
||||
this.onclose();
|
||||
if (typeof this._sock != "undefined") {
|
||||
setTimeout(this.connect.bind(this), this.retry);
|
||||
}
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
Sock.prototype._timedout = function() {
|
||||
// Divide timeout so slow browser doesn't trigger timeouts when the
|
||||
// connection is good.
|
||||
if (this.divisions <= ++this.count) {
|
||||
console.debug("connection timedout");
|
||||
this._timeout = undefined;
|
||||
this._sock.close();
|
||||
|
||||
Sock.prototype.onmessage = function () {}
|
||||
Sock.prototype.onopen = function () {}
|
||||
Sock.prototype.onclose = function () {}
|
||||
} else {
|
||||
this._set_timeout();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Sock.prototype.connect = function () {
|
||||
console.debug('connecting to', this.url);
|
||||
this.close();
|
||||
|
||||
this._sock = new SockJS(this.url);
|
||||
|
||||
this._sock.onmessage = function (e) {
|
||||
console.debug('msg:', e.data);
|
||||
this.heartbeat('msg');
|
||||
this.onmessage(e);
|
||||
}.bind(this);
|
||||
|
||||
|
||||
this._sock.onopen = function () {
|
||||
console.debug('connected');
|
||||
this.heartbeat('open');
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Sock.prototype._timedout = function () {
|
||||
// Divide timeout so slow browser doesn't trigger timeouts when the
|
||||
// connection is good.
|
||||
if (this.divisions <= ++this.count) {
|
||||
console.debug('connection timedout');
|
||||
Sock.prototype._cancel_timeout = function() {
|
||||
clearTimeout(this._timeout);
|
||||
this._timeout = undefined;
|
||||
this._sock.close();
|
||||
this.count = 0;
|
||||
};
|
||||
|
||||
} else this._set_timeout();
|
||||
}
|
||||
Sock.prototype._set_timeout = function() {
|
||||
this._timeout = setTimeout(this._timedout.bind(this),
|
||||
this.timeout / this.divisions);
|
||||
};
|
||||
|
||||
Sock.prototype.heartbeat = function() {
|
||||
this._cancel_timeout();
|
||||
this._set_timeout();
|
||||
};
|
||||
|
||||
Sock.prototype._cancel_timeout = function () {
|
||||
clearTimeout(this._timeout);
|
||||
this._timeout = undefined;
|
||||
this.count = 0;
|
||||
}
|
||||
Sock.prototype.close = function() {
|
||||
if (typeof this._sock != "undefined") {
|
||||
const sock = this._sock;
|
||||
this._sock = undefined;
|
||||
sock.close();
|
||||
}
|
||||
};
|
||||
|
||||
Sock.prototype.send = function(msg) {
|
||||
this._sock.send(msg);
|
||||
};
|
||||
|
||||
Sock.prototype._set_timeout = function () {
|
||||
this._timeout = setTimeout(this._timedout.bind(this),
|
||||
this.timeout / this.divisions);
|
||||
}
|
||||
|
||||
|
||||
Sock.prototype.heartbeat = function (msg) {
|
||||
//console.debug('heartbeat ' + new Date().toLocaleTimeString() + ' ' + msg);
|
||||
this._cancel_timeout();
|
||||
this._set_timeout();
|
||||
}
|
||||
|
||||
|
||||
Sock.prototype.close = function () {
|
||||
if (typeof this._sock != 'undefined') {
|
||||
var sock = this._sock;
|
||||
this._sock = undefined;
|
||||
sock.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Sock.prototype.send = function (msg) {this._sock.send(msg)}
|
||||
|
||||
|
||||
module.exports = Sock
|
||||
module.exports = Sock;
|
||||
|
||||
@@ -1,89 +1,69 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '#templated-input-template',
|
||||
props: ['name', 'model', 'template'],
|
||||
replace: true,
|
||||
template: "#templated-input-template",
|
||||
props: [ "name", "model", "template" ],
|
||||
|
||||
|
||||
data: function () {return {view: ''}},
|
||||
|
||||
|
||||
computed: {
|
||||
metric: function () {return this.$root.metric()},
|
||||
|
||||
|
||||
_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;
|
||||
data: function() {
|
||||
return { view: "" };
|
||||
},
|
||||
|
||||
computed: {
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
units: function () {
|
||||
return (this.metric || !this.template.iunit) ?
|
||||
this.template.unit : this.template.iunit;
|
||||
_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;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
_view: function() {
|
||||
this.view = this._view;
|
||||
},
|
||||
|
||||
title: function () {
|
||||
var s = 'Default ' + this.template.default + ' ' +
|
||||
(this.template.unit || '');
|
||||
if (typeof this.template.help != 'undefined')
|
||||
s = this.template.help + '\n' + s;
|
||||
return s;
|
||||
view: function() {
|
||||
if (this.template.scale && !this.metric) {
|
||||
this.model = this.view * this.template.scale;
|
||||
} else {
|
||||
this.model = this.view;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
this.view = this._view;
|
||||
},
|
||||
|
||||
methods: {
|
||||
change: function() {
|
||||
this.$dispatch("input-changed");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
watch: {
|
||||
_view: function () {this.view = this._view},
|
||||
|
||||
|
||||
view: function () {
|
||||
if (this.template.scale && !this.metric)
|
||||
this.model = this.view * this.template.scale;
|
||||
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.
|
||||
|
||||
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 api = require("./api");
|
||||
const modbus = require("./modbus.js");
|
||||
const merge = require("lodash.merge");
|
||||
|
||||
module.exports = {
|
||||
template: '#tool-view-template',
|
||||
props: ['config', 'template', 'state'],
|
||||
template: "#tool-view-template",
|
||||
props: [ "config", "template", "state" ],
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
address: 0,
|
||||
value: 0,
|
||||
toolList: [
|
||||
{
|
||||
id: "disabled",
|
||||
name: "Disabled"
|
||||
},
|
||||
{
|
||||
id: "router",
|
||||
type: "PWM Spindle",
|
||||
name: "Router (Makita, etc)"
|
||||
},
|
||||
{
|
||||
id: "laser",
|
||||
type: "PWM Spindle",
|
||||
name: "Laser (J Tech, etc)"
|
||||
},
|
||||
{
|
||||
id: "pwm",
|
||||
name: "PWM Spindle"
|
||||
},
|
||||
{
|
||||
id: "unsupported-separator",
|
||||
name: "Unsupported Tools",
|
||||
disabled: true,
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "huanyang-vfd",
|
||||
name: "Huanyang VFD",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "custom-modbus-vfd",
|
||||
name: "Custom Modbus VFD",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "ac-tech-vfd",
|
||||
name: "AC-Tech VFD",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "nowforever-vfd",
|
||||
name: "Nowforever VFD",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "delta-vfd",
|
||||
name: "Delta VFD015M21A (Beta)",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "yl600-vfd",
|
||||
name: "YL600, YL620, YL620-A VFD (Beta)",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "fr-d700-vfd",
|
||||
name: "FR-D700 (Beta)",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "sunfar-e300-vfd",
|
||||
name: "Sunfar E300 (Beta)",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "omron-mx2-vfd",
|
||||
name: "OMRON MX2",
|
||||
unsupported: true
|
||||
data: function() {
|
||||
return {
|
||||
address: 0,
|
||||
value: 0,
|
||||
toolList: [
|
||||
{
|
||||
id: "disabled",
|
||||
name: "Disabled"
|
||||
},
|
||||
{
|
||||
id: "router",
|
||||
type: "PWM Spindle",
|
||||
name: "Router (Makita, etc)"
|
||||
},
|
||||
{
|
||||
id: "laser",
|
||||
type: "PWM Spindle",
|
||||
name: "Laser (J Tech, etc)"
|
||||
},
|
||||
{
|
||||
id: "pwm",
|
||||
name: "PWM Spindle"
|
||||
},
|
||||
{
|
||||
id: "unsupported-separator",
|
||||
name: "Unsupported Tools",
|
||||
disabled: true,
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "huanyang-vfd",
|
||||
name: "Huanyang VFD",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "custom-modbus-vfd",
|
||||
name: "Custom Modbus VFD",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "ac-tech-vfd",
|
||||
name: "AC-Tech VFD",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "nowforever-vfd",
|
||||
name: "Nowforever VFD",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "delta-vfd",
|
||||
name: "Delta VFD015M21A (Beta)",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "yl600-vfd",
|
||||
name: "YL600, YL620, YL620-A VFD (Beta)",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "fr-d700-vfd",
|
||||
name: "FR-D700 (Beta)",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "sunfar-e300-vfd",
|
||||
name: "Sunfar E300 (Beta)",
|
||||
unsupported: true
|
||||
},
|
||||
{
|
||||
id: "omron-mx2-vfd",
|
||||
name: "OMRON MX2",
|
||||
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,58 +1,47 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
|
||||
props: ['value', 'precision', 'unit', 'iunit', 'scale'],
|
||||
replace: true,
|
||||
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
|
||||
props: [ "value", "precision", "unit", "iunit", "scale" ],
|
||||
|
||||
computed: {
|
||||
metric: {
|
||||
cache: false,
|
||||
get: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
metric: function () {return !this.$root.state.imperial},
|
||||
text: function() {
|
||||
let value = this.value;
|
||||
if (typeof value == "undefined") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!this.metric) {
|
||||
value /= this.scale;
|
||||
}
|
||||
|
||||
text: function () {
|
||||
var value = this.value;
|
||||
if (typeof value == 'undefined') return '';
|
||||
return (1 * value.toFixed(this.precision)).toLocaleString();
|
||||
}
|
||||
},
|
||||
|
||||
if (!this.metric) value /= this.scale;
|
||||
ready: function() {
|
||||
if (typeof this.precision == "undefined") {
|
||||
this.precision = 0;
|
||||
}
|
||||
|
||||
return (1 * value.toFixed(this.precision)).toLocaleString();
|
||||
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;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
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
@@ -0,0 +1,9 @@
|
||||
function clickFileInput(formClass) {
|
||||
const form = document.querySelector(`.${formClass}`);
|
||||
form.reset();
|
||||
form.querySelector("input").click();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
clickFileInput
|
||||
};
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
doctype html
|
||||
html(lang="en")
|
||||
head
|
||||
@@ -39,12 +12,17 @@ html(lang="en")
|
||||
style: include ../static/css/font-awesome.min.css
|
||||
style: include ../static/css/Audiowide.css
|
||||
style: include ../static/css/clusterize.css
|
||||
style: include ../svelte-components/node_modules/svelte-material-ui/bare.css
|
||||
style: include ../../build/http/svelte-components/smui.css
|
||||
style: include ../../build/http/svelte-components/style.css
|
||||
style: include:stylus ../stylus/style.styl
|
||||
|
||||
|
||||
body(v-cloak)
|
||||
#svelte-dialog-host
|
||||
|
||||
#overlay(v-if="status != 'connected'")
|
||||
span {{status}}
|
||||
|
||||
#layout
|
||||
a#menuLink.menu-link(href="#menu"): span
|
||||
|
||||
@@ -87,49 +65,43 @@ html(lang="en")
|
||||
li.pure-menu-heading
|
||||
a.pure-menu-link(href="#help") Help
|
||||
|
||||
button.pure-button.pure-button-primary(@click="confirmShutdown = true", style="width: 100%")
|
||||
.fa.fa-power-off
|
||||
message(:show.sync="confirmShutdown")
|
||||
h3(slot="header") Confirm shutdown?
|
||||
p(slot="body") Please wait for black screen before switching off power.
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="confirmShutdown = false") Cancel
|
||||
button.pure-button.button-success(@click="shutdown") Shutdown
|
||||
button.pure-button.button-success(@click="reboot") Restart
|
||||
|
||||
button.pure-button.pure-button-primary(@click="showShutdownDialog", style="width: 100%")
|
||||
.fa.fa-power-off
|
||||
|
||||
#main
|
||||
.header
|
||||
.header-content
|
||||
.banner
|
||||
img(src="/images/onefinity_logo.png")
|
||||
.title
|
||||
.fa.fa-thermometer-full(class="error",
|
||||
v-if="80 <= state.rpi_temp",
|
||||
title="Raspberry Pi temperature too high.")
|
||||
.subtitle
|
||||
| CNC Controller #[b {{state.demo ? 'Demo ' : ''}}]
|
||||
| v{{config.full_version}}
|
||||
a.upgrade-version(v-if="show_upgrade()", href="#admin-general")
|
||||
| Upgrade to v{{latestVersion}}
|
||||
.fa.fa-check(v-if="!show_upgrade() && latestVersion",
|
||||
title="Firmware up to date" style="font-size: inherit")
|
||||
.p {{get_ip_address()}} {{get_ssid()}}
|
||||
.nav-header
|
||||
.brand
|
||||
img(src="/images/onefinity_logo.png")
|
||||
.version
|
||||
div
|
||||
| Version: v{{config.full_version}}
|
||||
div
|
||||
| IP Address: {{config.ip}}
|
||||
div
|
||||
| WiFi: {{config.wifiName}}
|
||||
a.upgrade-link(v-if="show_upgrade()", href="#admin-general")
|
||||
| Upgrade to v{{latestVersion}}
|
||||
.fa.fa-exclamation-circle.upgrade-attention(v-if="show_upgrade()")
|
||||
|
||||
.estop(:class="{active: state.es}")
|
||||
estop(@click="estop")
|
||||
.pi-temp-warning
|
||||
.fa.fa-thermometer-full(class="error",
|
||||
v-if="80 <= state.rpi_temp",
|
||||
title="Raspberry Pi temperature too high.")
|
||||
|
||||
.video(title="Plug camera into USB.\n" +
|
||||
"Left click to toggle video size.\n" +
|
||||
"Right click to toggle crosshair.", @click="toggle_video",
|
||||
@contextmenu="toggle_crosshair", :class="video_size")
|
||||
.crosshair(v-if="crosshair")
|
||||
.vertical
|
||||
.horizontal
|
||||
.box
|
||||
img(src="/api/video")
|
||||
.whitespace
|
||||
|
||||
.clear
|
||||
.video(title="Plug camera into USB.\n" +
|
||||
"Left click to toggle video size.\n" +
|
||||
"Right click to toggle crosshair.", @click="toggle_video",
|
||||
@contextmenu="toggle_crosshair", :class="video_size")
|
||||
.crosshair(v-if="crosshair")
|
||||
.vertical
|
||||
.horizontal
|
||||
.box
|
||||
img(src="/api/video")
|
||||
|
||||
.estop(:class="{active: state.es}")
|
||||
estop(@click="estop")
|
||||
|
||||
.content(class="{{currentView}}-view")
|
||||
component(:is="currentView + '-view'", :index="index",
|
||||
@@ -205,10 +177,10 @@ html(lang="en")
|
||||
#templates: include ../../build/templates.pug
|
||||
iframe#download-target(style="display:none")
|
||||
|
||||
script: include ../static/js/jquery-1.11.3.min.js
|
||||
script: include ../static/js/vue.js
|
||||
script: include ../static/js/sockjs.min.js
|
||||
script: include ../static/js/clusterize.min.js
|
||||
script: include ../static/js/three.min.js
|
||||
script: include:browserify ../js/main.js
|
||||
script: include ../../build/http/svelte-components/index.js
|
||||
script: include ../static/js/ui.js
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#admin-general-view-template(type="text/x-template")
|
||||
#admin-general
|
||||
h2 Firmware
|
||||
@@ -45,9 +18,6 @@ script#admin-general-view-template(type="text/x-template")
|
||||
label.pure-button.pure-button-primary(@click="restore_config") Restore
|
||||
form.restore-config.file-upload
|
||||
input(type="file", accept=".json", @change="restore")
|
||||
message(:show.sync="configRestored")
|
||||
h3(slot="header") Success
|
||||
p(slot="body") Configuration restored.
|
||||
|
||||
button.pure-button.pure-button-primary(@click="confirmReset = true") Reset
|
||||
message(:show.sync="confirmReset")
|
||||
@@ -70,10 +40,6 @@ script#admin-general-view-template(type="text/x-template")
|
||||
button.pure-button(@click="confirmReset = false") Cancel
|
||||
button.pure-button.pure-button-primary(@click="reset") Reset
|
||||
|
||||
message(:show.sync="configReset")
|
||||
h3(slot="header") Success
|
||||
p(slot="body") Configuration reset.
|
||||
|
||||
h2 Debugging
|
||||
a(href="/api/log", target="_blank")
|
||||
button.pure-button.pure-button-primary View Log
|
||||
|
||||
@@ -1,130 +1,2 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#admin-network-view-template(type="text/x-template")
|
||||
#admin-network
|
||||
h2 Hostname
|
||||
.pure-form.pure-form-aligned
|
||||
.pure-control-group
|
||||
label(for="hostname") Hostname
|
||||
input(name="hostname", v-model="hostname", @keyup.enter="set_hostname")
|
||||
button.pure-button.pure-button-primary(@click="set_hostname") Set
|
||||
|
||||
message(:show.sync="hostnameSet")
|
||||
h3(slot="header") Hostname Set
|
||||
div(slot="body")
|
||||
p Hostname was successfuly set to #[strong {{hostname}}].
|
||||
p Rebooting to apply changes.
|
||||
p Redirecting to new hostname in {{redirectTimeout}} seconds.
|
||||
div(slot="footer")
|
||||
|
||||
h2 Remote SSH User
|
||||
.pure-form.pure-form-aligned
|
||||
.pure-control-group
|
||||
label(for="username") Username
|
||||
input(name="username", v-model="username", @keyup.enter="set_username")
|
||||
button.pure-button.pure-button-primary(@click="set_username") Set
|
||||
|
||||
.pure-form.pure-form-aligned
|
||||
.pure-control-group
|
||||
label(for="current") Current Password
|
||||
input(name="current", v-model="current", type="password")
|
||||
.pure-control-group
|
||||
label(for="pass1") New Password
|
||||
input(name="pass1", v-model="password", type="password")
|
||||
.pure-control-group
|
||||
label(for="pass2") New Password
|
||||
input(name="pass2", v-model="password2", type="password")
|
||||
button.pure-button.pure-button-primary(@click="set_password") Set
|
||||
|
||||
message(:show.sync="passwordSet")
|
||||
h3(slot="header") Password Set
|
||||
p(slot="body")
|
||||
|
||||
message(:show.sync="usernameSet")
|
||||
h3(slot="header") Username Set
|
||||
p(slot="body")
|
||||
|
||||
h2 Wifi Setup
|
||||
.pure-form.pure-form-aligned
|
||||
.pure-control-group
|
||||
label(for="wifi_mode") Mode
|
||||
select(name="wifi_mode", v-model="wifi_mode",
|
||||
title="Select client or access point mode")
|
||||
option(value="disabled") Disabled
|
||||
option(value="client") Client
|
||||
option(value="ap") Access Point
|
||||
button.pure-button.pure-button-primary(@click="wifiConfirm = true",
|
||||
v-if="wifi_mode == 'disabled'") Set
|
||||
|
||||
.pure-control-group(v-if="wifi_mode == 'ap'")
|
||||
label(for="wifi_ch") Channel
|
||||
select(name="wifi_ch", v-model="wifi_ch")
|
||||
each ch in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
option(value=ch)= ch
|
||||
|
||||
.pure-control-group(v-if="wifi_mode != 'disabled'")
|
||||
label(for="ssid") Network (SSID)
|
||||
input(name="ssid", v-model="wifi_ssid")
|
||||
|
||||
.pure-control-group(v-if="wifi_mode != 'disabled'")
|
||||
label(for="wifi_pass") Password
|
||||
input(name="wifi_pass", v-model="wifi_pass", type="password")
|
||||
button.pure-button.pure-button-primary(@click="wifiConfirm = true") Set
|
||||
|
||||
p(v-if="wifi_mode != 'disabled'").
|
||||
WARNING: WiFi may be unreliable in an electrically noisy environment
|
||||
such as a machine shop.
|
||||
|
||||
message.wifi-confirm(:show.sync="wifiConfirm")
|
||||
h3(slot="header") Configure Wifi and reboot?
|
||||
div(slot="body")
|
||||
p
|
||||
| After configuring the Wifi settings the controller will
|
||||
| automatically reboot.
|
||||
table
|
||||
tr
|
||||
th Mode
|
||||
td {{wifi_mode}}
|
||||
tr(v-if="wifi_mode == 'ap'")
|
||||
th Channel
|
||||
td {{wifi_ch}}
|
||||
tr(v-if="wifi_mode != 'disabled'")
|
||||
th SSID
|
||||
td {{wifi_ssid}}
|
||||
tr(v-if="wifi_mode != 'disabled'")
|
||||
th Auth
|
||||
td {{wifi_pass ? 'WPA2' : 'Open'}}
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="wifiConfirm = false") Cancel
|
||||
button.pure-button.button-success(@click="config_wifi") OK
|
||||
|
||||
message(:show.sync="rebooting")
|
||||
h3(slot="header") Rebooting
|
||||
p(slot="body") Please wait...
|
||||
div(slot="footer")
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#axis-control-template(type="text/x-template")
|
||||
svg(xmlns="http://www.w3.org/2000/svg",
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink",
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#cheat-sheet-view-template(type="text/x-template")
|
||||
// Modified from http://linuxcnc.org/docs/html/gcode.html
|
||||
- var base = 'http://linuxcnc.org/docs/html/gcode';
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#console-template(type="text/x-template")
|
||||
.console
|
||||
.toolbar
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#control-view-template(type="text/x-template")
|
||||
#control
|
||||
message(:show.sync="showGcodeMessage")
|
||||
@@ -36,109 +9,6 @@ script#control-view-template(type="text/x-template")
|
||||
|
||||
div(slot="footer")
|
||||
label Simulating {{(toolpath_progress || 0) | percent}}
|
||||
|
||||
message(:show.sync=`ask_home_msg`)
|
||||
h3(slot="header") Home Machine
|
||||
|
||||
div(slot="body")
|
||||
p Home the machine?
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="home()")
|
||||
| OK
|
||||
|
||||
button.pure-button(@click='ask_home_msg = false; ask_home = false')
|
||||
| Cancel
|
||||
|
||||
message(:show.sync=`ask_zero_xy_msg`)
|
||||
h3(slot="header") XY Origin
|
||||
|
||||
div(slot="body")
|
||||
p Move to XY origin?
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="goto_zero(1,1,0,0)")
|
||||
| Confirm
|
||||
|
||||
button.pure-button(@click='ask_zero_xy_msg = false')
|
||||
| Cancel
|
||||
|
||||
message(:show.sync=`ask_zero_z_msg`)
|
||||
h3(slot="header") Z Origin
|
||||
|
||||
div(slot="body")
|
||||
p Move to Z origin?
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="goto_zero(0,0,1,0)")
|
||||
| Confirm
|
||||
|
||||
button.pure-button(@click='ask_zero_z_msg = false')
|
||||
| Cancel
|
||||
|
||||
message(:show.sync=`show_probe_test_modal`)
|
||||
|
||||
h3(slot="header") Test probe connection
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
p Attach the probe magnet to the collet.
|
||||
p Touch the probe block to the bit.
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`show_probe_test_modal = false`)
|
||||
| Cancel
|
||||
|
||||
button.pure-button.button-success(
|
||||
:disabled=`!state.saw_probe_connected`
|
||||
@click=`finish_probe_test()`) Continue
|
||||
|
||||
message(:show.sync=`show_tool_diameter_modal`)
|
||||
h3(slot="header") Enter probe tool information
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
.pure-control-group
|
||||
label="{{metric ? 'Diameter (mm)' : 'Diameter (inches)'}}"
|
||||
input(v-model="tool_diameter_for_prompt", size="8")
|
||||
p
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`show_tool_diameter_modal = false`)
|
||||
| Cancel
|
||||
|
||||
button.pure-button.button-success(
|
||||
@click=`set_tool_diameter`)
|
||||
| Set
|
||||
|
||||
message(:show.sync=`state.show_probe_complete_modal`)
|
||||
h3(slot="header") Probing complete!
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
p Don't forget to put away the probe!
|
||||
div(v-if="state.goto_xy_zero_after_probe")
|
||||
p
|
||||
| The machine will now move
|
||||
br
|
||||
| to the X-Y zero point.
|
||||
p Watch your hands!
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button.button-success(@click=`$emit("finalize_probe")`)
|
||||
| Done
|
||||
|
||||
message(:show.sync=`state.show_probe_failed_modal`)
|
||||
h3(slot="header") Probing failed!
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
p Could not find the probe block during probing!
|
||||
p Make sure the tip of the bit is about 1/4" (~6mm) above the probe block, and try again.
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button.button-success(@click=`hide_probe_failed_modal()`)
|
||||
| OK
|
||||
|
||||
table(style="table-layout: fixed; width: 100%;")
|
||||
tr(style="height: fit-content;")
|
||||
@@ -151,55 +21,60 @@ script#control-view-template(type="text/x-template")
|
||||
col(style="width:100px")
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(-1,1,0,0)")
|
||||
button(@click="jog_fn(-1,1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(-135deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(0,1,0,0)") Y+
|
||||
button(@click="jog_fn(0,1,0,0)") Y+
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(1,1,0,0)")
|
||||
button(@click="jog_fn(1,1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(-45deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",,@click="jog_fn(0,0,1,0)") Z+
|
||||
button(,@click="jog_fn(0,0,1,0)") Z+
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(-1,0,0,0)") X-
|
||||
button(@click="jog_fn(-1,0,0,0)") X-
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="ask_zero_xy_msg = true")
|
||||
.fa.fa-bullseye(style="font-size: 172%")
|
||||
button(@click="showMoveToZeroDialog('xy')")
|
||||
.fa.fa-bullseye(style="font-size: 173%")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(1,0,0,0)") X+
|
||||
button(@click="jog_fn(1,0,0,0)") X+
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click='ask_zero_z_msg = true') Z0
|
||||
button(@click="showMoveToZeroDialog('z')") Z0
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(-1,-1,0,0)")
|
||||
button(@click="jog_fn(-1,-1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(135deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(0,-1,0,0)") Y-
|
||||
button(@click="jog_fn(0,-1,0,0)") Y-
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(1,-1,0,0)")
|
||||
button(@click="jog_fn(1,-1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(45deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(0,0,-1,0)") Z-
|
||||
button(@click="jog_fn(0,0,-1,0)") Z-
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button#jog_button_fine(style="height:100px;width:100px", @click=`set_jog_incr('fine')`) 0.1
|
||||
button(:style="getJogIncrStyle('fine')", @click="jog_incr = 'fine'")
|
||||
span {{jog_incr_amounts[display_units].fine}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button#jog_button_small(style="height:100px;width:100px", @click=`set_jog_incr('small')`) 1.0
|
||||
button(:style="getJogIncrStyle('small')", @click="jog_incr = 'small'")
|
||||
span {{jog_incr_amounts[display_units].small}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button#jog_button_medium(style="height:100px;width:100px", @click=`set_jog_incr('medium')`) 10
|
||||
button(:style="getJogIncrStyle('medium')", @click="jog_incr = 'medium'")
|
||||
span {{jog_incr_amounts[display_units].medium}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button#jog_button_large(style="height:100px;width:100px", @click=`set_jog_incr('large')`) 100
|
||||
button(:style="getJogIncrStyle('large')", @click="jog_incr = 'large'")
|
||||
span {{jog_incr_amounts[display_units].large}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
tr
|
||||
td(style="height:100px", align="center", colspan="2")
|
||||
button(:class="state['pw'] ? '' : 'load-on'",
|
||||
style="height:100px;width:200px",
|
||||
@click=`start_probe_test(prep_and_show_tool_diameter_modal)`)
|
||||
@click="showProbeDialog('xyz')")
|
||||
| Probe XYZ
|
||||
|
||||
td(style="height:100px", align="center", colspan="2")
|
||||
button(:class="state['pw'] ? '' : 'load-on'",
|
||||
style="height:100px;width:200px",
|
||||
@click=`start_probe_test(probe_z)`)
|
||||
@click="showProbeDialog('z')")
|
||||
| Probe Z
|
||||
|
||||
td(style="vertical-align: top;")
|
||||
@@ -211,8 +86,6 @@ script#control-view-template(type="text/x-template")
|
||||
th.offset Offset
|
||||
th.state State
|
||||
th.tstate Toolpath
|
||||
//th.tstate Min
|
||||
//th.tstate Max
|
||||
th.actions
|
||||
button.pure-button(disabled, style="height:60px;width:60px;display:none;")
|
||||
|
||||
@@ -234,79 +107,25 @@ script#control-view-template(type="text/x-template")
|
||||
td.state
|
||||
.fa(:class=`'fa-' + ${axis}.icon`)
|
||||
| {{#{axis}.state}}
|
||||
td.tstate(:class=`${axis}.tklass`, :title=`${axis}.toolmsg`, @click=`show_toolpath_msg('${axis}')`)
|
||||
td.tstate(:class=`${axis}.tklass`, :title=`${axis}.toolmsg`, @click=`showToolpathMessageDialog('${axis}')`)
|
||||
.fa(:class=`'fa-' + ${axis}.ticon`)
|
||||
| {{#{axis}.tstate}}
|
||||
//td.tstate: unit-value(:value=`${axis}.pathMin`, precision=4)
|
||||
//td.tstate: unit-value(:value=`${axis}.pathMax`, precision=4)
|
||||
|
||||
message(:show.sync=`toolpath_msg['${axis}']`)
|
||||
h3(slot="header") Tool path info {{'#{axis}' | upper}} axis
|
||||
|
||||
div(slot="body")
|
||||
p {{#{axis}.toolmsg}}
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`toolpath_msg['${axis}'] = false`)
|
||||
| OK
|
||||
|
||||
|
||||
th.actions
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title=`Set {{'${axis}' | upper}} axis position.`,
|
||||
@click=`show_set_position('${axis}')`,style="height:60px;width:60px")
|
||||
@click=`show_set_position('${axis}')`, style="height:60px;width:60px")
|
||||
.fa.fa-cog
|
||||
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title=`Zero {{'${axis}' | upper}} axis offset.`,
|
||||
@click=`zero('${axis}')`,style="height:60px;width:60px")
|
||||
@click=`zero('${axis}')`, style="height:60px;width:60px")
|
||||
.fa.fa-map-marker
|
||||
|
||||
button.pure-button(:disabled="!is_idle", @click=`home('${axis}')`,
|
||||
title=`Home {{'${axis}' | upper}} axis.`,style="height:60px;width:60px")
|
||||
title=`Home {{'${axis}' | upper}} axis.`, style="height:60px;width:60px")
|
||||
.fa.fa-home
|
||||
|
||||
message(:show.sync=`position_msg['${axis}']`)
|
||||
h3(slot="header") Set {{'#{axis}' | upper}} axis position
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
.pure-control-group
|
||||
label Position
|
||||
input(v-model="axis_position",
|
||||
@keyup.enter=`set_position('${axis}', axis_position)`)
|
||||
p
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`position_msg['${axis}'] = false`)
|
||||
| Cancel
|
||||
|
||||
button.pure-button(v-if=`${axis}.homed`,
|
||||
@click=`unhome('${axis}')`) Unhome
|
||||
|
||||
button.pure-button.button-success(
|
||||
@click=`set_position('${axis}', axis_position)`) Set
|
||||
|
||||
message(:show.sync=`manual_home['${axis}']`)
|
||||
h3(slot="header") Manually home {{'#{axis}' | upper}} axis
|
||||
|
||||
div(slot="body")
|
||||
p Set axis absolute position.
|
||||
|
||||
.pure-form
|
||||
.pure-control-group
|
||||
label Absolute
|
||||
input(v-model="axis_position",
|
||||
@keyup.enter=`set_home('${axis}', axis_position)`)
|
||||
|
||||
p
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`manual_home['${axis}'] = false`)
|
||||
| Cancel
|
||||
|
||||
button.pure-button.button-success(
|
||||
title=`Home {{'${axis}' | upper}} axis.`,
|
||||
@click=`set_home('${axis}', axis_position)`) Set
|
||||
|
||||
tr(style="vertical-align: top;")
|
||||
td
|
||||
@@ -323,10 +142,10 @@ script#control-view-template(type="text/x-template")
|
||||
td.message(:class="{attention: highlight_state}")
|
||||
| {{message.replace(/^#/, '')}}
|
||||
|
||||
tr(title="Active machine units")
|
||||
th Units
|
||||
td.mach_units
|
||||
select(v-model="mach_units", :disabled="!is_idle")
|
||||
tr
|
||||
th Display Units
|
||||
td.units
|
||||
select(v-model="display_units")
|
||||
option(value="METRIC") METRIC
|
||||
option(value="IMPERIAL") IMPERIAL
|
||||
|
||||
@@ -368,6 +187,11 @@ script#control-view-template(type="text/x-template")
|
||||
|
||||
td
|
||||
table.info
|
||||
tr
|
||||
th Current Time
|
||||
td
|
||||
span {{current_time}}
|
||||
|
||||
tr
|
||||
th Remaining
|
||||
td(title="Total run time (days:hours:mins:secs)").
|
||||
@@ -376,12 +200,7 @@ script#control-view-template(type="text/x-template")
|
||||
tr
|
||||
th ETA
|
||||
td.eta {{eta}}
|
||||
tr
|
||||
th Line
|
||||
td
|
||||
| {{0 <= state.line ? state.line : 0 | number}}
|
||||
span(v-if="toolpath.lines")
|
||||
| of {{toolpath.lines | number}}
|
||||
|
||||
tr
|
||||
th Progress
|
||||
td.progress
|
||||
@@ -484,6 +303,9 @@ script#control-view-template(type="text/x-template")
|
||||
|
||||
input(v-model="mdi", :disabled="!can_mdi", @keyup.enter="submit_mdi")
|
||||
|
||||
div
|
||||
em The machine is currently operating in #[strong {{mach_units}}] units. Use G20/G21 to switch units.
|
||||
|
||||
.history(:class="{placeholder: !history}")
|
||||
span(v-if="!history.length") MDI history displays here.
|
||||
ul
|
||||
@@ -497,9 +319,6 @@ script#control-view-template(type="text/x-template")
|
||||
section#content4.tab-content
|
||||
indicators(:state="state", :template="template")
|
||||
|
||||
|
||||
|
||||
|
||||
.override(title="Feed rate override.")
|
||||
label Feed
|
||||
input(type="range", min="0", max="2", step="0.01",
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#estop-template(type="text/x-template")
|
||||
svg(version="1.1", xmlns:svg="http://www.w3.org/2000/svg",
|
||||
xmlns="http://www.w3.org/2000/svg",
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#gcode-viewer-template(type="text/x-template")
|
||||
.gcode
|
||||
.clusterize
|
||||
|
||||
@@ -1,75 +1,2 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#help-view-template(type="text/x-template")
|
||||
#help
|
||||
h2 Contact
|
||||
p
|
||||
| You can contact us here
|
||||
|
|
||||
a(href="https://onefinitycnc.com/support", target="_blank")
|
||||
| onefinitycnc.com/support
|
||||
|.
|
||||
|
||||
h2 Discussion Forum
|
||||
p
|
||||
| Check out our support and discussion forum.
|
||||
|
|
||||
a(href="https://forum.onefinitycnc.com", target="_blank")
|
||||
| forum.onefinitycnc.com
|
||||
|. Register on the site and post a message. We can't wait to hear from you.
|
||||
|
||||
h2 Buildbotics
|
||||
p
|
||||
| This controller is based on the Buildbotics CNC Controller.
|
||||
| We encourage you to check out the
|
||||
a(href="http://buildbotics.com", target="_blank") Buildbotics Website
|
||||
|.
|
||||
|
||||
h2 CAD/CAM Software
|
||||
p
|
||||
a(href="http://wikipedia.com/wiki/Computer-aided_manufacturing",
|
||||
target="_blank") CAM
|
||||
|
|
||||
| software can be used to create GCode
|
||||
| automatically from
|
||||
|
|
||||
a(href="http://wikipedia.com/wiki/Computer-aided_design",
|
||||
target="_blank") CAD
|
||||
|
|
||||
| models. Here are a few CAD/CAM resources:
|
||||
ul
|
||||
li: a(href="http://camotics.org/", target="_blank")
|
||||
| CAMotics - Open-Source CNC Simulator
|
||||
li: a(href="http://librecad.org/", target="_blank")
|
||||
| LibreCAD - Open-Source 2D CAD
|
||||
li: a(href="https://www.freecadweb.org/", target="_blank")
|
||||
| FreeCAD - Open-Source 3D CAD
|
||||
li: a(href="http://www.openscad.org/", target="_blank")
|
||||
| OpenSCAD - Open-Source 3D CAD for programmers
|
||||
li: a(href="http://wiki.linuxcnc.org/cgi-bin/wiki.pl?Cam",
|
||||
target="_blank") LinuxCNC CAM resources
|
||||
#help
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#indicators-template(type="text/x-template")
|
||||
.indicators
|
||||
table.legend
|
||||
|
||||
@@ -1,29 +1,2 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#io-indicator-template(type="text/x-template")
|
||||
.fa.io(:class="klass", :title="tooltip")
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#io-view-template(type="text/x-template")
|
||||
#io
|
||||
h1 I/O Configuration
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#message-template(type="text/x-template")
|
||||
.modal-mask(v-if="show", :class="class")
|
||||
.modal-wrapper
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#modbus-reg-view-template(type="text/x-template")
|
||||
tr.modbus-reg
|
||||
td.reg-index {{index}}
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#motor-view-template(type="text/x-template")
|
||||
.motor(:class="{slave: is_slave}")
|
||||
h1 Motor {{index}} Configuration
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#path-viewer-template(type="text/x-template")
|
||||
.path-viewer(v-show="enabled", :class="{small: small}")
|
||||
.path-viewer-toolbar
|
||||
|
||||
@@ -1,85 +1,2 @@
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#settings-view-template(type="text/x-template")
|
||||
#settings
|
||||
h1 Settings
|
||||
|
||||
.pure-form.pure-form-aligned
|
||||
fieldset
|
||||
h2 Units
|
||||
templated-input(name="units", :model.sync="config.settings.units",
|
||||
:template="template.settings.units")
|
||||
|
||||
p
|
||||
| Note, #[tt units] sets both the machine default units and the
|
||||
| units used in motor configuration. GCode #[tt program-start],
|
||||
| set below, may also change the default machine units.
|
||||
|
||||
fieldset
|
||||
h2 Probing safety prompts
|
||||
templated-input(name="probing-prompts",
|
||||
:model.sync="config.settings['probing-prompts']",
|
||||
:template="template.settings['probing-prompts']")
|
||||
|
||||
fieldset
|
||||
h2 Probe Dimensions
|
||||
templated-input(v-for="templ in template.probe", :name="$key",
|
||||
:model.sync="config.probe[$key]", :template="templ")
|
||||
|
||||
fieldset
|
||||
h2 GCode
|
||||
templated-input(v-for="templ in template.gcode", :name="$key",
|
||||
:model.sync="config.gcode[$key]", :template="templ")
|
||||
|
||||
fieldset
|
||||
h2 Path Accuracy
|
||||
templated-input(name="max-deviation",
|
||||
:model.sync="config.settings['max-deviation']",
|
||||
:template="template.settings['max-deviation']")
|
||||
|
||||
p.
|
||||
Lower #[tt max-deviation] to follow the programmed path more precisely
|
||||
but at a slower speed.
|
||||
|
||||
p.
|
||||
In order to improve traversal speed, the path planner may merge
|
||||
consecutive moves or round off sharp corners if doing so would deviate
|
||||
from the program path by less than #[tt max-deviation].
|
||||
|
||||
- var base = '//linuxcnc.org/docs/html/gcode/g-code.html'
|
||||
p.
|
||||
GCode commands
|
||||
#[a(href=base + "#gcode:g61", target="_blank") G61, G61.1] and
|
||||
#[a(href=base + "#gcode:g64", target="_blank") G64] also affect path
|
||||
planning accuracy.
|
||||
|
||||
h2 Cornering Speed (Advanced)
|
||||
templated-input(name="junction-accel",
|
||||
:model.sync="config.settings['junction-accel']",
|
||||
:template="template.settings['junction-accel']")
|
||||
|
||||
p.
|
||||
Junction acceleration limits the cornering speed the planner will
|
||||
allow. Increasing this value will allow for faster traversal of
|
||||
corners but may cause the planner to violate axis jerk limits and
|
||||
stall the motors. Use with caution.
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#templated-input-template(type="text/x-template")
|
||||
.pure-control-group(class="tmpl-input-{{name}}", :title="title")
|
||||
label(:for="name") {{name}}
|
||||
@@ -38,22 +11,17 @@ script#templated-input-template(type="text/x-template")
|
||||
|
||||
input(v-if="template.type == 'float'", v-model.number="view", number,
|
||||
:min="template.min", :max="template.max", :step="template.step || 'any'",
|
||||
type="number", :name="name", @change="change")
|
||||
type="number", :name="name", @keyup="change")
|
||||
|
||||
input(v-if="template.type == 'int' && !template.values", number,
|
||||
v-model.number="view", :min="template.min", :max="template.max",
|
||||
type="number", :name="name", @change="change")
|
||||
type="number", :name="name", @keyup="change")
|
||||
|
||||
input(v-if="template.type == 'string'", v-model="view", type="text",
|
||||
:name="name", @change="change")
|
||||
:name="name", @keyup="change")
|
||||
|
||||
textarea(v-if="template.type == 'text'", v-model="view", :name="name",
|
||||
@change="change")
|
||||
|
||||
span.range(v-if="template.type == 'percent'")
|
||||
input(type="range", v-model="view", :name="name", number, min="0",
|
||||
max="100", step="1", @change="change")
|
||||
| {{view}}
|
||||
@keyup="change")
|
||||
|
||||
label.units {{units}}
|
||||
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#tool-view-template(type="text/x-template")
|
||||
#tool
|
||||
h1 Tool Configuration
|
||||
|
||||
@@ -1,52 +1,43 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of the Buildbotics firmware. #
|
||||
# #
|
||||
# Copyright (c) 2015 - 2018, Buildbotics LLC #
|
||||
# All rights reserved. #
|
||||
# #
|
||||
# This file ("the software") is free software: you can redistribute it #
|
||||
# and/or modify it under the terms of the GNU General Public License, #
|
||||
# version 2 as published by the Free Software Foundation. You should #
|
||||
# have received a copy of the GNU General Public License, version 2 #
|
||||
# along with the software. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# The software is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
|
||||
# Lesser General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU Lesser General Public #
|
||||
# License along with the software. If not, see #
|
||||
# <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# For information regarding this software email: #
|
||||
# "Joseph Coffland" <joseph@buildbotics.com> #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import bbctrl
|
||||
import glob
|
||||
import html
|
||||
import tornado
|
||||
from tornado import gen
|
||||
from tornado.web import HTTPError
|
||||
from tornado.escape import url_unescape
|
||||
|
||||
|
||||
def safe_remove(path):
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError: pass
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
@tornado.web.stream_request_body
|
||||
class FileHandler(bbctrl.APIHandler):
|
||||
def prepare(self): pass
|
||||
def prepare(self):
|
||||
if self.request.method == 'PUT':
|
||||
self.request.connection.set_max_body_size(2 ** 30)
|
||||
|
||||
filename = self.request.path.split('/')[-1]
|
||||
self.uploadFilename = url_unescape(filename) \
|
||||
.replace('\\', '/') \
|
||||
.replace('#', '-') \
|
||||
.replace('?', '-')
|
||||
|
||||
self.uploadFile = tempfile.NamedTemporaryFile("wb")
|
||||
|
||||
def data_received(self, data):
|
||||
if self.request.method == 'PUT':
|
||||
self.uploadFile.write(data)
|
||||
|
||||
def delete_ok(self, filename):
|
||||
if not filename:
|
||||
# Delete everything
|
||||
for path in glob.glob(self.get_upload('*')): safe_remove(path)
|
||||
for path in glob.glob(self.get_upload('*')):
|
||||
safe_remove(path)
|
||||
self.get_ctrl().preplanner.delete_all_plans()
|
||||
self.get_ctrl().state.clear_files()
|
||||
|
||||
@@ -57,26 +48,29 @@ class FileHandler(bbctrl.APIHandler):
|
||||
self.get_ctrl().preplanner.delete_plans(filename)
|
||||
self.get_ctrl().state.remove_file(filename)
|
||||
|
||||
|
||||
def put_ok(self, *args):
|
||||
gcode = self.request.files['gcode'][0]
|
||||
filename = os.path.basename(gcode['filename'].replace('\\', '/'))
|
||||
filename = filename.replace('#', '-').replace('?', '-')
|
||||
if not os.path.exists(self.get_upload()):
|
||||
os.mkdir(self.get_upload())
|
||||
|
||||
if not os.path.exists(self.get_upload()): os.mkdir(self.get_upload())
|
||||
filename = self.get_upload(self.uploadFilename).encode('utf8')
|
||||
safe_remove(filename)
|
||||
os.link(self.uploadFile.name, filename)
|
||||
|
||||
with open(self.get_upload(filename).encode('utf8'), 'wb') as f:
|
||||
f.write(gcode['body'])
|
||||
os.sync()
|
||||
self.uploadFile.close()
|
||||
|
||||
self.get_ctrl().preplanner.invalidate(filename)
|
||||
self.get_ctrl().state.add_file(filename)
|
||||
self.get_log('FileHandler').info('GCode received: ' + filename)
|
||||
del (self.uploadFile)
|
||||
|
||||
self.get_ctrl().preplanner.invalidate(self.uploadFilename)
|
||||
self.get_ctrl().state.add_file(self.uploadFilename)
|
||||
self.get_log('FileHandler').info(
|
||||
'GCode received: ' + self.uploadFilename)
|
||||
|
||||
del (self.uploadFilename)
|
||||
|
||||
@gen.coroutine
|
||||
def get(self, filename):
|
||||
if not filename: raise HTTPError(400, 'Missing filename')
|
||||
if not filename:
|
||||
raise HTTPError(400, 'Missing filename')
|
||||
filename = os.path.basename(filename)
|
||||
|
||||
try:
|
||||
@@ -84,6 +78,7 @@ class FileHandler(bbctrl.APIHandler):
|
||||
self.write(f.read())
|
||||
except Exception:
|
||||
self.get_ctrl().state.select_file('')
|
||||
raise HTTPError(400, "Unable to read file - doesn't appear to be GCode.")
|
||||
raise HTTPError(
|
||||
400, "Unable to read file - doesn't appear to be GCode.")
|
||||
|
||||
self.get_ctrl().state.select_file(filename)
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of the Buildbotics firmware. #
|
||||
# #
|
||||
# Copyright (c) 2015 - 2018, Buildbotics LLC #
|
||||
# All rights reserved. #
|
||||
# #
|
||||
# This file ("the software") is free software: you can redistribute it #
|
||||
# and/or modify it under the terms of the GNU General Public License, #
|
||||
# version 2 as published by the Free Software Foundation. You should #
|
||||
# have received a copy of the GNU General Public License, version 2 #
|
||||
# along with the software. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# The software is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
|
||||
# Lesser General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU Lesser General Public #
|
||||
# License along with the software. If not, see #
|
||||
# <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# For information regarding this software email: #
|
||||
# "Joseph Coffland" <joseph@buildbotics.com> #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import os
|
||||
import json
|
||||
import tornado
|
||||
@@ -34,9 +7,9 @@ import subprocess
|
||||
import socket
|
||||
from tornado.web import HTTPError
|
||||
from tornado import gen
|
||||
|
||||
import re
|
||||
import bbctrl
|
||||
|
||||
from urllib.request import urlopen
|
||||
|
||||
|
||||
def call_get_output(cmd):
|
||||
@@ -75,7 +48,7 @@ def check_password(password):
|
||||
class RebootHandler(bbctrl.APIHandler):
|
||||
def put_ok(self):
|
||||
self.get_ctrl().lcd.goodbye('Rebooting...')
|
||||
subprocess.Popen('reboot')
|
||||
subprocess.Popen(['reboot'])
|
||||
|
||||
class ShutdownHandler(bbctrl.APIHandler):
|
||||
def put_ok(self):
|
||||
@@ -153,6 +126,30 @@ class HostnameHandler(bbctrl.APIHandler):
|
||||
raise HTTPError(400, 'Failed to set hostname')
|
||||
|
||||
|
||||
class NetworkData(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
try:
|
||||
ipAddresses = subprocess.P(
|
||||
"ip -4 addr | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'", shell=True).decode().split()
|
||||
ipAddresses.remove("127.0.0.1")
|
||||
regex = re.compile(r'/255$/')
|
||||
filtered = [i for i in ipAddresses if not regex.match(i)]
|
||||
ipAddresses = filtered[0]
|
||||
except:
|
||||
ipAddresses = "Not Connected"
|
||||
try:
|
||||
wifi = subprocess.check_output(
|
||||
"sudo iw dev wlan0 info | grep ssid", shell=True).decode().split()
|
||||
wifi.pop(0)
|
||||
wifiName = " ".join(wifi)
|
||||
except:
|
||||
wifi = "not connected"
|
||||
self.write_json({
|
||||
'ipAddresses': ipAddresses,
|
||||
'wifi': wifiName
|
||||
})
|
||||
|
||||
class WifiHandler(bbctrl.APIHandler):
|
||||
def get(self):
|
||||
data = {'ssid': '', 'channel': 0}
|
||||
@@ -189,6 +186,7 @@ class WifiHandler(bbctrl.APIHandler):
|
||||
raise HTTPError(400, 'Failed to configure wifi')
|
||||
|
||||
|
||||
|
||||
class UsernameHandler(bbctrl.APIHandler):
|
||||
def get(self): self.write_json(get_username())
|
||||
|
||||
@@ -407,6 +405,97 @@ class JogHandler(bbctrl.APIHandler):
|
||||
self.get_ctrl().mach.jog(self.json)
|
||||
|
||||
|
||||
class ScreenRotationHandler(bbctrl.APIHandler):
|
||||
|
||||
@gen.coroutine
|
||||
def get(self):
|
||||
with open("/boot/config.txt", 'rt') as config:
|
||||
lines = config.readlines()
|
||||
for line in lines:
|
||||
if line.startswith('display_rotate'):
|
||||
self.write_json({
|
||||
'rotated':
|
||||
int(displayRotatePattern.search(line).group(1)) != 0
|
||||
})
|
||||
return
|
||||
|
||||
self.write_json({'rotated': False})
|
||||
return
|
||||
|
||||
@gen.coroutine
|
||||
def put_ok(self):
|
||||
rotated = self.json['rotated']
|
||||
|
||||
subprocess.Popen([
|
||||
'/usr/local/bin/edit-boot-config',
|
||||
'display_rotate={}'.format(2 if rotated else 0)
|
||||
])
|
||||
|
||||
with open("/usr/share/X11/xorg.conf.d/40-libinput.conf",
|
||||
'rt') as config:
|
||||
text = config.read()
|
||||
text = transformationMatrixPattern.sub(r'\1\2\3\5', text)
|
||||
if rotated:
|
||||
text = matchIsTouchscreenPattern.sub(
|
||||
r'\1\2\3\2Option "TransformationMatrix" "-1 0 1 0 -1 1 0 0 1"\1\4',
|
||||
text)
|
||||
with open("/usr/share/X11/xorg.conf.d/40-libinput.conf",
|
||||
'wt') as config:
|
||||
config.write(text)
|
||||
|
||||
subprocess.run('reboot')
|
||||
|
||||
|
||||
class TimeHandler(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
timeinfo = call_get_output(['timedatectl'])
|
||||
timezones = call_get_output(
|
||||
['timedatectl', 'list-timezones', '--no-pager'])
|
||||
self.get_log('TimeHandler').info('Time stuff: {}, {}'.format(
|
||||
timeinfo, timezones))
|
||||
|
||||
self.write_json({'timeinfo': timeinfo, 'timezones': timezones})
|
||||
|
||||
def put_ok(self):
|
||||
datetime = self.json['datetime']
|
||||
timezone = self.json['timezone']
|
||||
subprocess.Popen(['timedatectl', 'set-time', datetime])
|
||||
subprocess.Popen(['timedatectl', 'set-timezone', timezone])
|
||||
|
||||
|
||||
class RemoteDiagnosticsHandler(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
code = self.get_query_argument("code", "")
|
||||
command = self.get_query_argument("command", "")
|
||||
|
||||
log = self.get_log('RemoteDiagnostics')
|
||||
|
||||
if command == "disconnect":
|
||||
subprocess.Popen(['killall', 'ngrok'])
|
||||
self.write_json({'message': "Succesfully disconnected"})
|
||||
|
||||
if command == "connect":
|
||||
try:
|
||||
url = 'https://tinyurl.com/1f-remote?code={}'.format(code)
|
||||
with urlopen(url) as response:
|
||||
body = response.read()
|
||||
|
||||
os.makedirs("/tmp/ngrok", exist_ok=True)
|
||||
with open("/tmp/ngrok/1f-ngrok.sh", 'wb') as f:
|
||||
f.write(body)
|
||||
|
||||
subprocess.Popen(['/bin/bash', "/tmp/ngrok/1f-ngrok.sh"])
|
||||
self.write_json({'success': True})
|
||||
except Exception as e:
|
||||
log.info("Failed: {}".format(str(e)))
|
||||
self.write_json({
|
||||
'success': False,
|
||||
'code': e.code or None,
|
||||
'message': e.reason or "Unknown"
|
||||
})
|
||||
|
||||
# Base class for Web Socket connections
|
||||
class ClientConnection(object):
|
||||
def __init__(self, app):
|
||||
@@ -517,7 +606,8 @@ class Web(tornado.web.Application):
|
||||
(r'/api/reboot', RebootHandler),
|
||||
(r'/api/shutdown', ShutdownHandler),
|
||||
(r'/api/hostname', HostnameHandler),
|
||||
(r'/api/wifi', WifiHandler),
|
||||
(r'/api/wifi', NetworkData),
|
||||
(r'/api/network', WifiHandler),
|
||||
(r'/api/remote/username', UsernameHandler),
|
||||
(r'/api/remote/password', PasswordHandler),
|
||||
(r'/api/config/load', ConfigLoadHandler),
|
||||
@@ -544,6 +634,9 @@ class Web(tornado.web.Application):
|
||||
(r'/api/modbus/write', ModbusWriteHandler),
|
||||
(r'/api/jog', JogHandler),
|
||||
(r'/api/video', bbctrl.VideoHandler),
|
||||
(r'/api/screen-rotation', ScreenRotationHandler),
|
||||
(r'/api/time', TimeHandler),
|
||||
(r'/api/remote-diagnostics', RemoteDiagnosticsHandler),
|
||||
(r'/(.*)', StaticFileHandler,
|
||||
{'path': bbctrl.get_resource('http/'),
|
||||
'default_filename': 'index.html'}),
|
||||
|
||||
2
src/static/js/three.min.js
vendored
@@ -172,7 +172,7 @@ c.isSpriteMaterial?(t.diffuse.value=c.color,t.opacity.value=c.opacity,t.rotation
|
||||
b.envMap,a.flipEnvMap.value=b.envMap&&b.envMap.isCubeTexture?-1:1,a.reflectivity.value=b.reflectivity,a.refractionRatio.value=b.refractionRatio,a.maxMipLevel.value=Ca.get(b.envMap).__maxMipLevel);b.lightMap&&(a.lightMap.value=b.lightMap,a.lightMapIntensity.value=b.lightMapIntensity);b.aoMap&&(a.aoMap.value=b.aoMap,a.aoMapIntensity.value=b.aoMapIntensity);if(b.map)var c=b.map;else b.specularMap?c=b.specularMap:b.displacementMap?c=b.displacementMap:b.normalMap?c=b.normalMap:b.bumpMap?c=b.bumpMap:b.roughnessMap?
|
||||
c=b.roughnessMap:b.metalnessMap?c=b.metalnessMap:b.alphaMap?c=b.alphaMap:b.emissiveMap&&(c=b.emissiveMap);void 0!==c&&(c.isWebGLRenderTarget&&(c=c.texture),!0===c.matrixAutoUpdate&&c.updateMatrix(),a.uvTransform.value.copy(c.matrix))}function r(a,b){a.specular.value=b.specular;a.shininess.value=Math.max(b.shininess,1E-4);b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap);b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,1===b.side&&(a.bumpScale.value*=-1));b.normalMap&&(a.normalMap.value=
|
||||
b.normalMap,a.normalScale.value.copy(b.normalScale),1===b.side&&a.normalScale.value.negate());b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function v(a,b){a.roughness.value=b.roughness;a.metalness.value=b.metalness;b.roughnessMap&&(a.roughnessMap.value=b.roughnessMap);b.metalnessMap&&(a.metalnessMap.value=b.metalnessMap);b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap);b.bumpMap&&(a.bumpMap.value=
|
||||
b.bumpMap,a.bumpScale.value=b.bumpScale,1===b.side&&(a.bumpScale.value*=-1));b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),1===b.side&&a.normalScale.value.negate());b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias);b.envMap&&(a.envMapIntensity.value=b.envMapIntensity)}console.log("THREE.WebGLRenderer","96");a=a||{};var y=void 0!==a.canvas?a.canvas:document.createElementNS("http://www.w3.org/1999/xhtml",
|
||||
b.bumpMap,a.bumpScale.value=b.bumpScale,1===b.side&&(a.bumpScale.value*=-1));b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),1===b.side&&a.normalScale.value.negate());b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias);b.envMap&&(a.envMapIntensity.value=b.envMapIntensity)}a=a||{};var y=void 0!==a.canvas?a.canvas:document.createElementNS("http://www.w3.org/1999/xhtml",
|
||||
"canvas"),x=void 0!==a.context?a.context:null,w=void 0!==a.alpha?a.alpha:!1,G=void 0!==a.depth?a.depth:!0,D=void 0!==a.stencil?a.stencil:!0,O=void 0!==a.antialias?a.antialias:!1,S=void 0!==a.premultipliedAlpha?a.premultipliedAlpha:!0,E=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,z=void 0!==a.powerPreference?a.powerPreference:"default",A=null,B=null;this.domElement=y;this.context=null;this.sortObjects=this.autoClearStencil=this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.clippingPlanes=
|
||||
[];this.localClippingEnabled=!1;this.gammaFactor=2;this.physicallyCorrectLights=this.gammaOutput=this.gammaInput=!1;this.toneMappingWhitePoint=this.toneMappingExposure=this.toneMapping=1;this.maxMorphTargets=8;this.maxMorphNormals=4;var P=this,I=!1,F=null,L=null,M=null,Q=-1;var H=b=null;var U=!1;var V=null,Z=null,T=new aa,zc=new aa,Y=null,fa=0,X=y.width,N=y.height,W=1,cb=new aa(0,0,X,N),ha=new aa(0,0,X,N),ra=!1,pa=new od,ba=new Nf,qd=!1,Xd=!1,yc=new J,db=new p;try{w={alpha:w,depth:G,stencil:D,antialias:O,
|
||||
premultipliedAlpha:S,preserveDrawingBuffer:E,powerPreference:z};y.addEventListener("webglcontextlost",d,!1);y.addEventListener("webglcontextrestored",e,!1);var C=x||y.getContext("webgl",w)||y.getContext("experimental-webgl",w);if(null===C){if(null!==y.getContext("webgl"))throw Error("Error creating WebGL context with your selected attributes.");throw Error("Error creating WebGL context.");}void 0===C.getShaderPrecisionFormat&&(C.getShaderPrecisionFormat=function(){return{rangeMin:1,rangeMax:1,precision:1}})}catch(Lg){console.error("THREE.WebGLRenderer: "+
|
||||
|
||||
@@ -394,7 +394,7 @@
|
||||
var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]';
|
||||
|
||||
// detect devtools
|
||||
var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
|
||||
var devtools = false;
|
||||
|
||||
// UA sniffing for working around browser-specific quirks
|
||||
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
|
||||
@@ -9677,14 +9677,5 @@ var template = Object.freeze({
|
||||
|
||||
Vue.version = '1.0.17';
|
||||
|
||||
// devtools global hook
|
||||
/* istanbul ignore next */
|
||||
if (devtools) {
|
||||
devtools.emit('init', Vue);
|
||||
} else if ('development' !== 'production' && inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)) {
|
||||
console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools');
|
||||
}
|
||||
|
||||
return Vue;
|
||||
|
||||
}));
|
||||
@@ -4,6 +4,9 @@ body
|
||||
[v-cloak]
|
||||
display none
|
||||
|
||||
.menu-link
|
||||
z-index unset
|
||||
|
||||
tt
|
||||
color #000
|
||||
background #eee
|
||||
@@ -32,22 +35,62 @@ tt
|
||||
height 140px
|
||||
padding 0
|
||||
|
||||
.header-content
|
||||
max-width 90%
|
||||
margin auto
|
||||
text-align left
|
||||
.nav-header
|
||||
padding-left 60px
|
||||
display flex
|
||||
|
||||
.estop
|
||||
float right
|
||||
margin 5px
|
||||
.brand
|
||||
display flex
|
||||
flex-direction column
|
||||
align-self center
|
||||
white-space nowrap
|
||||
|
||||
img
|
||||
width 300px
|
||||
|
||||
.version
|
||||
font-size 18pt
|
||||
color #777
|
||||
display flex
|
||||
flex-direction column
|
||||
justify-content space-evenly
|
||||
border-left #777 2px solid;
|
||||
margin-left 15px
|
||||
padding 0px 10px
|
||||
font-weight bold
|
||||
|
||||
.upgrade-link
|
||||
margin-left 20px
|
||||
font-size 16pt
|
||||
align-self center
|
||||
color blue
|
||||
|
||||
.upgrade-attention
|
||||
color red
|
||||
font-size 18pt
|
||||
align-self center
|
||||
margin-left 5px
|
||||
|
||||
.pi-temp-warning
|
||||
align-self center
|
||||
font-size 30pt
|
||||
font-family Audiowide
|
||||
display inline
|
||||
margin 0 30px
|
||||
|
||||
.left
|
||||
color #444
|
||||
.right
|
||||
color #e5aa3d
|
||||
|
||||
.whitespace
|
||||
flex-grow 1
|
||||
|
||||
.video
|
||||
position relative
|
||||
float right
|
||||
width 174px
|
||||
height 130px
|
||||
margin 2px 5px
|
||||
border 2px solid #fff
|
||||
border 2px solid transparent
|
||||
border-radius 5px
|
||||
|
||||
&:hover
|
||||
@@ -87,29 +130,9 @@ tt
|
||||
width 100%
|
||||
height 100%
|
||||
|
||||
.banner
|
||||
float left
|
||||
padding-top 40px
|
||||
white-space nowrap
|
||||
|
||||
img
|
||||
vertical-align top
|
||||
|
||||
.title
|
||||
font-size 30pt
|
||||
font-family Audiowide
|
||||
display inline
|
||||
margin-right 0.5em
|
||||
|
||||
.left
|
||||
color #444
|
||||
.right
|
||||
color #e5aa3d
|
||||
|
||||
.subtitle
|
||||
font-size 18pt
|
||||
font-weight 100
|
||||
color #aaa
|
||||
.estop
|
||||
align-self center
|
||||
margin 0 30px
|
||||
|
||||
.error
|
||||
background red
|
||||
@@ -214,8 +237,6 @@ span.unit
|
||||
.pure-control-group
|
||||
label.units
|
||||
width 6em
|
||||
|
||||
label.units
|
||||
text-align left
|
||||
|
||||
textarea
|
||||
@@ -230,6 +251,7 @@ span.unit
|
||||
padding 0.7em 1em
|
||||
border-radius 3px
|
||||
display inline-block
|
||||
|
||||
|
||||
@keyframes blink
|
||||
50%
|
||||
@@ -267,6 +289,12 @@ span.unit
|
||||
// The jogging buttons, etc.
|
||||
.control-buttons button
|
||||
font-size 150%
|
||||
width 100px
|
||||
height 100px
|
||||
|
||||
.jog-units
|
||||
font-size initial
|
||||
margin-left 5px
|
||||
|
||||
&:first-child
|
||||
margin 0.5em 0
|
||||
@@ -335,6 +363,7 @@ span.unit
|
||||
.axis
|
||||
.name
|
||||
text-transform capitalize
|
||||
vertical-align middle
|
||||
|
||||
.name, .position
|
||||
font-size 24pt
|
||||
@@ -421,7 +450,7 @@ span.unit
|
||||
min-width 8em
|
||||
width 100%
|
||||
|
||||
.mach_units
|
||||
.units
|
||||
padding 0
|
||||
|
||||
select
|
||||
@@ -848,18 +877,6 @@ tt.save
|
||||
text-decoration none
|
||||
|
||||
|
||||
.upgrade-version
|
||||
display inline-block
|
||||
border-radius 4px
|
||||
padding 2px
|
||||
margin-left 0.5em
|
||||
color #555
|
||||
background-color #e5aa3d
|
||||
text-decoration none
|
||||
|
||||
&:hover
|
||||
color #fff
|
||||
|
||||
.modal-mask
|
||||
position fixed
|
||||
z-index 9998
|
||||
|
||||
24
src/svelte-components/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
48
src/svelte-components/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Svelte + TS + Vite
|
||||
|
||||
This template should help get you started developing with Svelte and TypeScript in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
|
||||
|
||||
## Need an official Svelte framework?
|
||||
|
||||
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
|
||||
|
||||
## Technical considerations
|
||||
|
||||
**Why use this over SvelteKit?**
|
||||
|
||||
- It brings its own routing solution which might not be preferable for some users.
|
||||
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
|
||||
`vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example.
|
||||
|
||||
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
|
||||
|
||||
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
|
||||
|
||||
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
|
||||
|
||||
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
|
||||
|
||||
**Why include `.vscode/extensions.json`?**
|
||||
|
||||
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
|
||||
|
||||
**Why enable `allowJs` in the TS template?**
|
||||
|
||||
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
|
||||
|
||||
**Why is HMR not preserving my local component state?**
|
||||
|
||||
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
|
||||
|
||||
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
|
||||
|
||||
```ts
|
||||
// store.ts
|
||||
// An extremely simple external store
|
||||
import { writable } from 'svelte/store'
|
||||
export default writable(0)
|
||||
```
|
||||
13
src/svelte-components/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Svelte + TS + Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
10382
src/svelte-components/package-lock.json
generated
Normal file
31
src/svelte-components/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "svelte-components",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"postbuild": "smui-theme compile dist/smui.css -i src/theme",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/kit": "^1.0.0-next.392",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"node-sass": "^7.0.1",
|
||||
"polyfill-object.fromentries": "^1.0.1",
|
||||
"smui-theme": "^6.0.0-beta.16",
|
||||
"string.prototype.matchall": "^4.0.7",
|
||||
"svelte": "^3.49.0",
|
||||
"svelte-check": "^2.8.0",
|
||||
"svelte-icon": "^1.2.4",
|
||||
"svelte-material-ui": "^6.0.0-beta.16",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"svelte-tiny-virtual-list": "^2.0.5",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^3.0.2"
|
||||
}
|
||||
}
|
||||
BIN
src/svelte-components/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
215
src/svelte-components/src/components/AdminNetworkView.svelte
Normal file
@@ -0,0 +1,215 @@
|
||||
<script lang="ts">
|
||||
import WifiConnectionDialog from "$dialogs/WifiConnectionDialog.svelte";
|
||||
import ChangeHostnameDialog from "$dialogs/ChangeHostnameDialog.svelte";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import List, { Item, Graphic, Text, Meta } from "@smui/list";
|
||||
import Card from "@smui/card";
|
||||
import { networkInfo, type WifiNetwork } from "$lib/NetworkInfo";
|
||||
import {GET} from "$lib/api"
|
||||
import {processNetworkInfo} from "$lib/NetworkInfo"
|
||||
|
||||
let networkData = GET("network")
|
||||
networkData.then(value=>processNetworkInfo(value))
|
||||
|
||||
let changeHostnameDialog = {
|
||||
open: false,
|
||||
};
|
||||
|
||||
let wifiConnectionDialog = {
|
||||
open: false,
|
||||
network: {} as WifiNetwork,
|
||||
};
|
||||
|
||||
function getWifiStrengthStyle(network: WifiNetwork) {
|
||||
const strength = Math.ceil((Number(network.Quality) / 100) * 4);
|
||||
|
||||
switch (strength) {
|
||||
case 0:
|
||||
return "clip-path: circle(0px at 12.5px 19px);";
|
||||
|
||||
case 1:
|
||||
return "clip-path: circle(4px at 12.5px 19px);";
|
||||
|
||||
case 2:
|
||||
return "clip-path: circle(8px at 12.5px 19px);";
|
||||
|
||||
case 3:
|
||||
return "clip-path: circle(14px at 12.5px 19px);";
|
||||
|
||||
case 4:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeHostname() {
|
||||
changeHostnameDialog = {
|
||||
open: true,
|
||||
};
|
||||
}
|
||||
|
||||
function onNetworkSelected(network: WifiNetwork) {
|
||||
wifiConnectionDialog = {
|
||||
open: true,
|
||||
network,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<WifiConnectionDialog {...wifiConnectionDialog} />
|
||||
<ChangeHostnameDialog {...changeHostnameDialog} />
|
||||
|
||||
<div class="admin-network-view">
|
||||
<h1>Network Info</h1>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-control-group">
|
||||
<label for="hostname">Hostname</label>
|
||||
<Card id="hostname" variant="outlined">
|
||||
<Text id="hostname">
|
||||
{$networkInfo.hostname}
|
||||
</Text>
|
||||
</Card>
|
||||
<Button on:click={onChangeHostname} touch variant="raised">
|
||||
<Label>Change</Label>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-control-group">
|
||||
<label for="ip-addresses">IP Addresses</label>
|
||||
<Card id="ip-addresses" variant="outlined">
|
||||
{#each $networkInfo.ipAddresses as ipAddress}
|
||||
<div>
|
||||
<Text id="hostname">
|
||||
{ipAddress}
|
||||
</Text>
|
||||
</div>
|
||||
{/each}
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-control-group">
|
||||
<label for="wifi">Wi-Fi</label>
|
||||
<div class="wifi-networks">
|
||||
<Card id="wifi" variant="outlined">
|
||||
<List>
|
||||
{#if $networkInfo.wifi.networks.length === 0}
|
||||
<Item class="wifi-network">
|
||||
<Text>Scanning...</Text>
|
||||
</Item>
|
||||
{:else}
|
||||
{#each $networkInfo.wifi.networks as network}
|
||||
<Item
|
||||
class="wifi-network"
|
||||
on:SMUI:action={() =>
|
||||
onNetworkSelected(network)}
|
||||
>
|
||||
<Graphic
|
||||
class="strength {$networkInfo.wifi
|
||||
.ssid === network.Name
|
||||
? 'active'
|
||||
: ''}"
|
||||
>
|
||||
<span class="fa fa-wifi background" />
|
||||
<span
|
||||
class="fa fa-wifi"
|
||||
style={getWifiStrengthStyle(
|
||||
network
|
||||
)}
|
||||
/>
|
||||
</Graphic>
|
||||
<Text style="margin-right: 20px;"
|
||||
>{network.Name}</Text
|
||||
>
|
||||
{#if network.Encryption !== "Open"}
|
||||
<Meta>
|
||||
<span class="fa fa-lock" />
|
||||
</Meta>
|
||||
{/if}
|
||||
</Item>
|
||||
{/each}
|
||||
{/if}
|
||||
</List>
|
||||
</Card>
|
||||
<em style="display: block;">
|
||||
Click on a Wi-Fi network to connect or disconnect.
|
||||
</em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
$primary: #0078e7;
|
||||
$very-dark: #555;
|
||||
$text: #777;
|
||||
$grey: #bbb;
|
||||
$light: #ddd;
|
||||
|
||||
:global {
|
||||
.admin-network-view {
|
||||
.pure-form-aligned .pure-control-group label {
|
||||
vertical-align: top;
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mdc-card {
|
||||
width: 400px;
|
||||
min-height: 38px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-bottom: 20px;
|
||||
margin-right: 20px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.wifi-networks {
|
||||
display: inline-block;
|
||||
|
||||
.mdc-card {
|
||||
padding: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.wifi-network {
|
||||
.lock {
|
||||
font-size: 20px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.strength {
|
||||
border-radius: 50%;
|
||||
padding: 3px;
|
||||
background-color: $light;
|
||||
color: $very-dark;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
background-color: $primary;
|
||||
color: white;
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
font-size: 22px;
|
||||
|
||||
&.background {
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
196
src/svelte-components/src/components/ConfigTemplatedInput.svelte
Normal file
@@ -0,0 +1,196 @@
|
||||
<script lang="ts">
|
||||
import configTemplate from "../../../resources/config-template.json";
|
||||
import { Config, DisplayUnits } from "$lib/ConfigStore";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
type ValueType =
|
||||
| string
|
||||
| number
|
||||
| { title: string; value: string | number };
|
||||
|
||||
type Template = {
|
||||
type?: string;
|
||||
values?: Array<ValueType>;
|
||||
unit?: "string";
|
||||
iunit?: "string";
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
help?: string;
|
||||
default?: string | number;
|
||||
scale?: number;
|
||||
};
|
||||
|
||||
const namesByKey = {
|
||||
"gamepad-default-type": "Default type",
|
||||
"probing-prompts": "Show safety prompts",
|
||||
"probe-xdim": "Probe block width",
|
||||
"probe-ydim": "Probe block length",
|
||||
"probe-zdim": "Probe block height",
|
||||
"probe-fast-seek": "Fast seek speed",
|
||||
"probe-slow-seek": "Slow seek speed",
|
||||
"program-start": "On program start",
|
||||
"tool-change": "On tool change",
|
||||
"program-end": "On program end",
|
||||
"max-deviation": "Maximum deviation",
|
||||
"junction-accel": "Junction acceleration",
|
||||
};
|
||||
|
||||
export let key: string;
|
||||
let keyParts: string[];
|
||||
let template: Template;
|
||||
let name: string;
|
||||
let title: string;
|
||||
let units: string;
|
||||
let value;
|
||||
|
||||
onMount(() => {
|
||||
keyParts = (key || "").split(".");
|
||||
template = getTemplate();
|
||||
|
||||
name = keyParts[keyParts.length - 1];
|
||||
name = namesByKey[name] || name;
|
||||
title = getTitle();
|
||||
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(event) {
|
||||
Config.update((config) => {
|
||||
let target = config;
|
||||
for (const part of keyParts.slice(0, -1)) {
|
||||
target = target[part];
|
||||
}
|
||||
|
||||
const value = getValueFromElement(event.target);
|
||||
target[keyParts[keyParts.length - 1]] = value;
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
ControllerMethods.dispatch("config-changed");
|
||||
}
|
||||
|
||||
function getValueFromElement(element) {
|
||||
switch (template.type) {
|
||||
case "float":
|
||||
case "int":
|
||||
return Number(element.value);
|
||||
|
||||
case "bool":
|
||||
return element.checked;
|
||||
|
||||
default:
|
||||
return element.value;
|
||||
}
|
||||
}
|
||||
|
||||
function getOptionValue(opt: ValueType) {
|
||||
switch (typeof opt) {
|
||||
case "object":
|
||||
return opt.value || opt;
|
||||
|
||||
default:
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
|
||||
function getOptionTitle(opt: ValueType) {
|
||||
switch (typeof opt) {
|
||||
case "object":
|
||||
return opt.title || opt;
|
||||
|
||||
default:
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if template}
|
||||
<div class="pure-control-group" {title}>
|
||||
<label for={name}>{name}</label>
|
||||
|
||||
{#if template.values}
|
||||
<select {name} bind:value on:change={onChange}>
|
||||
{#each template.values as opt}
|
||||
<option
|
||||
value={getOptionValue(opt)}
|
||||
disabled={opt === "-----"}
|
||||
>
|
||||
{getOptionTitle(opt)}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else if template.type === "bool"}
|
||||
<input
|
||||
{name}
|
||||
type="checkbox"
|
||||
checked={value}
|
||||
on:change={onChange}
|
||||
/>
|
||||
{:else if template.type === "float"}
|
||||
<input
|
||||
{name}
|
||||
type="number"
|
||||
min={template.min}
|
||||
max={template.max}
|
||||
step={template.step || "any"}
|
||||
bind:value
|
||||
on:keyup={onChange}
|
||||
/>
|
||||
{:else if template.type === "int"}
|
||||
<input
|
||||
{name}
|
||||
type="number"
|
||||
min={template.min}
|
||||
max={template.max}
|
||||
bind:value
|
||||
on:keyup={onChange}
|
||||
/>
|
||||
{:else if template.type === "string"}
|
||||
<input {name} type="text" bind:value on:keyup={onChange} />
|
||||
{:else if template.type == "text"}
|
||||
<textarea {name} bind:value on:keyup={onChange} />
|
||||
{/if}
|
||||
|
||||
<label for="" class="units">{units || ""}</label>
|
||||
|
||||
<slot name="extra" />
|
||||
</div>
|
||||
{/if}
|
||||
62
src/svelte-components/src/components/HelpView.svelte
Normal file
@@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import RemoteDiagnosticsDialog from "$dialogs/RemoteDiagnosticsDialog.svelte";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
let showRemoteDiagnosticsDialog = false;
|
||||
</script>
|
||||
|
||||
<RemoteDiagnosticsDialog bind:open={showRemoteDiagnosticsDialog} />
|
||||
|
||||
<h2>Support & Contact Info</h2>
|
||||
<p>
|
||||
Please visit
|
||||
<a href="https://onefinitycnc.com/support" target="_blank">
|
||||
onefinitycnc.com/support
|
||||
</a>
|
||||
for a variety of support resources, and to find our contact information.
|
||||
</p>
|
||||
|
||||
<Button
|
||||
touch
|
||||
variant="raised"
|
||||
on:click={() => (showRemoteDiagnosticsDialog = true)}
|
||||
>
|
||||
<Label>Remote Diagnostics</Label>
|
||||
</Button>
|
||||
|
||||
<h2>Discussion Forum</h2>
|
||||
<p>
|
||||
Check out our support and discussion forum at
|
||||
<a href="https://forum.onefinitycnc.com" target="_blank"
|
||||
>forum.onefinitycnc.com</a
|
||||
>. Register on the site and post a message. We can't wait to hear from you.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
We also maintain a list of
|
||||
<a
|
||||
href="https://forum.onefinitycnc.com/t/what-cad-cam-software-can-be-used-to-make-gcode-files-for-the-onefinity-cnc/10253"
|
||||
target="_blank"
|
||||
>
|
||||
recommended software packages
|
||||
</a>
|
||||
on the forum.
|
||||
</p>
|
||||
|
||||
<h2>Credits & Acknowledgements</h2>
|
||||
<h4 style="margin-bottom: 0;">Artwork</h4>
|
||||
<p style="margin-top: 0;">
|
||||
Special thanks to
|
||||
<a href="https://www.instagram.com/fierysquirrelart/" target="_blank">
|
||||
@fierysquirrelart
|
||||
</a>
|
||||
for many of the graphics used in the controller.
|
||||
</p>
|
||||
|
||||
<h4 style="margin-bottom: 0;">Buildbotics</h4>
|
||||
<p style="margin-top: 0;">
|
||||
This controller is based on the
|
||||
<a href="http://buildbotics.com" target="_blank"
|
||||
>Buildbotics CNC Controller</a
|
||||
>.
|
||||
</p>
|
||||
129
src/svelte-components/src/components/SettingsView.svelte
Normal file
@@ -0,0 +1,129 @@
|
||||
<script lang="ts">
|
||||
import configTemplate from "../../../resources/config-template.json";
|
||||
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
||||
import ConfigTemplatedInput from "./ConfigTemplatedInput.svelte";
|
||||
import SetTimeDialog from "$dialogs/SetTimeDialog.svelte";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
const gcodeURL = "https://linuxcnc.org/docs/html/gcode/g-code.html";
|
||||
|
||||
let showScreenRotationDialog = false;
|
||||
let showSetTimeDialog = false;
|
||||
</script>
|
||||
|
||||
<ScreenRotationDialog bind:open={showScreenRotationDialog} />
|
||||
<SetTimeDialog bind:open={showSetTimeDialog} />
|
||||
|
||||
<div class="settings-view">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<h2>User Interface</h2>
|
||||
<fieldset>
|
||||
<div class="pure-control-group">
|
||||
<label for="screen-rotation" />
|
||||
<Button
|
||||
name="screen-rotation"
|
||||
touch
|
||||
variant="raised"
|
||||
on:click={() => (showScreenRotationDialog = true)}
|
||||
>
|
||||
<Label>Change Screen Rotation</Label>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group">
|
||||
<label for="set-time" />
|
||||
<Button
|
||||
name="set-time"
|
||||
touch
|
||||
variant="raised"
|
||||
on:click={() => (showSetTimeDialog = true)}
|
||||
>
|
||||
<Label>Change Time & Timezone</Label>
|
||||
</Button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<h2>Gamepads / Joypads</h2>
|
||||
<fieldset>
|
||||
<ConfigTemplatedInput key={`settings.gamepad-default-type`} />
|
||||
<div class="tip">
|
||||
If your gamepad doesn't work as expected, try one of the other
|
||||
types.
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<h2>Probing</h2>
|
||||
<fieldset>
|
||||
<ConfigTemplatedInput key={`settings.probing-prompts`} />
|
||||
<div class="tip">
|
||||
Onefinity highly recommends that you keep the safety prompts
|
||||
enabled. If you choose to live dangerously, and disable the
|
||||
safety prompts, Onefinity cannot be held responsible.
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
{#each Object.keys(configTemplate.probe) as key}
|
||||
{#if key !== "probe-diameter"}
|
||||
<ConfigTemplatedInput key={`probe.${key}`} />
|
||||
{/if}
|
||||
{/each}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<h2>GCode</h2>
|
||||
{#each Object.keys(configTemplate.gcode) as key}
|
||||
<ConfigTemplatedInput key={`gcode.${key}`} />
|
||||
{/each}
|
||||
</fieldset>
|
||||
|
||||
<h2>Path Accuracy</h2>
|
||||
<fieldset>
|
||||
<ConfigTemplatedInput key={`settings.max-deviation`} />
|
||||
|
||||
<div class="tip">
|
||||
Lower the maximum deviation to follow the programmed path more
|
||||
precisely but at a slower speed.
|
||||
</div>
|
||||
|
||||
<div class="tip">
|
||||
In order to improve traversal speed, the path planner may merge
|
||||
consecutive moves or round off sharp corners if doing so would
|
||||
deviate from the program path by less than the maximum
|
||||
deviation.
|
||||
</div>
|
||||
|
||||
<div class="tip">
|
||||
GCode commands
|
||||
<a href={`${gcodeURL}#gcode:g61`} target="_blank">G61, G61.1</a>
|
||||
and <a href={`${gcodeURL}#gcode:g64`} target="_blank">G64</a> also
|
||||
affect path planning accuracy.
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<h2>Cornering Speed (Advanced)</h2>
|
||||
<fieldset>
|
||||
<ConfigTemplatedInput key={`settings.junction-accel`} />
|
||||
<div class="tip">
|
||||
Junction acceleration limits the cornering speed the planner
|
||||
will allow. Increasing this value will allow for faster
|
||||
traversal of corners but may cause the planner to violate axis
|
||||
jerk limits and stall the motors. Use with caution.
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.settings-view {
|
||||
.tip {
|
||||
margin-left: 210px;
|
||||
margin-bottom: 15px;
|
||||
font-style: italic;
|
||||
font-size: 90%;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
import TextField from "@smui/textfield";
|
||||
import Icon from "@smui/textfield/icon";
|
||||
import HelperText from "@smui/textfield/helper-text";
|
||||
import MenuSurface, {
|
||||
type MenuSurfaceComponentDev,
|
||||
} from "@smui/menu-surface";
|
||||
import List, { Item, Text } from "@smui/list";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
let menuSurface: MenuSurfaceComponentDev;
|
||||
let menuTimeout;
|
||||
let optionSelected: boolean = false;
|
||||
|
||||
export let value: string;
|
||||
export let options: string[][];
|
||||
export let valid: boolean;
|
||||
export let helperText: string;
|
||||
|
||||
onDestroy(() => {
|
||||
if (menuTimeout) {
|
||||
clearTimeout(menuTimeout);
|
||||
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);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="textfield-with-options">
|
||||
<TextField
|
||||
bind:value
|
||||
on:focusin={() => showMenu(true)}
|
||||
on:focusout={() => showMenu(false)}
|
||||
use={[virtualKeyboardChange((v) => (value = v))]}
|
||||
{...$$restProps}
|
||||
>
|
||||
<div slot="trailingIcon">
|
||||
{#if valid}
|
||||
<Icon class="fa fa-check-circle-o" style="color: green;" />
|
||||
{/if}
|
||||
</div>
|
||||
<HelperText persistent slot="helper">{helperText}</HelperText>
|
||||
</TextField>
|
||||
|
||||
<MenuSurface bind:this={menuSurface} anchorCorner="BOTTOM_LEFT">
|
||||
<div style="display: flex; flex-direction: row;">
|
||||
{#each options as group}
|
||||
<List>
|
||||
{#each group as option}
|
||||
<Item
|
||||
on:SMUI:action={() => {
|
||||
value = option;
|
||||
showMenu(false);
|
||||
|
||||
optionSelected = true;
|
||||
}}
|
||||
>
|
||||
<Text>{option}</Text>
|
||||
</Item>
|
||||
{/each}
|
||||
</List>
|
||||
{/each}
|
||||
</div>
|
||||
</MenuSurface>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
:global {
|
||||
.textfield-with-options {
|
||||
.mdc-deprecated-list-item {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
101
src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte
Normal file
@@ -0,0 +1,101 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} 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
|
||||
//
|
||||
// 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
|
||||
// characters long. Valid characters for hostnames are ASCII(7)
|
||||
// letters from a to z, the digits from 0 to 9, and the hyphen (-).
|
||||
// A hostname may not start with a hyphen.
|
||||
|
||||
const pattern = /[a-zA-Z0-9][a-zA-Z0-9-]{0,62}/;
|
||||
|
||||
export let open = false;
|
||||
|
||||
let rebooting = false;
|
||||
let redirectTimeout = 45;
|
||||
let hostname = "";
|
||||
|
||||
$: setTimeout(() => {
|
||||
hostname = (hostname.match(pattern) || [""])[0].toLowerCase();
|
||||
}, 0);
|
||||
|
||||
$: if (open) {
|
||||
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")) {
|
||||
return `${hostname}.lan`;
|
||||
}
|
||||
|
||||
return hostname;
|
||||
}
|
||||
</script>
|
||||
|
||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||
Rebooting to apply the hostname change...
|
||||
</MessageDialog>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="change-hostname-dialog-title"
|
||||
aria-describedby="change-hostname-dialog-content"
|
||||
>
|
||||
<Title id="change-hostname-dialog-title">Change Hostname</Title>
|
||||
|
||||
<Content id="change-hostname-dialog-content">
|
||||
<TextField
|
||||
bind:value={hostname}
|
||||
use={[InitialFocus, virtualKeyboardChange((v) => (hostname = v))]}
|
||||
label="New Hostname"
|
||||
spellcheck="false"
|
||||
variant="filled"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
<Button
|
||||
defaultAction
|
||||
on:click={onConfirm}
|
||||
disabled={hostname.length === 0}
|
||||
>
|
||||
<Label>Confirm & Reboot</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
244
src/svelte-components/src/dialogs/DialogHost.svelte
Normal file
@@ -0,0 +1,244 @@
|
||||
<script lang="ts" context="module">
|
||||
import { writable } from "svelte/store";
|
||||
import HomeMachineDialog from "$dialogs/HomeMachineDialog.svelte";
|
||||
import ProbeDialog from "$dialogs/ProbeDialog.svelte";
|
||||
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
||||
import UploadDialog from "$dialogs/UploadDialog.svelte";
|
||||
import SetTimeDialog from "./SetTimeDialog.svelte";
|
||||
import ManualHomeAxisDialog from "./ManualHomeAxisDialog.svelte";
|
||||
import SetAxisPositionDialog from "./SetAxisPositionDialog.svelte";
|
||||
import MoveToZeroDialog from "./MoveToZeroDialog.svelte";
|
||||
import ShutdownDialog from "./ShutdownDialog.svelte";
|
||||
import MessageDialog from "./MessageDialog.svelte";
|
||||
|
||||
const HomeMachineDialogProps = writable<HomeMachineDialogPropsType>();
|
||||
type HomeMachineDialogPropsType = {
|
||||
open: boolean;
|
||||
home: () => void;
|
||||
};
|
||||
|
||||
const ProbeDialogProps = writable<ProbeDialogPropsType>();
|
||||
type ProbeDialogPropsType = {
|
||||
open: boolean;
|
||||
probeType: "xyz" | "z";
|
||||
};
|
||||
|
||||
const ScreenRotationDialogProps = writable<ScreenRotationDialogPropsType>();
|
||||
type ScreenRotationDialogPropsType = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
const UploadDialogProps = writable<UploadDialogPropsType>();
|
||||
type UploadDialogPropsType = {
|
||||
open: boolean;
|
||||
file: File;
|
||||
onComplete: () => void;
|
||||
};
|
||||
|
||||
const SetTimeDialogProps = writable<SetTimeDialogPropsType>();
|
||||
type SetTimeDialogPropsType = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
const ManualHomeAxisDialogProps = writable<ManualHomeAxisDialogPropsType>();
|
||||
type ManualHomeAxisDialogPropsType = {
|
||||
open: boolean;
|
||||
axis: string;
|
||||
};
|
||||
|
||||
const SetAxisPositionDialogProps =
|
||||
writable<SetAxisPositionDialogPropsType>();
|
||||
type SetAxisPositionDialogPropsType = {
|
||||
open: boolean;
|
||||
axis: string;
|
||||
};
|
||||
|
||||
const MoveToZeroDialogProps = writable<MoveToZeroDialogPropsType>();
|
||||
type MoveToZeroDialogPropsType = {
|
||||
open: boolean;
|
||||
axes: "xy" | "z";
|
||||
};
|
||||
|
||||
const ShutdownDialogProps = writable<ShutdownDialogPropsType>();
|
||||
type ShutdownDialogPropsType = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
const MessageDialogProps = writable<MessageDialogPropsType>();
|
||||
type MessageDialogPropsType = {
|
||||
open: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
noaction: boolean;
|
||||
};
|
||||
|
||||
export function showDialog(
|
||||
dialog: "HomeMachine",
|
||||
props: Omit<HomeMachineDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "Probe",
|
||||
props: Omit<ProbeDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "ScreenRotation",
|
||||
props: Omit<ScreenRotationDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "Upload",
|
||||
props: Omit<UploadDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "SetTime",
|
||||
props: Omit<SetTimeDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "ManualHomeAxis",
|
||||
props: Omit<ManualHomeAxisDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "SetAxisPosition",
|
||||
props: Omit<SetAxisPositionDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "MoveToZero",
|
||||
props: Omit<MoveToZeroDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "Shutdown",
|
||||
props: Omit<ShutdownDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "Message",
|
||||
props: Omit<MessageDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(dialog: string, props: any) {
|
||||
switch (dialog) {
|
||||
case "HomeMachine":
|
||||
HomeMachineDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "Probe":
|
||||
ProbeDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "ScreenRotation":
|
||||
ScreenRotationDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "Upload":
|
||||
UploadDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "SetTime":
|
||||
SetTimeDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "ManualHomeAxis":
|
||||
ManualHomeAxisDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "SetAxisPosition":
|
||||
SetAxisPositionDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "MoveToZero":
|
||||
MoveToZeroDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "Shutdown":
|
||||
ShutdownDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "Message":
|
||||
MessageDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown dialog '${dialog}'`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
|
||||
let bodyObserver: MutationObserver;
|
||||
let keyboardObserver: MutationObserver;
|
||||
|
||||
onMount(() => {
|
||||
bodyObserver = new MutationObserver(() => {
|
||||
const virtualKeyboard = document.getElementById(
|
||||
"virtualKeyboardChromeExtension"
|
||||
);
|
||||
|
||||
if (virtualKeyboard) {
|
||||
bodyObserver.disconnect();
|
||||
bodyObserver = undefined;
|
||||
|
||||
const virtualKeyboardOverlay = document.getElementById(
|
||||
"virtualKeyboardChromeExtensionOverlayScrollExtend"
|
||||
);
|
||||
|
||||
keyboardObserver = new MutationObserver(() => {
|
||||
const open =
|
||||
virtualKeyboard.getAttribute("_state") === "open";
|
||||
const keyboardHeight = Number.parseFloat(
|
||||
virtualKeyboardOverlay.style.height
|
||||
);
|
||||
|
||||
const dialogContainers =
|
||||
document.querySelectorAll<HTMLDivElement>(
|
||||
".mdc-dialog .mdc-dialog__container"
|
||||
);
|
||||
|
||||
for (let dialogContainer of dialogContainers) {
|
||||
dialogContainer.style["marginBottom"] = open
|
||||
? `${keyboardHeight}px`
|
||||
: "";
|
||||
}
|
||||
});
|
||||
|
||||
keyboardObserver.observe(virtualKeyboard, { attributes: true });
|
||||
}
|
||||
});
|
||||
|
||||
bodyObserver.observe(document.querySelector("body"), {
|
||||
subtree: false,
|
||||
childList: true,
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (bodyObserver) {
|
||||
bodyObserver.disconnect();
|
||||
bodyObserver = undefined;
|
||||
}
|
||||
|
||||
if (keyboardObserver) {
|
||||
keyboardObserver.disconnect();
|
||||
keyboardObserver = undefined;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<HomeMachineDialog {...$HomeMachineDialogProps} />
|
||||
<ProbeDialog {...$ProbeDialogProps} />
|
||||
<ScreenRotationDialog {...$ScreenRotationDialogProps} />
|
||||
<UploadDialog {...$UploadDialogProps} />
|
||||
<SetTimeDialog {...$SetTimeDialogProps} />
|
||||
<ManualHomeAxisDialog {...$ManualHomeAxisDialogProps} />
|
||||
<SetAxisPositionDialog {...$SetAxisPositionDialogProps} />
|
||||
<MoveToZeroDialog {...$MoveToZeroDialogProps} />
|
||||
<ShutdownDialog {...$ShutdownDialogProps} />
|
||||
<MessageDialog {...$MessageDialogProps} />
|
||||
33
src/svelte-components/src/dialogs/HomeMachineDialog.svelte
Normal file
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
export let open;
|
||||
export let home: () => any;
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="home-machine-dialog-title"
|
||||
aria-describedby="home-machine-dialog-content"
|
||||
>
|
||||
<Title id="home-machine-dialog-title">Home Machine</Title>
|
||||
|
||||
<Content id="home-machine-dialog-content">Home the machine?</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button defaultAction use={[InitialFocus]} on:click={home}>
|
||||
<Label>OK</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
@@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
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 axis = "";
|
||||
|
||||
let value = 0;
|
||||
|
||||
function onConfirm() {
|
||||
ControllerMethods.set_home(axis, value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="manual-home-axis-dialog-title"
|
||||
aria-describedby="manual-home-axis-dialog-content"
|
||||
>
|
||||
<Title id="manual-home-axis-dialog-title">
|
||||
Manually Home {axis.toUpperCase()} Axis
|
||||
</Title>
|
||||
|
||||
<Content id="manual-home-axis-dialog-content">
|
||||
<p>Set axis absolute position</p>
|
||||
|
||||
<TextField
|
||||
label="Absolute"
|
||||
type="number"
|
||||
bind:value
|
||||
use={[InitialFocus, virtualKeyboardChange((v) => (value = v))]}
|
||||
variant="filled"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
<Button defaultAction on:click={onConfirm}>
|
||||
<Label>Set</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
36
src/svelte-components/src/dialogs/MessageDialog.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
export let open: boolean;
|
||||
export let title = "";
|
||||
export let message = "";
|
||||
export let noaction = false;
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
escapeKeyAction=""
|
||||
aria-labelledby="message-dialog-title"
|
||||
aria-describedby="message-dialog-content"
|
||||
>
|
||||
<Title id="message-dialog-title">{title}</Title>
|
||||
|
||||
<Content id="message-dialog-content">
|
||||
<slot>{message}</slot>
|
||||
</Content>
|
||||
|
||||
{#if !noaction}
|
||||
<Actions>
|
||||
<Button defaultAction use={[InitialFocus]}>
|
||||
<Label>OK</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
{/if}
|
||||
</Dialog>
|
||||
33
src/svelte-components/src/dialogs/MoveToZeroDialog.svelte
Normal file
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import Dialog, { Title, Actions, InitialFocus } from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
|
||||
export let open;
|
||||
export let axes: "xy" | "z";
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="move-to-zero-dialog-title"
|
||||
aria-describedby="move-to-zero-dialog-content"
|
||||
>
|
||||
<Title id="move-to-zero-dialog-title">
|
||||
Move to {(axes || "").toUpperCase()} origin?
|
||||
</Title>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
defaultAction
|
||||
use={[InitialFocus]}
|
||||
on:click={() => ControllerMethods.gotoZero(axes)}
|
||||
>
|
||||
<Label>Confirm</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
517
src/svelte-components/src/dialogs/ProbeDialog.svelte
Normal file
@@ -0,0 +1,517 @@
|
||||
<script type="ts">
|
||||
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import LinearProgress from "@smui/linear-progress";
|
||||
import { waitForChange } from "$lib/StoreHelpers";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
import { Config } from "$lib/ConfigStore";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import {
|
||||
probingActive,
|
||||
probeContacted,
|
||||
probingComplete,
|
||||
probingFailed,
|
||||
probingStarted,
|
||||
} from "$lib/ControllerState";
|
||||
import { numberWithUnit } from "$lib/RegexHelpers";
|
||||
import TextFieldWithOptions from "$components/TextFieldWithOptions.svelte";
|
||||
import Icon from "svelte-icon";
|
||||
import BitDiameter from "../svgs/probe-bit-diameter.svg?raw";
|
||||
import CheckXYZ from "../svgs/probe-check-xyz.svg?raw";
|
||||
import CheckZ from "../svgs/probe-check-z.svg?raw";
|
||||
import PlaceXYZ from "../svgs/probe-place-xyz.svg?raw";
|
||||
import PlaceZ from "../svgs/probe-place-z.svg?raw";
|
||||
import PutAwayXYZ from "../svgs/probe-put-away-xyz.svg?raw";
|
||||
import PutAwayZ from "../svgs/probe-put-away-z.svg?raw";
|
||||
|
||||
const ValidSteps = [
|
||||
"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();
|
||||
|
||||
$probingFailed = false;
|
||||
|
||||
const enableSafety = $Config.settings["probing-prompts"];
|
||||
|
||||
steps = [
|
||||
enableSafety ? "CheckProbe" : undefined,
|
||||
probeType === "xyz" ? "BitDimensions" : undefined,
|
||||
enableSafety ? "PlaceProbeBlock" : undefined,
|
||||
"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;
|
||||
|
||||
if (!steps.includes(currentStep)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
$probingComplete = false;
|
||||
$userAcknowledged = false;
|
||||
}
|
||||
|
||||
function updateButtons() {
|
||||
showCancelButton = true;
|
||||
|
||||
nextButton = {
|
||||
label: "Next",
|
||||
disabled: false,
|
||||
allowClose: false,
|
||||
};
|
||||
|
||||
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() {
|
||||
const probeBlockWidth = $Config.probe["probe-xdim"];
|
||||
const probeBlockLength = $Config.probe["probe-ydim"];
|
||||
const probeBlockHeight = $Config.probe["probe-zdim"];
|
||||
const slowSeek = $Config.probe["probe-slow-seek"];
|
||||
const fastSeek = $Config.probe["probe-fast-seek"];
|
||||
|
||||
const cutterLength = 12.7;
|
||||
const zLift = 1;
|
||||
const xOffset = probeBlockWidth + cutterDiameterMetric / 2.0;
|
||||
const yOffset = probeBlockLength + cutterDiameterMetric / 2.0;
|
||||
const zOffset = probeBlockHeight;
|
||||
|
||||
if (probeType === "z") {
|
||||
ControllerMethods.send(`
|
||||
G21
|
||||
G92 Z0
|
||||
|
||||
G38.2 Z -25.4 F${fastSeek}
|
||||
G91 G1 Z 1
|
||||
G38.2 Z -2 F${slowSeek}
|
||||
G92 Z ${zOffset}
|
||||
|
||||
G91 G0 Z 25
|
||||
|
||||
M2
|
||||
`);
|
||||
} else {
|
||||
// After probing Z, we want to drop the bit down:
|
||||
// Ideally, 12.7mm/0.5in
|
||||
// And we don't want to be more than 90% down on the probe block
|
||||
// Also, add zlift to compensate for the fact that we lift after probing Z
|
||||
const plunge = Math.min(cutterLength, zOffset * 0.9) + zLift;
|
||||
|
||||
ControllerMethods.send(`
|
||||
G21
|
||||
G92 X0 Y0 Z0
|
||||
|
||||
G38.2 Z -25 F${fastSeek}
|
||||
G91 G1 Z 1
|
||||
G38.2 Z -2 F${slowSeek}
|
||||
G92 Z ${zOffset}
|
||||
|
||||
G91 G0 Z ${zLift}
|
||||
G91 G0 X 20
|
||||
G91 G0 Z ${-plunge}
|
||||
G38.2 X -20 F${fastSeek}
|
||||
G91 G1 X 1
|
||||
G38.2 X -2 F${slowSeek}
|
||||
G92 X ${xOffset}
|
||||
|
||||
G91 G0 X 1
|
||||
G91 G0 Y 20
|
||||
G91 G0 X -20
|
||||
G38.2 Y -20 F${fastSeek}
|
||||
G91 G1 Y 1
|
||||
G38.2 Y -2 F${slowSeek}
|
||||
G92 Y ${yOffset}
|
||||
|
||||
G91 G0 Y 3
|
||||
G91 G0 Z 25
|
||||
|
||||
M2
|
||||
`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
class="probe-dialog"
|
||||
scrimClickAction=""
|
||||
aria-labelledby="probe-dialog-title"
|
||||
aria-describedby="probe-dialog-content"
|
||||
surface$style="width: 700px; max-width: calc(100vw - 32px);"
|
||||
>
|
||||
<Title id="probe-dialog-title">Probing {probeType?.toUpperCase()}</Title>
|
||||
|
||||
<Content id="probe-dialog-content" style="overflow: visible;">
|
||||
<div class="steps">
|
||||
<p>
|
||||
<b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b>
|
||||
</p>
|
||||
<ul>
|
||||
{#each steps as step}
|
||||
<li class:active={currentStep === step}>
|
||||
{stepLabels[step]}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<div style="width: 100%">
|
||||
{#if currentStep === "CheckProbe"}
|
||||
<p>
|
||||
Attach the probe magnet to the collet, then touch the probe
|
||||
block to the bit.
|
||||
</p>
|
||||
|
||||
<Icon
|
||||
data={probeType === "xyz" ? CheckXYZ : CheckZ}
|
||||
size="300px"
|
||||
class="probe-icon-svg"
|
||||
/>
|
||||
{:else if currentStep === "BitDimensions"}
|
||||
<TextFieldWithOptions
|
||||
label="Cutter diameter"
|
||||
variant="filled"
|
||||
spellcheck="false"
|
||||
style="width: 100%;"
|
||||
bind:value={cutterDiameterString}
|
||||
options={[imperialBits, metricBits]}
|
||||
valid={isFinite(cutterDiameterMetric)}
|
||||
helperText={`Examples: 1/2", 10 mm, 0.25 in`}
|
||||
/>
|
||||
|
||||
<Icon data={BitDiameter} size="150px" class="probe-icon-svg" />
|
||||
{:else if currentStep === "PlaceProbeBlock"}
|
||||
<p>
|
||||
{#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>
|
||||
|
||||
<Icon
|
||||
data={probeType === "xyz" ? PlaceXYZ : PlaceZ}
|
||||
width="304px"
|
||||
height="129px"
|
||||
class="probe-icon-svg"
|
||||
/>
|
||||
|
||||
<p>
|
||||
The probing procedure will begin as soon as you click
|
||||
'Next'.
|
||||
</p>
|
||||
{:else if currentStep === "Probe"}
|
||||
<p>Probing in progress...</p>
|
||||
|
||||
<LinearProgress indeterminate />
|
||||
{:else if currentStep === "Done"}
|
||||
{#if $probingFailed}
|
||||
<h3>Emergency Stop!</h3>
|
||||
|
||||
<p>Could not find the probe block during probing!</p>
|
||||
|
||||
<p>
|
||||
Make sure the tip of the bit is less than {metric
|
||||
? "25mm"
|
||||
: "1 in"}
|
||||
above the probe block, and try again.
|
||||
</p>
|
||||
{:else}
|
||||
<p>Don't forget to put away the probe!</p>
|
||||
|
||||
<Icon
|
||||
data={probeType === "xyz" ? PutAwayXYZ : PutAwayZ}
|
||||
width="329px"
|
||||
height="256px"
|
||||
class="probe-icon-svg"
|
||||
/>
|
||||
|
||||
{#if probeType === "xyz"}
|
||||
<p>The machine will now move to the XY origin.</p>
|
||||
|
||||
<p>Watch your hands!</p>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<style lang="scss">
|
||||
$primary: #0078e7;
|
||||
$very-dark: #555;
|
||||
$text: #777;
|
||||
$grey: #bbb;
|
||||
$light: #ddd;
|
||||
|
||||
:global {
|
||||
.probe-dialog {
|
||||
.mdc-linear-progress {
|
||||
height: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.mdc-linear-progress__bar-inner {
|
||||
border-top-width: 10px;
|
||||
}
|
||||
|
||||
.probe-icon-svg {
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#probe-dialog-content {
|
||||
display: flex;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<script lang="ts">
|
||||
import * as api from "$lib/api";
|
||||
import TextField from "@smui/textfield";
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
|
||||
export let open;
|
||||
|
||||
let code = "";
|
||||
|
||||
async function onContinue() {
|
||||
const url = `remote-diagnostics?command=connect&code=${code}`;
|
||||
const result = await api.GET(url);
|
||||
|
||||
if (result.code === 401) {
|
||||
alert("The 6-digit code you provided was incorrect");
|
||||
} else {
|
||||
alert("Success!");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="remote-diagnostics-dialog-title"
|
||||
aria-describedby="remote-diagnostics-dialog-content"
|
||||
>
|
||||
<Title id="remote-diagnostics-dialog-title">Remote Diagnostics</Title>
|
||||
|
||||
<Content id="remote-diagnostics-dialog-content">
|
||||
<p>
|
||||
This feature enables remote diagnosis of customer issues. It
|
||||
requires a 6-digit code that is provided by Onefinity support during
|
||||
a live support session.
|
||||
</p>
|
||||
|
||||
<TextField
|
||||
bind:value={code}
|
||||
label="6-digit code"
|
||||
type="number"
|
||||
variant="filled"
|
||||
invalid={code?.length !== 6}
|
||||
use={[InitialFocus, virtualKeyboardChange((v) => (code = v))]}
|
||||
/>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
defaultAction
|
||||
on:click={onContinue}
|
||||
disabled={code?.length !== 6}
|
||||
>
|
||||
<Label>Continue</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script type="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
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 = [
|
||||
{ value: 0, label: "Normal" },
|
||||
{ value: 1, label: "Upside-down" },
|
||||
];
|
||||
|
||||
export let open;
|
||||
let currentValue;
|
||||
let value;
|
||||
let rebooting;
|
||||
|
||||
onMount(async () => {
|
||||
const result = await Api.GET("screen-rotation");
|
||||
currentValue = value = result.rotated ? 1 : 0;
|
||||
});
|
||||
|
||||
async function onConfirm() {
|
||||
rebooting = true;
|
||||
|
||||
await Api.PUT("screen-rotation", { rotated: value === 1 });
|
||||
}
|
||||
</script>
|
||||
|
||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||
Rebooting to apply the new screen rotation...
|
||||
</MessageDialog>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="screen-rotation-dialog-title"
|
||||
aria-describedby="screen-rotation-dialog-content"
|
||||
>
|
||||
<Title id="screen-rotation-dialog-title">Screen Rotation</Title>
|
||||
|
||||
<Content id="screen-rotation-dialog-content">
|
||||
{#each options as option}
|
||||
<FormField>
|
||||
<Radio bind:group={value} value={option.value} />
|
||||
<span slot="label">
|
||||
{option.label}
|
||||
</span>
|
||||
</FormField>
|
||||
{/each}
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button use={[InitialFocus]}>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
<Button
|
||||
defaultAction
|
||||
disabled={value === currentValue}
|
||||
on:click={onConfirm}
|
||||
>
|
||||
<Label>Confirm & Reboot</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
:global {
|
||||
#screen-rotation-dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,72 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
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 axis = "";
|
||||
|
||||
let value = 0;
|
||||
let homed = false;
|
||||
let wasOpen = false;
|
||||
|
||||
$: if (open != wasOpen) {
|
||||
if (open) {
|
||||
homed = ControllerMethods.isAxisHomed(axis);
|
||||
}
|
||||
|
||||
wasOpen = open;
|
||||
}
|
||||
|
||||
function onUnhome() {
|
||||
ControllerMethods.unhome(axis);
|
||||
}
|
||||
|
||||
function onConfirm() {
|
||||
ControllerMethods.set_position(axis, value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="set-axis-position-dialog-title"
|
||||
aria-describedby="set-axis-position-dialog-content"
|
||||
>
|
||||
<Title id="set-axis-position-dialog-title">
|
||||
Set {axis.toUpperCase()} Axis Position
|
||||
</Title>
|
||||
|
||||
<Content id="set-axis-position-dialog-content">
|
||||
<TextField
|
||||
label="Position"
|
||||
type="number"
|
||||
bind:value
|
||||
use={[InitialFocus, virtualKeyboardChange((v) => (value = v))]}
|
||||
spellcheck="false"
|
||||
variant="filled"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</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>
|
||||
290
src/svelte-components/src/dialogs/SetTimeDialog.svelte
Normal file
@@ -0,0 +1,290 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import TextField from "@smui/textfield";
|
||||
import Select, { Option } from "@smui/select";
|
||||
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;
|
||||
|
||||
type Timezone = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export let open = false;
|
||||
let year = "";
|
||||
let month = "";
|
||||
let day = "";
|
||||
let hour = "";
|
||||
let minute = "";
|
||||
let am = true;
|
||||
let wasOpen = false;
|
||||
let loading = true;
|
||||
let timezones: Timezone[] = [];
|
||||
let currentTimezoneIndex: number;
|
||||
let selectedTimezoneIndex: number;
|
||||
let networkTimeSynchronized: boolean;
|
||||
|
||||
$: if (open != wasOpen) {
|
||||
if (!wasOpen) {
|
||||
loadData();
|
||||
}
|
||||
|
||||
wasOpen = open;
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
loading = true;
|
||||
|
||||
const result = await api.GET("time");
|
||||
|
||||
parseTimezones(result.timezones);
|
||||
parseTimeinfo(result.timeinfo);
|
||||
|
||||
const date = new Date();
|
||||
year = date.getFullYear().toString();
|
||||
month = (date.getMonth() + 1).toString();
|
||||
day = date.getDate().toString();
|
||||
hour = date.getHours().toString();
|
||||
minute = date.getMinutes().toString();
|
||||
am = date.getHours() >= 12;
|
||||
|
||||
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 = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
currentTimezoneIndex = timezones.findIndex(
|
||||
(tz) => tz.value === currentTimezoneValue
|
||||
);
|
||||
selectedTimezoneIndex = currentTimezoneIndex;
|
||||
}
|
||||
|
||||
function parseTimezones(str: string) {
|
||||
const matches = Array.from(str.matchAll(/\s*(\S+)\s*/gm));
|
||||
|
||||
timezones = [];
|
||||
for (let [, value] of matches) {
|
||||
timezones.push({
|
||||
label: value.replace(/_/g, " "),
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
// Sort alphabetically, but with the current timezone at the top of the list
|
||||
timezones.sort((a, b) => {
|
||||
switch (true) {
|
||||
case a.value === "UTC":
|
||||
return -1;
|
||||
|
||||
case b.value === "UTC":
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return a.value.localeCompare(b.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
const YY = year.toString().padStart(2, "0");
|
||||
const MM = month.toString().padStart(2, "0");
|
||||
const DD = day.toString().padStart(2, "0");
|
||||
let hh = hour.toString().padStart(2, "0");
|
||||
const mm = minute.toString().padStart(2, "0");
|
||||
|
||||
if (Number(hour) < 12 && !am) {
|
||||
hh = (hour + 12).toString().padStart(2, "0");
|
||||
}
|
||||
|
||||
await api.PUT("time", {
|
||||
datetime: `${YY}-${MM}-${DD} ${hh}:${mm}:00`,
|
||||
timezone: timezones[selectedTimezoneIndex].value,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="set-time-dialog-title"
|
||||
aria-describedby="set-time-dialog-content"
|
||||
>
|
||||
<Title id="set-time-dialog-title">Change Time & Timezone</Title>
|
||||
|
||||
<Content id="set-time-dialog-content">
|
||||
{#if loading}
|
||||
<div style="display: flex; justify-content: center">
|
||||
<CircularProgress
|
||||
style="height: 32px; width: 32px;"
|
||||
indeterminate
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
{#if networkTimeSynchronized}
|
||||
<p>
|
||||
Because this controller is connected to the internet, the
|
||||
time is managed automatically, and cannot be manually set.
|
||||
</p>
|
||||
{:else}
|
||||
<p>
|
||||
Any time the controller is turned off, the time will need to
|
||||
be reset. If you connect the controller to the internet, the
|
||||
time will be managed automatically.
|
||||
</p>
|
||||
|
||||
<TextField
|
||||
bind:value={year}
|
||||
use={[
|
||||
InitialFocus,
|
||||
virtualKeyboardChange((v) => (year = v)),
|
||||
]}
|
||||
label="Year"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
<TextField
|
||||
bind:value={month}
|
||||
use={[virtualKeyboardChange((v) => (month = v))]}
|
||||
label="Month"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
<TextField
|
||||
bind:value={day}
|
||||
use={[virtualKeyboardChange((v) => (day = v))]}
|
||||
label="Day"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
|
||||
<span style="display: inline-block; width: 20px;" />
|
||||
|
||||
<TextField
|
||||
bind:value={hour}
|
||||
use={[virtualKeyboardChange((v) => (hour = v))]}
|
||||
label="Hour"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
<TextField
|
||||
bind:value={minute}
|
||||
use={[virtualKeyboardChange((v) => (minute = v))]}
|
||||
label="Minute"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
|
||||
<Select
|
||||
label=""
|
||||
bind:value={am}
|
||||
style="width: 90px;"
|
||||
variant="filled"
|
||||
>
|
||||
<Option value={true}>AM</Option>
|
||||
<Option value={false}>PM</Option>
|
||||
</Select>
|
||||
{/if}
|
||||
|
||||
<div class="timezones-container" style="margin-top: 30px;">
|
||||
<VirtualList
|
||||
width="100%"
|
||||
height={itemHeight * 6}
|
||||
itemCount={timezones.length}
|
||||
itemSize={itemHeight}
|
||||
scrollToIndex={currentTimezoneIndex}
|
||||
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}
|
||||
>
|
||||
<Label>Confirm</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
@use "sass:color";
|
||||
|
||||
$primary: #0078e7;
|
||||
$very-dark: #555;
|
||||
$text: #777;
|
||||
$grey: #bbb;
|
||||
$light: #ddd;
|
||||
|
||||
.timezones-container {
|
||||
:global {
|
||||
.virtual-list-wrapper {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
overflow-x: hidden;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
38
src/svelte-components/src/dialogs/ShutdownDialog.svelte
Normal file
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import Dialog, { Title, Actions, InitialFocus } from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import * as Api from "$lib/api";
|
||||
|
||||
export let open;
|
||||
|
||||
function shutdown() {
|
||||
Api.PUT("shutdown");
|
||||
}
|
||||
|
||||
function restart() {
|
||||
Api.PUT("reboot");
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="shutdown-dialog-title"
|
||||
aria-describedby="shutdown-dialog-content"
|
||||
>
|
||||
<Title id="shutdown-dialog-title">Confirm Shutdown?</Title>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button on:click={shutdown}>
|
||||
<Label>Shutdown</Label>
|
||||
</Button>
|
||||
|
||||
<Button use={[InitialFocus]} on:click={restart}>
|
||||
<Label>Restart</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
83
src/svelte-components/src/dialogs/UploadDialog.svelte
Normal file
@@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import LinearProgress from "@smui/linear-progress";
|
||||
|
||||
export let open = false;
|
||||
export let file: File;
|
||||
export let onComplete: () => void;
|
||||
|
||||
let wasOpen = false;
|
||||
let xhr;
|
||||
let progress;
|
||||
|
||||
$: if (open != wasOpen) {
|
||||
if (!wasOpen) {
|
||||
beginUpload();
|
||||
}
|
||||
|
||||
wasOpen = open;
|
||||
}
|
||||
|
||||
$: if (!open) {
|
||||
xhr = undefined;
|
||||
}
|
||||
|
||||
async function beginUpload() {
|
||||
progress = 0;
|
||||
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.upload.onload = () => {
|
||||
open = false;
|
||||
if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
};
|
||||
|
||||
xhr.upload.onerror = () => {
|
||||
open = false;
|
||||
alert("Upload failed.");
|
||||
};
|
||||
|
||||
xhr.upload.onabort = () => {
|
||||
open = false;
|
||||
};
|
||||
|
||||
xhr.upload.onprogress = (event) => {
|
||||
progress = event.loaded / event.total;
|
||||
};
|
||||
|
||||
xhr.open("PUT", `/api/file/${encodeURIComponent(file.name)}`);
|
||||
xhr.send(file);
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
xhr.abort();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="upload-dialog-title"
|
||||
aria-describedby="upload-dialog-content"
|
||||
>
|
||||
<Title id="upload-dialog-title">
|
||||
Uploading {#if file}{file.name}...{/if}
|
||||
</Title>
|
||||
|
||||
<Content id="upload-dialog-content">
|
||||
<LinearProgress {progress} />
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button on:click={onCancel} use={[InitialFocus]}>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
112
src/svelte-components/src/dialogs/WifiConnectionDialog.svelte
Normal file
@@ -0,0 +1,112 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import TextField from "@smui/textfield";
|
||||
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 network: WifiNetwork;
|
||||
|
||||
let rebooting = false;
|
||||
let password = "";
|
||||
let showPassword = false;
|
||||
|
||||
$: needPassword = !network?.active && network?.Encryption !== "Open";
|
||||
$: connectOrDisconnect = network?.active ? "Disconnect" : "Connect";
|
||||
$: connectToOrDisconnectFrom = network?.active
|
||||
? "Disconnect from"
|
||||
: "Connect to";
|
||||
|
||||
$: if (open) {
|
||||
password = "";
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
rebooting = true;
|
||||
|
||||
await api.PUT("network", {
|
||||
wifi: {
|
||||
enabled: !network.active,
|
||||
ssid: network.Name,
|
||||
password,
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||
Rebooting to apply Wifi changes...
|
||||
</MessageDialog>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="wifi-connection-dialog-title"
|
||||
aria-describedby="wifi-connection-dialog-content"
|
||||
>
|
||||
<Title id="wifi-connection-dialog-title">
|
||||
{connectToOrDisconnectFrom}
|
||||
{network.Name}
|
||||
</Title>
|
||||
|
||||
<Content id="wifi-connection-dialog-content">
|
||||
{#if needPassword}
|
||||
<TextField
|
||||
bind:value={password}
|
||||
use={[
|
||||
InitialFocus,
|
||||
virtualKeyboardChange((v) => (password = v)),
|
||||
]}
|
||||
label="Password"
|
||||
spellcheck="false"
|
||||
variant="filled"
|
||||
type={showPassword ? "text" : "password"}
|
||||
style="width: 100%;"
|
||||
>
|
||||
<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)}
|
||||
>
|
||||
<Label>{connectOrDisconnect} & Reboot</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
14
src/svelte-components/src/lib/ConfigStore.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
type DisplayUnits = "METRIC" | "IMPERIAL";
|
||||
|
||||
export const Config = writable<Record<string, any>>({});
|
||||
export const DisplayUnits = writable<DisplayUnits>();
|
||||
|
||||
export function handleConfigUpdate(config: Record<string, any>) {
|
||||
Config.set(config);
|
||||
}
|
||||
|
||||
export function setDisplayUnits(value: DisplayUnits) {
|
||||
DisplayUnits.set(value);
|
||||
}
|
||||
32
src/svelte-components/src/lib/ControllerState.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { get, writable } from "svelte/store";
|
||||
import { processNetworkInfo } from "./NetworkInfo";
|
||||
|
||||
export const networkInfo = writable({});
|
||||
|
||||
export const probingActive = writable(false);
|
||||
export const probeContacted = writable(false);
|
||||
export const probingStarted = writable(false);
|
||||
export const probingFailed = writable(false);
|
||||
export const probingComplete = writable(false);
|
||||
|
||||
export function handleControllerStateUpdate(state: Record<string, any>) {
|
||||
|
||||
if (get(probingActive)) {
|
||||
if (state.pw === 0) {
|
||||
probeContacted.set(true);
|
||||
}
|
||||
|
||||
if (state.log?.msg === "Switch not found") {
|
||||
probingFailed.set(true);
|
||||
}
|
||||
|
||||
if (state.cycle !== "idle") {
|
||||
probingStarted.set(true);
|
||||
}
|
||||
|
||||
if (state.cycle === "idle" && get(probingStarted)) {
|
||||
probingStarted.set(false);
|
||||
probingComplete.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/svelte-components/src/lib/CustomActions.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { HTMLActionEntry } from "@smui/common/internal";
|
||||
|
||||
export function virtualKeyboardChange(cb: (v: any) => void): HTMLActionEntry {
|
||||
const func = (node: HTMLElement, cb: (value: any) => void) => {
|
||||
const input = node.querySelector("input");
|
||||
if (!input) {
|
||||
console.error("Could not find the textfield's <input>:", node);
|
||||
throw new Error("Could not find the textfield's <input>");
|
||||
}
|
||||
|
||||
input.addEventListener("keyup", () => cb(input.value));
|
||||
};
|
||||
|
||||
return [ func, cb ];
|
||||
}
|
||||
81
src/svelte-components/src/lib/NetworkInfo.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export type WifiNetwork = {
|
||||
Quality: string;
|
||||
Channel: string;
|
||||
Frequency: string;
|
||||
Mode: string;
|
||||
"Bit Rates": string;
|
||||
Name: string;
|
||||
Address: string;
|
||||
Encryption: string;
|
||||
"Signal Level": string;
|
||||
"Noise Level": string;
|
||||
lastSeen: number;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
export type NetworkInfo = {
|
||||
ipAddresses: Array<string>;
|
||||
hostname: string;
|
||||
wifi: {
|
||||
ssid: string;
|
||||
networks: Array<WifiNetwork>;
|
||||
};
|
||||
};
|
||||
|
||||
const empty: NetworkInfo = {
|
||||
ipAddresses: [],
|
||||
hostname: "",
|
||||
wifi: {
|
||||
ssid: "",
|
||||
networks: []
|
||||
}
|
||||
};
|
||||
|
||||
export const networkInfo = writable<NetworkInfo>(empty);
|
||||
|
||||
export function processNetworkInfo(rawNetworkInfo: NetworkInfo) {
|
||||
const now = Date.now();
|
||||
const networksByName: Record<string, WifiNetwork> = {};
|
||||
|
||||
for (const network of rawNetworkInfo.wifi.networks) {
|
||||
if (network.Name) {
|
||||
network.lastSeen = now;
|
||||
network.active = rawNetworkInfo.wifi.ssid === network.Name;
|
||||
|
||||
// There can be many entries for the same ssid, so
|
||||
// we want to take the one with the highest quality
|
||||
const currentNetwork = networksByName[network.Name] ?? { Quality: 0 };
|
||||
if (network.Quality >= currentNetwork.Quality) {
|
||||
networksByName[network.Name] = network;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const network of Object.values(networksByName)) {
|
||||
if (network.lastSeen - now > 30000) {
|
||||
delete networksByName[network.Name];
|
||||
}
|
||||
}
|
||||
|
||||
networkInfo.set({
|
||||
ipAddresses: rawNetworkInfo.ipAddresses,
|
||||
hostname: rawNetworkInfo.hostname,
|
||||
wifi: {
|
||||
ssid: rawNetworkInfo.wifi.ssid,
|
||||
networks: Object.values(networksByName).sort((a, b) => {
|
||||
switch (true) {
|
||||
case a.active:
|
||||
return -1;
|
||||
|
||||
case b.active:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return a.Name.localeCompare(b.Name);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
85
src/svelte-components/src/lib/RegexHelpers.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
function numberWithUnitToMetric() {
|
||||
return this.metric ? this.value : this.value * 25.4;
|
||||
}
|
||||
|
||||
function isPojo(value) {
|
||||
if (value === null || typeof value !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Object.getPrototypeOf(value) === Object.prototype;
|
||||
}
|
||||
|
||||
const fractions = [
|
||||
{ value: 0.75, formatted: "3/4" },
|
||||
{ value: 0.625, formatted: "5/8" },
|
||||
{ value: 0.5, formatted: "1/2" },
|
||||
{ value: 0.375, formatted: "3/8" },
|
||||
{ value: 0.25, formatted: "1/4" },
|
||||
{ value: 0.1875, formatted: "3/16" },
|
||||
{ value: 0.125, formatted: "1/8" },
|
||||
{ value: 0.09375, formatted: "3/32" },
|
||||
{ value: 0.0625, formatted: "1/16" },
|
||||
{ value: 0.03125, formatted: "1/32" },
|
||||
];
|
||||
|
||||
function formatFraction(value: number) {
|
||||
const fraction = fractions.find(f => f.value === value);
|
||||
|
||||
return fraction ? fraction.formatted : value.toString();
|
||||
}
|
||||
|
||||
export const numberWithUnit = {
|
||||
regex: /^\s*(?:(\d+)\s*\/\s*(\d+)|(\d*\.\d+)|(\d+(?:\.\d+)?))\s*("|in|inch|inches|mm|millimeters)\s*$/,
|
||||
parse: function(str: string) {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let [ , numerator, denominator, decimal1, decimal2, unit ]: any = str?.match(numberWithUnit.regex) ?? [];
|
||||
|
||||
numerator = Number.parseFloat(numerator);
|
||||
denominator = Number.parseFloat(denominator);
|
||||
decimal1 = Number.parseFloat(decimal1);
|
||||
decimal2 = Number.parseFloat(decimal2);
|
||||
|
||||
const metric = (unit ?? "").includes("m");
|
||||
|
||||
switch (true) {
|
||||
case isFinite(numerator) && isFinite(denominator):
|
||||
return {
|
||||
value: numerator / denominator,
|
||||
metric,
|
||||
toMetric: numberWithUnitToMetric
|
||||
};
|
||||
|
||||
case isFinite(decimal1) && decimal1 !== 0:
|
||||
return {
|
||||
value: decimal1,
|
||||
metric,
|
||||
toMetric: numberWithUnitToMetric
|
||||
};
|
||||
|
||||
case isFinite(decimal2) && decimal2 !== 0:
|
||||
return {
|
||||
value: decimal2,
|
||||
metric,
|
||||
toMetric: numberWithUnitToMetric
|
||||
};
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
normalize: function(str) {
|
||||
const value = this.parse(str);
|
||||
|
||||
switch (true) {
|
||||
case !isPojo(value):
|
||||
return "";
|
||||
|
||||
case value.metric:
|
||||
return `${value.value} mm`;
|
||||
|
||||
default:
|
||||
return `${formatFraction(value.value)} in`;
|
||||
}
|
||||
}
|
||||
};
|
||||
44
src/svelte-components/src/lib/RegisterControllerMethods.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
interface RegisterableControllerMethods {
|
||||
stop: () => void;
|
||||
send: (gcode: string) => void;
|
||||
dispatch: (event: string, ...args: any[]) => void;
|
||||
isAxisHomed: (axis: string) => boolean;
|
||||
unhome: (axis: string) => void;
|
||||
set_position: (axis: string, value: number) => void;
|
||||
set_home: (axis: string, value: number) => void;
|
||||
}
|
||||
|
||||
interface ControllerMethods extends RegisterableControllerMethods {
|
||||
gotoZero: (axes: "xy" | "z") => void;
|
||||
}
|
||||
|
||||
export let ControllerMethods: ControllerMethods;
|
||||
|
||||
export function registerControllerMethods(methods: Partial<RegisterableControllerMethods>) {
|
||||
ControllerMethods = {
|
||||
...ControllerMethods,
|
||||
...methods,
|
||||
gotoZero
|
||||
};
|
||||
}
|
||||
|
||||
function gotoZero(axes: "xy" | "z") {
|
||||
let axesClause = "";
|
||||
switch (axes.toLowerCase()) {
|
||||
case "xy":
|
||||
axesClause = "X0Y0";
|
||||
break;
|
||||
|
||||
case "z":
|
||||
axesClause = "Z0";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid axes: ${axes}`);
|
||||
}
|
||||
|
||||
ControllerMethods.send(`
|
||||
G90
|
||||
G0 ${axesClause}
|
||||
`);
|
||||
}
|
||||
18
src/svelte-components/src/lib/StoreHelpers.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { get, type Writable } from "svelte/store";
|
||||
|
||||
export function listenForChange<T>(writable: Writable<T>, cb: (value: T) => void) {
|
||||
const priorValue = get(writable);
|
||||
|
||||
const unsubscribe = writable.subscribe((value) => {
|
||||
if (value !== priorValue) {
|
||||
unsubscribe();
|
||||
cb(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function waitForChange<T>(writable: Writable<T>): Promise<T> {
|
||||
return new Promise((resolve) => {
|
||||
listenForChange(writable, (value) => resolve(value));
|
||||
});
|
||||
}
|
||||
41
src/svelte-components/src/lib/api.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
type HttpMethod = "GET" | "PUT" | "POST" | "DELETE";
|
||||
|
||||
async function doFetch(method: HttpMethod, url: string, data: any, config: RequestInit) {
|
||||
try {
|
||||
const response = await fetch(`/api/${url}`, {
|
||||
...config,
|
||||
method,
|
||||
cache: "no-cache",
|
||||
body: (typeof data === "object")
|
||||
? JSON.stringify(data)
|
||||
: undefined,
|
||||
headers: (typeof data === "object")
|
||||
? {
|
||||
"Content-Type": "application/json; charset=utf-8"
|
||||
}
|
||||
: {}
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.debug(`API Error: ${url}: ${error}`);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function GET(url: string, config: RequestInit = {}) {
|
||||
return doFetch("GET", url, undefined, config);
|
||||
}
|
||||
|
||||
export function PUT(url: string, data: any = undefined, config: RequestInit = {}) {
|
||||
return doFetch("PUT", url, data, config);
|
||||
}
|
||||
|
||||
export function POST(url: string, data: any = undefined, config: RequestInit = {}) {
|
||||
return doFetch("POST", url, data, config);
|
||||
}
|
||||
|
||||
export function DELETE(url: string, config = {}) {
|
||||
return doFetch("DELETE", url, undefined, config);
|
||||
}
|
||||
39
src/svelte-components/src/main.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import "polyfill-object.fromentries";
|
||||
import matchAll from "string.prototype.matchall";
|
||||
|
||||
matchAll.shim();
|
||||
|
||||
import AdminNetworkView from "$components/AdminNetworkView.svelte";
|
||||
import SettingsView from "$components/SettingsView.svelte";
|
||||
import HelpView from "$components/HelpView.svelte";
|
||||
import DialogHost, { showDialog } from "$dialogs/DialogHost.svelte";
|
||||
import { handleConfigUpdate, setDisplayUnits } from "$lib/ConfigStore";
|
||||
import { handleControllerStateUpdate } from "$lib/ControllerState";
|
||||
import { registerControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
|
||||
export function createComponent(component: string, target: HTMLElement, props: Record<string, any>) {
|
||||
switch (component) {
|
||||
case "AdminNetworkView":
|
||||
return new AdminNetworkView({ target, props });
|
||||
|
||||
case "SettingsView":
|
||||
return new SettingsView({ target, props });
|
||||
|
||||
case "HelpView":
|
||||
return new HelpView({ target, props });
|
||||
|
||||
case "DialogHost":
|
||||
return new DialogHost({ target, props });
|
||||
|
||||
default:
|
||||
throw new Error("Unknown component");
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
showDialog,
|
||||
handleControllerStateUpdate,
|
||||
handleConfigUpdate,
|
||||
registerControllerMethods,
|
||||
setDisplayUnits
|
||||
};
|
||||
75
src/svelte-components/src/svgs/probe-bit-diameter.svg
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100.88 191.47">
|
||||
<polygon
|
||||
points="21.64 186.89 12.57 189.89 3.5 186.89 3.5 88.81 1.5 85.76 1.5 1.5 23.64 1.5 23.64 85.76 21.64 88.81 21.64 186.89"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="3.5" y1="88.81" x2="21.64" y2="88.81"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="3.5" y1="185.56" x2="21.64" y2="177.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="3.5" y1="174.91" x2="21.64" y2="166.4"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="3.5" y1="164.21" x2="21.64" y2="155.71"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="3.5" y1="153.6" x2="21.64" y2="145.09"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="3.5" y1="142.9" x2="21.64" y2="134.4"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="3.5" y1="131.88" x2="21.64" y2="123.37"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="3.5" y1="121.18" x2="21.64" y2="112.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="3.5" y1="110.57" x2="21.64" y2="102.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="3.5" y1="99.87" x2="21.64" y2="91.37"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<g>
|
||||
<line x1="27.28" y1="1.5" x2="29.78" y2="1.5"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="31.28" y1="1.5" x2="38.5" y2="1.5"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 2.49 1.5 2.49 1.5; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<polyline points="39.25 1.5 41.75 1.5 41.75 4"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="41.75" y1="6.64" x2="41.75" y2="19.39"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 4.4 2.64 4.4 2.64; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="41.75" y1="20.71" x2="41.75" y2="82.72"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 0 0 4.4 2.64 4.4 2.64 4.4 2.64; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<polyline points="41.75 84.04 41.75 86.54 39.25 86.54"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="37.76" y1="86.54" x2="30.53" y2="86.54"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 2.49 1.5 2.49 1.5; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="29.78" y1="86.54" x2="27.28" y2="86.54"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<g>
|
||||
<line x1="27.28" y1="90.04" x2="29.78" y2="90.04"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="30.59" y1="90.04" x2="34.53" y2="90.04"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 1.36 .81 1.36 .81; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<polyline points="34.94 90.04 37.44 90.04 37.44 92.54"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="37.44" y1="95.64" x2="37.44" y2="110.63"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 5.17 3.1 5.17 3.1; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="37.44" y1="112.18" x2="37.44" y2="185.03"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 0 0 5.17 3.1 5.17 3.1 5.17 3.1; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<polyline points="37.44 186.58 37.44 189.08 34.94 189.08"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="34.13" y1="189.08" x2="30.22" y2="189.08"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 1.35 .81 1.35 .81; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="29.81" y1="189.08" x2="27.31" y2="189.08"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<line x1="41.75" y1="44.02" x2="56.79" y2="44.02"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="37.44" y1="138.65" x2="56.79" y2="138.65"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<circle cx="78.33" cy="44.02" r="21.54"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<circle cx="78.33" cy="138.65" r="21.54"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="68.56" y1="34.24" x2="88.11" y2="53.8"
|
||||
style="fill:none; stroke:#be1e2d; stroke-miterlimit:10; stroke-width:4px;" />
|
||||
<line x1="88.11" y1="34.24" x2="68.56" y2="53.8"
|
||||
style="fill:none; stroke:#be1e2d; stroke-miterlimit:10; stroke-width:4px;" />
|
||||
<polyline points="64.73 141.61 72.43 149.31 91.94 129.81"
|
||||
style="fill:none; stroke:#00a651; stroke-miterlimit:10; stroke-width:4px;" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
243
src/svelte-components/src/svgs/probe-check-xyz.svg
Normal file
@@ -0,0 +1,243 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 307.88 290.65">
|
||||
<g>
|
||||
<polygon
|
||||
points="306.38 163.14 243.73 245.91 98.03 245.91 98.03 230.65 160.68 147.88 306.38 147.88 306.38 163.14"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="306.38 147.88 243.73 230.65 98.03 230.65"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="243.73" y1="230.65" x2="243.73" y2="245.91"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<line x1="146.89" y1="186.96" x2="146.89" y2="190.46"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:1.75px;" />
|
||||
<line x1="146.89" y1="191.76" x2="146.89" y2="202.85"
|
||||
style="fill:none; stroke:#37b34a; stroke-dasharray:0 0 0 0 4.56 1.3 4.56 1.3; stroke-miterlimit:10; stroke-width:1.75px;" />
|
||||
<line x1="146.89" y1="203.5" x2="146.89" y2="207"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:1.75px;" />
|
||||
</g>
|
||||
<polyline points="142.67 191.08 146.89 185.87 150.95 190.95"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:1.75px;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon points="150.06 174.24 132.23 197.79 90.77 197.79 90.77 186.45 108.6 162.89 150.06 162.89 150.06 174.24"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="150.06 162.89 132.23 186.45 90.77 186.45"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="132.23" y1="186.45" x2="132.23" y2="197.79"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<ellipse cx="138.4" cy="167.46" rx="3.83" ry="1.56" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<circle cx="110.61" cy="192.12" r="3.12" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<g>
|
||||
<polygon points="123.91 172.78 120.77 169.36 118.81 171.98 121.94 175.4 123.91 172.78"
|
||||
style="fill:#29b473; opacity:.42;" />
|
||||
<polygon points="120.79 176.92 117.66 173.5 115.69 176.12 118.83 179.54 120.79 176.92"
|
||||
style="fill:#29b473; opacity:.42;" />
|
||||
<polygon points="117.09 181.85 117.68 181.06 114.55 177.65 111.39 181.85 114.47 181.55 117.09 181.85"
|
||||
style="fill:#29b473; opacity:.42;" />
|
||||
<polygon points="127.88 167.49 122.18 167.49 121.92 167.84 125.05 171.25 127.88 167.49"
|
||||
style="fill:#29b473; opacity:.42;" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M1.25,178.73c8.14-4.37,16.49-8.51,25.42-10.9s16.85-8.86,25.66-6.05c3.11,.99,4.86,.29,7.84,1.63,7.16,3.22,14.5,6.16,22.19,7.73,.92,.19,1.85,.36,2.75,.64,.76,.24,1.49,.57,2.23,.85,3.03,1.13,6.35,1.46,9.56,1.07,1.51-.18,3.03-.52,4.52-.25s2.98,1.36,3.1,2.87c.15,1.87-1.69,3.22-3.31,4.16-5.19,3.04-10.67,6.16-16.68,6.31-1.06,.03-2.12-.04-3.16,.13-.96,.16-1.87,.52-2.8,.81-5.16,1.61-10.98,.91-15.62-1.86-.45-.27-.97-.56-1.45-.36-.27,.11-.46,.36-.64,.6-5.22,6.94-13.45,11.53-22.1,12.33"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M1.88,179.81c5.74-3.07,11.56-6.05,17.66-8.36,2.91-1.1,5.89-1.89,8.85-2.83,2.68-.84,5.25-1.98,7.82-3.08s5.22-2.2,7.99-2.8,5.43-.39,8.23,.37c2.52,.69,5.09,.51,7.53,1.56,2.93,1.27,5.84,2.56,8.82,3.7s6.08,2.19,9.2,3.02c1.49,.4,2.99,.73,4.5,1.04,1.66,.34,3.14,.9,4.73,1.47,2.8,1,5.85,1.37,8.81,1.13,1.5-.12,2.99-.5,4.5-.43,1.2,.05,3.18,.83,2.69,2.38-.36,1.13-1.67,1.85-2.62,2.42-1.33,.79-2.68,1.56-4.05,2.3-2.68,1.44-5.47,2.72-8.45,3.39s-6.08,.21-9.05,1.18-5.77,1.32-8.75,.89c-1.5-.22-3-.62-4.4-1.2s-2.85-1.93-4.35-1.72c-1.35,.19-2.09,1.71-2.9,2.64-.98,1.12-2.04,2.17-3.17,3.15-4.66,4.01-10.59,6.55-16.7,7.15-1.59,.16-1.6,2.66,0,2.5,5.78-.57,11.36-2.61,16.08-6.01,2.3-1.65,4.4-3.57,6.18-5.77,.18-.22,.7-1.1,.95-1.15,.34-.07,1.44,.81,1.81,.98,1.47,.71,3.01,1.26,4.61,1.6,2.93,.63,5.97,.65,8.91,.02,1.49-.32,2.91-1.01,4.41-1.23,1.65-.24,3.34-.05,5-.27,3.13-.41,6.08-1.52,8.91-2.88s5.65-2.73,7.99-4.62c2.05-1.66,3.05-4.46,1-6.58-2.35-2.43-5.65-1.48-8.6-1.24-3.43,.27-6.5-.44-9.69-1.64s-6.65-1.57-9.9-2.54-6.43-2.13-9.56-3.41c-1.61-.66-3.21-1.34-4.8-2.05-1.38-.61-2.73-1.24-4.23-1.49-1.3-.22-2.63-.22-3.92-.5-1.45-.31-2.83-.8-4.32-.97-2.8-.32-5.64,.17-8.31,1-3.25,1.01-6.32,2.49-9.46,3.78-3.37,1.38-6.89,2.22-10.33,3.39-7.25,2.48-14.12,5.94-20.86,9.55-1.42,.76-.16,2.92,1.26,2.16h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M90.74,193.11c-1.29,.17-2.57,.39-3.84,.65-.92,.19-1.85,.39-2.79,.36-.92-.04-1.82-.31-2.72-.51-4.42-.97-7.32-.5-11.78,.27"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M90.41,191.9c-1.83,.25-3.67,.77-5.5,.93s-3.78-.62-5.62-.86c-3.38-.43-6.69,.12-10.01,.69-1.58,.27-.91,2.68,.66,2.41,3.37-.58,6.73-1.08,10.14-.47,1.83,.33,3.52,.91,5.4,.66s3.72-.7,5.6-.96c1.59-.22,.91-2.63-.66-2.41h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M69.76,187.81c-1.62,2.32-1,5.49-1.75,8.21-.39,1.43-1.18,2.75-1.38,4.22-.21,1.55,.25,3.1,.55,4.63,.07,.36,.13,.74,0,1.09-.11,.31-.36,.55-.59,.8-1.37,1.46-2.16,3.44-2.16,5.44"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M68.68,187.18c-1.16,1.8-1.31,3.87-1.49,5.95-.09,1.07-.22,2.11-.57,3.14-.37,1.08-.89,2.12-1.12,3.24s-.17,2.25,.01,3.38c.1,.62,.27,1.23,.37,1.85,.12,.73,.02,.88-.44,1.43-1.45,1.71-2.2,3.8-2.26,6.03-.04,1.61,2.46,1.61,2.5,0,.03-1.13,.28-2.2,.82-3.2,.48-.88,1.34-1.51,1.76-2.41,.38-.82,.19-1.66,.03-2.51-.26-1.28-.63-2.59-.39-3.9,.36-1.96,1.39-3.66,1.65-5.68s.12-4.25,1.28-6.06c.88-1.36-1.29-2.61-2.16-1.26h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M67.04,206.15c1.3-.46,2.66-.76,4.03-.87,.35-.03,.69-.05,1.03-.11,.48-.09,.93-.27,1.39-.44,3.91-1.5,8.12-2.77,12.23-2.02"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M67.37,207.35c1.57-.54,3.1-.69,4.72-.93,1.39-.21,2.71-.89,4.04-1.33,2.97-.99,6.13-1.71,9.26-1.18,1.57,.27,2.25-2.14,.66-2.41-3.36-.57-6.69-.06-9.92,.96-1.44,.46-2.88,1.23-4.37,1.5-1.73,.31-3.37,.4-5.07,.98-1.51,.52-.86,2.94,.66,2.41h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M58.89,226.43c-2.05-4.85,.54-11.14,5.42-13.14,2.12-.87,4.51-.99,6.57-2,2.05-1,3.61-2.78,5.6-3.89,2.67-1.49,5.93-1.66,8.55-3.22,1.82-1.08,3.21-2.76,4.88-4.07,2.03-1.59,4.44-2.59,6.82-3.58,1.75-.73,3.72-1.46,5.49-.79,2.12,.8,3.09,3.41,2.7,5.64s-1.83,4.12-3.34,5.8c-7.7,8.57-18.19,14.03-28.44,19.3-2.72,1.4-5.45,2.8-8.28,3.96-2.26,.92-4.6,1.69-6.7,2.94-1.64,.98-3.1,2.23-4.66,3.35-5.43,3.91-11.97,6.11-18.6,6.96-6.63,.86-13.38,.43-19.99-.57"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M60.1,226.09c-1.55-3.87-.15-8.67,3.4-10.97,2.14-1.39,4.8-1.45,7.13-2.37s4.01-2.79,6.14-4.07c2.27-1.37,4.99-1.67,7.41-2.69s4.22-3.03,6.29-4.72,4.72-2.79,7.21-3.78c2.31-.91,5.06-1.29,5.9,1.63,.7,2.44-.84,4.79-2.36,6.57-1.71,2.01-3.64,3.85-5.65,5.56-4.04,3.43-8.55,6.28-13.16,8.88s-9.7,5.28-14.66,7.65c-2.68,1.28-5.5,2.22-8.2,3.46-2.54,1.16-4.6,2.92-6.86,4.52-10.84,7.7-24.78,8.03-37.46,6.14-1.57-.23-2.25,2.17-.66,2.41,12.35,1.84,25.58,1.74,36.69-4.67,2.65-1.53,4.89-3.64,7.52-5.2,2.79-1.66,5.97-2.56,8.92-3.9,5.5-2.5,10.9-5.39,16.16-8.37s9.92-6.02,14.27-9.85c3.62-3.19,8.96-7.83,8-13.24-.39-2.22-1.92-4.25-4.2-4.73-2.8-.6-5.73,.98-8.21,2.11-2.8,1.27-4.98,2.98-7.24,5.02-1.1,.99-2.27,1.84-3.66,2.37s-2.94,.87-4.39,1.36-2.72,1.1-3.93,1.98c-1.3,.94-2.48,2.07-3.9,2.83-2.74,1.46-6.03,1.3-8.65,3.12-4.22,2.94-6.21,8.78-4.27,13.61,.59,1.47,3.01,.83,2.41-.66h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M110.32,191.95c2.31,.23,4.18,2.27,4.64,4.55s-.3,4.67-1.62,6.57c-1.32,1.91-3.14,3.4-5.01,4.77-6.3,4.62-13.39,8.15-20.87,10.4"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M109.98,193.15c3.4,.48,4.45,4.11,3.45,7.02-1.14,3.32-4.23,5.56-6.99,7.49-5.91,4.13-12.43,7.28-19.32,9.38-1.53,.47-.88,2.88,.66,2.41,8.11-2.47,15.89-6.34,22.57-11.59,3.04-2.38,5.73-5.52,5.97-9.55,.21-3.46-2.09-7.07-5.68-7.57-1.57-.22-2.25,2.19-.66,2.41h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M85.05,190.29l-.83,3.58" style="fill:#fff;" />
|
||||
<path
|
||||
d="M83.84,189.96l-.83,3.58c-.15,.64,.2,1.38,.87,1.54,.64,.15,1.38-.19,1.54-.87l.83-3.58c.15-.64-.2-1.38-.87-1.54-.64-.15-1.38,.19-1.54,.87h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M84.5,198.52c-.06,.99-.02,1.98,.13,2.96" style="fill:#fff;" />
|
||||
<path
|
||||
d="M83.25,198.52c-.07,1.1,0,2.2,.17,3.29,.04,.29,.33,.61,.57,.75,.27,.16,.66,.22,.96,.13s.59-.29,.75-.57l.13-.3c.06-.22,.06-.44,0-.66-.01-.08-.02-.15-.03-.23l.04,.33c-.12-.9-.15-1.82-.1-2.73,.02-.31-.15-.67-.37-.88s-.57-.38-.88-.37-.66,.12-.88,.37-.34,.54-.37,.88h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M72.52,210.78c-.46,1.52-.91,3.05-1.05,4.64s.08,3.24,.9,4.6" style="fill:#fff;" />
|
||||
<path
|
||||
d="M71.31,210.44c-.99,3.32-1.88,7-.02,10.2,.81,1.39,2.97,.13,2.16-1.26-1.47-2.53-.52-5.62,.28-8.27,.19-.65-.23-1.36-.87-1.54s-1.34,.22-1.54,.87h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M84.74,205.93c-.61,1.54-.48,3.36,.34,4.8" style="fill:#fff;" />
|
||||
<path
|
||||
d="M83.54,205.6c-.71,1.86-.5,4.03,.46,5.76,.32,.57,1.15,.81,1.71,.45s.79-1.1,.45-1.71c-.05-.09-.1-.18-.15-.28-.02-.05-.05-.09-.07-.14-.05-.12-.04-.1,.03,.08,.01-.04-.08-.21-.1-.26-.03-.09-.06-.17-.08-.26-.06-.2-.11-.41-.14-.61v-.08c-.03-.14-.03-.1,0,.1,.02-.09-.02-.22-.03-.31-.01-.18-.01-.37,0-.55,0-.11,0-.21,.02-.31,0-.05,0-.1,.02-.16-.03,.2-.03,.23-.01,.1,.07-.39,.17-.77,.31-1.14,.23-.61-.26-1.4-.87-1.54-.71-.16-1.29,.22-1.54,.87h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M52.32,198.92c-4.57,5.84-6,13.57-6.04,20.99-.02,2.89,.15,5.85-.65,8.63" style="fill:#fff;" />
|
||||
<path
|
||||
d="M51.25,198.29c-3.09,4.01-4.82,8.88-5.6,13.85-.38,2.44-.58,4.9-.61,7.36-.04,2.92,.16,5.85-.61,8.71-.42,1.56,1.99,2.22,2.41,.66,.71-2.63,.7-5.34,.69-8.04,0-2.54,.09-5.09,.46-7.61,.72-4.93,2.35-9.68,5.41-13.66,.98-1.27-1.19-2.52-2.16-1.26h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M43.94,197.7c-.58,3.78-3.36,6.94-6.64,8.9s-7.05,2.93-10.75,3.87" style="fill:#fff;" />
|
||||
<path
|
||||
d="M42.73,197.37c-1.3,7.8-9.97,10.24-16.52,11.9-1.56,.39-.9,2.81,.66,2.41,4.15-1.05,8.48-2.15,12.04-4.64,3.06-2.14,5.59-5.25,6.22-9.01,.26-1.57-2.15-2.25-2.41-.66h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M91.16,173.72c.9,.4,1.5,1.25,2.27,1.85,1.63,1.28,3.91,1.36,5.95,.98,.45-.08,.91-.19,1.3-.43s.71-.64,.75-1.1c.06-.7-.53-1.28-1.06-1.74"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M90.53,174.79c.58,.28,.96,.69,1.44,1.14s1.03,.91,1.63,1.22c1.28,.66,2.75,.9,4.19,.83s3.25-.23,4.25-1.39c.56-.65,.78-1.51,.56-2.34-.2-.75-.77-1.36-1.34-1.86-.51-.44-1.27-.5-1.77,0-.45,.45-.51,1.32,0,1.77,.19,.17,.4,.34,.56,.54,0,0,.17,.36,.14,.17,0-.03,.04-.03,.05-.05-.06,.15-.05,.1-.12,.17-.11,.1-.29,.17-.46,.21-.96,.28-1.98,.34-2.99,.26-.84-.07-1.76-.34-2.47-.89-.81-.63-1.44-1.49-2.4-1.95-.61-.29-1.35-.17-1.71,.45-.32,.55-.16,1.42,.45,1.71h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M1.25,178.57c8.14-4.37,16.49-8.51,25.42-10.9,8.93-2.39,16.85-8.86,25.66-6.05,3.11,.99,4.86,.29,7.84,1.63,7.16,3.22,14.5,6.16,22.19,7.73,.92,.19,1.85,.36,2.75,.64,.76,.24,1.49,.57,2.23,.85,3.03,1.13,6.35,1.46,9.56,1.07,1.51-.18,3.03-.52,4.52-.25s2.98,1.36,3.1,2.87c.15,1.87-1.69,3.22-3.31,4.16-5.19,3.04-10.67,6.16-16.68,6.31-1.06,.03-2.12-.04-3.16,.13-.96,.16-1.87,.52-2.8,.81-5.16,1.61-10.98,.91-15.62-1.86-.45-.27-.97-.56-1.45-.36-.27,.11-.46,.36-.64,.6"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M1.88,179.65c4.86-2.6,9.78-5.14,14.87-7.25,2.49-1.03,5.01-1.96,7.58-2.76,2.41-.74,4.83-1.37,7.18-2.29,4.42-1.74,8.72-4.06,13.44-4.92,2.29-.42,4.6-.32,6.84,.33s4.33,.55,6.45,1.12c2.51,.67,4.91,2.06,7.31,3.05s5.01,1.99,7.57,2.84,5.29,1.63,7.99,2.22c1.22,.27,2.46,.45,3.65,.83,1.32,.42,2.57,1.02,3.92,1.38,2.52,.68,5.18,.89,7.77,.63,1.78-.18,5.97-1.37,6.75,1.15,.63,2.02-3.24,3.63-4.54,4.37-2.34,1.34-4.72,2.63-7.25,3.57-2.8,1.04-5.51,1.41-8.47,1.47-2.79,.06-5.28,1.39-8.03,1.68s-5.61-.04-8.26-.98c-2.04-.72-4.85-3.38-6.68-1.03-.98,1.25,.78,3.03,1.77,1.77,.36-.46,.86,.14,1.3,.37,.49,.26,.99,.5,1.5,.72,1.24,.54,2.53,.97,3.85,1.26,2.53,.55,5.13,.63,7.68,.24,1.24-.19,2.43-.52,3.63-.9,1.41-.46,2.68-.62,4.16-.63,2.68-.02,5.3-.5,7.82-1.4s5.08-2.18,7.48-3.54c2-1.13,4.41-2.21,5.78-4.13,1.29-1.8,.98-4.13-.71-5.56-2.12-1.78-4.81-1.28-7.34-.97-2.82,.34-5.69,.13-8.4-.74-1.34-.43-2.61-1.04-3.97-1.39s-2.89-.6-4.32-.94c-5.66-1.37-11.12-3.43-16.46-5.74-2.36-1.02-4.53-2.1-7.12-2.39-1.12-.12-2.24-.2-3.33-.49-1.24-.33-2.43-.68-3.71-.82-4.99-.56-9.77,1.38-14.26,3.29-2.63,1.12-5.26,2.29-8,3.12-3.06,.92-6.11,1.8-9.1,2.95-6.07,2.35-11.88,5.29-17.61,8.36-1.42,.76-.16,2.92,1.26,2.16h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<path d="M69.76,189.67c1.61,0,1.61-2.5,0-2.5s-1.61,2.5,0,2.5h0Z" style="fill:#231f20;" />
|
||||
<g>
|
||||
<path
|
||||
d="M91.16,173.56c.9,.4,1.5,1.25,2.27,1.85,1.63,1.28,3.91,1.36,5.95,.98,.45-.08,.91-.19,1.3-.43s.71-.64,.75-1.1c.06-.7-.53-1.28-1.06-1.74"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M90.53,174.64c.58,.28,.96,.69,1.44,1.14s1.03,.91,1.63,1.22c1.28,.66,2.75,.9,4.19,.83s3.25-.23,4.25-1.39c.56-.65,.78-1.51,.56-2.34-.2-.75-.77-1.36-1.34-1.86-.51-.44-1.27-.5-1.77,0-.45,.45-.51,1.32,0,1.77,.19,.17,.4,.34,.56,.54,0,0,.17,.36,.14,.17,0-.03,.04-.03,.05-.05-.06,.15-.05,.1-.12,.17-.11,.1-.29,.17-.46,.21-.96,.28-1.98,.34-2.99,.26-.84-.07-1.76-.34-2.47-.89-.81-.63-1.44-1.49-2.4-1.95-.61-.29-1.35-.17-1.71,.45-.32,.55-.16,1.42,.45,1.71h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M107.25,200.34c-.57,.69-1.44,1.13-2.4,1.13-1.72,0-3.12-1.4-3.12-3.12,0-.71,.24-1.36,.63-1.88l5.79-6.56c.56-.56,1.34-.91,2.2-.91,1.72,0,3.12,1.4,3.12,3.12,0,.91-.39,1.72-1,2.29l-5.22,5.93Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<path
|
||||
d="M107.97,198.36c0,.75-.27,1.45-.71,1.99-.57,.69-1.44,1.13-2.4,1.13-1.72,0-3.12-1.4-3.12-3.12,0-.71,.24-1.36,.63-1.88,.57-.75,1.47-1.23,2.48-1.23,1.72,0,3.12,1.4,3.12,3.12Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="142.87 164.83 137.76 166.52 132.66 164.83 132.66 109.62 131.53 107.91 131.53 60.47 144 60.47 144 107.91 142.87 109.62 142.87 164.83"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="132.66" y1="109.62" x2="142.87" y2="109.62"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="132.66" y1="164.09" x2="142.87" y2="159.3"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="158.09" x2="142.87" y2="153.3"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="152.07" x2="142.87" y2="147.28"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="146.09" x2="142.87" y2="141.31"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="140.07" x2="142.87" y2="135.28"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="133.87" x2="142.87" y2="129.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="127.85" x2="142.87" y2="123.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="121.87" x2="142.87" y2="117.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="115.85" x2="142.87" y2="111.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<path d="M169.68,0V57.54c0,2.23-1.82,4.05-4.05,4.05h-56.36c-2.23,0-4.05-1.82-4.05-4.05V0"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<rect x="125.74" y="61.6" width="24.04" height="24.04"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<rect x="122.08" y="85.64" width="31.37" height="19.04"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="134.29" y1="85.64" x2="134.29" y2="104.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="148.31" y1="85.64" x2="148.31" y2="104.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<ellipse cx="128.14" cy="95.16" rx="2.39" ry="4.57"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<path
|
||||
d="M113.51,99.73c-1.32,0-2.39-2.05-2.39-4.57s1.07-4.57,2.39-4.57h14.44c1.32,0,2.39,2.05,2.39,4.57s-1.07,4.57-2.39,4.57h-14.44Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<ellipse cx="113.51" cy="95.16" rx="2.39" ry="4.57"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<g>
|
||||
<g>
|
||||
<line x1="127.88" y1="202.38" x2="127.88" y2="205.88"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="127.88" y1="207.38" x2="127.88" y2="220.16"
|
||||
style="fill:none; stroke:#37b34a; stroke-dasharray:0 0 0 0 5.26 1.5 5.26 1.5; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="127.88" y1="220.92" x2="127.88" y2="224.42"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<polyline points="122.99 207.33 127.88 201.28 132.72 207.33"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<line x1="95.86" y1="94.9" x2="92.36" y2="94.9"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="90.8" y1="94.9" x2="77.59" y2="94.9"
|
||||
style="fill:none; stroke:#37b34a; stroke-dasharray:0 0 0 0 5.44 1.55 5.44 1.55; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="76.81" y1="94.9" x2="56.6" y2="94.9"
|
||||
style="fill:none; stroke:#37b34a; stroke-dasharray:0 0 0 0 0 0 5.44 1.55 5.44 1.55 5.44 1.55; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="55.82" y1="94.9" x2="52.32" y2="94.9"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<polyline points="90.9 90 96.95 94.9 90.9 99.73"
|
||||
style="fill:none; stroke:#37b34a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<path
|
||||
d="M111.84,93.61c-5.44,.15-10.02,3.72-13.85,7.24-2.01,1.85-3.9,3.81-5.83,5.74s-4.16,3.88-6.06,6.02c-4.2,4.74-7.49,10.2-10.3,15.85-2.66,5.36-5.36,10.73-7.5,16.33-2.35,6.16-3.71,12.57-4.38,19.12-.1,1.01,.94,1.88,1.88,1.88,1.1,0,1.77-.86,1.88-1.88,.57-5.54,1.71-10.99,3.56-16.25s4.22-10.04,6.62-14.91c2.64-5.36,5.4-10.68,9.12-15.38,3.31-4.18,7.41-7.76,11.19-11.51s8.22-8.36,13.67-8.51c2.41-.07,2.42-3.82,0-3.75h0Z"
|
||||
style="fill:#231f20;" />
|
||||
<path
|
||||
d="M59.12,233.43c2.05,17.35-4.54,34.73-17.73,46.21-3.46,3.02-7.33,5.53-11.45,7.56-2.17,1.06-.27,4.3,1.89,3.24,16.06-7.89,27.69-23.39,30.62-41.06,.88-5.28,1.04-10.63,.41-15.94-.12-1.01-.78-1.88-1.88-1.88-.92,0-1.99,.86-1.88,1.88h0Z"
|
||||
style="fill:#231f20;" />
|
||||
<path
|
||||
d="M103.49,197.11c-7.6,6.84-13.89,15.16-18.3,24.39-2.34,4.9-4.19,10.04-7.16,14.61s-6.89,8.75-10.54,12.93c-4.48,5.13-8.96,10.26-13.44,15.38-1.58,1.81,1.06,4.47,2.65,2.65,3.93-4.5,7.87-9.01,11.8-13.51,3.72-4.25,7.58-8.42,10.96-12.96s5.44-9.33,7.66-14.32c2.1-4.74,4.55-9.28,7.51-13.54,3.31-4.76,7.2-9.1,11.52-12.98,1.8-1.62-.86-4.26-2.65-2.65h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 21 KiB |
275
src/svelte-components/src/svgs/probe-check-z.svg
Normal file
@@ -0,0 +1,275 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 307.88 288.65">
|
||||
<g>
|
||||
<polygon
|
||||
points="306.38 161.14 243.73 243.91 98.03 243.91 98.03 228.65 160.68 145.88 306.38 145.88 306.38 161.14"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="306.38 145.88 243.73 228.65 98.03 228.65"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="243.73" y1="228.65" x2="243.73" y2="243.91"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<line x1="146.89" y1="184.96" x2="146.89" y2="188.46"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:1.75px;" />
|
||||
<line x1="146.89" y1="189.76" x2="146.89" y2="200.85"
|
||||
style="fill:none; stroke:#39b54a; stroke-dasharray:0 0 0 0 4.56 1.3 4.56 1.3; stroke-miterlimit:10; stroke-width:1.75px;" />
|
||||
<line x1="146.89" y1="201.5" x2="146.89" y2="205"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:1.75px;" />
|
||||
</g>
|
||||
<polyline points="142.67 189.08 146.89 183.87 150.95 188.95"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:1.75px;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="150.06 172.24 132.23 195.79 90.77 195.79 90.77 184.45 108.6 160.89 118.18 160.89 118.19 163.9 150.06 163.9 150.06 172.24"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="149.25 164.78 137.31 180.56 137.3 178.27 136.9 178.28 132.23 184.45 90.77 184.45"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="132.23" y1="184.45" x2="132.23" y2="195.79"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<ellipse cx="138.4" cy="165.46" rx="3.83" ry="1.56" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<circle cx="110.61" cy="190.12" r="3.12" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<polygon
|
||||
points="149.65 164.78 149.65 172.23 131.83 195.79 131.83 184.45 136.5 178.28 136.9 178.27 136.91 180.56 148.85 164.78 149.65 164.78"
|
||||
style="fill:#fff;" />
|
||||
<polygon
|
||||
points="117.79 163.9 149.65 163.9 149.65 164.78 148.85 164.78 136.91 180.56 136.9 178.27 136.5 178.28 136.3 177.75 107.89 177.75 107.89 177.3 117.79 163.9"
|
||||
style="fill:#8dc63f;" />
|
||||
<polygon
|
||||
points="136.3 177.75 136.5 178.28 131.83 184.45 110.21 184.45 90.37 184.45 108.19 160.89 116.92 160.89 104.6 177.75 107.89 177.75 136.3 177.75"
|
||||
style="fill:#ed1c24;" />
|
||||
<path d="M131.83,184.45v11.34h-21.62v-2.55c1.72,0,3.12-1.4,3.12-3.12s-1.4-3.12-3.12-3.12v-2.55h21.62Z"
|
||||
style="fill:#fff;" />
|
||||
<circle cx="110.21" cy="190.12" r="3.12" style="fill:#fff;" />
|
||||
<path d="M110.21,193.24v2.55h-19.84v-11.34h19.84v2.55c-1.72,0-3.12,1.39-3.12,3.12s1.4,3.12,3.12,3.12Z"
|
||||
style="fill:#fff;" />
|
||||
<polygon
|
||||
points="104.6 177.75 116.92 160.89 117.77 160.89 117.79 163.9 107.89 177.3 107.89 177.75 104.6 177.75"
|
||||
style="fill:#fff;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="149.65 164.78 149.65 172.23 131.83 195.79 110.21 195.79 90.37 195.79 90.37 184.45 108.19 160.89 116.92 160.89 117.77 160.89 117.79 163.9 149.65 163.9 149.65 164.78"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline
|
||||
points="148.85 164.78 136.91 180.56 136.9 178.27 136.5 178.28 131.83 184.45 110.21 184.45 90.37 184.45"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="131.83" y1="184.45" x2="131.83" y2="195.79"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<circle cx="110.21" cy="190.12" r="3.12" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<polyline points="116.92 160.89 104.6 177.75 107.89 177.75 136.3 177.75"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="117.79" y1="163.9" x2="107.89" y2="177.3"
|
||||
style="fill:none; stroke:#231f20; stroke-linecap:round; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M1.25,176.73c8.14-4.37,16.49-8.51,25.42-10.9,8.93-2.39,16.85-8.86,25.66-6.05,3.11,.99,4.86,.29,7.84,1.63,7.16,3.22,14.5,6.16,22.19,7.73,.92,.19,1.85,.36,2.75,.64,.76,.24,1.49,.57,2.23,.85,3.03,1.13,6.35,1.46,9.56,1.07,1.51-.18,3.03-.52,4.52-.25s2.98,1.36,3.1,2.87c.15,1.87-1.69,3.22-3.31,4.16-5.19,3.04-10.67,6.16-16.68,6.31-1.06,.03-2.12-.04-3.16,.13-.96,.16-1.87,.52-2.8,.81-5.16,1.61-10.98,.91-15.62-1.86-.45-.27-.97-.56-1.45-.36-.27,.11-.46,.36-.64,.6-5.22,6.94-13.45,11.53-22.1,12.33"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M1.88,177.81c5.74-3.07,11.56-6.05,17.66-8.36,2.91-1.1,5.89-1.89,8.85-2.83,2.68-.84,5.25-1.98,7.82-3.08s5.22-2.2,7.99-2.8,5.43-.39,8.23,.37c2.52,.69,5.09,.51,7.53,1.56,2.93,1.27,5.84,2.56,8.82,3.7s6.08,2.19,9.2,3.02c1.49,.4,2.99,.73,4.5,1.04,1.66,.34,3.14,.9,4.73,1.47,2.8,1,5.85,1.37,8.81,1.13,1.5-.12,2.99-.5,4.5-.43,1.2,.05,3.18,.83,2.69,2.38-.36,1.13-1.67,1.85-2.62,2.42-1.33,.79-2.68,1.56-4.05,2.3-2.68,1.44-5.47,2.72-8.45,3.39s-6.08,.21-9.05,1.18-5.77,1.32-8.75,.89c-1.5-.22-3-.62-4.4-1.2s-2.85-1.93-4.35-1.72c-1.35,.19-2.09,1.71-2.9,2.64-.98,1.12-2.04,2.17-3.17,3.15-4.66,4.01-10.59,6.55-16.7,7.15-1.59,.16-1.6,2.66,0,2.5,5.78-.57,11.36-2.61,16.08-6.01,2.3-1.65,4.4-3.57,6.18-5.77,.18-.22,.7-1.1,.95-1.15,.34-.07,1.44,.81,1.81,.98,1.47,.71,3.01,1.26,4.61,1.6,2.93,.63,5.97,.65,8.91,.02,1.49-.32,2.91-1.01,4.41-1.23,1.65-.24,3.34-.05,5-.27,3.13-.41,6.08-1.52,8.91-2.88s5.65-2.73,7.99-4.62c2.05-1.66,3.05-4.46,1-6.58-2.35-2.43-5.65-1.48-8.6-1.24-3.43,.27-6.5-.44-9.69-1.64s-6.65-1.57-9.9-2.54-6.43-2.13-9.56-3.41c-1.61-.66-3.21-1.34-4.8-2.05-1.38-.61-2.73-1.24-4.23-1.49-1.3-.22-2.63-.22-3.92-.5-1.45-.31-2.83-.8-4.32-.97-2.8-.32-5.64,.17-8.31,1-3.25,1.01-6.32,2.49-9.46,3.78-3.37,1.38-6.89,2.22-10.33,3.39-7.25,2.48-14.12,5.94-20.86,9.55-1.42,.76-.16,2.92,1.26,2.16h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M90.74,191.11c-1.29,.17-2.57,.39-3.84,.65-.92,.19-1.85,.39-2.79,.36-.92-.04-1.82-.31-2.72-.51-4.42-.97-7.32-.5-11.78,.27"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M90.41,189.9c-1.83,.25-3.67,.77-5.5,.93s-3.78-.62-5.62-.86c-3.38-.43-6.69,.12-10.01,.69-1.58,.27-.91,2.68,.66,2.41,3.37-.58,6.73-1.08,10.14-.47,1.83,.33,3.52,.91,5.4,.66s3.72-.7,5.6-.96c1.59-.22,.91-2.63-.66-2.41h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M69.76,185.81c-1.62,2.32-1,5.49-1.75,8.21-.39,1.43-1.18,2.75-1.38,4.22-.21,1.55,.25,3.1,.55,4.63,.07,.36,.13,.74,0,1.09-.11,.31-.36,.55-.59,.8-1.37,1.46-2.16,3.44-2.16,5.44"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M68.68,185.18c-1.16,1.8-1.31,3.87-1.49,5.95-.09,1.07-.22,2.11-.57,3.14-.37,1.08-.89,2.12-1.12,3.24s-.17,2.25,.01,3.38c.1,.62,.27,1.23,.37,1.85,.12,.73,.02,.88-.44,1.43-1.45,1.71-2.2,3.8-2.26,6.03-.04,1.61,2.46,1.61,2.5,0,.03-1.13,.28-2.2,.82-3.2,.48-.88,1.34-1.51,1.76-2.41,.38-.82,.19-1.66,.03-2.51-.26-1.28-.63-2.59-.39-3.9,.36-1.96,1.39-3.66,1.65-5.68s.12-4.25,1.28-6.06c.88-1.36-1.29-2.61-2.16-1.26h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M67.04,204.15c1.3-.46,2.66-.76,4.03-.87,.35-.03,.69-.05,1.03-.11,.48-.09,.93-.27,1.39-.44,3.91-1.5,8.12-2.77,12.23-2.02"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M67.37,205.35c1.57-.54,3.1-.69,4.72-.93,1.39-.21,2.71-.89,4.04-1.33,2.97-.99,6.13-1.71,9.26-1.18,1.57,.27,2.25-2.14,.66-2.41-3.36-.57-6.69-.06-9.92,.96-1.44,.46-2.88,1.23-4.37,1.5-1.73,.31-3.37,.4-5.07,.98-1.51,.52-.86,2.94,.66,2.41h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M58.89,224.43c-2.05-4.85,.54-11.14,5.42-13.14,2.12-.87,4.51-.99,6.57-2,2.05-1,3.61-2.78,5.6-3.89,2.67-1.49,5.93-1.66,8.55-3.22,1.82-1.08,3.21-2.76,4.88-4.07,2.03-1.59,4.44-2.59,6.82-3.58,1.75-.73,3.72-1.46,5.49-.79,2.12,.8,3.09,3.41,2.7,5.64s-1.83,4.12-3.34,5.8c-7.7,8.57-18.19,14.03-28.44,19.3-2.72,1.4-5.45,2.8-8.28,3.96-2.26,.92-4.6,1.69-6.7,2.94-1.64,.98-3.1,2.23-4.66,3.35-5.43,3.91-11.97,6.11-18.6,6.96-6.63,.86-13.38,.43-19.99-.57"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M60.1,224.09c-1.55-3.87-.15-8.67,3.4-10.97,2.14-1.39,4.8-1.45,7.13-2.37s4.01-2.79,6.14-4.07c2.27-1.37,4.99-1.67,7.41-2.69s4.22-3.03,6.29-4.72,4.72-2.79,7.21-3.78c2.31-.91,5.06-1.29,5.9,1.63,.7,2.44-.84,4.79-2.36,6.57-1.71,2.01-3.64,3.85-5.65,5.56-4.04,3.43-8.55,6.28-13.16,8.88s-9.7,5.28-14.66,7.65c-2.68,1.28-5.5,2.22-8.2,3.46-2.54,1.16-4.6,2.92-6.86,4.52-10.84,7.7-24.78,8.03-37.46,6.14-1.57-.23-2.25,2.17-.66,2.41,12.35,1.84,25.58,1.74,36.69-4.67,2.65-1.53,4.89-3.64,7.52-5.2,2.79-1.66,5.97-2.56,8.92-3.9,5.5-2.5,10.9-5.39,16.16-8.37s9.92-6.02,14.27-9.85c3.62-3.19,8.96-7.83,8-13.24-.39-2.22-1.92-4.25-4.2-4.73-2.8-.6-5.73,.98-8.21,2.11-2.8,1.27-4.98,2.98-7.24,5.02-1.1,.99-2.27,1.84-3.66,2.37s-2.94,.87-4.39,1.36-2.72,1.1-3.93,1.98c-1.3,.94-2.48,2.07-3.9,2.83-2.74,1.46-6.03,1.3-8.65,3.12-4.22,2.94-6.21,8.78-4.27,13.61,.59,1.47,3.01,.83,2.41-.66h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M110.32,189.95c2.31,.23,4.18,2.27,4.64,4.55s-.3,4.67-1.62,6.57c-1.32,1.91-3.14,3.4-5.01,4.77-6.3,4.62-13.39,8.15-20.87,10.4"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M109.98,191.15c3.4,.48,4.45,4.11,3.45,7.02-1.14,3.32-4.23,5.56-6.99,7.49-5.91,4.13-12.43,7.28-19.32,9.38-1.53,.47-.88,2.88,.66,2.41,8.11-2.47,15.89-6.34,22.57-11.59,3.04-2.38,5.73-5.52,5.97-9.55,.21-3.46-2.09-7.07-5.68-7.57-1.57-.22-2.25,2.19-.66,2.41h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M85.05,188.29l-.83,3.58" style="fill:#fff;" />
|
||||
<path
|
||||
d="M83.84,187.96l-.83,3.58c-.15,.64,.2,1.38,.87,1.54,.64,.15,1.38-.19,1.54-.87l.83-3.58c.15-.64-.2-1.38-.87-1.54-.64-.15-1.38,.19-1.54,.87h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M84.5,196.52c-.06,.99-.02,1.98,.13,2.96" style="fill:#fff;" />
|
||||
<path
|
||||
d="M83.25,196.52c-.07,1.1,0,2.2,.17,3.29,.04,.29,.33,.61,.57,.75,.27,.16,.66,.22,.96,.13s.59-.29,.75-.57l.13-.3c.06-.22,.06-.44,0-.66-.01-.08-.02-.15-.03-.23l.04,.33c-.12-.9-.15-1.82-.1-2.73,.02-.31-.15-.67-.37-.88s-.57-.38-.88-.37-.66,.12-.88,.37-.34,.54-.37,.88h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M72.52,208.78c-.46,1.52-.91,3.05-1.05,4.64s.08,3.24,.9,4.6" style="fill:#fff;" />
|
||||
<path
|
||||
d="M71.31,208.44c-.99,3.32-1.88,7-.02,10.2,.81,1.39,2.97,.13,2.16-1.26-1.47-2.53-.52-5.62,.28-8.27,.19-.65-.23-1.36-.87-1.54s-1.34,.22-1.54,.87h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M84.74,203.93c-.61,1.54-.48,3.36,.34,4.8" style="fill:#fff;" />
|
||||
<path
|
||||
d="M83.54,203.6c-.71,1.86-.5,4.03,.46,5.76,.32,.57,1.15,.81,1.71,.45s.79-1.1,.45-1.71c-.05-.09-.1-.18-.15-.28-.02-.05-.05-.09-.07-.14-.05-.12-.04-.1,.03,.08,.01-.04-.08-.21-.1-.26-.03-.09-.06-.17-.08-.26-.06-.2-.11-.41-.14-.61v-.08c-.03-.14-.03-.1,0,.1,.02-.09-.02-.22-.03-.31-.01-.18-.01-.37,0-.55,0-.11,0-.21,.02-.31,0-.05,0-.1,.02-.16-.03,.2-.03,.23-.01,.1,.07-.39,.17-.77,.31-1.14,.23-.61-.26-1.4-.87-1.54-.71-.16-1.29,.22-1.54,.87h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M52.32,196.92c-4.57,5.84-6,13.57-6.04,20.99-.02,2.89,.15,5.85-.65,8.63" style="fill:#fff;" />
|
||||
<path
|
||||
d="M51.25,196.29c-3.09,4.01-4.82,8.88-5.6,13.85-.38,2.44-.58,4.9-.61,7.36-.04,2.92,.16,5.85-.61,8.71-.42,1.56,1.99,2.22,2.41,.66,.71-2.63,.7-5.34,.69-8.04,0-2.54,.09-5.09,.46-7.61,.72-4.93,2.35-9.68,5.41-13.66,.98-1.27-1.19-2.52-2.16-1.26h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path d="M43.94,195.7c-.58,3.78-3.36,6.94-6.64,8.9s-7.05,2.93-10.75,3.87" style="fill:#fff;" />
|
||||
<path
|
||||
d="M42.73,195.37c-1.3,7.8-9.97,10.24-16.52,11.9-1.56,.39-.9,2.81,.66,2.41,4.15-1.05,8.48-2.15,12.04-4.64,3.06-2.14,5.59-5.25,6.22-9.01,.26-1.57-2.15-2.25-2.41-.66h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M91.16,171.72c.9,.4,1.5,1.25,2.27,1.85,1.63,1.28,3.91,1.36,5.95,.98,.45-.08,.91-.19,1.3-.43s.71-.64,.75-1.1c.06-.7-.53-1.28-1.06-1.74"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M90.53,172.79c.58,.28,.96,.69,1.44,1.14s1.03,.91,1.63,1.22c1.28,.66,2.75,.9,4.19,.83s3.25-.23,4.25-1.39c.56-.65,.78-1.51,.56-2.34-.2-.75-.77-1.36-1.34-1.86-.51-.44-1.27-.5-1.77,0-.45,.45-.51,1.32,0,1.77,.19,.17,.4,.34,.56,.54,0,0,.17,.36,.14,.17,0-.03,.04-.03,.05-.05-.06,.15-.05,.1-.12,.17-.11,.1-.29,.17-.46,.21-.96,.28-1.98,.34-2.99,.26-.84-.07-1.76-.34-2.47-.89-.81-.63-1.44-1.49-2.4-1.95-.61-.29-1.35-.17-1.71,.45-.32,.55-.16,1.42,.45,1.71h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M1.25,176.57c8.14-4.37,16.49-8.51,25.42-10.9,8.93-2.39,16.85-8.86,25.66-6.05,3.11,.99,4.86,.29,7.84,1.63,7.16,3.22,14.5,6.16,22.19,7.73,.92,.19,1.85,.36,2.75,.64,.76,.24,1.49,.57,2.23,.85,3.03,1.13,6.35,1.46,9.56,1.07,1.51-.18,3.03-.52,4.52-.25s2.98,1.36,3.1,2.87c.15,1.87-1.69,3.22-3.31,4.16-5.19,3.04-10.67,6.16-16.68,6.31-1.06,.03-2.12-.04-3.16,.13-.96,.16-1.87,.52-2.8,.81-5.16,1.61-10.98,.91-15.62-1.86-.45-.27-.97-.56-1.45-.36-.27,.11-.46,.36-.64,.6"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M1.88,177.65c4.86-2.6,9.78-5.14,14.87-7.25,2.49-1.03,5.01-1.96,7.58-2.76,2.41-.74,4.83-1.37,7.18-2.29,4.42-1.74,8.72-4.06,13.44-4.92,2.29-.42,4.6-.32,6.84,.33s4.33,.55,6.45,1.12c2.51,.67,4.91,2.06,7.31,3.05s5.01,1.99,7.57,2.84,5.29,1.63,7.99,2.22c1.22,.27,2.46,.45,3.65,.83,1.32,.42,2.57,1.02,3.92,1.38,2.52,.68,5.18,.89,7.77,.63,1.78-.18,5.97-1.37,6.75,1.15,.63,2.02-3.24,3.63-4.54,4.37-2.34,1.34-4.72,2.63-7.25,3.57-2.8,1.04-5.51,1.41-8.47,1.47-2.79,.06-5.28,1.39-8.03,1.68s-5.61-.04-8.26-.98c-2.04-.72-4.85-3.38-6.68-1.03-.98,1.25,.78,3.03,1.77,1.77,.36-.46,.86,.14,1.3,.37,.49,.26,.99,.5,1.5,.72,1.24,.54,2.53,.97,3.85,1.26,2.53,.55,5.13,.63,7.68,.24,1.24-.19,2.43-.52,3.63-.9,1.41-.46,2.68-.62,4.16-.63,2.68-.02,5.3-.5,7.82-1.4s5.08-2.18,7.48-3.54c2-1.13,4.41-2.21,5.78-4.13,1.29-1.8,.98-4.13-.71-5.56-2.12-1.78-4.81-1.28-7.34-.97-2.82,.34-5.69,.13-8.4-.74-1.34-.43-2.61-1.04-3.97-1.39s-2.89-.6-4.32-.94c-5.66-1.37-11.12-3.43-16.46-5.74-2.36-1.02-4.53-2.1-7.12-2.39-1.12-.12-2.24-.2-3.33-.49-1.24-.33-2.43-.68-3.71-.82-4.99-.56-9.77,1.38-14.26,3.29-2.63,1.12-5.26,2.29-8,3.12-3.06,.92-6.11,1.8-9.1,2.95-6.07,2.35-11.88,5.29-17.61,8.36-1.42,.76-.16,2.92,1.26,2.16h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
<path d="M69.76,187.67c1.61,0,1.61-2.5,0-2.5s-1.61,2.5,0,2.5h0Z" style="fill:#231f20;" />
|
||||
<g>
|
||||
<path
|
||||
d="M91.16,171.56c.9,.4,1.5,1.25,2.27,1.85,1.63,1.28,3.91,1.36,5.95,.98,.45-.08,.91-.19,1.3-.43s.71-.64,.75-1.1c.06-.7-.53-1.28-1.06-1.74"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M90.53,172.64c.58,.28,.96,.69,1.44,1.14s1.03,.91,1.63,1.22c1.28,.66,2.75,.9,4.19,.83s3.25-.23,4.25-1.39c.56-.65,.78-1.51,.56-2.34-.2-.75-.77-1.36-1.34-1.86-.51-.44-1.27-.5-1.77,0-.45,.45-.51,1.32,0,1.77,.19,.17,.4,.34,.56,.54,0,0,.17,.36,.14,.17,0-.03,.04-.03,.05-.05-.06,.15-.05,.1-.12,.17-.11,.1-.29,.17-.46,.21-.96,.28-1.98,.34-2.99,.26-.84-.07-1.76-.34-2.47-.89-.81-.63-1.44-1.49-2.4-1.95-.61-.29-1.35-.17-1.71,.45-.32,.55-.16,1.42,.45,1.71h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M107.25,198.34c-.57,.69-1.44,1.13-2.4,1.13-1.72,0-3.12-1.4-3.12-3.12,0-.71,.24-1.36,.63-1.88l5.79-6.56c.56-.56,1.34-.91,2.2-.91,1.72,0,3.12,1.4,3.12,3.12,0,.91-.39,1.72-1,2.29l-5.22,5.93Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<path
|
||||
d="M107.97,196.36c0,.75-.27,1.45-.71,1.99-.57,.69-1.44,1.13-2.4,1.13-1.72,0-3.12-1.4-3.12-3.12,0-.71,.24-1.36,.63-1.88,.57-.75,1.47-1.23,2.48-1.23,1.72,0,3.12,1.4,3.12,3.12Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="142.87 164.83 137.76 166.52 132.66 164.83 132.66 109.62 131.53 107.91 131.53 60.47 144 60.47 144 107.91 142.87 109.62 142.87 164.83"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="132.66" y1="109.62" x2="142.87" y2="109.62"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="132.66" y1="164.09" x2="142.87" y2="159.3"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="158.09" x2="142.87" y2="153.3"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="152.07" x2="142.87" y2="147.28"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="146.09" x2="142.87" y2="141.31"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="140.07" x2="142.87" y2="135.28"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="133.87" x2="142.87" y2="129.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="127.85" x2="142.87" y2="123.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="121.87" x2="142.87" y2="117.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="132.66" y1="115.85" x2="142.87" y2="111.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<path d="M169.68,0V57.54c0,2.23-1.82,4.05-4.05,4.05h-56.36c-2.23,0-4.05-1.82-4.05-4.05V0"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<rect x="125.74" y="61.6" width="24.04" height="24.04"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<rect x="122.08" y="85.64" width="31.37" height="19.04"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="134.29" y1="85.64" x2="134.29" y2="104.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="148.31" y1="85.64" x2="148.31" y2="104.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<ellipse cx="128.14" cy="95.16" rx="2.39" ry="4.57"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<path
|
||||
d="M113.51,99.73c-1.32,0-2.39-2.05-2.39-4.57s1.07-4.57,2.39-4.57h14.44c1.32,0,2.39,2.05,2.39,4.57s-1.07,4.57-2.39,4.57h-14.44Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<ellipse cx="113.51" cy="95.16" rx="2.39" ry="4.57"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<g>
|
||||
<g>
|
||||
<line x1="127.88" y1="200.38" x2="127.88" y2="203.88"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="127.88" y1="205.38" x2="127.88" y2="218.16"
|
||||
style="fill:none; stroke:#39b54a; stroke-dasharray:0 0 0 0 5.26 1.5 5.26 1.5; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="127.88" y1="218.92" x2="127.88" y2="222.42"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<polyline points="122.99 205.33 127.88 199.28 132.72 205.33"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<line x1="95.86" y1="94.9" x2="92.36" y2="94.9"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="90.8" y1="94.9" x2="77.59" y2="94.9"
|
||||
style="fill:none; stroke:#39b54a; stroke-dasharray:0 0 0 0 5.44 1.55 5.44 1.55; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="76.81" y1="94.9" x2="56.6" y2="94.9"
|
||||
style="fill:none; stroke:#39b54a; stroke-dasharray:0 0 0 0 0 0 5.44 1.55 5.44 1.55 5.44 1.55; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="55.82" y1="94.9" x2="52.32" y2="94.9"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<polyline points="90.9 90 96.95 94.9 90.9 99.73"
|
||||
style="fill:none; stroke:#39b54a; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<path
|
||||
d="M111.84,93.61c-5.44,.15-10.02,3.72-13.85,7.24-2.01,1.85-3.9,3.81-5.83,5.74s-4.16,3.88-6.06,6.02c-4.2,4.74-7.48,10.2-10.3,15.85-2.6,5.24-5.32,10.43-7.5,15.87-2.39,5.97-3.72,12.19-4.38,18.58-.1,1.01,.94,1.88,1.88,1.88,1.1,0,1.77-.86,1.88-1.88,.56-5.4,1.68-10.7,3.56-15.79s4.27-9.66,6.62-14.38c2.66-5.35,5.4-10.67,9.12-15.37,3.31-4.18,7.41-7.76,11.19-11.51s8.22-8.36,13.67-8.51c2.41-.07,2.42-3.82,0-3.75h0Z"
|
||||
style="fill:#231f20;" />
|
||||
<path
|
||||
d="M59.12,231.43c2.05,17.35-4.54,34.73-17.73,46.21-3.46,3.02-7.33,5.53-11.45,7.56-2.17,1.06-.27,4.3,1.89,3.24,16.06-7.89,27.69-23.39,30.62-41.06,.88-5.28,1.04-10.63,.41-15.94-.12-1.01-.78-1.88-1.88-1.88-.92,0-1.99,.86-1.88,1.88h0Z"
|
||||
style="fill:#231f20;" />
|
||||
<path
|
||||
d="M103.49,195.11c-7.6,6.84-13.89,15.16-18.3,24.39-2.34,4.9-4.19,10.04-7.16,14.61s-6.89,8.75-10.54,12.93c-4.48,5.13-8.96,10.26-13.44,15.38-1.58,1.81,1.06,4.47,2.65,2.65,3.93-4.5,7.87-9.01,11.8-13.51,3.72-4.25,7.58-8.42,10.96-12.96s5.44-9.33,7.66-14.32c2.1-4.74,4.55-9.28,7.51-13.54,3.31-4.76,7.2-9.1,11.52-12.98,1.8-1.62-.86-4.26-2.65-2.65h0Z"
|
||||
style="fill:#231f20;" />
|
||||
<polyline points="117.33 160.89 105.01 177.76 136.71 177.76"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="118.19" y1="163.9" x2="108.3" y2="177.3"
|
||||
style="fill:none; stroke:#231f20; stroke-linecap:round; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
49
src/svelte-components/src/svgs/probe-place-xyz.svg
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 303.91 128.94">
|
||||
<polygon
|
||||
points="167.2 14.19 171.09 20.83 167.49 22.75 163.59 16.12 152.92 21.83 151.59 19.59 162.28 13.87 158.38 7.24 161.99 5.31 165.88 11.95 176.56 6.23 177.87 8.48 167.2 14.19"
|
||||
style="fill:#be1e2d;" />
|
||||
<polygon
|
||||
points="277.18 14.19 281.07 20.83 277.47 22.75 273.57 16.12 262.9 21.83 261.58 19.59 272.26 13.87 268.36 7.24 271.97 5.31 275.86 11.95 286.55 6.23 287.86 8.48 277.18 14.19"
|
||||
style="fill:#be1e2d;" />
|
||||
<polygon
|
||||
points="234.48 71.98 238.37 78.62 234.77 80.55 230.87 73.91 220.2 79.62 218.88 77.38 229.56 71.67 225.66 65.03 229.27 63.1 233.16 69.74 243.85 64.02 245.15 66.27 234.48 71.98"
|
||||
style="fill:#be1e2d;" />
|
||||
<polygon
|
||||
points="200.71 41.95 204.59 48.59 200.99 50.51 197.09 43.88 186.42 49.59 185.1 47.35 195.78 41.63 191.88 35 195.5 33.07 199.38 39.71 210.07 33.99 211.38 36.24 200.71 41.95"
|
||||
style="fill:#be1e2d;" />
|
||||
<polyline points="112.27 66.96 118.93 76.72 136.87 63.1"
|
||||
style="fill:none; stroke:#00a651; stroke-miterlimit:10; stroke-width:4px;" />
|
||||
<polygon points="302.41 16.76 239.77 99.53 94.07 99.53 94.07 84.27 156.71 1.5 302.41 1.5 302.41 16.76"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="302.41 1.5 239.77 84.27 94.07 84.27"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="239.77" y1="84.27" x2="239.77" y2="99.53"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polygon points="60.79 103.89 42.96 127.44 1.5 127.44 1.5 116.1 19.33 92.55 60.79 92.55 60.79 103.89"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="60.79 92.55 42.96 116.1 1.5 116.1"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="42.96" y1="116.1" x2="42.96" y2="127.44"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<ellipse cx="49.13" cy="97.11" rx="3.83" ry="1.56" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<circle cx="21.34" cy="121.77" r="3.12" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<g>
|
||||
<polygon points="34.63 102.43 31.5 99.01 29.54 101.63 32.67 105.05 34.63 102.43" style="fill:#bcbec0;" />
|
||||
<polygon points="31.52 106.57 28.39 103.16 26.42 105.77 29.56 109.19 31.52 106.57" style="fill:#bcbec0;" />
|
||||
<polygon points="27.82 111.51 28.41 110.72 25.28 107.3 22.12 111.51 25.2 111.2 27.82 111.51"
|
||||
style="fill:#bcbec0;" />
|
||||
<polygon points="38.61 97.14 32.91 97.14 32.65 97.49 35.78 100.91 38.61 97.14" style="fill:#bcbec0;" />
|
||||
</g>
|
||||
<g>
|
||||
<line x1="93.77" y1="75.66" x2="91.3" y2="76.07"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="87.94" y1="76.63" x2="71.68" y2="79.35"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 5.68 3.41 5.68 3.41; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="70" y1="79.64" x2="44.78" y2="83.86"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 0 0 5.68 3.41 5.68 3.41 5.68 3.41; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="43.1" y1="84.14" x2="40.63" y2="84.55"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
</g>
|
||||
<polyline points="82.19 71.95 93.77 75.66 85.96 83.49"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
60
src/svelte-components/src/svgs/probe-place-z.svg
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 326.35 123.59">
|
||||
<polygon points="324.85 16.76 262.21 99.53 116.51 99.53 116.51 84.27 179.15 1.5 324.85 1.5 324.85 16.76"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polygon points="206.56 67.29 185.57 36.09 197.9 27.68 210.04 45.9 256.99 10.26 266.02 22.15 206.56 67.29"
|
||||
style="fill:#00a651;" />
|
||||
<polyline points="324.85 1.5 262.21 84.27 116.51 84.27"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="262.21" y1="84.27" x2="262.21" y2="99.53"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<g>
|
||||
<line x1="159.81" y1="65.69" x2="157.36" y2="66.17"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="154.33" y1="66.76" x2="139.7" y2="69.61"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 5.14 3.08 5.14 3.08; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="138.19" y1="69.91" x2="67.04" y2="83.78"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-dasharray:0 0 0 0 0 0 5.14 3.08 5.14 3.08 5.14 3.08; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="65.53" y1="84.07" x2="63.07" y2="84.55"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
</g>
|
||||
<polyline points="151.51 61.52 163.08 65.23 155.28 73.06"
|
||||
style="fill:none; stroke:#a7a9ac; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<g>
|
||||
<g>
|
||||
<polygon
|
||||
points="60.79 91.08 60.79 98.53 42.96 122.09 42.96 110.74 47.63 104.58 48.03 104.56 48.04 106.86 59.98 91.08 60.79 91.08"
|
||||
style="fill:#fff;" />
|
||||
<polygon
|
||||
points="28.92 90.2 60.79 90.2 60.79 91.08 59.98 91.08 48.04 106.86 48.03 104.56 47.63 104.58 47.44 104.05 19.03 104.05 19.03 103.6 28.92 90.2"
|
||||
style="fill:#8dc63f;" />
|
||||
<polygon
|
||||
points="47.44 104.05 47.63 104.58 42.96 110.74 21.34 110.74 1.5 110.74 19.33 87.19 28.06 87.19 15.74 104.05 19.03 104.05 47.44 104.05"
|
||||
style="fill:#ed1c24;" />
|
||||
<path d="M42.96,110.74v11.35H21.34v-2.56c1.73,0,3.12-1.39,3.12-3.11s-1.39-3.12-3.12-3.12v-2.56h21.62Z"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M21.34,113.3c1.73,0,3.12,1.39,3.12,3.12s-1.39,3.11-3.12,3.11-3.11-1.39-3.11-3.11,1.39-3.12,3.11-3.12Z"
|
||||
style="fill:#fff;" />
|
||||
<path d="M21.34,119.53v2.56H1.5v-11.35H21.34v2.56c-1.72,0-3.11,1.39-3.11,3.12s1.39,3.11,3.11,3.11Z"
|
||||
style="fill:#fff;" />
|
||||
<polygon points="15.74 104.05 28.06 87.19 28.91 87.19 28.92 90.2 19.03 103.6 19.03 104.05 15.74 104.05"
|
||||
style="fill:#fff;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="60.79 91.08 60.79 98.53 42.96 122.09 21.34 122.09 1.5 122.09 1.5 110.74 19.33 87.19 28.06 87.19 28.91 87.19 28.92 90.2 60.79 90.2 60.79 91.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="59.98 91.08 48.04 106.86 48.03 104.56 47.63 104.58 42.96 110.74 21.34 110.74 1.5 110.74"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="42.96" y1="110.74" x2="42.96" y2="122.09"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<path
|
||||
d="M21.34,113.3c1.73,0,3.12,1.39,3.12,3.12s-1.39,3.11-3.12,3.11-3.11-1.39-3.11-3.11,1.39-3.12,3.11-3.12Z"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<polyline points="28.06 87.19 15.74 104.05 19.03 104.05 47.44 104.05"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="28.92" y1="90.2" x2="19.03" y2="103.6"
|
||||
style="fill:none; stroke:#231f20; stroke-linecap:round; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
115
src/svelte-components/src/svgs/probe-put-away-xyz.svg
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 328.41 256.15">
|
||||
<g>
|
||||
<polygon
|
||||
points="326.91 163.14 264.27 245.91 118.57 245.91 118.57 230.65 181.21 147.88 326.91 147.88 326.91 163.14"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="326.91 147.88 264.27 230.65 118.57 230.65"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="264.27" y1="230.65" x2="264.27" y2="245.91"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon points="111.56 197.79 93.74 221.34 52.27 221.34 52.27 210 70.1 186.45 111.56 186.45 111.56 197.79"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="111.56 186.45 93.74 210 52.27 210"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="93.74" y1="210" x2="93.74" y2="221.34"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<ellipse cx="99.9" cy="191.01" rx="3.83" ry="1.56" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<circle cx="72.12" cy="215.67" r="3.12" style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<g>
|
||||
<polygon points="85.41 196.33 82.28 192.91 80.31 195.53 83.44 198.95 85.41 196.33"
|
||||
style="fill:#2bb673; opacity:.42;" />
|
||||
<polygon points="82.3 200.48 79.16 197.06 77.2 199.68 80.33 203.09 82.3 200.48"
|
||||
style="fill:#2bb673; opacity:.42;" />
|
||||
<polygon points="78.59 205.41 79.19 204.62 76.05 201.2 72.89 205.41 75.97 205.1 78.59 205.41"
|
||||
style="fill:#2bb673; opacity:.42;" />
|
||||
<polygon points="89.38 191.04 83.68 191.04 83.42 191.39 86.56 194.81 89.38 191.04"
|
||||
style="fill:#2bb673; opacity:.42;" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M69.35,223.89c-.57,.69-1.44,1.13-2.4,1.13-1.72,0-3.12-1.4-3.12-3.12,0-.71,.24-1.36,.63-1.88l5.79-6.56c.56-.56,1.34-.91,2.2-.91,1.72,0,3.12,1.4,3.12,3.12,0,.91-.39,1.72-1,2.29l-5.22,5.93Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<path
|
||||
d="M70.06,221.91c0,.75-.27,1.45-.71,1.99-.57,.69-1.44,1.13-2.4,1.13-1.72,0-3.12-1.4-3.12-3.12,0-.71,.24-1.36,.63-1.88,.57-.75,1.47-1.23,2.48-1.23,1.72,0,3.12,1.4,3.12,3.12Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="163.4 164.83 158.3 166.52 153.19 164.83 153.19 109.62 152.06 107.91 152.06 60.47 164.53 60.47 164.53 107.91 163.4 109.62 163.4 164.83"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="153.19" y1="109.62" x2="163.4" y2="109.62"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="153.19" y1="164.09" x2="163.4" y2="159.3"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="158.09" x2="163.4" y2="153.3"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="152.07" x2="163.4" y2="147.28"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="146.09" x2="163.4" y2="141.31"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="140.07" x2="163.4" y2="135.28"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="133.87" x2="163.4" y2="129.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="127.85" x2="163.4" y2="123.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="121.87" x2="163.4" y2="117.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="115.85" x2="163.4" y2="111.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<path d="M190.21,0V57.54c0,2.23-1.82,4.05-4.05,4.05h-56.36c-2.23,0-4.05-1.82-4.05-4.05V0"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<rect x="146.28" y="61.6" width="24.04" height="24.04"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<rect x="142.61" y="85.64" width="31.37" height="19.04"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="154.82" y1="85.64" x2="154.82" y2="104.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="168.85" y1="85.64" x2="168.85" y2="104.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<ellipse cx="148.67" cy="95.16" rx="2.39" ry="4.57"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<path
|
||||
d="M71.21,99.73c-1.32,0-2.39-2.05-2.39-4.57s1.07-4.57,2.39-4.57h14.44c1.32,0,2.39,2.05,2.39,4.57s-1.07,4.57-2.39,4.57h-14.44Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<ellipse cx="71.21" cy="95.16" rx="2.39" ry="4.57"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<g>
|
||||
<g>
|
||||
<line x1="94.11" y1="94.84" x2="97.61" y2="94.84"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="99.16" y1="94.84" x2="112.38" y2="94.84"
|
||||
style="fill:none; stroke:#ed1c24; stroke-dasharray:0 0 0 0 5.44 1.55 5.44 1.55; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="113.16" y1="94.84" x2="133.36" y2="94.84"
|
||||
style="fill:none; stroke:#ed1c24; stroke-dasharray:0 0 0 0 0 0 5.44 1.55 5.44 1.55 5.44 1.55; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="134.14" y1="94.84" x2="137.64" y2="94.84"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<polyline points="99.06 99.73 93.02 94.84 99.06 90"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<line x1="116.95" y1="190.46" x2="120.38" y2="189.78"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="121.44" y1="189.58" x2="130.44" y2="187.81"
|
||||
style="fill:none; stroke:#ed1c24; stroke-dasharray:0 0 0 0 3.78 1.08 3.78 1.08; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="130.97" y1="187.71" x2="144.73" y2="185"
|
||||
style="fill:none; stroke:#ed1c24; stroke-dasharray:0 0 0 0 0 0 3.78 1.08 3.78 1.08 3.78 1.08; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="145.26" y1="184.9" x2="148.7" y2="184.23"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<polyline points="122.75 194.3 115.88 190.67 120.88 184.75"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<path
|
||||
d="M69.9,93.3c-9.07-.24-17.32,4.41-24.11,10.04s-11.88,12.04-15.84,19.44c-3.11,5.81-5.51,11.98-5.9,18.6-.35,5.97,.4,12,1.35,17.89,2.21,13.7,5.99,27.09,8.74,40.68,1.18,5.8,2.23,11.68,2.47,17.61,.23,5.59-.33,11.4-3.15,16.41-2.42,4.3-6.54,6.35-11.01,7.98-5.9,2.15-11.79,4.15-17.12,7.56-1.66,1.06-3.24,2.24-4.78,3.47-.8,.64-.65,1.98,0,2.65,.77,.79,1.87,.63,2.65,0,5.19-4.16,11.12-6.84,17.35-9.03,4.73-1.67,9.74-3.29,13.34-6.97,4.21-4.31,5.86-10.66,6.38-16.5,.48-5.48-.24-11.05-1.1-16.46-2.14-13.43-5.87-26.56-8.58-39.87-1.26-6.18-2.38-12.42-2.75-18.72-.32-5.39,0-10.38,1.81-15.46,2.77-7.7,7.16-14.85,12.71-20.85s12.9-11.91,21.18-14c2.09-.53,4.2-.76,6.36-.7,2.41,.06,2.41-3.69,0-3.75h0Z"
|
||||
style="fill:#231f20;" />
|
||||
<path
|
||||
d="M64.5,221.06c-1.88,3.18-2.66,6.86-3.58,10.4-.78,3.05-1.59,6.62-4.02,8.83-1.32,1.2-3.18,1.8-4.87,2.26-2.03,.56-4.11,.84-6.21,.96-4.68,.27-9.37-.13-14.04-.35-5.05-.24-10.18-.31-15.11,.94-2.34,.59-1.35,4.21,1,3.62,9.17-2.32,18.84,.08,28.16-.46,4.16-.24,8.99-.9,12.5-3.33,3.09-2.14,4.57-5.69,5.59-9.17,1.15-3.94,1.71-8.22,3.82-11.8,1.23-2.08-2.01-3.97-3.24-1.89h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
134
src/svelte-components/src/svgs/probe-put-away-z.svg
Normal file
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 328.41 256.15">
|
||||
<g>
|
||||
<g>
|
||||
<polygon
|
||||
points="111.57 190.34 111.57 197.79 93.74 221.35 93.74 210.01 98.41 203.84 98.81 203.83 98.82 206.12 110.76 190.34 111.57 190.34"
|
||||
style="fill:#fff;" />
|
||||
<polygon
|
||||
points="98.22 203.31 69.81 203.31 69.81 202.86 79.7 189.46 111.57 189.46 111.57 190.34 110.76 190.34 98.82 206.12 98.81 203.83 98.41 203.84 98.22 203.31"
|
||||
style="fill:#8dc63f;" />
|
||||
<polygon
|
||||
points="98.22 203.31 98.41 203.84 93.74 210.01 72.12 210.01 52.28 210.01 70.11 186.45 78.83 186.45 66.52 203.31 69.81 203.31 98.22 203.31"
|
||||
style="fill:#ed1c24;" />
|
||||
<path d="M93.74,210.01v11.34h-21.62v-2.56c1.72,0,3.12-1.39,3.12-3.11s-1.4-3.12-3.12-3.12v-2.55h21.62Z"
|
||||
style="fill:#fff;" />
|
||||
<path
|
||||
d="M72.12,212.56c1.72,0,3.12,1.39,3.12,3.12s-1.4,3.11-3.12,3.11-3.12-1.39-3.12-3.11,1.4-3.12,3.12-3.12Z"
|
||||
style="fill:#fff;" />
|
||||
<path d="M72.12,218.79v2.56h-19.84v-11.34h19.84v2.55c-1.72,0-3.12,1.39-3.12,3.12s1.4,3.11,3.12,3.11Z"
|
||||
style="fill:#fff;" />
|
||||
<polygon points="79.7 189.46 69.81 202.86 69.81 203.31 66.52 203.31 78.83 186.45 79.68 186.45 79.7 189.46"
|
||||
style="fill:#fff;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="111.57 190.34 111.57 197.79 93.74 221.35 72.12 221.35 52.28 221.35 52.28 210.01 70.11 186.45 78.83 186.45 79.68 186.45 79.7 189.46 111.57 189.46 111.57 190.34"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline
|
||||
points="110.76 190.34 98.82 206.12 98.81 203.83 98.41 203.84 93.74 210.01 72.12 210.01 52.28 210.01"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="93.74" y1="210.01" x2="93.74" y2="221.35"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<path
|
||||
d="M72.12,212.56c1.72,0,3.12,1.39,3.12,3.12s-1.4,3.11-3.12,3.11-3.12-1.39-3.12-3.11,1.4-3.12,3.12-3.12Z"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<polyline points="78.83 186.45 66.52 203.31 69.81 203.31 98.22 203.31"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="79.7" y1="189.46" x2="69.81" y2="202.86"
|
||||
style="fill:none; stroke:#231f20; stroke-linecap:round; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="326.91 163.14 264.27 245.91 118.57 245.91 118.57 230.65 181.21 147.88 326.91 147.88 326.91 163.14"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<polyline points="326.91 147.88 264.27 230.65 118.57 230.65"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="264.27" y1="230.65" x2="264.27" y2="245.91"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M69.35,223.89c-.57,.69-1.44,1.13-2.4,1.13-1.72,0-3.12-1.4-3.12-3.12,0-.71,.24-1.36,.63-1.88l5.79-6.56c.56-.56,1.34-.91,2.2-.91,1.72,0,3.12,1.4,3.12,3.12,0,.91-.39,1.72-1,2.29l-5.22,5.93Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
<path
|
||||
d="M70.06,221.91c0,.75-.27,1.45-.71,1.99-.57,.69-1.44,1.13-2.4,1.13-1.72,0-3.12-1.4-3.12-3.12,0-.71,.24-1.36,.63-1.88,.57-.75,1.47-1.23,2.48-1.23,1.72,0,3.12,1.4,3.12,3.12Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10;" />
|
||||
</g>
|
||||
<g>
|
||||
<polygon
|
||||
points="163.4 164.83 158.3 166.52 153.19 164.83 153.19 109.62 152.06 107.91 152.06 60.47 164.53 60.47 164.53 107.91 163.4 109.62 163.4 164.83"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="153.19" y1="109.62" x2="163.4" y2="109.62"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="153.19" y1="164.09" x2="163.4" y2="159.3"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="158.09" x2="163.4" y2="153.3"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="152.07" x2="163.4" y2="147.28"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="146.09" x2="163.4" y2="141.31"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="140.07" x2="163.4" y2="135.28"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="133.87" x2="163.4" y2="129.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="127.85" x2="163.4" y2="123.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="121.87" x2="163.4" y2="117.08"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="153.19" y1="115.85" x2="163.4" y2="111.06"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<path d="M190.21,0V57.54c0,2.23-1.82,4.05-4.05,4.05h-56.36c-2.23,0-4.05-1.82-4.05-4.05V0"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<rect x="146.28" y="61.6" width="24.04" height="24.04"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<rect x="142.61" y="85.64" width="31.37" height="19.04"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="154.82" y1="85.64" x2="154.82" y2="104.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<line x1="168.85" y1="85.64" x2="168.85" y2="104.68"
|
||||
style="fill:none; stroke:#231f20; stroke-miterlimit:10; stroke-width:3px;" />
|
||||
<ellipse cx="148.67" cy="95.16" rx="2.39" ry="4.57"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<path
|
||||
d="M71.21,99.73c-1.32,0-2.39-2.05-2.39-4.57s1.07-4.57,2.39-4.57h14.44c1.32,0,2.39,2.05,2.39,4.57s-1.07,4.57-2.39,4.57h-14.44Z"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<ellipse cx="71.21" cy="95.16" rx="2.39" ry="4.57"
|
||||
style="fill:#fff; stroke:#231f20; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<g>
|
||||
<g>
|
||||
<line x1="94.11" y1="94.84" x2="97.61" y2="94.84"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="99.16" y1="94.84" x2="112.38" y2="94.84"
|
||||
style="fill:none; stroke:#ed1c24; stroke-dasharray:0 0 0 0 5.44 1.55 5.44 1.55; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="113.16" y1="94.84" x2="133.36" y2="94.84"
|
||||
style="fill:none; stroke:#ed1c24; stroke-dasharray:0 0 0 0 0 0 5.44 1.55 5.44 1.55 5.44 1.55; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="134.14" y1="94.84" x2="137.64" y2="94.84"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<polyline points="99.06 99.73 93.02 94.84 99.06 90"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<line x1="116.95" y1="190.46" x2="120.38" y2="189.78"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="121.44" y1="189.58" x2="130.44" y2="187.81"
|
||||
style="fill:none; stroke:#ed1c24; stroke-dasharray:0 0 0 0 3.78 1.08 3.78 1.08; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="130.97" y1="187.71" x2="144.73" y2="185"
|
||||
style="fill:none; stroke:#ed1c24; stroke-dasharray:0 0 0 0 0 0 3.78 1.08 3.78 1.08 3.78 1.08; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
<line x1="145.26" y1="184.9" x2="148.7" y2="184.23"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<polyline points="122.75 194.3 115.88 190.67 120.88 184.75"
|
||||
style="fill:none; stroke:#ed1c24; stroke-miterlimit:10; stroke-width:2px;" />
|
||||
</g>
|
||||
<path
|
||||
d="M69.9,93.3c-9.07-.24-17.32,4.41-24.11,10.04s-11.88,12.04-15.84,19.44c-3.11,5.81-5.51,11.98-5.9,18.6-.35,5.97,.4,12,1.35,17.89,2.21,13.7,5.99,27.09,8.74,40.68,1.18,5.8,2.23,11.68,2.47,17.61,.23,5.59-.33,11.4-3.15,16.41-2.42,4.3-6.54,6.35-11.01,7.98-5.9,2.15-11.79,4.15-17.12,7.56-1.66,1.06-3.24,2.24-4.78,3.47-.8,.64-.65,1.98,0,2.65,.77,.79,1.87,.63,2.65,0,5.19-4.16,11.12-6.84,17.35-9.03,4.73-1.67,9.74-3.29,13.34-6.97,4.21-4.31,5.86-10.66,6.38-16.5,.48-5.48-.24-11.05-1.1-16.46-2.14-13.43-5.87-26.56-8.58-39.87-1.26-6.18-2.38-12.42-2.75-18.72-.32-5.39,0-10.38,1.81-15.46,2.77-7.7,7.16-14.85,12.71-20.85s12.9-11.91,21.18-14c2.09-.53,4.2-.76,6.36-.7,2.41,.06,2.41-3.69,0-3.75h0Z"
|
||||
style="fill:#231f20;" />
|
||||
<path
|
||||
d="M64.5,221.06c-1.88,3.18-2.66,6.86-3.58,10.4-.78,3.05-1.59,6.62-4.02,8.83-1.32,1.2-3.18,1.8-4.87,2.26-2.03,.56-4.11,.84-6.21,.96-4.68,.27-9.37-.13-14.04-.35-5.05-.24-10.18-.31-15.11,.94-2.34,.59-1.35,4.21,1,3.62,9.17-2.32,18.84,.08,28.16-.46,4.16-.24,8.99-.9,12.5-3.33,3.09-2.14,4.57-5.69,5.59-9.17,1.15-3.94,1.71-8.22,3.82-11.8,1.23-2.08-2.01-3.97-3.24-1.89h0Z"
|
||||
style="fill:#231f20;" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
46
src/svelte-components/src/theme/_smui-theme.scss
Normal file
@@ -0,0 +1,46 @@
|
||||
@use "sass:color";
|
||||
|
||||
@use "@material/theme/color-palette";
|
||||
|
||||
// Svelte Colors!
|
||||
@use "@material/theme/index" as theme with (
|
||||
$primary: #0078e7,
|
||||
$secondary: #676778,
|
||||
$surface: #fff,
|
||||
$background: #fff,
|
||||
$error: color-palette.$red-900,
|
||||
$on-surface: #777
|
||||
);
|
||||
|
||||
@use "@material/elevation/mdc-elevation";
|
||||
@use "@material/list";
|
||||
|
||||
@include list.deprecated-core-styles;
|
||||
|
||||
:root {
|
||||
--mdc-theme-text-primary-on-background: #777;
|
||||
|
||||
.mdc-dialog .mdc-dialog__container {
|
||||
transition: margin-bottom 0.5s;
|
||||
}
|
||||
|
||||
.mdc-dialog .mdc-dialog__content {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.mdc-dialog .mdc-dialog__title {
|
||||
color: #777;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
}
|
||||
12
src/svelte-components/src/theme/dark/_smui-theme.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
@use 'sass:color';
|
||||
|
||||
@use '@material/theme/color-palette';
|
||||
|
||||
// Svelte Colors! (Dark Theme)
|
||||
@use '@material/theme/index' as theme with (
|
||||
$primary: #ff3e00,
|
||||
$secondary: color.scale(#676778, $whiteness: -10%),
|
||||
$surface: color.adjust(color-palette.$grey-900, $blue: +4),
|
||||
$background: #000,
|
||||
$error: color-palette.$red-700
|
||||
);
|
||||
2
src/svelte-components/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
||||
7
src/svelte-components/svelte.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import sveltePreprocess from "svelte-preprocess";
|
||||
|
||||
export default {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: sveltePreprocess()
|
||||
};
|
||||
27
src/svelte-components/tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"$lib/*": ["src/lib/*"],
|
||||
"$components/*": ["src/components/*"],
|
||||
"$dialogs/*": ["src/dialogs/*"]
|
||||
},
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable checkJs if you'd like to use dynamic types in JS.
|
||||
* Note that setting allowJs false does not prevent the use
|
||||
* of JS in `.svelte` files.
|
||||
*/
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"isolatedModules": true,
|
||||
"moduleSuffixes": [".svelte", ""]
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||