v1.0.9 changes from 1.1.1

This commit is contained in:
saifullah-N
2022-12-21 16:26:22 +05:30
parent 44d80a59cc
commit e93296ef00
102 changed files with 18572 additions and 5777 deletions

View File

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

View File

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

View File

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

View File

@@ -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`);
}
},
},
});

View File

@@ -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 ? "" : "%");
}
}
}
}
};

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,69 +1,49 @@
/******************************************************************************\
"use strict";
Copyright 2018. Buildbotics LLC
All Rights Reserved.
For information regarding this software email:
Joseph Coffland
joseph@buildbotics.com
This software is free software: you clan redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 2.1 of
the License, or (at your option) any later version.
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the C! library. If not, see
<http://www.gnu.org/licenses/>.
\******************************************************************************/
'use strict'
module.exports = function (prefix) {
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;
};

View File

@@ -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 = {
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
'/': '&#x2F;', '`': '&#x60;', '=': '&#x3D;'}
"use strict";
const entityMap = {
"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;",
"/": "&#x2F;", "`": "&#x60;", "=": "&#x3D;" };
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
View 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();
}
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,58 +1,47 @@
/******************************************************************************\
This file is part of the Buildbotics firmware.
Copyright (c) 2015 - 2018, Buildbotics LLC
All rights reserved.
This file ("the software") is free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License,
version 2 as published by the Free Software Foundation. You should
have received a copy of the GNU General Public License, version 2
along with the software. If not, see <http://www.gnu.org/licenses/>.
The software is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the software. If not, see
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
"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
View File

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