Merge pull request #81 from saifullah-N/v1.2.1-without-em60
V1.2.1 without em60
This commit is contained in:
7
.devcontainer/Dockerfile
Normal file
7
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/blob/v0.241.1/containers/debian/.devcontainer/base.Dockerfile
|
||||
|
||||
# [Choice] Debian version (use bullseye on local arm64/Apple Silicon): bullseye, buster
|
||||
ARG VARIANT=bullseye
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/base:${VARIANT}
|
||||
COPY ./install_tools.sh /tmp/install_tools.sh
|
||||
RUN /tmp/install_tools.sh
|
||||
36
.devcontainer/devcontainer.json
Normal file
36
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,36 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/blob/v0.241.1/containers/debian
|
||||
{
|
||||
"name": "Debian",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
// Update 'VARIANT' to pick an Debian version: bullseye, buster
|
||||
// Use bullseye on local arm64/Apple Silicon.
|
||||
"args": {
|
||||
"VARIANT": "bullseye"
|
||||
}
|
||||
},
|
||||
"runArgs": [
|
||||
// The primary reason this is here is to enable loopback devices,
|
||||
// which are used for mounting disk images during builds, etc.
|
||||
"--privileged"
|
||||
],
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
//
|
||||
// Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker.
|
||||
// "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ],
|
||||
"features": {
|
||||
"sshd": "latest"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-azuretools.vscode-docker",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"eamodio.gitlens",
|
||||
"ms-vscode.makefile-tools",
|
||||
"ms-python.python",
|
||||
"svelte.svelte-vscode",
|
||||
"redhat.vscode-yaml",
|
||||
"ryu1kn.partial-diff"
|
||||
]
|
||||
}
|
||||
47
.devcontainer/install_tools.sh
Normal file
47
.devcontainer/install_tools.sh
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env -S bash -e
|
||||
|
||||
APT_PACKAGES=(
|
||||
"build-essential"
|
||||
"git"
|
||||
"wget"
|
||||
"binfmt-support"
|
||||
"qemu"
|
||||
"gcc-9"
|
||||
"parted"
|
||||
"udev"
|
||||
"zerofree"
|
||||
"gcc-avr"
|
||||
"avr-libc"
|
||||
"avrdude"
|
||||
"python3"
|
||||
"python3-pip"
|
||||
"python3-tornado"
|
||||
"inetutils-ping"
|
||||
"curl"
|
||||
"unzip"
|
||||
"python3-setuptools"
|
||||
"gcc-arm-linux-gnueabihf"
|
||||
"bc"
|
||||
"vim"
|
||||
"locate"
|
||||
"sudo"
|
||||
"sshpass"
|
||||
)
|
||||
|
||||
apt-get update
|
||||
apt-get upgrade -y
|
||||
|
||||
apt-get install -y "${APT_PACKAGES[@]}"
|
||||
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9
|
||||
|
||||
/usr/bin/python3 -m pip install -U yapf
|
||||
|
||||
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
|
||||
apt-get install -y nodejs
|
||||
|
||||
mkdir -p /root/.ssh
|
||||
cat > /root/.ssh/config <<- END
|
||||
Host onefinity
|
||||
User bbmc
|
||||
END
|
||||
31
.vscode/settings.json
vendored
Normal file
31
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/Thumbs.db": true,
|
||||
"**/node_modules": true,
|
||||
"**/build": true,
|
||||
"**/dist": true,
|
||||
"**/rpi-share": true,
|
||||
"**/rpi-root": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/bower_components": true,
|
||||
"**/*.code-search": true,
|
||||
"**/node_modules": true,
|
||||
"**/build": true,
|
||||
"**/dist": true,
|
||||
"**/rpi-share": true,
|
||||
"**/rpi-root": true
|
||||
},
|
||||
"explorer.excludeGitIgnore": true,
|
||||
"git.ignoredRepositories": [
|
||||
"rpi-share/camotics",
|
||||
"rpi-share/cbang"
|
||||
],
|
||||
"git.autofetch": false,
|
||||
"git.confirmSync": false
|
||||
}
|
||||
72
Makefile
72
Makefile
@@ -22,13 +22,8 @@ RSYNC_OPTS := $(RSYNC_EXCLUDE) -rv --no-g --delete --force
|
||||
|
||||
VERSION := $(shell sed -n 's/^.*"version": "\([^"]*\)",.*$$/\1/p' package.json)
|
||||
PKG_NAME := bbctrl-$(VERSION)
|
||||
PUB_PATH := root@buildbotics.com:/var/www/buildbotics.com/bbctrl
|
||||
BETA_VERSION := $(VERSION)-rc$(shell ./scripts/next-rc)
|
||||
BETA_PKG_NAME := bbctrl-$(BETA_VERSION)
|
||||
|
||||
SUBPROJECTS := avr boot pwr jig
|
||||
WATCH := src/pug src/pug/templates src/stylus src/js src/resources Makefile
|
||||
WATCH += src/static
|
||||
|
||||
ifndef HOST
|
||||
HOST=onefinity
|
||||
@@ -38,16 +33,12 @@ ifndef PASSWORD
|
||||
PASSWORD=onefinity
|
||||
endif
|
||||
|
||||
|
||||
all: $(HTML) $(RESOURCES)
|
||||
@for SUB in $(SUBPROJECTS); do $(MAKE) -C src/$$SUB; done
|
||||
|
||||
pkg: all $(AVR_FIRMWARE) bbserial
|
||||
./setup.py sdist
|
||||
|
||||
beta-pkg: pkg
|
||||
cp dist/$(PKG_NAME).tar.bz2 dist/$(BETA_PKG_NAME).tar.bz2
|
||||
|
||||
bbserial:
|
||||
$(MAKE) -C src/bbserial
|
||||
|
||||
@@ -58,12 +49,6 @@ $(GPLAN_TARGET): $(GPLAN_MOD)
|
||||
|
||||
$(GPLAN_MOD): $(GPLAN_IMG)
|
||||
./scripts/gplan-init-build.sh
|
||||
git -C rpi-share/cbang fetch
|
||||
git -C rpi-share/cbang reset --hard FETCH_HEAD
|
||||
git -C rpi-share/cbang checkout 18f1e963107ef26abe750c023355a5c40dd07853
|
||||
git -C rpi-share/camotics fetch
|
||||
git -C rpi-share/camotics reset --hard FETCH_HEAD
|
||||
git -C rpi-share/camotics checkout ec876c80d20fc19837133087cef0c447df5a939d
|
||||
cp ./scripts/gplan-build.sh rpi-share/
|
||||
chmod +x rpi-share/gplan-build.sh
|
||||
sudo ./scripts/rpi-chroot.sh $(GPLAN_IMG) /mnt/host/gplan-build.sh
|
||||
@@ -75,15 +60,6 @@ $(GPLAN_IMG):
|
||||
$(AVR_FIRMWARE):
|
||||
$(MAKE) -C src/avr
|
||||
|
||||
publish: pkg
|
||||
echo -n $(VERSION) > dist/latest.txt
|
||||
rsync $(RSYNC_OPTS) dist/$(PKG_NAME).tar.bz2 dist/latest.txt $(PUB_PATH)/
|
||||
|
||||
publish-beta: beta-pkg
|
||||
echo -n $(BETA_VERSION) > dist/latest-beta.txt
|
||||
rsync $(RSYNC_OPTS) dist/$(BETA_PKG_NAME).tar.bz2 dist/latest-beta.txt \
|
||||
$(PUB_PATH)/
|
||||
|
||||
update: pkg
|
||||
http_proxy= curl -i -X PUT -H "Content-Type: multipart/form-data" \
|
||||
-F "firmware=@dist/$(PKG_NAME).tar.bz2" -F "password=$(PASSWORD)" \
|
||||
@@ -100,6 +76,9 @@ node_modules: package.json
|
||||
$(TARGET_DIR)/%: src/resources/%
|
||||
install -D $< $@
|
||||
|
||||
src/svelte-components/dist/%:
|
||||
cd src/svelte-components && rm -rf dist && npm run build
|
||||
|
||||
$(TARGET_DIR)/index.html: build/templates.pug
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/static/js/*)
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/static/css/*)
|
||||
@@ -108,39 +87,20 @@ $(TARGET_DIR)/index.html: $(wildcard src/js/*)
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/stylus/*)
|
||||
$(TARGET_DIR)/index.html: src/resources/config-template.json
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/resources/onefinity*defaults.json)
|
||||
$(TARGET_DIR)/index.html: $(wildcard src/svelte-components/dist/*)
|
||||
|
||||
$(TARGET_DIR)/%.html: src/pug/%.pug node_modules
|
||||
@mkdir -p $(shell dirname $@)
|
||||
FORCE:
|
||||
|
||||
$(TARGET_DIR)/%.html: src/pug/%.pug node_modules FORCE
|
||||
cd src/svelte-components && rm -rf dist && npm run build
|
||||
@mkdir -p $(TARGET_DIR)/svelte-components
|
||||
cp src/svelte-components/dist/* $(TARGET_DIR)/svelte-components/
|
||||
|
||||
@mkdir -p $(TARGET_DIR)
|
||||
$(PUG) -O pug-opts.js -P $< -o $(TARGET_DIR) || (rm -f $@; exit 1)
|
||||
|
||||
pylint:
|
||||
pylint3 -E $(shell find src/py -name \*.py | grep -v flycheck_)
|
||||
clean:
|
||||
rm -rf rpi-share
|
||||
git clean -fxd
|
||||
|
||||
jshint:
|
||||
./node_modules/jshint/bin/jshint --config jshint.json src/js/*.js
|
||||
|
||||
lint: pylint jshint
|
||||
|
||||
watch:
|
||||
@clear
|
||||
$(MAKE)
|
||||
@while sleep 1; do \
|
||||
inotifywait -qr -e modify -e create -e delete \
|
||||
--exclude .*~ --exclude \#.* $(WATCH); \
|
||||
clear; \
|
||||
$(MAKE); \
|
||||
done
|
||||
|
||||
tidy:
|
||||
rm -f $(shell find "$(DIR)" -name \*~)
|
||||
|
||||
clean: tidy
|
||||
rm -rf build html dist
|
||||
@for SUB in $(SUBPROJECTS); do \
|
||||
$(MAKE) -C src/$$SUB clean; \
|
||||
done
|
||||
|
||||
dist-clean: clean
|
||||
rm -rf node_modules
|
||||
|
||||
.PHONY: all install clean tidy pkg gplan lint pylint jshint bbserial
|
||||
.PHONY: all install clean tidy pkg gplan lint pylint jshint bbserial
|
||||
42
package.json
42
package.json
@@ -1,16 +1,28 @@
|
||||
{
|
||||
"name": "bbctrl",
|
||||
"version": "1.0.9",
|
||||
"homepage": "https://onefinitycnc.com/",
|
||||
"repository": "https://github.com/OneFinityCNC/onefinity",
|
||||
"license": "GPL-3.0+",
|
||||
"dependencies": {
|
||||
"browserify": "",
|
||||
"jshint": "",
|
||||
"jstransformer-escape-html": "",
|
||||
"jstransformer-stylus": "",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.omit": "^4.5.0",
|
||||
"pug-cli": ""
|
||||
}
|
||||
}
|
||||
"name": "bbctrl",
|
||||
"version": "1.2.1",
|
||||
"homepage": "https://onefinitycnc.com/",
|
||||
"repository": "https://github.com/OneFinityCNC/onefinity",
|
||||
"license": "GPL-3.0+",
|
||||
"scripts": {
|
||||
"postinstall": "cd src/svelte-components && npm i"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
||||
"@typescript-eslint/parser": "^5.36.1",
|
||||
"browserify": "^17.0.0",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-standard-with-typescript": "^22.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.2.5",
|
||||
"eslint-plugin-promise": "^6.0.1",
|
||||
"glob": "^8.0.3",
|
||||
"inquirer": "^8.2.4",
|
||||
"jshint": "^2.13.4",
|
||||
"jstransformer-escape-html": "^1.1.0",
|
||||
"jstransformer-scss": "^2.0.0",
|
||||
"jstransformer-stylus": "^1.5.0",
|
||||
"lodash.merge": "4.6.2",
|
||||
"pug-cli": "^1.0.0-alpha6"
|
||||
}
|
||||
}
|
||||
10677
pakage-lock.json
Normal file
10677
pakage-lock.json
Normal file
File diff suppressed because it is too large
Load Diff
2
setup.py
2
setup.py
@@ -17,7 +17,7 @@ setup(
|
||||
license = pkg['license'],
|
||||
url = pkg['homepage'],
|
||||
package_dir = {'': 'src/py'},
|
||||
packages = ['bbctrl', 'inevent', 'lcd', 'camotics'],
|
||||
packages = ['bbctrl', 'inevent', 'lcd', 'camotics','iw_parse'],
|
||||
include_package_data = True,
|
||||
entry_points = {
|
||||
'console_scripts': [
|
||||
|
||||
@@ -54,7 +54,7 @@ static struct {
|
||||
line_t line;
|
||||
|
||||
int section;
|
||||
int seg;
|
||||
uint32_t seg;
|
||||
|
||||
float iD; // Initial section distance
|
||||
float iV; // Initial section velocity
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "config.h"
|
||||
#include "command.h"
|
||||
#include "exec.h"
|
||||
#include "estop.h"
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
|
||||
@@ -94,6 +95,8 @@ static void _set_speed(float speed) {
|
||||
|
||||
float power = _speed_to_power(speed);
|
||||
|
||||
if (estop_triggered()) power = 0;
|
||||
|
||||
switch (spindle.type) {
|
||||
case SPINDLE_TYPE_DISABLED: break;
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ typedef enum {
|
||||
SPINDLE_TYPE_SUNFAR_E300,
|
||||
SPINDLE_TYPE_OMRON_MX2,
|
||||
SPINDLE_TYPE_V70,
|
||||
SPINDLE_TYPE_EM60,
|
||||
} spindle_type_t;
|
||||
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ typedef enum {
|
||||
|
||||
REG_FREQ_SET,
|
||||
REG_FREQ_SIGN_SET,
|
||||
REG_FREQ_SCALED_SET,
|
||||
|
||||
REG_STOP_WRITE,
|
||||
REG_FWD_WRITE,
|
||||
@@ -179,6 +180,17 @@ const vfd_reg_t v70_regs[] PROGMEM = {
|
||||
{REG_DISABLED},
|
||||
};
|
||||
|
||||
const vfd_reg_t em60_regs[] PROGMEM = {
|
||||
{REG_MAX_FREQ_READ, 0x0007, 0}, // Read max frequency
|
||||
{REG_FREQ_SCALED_SET, 0xa001, 10000}, // Set scaled frequency
|
||||
{REG_FREQ_READ, 0x9000, 0}, // Read frequency
|
||||
{REG_FWD_WRITE, 0xa000, 1}, // Run forward
|
||||
{REG_REV_WRITE, 0xa000, 2}, // Run reverse
|
||||
{REG_STOP_WRITE, 0xa000, 5}, // Stop
|
||||
{REG_STATUS_READ, 0xb000, 0}, // Read status
|
||||
{REG_DISABLED},
|
||||
};
|
||||
|
||||
|
||||
static vfd_reg_t regs[VFDREG];
|
||||
static vfd_reg_t custom_regs[VFDREG];
|
||||
@@ -215,6 +227,12 @@ static bool _next_state() {
|
||||
else vfd.state = REG_FREQ_SET;
|
||||
break;
|
||||
|
||||
case REG_FREQ_SCALED_SET:
|
||||
if (vfd.power < 0) vfd.state = REG_REV_WRITE;
|
||||
else if (0 < vfd.power) vfd.state = REG_FWD_WRITE;
|
||||
else vfd.state = REG_STOP_WRITE;
|
||||
break;
|
||||
|
||||
case REG_FREQ_SIGN_SET:
|
||||
if (vfd.power < 0) vfd.state = REG_REV_WRITE;
|
||||
else if (0 < vfd.power) vfd.state = REG_FWD_WRITE;
|
||||
@@ -344,6 +362,11 @@ static bool _exec_command() {
|
||||
reg.value = vfd.power * vfd.max_freq;
|
||||
break;
|
||||
|
||||
case REG_FREQ_SCALED_SET:
|
||||
write = true;
|
||||
reg.value = fabs(vfd.power) * reg.value;
|
||||
break;
|
||||
|
||||
case REG_CONNECT_WRITE:
|
||||
case REG_STOP_WRITE:
|
||||
case REG_FWD_WRITE:
|
||||
@@ -399,6 +422,7 @@ void vfd_spindle_init() {
|
||||
case SPINDLE_TYPE_SUNFAR_E300: _load(sunfar_e300_regs); break;
|
||||
case SPINDLE_TYPE_OMRON_MX2: _load(omron_mx2_regs); break;
|
||||
case SPINDLE_TYPE_V70: _load(v70_regs); break;
|
||||
case SPINDLE_TYPE_EM60: _load(em60_regs); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,150 +1,156 @@
|
||||
/******************************************************************************\
|
||||
|
||||
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');
|
||||
const z_slider_defaults = {
|
||||
"Z-16 Original": {
|
||||
"travel-per-rev": 4,
|
||||
"min-soft-limit": -133,
|
||||
"max-velocity": 3,
|
||||
},
|
||||
"Z-20 Heavy Duty": {
|
||||
"travel-per-rev": 10,
|
||||
"min-soft-limit": -160,
|
||||
"max-velocity": 7,
|
||||
},
|
||||
};
|
||||
|
||||
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: "",
|
||||
z_slider: "",
|
||||
z_slider_variant:" ",
|
||||
config:""
|
||||
};
|
||||
},
|
||||
|
||||
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"
|
||||
});
|
||||
this.confirmReset= false
|
||||
} catch (error) {
|
||||
console.error("Restore failed:", error);
|
||||
alert("Restore failed");
|
||||
}
|
||||
};
|
||||
|
||||
fileReader.readAsText(files[0]);
|
||||
},
|
||||
|
||||
next: 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.config= config
|
||||
this.z_slider = true;
|
||||
} catch (error) {
|
||||
console.error("Restore failed:", error);
|
||||
alert("Restore failed");
|
||||
}
|
||||
},
|
||||
|
||||
set_z_slider: async function(){
|
||||
const z_variant = merge(
|
||||
{},
|
||||
this.config.motors[3],
|
||||
z_slider_defaults[this.z_slider_variant],
|
||||
);
|
||||
|
||||
this.config.motors[3] = z_variant;
|
||||
try {
|
||||
await api.put("config/save", this.config);
|
||||
this.$dispatch("update");
|
||||
SvelteComponents.showDialog("Message", {
|
||||
title: "Success",
|
||||
message: "Configuration restored",
|
||||
});
|
||||
this.z_slider = false;
|
||||
} catch (error) {
|
||||
console.error("Z slider failed:", error);
|
||||
alert("failed to set Z slider ");
|
||||
}
|
||||
},
|
||||
check: function() {
|
||||
this.$dispatch("check");
|
||||
},
|
||||
|
||||
upgrade: function() {
|
||||
this.$dispatch("upgrade");
|
||||
},
|
||||
|
||||
upload_firmware: function() {
|
||||
utils.clickFileInput("upload-firmware");
|
||||
},
|
||||
|
||||
upload: function(e) {
|
||||
const files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
this.$dispatch("upload", files[0]);
|
||||
},
|
||||
|
||||
change_auto_check_upgrade: function() {
|
||||
this.config.admin["auto-check-upgrade"] = this.autoCheckUpgrade;
|
||||
this.$dispatch("config-changed");
|
||||
}
|
||||
|
||||
api.put('config/save', config).done(function (data) {
|
||||
this.$dispatch('update');
|
||||
this.configRestored = true;
|
||||
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Restore failed', error);
|
||||
})
|
||||
}.bind(this);
|
||||
|
||||
fr.readAsText(files[0]);
|
||||
},
|
||||
|
||||
reset: async function () {
|
||||
const config = merge(
|
||||
{},
|
||||
config_defaults,
|
||||
variant_defaults[this.reset_variant]
|
||||
);
|
||||
|
||||
try {
|
||||
await api.put('config/save', config)
|
||||
this.confirmReset = false;
|
||||
this.$dispatch('update');
|
||||
this.configRestored = true;
|
||||
} catch (err) {
|
||||
api.alert('Restore failed');
|
||||
console.error('Restore failed', err);
|
||||
}
|
||||
},
|
||||
|
||||
check: function () {
|
||||
this.$dispatch('check')
|
||||
},
|
||||
|
||||
upgrade: function () {
|
||||
this.$dispatch('upgrade')
|
||||
},
|
||||
|
||||
upload_firmware: function () {
|
||||
// If we don't reset the form the browser may cache file if name is same
|
||||
// even if contents have changed
|
||||
$('.upload-firmware')[0].reset();
|
||||
$('.upload-firmware input').click();
|
||||
},
|
||||
|
||||
upload: function (e) {
|
||||
var files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length) return;
|
||||
this.$dispatch('upload', files[0]);
|
||||
},
|
||||
|
||||
change_auto_check_upgrade: function () {
|
||||
this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade;
|
||||
this.$dispatch('config-changed');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,177 +1,14 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
var api = require('./api');
|
||||
|
||||
|
||||
module.exports = {
|
||||
template: '#admin-network-view-template',
|
||||
props: ['config', 'state'],
|
||||
template: "#admin-network-view-template",
|
||||
|
||||
attached: function() {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"AdminNetworkView",
|
||||
document.getElementById("admin-network")
|
||||
);
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
hostnameSet: false,
|
||||
usernameSet: false,
|
||||
passwordSet: false,
|
||||
redirectTimeout: 0,
|
||||
hostname: '',
|
||||
username: '',
|
||||
current: '',
|
||||
password: '',
|
||||
password2: '',
|
||||
wifi_mode: 'client',
|
||||
wifi_ssid: '',
|
||||
wifi_ch: undefined,
|
||||
wifi_pass: '',
|
||||
wifiConfirm: false,
|
||||
rebooting: false
|
||||
detached: function() {
|
||||
this.svelteComponent.$destroy();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
ready: function () {
|
||||
api.get('hostname').done(function (hostname) {
|
||||
this.hostname = hostname;
|
||||
}.bind(this));
|
||||
|
||||
api.get('remote/username').done(function (username) {
|
||||
this.username = username;
|
||||
}.bind(this));
|
||||
|
||||
api.get('wifi').done(function (config) {
|
||||
this.wifi_mode = config.mode;
|
||||
this.wifi_ssid = config.ssid;
|
||||
this.wifi_ch = config.channel;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
redirect: function (hostname) {
|
||||
if (0 < this.redirectTimeout) {
|
||||
this.redirectTimeout -= 1;
|
||||
setTimeout(function () {this.redirect(hostname)}.bind(this), 1000);
|
||||
|
||||
} else location.hostname = hostname;
|
||||
},
|
||||
|
||||
|
||||
set_hostname: function () {
|
||||
api.put('hostname', {hostname: this.hostname}).done(function () {
|
||||
this.redirectTimeout = 45;
|
||||
this.hostnameSet = true;
|
||||
|
||||
api.put('reboot').always(function () {
|
||||
if (String(location.hostname) == 'localhost') return;
|
||||
|
||||
var hostname = this.hostname;
|
||||
if (String(location.hostname).endsWith('.local'))
|
||||
hostname += '.local'
|
||||
this.$dispatch('hostname-changed', hostname);
|
||||
this.redirect(hostname);
|
||||
}.bind(this));
|
||||
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Set hostname failed', error);
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
set_username: function () {
|
||||
api.put('remote/username', {username: this.username}).done(function () {
|
||||
this.usernameSet = true;
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Set username failed', error);
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
set_password: function () {
|
||||
if (this.password != this.password2) {
|
||||
alert('Passwords to not match');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.password.length < 6) {
|
||||
alert('Password too short');
|
||||
return;
|
||||
}
|
||||
|
||||
api.put('remote/password', {
|
||||
current: this.current,
|
||||
password: this.password
|
||||
}).done(function () {
|
||||
this.passwordSet = true;
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Set password failed', error);
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
config_wifi: function () {
|
||||
this.wifiConfirm = false;
|
||||
|
||||
if (!this.wifi_ssid.length) {
|
||||
alert('SSID not set');
|
||||
return;
|
||||
}
|
||||
|
||||
if (32 < this.wifi_ssid.length) {
|
||||
alert('SSID longer than 32 characters');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.wifi_pass.length && this.wifi_pass.length < 8) {
|
||||
alert('WiFi password shorter than 8 characters');
|
||||
return;
|
||||
}
|
||||
|
||||
if (128 < this.wifi_pass.length) {
|
||||
alert('WiFi password longer than 128 characters');
|
||||
return;
|
||||
}
|
||||
|
||||
this.rebooting = true;
|
||||
|
||||
var config = {
|
||||
mode: this.wifi_mode,
|
||||
channel: this.wifi_ch,
|
||||
ssid: this.wifi_ssid,
|
||||
pass: this.wifi_pass
|
||||
}
|
||||
|
||||
api.put('wifi', config).fail(function (error) {
|
||||
api.alert('Failed to configure WiFi', error);
|
||||
this.rebooting = false;
|
||||
}.bind(this))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
128
src/js/api.js
128
src/js/api.js
@@ -1,104 +1,48 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
async function callApi(method, url, data) {
|
||||
try {
|
||||
const headers = {};
|
||||
let body = undefined;
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
if (data) {
|
||||
if (data instanceof FormData) {
|
||||
body = data;
|
||||
} else {
|
||||
headers["Content-Type"] = "application/json; charset=utf-8";
|
||||
body = JSON.stringify(data);
|
||||
}
|
||||
}
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
const response = await fetch(`/api/${url}`, {
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
cache: "no-cache",
|
||||
});
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
throw new Error(await response.text());
|
||||
} catch (error) {
|
||||
console.debug(`API Error: ${url}: ${error}`);
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
function api_cb(method, url, data, config) {
|
||||
config = $.extend({
|
||||
type: method,
|
||||
url: '/api/' + url,
|
||||
dataType: 'json',
|
||||
cache: false
|
||||
}, config);
|
||||
|
||||
if (typeof data == 'object') {
|
||||
config.data = JSON.stringify(data);
|
||||
config.contentType = 'application/json; charset=utf-8';
|
||||
}
|
||||
|
||||
var d = $.Deferred();
|
||||
|
||||
$.ajax(config).success(function (data, status, xhr) {
|
||||
d.resolve(data, status, xhr);
|
||||
|
||||
}).error(function (xhr, status, error) {
|
||||
var text = xhr.responseText;
|
||||
try {text = $.parseJSON(xhr.responseText)} catch(e) {}
|
||||
if (!text) text = error;
|
||||
|
||||
d.reject(text, xhr, status, error);
|
||||
console.debug('API Error: ' + url + ': ' + text);
|
||||
});
|
||||
|
||||
return d.promise();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
get: function (url, config) {
|
||||
return api_cb('GET', url, undefined, config);
|
||||
},
|
||||
get: function(url) {
|
||||
return callApi("GET", url);
|
||||
},
|
||||
|
||||
put: function(url, body = undefined) {
|
||||
return callApi("PUT", url, body);
|
||||
},
|
||||
|
||||
put: function(url, data, config) {
|
||||
return api_cb('PUT', url, data, config);
|
||||
},
|
||||
|
||||
|
||||
post: function(url, data, config) {
|
||||
return api_cb('POST', url, data, config);
|
||||
},
|
||||
|
||||
|
||||
upload: function(url, data, config) {
|
||||
config = $.extend({
|
||||
processData: false,
|
||||
contentType: false,
|
||||
cache: false,
|
||||
data: data
|
||||
}, config);
|
||||
|
||||
return api_cb('PUT', url, undefined, config);
|
||||
},
|
||||
|
||||
|
||||
'delete': function (url, config) {
|
||||
return api_cb('DELETE', url, undefined, config);
|
||||
},
|
||||
|
||||
|
||||
alert: function (msg, error) {
|
||||
if (typeof error != 'undefined') {
|
||||
if (typeof error.message != 'undefined')
|
||||
msg += '\n' + error.message;
|
||||
else msg += '\n' + JSON.stringify(error);
|
||||
delete: function(url) {
|
||||
return callApi("DELETE", url);
|
||||
}
|
||||
|
||||
alert(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
860
src/js/app.js
860
src/js/app.js
@@ -1,506 +1,446 @@
|
||||
/******************************************************************************\
|
||||
"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.
|
||||
|
||||
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 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;
|
||||
}
|
||||
if (document.getElementById("svelte-dialog-host") != undefined) {
|
||||
SvelteComponents.createComponent(
|
||||
"DialogHost",
|
||||
document.getElementById("svelte-dialog-host")
|
||||
);
|
||||
}
|
||||
if (document.getElementById("adminViewSvelte") != undefined) {
|
||||
SvelteComponents.createComponent("AdminNetworkView");
|
||||
}
|
||||
|
||||
function is_object(o) { return o !== null && typeof o == 'object' }
|
||||
function is_array(o) { return Array.isArray(o) }
|
||||
function parse_version(v) {
|
||||
const pattern = /^(\d+)\.(\d+)\.(\d+)(?:[-.]?(.*))?$/;
|
||||
const [ version, major, minor, patch, pre ] = v.trim().match(pattern) || [];
|
||||
|
||||
return {
|
||||
version,
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
pre
|
||||
};
|
||||
}
|
||||
|
||||
function fixup_version_number(version) {
|
||||
const v = parse_version(version);
|
||||
|
||||
version = `${v.major}.${v.minor}.${v.patch}`;
|
||||
if (v.pre) {
|
||||
const [ , prefix, num ] = v.pre.match(/([a-zA-Z])(\d+)/);
|
||||
|
||||
const suffix = prefix === "b"
|
||||
? `beta.${num}`
|
||||
: v.pre;
|
||||
|
||||
version = `${version}-${suffix}`;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
function is_object(o) {
|
||||
return o !== null && typeof o == "object";
|
||||
}
|
||||
|
||||
function is_array(o) {
|
||||
return Array.isArray(o);
|
||||
}
|
||||
|
||||
function update_array(dst, src) {
|
||||
while (dst.length) dst.pop()
|
||||
for (var i = 0; i < src.length; i++)
|
||||
Vue.set(dst, i, src[i]);
|
||||
while (dst.length) {
|
||||
dst.pop();
|
||||
}
|
||||
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
Vue.set(dst, i, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function hasOwnProperty(obj, key) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
|
||||
function update_object(dst, src, remove) {
|
||||
var props, index, key, value;
|
||||
let props, index, key, value;
|
||||
|
||||
if (remove) {
|
||||
props = Object.getOwnPropertyNames(dst);
|
||||
if (remove) {
|
||||
props = Object.getOwnPropertyNames(dst);
|
||||
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
if (!src.hasOwnProperty(key))
|
||||
Vue.delete(dst, key);
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
if (!hasOwnProperty(src, key)) {
|
||||
Vue.delete(dst, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
props = Object.getOwnPropertyNames(src);
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
value = src[key];
|
||||
props = Object.getOwnPropertyNames(src);
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
value = src[key];
|
||||
|
||||
if (is_array(value) && dst.hasOwnProperty(key) && is_array(dst[key]))
|
||||
update_array(dst[key], value);
|
||||
|
||||
else if (is_object(value) && dst.hasOwnProperty(key) && is_object(dst[key]))
|
||||
update_object(dst[key], value, remove);
|
||||
|
||||
else Vue.set(dst, key, value);
|
||||
}
|
||||
if (is_array(value) && hasOwnProperty(dst, key) && is_array(dst[key])) {
|
||||
update_array(dst[key], value);
|
||||
} else if (is_object(value) && hasOwnProperty(dst, key) && is_object(dst[key])) {
|
||||
update_object(dst[key], value, remove);
|
||||
} else {
|
||||
Vue.set(dst, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Vue({
|
||||
el: 'body',
|
||||
el: "body",
|
||||
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
status: 'connecting',
|
||||
currentView: 'loading',
|
||||
index: -1,
|
||||
modified: false,
|
||||
template: require('../resources/config-template.json'),
|
||||
config: {
|
||||
settings: { units: 'METRIC' },
|
||||
motors: [{}, {}, {}, {}],
|
||||
version: '<loading>',
|
||||
full_version: '<loading>'
|
||||
},
|
||||
state: {
|
||||
messages: [],
|
||||
probing_active: false,
|
||||
wait_for_probing_complete: false,
|
||||
show_probe_complete_modal: false,
|
||||
show_probe_failed_modal: false
|
||||
},
|
||||
video_size: cookie.get('video-size', 'small'),
|
||||
crosshair: cookie.get('crosshair', 'false') != 'false',
|
||||
errorTimeout: 30,
|
||||
errorTimeoutStart: 0,
|
||||
errorShow: false,
|
||||
errorMessage: '',
|
||||
confirmUpgrade: false,
|
||||
confirmUpload: false,
|
||||
firmwareUpgrading: false,
|
||||
checkedUpgrade: false,
|
||||
firmwareName: '',
|
||||
latestVersion: '',
|
||||
ipAddress: '0.0.0.0',
|
||||
wifiSSID: '',
|
||||
confirmShutdown: false,
|
||||
diskSpace: ''
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
'estop': { template: '#estop-template' },
|
||||
'loading-view': { template: '<h1>Loading...</h1>' },
|
||||
'control-view': require('./control-view'),
|
||||
'settings-view': require('./settings-view'),
|
||||
'motor-view': require('./motor-view'),
|
||||
'tool-view': require('./tool-view'),
|
||||
'io-view': require('./io-view'),
|
||||
'admin-general-view': require('./admin-general-view'),
|
||||
'admin-network-view': require('./admin-network-view'),
|
||||
'help-view': { template: '#help-view-template' },
|
||||
'cheat-sheet-view': {
|
||||
template: '#cheat-sheet-view-template',
|
||||
data: function () { return { showUnimplemented: false } }
|
||||
}
|
||||
},
|
||||
|
||||
events: {
|
||||
'config-changed': function () {
|
||||
this.modified = true;
|
||||
data: function() {
|
||||
return {
|
||||
status: "connecting",
|
||||
currentView: "loading",
|
||||
display_units: localStorage.getItem("display_units") || "METRIC",
|
||||
index: -1,
|
||||
modified: false,
|
||||
template: require("../resources/config-template.json"),
|
||||
config: {
|
||||
settings: { units: "METRIC" },
|
||||
motors: [{}, {}, {}, {}],
|
||||
version: "<loading>",
|
||||
full_version: "<loading>",
|
||||
ip: "<>",
|
||||
wifiName: "not connected",
|
||||
},
|
||||
state: {
|
||||
messages: [],
|
||||
},
|
||||
video_size: cookie.get("video-size", "small"),
|
||||
crosshair: cookie.get("crosshair", "false") != "false",
|
||||
errorTimeout: 30,
|
||||
errorTimeoutStart: 0,
|
||||
errorShow: false,
|
||||
errorMessage: "",
|
||||
confirmUpgrade: false,
|
||||
confirmUpload: false,
|
||||
firmwareUpgrading: false,
|
||||
checkedUpgrade: false,
|
||||
firmwareName: "",
|
||||
latestVersion: "",
|
||||
};
|
||||
},
|
||||
|
||||
'hostname-changed': function (hostname) {
|
||||
this.hostname = hostname
|
||||
components: {
|
||||
estop: { template: "#estop-template" },
|
||||
"loading-view": { template: "<h1>Loading...</h1>" },
|
||||
"control-view": require("./control-view"),
|
||||
"settings-view": require("./settings-view"),
|
||||
"motor-view": require("./motor-view"),
|
||||
"tool-view": require("./tool-view"),
|
||||
"io-view": require("./io-view"),
|
||||
"admin-general-view": require("./admin-general-view"),
|
||||
"admin-network-view": require("./admin-network-view"),
|
||||
"help-view": require("./help-view"),
|
||||
"cheat-sheet-view": {
|
||||
template: "#cheat-sheet-view-template",
|
||||
data: function() {
|
||||
return {
|
||||
showUnimplemented: false
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
send: function (msg) {
|
||||
if (this.status == 'connected') {
|
||||
console.debug('>', msg);
|
||||
this.sock.send(msg);
|
||||
}
|
||||
watch: {
|
||||
display_units: function(value) {
|
||||
localStorage.setItem("display_units", value);
|
||||
SvelteComponents.setDisplayUnits(value);
|
||||
},
|
||||
},
|
||||
|
||||
connected: function () {
|
||||
this.update()
|
||||
},
|
||||
events: {
|
||||
"config-changed": function() {
|
||||
this.modified = true;
|
||||
},
|
||||
|
||||
update: function () {
|
||||
this.update()
|
||||
},
|
||||
send: function(msg) {
|
||||
if (this.status == "connected") {
|
||||
this.sock.send(msg);
|
||||
}
|
||||
},
|
||||
|
||||
check: function () {
|
||||
this.latestVersion = '';
|
||||
connected: function() {
|
||||
this.update();
|
||||
},
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt',
|
||||
data: { hid: this.state.hid },
|
||||
cache: false
|
||||
update: function() {
|
||||
this.update();
|
||||
},
|
||||
|
||||
}).done(function (data) {
|
||||
this.latestVersion = data;
|
||||
this.$broadcast('latest_version', data);
|
||||
}.bind(this))
|
||||
},
|
||||
check: async function() {
|
||||
try {
|
||||
const response = await fetch("https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt", {
|
||||
cache: "no-cache"
|
||||
});
|
||||
|
||||
upgrade: function () {
|
||||
this.confirmUpgrade = true;
|
||||
},
|
||||
this.latestVersion = (await response.text()).trim();
|
||||
} catch (err) {
|
||||
this.latestVersion = "";
|
||||
}
|
||||
},
|
||||
|
||||
upload: function (firmware) {
|
||||
this.firmware = firmware;
|
||||
this.firmwareName = firmware.name;
|
||||
this.confirmUpload = true;
|
||||
},
|
||||
upgrade: function() {
|
||||
this.confirmUpgrade = true;
|
||||
},
|
||||
|
||||
error: function (msg) {
|
||||
// Honor user error blocking
|
||||
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000)
|
||||
return;
|
||||
upload: function(firmware) {
|
||||
this.firmware = firmware;
|
||||
this.firmwareName = firmware.name;
|
||||
this.confirmUpload = true;
|
||||
},
|
||||
|
||||
// Wait at least 1 sec to pop up repeated errors
|
||||
if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Popup error dialog
|
||||
this.errorShow = true;
|
||||
this.errorMessage = msg.msg;
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
popupMessages: function () {
|
||||
const msgs = [];
|
||||
|
||||
for (let i = 0; i < this.state.messages.length; i++) {
|
||||
const text = this.state.messages[i].text;
|
||||
if (!/^#/.test(text)) {
|
||||
msgs.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
return msgs;
|
||||
}
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
$(window).on('hashchange', this.parse_hash);
|
||||
this.connect();
|
||||
},
|
||||
|
||||
methods: {
|
||||
metric: function () {
|
||||
return this.config.settings.units != 'IMPERIAL'
|
||||
},
|
||||
|
||||
block_error_dialog: function () {
|
||||
this.errorTimeoutStart = Date.now();
|
||||
this.errorShow = false;
|
||||
},
|
||||
|
||||
toggle_video: function (e) {
|
||||
if (this.video_size == 'small') this.video_size = 'large';
|
||||
else if (this.video_size == 'large') this.video_size = 'small';
|
||||
cookie.set('video-size', this.video_size);
|
||||
},
|
||||
|
||||
toggle_crosshair: function (e) {
|
||||
e.preventDefault();
|
||||
this.crosshair = !this.crosshair;
|
||||
cookie.set('crosshair', this.crosshair);
|
||||
},
|
||||
|
||||
estop: function () {
|
||||
if (this.state.xx == 'ESTOPPED') api.put('clear');
|
||||
else api.put('estop');
|
||||
},
|
||||
|
||||
upgrade_confirmed: async function () {
|
||||
this.confirmUpgrade = false;
|
||||
|
||||
try {
|
||||
await api.put('upgrade');
|
||||
this.firmwareUpgrading = true;
|
||||
} catch (err) {
|
||||
api.alert('Error during upgrade.');
|
||||
console.error("Error during upgrade", err);
|
||||
}
|
||||
},
|
||||
|
||||
upload_confirmed: function () {
|
||||
this.confirmUpload = false;
|
||||
|
||||
const form = new FormData();
|
||||
form.append('firmware', this.firmware);
|
||||
|
||||
$.ajax({
|
||||
url: '/api/firmware/update',
|
||||
type: 'PUT',
|
||||
data: form,
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false
|
||||
|
||||
}).success(function () {
|
||||
this.firmwareUpgrading = true;
|
||||
}.bind(this)).error(function (err) {
|
||||
api.alert('Firmware update failed');
|
||||
console.error('Firmware update failed', err);
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
show_upgrade: function () {
|
||||
if (!this.latestVersion) return false;
|
||||
return is_newer_version(this.config.version, this.latestVersion);
|
||||
},
|
||||
|
||||
update: function () {
|
||||
api.get('config/load').done(function (config) {
|
||||
update_object(this.config, config, true);
|
||||
this.parse_hash();
|
||||
|
||||
if (!this.checkedUpgrade) {
|
||||
this.checkedUpgrade = true;
|
||||
|
||||
var check = this.config.admin['auto-check-upgrade'];
|
||||
if (typeof check == 'undefined' || check)
|
||||
this.$emit('check');
|
||||
}
|
||||
|
||||
this.check_ip_address();
|
||||
this.check_ssid();
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
check_ip_address: function () {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'hostinfo.txt',
|
||||
data: { hid: this.state.hid },
|
||||
cache: false
|
||||
|
||||
}).done(function (data) {
|
||||
console.debug('>', data);
|
||||
this.ipAddress = 'IP:' + data;
|
||||
this.$broadcast('ipAddress', data);
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
check_ssid: function () {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'ssidinfo.txt',
|
||||
data: { hid: this.state.hid },
|
||||
cache: false
|
||||
|
||||
}).done(function (data) {
|
||||
console.debug('>', data);
|
||||
this.wifiSSID = 'SSID:' + data;
|
||||
this.$broadcast('wifiSSID', data);
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
get_ip_address: function () {
|
||||
console.debug('get_ip>', this.ipAddress);
|
||||
return this.ipAddress;
|
||||
},
|
||||
|
||||
get_ssid: function () {
|
||||
console.debug('get_ssid>', this.wifiSSID);
|
||||
return this.wifiSSID;
|
||||
},
|
||||
|
||||
shutdown: function () {
|
||||
this.confirmShutdown = false;
|
||||
api.put('shutdown');
|
||||
|
||||
},
|
||||
|
||||
reboot: function () {
|
||||
this.confirmShutdown = false;
|
||||
api.put('reboot');
|
||||
},
|
||||
|
||||
connect: function () {
|
||||
this.sock = new Sock(`//${location.host}/sockjs`);
|
||||
|
||||
this.sock.onmessage = (e) => {
|
||||
if (typeof e.data != 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('log' in e.data) {
|
||||
if (e.data.log.msg === "Switch not found") {
|
||||
this.$broadcast('probing_failed');
|
||||
} else {
|
||||
this.$broadcast('log', e.data.log);
|
||||
}
|
||||
|
||||
delete e.data.log;
|
||||
}
|
||||
|
||||
// Check for session ID change on controller
|
||||
if ('sid' in e.data) {
|
||||
if (typeof this.sid == 'undefined') {
|
||||
this.sid = e.data.sid;
|
||||
} else if (this.sid != e.data.sid) {
|
||||
if (typeof this.hostname !== 'undefined' && location.hostname !== 'localhost') {
|
||||
location.hostname = this.hostname;
|
||||
error: function(msg) {
|
||||
// Honor user error blocking
|
||||
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
// Wait at least 1 sec to pop up repeated errors
|
||||
if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set this to true to get console output of changes to the state
|
||||
const debugStateChanges = false;
|
||||
if (debugStateChanges) {
|
||||
const data = omit(e.data, [
|
||||
'vdd',
|
||||
'vin',
|
||||
'vout',
|
||||
'motor',
|
||||
'temp',
|
||||
'heartbeat',
|
||||
'load1',
|
||||
'load2',
|
||||
'rpi_temp'
|
||||
]);
|
||||
if (Object.keys(data).length > 0) {
|
||||
console.log(JSON.stringify(data, null, 4));
|
||||
}
|
||||
}
|
||||
|
||||
update_object(this.state, e.data, false);
|
||||
|
||||
if (this.state.pw === 0) {
|
||||
Vue.set(this.state, "saw_probe_connected", true);
|
||||
}
|
||||
|
||||
if (this.state.cycle === 'idle') {
|
||||
if (this.state.wait_for_probing_complete) {
|
||||
Vue.set(this.state, "wait_for_probing_complete", false);
|
||||
this.$broadcast("probing_complete");
|
||||
}
|
||||
}
|
||||
|
||||
this.$broadcast('update');
|
||||
};
|
||||
|
||||
this.sock.onopen = () => {
|
||||
this.status = 'connected';
|
||||
this.$emit(this.status);
|
||||
this.$broadcast(this.status);
|
||||
};
|
||||
|
||||
this.sock.onclose = () => {
|
||||
this.status = 'disconnected';
|
||||
this.$emit(this.status);
|
||||
this.$broadcast(this.status);
|
||||
};
|
||||
// Popup error dialog
|
||||
this.errorShow = true;
|
||||
this.errorMessage = msg.msg;
|
||||
},
|
||||
},
|
||||
|
||||
parse_hash: function () {
|
||||
var hash = location.hash.substr(1);
|
||||
computed: {
|
||||
popupMessages: function() {
|
||||
const msgs = [];
|
||||
|
||||
if (!hash.trim().length) {
|
||||
location.hash = 'control';
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < this.state.messages.length; i++) {
|
||||
const text = this.state.messages[i].text;
|
||||
if (!/^#/.test(text)) {
|
||||
msgs.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
var parts = hash.split(':');
|
||||
|
||||
if (parts.length == 2) this.index = parts[1];
|
||||
|
||||
this.currentView = parts[0];
|
||||
return msgs;
|
||||
},
|
||||
},
|
||||
|
||||
save: function () {
|
||||
const selected_tool = this.config.tool['selected-tool'];
|
||||
const saveModbus = selected_tool !== "pwm" &&
|
||||
selected_tool !== "laser" &&
|
||||
selected_tool !== "router";
|
||||
const settings = {
|
||||
['tool']: { ...this.config.tool },
|
||||
['pwm-spindle']: { ...this.config['pwm-spindle'] },
|
||||
['modbus-spindle']: saveModbus ? { ...this.config['modbus-spindle'] } : undefined
|
||||
}
|
||||
delete settings.tool['tool-type'];
|
||||
ready: function() {
|
||||
window.onhashchange = () => this.parse_hash();
|
||||
this.connect();
|
||||
|
||||
this.config['selected-tool-settings'][selected_tool] = settings;
|
||||
|
||||
api.put('config/save', this.config).done(function (data) {
|
||||
this.modified = false;
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Save failed', error);
|
||||
});
|
||||
SvelteComponents.registerControllerMethods({
|
||||
dispatch: (...args) => this.$dispatch(...args)
|
||||
});
|
||||
},
|
||||
|
||||
close_messages: function (action) {
|
||||
if (action == 'stop') api.put('stop');
|
||||
if (action == 'continue') api.put('unpause');
|
||||
methods: {
|
||||
block_error_dialog: function() {
|
||||
this.errorTimeoutStart = Date.now();
|
||||
this.errorShow = false;
|
||||
},
|
||||
|
||||
// Acknowledge messages
|
||||
if (this.state.messages.length) {
|
||||
var id = this.state.messages.slice(-1)[0].id
|
||||
api.put('message/' + id + '/ack');
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
toggle_video: function() {
|
||||
if (this.video_size == "small") {
|
||||
this.video_size = "large";
|
||||
} else if (this.video_size == "large") {
|
||||
this.video_size = "small";
|
||||
}
|
||||
cookie.set("video-size", this.video_size);
|
||||
},
|
||||
|
||||
toggle_crosshair: function(e) {
|
||||
e.preventDefault();
|
||||
this.crosshair = !this.crosshair;
|
||||
cookie.set("crosshair", this.crosshair);
|
||||
},
|
||||
|
||||
estop: function() {
|
||||
if (this.state.xx == "ESTOPPED") {
|
||||
api.put("clear");
|
||||
} else {
|
||||
api.put("estop");
|
||||
}
|
||||
},
|
||||
|
||||
upgrade_confirmed: async function() {
|
||||
this.confirmUpgrade = false;
|
||||
|
||||
try {
|
||||
await api.put("upgrade");
|
||||
this.firmwareUpgrading = true;
|
||||
} catch (error) {
|
||||
console.error("Error during upgrade:", error);
|
||||
alert("Error during upgrade");
|
||||
}
|
||||
},
|
||||
|
||||
upload_confirmed: async function() {
|
||||
this.confirmUpload = false;
|
||||
|
||||
const form = new FormData();
|
||||
form.append("firmware", this.firmware);
|
||||
|
||||
try {
|
||||
await api.put("firmware/update", form);
|
||||
this.firmwareUpgrading = true;
|
||||
} catch (error) {
|
||||
console.error("Firmware update failed:", error);
|
||||
alert("Firmware update failed");
|
||||
}
|
||||
},
|
||||
|
||||
show_upgrade: function() {
|
||||
if (!this.latestVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return semverLt(this.config.full_version, this.latestVersion);
|
||||
},
|
||||
|
||||
showShutdownDialog: function() {
|
||||
SvelteComponents.showDialog("Shutdown");
|
||||
},
|
||||
|
||||
update: async function() {
|
||||
const config = await api.get("config/load");
|
||||
const wifi = await api.get("wifi");
|
||||
update_object(this.config, config, true);
|
||||
this.config.full_version = fixup_version_number(this.config.full_version);
|
||||
this.config.ip = wifi.ipAddresses;
|
||||
this.config.wifiName = wifi.wifi;
|
||||
this.parse_hash();
|
||||
|
||||
if (!this.checkedUpgrade) {
|
||||
this.checkedUpgrade = true;
|
||||
|
||||
const check = this.config.admin["auto-check-upgrade"];
|
||||
if (typeof check == "undefined" || check) {
|
||||
this.$emit("check");
|
||||
}
|
||||
}
|
||||
|
||||
SvelteComponents.handleConfigUpdate(this.config);
|
||||
},
|
||||
|
||||
connect: function() {
|
||||
this.sock = new Sock(`//${location.host}/sockjs`);
|
||||
|
||||
this.sock.onmessage = (e) => {
|
||||
if (typeof e.data != "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.data.log && e.data.log.msg !== "Switch not found") {
|
||||
this.$broadcast("log", e.data.log);
|
||||
|
||||
if (Object.keys(e.data).length === 1) {
|
||||
// If there's only log data, we're done
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for session ID change on controller
|
||||
if ("sid" in e.data) {
|
||||
if (typeof this.sid == "undefined") {
|
||||
this.sid = e.data.sid;
|
||||
} else if (this.sid != e.data.sid) {
|
||||
if (this.hostname && location.hostname !== "localhost") {
|
||||
location.hostname = this.hostname;
|
||||
}
|
||||
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
update_object(this.state, e.data, false);
|
||||
|
||||
SvelteComponents.handleControllerStateUpdate(this.state);
|
||||
|
||||
delete this.state.log;
|
||||
|
||||
this.$broadcast("update");
|
||||
};
|
||||
|
||||
this.sock.onopen = () => {
|
||||
this.status = "connected";
|
||||
this.$emit(this.status);
|
||||
this.$broadcast(this.status);
|
||||
};
|
||||
|
||||
this.sock.onclose = () => {
|
||||
this.status = "disconnected";
|
||||
this.$emit(this.status);
|
||||
this.$broadcast(this.status);
|
||||
};
|
||||
},
|
||||
|
||||
parse_hash: function() {
|
||||
const hash = location.hash.substr(1);
|
||||
|
||||
if (!hash.trim().length) {
|
||||
location.hash = "control";
|
||||
return;
|
||||
}
|
||||
|
||||
const parts = hash.split(":");
|
||||
|
||||
if (parts.length == 2) {
|
||||
this.index = parts[1];
|
||||
}
|
||||
|
||||
this.currentView = parts[0];
|
||||
},
|
||||
|
||||
save: async function() {
|
||||
const selected_tool = this.config.tool["selected-tool"];
|
||||
const saveModbus =
|
||||
selected_tool !== "pwm" &&
|
||||
selected_tool !== "laser" &&
|
||||
selected_tool !== "router";
|
||||
const settings = {
|
||||
["tool"]: { ...this.config.tool },
|
||||
["pwm-spindle"]: { ...this.config["pwm-spindle"] },
|
||||
["modbus-spindle"]: saveModbus
|
||||
? { ...this.config["modbus-spindle"] }
|
||||
: undefined,
|
||||
};
|
||||
delete settings.tool["tool-type"];
|
||||
|
||||
this.config["selected-tool-settings"][selected_tool] = settings;
|
||||
|
||||
try {
|
||||
await api.put("config/save", this.config);
|
||||
this.modified = false;
|
||||
} catch (error) {
|
||||
console.error("Save failed:", error);
|
||||
alert("Save failed");
|
||||
}
|
||||
},
|
||||
|
||||
close_messages: function(action) {
|
||||
if (action == "stop") {
|
||||
api.put("stop");
|
||||
}
|
||||
|
||||
if (action == "continue") {
|
||||
api.put("unpause");
|
||||
}
|
||||
|
||||
// Acknowledge messages
|
||||
if (this.state.messages.length) {
|
||||
const id = this.state.messages.slice(-1)[0].id;
|
||||
api.put(`message/${id}/ack`);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,65 +1,37 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#axis-control-template',
|
||||
props: ['axes', 'colors', 'enabled', 'adjust', 'step'],
|
||||
template: "#axis-control-template",
|
||||
props: [ "axes", "colors", "enabled", "adjust", "step" ],
|
||||
|
||||
methods: {
|
||||
jog: function(axis, ring, direction) {
|
||||
const value = direction * this.value(ring);
|
||||
this.$dispatch(this.step ? "step" : "jog", this.axes[axis], value);
|
||||
},
|
||||
|
||||
methods: {
|
||||
jog: function (axis, ring, direction) {
|
||||
var value = direction * this.value(ring);
|
||||
this.$dispatch(this.step ? 'step' : 'jog', this.axes[axis], value);
|
||||
},
|
||||
back2zero: function(axis0,axis1) {
|
||||
this.$dispatch("back2zero",this.axes[axis0],this.axes[axis1]);
|
||||
},
|
||||
|
||||
back2zero: function(axis0,axis1) {
|
||||
this.$dispatch('back2zero',this.axes[axis0],this.axes[axis1])
|
||||
},
|
||||
release: function(axis) {
|
||||
if (!this.step) {
|
||||
this.$dispatch("jog", this.axes[axis], 0);
|
||||
}
|
||||
},
|
||||
|
||||
value: function(ring) {
|
||||
const adjust = [ 0.01, 0.1, 1 ][this.adjust];
|
||||
if (this.step) {
|
||||
return adjust * [ 0.1, 1, 10, 100 ][ring];
|
||||
}
|
||||
return adjust * [ 0.1, 0.25, 0.5, 1 ][ring];
|
||||
},
|
||||
|
||||
release: function (axis) {
|
||||
if (!this.step) this.$dispatch('jog', this.axes[axis], 0)
|
||||
},
|
||||
|
||||
|
||||
value: function (ring) {
|
||||
var adjust = [0.01, 0.1, 1][this.adjust];
|
||||
if (this.step) return adjust * [0.1, 1, 10, 100][ring];
|
||||
return adjust * [0.1, 0.25, 0.5, 1][ring];
|
||||
},
|
||||
|
||||
|
||||
text: function (ring) {
|
||||
var value = this.value(ring) * (this.step ? 1 : 100);
|
||||
value = parseFloat(value.toFixed(3));
|
||||
return value + (this.step ? '' : '%');
|
||||
text: function(ring) {
|
||||
let value = this.value(ring) * (this.step ? 1 : 100);
|
||||
value = parseFloat(value.toFixed(3));
|
||||
return value + (this.step ? "" : "%");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,232 +1,254 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
function is_defined(x) {return typeof x != 'undefined'}
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
props: ['state', 'config'],
|
||||
props: [ "state", "config" ],
|
||||
|
||||
computed: {
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
computed: {
|
||||
x: function () {return this._compute_axis('x')},
|
||||
y: function () {return this._compute_axis('y')},
|
||||
z: function () {return this._compute_axis('z')},
|
||||
a: function () {return this._compute_axis('a')},
|
||||
b: function () {return this._compute_axis('b')},
|
||||
c: function () {return this._compute_axis('c')},
|
||||
axes: function () {return this._compute_axes()}
|
||||
},
|
||||
x: function() {
|
||||
return this._compute_axis("x");
|
||||
},
|
||||
|
||||
y: function() {
|
||||
return this._compute_axis("y");
|
||||
},
|
||||
|
||||
methods: {
|
||||
_convert_length: function (value) {
|
||||
return this.state.imperial ? value / 25.4 : value;
|
||||
},
|
||||
z: function() {
|
||||
return this._compute_axis("z");
|
||||
},
|
||||
|
||||
a: function() {
|
||||
return this._compute_axis("a");
|
||||
},
|
||||
|
||||
_length_str: function (value) {
|
||||
return this._convert_length(value).toLocaleString() +
|
||||
(this.state.imperial ? ' in' : ' mm');
|
||||
},
|
||||
b: function() {
|
||||
return this._compute_axis("b");
|
||||
},
|
||||
|
||||
c: function() {
|
||||
return this._compute_axis("c");
|
||||
},
|
||||
|
||||
_compute_axis: function (axis) {
|
||||
var abs = this.state[axis + 'p'] || 0;
|
||||
var off = this.state['offset_' + axis];
|
||||
var motor_id = this._get_motor_id(axis);
|
||||
var motor = motor_id == -1 ? {} : this.config.motors[motor_id];
|
||||
var enabled = typeof motor.enabled != 'undefined' && motor.enabled;
|
||||
var homingMode = motor['homing-mode']
|
||||
var homed = this.state[motor_id + 'homed'];
|
||||
var min = this.state[motor_id + 'tn'];
|
||||
var max = this.state[motor_id + 'tm'];
|
||||
var dim = max - min;
|
||||
var pathMin = this.state['path_min_' + axis];
|
||||
var pathMax = this.state['path_max_' + axis];
|
||||
var pathDim = pathMax - pathMin;
|
||||
var under = pathMin + off < min;
|
||||
var over = max < pathMax + off;
|
||||
var klass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis;
|
||||
var state = 'UNHOMED';
|
||||
var icon = 'question-circle';
|
||||
var fault = this.state[motor_id + 'df'] & 0x1f;
|
||||
var shutdown = this.state.power_shutdown;
|
||||
var title;
|
||||
var ticon = 'question-circle';
|
||||
var tstate = 'NO FILE';
|
||||
var toolmsg;
|
||||
var tklass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis;
|
||||
|
||||
if (fault || shutdown) {
|
||||
state = shutdown ? 'SHUTDOWN' : 'FAULT';
|
||||
klass += ' error';
|
||||
icon = 'exclamation-circle';
|
||||
|
||||
} else if(homed) {
|
||||
state = 'HOMED';
|
||||
icon = 'check-circle';
|
||||
}
|
||||
|
||||
if (0 < dim && dim < pathDim) {
|
||||
tstate = 'NO FIT';
|
||||
tklass += ' error';
|
||||
ticon = 'ban';
|
||||
|
||||
} else {
|
||||
|
||||
if (over || under) {
|
||||
tstate = over ? 'OVER' : 'UNDER';
|
||||
tklass += ' warn';
|
||||
ticon = 'exclamation-circle';
|
||||
} else {
|
||||
tstate = 'OK';
|
||||
ticon = 'check-circle';
|
||||
axes: function() {
|
||||
return this._compute_axes();
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case 'UNHOMED': title = 'Click the home button to home axis.'; break;
|
||||
case 'HOMED': title = 'Axis successfuly homed.'; break;
|
||||
case 'FAULT':
|
||||
title = 'Motor driver fault. A potentially damaging electrical ' +
|
||||
'condition was detected and the motor driver was shutdown. ' +
|
||||
'Please power down the controller and check your motor cabling. ' +
|
||||
'See the "Motor Faults" table on the "Indicators" tab for more ' +
|
||||
'information.';
|
||||
break;
|
||||
case 'SHUTDOWN':
|
||||
title = 'Motor power fault. All motors in shutdown. ' +
|
||||
'See the "Power Faults" table on the "Indicators" tab for more ' +
|
||||
'information. Reboot controller to reset.';
|
||||
}
|
||||
|
||||
switch(tstate) {
|
||||
|
||||
case 'OVER':
|
||||
toolmsg = 'Caution: The current tool path file would move ' +
|
||||
this._length_str(pathMax + off - max) + ' above axis limit with the current offset.';
|
||||
break;
|
||||
|
||||
case 'UNDER':
|
||||
toolmsg = 'Caution: The current tool path file would move ' +
|
||||
this._length_str(min - pathMin - off) + ' below limit with the current offset.';
|
||||
break;
|
||||
|
||||
case 'NO FIT':
|
||||
toolmsg = 'Warning: The current tool path dimensions (' +
|
||||
this._length_str(pathDim) + ') exceed axis dimensions (' +
|
||||
this._length_str(dim) + ') by ' +
|
||||
this._length_str(pathDim - dim) + '.';
|
||||
break;
|
||||
|
||||
default:
|
||||
toolmsg = 'Tool path ' + axis + ' dimensions OK.';
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
pos: abs - off,
|
||||
abs: abs,
|
||||
off: off,
|
||||
min: min,
|
||||
max: max,
|
||||
dim: dim,
|
||||
pathMin: pathMin,
|
||||
pathMax: pathMax,
|
||||
pathDim: pathDim,
|
||||
motor: motor_id,
|
||||
enabled: enabled,
|
||||
homingMode: homingMode,
|
||||
homed: homed,
|
||||
klass: klass,
|
||||
state: state,
|
||||
icon: icon,
|
||||
title: title,
|
||||
ticon: ticon,
|
||||
tstate: tstate,
|
||||
toolmsg: toolmsg,
|
||||
tklass: tklass
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
_convert_length: function(value) {
|
||||
return this.metric
|
||||
? value
|
||||
: value / 25.4;
|
||||
},
|
||||
|
||||
_get_motor_id: function (axis) {
|
||||
for (var i = 0; i < this.config.motors.length; i++) {
|
||||
var motor = this.config.motors[i];
|
||||
if (motor.axis.toLowerCase() == axis) return i;
|
||||
}
|
||||
_length_str: function(value) {
|
||||
return this._convert_length(value).toLocaleString() + (this.metric ? " mm" : " in");
|
||||
},
|
||||
|
||||
return -1;
|
||||
},
|
||||
_compute_axis: function(axis) {
|
||||
const abs = this.state[`${axis}p`] || 0;
|
||||
const off = this.state[`offset_${axis}`];
|
||||
const motor_id = this._get_motor_id(axis);
|
||||
const motor = motor_id == -1 ? {} : this.config.motors[motor_id];
|
||||
const enabled = typeof motor.enabled != "undefined" && motor.enabled;
|
||||
const homingMode = motor["homing-mode"];
|
||||
const homed = this.state[`${motor_id}homed`];
|
||||
const min = this.state[`${motor_id}tn`];
|
||||
const max = this.state[`${motor_id}tm`];
|
||||
const dim = max - min;
|
||||
const pathMin = this.state[`path_min_${axis}`];
|
||||
const pathMax = this.state[`path_max_${axis}`];
|
||||
const pathDim = pathMax - pathMin;
|
||||
const under = pathMin + off < min;
|
||||
const over = max < pathMax + off;
|
||||
let klass = `${homed ? "homed" : "unhomed"} axis-${axis}`;
|
||||
let state = "UNHOMED";
|
||||
let icon = "question-circle";
|
||||
const fault = this.state[`${motor_id}df`] & 0x1f;
|
||||
const shutdown = this.state.power_shutdown;
|
||||
let title;
|
||||
let ticon = "question-circle";
|
||||
let tstate = "NO FILE";
|
||||
let toolmsg;
|
||||
let tklass = `${homed ? "homed" : "unhomed"} axis-${axis}`;
|
||||
|
||||
if (fault || shutdown) {
|
||||
state = shutdown ? "SHUTDOWN" : "FAULT";
|
||||
klass += " error";
|
||||
icon = "exclamation-circle";
|
||||
} else if (homed) {
|
||||
state = "HOMED";
|
||||
icon = "check-circle";
|
||||
}
|
||||
|
||||
_compute_axes: function () {
|
||||
var homed = false;
|
||||
if (0 < dim && dim < pathDim) {
|
||||
tstate = "NO FIT";
|
||||
tklass += " error";
|
||||
ticon = "ban";
|
||||
} else {
|
||||
if (over || under) {
|
||||
tstate = over ? "OVER" : "UNDER";
|
||||
tklass += " warn";
|
||||
ticon = "exclamation-circle";
|
||||
} else {
|
||||
tstate = "OK";
|
||||
ticon = "check-circle";
|
||||
}
|
||||
}
|
||||
|
||||
for (var name of 'xyzabc') {
|
||||
var axis = this[name];
|
||||
switch (state) {
|
||||
case "UNHOMED":
|
||||
title = "Click the home button to home axis.";
|
||||
break;
|
||||
|
||||
if (!axis.enabled) continue
|
||||
if (!axis.homed) {homed = false; break}
|
||||
homed = true;
|
||||
}
|
||||
case "HOMED":
|
||||
title = "Axis successfuly homed.";
|
||||
break;
|
||||
|
||||
var error = false;
|
||||
var warn = false;
|
||||
case "FAULT":
|
||||
title = [
|
||||
`Motor driver fault. A potentially damaging electrical`,
|
||||
`condition was detected and the motor driver was shutdown.`,
|
||||
`Please power down the controller and check your motor cabling.`,
|
||||
`See the "Motor Faults" table on the "Indicators" tab for more`,
|
||||
`information.`,
|
||||
].join(" ");
|
||||
break;
|
||||
|
||||
if (homed)
|
||||
for (name of 'xyzabc') {
|
||||
axis = this[name];
|
||||
case "SHUTDOWN":
|
||||
title = [
|
||||
`Motor power fault. All motors in shutdown.`,
|
||||
`See the "Power Faults" table on the "Indicators" tab for more`,
|
||||
`information. Reboot controller to reset.`
|
||||
].join(" ");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!axis.enabled) continue;
|
||||
if (axis.klass.indexOf('error') != -1) error = true;
|
||||
if (axis.klass.indexOf('warn') != -1) warn = true;
|
||||
switch (tstate) {
|
||||
case "OVER":
|
||||
toolmsg = [
|
||||
`Caution: The current tool path file would move`,
|
||||
`${this._length_str(pathMax + off - max)}`,
|
||||
`above axis limit with the current offset.`
|
||||
].join(" ");
|
||||
break;
|
||||
|
||||
case "UNDER":
|
||||
toolmsg = [
|
||||
`Caution: The current tool path file would move`,
|
||||
`${this._length_str(min - pathMin - off)}`,
|
||||
`below limit with the current offset.`
|
||||
].join(" ");
|
||||
break;
|
||||
|
||||
case "NO FIT":
|
||||
toolmsg = [
|
||||
`Warning: The current tool path dimensions`,
|
||||
`(${this._length_str(pathDim)}) exceed axis dimensions`,
|
||||
`(${this._length_str(dim)}) by ${this._length_str(pathDim - dim)}.`
|
||||
].join(" ");
|
||||
break;
|
||||
|
||||
default:
|
||||
toolmsg = `Tool path ${axis} dimensions OK.`;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
pos: abs - off,
|
||||
abs: abs,
|
||||
off: off,
|
||||
min: min,
|
||||
max: max,
|
||||
dim: dim,
|
||||
pathMin: pathMin,
|
||||
pathMax: pathMax,
|
||||
pathDim: pathDim,
|
||||
motor: motor_id,
|
||||
enabled: enabled,
|
||||
homingMode: homingMode,
|
||||
homed: homed,
|
||||
klass: klass,
|
||||
state: state,
|
||||
icon: icon,
|
||||
title: title,
|
||||
ticon: ticon,
|
||||
tstate: tstate,
|
||||
toolmsg: toolmsg,
|
||||
tklass: tklass
|
||||
};
|
||||
},
|
||||
|
||||
_get_motor_id: function(axis) {
|
||||
for (let i = 0; i < this.config.motors.length; i++) {
|
||||
const motor = this.config.motors[i];
|
||||
if (motor.axis.toLowerCase() == axis) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
|
||||
_compute_axes: function() {
|
||||
let homed = false;
|
||||
|
||||
for (const name of "xyzabc") {
|
||||
const axis = this[name];
|
||||
|
||||
if (!axis.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!axis.homed) {
|
||||
homed = false; break;
|
||||
}
|
||||
|
||||
homed = true;
|
||||
}
|
||||
|
||||
let error = false;
|
||||
let warn = false;
|
||||
|
||||
if (homed) {
|
||||
for (const name of "xyzabc") {
|
||||
const axis = this[name];
|
||||
|
||||
if (!axis.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (axis.klass.indexOf("error") != -1) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (axis.klass.indexOf("warn") != -1) {
|
||||
warn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let klass = homed ? "homed" : "unhomed";
|
||||
if (error) {
|
||||
klass += " error";
|
||||
} else if (warn) {
|
||||
klass += " warn";
|
||||
}
|
||||
|
||||
if (!homed && this.ask_home) {
|
||||
this.ask_home = false;
|
||||
SvelteComponents.showDialog("HomeMachine", {
|
||||
home: () => this.home()
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
homed: homed,
|
||||
klass: klass
|
||||
};
|
||||
}
|
||||
|
||||
var klass = homed ? 'homed' : 'unhomed';
|
||||
if (error) klass += ' error';
|
||||
else if (warn) klass += ' warn';
|
||||
|
||||
if(!homed && this.ask_home)
|
||||
{
|
||||
this.ask_home_msg = true;
|
||||
this.ask_home = false;
|
||||
}
|
||||
|
||||
return {
|
||||
homed: homed,
|
||||
klass: klass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,87 +1,77 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
function _msg_equal(a, b) {
|
||||
return a.level == b.level && a.source == b.source && a.where == b.where &&
|
||||
a.msg == b.msg;
|
||||
return a.level == b.level
|
||||
&& a.source == b.source
|
||||
&& a.where == b.where
|
||||
&& a.msg == b.msg;
|
||||
}
|
||||
|
||||
|
||||
// Shared among all instances
|
||||
var messages = [];
|
||||
|
||||
const messages = [];
|
||||
|
||||
module.exports = {
|
||||
template: '#console-template',
|
||||
template: "#console-template",
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
messages
|
||||
};
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {messages: messages}
|
||||
},
|
||||
events: {
|
||||
log: function(msg) {
|
||||
// There may be multiple instances of this module so ignore messages
|
||||
// that have already been processed.
|
||||
if (msg.logged) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.logged = true;
|
||||
|
||||
events: {
|
||||
log: function (msg) {
|
||||
// There may be multiple instances of this module so ignore messages
|
||||
// that have already been processed.
|
||||
if (msg.logged) return;
|
||||
msg.logged = true;
|
||||
// Make sure we have a message level
|
||||
msg.level = msg.level || "info";
|
||||
|
||||
// Make sure we have a message level
|
||||
msg.level = msg.level || 'info';
|
||||
// Add to message log and count and collapse repeats
|
||||
const repeat = messages.length && _msg_equal(msg, messages[0]);
|
||||
if (repeat) {
|
||||
messages[0].repeat++;
|
||||
} else {
|
||||
msg.repeat = msg.repeat || 1;
|
||||
messages.unshift(msg);
|
||||
while (256 < messages.length) {
|
||||
messages.pop();
|
||||
}
|
||||
}
|
||||
|
||||
// Add to message log and count and collapse repeats
|
||||
var repeat = messages.length && _msg_equal(msg, messages[0]);
|
||||
if (repeat) messages[0].repeat++;
|
||||
else {
|
||||
msg.repeat = msg.repeat || 1;
|
||||
messages.unshift(msg);
|
||||
while (256 < messages.length) messages.pop();
|
||||
}
|
||||
msg.ts = Date.now();
|
||||
if (messages[0].repeat > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write message to browser console for debugging
|
||||
var text = JSON.stringify(msg);
|
||||
if (msg.level == 'error' || msg.level == 'critical') console.error(text);
|
||||
else if (msg.level == 'warning') console.warn(text);
|
||||
else if (msg.level == 'debug' && console.debug) console.debug(text);
|
||||
else console.log(text);
|
||||
msg.ts = Date.now();
|
||||
|
||||
// Event on errors
|
||||
if (msg.level == 'error' || msg.level == 'critical')
|
||||
this.$dispatch('error', msg);
|
||||
// Write message to browser console for debugging
|
||||
const text = JSON.stringify(msg);
|
||||
if (msg.level == "error" || msg.level == "critical") {
|
||||
console.error(text);
|
||||
} else if (msg.level == "warning") {
|
||||
console.warn(text);
|
||||
} else if (msg.level == "debug" && console.debug) {
|
||||
console.debug(text);
|
||||
} else {
|
||||
console.log(text);
|
||||
}
|
||||
|
||||
// Event on errors
|
||||
if (msg.level == "error" || msg.level == "critical") {
|
||||
this.$dispatch("error", msg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
clear: function() {
|
||||
messages.splice(0, messages.length);
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
clear: function () {messages.splice(0, messages.length);},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
112
src/js/cookie.js
112
src/js/cookie.js
@@ -1,69 +1,49 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
Copyright 2018. Buildbotics LLC
|
||||
All Rights Reserved.
|
||||
|
||||
For information regarding this software email:
|
||||
Joseph Coffland
|
||||
joseph@buildbotics.com
|
||||
|
||||
This software is free software: you clan redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public License
|
||||
as published by the Free Software Foundation, either version 2.1 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This software is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the C! library. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
module.exports = function (prefix) {
|
||||
if (typeof prefix == 'undefined') prefix = '';
|
||||
|
||||
var cookie = {
|
||||
get: function (name, defaultValue) {
|
||||
var decodedCookie = decodeURIComponent(document.cookie);
|
||||
var ca = decodedCookie.split(';');
|
||||
name = prefix + name + '=';
|
||||
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1);
|
||||
if (!c.indexOf(name)) return c.substring(name.length, c.length);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
},
|
||||
|
||||
|
||||
set: function (name, value, days) {
|
||||
var offset = 2147483647; // Max value
|
||||
if (typeof days != 'undefined') offset = days * 24 * 60 * 60 * 1000;
|
||||
var d = new Date();
|
||||
d.setTime(d.getTime() + offset);
|
||||
var expires = 'expires=' + d.toUTCString();
|
||||
document.cookie = prefix + name + '=' + value + ';' + expires + ';path=/';
|
||||
},
|
||||
|
||||
|
||||
set_bool: function (name, value) {
|
||||
cookie.set(name, value ? 'true' : 'false');
|
||||
},
|
||||
|
||||
|
||||
get_bool: function (name, defaultValue) {
|
||||
return cookie.get(name, defaultValue ? 'true' : 'false') == 'true';
|
||||
module.exports = function(prefix) {
|
||||
if (typeof prefix == "undefined") {
|
||||
prefix = "";
|
||||
}
|
||||
}
|
||||
|
||||
return cookie;
|
||||
}
|
||||
const cookie = {
|
||||
get: function(name, defaultValue) {
|
||||
const decodedCookie = decodeURIComponent(document.cookie);
|
||||
const ca = decodedCookie.split(";");
|
||||
name = `${prefix + name}=`;
|
||||
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == " ") {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (!c.indexOf(name)) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
},
|
||||
|
||||
set: function(name, value, days) {
|
||||
let offset = 2147483647; // Max value
|
||||
if (typeof days != "undefined") {
|
||||
offset = days * 24 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + offset);
|
||||
const expires = `expires=${d.toUTCString()}`;
|
||||
document.cookie = `${prefix}${name}=${value};${expires};path=/`;
|
||||
},
|
||||
|
||||
set_bool: function(name, value) {
|
||||
cookie.set(name, value ? "true" : "false");
|
||||
},
|
||||
|
||||
get_bool: function(name, defaultValue) {
|
||||
return cookie.get(name, defaultValue ? "true" : "false") == "true";
|
||||
}
|
||||
};
|
||||
|
||||
return cookie;
|
||||
};
|
||||
|
||||
@@ -1,168 +1,152 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
var api = require('./api');
|
||||
|
||||
|
||||
var entityMap = {
|
||||
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
||||
'/': '/', '`': '`', '=': '='}
|
||||
"use strict";
|
||||
|
||||
const entityMap = {
|
||||
"&": "&", "<": "<", ">": ">", '"': """, "'": "'",
|
||||
"/": "/", "`": "`", "=": "=" };
|
||||
|
||||
function escapeHTML(s) {
|
||||
return s.replace(/[&<>"'`=\/]/g, function (c) {return entityMap[c]})
|
||||
return s.replace(/[&<>"'`=\\/]/g, function(c) {
|
||||
return entityMap[c];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
template: '#gcode-viewer-template',
|
||||
template: "#gcode-viewer-template",
|
||||
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
empty: true,
|
||||
file: '',
|
||||
line: -1,
|
||||
scrolling: false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
events: {
|
||||
'gcode-load': function (file) {this.load(file)},
|
||||
'gcode-clear': function () {this.clear()},
|
||||
'gcode-reload': function (file) {this.reload(file)},
|
||||
'gcode-line': function (line) {this.update_line(line)}
|
||||
},
|
||||
|
||||
|
||||
ready: function () {
|
||||
this.clusterize = new Clusterize({
|
||||
rows: [],
|
||||
scrollElem: $(this.$el).find('.clusterize-scroll')[0],
|
||||
contentElem: $(this.$el).find('.clusterize-content')[0],
|
||||
no_data_text: 'GCode view...',
|
||||
callbacks: {clusterChanged: this.highlight}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
attached: function () {
|
||||
if (typeof this.clusterize != 'undefined')
|
||||
this.clusterize.refresh(true);
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
load: async function(file) {
|
||||
if (file == this.file) return;
|
||||
this.clear();
|
||||
this.file = file;
|
||||
|
||||
if (!file) return;
|
||||
|
||||
const response = await fetch(`/api/file/${file}?${Math.random()}`);
|
||||
const text = await response.text();
|
||||
|
||||
if (text.length > 20e6) {
|
||||
this.clusterize.update(['File is large - gcode view disabled']);
|
||||
} else {
|
||||
const lines = escapeHTML(text.trimRight())
|
||||
.split(/[\r\n]/)
|
||||
.map((line, i) => `<li class="ln${i + 1}"><b>${i + 1}</b>${line}</li>`);
|
||||
|
||||
this.clusterize.update(lines);
|
||||
}
|
||||
|
||||
this.empty = false;
|
||||
|
||||
Vue.nextTick(this.update_line);
|
||||
data: function() {
|
||||
return {
|
||||
empty: true,
|
||||
file: "",
|
||||
line: -1
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
clear: function () {
|
||||
this.empty = true;
|
||||
this.file = '';
|
||||
this.line = -1;
|
||||
this.clusterize.clear();
|
||||
},
|
||||
|
||||
|
||||
reload: function (file) {
|
||||
if (file != this.file) return;
|
||||
this.clear();
|
||||
this.load(file);
|
||||
},
|
||||
|
||||
|
||||
highlight: function () {
|
||||
var e = $(this.$el).find('.highlight');
|
||||
if (e.length) e.removeClass('highlight');
|
||||
|
||||
e = $(this.$el).find('.ln' + this.line);
|
||||
if (e.length) e.addClass('highlight');
|
||||
},
|
||||
|
||||
|
||||
update_line: function(line) {
|
||||
if (typeof line != 'undefined') {
|
||||
if (this.line == line) return;
|
||||
this.line = line;
|
||||
|
||||
} else line = this.line;
|
||||
|
||||
var totalLines = this.clusterize.getRowsAmount();
|
||||
|
||||
if (line <= 0) line = 1;
|
||||
if (totalLines < line) line = totalLines;
|
||||
|
||||
var e = $(this.$el).find('.clusterize-scroll');
|
||||
|
||||
var lineHeight = e[0].scrollHeight / totalLines;
|
||||
var linesPerPage = Math.floor(e[0].clientHeight / lineHeight);
|
||||
var current = e[0].scrollTop / lineHeight;
|
||||
var target = line - 1 - Math.floor(linesPerPage / 2);
|
||||
|
||||
// Update scroll position
|
||||
if (!this.scrolling) {
|
||||
if (target < current - 20 || current + 20 < target)
|
||||
e[0].scrollTop = target * lineHeight;
|
||||
|
||||
else {
|
||||
this.scrolling = true;
|
||||
e.animate({scrollTop: target * lineHeight}, {
|
||||
complete: function () {this.scrolling = false}.bind(this)
|
||||
})
|
||||
events: {
|
||||
"gcode-load": function(file) {
|
||||
this.load(file);
|
||||
},
|
||||
"gcode-clear": function() {
|
||||
this.clear();
|
||||
},
|
||||
"gcode-reload": function(file) {
|
||||
this.reload(file);
|
||||
},
|
||||
"gcode-line": function(line) {
|
||||
this.update_line(line);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Vue.nextTick(this.highlight);
|
||||
ready: function() {
|
||||
this.clusterize = new Clusterize({
|
||||
rows: [],
|
||||
scrollElem: this.$el.querySelector(".clusterize-scroll"),
|
||||
contentElem: this.$el.querySelector(".clusterize-content"),
|
||||
no_data_text: "GCode view...",
|
||||
callbacks: { clusterChanged: this.highlight }
|
||||
});
|
||||
},
|
||||
|
||||
attached: function() {
|
||||
if (typeof this.clusterize != "undefined") {
|
||||
this.clusterize.refresh(true);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
load: async function(file) {
|
||||
if (file == this.file) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clear();
|
||||
this.file = file;
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/file/${file}`, { cache: "no-cache" });
|
||||
const text = await response.text();
|
||||
|
||||
if (text.length > 20e6) {
|
||||
this.clusterize.update([ "File is large - gcode view disabled" ]);
|
||||
} else {
|
||||
const lines = escapeHTML(text.trimRight())
|
||||
.split(/[\r\n]/)
|
||||
.map((line, i) => `<li class="ln${i + 1}"><b>${i + 1}</b>${line}</li>`);
|
||||
|
||||
this.clusterize.update(lines);
|
||||
}
|
||||
|
||||
this.empty = false;
|
||||
|
||||
Vue.nextTick(this.update_line);
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.empty = true;
|
||||
this.file = "";
|
||||
this.line = -1;
|
||||
this.clusterize.clear();
|
||||
},
|
||||
|
||||
reload: function(file) {
|
||||
if (file == this.file) {
|
||||
return;
|
||||
}
|
||||
this.clear();
|
||||
this.load(file);
|
||||
},
|
||||
|
||||
highlight: function() {
|
||||
const highlights = this.$el.querySelectorAll(".highlight");
|
||||
for (const highlight of highlights) {
|
||||
highlight.className = (highlight.className || "")
|
||||
.split(" ")
|
||||
.filter(c => c !== "highlight")
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
const lines = this.$el.querySelectorAll(`.ln${this.line}`);
|
||||
for (const line of lines) {
|
||||
line.className = (line.className || "")
|
||||
.split(" ")
|
||||
.filter(c => c !== "highlight")
|
||||
.concat([ "highlight" ])
|
||||
.join(" ");
|
||||
}
|
||||
},
|
||||
|
||||
update_line: function(line) {
|
||||
if (typeof line != "undefined") {
|
||||
if (this.line == line) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.line = line;
|
||||
} else {
|
||||
line = this.line;
|
||||
}
|
||||
|
||||
const totalLines = this.clusterize.getRowsAmount();
|
||||
|
||||
if (line <= 0) {
|
||||
line = 1;
|
||||
}
|
||||
|
||||
if (totalLines < line) {
|
||||
line = totalLines;
|
||||
}
|
||||
|
||||
const scroll = this.$el.querySelector(".clusterize-scroll");
|
||||
|
||||
const lineHeight = scroll.scrollHeight / totalLines;
|
||||
const linesPerPage = Math.floor(scroll.clientHeight / lineHeight);
|
||||
const target = line - 1 - Math.floor(linesPerPage / 2);
|
||||
|
||||
// Update scroll position
|
||||
scroll.scrollTop = target * lineHeight;
|
||||
|
||||
Vue.nextTick(this.highlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
14
src/js/help-view.js
Normal file
14
src/js/help-view.js
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
template: "#help-view-template",
|
||||
|
||||
attached: function() {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"HelpView",
|
||||
document.getElementById("help")
|
||||
);
|
||||
},
|
||||
|
||||
detached: function() {
|
||||
this.svelteComponent.$destroy();
|
||||
}
|
||||
};
|
||||
@@ -1,107 +1,94 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
var modbus = require('./modbus.js');
|
||||
"use strict";
|
||||
|
||||
const modbus = require("./modbus.js");
|
||||
|
||||
module.exports = {
|
||||
template: '#indicators-template',
|
||||
props: ['state'],
|
||||
template: "#indicators-template",
|
||||
props: [ "state" ],
|
||||
|
||||
computed: {
|
||||
modbus_status: function() {
|
||||
return modbus.status_to_string(this.state.mx);
|
||||
},
|
||||
|
||||
computed: {
|
||||
modbus_status: function () {return modbus.status_to_string(this.state.mx)},
|
||||
sense_error: function() {
|
||||
let error = "";
|
||||
|
||||
if (this.state.motor_voltage_sense_error) {
|
||||
error += "Motor voltage\n";
|
||||
}
|
||||
if (this.state.motor_current_sense_error) {
|
||||
error += "Motor current\n";
|
||||
}
|
||||
if (this.state.load1_sense_error) {
|
||||
error += "Load 1\n";
|
||||
}
|
||||
if (this.state.load2_sense_error) {
|
||||
error += "Load 2\n";
|
||||
}
|
||||
if (this.state.vdd_current_sense_error) {
|
||||
error += "Vdd current\n";
|
||||
}
|
||||
|
||||
sense_error: function () {
|
||||
var error = '';
|
||||
return error;
|
||||
}
|
||||
},
|
||||
|
||||
if (this.state.motor_voltage_sense_error) error += 'Motor voltage\n';
|
||||
if (this.state.motor_current_sense_error) error += 'Motor current\n';
|
||||
if (this.state.load1_sense_error) error += 'Load 1\n';
|
||||
if (this.state.load2_sense_error) error += 'Load 2\n';
|
||||
if (this.state.vdd_current_sense_error) error += 'Vdd current\n';
|
||||
methods: {
|
||||
is_motor_enabled: function(motor) {
|
||||
return typeof this.state[`${motor}me`] != "undefined" && this.state[`${motor}me`];
|
||||
},
|
||||
|
||||
return error;
|
||||
get_min_pin: function(motor) {
|
||||
switch (motor) {
|
||||
case 0: return 3;
|
||||
case 1: return 5;
|
||||
case 2: return 9;
|
||||
case 3: return 11;
|
||||
}
|
||||
},
|
||||
|
||||
get_max_pin: function(motor) {
|
||||
switch (motor) {
|
||||
case 0: return 4;
|
||||
case 1: return 8;
|
||||
case 2: return 10;
|
||||
case 3: return 12;
|
||||
}
|
||||
},
|
||||
|
||||
motor_fault_class: function(motor, bit) {
|
||||
if (typeof motor == "undefined") {
|
||||
const status = this.state["fa"];
|
||||
|
||||
if (typeof status == "undefined") {
|
||||
return "fa-question";
|
||||
}
|
||||
|
||||
return `fa-thumbs-${status ? "down error" : "up success"}`;
|
||||
}
|
||||
|
||||
const flags = this.state[`${motor}df`];
|
||||
|
||||
if (typeof flags == "undefined") {
|
||||
return "fa-question";
|
||||
}
|
||||
|
||||
return (flags & (1 << bit)) ? "fa-thumbs-down error" :
|
||||
"fa-thumbs-up success";
|
||||
},
|
||||
|
||||
motor_reset: function(motor) {
|
||||
if (typeof motor == "undefined") {
|
||||
let cmd = "";
|
||||
for (let i = 0; i < 4; i++) {
|
||||
cmd += `\\$${i}df=0\n`;
|
||||
}
|
||||
|
||||
this.$dispatch("send", cmd);
|
||||
} else {
|
||||
this.$dispatch("send", `\\$${motor}df=0`);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
is_motor_enabled: function (motor) {
|
||||
return typeof this.state[motor + 'me'] != 'undefined' &&
|
||||
this.state[motor + 'me'];
|
||||
},
|
||||
|
||||
|
||||
get_min_pin: function (motor) {
|
||||
switch (motor) {
|
||||
case 0: return 3;
|
||||
case 1: return 5;
|
||||
case 2: return 9;
|
||||
case 3: return 11;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
get_max_pin: function (motor) {
|
||||
switch (motor) {
|
||||
case 0: return 4;
|
||||
case 1: return 8;
|
||||
case 2: return 10;
|
||||
case 3: return 12;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
motor_fault_class: function (motor, bit) {
|
||||
if (typeof motor == 'undefined') {
|
||||
var status = this.state['fa'];
|
||||
if (typeof status == 'undefined') return 'fa-question';
|
||||
return 'fa-thumbs-' + (status ? 'down error' : 'up success')
|
||||
}
|
||||
|
||||
var flags = this.state[motor + 'df'];
|
||||
if (typeof flags == 'undefined') return 'fa-question';
|
||||
return (flags & (1 << bit)) ? 'fa-thumbs-down error' :
|
||||
'fa-thumbs-up success';
|
||||
},
|
||||
|
||||
|
||||
motor_reset: function (motor) {
|
||||
if (typeof motor == 'undefined') {
|
||||
var cmd = '';
|
||||
for (var i = 0; i < 4; i++)
|
||||
cmd += '\\$' + i + 'df=0\n';
|
||||
this.$dispatch('send', cmd);
|
||||
|
||||
} else this.$dispatch('send', '\\$' + motor + 'df=0');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,177 +1,155 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: "#io-indicator-template",
|
||||
props: ['name', 'state'],
|
||||
template: "#io-indicator-template",
|
||||
props: [ "name", "state" ],
|
||||
|
||||
computed: {
|
||||
klass: function() {
|
||||
switch (this.name) {
|
||||
case "min-switch-0": return this.get_motor_min_class(0);
|
||||
case "min-switch-1": return this.get_motor_min_class(1);
|
||||
case "min-switch-2": return this.get_motor_min_class(2);
|
||||
case "min-switch-3": return this.get_motor_min_class(3);
|
||||
case "max-switch-0": return this.get_motor_max_class(0);
|
||||
case "max-switch-1": return this.get_motor_max_class(1);
|
||||
case "max-switch-2": return this.get_motor_max_class(2);
|
||||
case "max-switch-3": return this.get_motor_max_class(3);
|
||||
case "estop": return this.get_input_class("ew", "et");
|
||||
case "probe": return this.get_input_class("pw", "pt");
|
||||
case "load-1": return this.get_output_class("1");
|
||||
case "load-2": return this.get_output_class("2");
|
||||
case "fault": return this.get_output_class("f");
|
||||
case "tool-enable-mode": return this.get_output_class("e");
|
||||
case "tool-direction-mode": return this.get_output_class("d");
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
klass: function () {
|
||||
if (this.name == 'min-switch-0') return this.get_motor_min_class(0);
|
||||
if (this.name == 'min-switch-1') return this.get_motor_min_class(1);
|
||||
if (this.name == 'min-switch-2') return this.get_motor_min_class(2);
|
||||
if (this.name == 'min-switch-3') return this.get_motor_min_class(3);
|
||||
if (this.name == 'max-switch-0') return this.get_motor_max_class(0);
|
||||
if (this.name == 'max-switch-1') return this.get_motor_max_class(1);
|
||||
if (this.name == 'max-switch-2') return this.get_motor_max_class(2);
|
||||
if (this.name == 'max-switch-3') return this.get_motor_max_class(3);
|
||||
if (this.name == 'estop') return this.get_input_class('ew', 'et');
|
||||
if (this.name == 'probe') return this.get_input_class('pw', 'pt');
|
||||
if (this.name == 'load-1') return this.get_output_class('1');
|
||||
if (this.name == 'load-2') return this.get_output_class('2');
|
||||
if (this.name == 'fault') return this.get_output_class('f');
|
||||
if (this.name == 'tool-enable-mode') return this.get_output_class('e');
|
||||
if (this.name == 'tool-direction-mode') return this.get_output_class('d');
|
||||
tooltip: function() {
|
||||
switch (this.name) {
|
||||
case "min-switch-0": return this.get_motor_min_tooltip(0);
|
||||
case "min-switch-1": return this.get_motor_min_tooltip(1);
|
||||
case "min-switch-2": return this.get_motor_min_tooltip(2);
|
||||
case "min-switch-3": return this.get_motor_min_tooltip(3);
|
||||
case "max-switch-0": return this.get_motor_max_tooltip(0);
|
||||
case "max-switch-1": return this.get_motor_max_tooltip(1);
|
||||
case "max-switch-2": return this.get_motor_max_tooltip(2);
|
||||
case "max-switch-3": return this.get_motor_max_tooltip(3);
|
||||
case "estop": return this.get_input_tooltip("ew", "et");
|
||||
case "probe": return this.get_input_tooltip("pw", "pt");
|
||||
case "load-1": return this.get_output_tooltip("1");
|
||||
case "load-2": return this.get_output_tooltip("2");
|
||||
case "fault": return this.get_output_tooltip("f");
|
||||
case "tool-direction-mode": return this.get_output_tooltip("d");
|
||||
case "tool-enable-mode": return this.get_output_tooltip("e");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
get_io_state_class: function(active, state) {
|
||||
if (typeof active == "undefined" || typeof state == "undefined") {
|
||||
return "fa-exclamation-triangle warn";
|
||||
}
|
||||
|
||||
tooltip: function () {
|
||||
if (this.name == 'min-switch-0') return this.get_motor_min_tooltip(0);
|
||||
if (this.name == 'min-switch-1') return this.get_motor_min_tooltip(1);
|
||||
if (this.name == 'min-switch-2') return this.get_motor_min_tooltip(2);
|
||||
if (this.name == 'min-switch-3') return this.get_motor_min_tooltip(3);
|
||||
if (this.name == 'max-switch-0') return this.get_motor_max_tooltip(0);
|
||||
if (this.name == 'max-switch-1') return this.get_motor_max_tooltip(1);
|
||||
if (this.name == 'max-switch-2') return this.get_motor_max_tooltip(2);
|
||||
if (this.name == 'max-switch-3') return this.get_motor_max_tooltip(3);
|
||||
if (this.name == 'estop') return this.get_input_tooltip('ew', 'et');
|
||||
if (this.name == 'probe') return this.get_input_tooltip('pw', 'pt');
|
||||
if (this.name == 'load-1') return this.get_output_tooltip('1');
|
||||
if (this.name == 'load-2') return this.get_output_tooltip('2');
|
||||
if (this.name == 'fault') return this.get_output_tooltip('f');
|
||||
if (this.name == 'tool-direction-mode')
|
||||
return this.get_output_tooltip('d');
|
||||
if (this.name == 'tool-enable-mode')
|
||||
return this.get_output_tooltip('e');
|
||||
if (state == 2) {
|
||||
return "fa-circle-o";
|
||||
}
|
||||
|
||||
const icon = state ? "fa-plus-circle" : "fa-minus-circle";
|
||||
return `${icon} ${active ? "active" : "inactive"}`;
|
||||
},
|
||||
|
||||
get_input_active: function(stateCode, typeCode) {
|
||||
const type = this.state[typeCode];
|
||||
const state = this.state[stateCode];
|
||||
|
||||
if (type == 1) {
|
||||
return !state; // Normally open
|
||||
} else if (type == 2) {
|
||||
return state; // Normally closed
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
get_input_class: function(stateCode, typeCode) {
|
||||
return this.get_io_state_class(this.get_input_active(stateCode, typeCode), this.state[stateCode]);
|
||||
},
|
||||
|
||||
get_output_class: function(output) {
|
||||
return this.get_io_state_class(this.state[`${output}oa`], this.state[`${output}os`]);
|
||||
},
|
||||
|
||||
get_motor_min_class: function(motor) {
|
||||
return this.get_input_class(`${motor}lw`, `${motor}ls`);
|
||||
},
|
||||
|
||||
get_motor_max_class: function(motor) {
|
||||
return this.get_input_class(`${motor}xw`, `${motor}xs`);
|
||||
},
|
||||
|
||||
get_tooltip: function(mode, active, state) {
|
||||
if (typeof mode == "undefined" || typeof active == "undefined" || typeof state == "undefined") {
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
if (state == 0) {
|
||||
state = "Lo/Gnd";
|
||||
} else if (state == 1) {
|
||||
state = "Hi/+3.3v";
|
||||
} else if (state == 2) {
|
||||
state = "Tristated";
|
||||
} else {
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
return `Mode: ${mode}\nActive: ${active ? "True" : "False"}\nLevel: ${state}`;
|
||||
},
|
||||
|
||||
get_input_tooltip: function(stateCode, typeCode) {
|
||||
let type = this.state[typeCode];
|
||||
if (type == 0) {
|
||||
return "Disabled";
|
||||
} else if (type == 1) {
|
||||
type = "Normally open";
|
||||
} else if (type == 2) {
|
||||
type = "Normally closed";
|
||||
}
|
||||
|
||||
const active = this.get_input_active(stateCode, typeCode);
|
||||
const state = this.state[stateCode];
|
||||
|
||||
return this.get_tooltip(type, active, state);
|
||||
},
|
||||
|
||||
get_output_tooltip: function(output) {
|
||||
let mode = this.state[`${output}om`];
|
||||
|
||||
switch (mode) {
|
||||
case 0: return "Disabled";
|
||||
case 1: mode = "Lo/Hi"; break;
|
||||
case 2: mode = "Hi/Lo"; break;
|
||||
case 3: mode = "Tri/Lo"; break;
|
||||
case 4: mode = "Tri/Hi"; break;
|
||||
case 5: mode = "Lo/Tri"; break;
|
||||
case 6: mode = "Hi/Tri"; break;
|
||||
default:
|
||||
mode = undefined;
|
||||
}
|
||||
|
||||
const active = this.state[`${output}oa`];
|
||||
const state = this.state[`${output}os`];
|
||||
|
||||
return this.get_tooltip(mode, active, state);
|
||||
},
|
||||
|
||||
get_motor_min_tooltip: function(motor) {
|
||||
return this.get_input_tooltip(`${motor}lw`, `${motor}ls`);
|
||||
},
|
||||
|
||||
get_motor_max_tooltip: function(motor) {
|
||||
return this.get_input_tooltip(`${motor}xw`, `${motor}xs`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
get_io_state_class: function (active, state) {
|
||||
if (typeof active == 'undefined' || typeof state == 'undefined')
|
||||
return 'fa-exclamation-triangle warn';
|
||||
|
||||
if (state == 2) return 'fa-circle-o';
|
||||
|
||||
return (state ? 'fa-plus-circle' : 'fa-minus-circle') + ' ' +
|
||||
(active ? 'active' : 'inactive');
|
||||
},
|
||||
|
||||
|
||||
get_input_active: function (stateCode, typeCode) {
|
||||
var type = this.state[typeCode];
|
||||
var state = this.state[stateCode];
|
||||
|
||||
if (type == 1) return !state; // Normally open
|
||||
else if (type == 2) return state; // Normally closed
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
|
||||
get_input_class: function (stateCode, typeCode) {
|
||||
return this.get_io_state_class(this.get_input_active(stateCode, typeCode),
|
||||
this.state[stateCode]);
|
||||
},
|
||||
|
||||
|
||||
get_output_class: function (output) {
|
||||
return this.get_io_state_class(this.state[output + 'oa'],
|
||||
this.state[output + 'os']);
|
||||
},
|
||||
|
||||
|
||||
get_motor_min_class: function (motor) {
|
||||
return this.get_input_class(motor + 'lw', motor + 'ls');
|
||||
},
|
||||
|
||||
|
||||
get_motor_max_class: function (motor) {
|
||||
return this.get_input_class(motor + 'xw', motor + 'xs');
|
||||
},
|
||||
|
||||
|
||||
get_tooltip: function (mode, active, state) {
|
||||
if (typeof mode == 'undefined' || typeof active == 'undefined' ||
|
||||
typeof state == 'undefined') return 'Invalid';
|
||||
|
||||
if (state == 0) state = 'Lo/Gnd';
|
||||
else if (state == 1) state = 'Hi/+3.3v';
|
||||
else if (state == 2) state = 'Tristated';
|
||||
else return 'Invalid';
|
||||
|
||||
return 'Mode: ' + mode + '\nActive: ' + (active ? 'True' : 'False') +
|
||||
'\nLevel: ' + state;
|
||||
},
|
||||
|
||||
|
||||
get_input_tooltip: function (stateCode, typeCode) {
|
||||
var type = this.state[typeCode];
|
||||
if (type == 0) return 'Disabled';
|
||||
else if (type == 1) type = 'Normally open';
|
||||
else if (type == 2) type = 'Normally closed';
|
||||
|
||||
var active = this.get_input_active(stateCode, typeCode);
|
||||
var state = this.state[stateCode];
|
||||
|
||||
return this.get_tooltip(type, active, state);
|
||||
},
|
||||
|
||||
|
||||
get_output_tooltip: function (output) {
|
||||
var mode = this.state[output + 'om'];
|
||||
if (mode == 0) return 'Disabled';
|
||||
else if (mode == 1) mode = 'Lo/Hi';
|
||||
else if (mode == 2) mode = 'Hi/Lo';
|
||||
else if (mode == 3) mode = 'Tri/Lo';
|
||||
else if (mode == 4) mode = 'Tri/Hi';
|
||||
else if (mode == 5) mode = 'Lo/Tri';
|
||||
else if (mode == 6) mode = 'Hi/Tri';
|
||||
else mode = undefined;
|
||||
|
||||
var active = this.state[output + 'oa'];
|
||||
var state = this.state[output + 'os'];
|
||||
|
||||
return this.get_tooltip(mode, active, state);
|
||||
},
|
||||
|
||||
|
||||
get_motor_min_tooltip: function (motor) {
|
||||
return this.get_input_tooltip(motor + 'lw', motor + 'ls');
|
||||
},
|
||||
|
||||
|
||||
get_motor_max_tooltip: function (motor) {
|
||||
return this.get_input_tooltip(motor + 'xw', motor + 'xs');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,42 +1,13 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#io-view-template',
|
||||
props: ['config', 'template', 'state'],
|
||||
template: "#io-view-template",
|
||||
props: [ "config", "template", "state" ],
|
||||
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
this.$dispatch('config-changed');
|
||||
return false;
|
||||
events: {
|
||||
"input-changed": function() {
|
||||
this.$dispatch("config-changed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
251
src/js/main.js
251
src/js/main.js
@@ -1,147 +1,148 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict';
|
||||
|
||||
"use strict";
|
||||
|
||||
function cookie_get(name) {
|
||||
var decodedCookie = decodeURIComponent(document.cookie);
|
||||
var ca = decodedCookie.split(';');
|
||||
name = name + '=';
|
||||
const decodedCookie = decodeURIComponent(document.cookie);
|
||||
const ca = decodedCookie.split(";");
|
||||
name = `${name}=`;
|
||||
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1);
|
||||
if (!c.indexOf(name)) return c.substring(name.length, c.length);
|
||||
}
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == " ") {
|
||||
c = c.substring(1);
|
||||
}
|
||||
|
||||
if (!c.indexOf(name)) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cookie_set(name, value, days) {
|
||||
var d = new Date();
|
||||
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
|
||||
var expires = 'expires=' + d.toUTCString();
|
||||
document.cookie = name + '=' + value + ';' + expires + ';path=/';
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
|
||||
const expires = `expires=${d.toUTCString()}`;
|
||||
document.cookie = `${name}=${value};${expires};path=/`;
|
||||
}
|
||||
|
||||
|
||||
var uuid_chars =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+';
|
||||
|
||||
const uuid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+";
|
||||
|
||||
function uuid(length) {
|
||||
if (typeof length == 'undefined') length = 52;
|
||||
if (typeof length == "undefined") {
|
||||
length = 52;
|
||||
}
|
||||
|
||||
var s = '';
|
||||
for (var i = 0; i < length; i++)
|
||||
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
|
||||
let s = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
|
||||
}
|
||||
|
||||
return s
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
$(function() {
|
||||
if (typeof cookie_get('client-id') == 'undefined')
|
||||
cookie_set('client-id', uuid(), 10000);
|
||||
|
||||
// Vue debugging
|
||||
Vue.config.debug = true;
|
||||
//Vue.util.warn = function (msg) {console.debug('[Vue warn]: ' + msg)}
|
||||
|
||||
// Register global components
|
||||
Vue.component('templated-input', require('./templated-input'));
|
||||
Vue.component('message', require('./message'));
|
||||
Vue.component('indicators', require('./indicators'));
|
||||
Vue.component('io-indicator', require('./io-indicator'));
|
||||
Vue.component('console', require('./console'));
|
||||
Vue.component('unit-value', require('./unit-value'));
|
||||
|
||||
Vue.filter('number', function (value) {
|
||||
if (isNaN(value)) return 'NaN';
|
||||
return value.toLocaleString();
|
||||
});
|
||||
|
||||
Vue.filter('percent', function (value, precision) {
|
||||
if (typeof value == 'undefined') return '';
|
||||
if (typeof precision == 'undefined') precision = 2;
|
||||
return (value * 100.0).toFixed(precision) + '%';
|
||||
});
|
||||
|
||||
Vue.filter('non_zero_percent', function (value, precision) {
|
||||
if (!value) return '';
|
||||
if (typeof precision == 'undefined') precision = 2;
|
||||
return (value * 100.0).toFixed(precision) + '%';
|
||||
});
|
||||
|
||||
Vue.filter('fixed', function (value, precision) {
|
||||
if (typeof value == 'undefined') return '0';
|
||||
return parseFloat(value).toFixed(precision)
|
||||
});
|
||||
|
||||
Vue.filter('upper', function (value) {
|
||||
if (typeof value == 'undefined') return '';
|
||||
return value.toUpperCase()
|
||||
});
|
||||
|
||||
Vue.filter('time', function (value, precision) {
|
||||
if (isNaN(value)) return '';
|
||||
if (isNaN(precision)) precision = 0;
|
||||
|
||||
var MIN = 60;
|
||||
var HR = MIN * 60;
|
||||
var DAY = HR * 24;
|
||||
var parts = [];
|
||||
|
||||
if (DAY <= value) {
|
||||
parts.push(Math.floor(value / DAY));
|
||||
value %= DAY;
|
||||
window.onload = function() {
|
||||
if (typeof cookie_get("client-id") == "undefined") {
|
||||
cookie_set("client-id", uuid(), 10000);
|
||||
}
|
||||
|
||||
if (HR <= value) {
|
||||
parts.push(Math.floor(value / HR));
|
||||
value %= HR;
|
||||
}
|
||||
// Register global components
|
||||
Vue.component("templated-input", require("./templated-input"));
|
||||
Vue.component("message", require("./message"));
|
||||
Vue.component("indicators", require("./indicators"));
|
||||
Vue.component("io-indicator", require("./io-indicator"));
|
||||
Vue.component("console", require("./console"));
|
||||
Vue.component("unit-value", require("./unit-value"));
|
||||
|
||||
if (MIN <= value) {
|
||||
parts.push(Math.floor(value / MIN));
|
||||
value %= MIN;
|
||||
Vue.filter("number", function(value) {
|
||||
if (isNaN(value)) {
|
||||
return "NaN";
|
||||
}
|
||||
|
||||
} else parts.push(0);
|
||||
return value.toLocaleString();
|
||||
});
|
||||
|
||||
parts.push(value);
|
||||
Vue.filter("percent", function(value, precision) {
|
||||
if (typeof value == "undefined") {
|
||||
return "";
|
||||
}
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
|
||||
if (i && parts[i] < 10) parts[i] = '0' + parts[i];
|
||||
}
|
||||
if (typeof precision == "undefined") {
|
||||
precision = 2;
|
||||
}
|
||||
|
||||
return parts.join(':');
|
||||
});
|
||||
return `${(value * 100.0).toFixed(precision)}%`;
|
||||
});
|
||||
|
||||
// Vue app
|
||||
require('./app');
|
||||
});
|
||||
Vue.filter("non_zero_percent", function(value, precision) {
|
||||
if (!value) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (typeof precision == "undefined") {
|
||||
precision = 2;
|
||||
}
|
||||
|
||||
return `${(value * 100.0).toFixed(precision)}%`;
|
||||
});
|
||||
|
||||
Vue.filter("fixed", function(value, precision) {
|
||||
if (typeof value == "undefined") {
|
||||
return "0";
|
||||
}
|
||||
|
||||
return parseFloat(value).toFixed(precision);
|
||||
});
|
||||
|
||||
Vue.filter("upper", function(value) {
|
||||
if (typeof value == "undefined") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return value.toUpperCase();
|
||||
});
|
||||
|
||||
Vue.filter("time", function(value, precision) {
|
||||
if (isNaN(value)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (isNaN(precision)) {
|
||||
precision = 0;
|
||||
}
|
||||
|
||||
const MIN = 60;
|
||||
const HR = MIN * 60;
|
||||
const DAY = HR * 24;
|
||||
const parts = [];
|
||||
|
||||
if (DAY <= value) {
|
||||
parts.push(Math.floor(value / DAY));
|
||||
value %= DAY;
|
||||
}
|
||||
|
||||
if (HR <= value) {
|
||||
parts.push(Math.floor(value / HR));
|
||||
value %= HR;
|
||||
}
|
||||
|
||||
if (MIN <= value) {
|
||||
parts.push(Math.floor(value / MIN));
|
||||
value %= MIN;
|
||||
} else {
|
||||
parts.push(0);
|
||||
}
|
||||
|
||||
parts.push(value);
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
|
||||
if (i && parts[i] < 10) {
|
||||
parts[i] = `0${parts[i]}`;
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join(":");
|
||||
});
|
||||
|
||||
// Vue app
|
||||
require("./app");
|
||||
};
|
||||
|
||||
@@ -1,47 +1,19 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#message-template',
|
||||
template: "#message-template",
|
||||
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
twoWay: true
|
||||
},
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
twoWay: true
|
||||
},
|
||||
|
||||
class: {
|
||||
type: String,
|
||||
required: false,
|
||||
twoWay: false
|
||||
class: {
|
||||
type: String,
|
||||
required: false,
|
||||
twoWay: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,48 +1,20 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '#modbus-reg-view-template',
|
||||
props: ['index', 'model', 'template', 'enable'],
|
||||
replace: true,
|
||||
template: "#modbus-reg-view-template",
|
||||
props: [ "index", "model", "template", "enable" ],
|
||||
|
||||
computed: {
|
||||
has_user_value: function() {
|
||||
const type = this.model["reg-type"];
|
||||
return type.includes("write") || type.includes("fixed") || type.includes("scaled");
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
has_user_value: function () {
|
||||
var type = this.model['reg-type'];
|
||||
return type.indexOf('write') != -1 || type.indexOf('fixed') != -1;
|
||||
methods: {
|
||||
change: function() {
|
||||
this.$dispatch("input-changed");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
change: function () {this.$dispatch('input-changed')}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,51 +1,23 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
// Must match modbus.c
|
||||
var exports = {
|
||||
DISCONNECTED: 0,
|
||||
OK: 1,
|
||||
CRC: 2,
|
||||
INVALID: 3,
|
||||
TIMEDOUT: 4
|
||||
const constants = {
|
||||
DISCONNECTED: 0,
|
||||
OK: 1,
|
||||
CRC: 2,
|
||||
INVALID: 3,
|
||||
TIMEDOUT: 4
|
||||
};
|
||||
|
||||
|
||||
exports.status_to_string =
|
||||
function (status) {
|
||||
if (status == exports.OK) return 'Ok';
|
||||
if (status == exports.CRC) return 'CRC error';
|
||||
if (status == exports.INVALID) return 'Invalid response';
|
||||
if (status == exports.TIMEDOUT) return 'Timedout';
|
||||
return 'Disconnected';
|
||||
}
|
||||
|
||||
|
||||
module.exports = exports;
|
||||
module.exports = {
|
||||
...constants,
|
||||
status_to_string: function(status) {
|
||||
switch (status) {
|
||||
case constants.OK: return "Ok";
|
||||
case constants.CRC: return "CRC error";
|
||||
case constants.INVALID: return "Invalid response";
|
||||
case constants.TIMEDOUT: return "Timedout";
|
||||
default: return "Disconnected";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,136 +1,120 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#motor-view-template',
|
||||
props: ['index', 'config', 'template', 'state'],
|
||||
template: "#motor-view-template",
|
||||
props: [ "index", "config", "template", "state" ],
|
||||
|
||||
computed: {
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
computed: {
|
||||
metric: function () {return this.$root.metric()},
|
||||
is_slave: function() {
|
||||
for (let i = 0; i < this.index; i++) {
|
||||
if (this.motor.axis == this.config.motors[i].axis) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
is_slave: function () {
|
||||
for (var i = 0; i < this.index; i++)
|
||||
if (this.motor.axis == this.config.motors[i].axis)
|
||||
return true;
|
||||
motor: function() {
|
||||
return this.config.motors[this.index];
|
||||
},
|
||||
|
||||
return false;
|
||||
invalidMaxVelocity: function() {
|
||||
return this.maxMaxVelocity < this.motor["max-velocity"];
|
||||
},
|
||||
|
||||
maxMaxVelocity: function() {
|
||||
return 1 * (15 * this.umPerStep / this.motor["microsteps"]).toFixed(3);
|
||||
},
|
||||
|
||||
ustepPerSec: function() {
|
||||
return this.rpm * this.stepsPerRev * this.motor["microsteps"] / 60;
|
||||
},
|
||||
|
||||
rpm: function() {
|
||||
return 1000 * this.motor["max-velocity"] / this.motor["travel-per-rev"];
|
||||
},
|
||||
|
||||
gForce: function() {
|
||||
return this.motor["max-accel"] * 0.0283254504;
|
||||
},
|
||||
|
||||
gForcePerMin: function() {
|
||||
return this.motor["max-jerk"] * 0.0283254504;
|
||||
},
|
||||
|
||||
stepsPerRev: function() {
|
||||
return 360 / this.motor["step-angle"];
|
||||
},
|
||||
|
||||
umPerStep: function() {
|
||||
return this.motor["travel-per-rev"] * this.motor["step-angle"] / 0.36;
|
||||
},
|
||||
|
||||
milPerStep: function() {
|
||||
return this.umPerStep / 25.4;
|
||||
},
|
||||
|
||||
invalidStallVelocity: function() {
|
||||
if (!this.motor["homing-mode"].startsWith("stall-")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.maxStallVelocity < this.motor["search-velocity"];
|
||||
},
|
||||
|
||||
stallRPM: function() {
|
||||
const v = this.motor["search-velocity"];
|
||||
return 1000 * v / this.motor["travel-per-rev"];
|
||||
},
|
||||
|
||||
maxStallVelocity: function() {
|
||||
const maxRate = 900000 / this.motor["stall-sample-time"];
|
||||
const ustep = this.motor["stall-microstep"];
|
||||
const angle = this.motor["step-angle"];
|
||||
const travel = this.motor["travel-per-rev"];
|
||||
const maxStall = maxRate * 60 / 360 / 1000 * angle / ustep * travel;
|
||||
|
||||
return 1 * maxStall.toFixed(3);
|
||||
},
|
||||
|
||||
stallUStepPerSec: function() {
|
||||
const ustep = this.motor["stall-microstep"];
|
||||
return this.stallRPM * this.stepsPerRev * ustep / 60;
|
||||
}
|
||||
},
|
||||
|
||||
events: {
|
||||
"input-changed": function() {
|
||||
Vue.nextTick(function() {
|
||||
// Limit max-velocity
|
||||
if (this.invalidMaxVelocity) {
|
||||
this.$set('motor["max-velocity"]', this.maxMaxVelocity);
|
||||
}
|
||||
|
||||
motor: function () {return this.config.motors[this.index]},
|
||||
//Limit stall-velocity
|
||||
if (this.invalidStallVelocity) {
|
||||
this.$set('motor["search-velocity"]', this.maxStallVelocity);
|
||||
}
|
||||
|
||||
this.$dispatch("config-changed");
|
||||
}.bind(this));
|
||||
|
||||
invalidMaxVelocity: function () {
|
||||
return this.maxMaxVelocity < this.motor['max-velocity'];
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
show: function(name, templ) {
|
||||
if (templ.hmodes == undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
maxMaxVelocity: function () {
|
||||
return 1 * (15 * this.umPerStep / this.motor['microsteps']).toFixed(3);
|
||||
},
|
||||
|
||||
|
||||
ustepPerSec: function () {
|
||||
return this.rpm * this.stepsPerRev * this.motor['microsteps'] / 60;
|
||||
},
|
||||
|
||||
|
||||
rpm: function () {
|
||||
return 1000 * this.motor['max-velocity'] / this.motor['travel-per-rev'];
|
||||
},
|
||||
|
||||
|
||||
gForce: function () {return this.motor['max-accel'] * 0.0283254504},
|
||||
gForcePerMin: function () {return this.motor['max-jerk'] * 0.0283254504},
|
||||
stepsPerRev: function () {return 360 / this.motor['step-angle']},
|
||||
|
||||
|
||||
umPerStep: function () {
|
||||
return this.motor['travel-per-rev'] * this.motor['step-angle'] / 0.36
|
||||
},
|
||||
|
||||
|
||||
milPerStep: function () {return this.umPerStep / 25.4},
|
||||
|
||||
invalidStallVelocity: function() {
|
||||
if(!this.motor['homing-mode'].startsWith('stall-')) return false;
|
||||
return this.maxStallVelocity < this.motor['search-velocity'];
|
||||
},
|
||||
|
||||
stallRPM: function() {
|
||||
var v = this.motor['search-velocity'];
|
||||
return 1000 * v / this.motor['travel-per-rev'];
|
||||
},
|
||||
|
||||
maxStallVelocity: function() {
|
||||
var maxRate = 900000/this.motor['stall-sample-time'];
|
||||
var ustep = this.motor['stall-microstep'];
|
||||
var angle = this.motor['step-angle'];
|
||||
var travel = this.motor['travel-per-rev'];
|
||||
var maxStall = maxRate * 60/ 360 /1000 * angle/ ustep * travel;
|
||||
|
||||
return 1 * maxStall.toFixed(3);
|
||||
},
|
||||
|
||||
stallUStepPerSec: function() {
|
||||
var ustep = this.motor['stall-microstep'];
|
||||
return this.stallRPM * this.stepsPerRev * ustep / 60;
|
||||
return templ.hmodes.indexOf(this.motor["homing-mode"]) != -1;
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
Vue.nextTick(function () {
|
||||
// Limit max-velocity
|
||||
if (this.invalidMaxVelocity)
|
||||
this.$set('motor["max-velocity"]', this.maxMaxVelocity);
|
||||
|
||||
//Limit stall-velocity
|
||||
if(this.invalidStallVelocity)
|
||||
this.$set('motor["search-velocity"]', this.maxStallVelocity);
|
||||
|
||||
this.$dispatch('config-changed');
|
||||
}.bind(this))
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
show: function(name, templ) {
|
||||
if(templ.hmodes == undefined) return true;
|
||||
return templ.hmodes.indexOf(this.motor['homing-mode']) != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
1191
src/js/orbit.js
1191
src/js/orbit.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,42 +1,14 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
module.exports = {
|
||||
template: '#settings-view-template',
|
||||
props: ['config', 'template'],
|
||||
template: "#settings-view-template",
|
||||
|
||||
attached: function() {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"SettingsView",
|
||||
document.getElementById("settings")
|
||||
);
|
||||
},
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
this.$dispatch('config-changed');
|
||||
return false;
|
||||
detached: function() {
|
||||
this.svelteComponent.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
189
src/js/sock.js
189
src/js/sock.js
@@ -1,127 +1,106 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
const Sock = function(url, retry, timeout) {
|
||||
if (!(this instanceof Sock)) {
|
||||
return new Sock(url, retry);
|
||||
}
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
if (typeof retry == "undefined") {
|
||||
retry = 2000;
|
||||
}
|
||||
if (typeof timeout == "undefined") {
|
||||
timeout = 16000;
|
||||
}
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
this.url = url;
|
||||
this.retry = retry;
|
||||
this.timeout = timeout;
|
||||
this.divisions = 4;
|
||||
this.count = 0;
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
this.connect();
|
||||
};
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
Sock.prototype.onmessage = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
Sock.prototype.onopen = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
\******************************************************************************/
|
||||
Sock.prototype.onclose = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
'use strict'
|
||||
Sock.prototype.connect = function() {
|
||||
console.debug("connecting to", this.url);
|
||||
this.close();
|
||||
|
||||
this._sock = new SockJS(this.url);
|
||||
|
||||
var Sock = function (url, retry, timeout) {
|
||||
if (!(this instanceof Sock)) return new Sock(url, retry);
|
||||
this._sock.onmessage = function(e) {
|
||||
console.debug("msg:", e.data);
|
||||
this.heartbeat("msg");
|
||||
this.onmessage(e);
|
||||
}.bind(this);
|
||||
|
||||
if (typeof retry == 'undefined') retry = 2000;
|
||||
if (typeof timeout == 'undefined') timeout = 16000;
|
||||
this._sock.onopen = function() {
|
||||
console.debug("connected");
|
||||
this.heartbeat("open");
|
||||
this.onopen();
|
||||
}.bind(this);
|
||||
|
||||
this.url = url;
|
||||
this.retry = retry;
|
||||
this.timeout = timeout;
|
||||
this.divisions = 4;
|
||||
this.count = 0;
|
||||
this._sock.onclose = function() {
|
||||
console.debug("disconnected");
|
||||
this._cancel_timeout();
|
||||
|
||||
this.connect();
|
||||
}
|
||||
this.onclose();
|
||||
if (typeof this._sock != "undefined") {
|
||||
setTimeout(this.connect.bind(this), this.retry);
|
||||
}
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
Sock.prototype._timedout = function() {
|
||||
// Divide timeout so slow browser doesn't trigger timeouts when the
|
||||
// connection is good.
|
||||
if (this.divisions <= ++this.count) {
|
||||
console.debug("connection timedout");
|
||||
this._timeout = undefined;
|
||||
this._sock.close();
|
||||
|
||||
Sock.prototype.onmessage = function () {}
|
||||
Sock.prototype.onopen = function () {}
|
||||
Sock.prototype.onclose = function () {}
|
||||
} else {
|
||||
this._set_timeout();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Sock.prototype.connect = function () {
|
||||
console.debug('connecting to', this.url);
|
||||
this.close();
|
||||
|
||||
this._sock = new SockJS(this.url);
|
||||
|
||||
this._sock.onmessage = function (e) {
|
||||
console.debug('msg:', e.data);
|
||||
this.heartbeat('msg');
|
||||
this.onmessage(e);
|
||||
}.bind(this);
|
||||
|
||||
|
||||
this._sock.onopen = function () {
|
||||
console.debug('connected');
|
||||
this.heartbeat('open');
|
||||
this.onopen();
|
||||
}.bind(this);
|
||||
|
||||
|
||||
this._sock.onclose = function () {
|
||||
console.debug('disconnected');
|
||||
this._cancel_timeout();
|
||||
|
||||
this.onclose();
|
||||
if (typeof this._sock != 'undefined')
|
||||
setTimeout(this.connect.bind(this), this.retry);
|
||||
}.bind(this);
|
||||
}
|
||||
|
||||
|
||||
Sock.prototype._timedout = function () {
|
||||
// Divide timeout so slow browser doesn't trigger timeouts when the
|
||||
// connection is good.
|
||||
if (this.divisions <= ++this.count) {
|
||||
console.debug('connection timedout');
|
||||
Sock.prototype._cancel_timeout = function() {
|
||||
clearTimeout(this._timeout);
|
||||
this._timeout = undefined;
|
||||
this._sock.close();
|
||||
this.count = 0;
|
||||
};
|
||||
|
||||
} else this._set_timeout();
|
||||
}
|
||||
Sock.prototype._set_timeout = function() {
|
||||
this._timeout = setTimeout(this._timedout.bind(this),
|
||||
this.timeout / this.divisions);
|
||||
};
|
||||
|
||||
Sock.prototype.heartbeat = function() {
|
||||
this._cancel_timeout();
|
||||
this._set_timeout();
|
||||
};
|
||||
|
||||
Sock.prototype._cancel_timeout = function () {
|
||||
clearTimeout(this._timeout);
|
||||
this._timeout = undefined;
|
||||
this.count = 0;
|
||||
}
|
||||
Sock.prototype.close = function() {
|
||||
if (typeof this._sock != "undefined") {
|
||||
const sock = this._sock;
|
||||
this._sock = undefined;
|
||||
sock.close();
|
||||
}
|
||||
};
|
||||
|
||||
Sock.prototype.send = function(msg) {
|
||||
this._sock.send(msg);
|
||||
};
|
||||
|
||||
Sock.prototype._set_timeout = function () {
|
||||
this._timeout = setTimeout(this._timedout.bind(this),
|
||||
this.timeout / this.divisions);
|
||||
}
|
||||
|
||||
|
||||
Sock.prototype.heartbeat = function (msg) {
|
||||
//console.debug('heartbeat ' + new Date().toLocaleTimeString() + ' ' + msg);
|
||||
this._cancel_timeout();
|
||||
this._set_timeout();
|
||||
}
|
||||
|
||||
|
||||
Sock.prototype.close = function () {
|
||||
if (typeof this._sock != 'undefined') {
|
||||
var sock = this._sock;
|
||||
this._sock = undefined;
|
||||
sock.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Sock.prototype.send = function (msg) {this._sock.send(msg)}
|
||||
|
||||
|
||||
module.exports = Sock
|
||||
module.exports = Sock;
|
||||
|
||||
@@ -1,89 +1,69 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '#templated-input-template',
|
||||
props: ['name', 'model', 'template'],
|
||||
replace: true,
|
||||
template: "#templated-input-template",
|
||||
props: [ "name", "model", "template" ],
|
||||
|
||||
|
||||
data: function () {return {view: ''}},
|
||||
|
||||
|
||||
computed: {
|
||||
metric: function () {return this.$root.metric()},
|
||||
|
||||
|
||||
_view: function () {
|
||||
if (this.template.scale) {
|
||||
if (this.metric) return 1 * this.model.toFixed(3);
|
||||
|
||||
return 1 * (this.model / this.template.scale).toFixed(4);
|
||||
}
|
||||
|
||||
return this.model;
|
||||
data: function() {
|
||||
return { view: "" };
|
||||
},
|
||||
|
||||
computed: {
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
units: function () {
|
||||
return (this.metric || !this.template.iunit) ?
|
||||
this.template.unit : this.template.iunit;
|
||||
_view: function() {
|
||||
if (this.template.scale) {
|
||||
if (this.metric) {
|
||||
return 1 * this.model.toFixed(3);
|
||||
}
|
||||
|
||||
return 1 * (this.model / this.template.scale).toFixed(4);
|
||||
}
|
||||
|
||||
return this.model;
|
||||
},
|
||||
|
||||
units: function() {
|
||||
return (this.metric || !this.template.iunit)
|
||||
? this.template.unit
|
||||
: this.template.iunit;
|
||||
},
|
||||
|
||||
title: function() {
|
||||
let s = `Default :${this.template.default} ${(this.template.unit || "")}`;
|
||||
|
||||
if (typeof this.template.help != "undefined") {
|
||||
s = `${this.template.help}\n${s}`;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
_view: function() {
|
||||
this.view = this._view;
|
||||
},
|
||||
|
||||
title: function () {
|
||||
var s = 'Default ' + this.template.default + ' ' +
|
||||
(this.template.unit || '');
|
||||
if (typeof this.template.help != 'undefined')
|
||||
s = this.template.help + '\n' + s;
|
||||
return s;
|
||||
view: function() {
|
||||
if (this.template.scale && !this.metric) {
|
||||
this.model = this.view * this.template.scale;
|
||||
} else {
|
||||
this.model = this.view;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
this.view = this._view;
|
||||
},
|
||||
|
||||
methods: {
|
||||
change: function() {
|
||||
this.$dispatch("input-changed");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
watch: {
|
||||
_view: function () {this.view = this._view},
|
||||
|
||||
|
||||
view: function () {
|
||||
if (this.template.scale && !this.metric)
|
||||
this.model = this.view * this.template.scale;
|
||||
else this.model = this.view;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
ready: function () {this.view = this._view},
|
||||
|
||||
|
||||
methods: {
|
||||
change: function () {this.$dispatch('input-changed')}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,277 +1,256 @@
|
||||
/******************************************************************************\
|
||||
"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,
|
||||
},
|
||||
{
|
||||
id: "EM60",
|
||||
name: "EM60",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
components: {
|
||||
"modbus-reg": require("./modbus-reg.js")
|
||||
},
|
||||
|
||||
watch: {
|
||||
"state.mr": function() {
|
||||
this.value = this.state.mr;
|
||||
}
|
||||
},
|
||||
|
||||
events: {
|
||||
"input-changed": function() {
|
||||
this.$dispatch("config-changed");
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
this.value = this.state.mr;
|
||||
},
|
||||
|
||||
computed: {
|
||||
regs_tmpl: function() {
|
||||
return this.template["modbus-spindle"].regs;
|
||||
},
|
||||
|
||||
tool_type: function() {
|
||||
return this.config.tool["tool-type"].toUpperCase();
|
||||
},
|
||||
|
||||
selected_tool: function() {
|
||||
return this.config.tool["selected-tool"];
|
||||
},
|
||||
|
||||
is_pwm_spindle: function() {
|
||||
return this.selected_tool == "pwm";
|
||||
},
|
||||
|
||||
is_modbus: function() {
|
||||
switch (this.selected_tool) {
|
||||
case "disabled":
|
||||
case "laser":
|
||||
case "router":
|
||||
case "pwm":
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
modbus_status: function() {
|
||||
return modbus.status_to_string(this.state.mx);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
change_selected_tool: function() {
|
||||
const selectedToolSettings = this.config["selected-tool-settings"] || {};
|
||||
const settings = selectedToolSettings[this.selected_tool] || {};
|
||||
this.config.tool = merge({}, this.config.tool, settings["tool"]);
|
||||
this.config["pwm-spindle"] = merge({}, this.config["pwm-spindle"], settings["pwm-spindle"]);
|
||||
this.config["modbus-spindle"] = merge({}, this.config["modbus-spindle"], settings["modbus-spindle"]);
|
||||
|
||||
const tool = this.toolList.find(tool => tool.id == this.config.tool["selected-tool"]);
|
||||
this.config.tool["tool-type"] = tool.type || tool.name;
|
||||
|
||||
this.$dispatch("config-changed");
|
||||
},
|
||||
|
||||
show_tool_settings: function(key) {
|
||||
switch (true) {
|
||||
case key === "tool-type":
|
||||
case key === "selected-tool":
|
||||
return false;
|
||||
|
||||
case this.selected_tool === "disabled":
|
||||
return false;
|
||||
|
||||
case this.selected_tool === "laser":
|
||||
case this.selected_tool === "router":
|
||||
switch (key) {
|
||||
case "tool-enable-mode":
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
get_reg_type: function(reg) {
|
||||
return this.regs_tmpl.template["reg-type"].values[this.state[`${reg}vt`]];
|
||||
},
|
||||
|
||||
get_reg_addr: function(reg) {
|
||||
return this.state[`${reg}va`];
|
||||
},
|
||||
|
||||
get_reg_value: function(reg) {
|
||||
return this.state[`${reg}vv`];
|
||||
},
|
||||
|
||||
get_reg_fails: function(reg) {
|
||||
const fails = this.state[`${reg}vr`];
|
||||
return fails == 255 ? "Max" : fails;
|
||||
},
|
||||
|
||||
show_modbus_field: function(key) {
|
||||
return key != "regs" && (key != "multi-write" || this.tool_type == "CUSTOM MODBUS VFD");
|
||||
},
|
||||
|
||||
read: function(e) {
|
||||
e.preventDefault();
|
||||
api.put("modbus/read", { address: this.address });
|
||||
},
|
||||
|
||||
write: function(e) {
|
||||
e.preventDefault();
|
||||
api.put("modbus/write", { address: this.address, value: this.value });
|
||||
},
|
||||
|
||||
customize: function(e) {
|
||||
e.preventDefault();
|
||||
this.config.tool["tool-type"] = "Custom Modbus VFD";
|
||||
|
||||
const regs = this.config["modbus-spindle"].regs;
|
||||
for (let i = 0; i < regs.length; i++) {
|
||||
const reg = this.regs_tmpl.index[i];
|
||||
regs[i]["reg-type"] = this.get_reg_type(reg);
|
||||
regs[i]["reg-addr"] = this.get_reg_addr(reg);
|
||||
regs[i]["reg-value"] = this.get_reg_value(reg);
|
||||
}
|
||||
|
||||
this.$dispatch("config-changed");
|
||||
},
|
||||
|
||||
clear: function(e) {
|
||||
e.preventDefault();
|
||||
this.config.tool["tool-type"] = "Custom Modbus VFD";
|
||||
|
||||
const regs = this.config["modbus-spindle"].regs;
|
||||
for (let i = 0; i < regs.length; i++) {
|
||||
regs[i]["reg-type"] = "disabled";
|
||||
regs[i]["reg-addr"] = 0;
|
||||
regs[i]["reg-value"] = 0;
|
||||
}
|
||||
|
||||
this.$dispatch("config-changed");
|
||||
},
|
||||
|
||||
reset_failures: function(e) {
|
||||
e.preventDefault();
|
||||
const regs = this.config["modbus-spindle"].regs;
|
||||
for (let reg = 0; reg < regs.length; reg++) {
|
||||
this.$dispatch("send", `$${reg}vr=0`);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
'modbus-reg': require('./modbus-reg.js')
|
||||
},
|
||||
|
||||
watch: {
|
||||
'state.mr': function () { this.value = this.state.mr }
|
||||
},
|
||||
|
||||
events: {
|
||||
'input-changed': function () {
|
||||
this.$dispatch('config-changed');
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
this.value = this.state.mr;
|
||||
},
|
||||
|
||||
computed: {
|
||||
regs_tmpl: function () {
|
||||
return this.template['modbus-spindle'].regs;
|
||||
},
|
||||
|
||||
tool_type: function () {
|
||||
return this.config.tool['tool-type'].toUpperCase();
|
||||
},
|
||||
|
||||
selected_tool: function () {
|
||||
return this.config.tool['selected-tool'];
|
||||
},
|
||||
|
||||
is_pwm_spindle: function () {
|
||||
return this.selected_tool == 'pwm';
|
||||
},
|
||||
|
||||
is_modbus: function () {
|
||||
switch (this.selected_tool) {
|
||||
case "disabled":
|
||||
case "laser":
|
||||
case "router":
|
||||
case "pwm":
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
modbus_status: function () {
|
||||
return modbus.status_to_string(this.state.mx);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
change_selected_tool: function () {
|
||||
const selectedToolSettings = this.config['selected-tool-settings'] || {};
|
||||
const settings = selectedToolSettings[this.selected_tool] || {};
|
||||
this.config.tool = merge({}, this.config.tool, settings['tool']);
|
||||
this.config['pwm-spindle'] = merge({}, this.config['pwm-spindle'], settings['pwm-spindle']);
|
||||
this.config['modbus-spindle'] = merge({}, this.config['modbus-spindle'], settings['modbus-spindle']);
|
||||
|
||||
const tool = this.toolList.find(tool => tool.id == this.config.tool['selected-tool']);
|
||||
this.config.tool["tool-type"] = tool.type || tool.name;
|
||||
|
||||
this.$dispatch("config-changed");
|
||||
},
|
||||
|
||||
show_tool_settings: function (key) {
|
||||
switch (true) {
|
||||
case key === "tool-type":
|
||||
case key === "selected-tool":
|
||||
return false;
|
||||
|
||||
case this.selected_tool === "disabled":
|
||||
return false;
|
||||
|
||||
case this.selected_tool === "laser":
|
||||
case this.selected_tool === "router":
|
||||
switch (key) {
|
||||
case "tool-enable-mode":
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
get_reg_type: function (reg) {
|
||||
return this.regs_tmpl.template['reg-type'].values[this.state[reg + 'vt']];
|
||||
},
|
||||
|
||||
get_reg_addr: function (reg) {
|
||||
return this.state[reg + 'va'];
|
||||
},
|
||||
|
||||
get_reg_value: function (reg) {
|
||||
return this.state[reg + 'vv'];
|
||||
},
|
||||
|
||||
get_reg_fails: function (reg) {
|
||||
const fails = this.state[reg + 'vr']
|
||||
return fails == 255 ? 'Max' : fails;
|
||||
},
|
||||
|
||||
show_modbus_field: function (key) {
|
||||
return key != 'regs' &&
|
||||
(key != 'multi-write' || this.tool_type == 'CUSTOM MODBUS VFD');
|
||||
},
|
||||
|
||||
read: function (e) {
|
||||
e.preventDefault();
|
||||
api.put('modbus/read', { address: this.address });
|
||||
},
|
||||
|
||||
write: function (e) {
|
||||
e.preventDefault();
|
||||
api.put('modbus/write', { address: this.address, value: this.value });
|
||||
},
|
||||
|
||||
customize: function (e) {
|
||||
e.preventDefault();
|
||||
this.config.tool['tool-type'] = 'Custom Modbus VFD';
|
||||
|
||||
const regs = this.config['modbus-spindle'].regs;
|
||||
for (let i = 0; i < regs.length; i++) {
|
||||
const reg = this.regs_tmpl.index[i];
|
||||
regs[i]['reg-type'] = this.get_reg_type(reg);
|
||||
regs[i]['reg-addr'] = this.get_reg_addr(reg);
|
||||
regs[i]['reg-value'] = this.get_reg_value(reg);
|
||||
}
|
||||
|
||||
this.$dispatch('config-changed');
|
||||
},
|
||||
|
||||
clear: function (e) {
|
||||
e.preventDefault();
|
||||
this.config.tool['tool-type'] = 'Custom Modbus VFD';
|
||||
|
||||
const regs = this.config['modbus-spindle'].regs;
|
||||
for (let i = 0; i < regs.length; i++) {
|
||||
regs[i]['reg-type'] = 'disabled';
|
||||
regs[i]['reg-addr'] = 0;
|
||||
regs[i]['reg-value'] = 0;
|
||||
}
|
||||
|
||||
this.$dispatch('config-changed');
|
||||
},
|
||||
|
||||
reset_failures: function (e) {
|
||||
e.preventDefault();
|
||||
const regs = this.config['modbus-spindle'].regs;
|
||||
for (let reg = 0; reg < regs.length; reg++)
|
||||
this.$dispatch('send', '\$' + reg + 'vr=0');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,58 +1,47 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
|
||||
props: ['value', 'precision', 'unit', 'iunit', 'scale'],
|
||||
replace: true,
|
||||
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
|
||||
props: [ "value", "precision", "unit", "iunit", "scale" ],
|
||||
|
||||
computed: {
|
||||
metric: {
|
||||
cache: false,
|
||||
get: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
metric: function () {return !this.$root.state.imperial},
|
||||
text: function() {
|
||||
let value = this.value;
|
||||
if (typeof value == "undefined") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!this.metric) {
|
||||
value /= this.scale;
|
||||
}
|
||||
|
||||
text: function () {
|
||||
var value = this.value;
|
||||
if (typeof value == 'undefined') return '';
|
||||
return (1 * value.toFixed(this.precision)).toLocaleString();
|
||||
}
|
||||
},
|
||||
|
||||
if (!this.metric) value /= this.scale;
|
||||
ready: function() {
|
||||
if (typeof this.precision == "undefined") {
|
||||
this.precision = 0;
|
||||
}
|
||||
|
||||
return (1 * value.toFixed(this.precision)).toLocaleString();
|
||||
if (typeof this.unit == "undefined") {
|
||||
this.unit = "mm";
|
||||
}
|
||||
|
||||
if (typeof this.iunit == "undefined") {
|
||||
this.iunit = "in";
|
||||
}
|
||||
|
||||
if (typeof this.scale == "undefined") {
|
||||
this.scale = 25.4;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
ready: function () {
|
||||
if (typeof this.precision == 'undefined') this.precision = 0;
|
||||
if (typeof this.unit == 'undefined') this.unit = 'mm';
|
||||
if (typeof this.iunit == 'undefined') this.iunit = 'in';
|
||||
if (typeof this.scale == 'undefined') this.scale = 25.4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
9
src/js/utils.js
Normal file
9
src/js/utils.js
Normal file
@@ -0,0 +1,9 @@
|
||||
function clickFileInput(formClass) {
|
||||
const form = document.querySelector(`.${formClass}`);
|
||||
form.reset();
|
||||
form.querySelector("input").click();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
clickFileInput
|
||||
};
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
doctype html
|
||||
html(lang="en")
|
||||
head
|
||||
@@ -39,12 +12,17 @@ html(lang="en")
|
||||
style: include ../static/css/font-awesome.min.css
|
||||
style: include ../static/css/Audiowide.css
|
||||
style: include ../static/css/clusterize.css
|
||||
style: include ../svelte-components/node_modules/svelte-material-ui/bare.css
|
||||
style: include ../../build/http/svelte-components/smui.css
|
||||
style: include ../../build/http/svelte-components/style.css
|
||||
style: include:stylus ../stylus/style.styl
|
||||
|
||||
|
||||
body(v-cloak)
|
||||
#svelte-dialog-host
|
||||
|
||||
#overlay(v-if="status != 'connected'")
|
||||
span {{status}}
|
||||
|
||||
#layout
|
||||
a#menuLink.menu-link(href="#menu"): span
|
||||
|
||||
@@ -87,49 +65,40 @@ html(lang="en")
|
||||
li.pure-menu-heading
|
||||
a.pure-menu-link(href="#help") Help
|
||||
|
||||
button.pure-button.pure-button-primary(@click="confirmShutdown = true", style="width: 100%")
|
||||
.fa.fa-power-off
|
||||
message(:show.sync="confirmShutdown")
|
||||
h3(slot="header") Confirm shutdown?
|
||||
p(slot="body") Please wait for black screen before switching off power.
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="confirmShutdown = false") Cancel
|
||||
button.pure-button.button-success(@click="shutdown") Shutdown
|
||||
button.pure-button.button-success(@click="reboot") Restart
|
||||
|
||||
button.pure-button.pure-button-primary(@click="showShutdownDialog", style="width: 100%")
|
||||
.fa.fa-power-off
|
||||
|
||||
#main
|
||||
.header
|
||||
.header-content
|
||||
.banner
|
||||
img(src="/images/onefinity_logo.png")
|
||||
.title
|
||||
.fa.fa-thermometer-full(class="error",
|
||||
v-if="80 <= state.rpi_temp",
|
||||
title="Raspberry Pi temperature too high.")
|
||||
.subtitle
|
||||
| CNC Controller #[b {{state.demo ? 'Demo ' : ''}}]
|
||||
| v{{config.full_version}}
|
||||
a.upgrade-version(v-if="show_upgrade()", href="#admin-general")
|
||||
| Upgrade to v{{latestVersion}}
|
||||
.fa.fa-check(v-if="!show_upgrade() && latestVersion",
|
||||
title="Firmware up to date" style="font-size: inherit")
|
||||
.p {{get_ip_address()}} {{get_ssid()}}
|
||||
.nav-header
|
||||
.brand
|
||||
img(src="/images/onefinity_logo.png")
|
||||
.version
|
||||
div Version: v{{config.full_version}}
|
||||
div IP Address: {{config.ip}}
|
||||
div WiFi: {{config.wifiName}}
|
||||
a.upgrade-link(v-if="show_upgrade()", href="#admin-general")
|
||||
| Upgrade to v{{latestVersion}}
|
||||
.fa.fa-exclamation-circle.upgrade-attention(v-if="show_upgrade()")
|
||||
|
||||
.estop(:class="{active: state.es}")
|
||||
estop(@click="estop")
|
||||
.pi-temp-warning
|
||||
.fa.fa-thermometer-full(class="error",
|
||||
v-if="80 <= state.rpi_temp",
|
||||
title="Raspberry Pi temperature too high.")
|
||||
|
||||
.video(title="Plug camera into USB.\n" +
|
||||
"Left click to toggle video size.\n" +
|
||||
"Right click to toggle crosshair.", @click="toggle_video",
|
||||
@contextmenu="toggle_crosshair", :class="video_size")
|
||||
.crosshair(v-if="crosshair")
|
||||
.vertical
|
||||
.horizontal
|
||||
.box
|
||||
img(src="/api/video")
|
||||
.whitespace
|
||||
|
||||
.clear
|
||||
.video(title="Plug camera into USB.\n" +
|
||||
"Left click to toggle video size.\n" +
|
||||
"Right click to toggle crosshair.", @click="toggle_video",
|
||||
@contextmenu="toggle_crosshair", :class="video_size")
|
||||
.crosshair(v-if="crosshair")
|
||||
.vertical
|
||||
.horizontal
|
||||
.box
|
||||
img(src="/api/video")
|
||||
|
||||
.estop(:class="{active: state.es}")
|
||||
estop(@click="estop")
|
||||
|
||||
.content(class="{{currentView}}-view")
|
||||
component(:is="currentView + '-view'", :index="index",
|
||||
@@ -205,10 +174,10 @@ html(lang="en")
|
||||
#templates: include ../../build/templates.pug
|
||||
iframe#download-target(style="display:none")
|
||||
|
||||
script: include ../static/js/jquery-1.11.3.min.js
|
||||
script: include ../static/js/vue.js
|
||||
script: include ../static/js/sockjs.min.js
|
||||
script: include ../static/js/clusterize.min.js
|
||||
script: include ../static/js/three.min.js
|
||||
script: include:browserify ../js/main.js
|
||||
script: include ../../build/http/svelte-components/index.js
|
||||
script: include ../static/js/ui.js
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#admin-general-view-template(type="text/x-template")
|
||||
#admin-general
|
||||
h2 Firmware
|
||||
@@ -45,9 +18,6 @@ script#admin-general-view-template(type="text/x-template")
|
||||
label.pure-button.pure-button-primary(@click="restore_config") Restore
|
||||
form.restore-config.file-upload
|
||||
input(type="file", accept=".json", @change="restore")
|
||||
message(:show.sync="configRestored")
|
||||
h3(slot="header") Success
|
||||
p(slot="body") Configuration restored.
|
||||
|
||||
button.pure-button.pure-button-primary(@click="confirmReset = true") Reset
|
||||
message(:show.sync="confirmReset")
|
||||
@@ -68,12 +38,23 @@ script#admin-general-view-template(type="text/x-template")
|
||||
label(for="tab4", title="Journeyman X-50") Journeyman X-50
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="confirmReset = false") Cancel
|
||||
button.pure-button.pure-button-primary(@click="reset") Reset
|
||||
button.pure-button.pure-button-primary(@click="next") next
|
||||
|
||||
message(:show.sync="configReset")
|
||||
h3(slot="header") Success
|
||||
p(slot="body") Configuration reset.
|
||||
message(:show.sync="z_slider")
|
||||
h3(slot="header") Pick your Z Slider Type
|
||||
p(slot="body") Non-network configuration changes will be lost.
|
||||
p(slot="body") Select defaults to restore:
|
||||
p.reset-variants(slot="body")
|
||||
input#tab1(type="radio", name="z_slider_variant" @click="z_slider_variant = 'Z-16 Original'")
|
||||
label(for="tab1", title="Z-16 Original") Z-16 Original
|
||||
|
||||
input#tab2(type="radio", name="z_slider_variant" @click="z_slider_variant = 'Z-20 Heavy Duty'")
|
||||
label(for="tab2", title="Z-20 Heavy Duty") Z-20 Heavy Duty
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="z_slider = false") Cancel
|
||||
button.pure-button.pure-button-primary(@click="set_z_slider") set z slider
|
||||
|
||||
h2 Debugging
|
||||
a(href="/api/log", target="_blank")
|
||||
button.pure-button.pure-button-primary View Log
|
||||
|
||||
@@ -1,130 +1,2 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#admin-network-view-template(type="text/x-template")
|
||||
#admin-network
|
||||
h2 Hostname
|
||||
.pure-form.pure-form-aligned
|
||||
.pure-control-group
|
||||
label(for="hostname") Hostname
|
||||
input(name="hostname", v-model="hostname", @keyup.enter="set_hostname")
|
||||
button.pure-button.pure-button-primary(@click="set_hostname") Set
|
||||
|
||||
message(:show.sync="hostnameSet")
|
||||
h3(slot="header") Hostname Set
|
||||
div(slot="body")
|
||||
p Hostname was successfuly set to #[strong {{hostname}}].
|
||||
p Rebooting to apply changes.
|
||||
p Redirecting to new hostname in {{redirectTimeout}} seconds.
|
||||
div(slot="footer")
|
||||
|
||||
h2 Remote SSH User
|
||||
.pure-form.pure-form-aligned
|
||||
.pure-control-group
|
||||
label(for="username") Username
|
||||
input(name="username", v-model="username", @keyup.enter="set_username")
|
||||
button.pure-button.pure-button-primary(@click="set_username") Set
|
||||
|
||||
.pure-form.pure-form-aligned
|
||||
.pure-control-group
|
||||
label(for="current") Current Password
|
||||
input(name="current", v-model="current", type="password")
|
||||
.pure-control-group
|
||||
label(for="pass1") New Password
|
||||
input(name="pass1", v-model="password", type="password")
|
||||
.pure-control-group
|
||||
label(for="pass2") New Password
|
||||
input(name="pass2", v-model="password2", type="password")
|
||||
button.pure-button.pure-button-primary(@click="set_password") Set
|
||||
|
||||
message(:show.sync="passwordSet")
|
||||
h3(slot="header") Password Set
|
||||
p(slot="body")
|
||||
|
||||
message(:show.sync="usernameSet")
|
||||
h3(slot="header") Username Set
|
||||
p(slot="body")
|
||||
|
||||
h2 Wifi Setup
|
||||
.pure-form.pure-form-aligned
|
||||
.pure-control-group
|
||||
label(for="wifi_mode") Mode
|
||||
select(name="wifi_mode", v-model="wifi_mode",
|
||||
title="Select client or access point mode")
|
||||
option(value="disabled") Disabled
|
||||
option(value="client") Client
|
||||
option(value="ap") Access Point
|
||||
button.pure-button.pure-button-primary(@click="wifiConfirm = true",
|
||||
v-if="wifi_mode == 'disabled'") Set
|
||||
|
||||
.pure-control-group(v-if="wifi_mode == 'ap'")
|
||||
label(for="wifi_ch") Channel
|
||||
select(name="wifi_ch", v-model="wifi_ch")
|
||||
each ch in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
option(value=ch)= ch
|
||||
|
||||
.pure-control-group(v-if="wifi_mode != 'disabled'")
|
||||
label(for="ssid") Network (SSID)
|
||||
input(name="ssid", v-model="wifi_ssid")
|
||||
|
||||
.pure-control-group(v-if="wifi_mode != 'disabled'")
|
||||
label(for="wifi_pass") Password
|
||||
input(name="wifi_pass", v-model="wifi_pass", type="password")
|
||||
button.pure-button.pure-button-primary(@click="wifiConfirm = true") Set
|
||||
|
||||
p(v-if="wifi_mode != 'disabled'").
|
||||
WARNING: WiFi may be unreliable in an electrically noisy environment
|
||||
such as a machine shop.
|
||||
|
||||
message.wifi-confirm(:show.sync="wifiConfirm")
|
||||
h3(slot="header") Configure Wifi and reboot?
|
||||
div(slot="body")
|
||||
p
|
||||
| After configuring the Wifi settings the controller will
|
||||
| automatically reboot.
|
||||
table
|
||||
tr
|
||||
th Mode
|
||||
td {{wifi_mode}}
|
||||
tr(v-if="wifi_mode == 'ap'")
|
||||
th Channel
|
||||
td {{wifi_ch}}
|
||||
tr(v-if="wifi_mode != 'disabled'")
|
||||
th SSID
|
||||
td {{wifi_ssid}}
|
||||
tr(v-if="wifi_mode != 'disabled'")
|
||||
th Auth
|
||||
td {{wifi_pass ? 'WPA2' : 'Open'}}
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="wifiConfirm = false") Cancel
|
||||
button.pure-button.button-success(@click="config_wifi") OK
|
||||
|
||||
message(:show.sync="rebooting")
|
||||
h3(slot="header") Rebooting
|
||||
p(slot="body") Please wait...
|
||||
div(slot="footer")
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#axis-control-template(type="text/x-template")
|
||||
svg(xmlns="http://www.w3.org/2000/svg",
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink",
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#cheat-sheet-view-template(type="text/x-template")
|
||||
// Modified from http://linuxcnc.org/docs/html/gcode.html
|
||||
- var base = 'http://linuxcnc.org/docs/html/gcode';
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#console-template(type="text/x-template")
|
||||
.console
|
||||
.toolbar
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#control-view-template(type="text/x-template")
|
||||
#control
|
||||
message(:show.sync="showGcodeMessage")
|
||||
@@ -36,109 +9,6 @@ script#control-view-template(type="text/x-template")
|
||||
|
||||
div(slot="footer")
|
||||
label Simulating {{(toolpath_progress || 0) | percent}}
|
||||
|
||||
message(:show.sync=`ask_home_msg`)
|
||||
h3(slot="header") Home Machine
|
||||
|
||||
div(slot="body")
|
||||
p Home the machine?
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="home()")
|
||||
| OK
|
||||
|
||||
button.pure-button(@click='ask_home_msg = false; ask_home = false')
|
||||
| Cancel
|
||||
|
||||
message(:show.sync=`ask_zero_xy_msg`)
|
||||
h3(slot="header") XY Origin
|
||||
|
||||
div(slot="body")
|
||||
p Move to XY origin?
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="goto_zero(1,1,0,0)")
|
||||
| Confirm
|
||||
|
||||
button.pure-button(@click='ask_zero_xy_msg = false')
|
||||
| Cancel
|
||||
|
||||
message(:show.sync=`ask_zero_z_msg`)
|
||||
h3(slot="header") Z Origin
|
||||
|
||||
div(slot="body")
|
||||
p Move to Z origin?
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click="goto_zero(0,0,1,0)")
|
||||
| Confirm
|
||||
|
||||
button.pure-button(@click='ask_zero_z_msg = false')
|
||||
| Cancel
|
||||
|
||||
message(:show.sync=`show_probe_test_modal`)
|
||||
|
||||
h3(slot="header") Test probe connection
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
p Attach the probe magnet to the collet.
|
||||
p Touch the probe block to the bit.
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`show_probe_test_modal = false`)
|
||||
| Cancel
|
||||
|
||||
button.pure-button.button-success(
|
||||
:disabled=`!state.saw_probe_connected`
|
||||
@click=`finish_probe_test()`) Continue
|
||||
|
||||
message(:show.sync=`show_tool_diameter_modal`)
|
||||
h3(slot="header") Enter probe tool information
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
.pure-control-group
|
||||
label="{{metric ? 'Diameter (mm)' : 'Diameter (inches)'}}"
|
||||
input(v-model="tool_diameter_for_prompt", size="8")
|
||||
p
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`show_tool_diameter_modal = false`)
|
||||
| Cancel
|
||||
|
||||
button.pure-button.button-success(
|
||||
@click=`set_tool_diameter`)
|
||||
| Set
|
||||
|
||||
message(:show.sync=`state.show_probe_complete_modal`)
|
||||
h3(slot="header") Probing complete!
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
p Don't forget to put away the probe!
|
||||
div(v-if="state.goto_xy_zero_after_probe")
|
||||
p
|
||||
| The machine will now move
|
||||
br
|
||||
| to the X-Y zero point.
|
||||
p Watch your hands!
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button.button-success(@click=`$emit("finalize_probe")`)
|
||||
| Done
|
||||
|
||||
message(:show.sync=`state.show_probe_failed_modal`)
|
||||
h3(slot="header") Probing failed!
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
p Could not find the probe block during probing!
|
||||
p Make sure the tip of the bit is about 1/4" (~6mm) above the probe block, and try again.
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button.button-success(@click=`hide_probe_failed_modal()`)
|
||||
| OK
|
||||
|
||||
table(style="table-layout: fixed; width: 100%;")
|
||||
tr(style="height: fit-content;")
|
||||
@@ -151,55 +21,60 @@ script#control-view-template(type="text/x-template")
|
||||
col(style="width:100px")
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(-1,1,0,0)")
|
||||
button(@click="jog_fn(-1,1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(-135deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(0,1,0,0)") Y+
|
||||
button(@click="jog_fn(0,1,0,0)") Y+
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(1,1,0,0)")
|
||||
button(@click="jog_fn(1,1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(-45deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",,@click="jog_fn(0,0,1,0)") Z+
|
||||
button(,@click="jog_fn(0,0,1,0)") Z+
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(-1,0,0,0)") X-
|
||||
button(@click="jog_fn(-1,0,0,0)") X-
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="ask_zero_xy_msg = true")
|
||||
.fa.fa-bullseye(style="font-size: 172%")
|
||||
button(@click="showMoveToZeroDialog('xy')")
|
||||
.fa.fa-bullseye(style="font-size: 173%")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(1,0,0,0)") X+
|
||||
button(@click="jog_fn(1,0,0,0)") X+
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click='ask_zero_z_msg = true') Z0
|
||||
button(@click="showMoveToZeroDialog('z')") Z0
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(-1,-1,0,0)")
|
||||
button(@click="jog_fn(-1,-1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(135deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(0,-1,0,0)") Y-
|
||||
button(@click="jog_fn(0,-1,0,0)") Y-
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(1,-1,0,0)")
|
||||
button(@click="jog_fn(1,-1,0,0)")
|
||||
.fa.fa-arrow-right(style="transform: rotate(45deg);")
|
||||
td(style="height:100px",align="center")
|
||||
button(style="height:100px;width:100px",@click="jog_fn(0,0,-1,0)") Z-
|
||||
button(@click="jog_fn(0,0,-1,0)") Z-
|
||||
tr
|
||||
td(style="height:100px",align="center")
|
||||
button#jog_button_fine(style="height:100px;width:100px", @click=`set_jog_incr('fine')`) 0.1
|
||||
button(:style="getJogIncrStyle('fine')", @click="jog_incr = 'fine'")
|
||||
span {{jog_incr_amounts[display_units].fine}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button#jog_button_small(style="height:100px;width:100px", @click=`set_jog_incr('small')`) 1.0
|
||||
button(:style="getJogIncrStyle('small')", @click="jog_incr = 'small'")
|
||||
span {{jog_incr_amounts[display_units].small}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button#jog_button_medium(style="height:100px;width:100px", @click=`set_jog_incr('medium')`) 10
|
||||
button(:style="getJogIncrStyle('medium')", @click="jog_incr = 'medium'")
|
||||
span {{jog_incr_amounts[display_units].medium}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
td(style="height:100px",align="center")
|
||||
button#jog_button_large(style="height:100px;width:100px", @click=`set_jog_incr('large')`) 100
|
||||
button(:style="getJogIncrStyle('large')", @click="jog_incr = 'large'")
|
||||
span {{jog_incr_amounts[display_units].large}}#[span.jog-units {{metric ? 'mm' : 'in'}}]
|
||||
tr
|
||||
td(style="height:100px", align="center", colspan="2")
|
||||
button(:class="state['pw'] ? '' : 'load-on'",
|
||||
style="height:100px;width:200px",
|
||||
@click=`start_probe_test(prep_and_show_tool_diameter_modal)`)
|
||||
@click="showProbeDialog('xyz')")
|
||||
| Probe XYZ
|
||||
|
||||
td(style="height:100px", align="center", colspan="2")
|
||||
button(:class="state['pw'] ? '' : 'load-on'",
|
||||
style="height:100px;width:200px",
|
||||
@click=`start_probe_test(probe_z)`)
|
||||
@click="showProbeDialog('z')")
|
||||
| Probe Z
|
||||
|
||||
td(style="vertical-align: top;")
|
||||
@@ -211,8 +86,6 @@ script#control-view-template(type="text/x-template")
|
||||
th.offset Offset
|
||||
th.state State
|
||||
th.tstate Toolpath
|
||||
//th.tstate Min
|
||||
//th.tstate Max
|
||||
th.actions
|
||||
button.pure-button(disabled, style="height:60px;width:60px;display:none;")
|
||||
|
||||
@@ -234,79 +107,25 @@ script#control-view-template(type="text/x-template")
|
||||
td.state
|
||||
.fa(:class=`'fa-' + ${axis}.icon`)
|
||||
| {{#{axis}.state}}
|
||||
td.tstate(:class=`${axis}.tklass`, :title=`${axis}.toolmsg`, @click=`show_toolpath_msg('${axis}')`)
|
||||
td.tstate(:class=`${axis}.tklass`, :title=`${axis}.toolmsg`, @click=`showToolpathMessageDialog('${axis}')`)
|
||||
.fa(:class=`'fa-' + ${axis}.ticon`)
|
||||
| {{#{axis}.tstate}}
|
||||
//td.tstate: unit-value(:value=`${axis}.pathMin`, precision=4)
|
||||
//td.tstate: unit-value(:value=`${axis}.pathMax`, precision=4)
|
||||
|
||||
message(:show.sync=`toolpath_msg['${axis}']`)
|
||||
h3(slot="header") Tool path info {{'#{axis}' | upper}} axis
|
||||
|
||||
div(slot="body")
|
||||
p {{#{axis}.toolmsg}}
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`toolpath_msg['${axis}'] = false`)
|
||||
| OK
|
||||
|
||||
|
||||
th.actions
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title=`Set {{'${axis}' | upper}} axis position.`,
|
||||
@click=`show_set_position('${axis}')`,style="height:60px;width:60px")
|
||||
@click=`show_set_position('${axis}')`, style="height:60px;width:60px")
|
||||
.fa.fa-cog
|
||||
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title=`Zero {{'${axis}' | upper}} axis offset.`,
|
||||
@click=`zero('${axis}')`,style="height:60px;width:60px")
|
||||
@click=`zero('${axis}')`, style="height:60px;width:60px")
|
||||
.fa.fa-map-marker
|
||||
|
||||
button.pure-button(:disabled="!is_idle", @click=`home('${axis}')`,
|
||||
title=`Home {{'${axis}' | upper}} axis.`,style="height:60px;width:60px")
|
||||
title=`Home {{'${axis}' | upper}} axis.`, style="height:60px;width:60px")
|
||||
.fa.fa-home
|
||||
|
||||
message(:show.sync=`position_msg['${axis}']`)
|
||||
h3(slot="header") Set {{'#{axis}' | upper}} axis position
|
||||
|
||||
div(slot="body")
|
||||
.pure-form
|
||||
.pure-control-group
|
||||
label Position
|
||||
input(v-model="axis_position",
|
||||
@keyup.enter=`set_position('${axis}', axis_position)`)
|
||||
p
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`position_msg['${axis}'] = false`)
|
||||
| Cancel
|
||||
|
||||
button.pure-button(v-if=`${axis}.homed`,
|
||||
@click=`unhome('${axis}')`) Unhome
|
||||
|
||||
button.pure-button.button-success(
|
||||
@click=`set_position('${axis}', axis_position)`) Set
|
||||
|
||||
message(:show.sync=`manual_home['${axis}']`)
|
||||
h3(slot="header") Manually home {{'#{axis}' | upper}} axis
|
||||
|
||||
div(slot="body")
|
||||
p Set axis absolute position.
|
||||
|
||||
.pure-form
|
||||
.pure-control-group
|
||||
label Absolute
|
||||
input(v-model="axis_position",
|
||||
@keyup.enter=`set_home('${axis}', axis_position)`)
|
||||
|
||||
p
|
||||
|
||||
div(slot="footer")
|
||||
button.pure-button(@click=`manual_home['${axis}'] = false`)
|
||||
| Cancel
|
||||
|
||||
button.pure-button.button-success(
|
||||
title=`Home {{'${axis}' | upper}} axis.`,
|
||||
@click=`set_home('${axis}', axis_position)`) Set
|
||||
|
||||
tr(style="vertical-align: top;")
|
||||
td
|
||||
@@ -323,10 +142,10 @@ script#control-view-template(type="text/x-template")
|
||||
td.message(:class="{attention: highlight_state}")
|
||||
| {{message.replace(/^#/, '')}}
|
||||
|
||||
tr(title="Active machine units")
|
||||
th Units
|
||||
td.mach_units
|
||||
select(v-model="mach_units", :disabled="!is_idle")
|
||||
tr
|
||||
th Display Units
|
||||
td.units
|
||||
select(v-model="display_units")
|
||||
option(value="METRIC") METRIC
|
||||
option(value="IMPERIAL") IMPERIAL
|
||||
|
||||
@@ -368,6 +187,11 @@ script#control-view-template(type="text/x-template")
|
||||
|
||||
td
|
||||
table.info
|
||||
tr
|
||||
th Current Time
|
||||
td
|
||||
span {{current_time}}
|
||||
|
||||
tr
|
||||
th Remaining
|
||||
td(title="Total run time (days:hours:mins:secs)").
|
||||
@@ -376,12 +200,7 @@ script#control-view-template(type="text/x-template")
|
||||
tr
|
||||
th ETA
|
||||
td.eta {{eta}}
|
||||
tr
|
||||
th Line
|
||||
td
|
||||
| {{0 <= state.line ? state.line : 0 | number}}
|
||||
span(v-if="toolpath.lines")
|
||||
| of {{toolpath.lines | number}}
|
||||
|
||||
tr
|
||||
th Progress
|
||||
td.progress
|
||||
@@ -484,6 +303,9 @@ script#control-view-template(type="text/x-template")
|
||||
|
||||
input(v-model="mdi", :disabled="!can_mdi", @keyup.enter="submit_mdi")
|
||||
|
||||
div
|
||||
em The machine is currently operating in #[strong {{mach_units}}] units. Use G20/G21 to switch units.
|
||||
|
||||
.history(:class="{placeholder: !history}")
|
||||
span(v-if="!history.length") MDI history displays here.
|
||||
ul
|
||||
@@ -497,9 +319,6 @@ script#control-view-template(type="text/x-template")
|
||||
section#content4.tab-content
|
||||
indicators(:state="state", :template="template")
|
||||
|
||||
|
||||
|
||||
|
||||
.override(title="Feed rate override.")
|
||||
label Feed
|
||||
input(type="range", min="0", max="2", step="0.01",
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#estop-template(type="text/x-template")
|
||||
svg(version="1.1", xmlns:svg="http://www.w3.org/2000/svg",
|
||||
xmlns="http://www.w3.org/2000/svg",
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#gcode-viewer-template(type="text/x-template")
|
||||
.gcode
|
||||
.clusterize
|
||||
|
||||
@@ -1,75 +1,2 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#help-view-template(type="text/x-template")
|
||||
#help
|
||||
h2 Contact
|
||||
p
|
||||
| You can contact us here
|
||||
|
|
||||
a(href="https://onefinitycnc.com/support", target="_blank")
|
||||
| onefinitycnc.com/support
|
||||
|.
|
||||
|
||||
h2 Discussion Forum
|
||||
p
|
||||
| Check out our support and discussion forum.
|
||||
|
|
||||
a(href="https://forum.onefinitycnc.com", target="_blank")
|
||||
| forum.onefinitycnc.com
|
||||
|. Register on the site and post a message. We can't wait to hear from you.
|
||||
|
||||
h2 Buildbotics
|
||||
p
|
||||
| This controller is based on the Buildbotics CNC Controller.
|
||||
| We encourage you to check out the
|
||||
a(href="http://buildbotics.com", target="_blank") Buildbotics Website
|
||||
|.
|
||||
|
||||
h2 CAD/CAM Software
|
||||
p
|
||||
a(href="http://wikipedia.com/wiki/Computer-aided_manufacturing",
|
||||
target="_blank") CAM
|
||||
|
|
||||
| software can be used to create GCode
|
||||
| automatically from
|
||||
|
|
||||
a(href="http://wikipedia.com/wiki/Computer-aided_design",
|
||||
target="_blank") CAD
|
||||
|
|
||||
| models. Here are a few CAD/CAM resources:
|
||||
ul
|
||||
li: a(href="http://camotics.org/", target="_blank")
|
||||
| CAMotics - Open-Source CNC Simulator
|
||||
li: a(href="http://librecad.org/", target="_blank")
|
||||
| LibreCAD - Open-Source 2D CAD
|
||||
li: a(href="https://www.freecadweb.org/", target="_blank")
|
||||
| FreeCAD - Open-Source 3D CAD
|
||||
li: a(href="http://www.openscad.org/", target="_blank")
|
||||
| OpenSCAD - Open-Source 3D CAD for programmers
|
||||
li: a(href="http://wiki.linuxcnc.org/cgi-bin/wiki.pl?Cam",
|
||||
target="_blank") LinuxCNC CAM resources
|
||||
#help
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#indicators-template(type="text/x-template")
|
||||
.indicators
|
||||
table.legend
|
||||
|
||||
@@ -1,29 +1,2 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#io-indicator-template(type="text/x-template")
|
||||
.fa.io(:class="klass", :title="tooltip")
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#io-view-template(type="text/x-template")
|
||||
#io
|
||||
h1 I/O Configuration
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#message-template(type="text/x-template")
|
||||
.modal-mask(v-if="show", :class="class")
|
||||
.modal-wrapper
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#modbus-reg-view-template(type="text/x-template")
|
||||
tr.modbus-reg
|
||||
td.reg-index {{index}}
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#motor-view-template(type="text/x-template")
|
||||
.motor(:class="{slave: is_slave}")
|
||||
h1 Motor {{index}} Configuration
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#path-viewer-template(type="text/x-template")
|
||||
.path-viewer(v-show="enabled", :class="{small: small}")
|
||||
.path-viewer-toolbar
|
||||
|
||||
@@ -1,85 +1,2 @@
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#settings-view-template(type="text/x-template")
|
||||
#settings
|
||||
h1 Settings
|
||||
|
||||
.pure-form.pure-form-aligned
|
||||
fieldset
|
||||
h2 Units
|
||||
templated-input(name="units", :model.sync="config.settings.units",
|
||||
:template="template.settings.units")
|
||||
|
||||
p
|
||||
| Note, #[tt units] sets both the machine default units and the
|
||||
| units used in motor configuration. GCode #[tt program-start],
|
||||
| set below, may also change the default machine units.
|
||||
|
||||
fieldset
|
||||
h2 Probing safety prompts
|
||||
templated-input(name="probing-prompts",
|
||||
:model.sync="config.settings['probing-prompts']",
|
||||
:template="template.settings['probing-prompts']")
|
||||
|
||||
fieldset
|
||||
h2 Probe Dimensions
|
||||
templated-input(v-for="templ in template.probe", :name="$key",
|
||||
:model.sync="config.probe[$key]", :template="templ")
|
||||
|
||||
fieldset
|
||||
h2 GCode
|
||||
templated-input(v-for="templ in template.gcode", :name="$key",
|
||||
:model.sync="config.gcode[$key]", :template="templ")
|
||||
|
||||
fieldset
|
||||
h2 Path Accuracy
|
||||
templated-input(name="max-deviation",
|
||||
:model.sync="config.settings['max-deviation']",
|
||||
:template="template.settings['max-deviation']")
|
||||
|
||||
p.
|
||||
Lower #[tt max-deviation] to follow the programmed path more precisely
|
||||
but at a slower speed.
|
||||
|
||||
p.
|
||||
In order to improve traversal speed, the path planner may merge
|
||||
consecutive moves or round off sharp corners if doing so would deviate
|
||||
from the program path by less than #[tt max-deviation].
|
||||
|
||||
- var base = '//linuxcnc.org/docs/html/gcode/g-code.html'
|
||||
p.
|
||||
GCode commands
|
||||
#[a(href=base + "#gcode:g61", target="_blank") G61, G61.1] and
|
||||
#[a(href=base + "#gcode:g64", target="_blank") G64] also affect path
|
||||
planning accuracy.
|
||||
|
||||
h2 Cornering Speed (Advanced)
|
||||
templated-input(name="junction-accel",
|
||||
:model.sync="config.settings['junction-accel']",
|
||||
:template="template.settings['junction-accel']")
|
||||
|
||||
p.
|
||||
Junction acceleration limits the cornering speed the planner will
|
||||
allow. Increasing this value will allow for faster traversal of
|
||||
corners but may cause the planner to violate axis jerk limits and
|
||||
stall the motors. Use with caution.
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#templated-input-template(type="text/x-template")
|
||||
.pure-control-group(class="tmpl-input-{{name}}", :title="title")
|
||||
label(:for="name") {{name}}
|
||||
@@ -38,22 +11,17 @@ script#templated-input-template(type="text/x-template")
|
||||
|
||||
input(v-if="template.type == 'float'", v-model.number="view", number,
|
||||
:min="template.min", :max="template.max", :step="template.step || 'any'",
|
||||
type="number", :name="name", @change="change")
|
||||
type="number", :name="name", @keyup="change")
|
||||
|
||||
input(v-if="template.type == 'int' && !template.values", number,
|
||||
v-model.number="view", :min="template.min", :max="template.max",
|
||||
type="number", :name="name", @change="change")
|
||||
type="number", :name="name", @keyup="change")
|
||||
|
||||
input(v-if="template.type == 'string'", v-model="view", type="text",
|
||||
:name="name", @change="change")
|
||||
:name="name", @keyup="change")
|
||||
|
||||
textarea(v-if="template.type == 'text'", v-model="view", :name="name",
|
||||
@change="change")
|
||||
|
||||
span.range(v-if="template.type == 'percent'")
|
||||
input(type="range", v-model="view", :name="name", number, min="0",
|
||||
max="100", step="1", @change="change")
|
||||
| {{view}}
|
||||
@keyup="change")
|
||||
|
||||
label.units {{units}}
|
||||
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
//- //
|
||||
//- This file is part of the Buildbotics firmware. //
|
||||
//- //
|
||||
//- Copyright (c) 2015 - 2018, Buildbotics LLC //
|
||||
//- All rights reserved. //
|
||||
//- //
|
||||
//- This file ("the software") is free software: you can redistribute it //
|
||||
//- and/or modify it under the terms of the GNU General Public License, //
|
||||
//- version 2 as published by the Free Software Foundation. You should //
|
||||
//- have received a copy of the GNU General Public License, version 2 //
|
||||
//- along with the software. If not, see <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- The software is distributed in the hope that it will be useful, but //
|
||||
//- WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
//- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
||||
//- Lesser General Public License for more details. //
|
||||
//- //
|
||||
//- You should have received a copy of the GNU Lesser General Public //
|
||||
//- License along with the software. If not, see //
|
||||
//- <http://www.gnu.org/licenses/>. //
|
||||
//- //
|
||||
//- For information regarding this software email: //
|
||||
//- "Joseph Coffland" <joseph@buildbotics.com> //
|
||||
//- //
|
||||
//-/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
script#tool-view-template(type="text/x-template")
|
||||
#tool
|
||||
h1 Tool Configuration
|
||||
@@ -70,7 +43,7 @@ script#tool-view-template(type="text/x-template")
|
||||
label.units RPM
|
||||
|
||||
fieldset.modbus-program(
|
||||
v-if="is_modbus && this.tool_type != 'HUANYANG VFD'")
|
||||
v-if="is_modbus && tool_type != 'HUANYANG VFD' && tool_type != 'EM60'")
|
||||
h2 Active Modbus Program
|
||||
p(v-if="$root.modified")
|
||||
| (Click #[tt(class="save") Save] to activate the selected
|
||||
@@ -407,3 +380,64 @@ script#tool-view-template(type="text/x-template")
|
||||
|
|
||||
| and spindle type. The VFD must be rebooted after changing
|
||||
| the above settings.
|
||||
|
||||
|
||||
.notes(v-if="tool_type.startsWith('EM60')")
|
||||
h2 Notes
|
||||
p Set the following using the VFD's front panel.
|
||||
table.modbus-regs.fixed-regs
|
||||
tr
|
||||
th Address
|
||||
th Value
|
||||
th Meaning
|
||||
th Description
|
||||
tr
|
||||
td.reg-addr P0.0.03
|
||||
td.reg-value 2
|
||||
td Modbus
|
||||
td Control mode
|
||||
tr
|
||||
td.reg-addr P0.0.04
|
||||
td.reg-value 9
|
||||
td Modbus
|
||||
td Frequency source A
|
||||
tr
|
||||
td.reg-addr P0.1.00
|
||||
td.reg-value 0
|
||||
td Source A
|
||||
td Frequency source
|
||||
tr
|
||||
td.reg-addr P4.1.00
|
||||
td.reg-value 3
|
||||
td 9600 BAUD
|
||||
td Must match #[tt baud] above
|
||||
tr
|
||||
td.reg-addr P4.1.01
|
||||
td.reg-value 0
|
||||
td 8N2
|
||||
td Data format
|
||||
tr
|
||||
td.reg-addr P4.1.02
|
||||
td.reg-value 1
|
||||
td Bus id
|
||||
td Must match #[tt bus-id] above
|
||||
tr
|
||||
td.reg-addr P4.1.03
|
||||
td.reg-value 2
|
||||
td No delay
|
||||
td Communications response delay
|
||||
tr
|
||||
td.reg-addr P4.1.04
|
||||
td.reg-value 0
|
||||
td No timeout
|
||||
td Communications timeout
|
||||
tr
|
||||
td.reg-addr P4.1.05
|
||||
td.reg-value 1
|
||||
td RTU
|
||||
td Modbus format
|
||||
p
|
||||
| Other settings according to the
|
||||
|
|
||||
a(href="https://buildbotics.com/upload/vfd/em60.pdf",
|
||||
target="_blank") EM60 VFD manual
|
||||
@@ -43,7 +43,7 @@ class Config(object):
|
||||
self.values = {}
|
||||
|
||||
try:
|
||||
self.version = pkg_resources.require('bbctrl')[0].version
|
||||
self.version = "1.2.1"
|
||||
|
||||
# Load config template
|
||||
with open(get_resource('http/config-template.json'), 'r',
|
||||
|
||||
@@ -1,52 +1,43 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of the Buildbotics firmware. #
|
||||
# #
|
||||
# Copyright (c) 2015 - 2018, Buildbotics LLC #
|
||||
# All rights reserved. #
|
||||
# #
|
||||
# This file ("the software") is free software: you can redistribute it #
|
||||
# and/or modify it under the terms of the GNU General Public License, #
|
||||
# version 2 as published by the Free Software Foundation. You should #
|
||||
# have received a copy of the GNU General Public License, version 2 #
|
||||
# along with the software. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# The software is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
|
||||
# Lesser General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU Lesser General Public #
|
||||
# License along with the software. If not, see #
|
||||
# <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# For information regarding this software email: #
|
||||
# "Joseph Coffland" <joseph@buildbotics.com> #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import bbctrl
|
||||
import glob
|
||||
import html
|
||||
import tornado
|
||||
from tornado import gen
|
||||
from tornado.web import HTTPError
|
||||
from tornado.escape import url_unescape
|
||||
|
||||
|
||||
def safe_remove(path):
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError: pass
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
@tornado.web.stream_request_body
|
||||
class FileHandler(bbctrl.APIHandler):
|
||||
def prepare(self): pass
|
||||
def prepare(self):
|
||||
if self.request.method == 'PUT':
|
||||
self.request.connection.set_max_body_size(2 ** 30)
|
||||
|
||||
filename = self.request.path.split('/')[-1]
|
||||
self.uploadFilename = url_unescape(filename) \
|
||||
.replace('\\', '/') \
|
||||
.replace('#', '-') \
|
||||
.replace('?', '-')
|
||||
|
||||
self.uploadFile = tempfile.NamedTemporaryFile("wb")
|
||||
|
||||
def data_received(self, data):
|
||||
if self.request.method == 'PUT':
|
||||
self.uploadFile.write(data)
|
||||
|
||||
def delete_ok(self, filename):
|
||||
if not filename:
|
||||
# Delete everything
|
||||
for path in glob.glob(self.get_upload('*')): safe_remove(path)
|
||||
for path in glob.glob(self.get_upload('*')):
|
||||
safe_remove(path)
|
||||
self.get_ctrl().preplanner.delete_all_plans()
|
||||
self.get_ctrl().state.clear_files()
|
||||
|
||||
@@ -57,26 +48,29 @@ class FileHandler(bbctrl.APIHandler):
|
||||
self.get_ctrl().preplanner.delete_plans(filename)
|
||||
self.get_ctrl().state.remove_file(filename)
|
||||
|
||||
|
||||
def put_ok(self, *args):
|
||||
gcode = self.request.files['gcode'][0]
|
||||
filename = os.path.basename(gcode['filename'].replace('\\', '/'))
|
||||
filename = filename.replace('#', '-').replace('?', '-')
|
||||
if not os.path.exists(self.get_upload()):
|
||||
os.mkdir(self.get_upload())
|
||||
|
||||
if not os.path.exists(self.get_upload()): os.mkdir(self.get_upload())
|
||||
filename = self.get_upload(self.uploadFilename).encode('utf8')
|
||||
safe_remove(filename)
|
||||
os.link(self.uploadFile.name, filename)
|
||||
|
||||
with open(self.get_upload(filename).encode('utf8'), 'wb') as f:
|
||||
f.write(gcode['body'])
|
||||
os.sync()
|
||||
self.uploadFile.close()
|
||||
|
||||
self.get_ctrl().preplanner.invalidate(filename)
|
||||
self.get_ctrl().state.add_file(filename)
|
||||
self.get_log('FileHandler').info('GCode received: ' + filename)
|
||||
del (self.uploadFile)
|
||||
|
||||
self.get_ctrl().preplanner.invalidate(self.uploadFilename)
|
||||
self.get_ctrl().state.add_file(self.uploadFilename)
|
||||
self.get_log('FileHandler').info(
|
||||
'GCode received: ' + self.uploadFilename)
|
||||
|
||||
del (self.uploadFilename)
|
||||
|
||||
@gen.coroutine
|
||||
def get(self, filename):
|
||||
if not filename: raise HTTPError(400, 'Missing filename')
|
||||
if not filename:
|
||||
raise HTTPError(400, 'Missing filename')
|
||||
filename = os.path.basename(filename)
|
||||
|
||||
try:
|
||||
@@ -84,6 +78,7 @@ class FileHandler(bbctrl.APIHandler):
|
||||
self.write(f.read())
|
||||
except Exception:
|
||||
self.get_ctrl().state.select_file('')
|
||||
raise HTTPError(400, "Unable to read file - doesn't appear to be GCode.")
|
||||
raise HTTPError(
|
||||
400, "Unable to read file - doesn't appear to be GCode.")
|
||||
|
||||
self.get_ctrl().state.select_file(filename)
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of the Buildbotics firmware. #
|
||||
# #
|
||||
# Copyright (c) 2015 - 2018, Buildbotics LLC #
|
||||
# All rights reserved. #
|
||||
# #
|
||||
# This file ("the software") is free software: you can redistribute it #
|
||||
# and/or modify it under the terms of the GNU General Public License, #
|
||||
# version 2 as published by the Free Software Foundation. You should #
|
||||
# have received a copy of the GNU General Public License, version 2 #
|
||||
# along with the software. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# The software is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
|
||||
# Lesser General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU Lesser General Public #
|
||||
# License along with the software. If not, see #
|
||||
# <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# For information regarding this software email: #
|
||||
# "Joseph Coffland" <joseph@buildbotics.com> #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import os
|
||||
import json
|
||||
import tornado
|
||||
@@ -34,9 +7,10 @@ import subprocess
|
||||
import socket
|
||||
from tornado.web import HTTPError
|
||||
from tornado import gen
|
||||
|
||||
import re
|
||||
import bbctrl
|
||||
|
||||
from urllib.request import urlopen
|
||||
import iw_parse
|
||||
|
||||
|
||||
def call_get_output(cmd):
|
||||
@@ -75,7 +49,7 @@ def check_password(password):
|
||||
class RebootHandler(bbctrl.APIHandler):
|
||||
def put_ok(self):
|
||||
self.get_ctrl().lcd.goodbye('Rebooting...')
|
||||
subprocess.Popen('reboot')
|
||||
subprocess.Popen(['reboot'])
|
||||
|
||||
class ShutdownHandler(bbctrl.APIHandler):
|
||||
def put_ok(self):
|
||||
@@ -153,38 +127,85 @@ class HostnameHandler(bbctrl.APIHandler):
|
||||
raise HTTPError(400, 'Failed to set hostname')
|
||||
|
||||
|
||||
class WifiHandler(bbctrl.APIHandler):
|
||||
def get(self):
|
||||
data = {'ssid': '', 'channel': 0}
|
||||
try:
|
||||
data = json.loads(call_get_output(['config-wifi', '-j']))
|
||||
except: pass
|
||||
self.write_json(data)
|
||||
class NetworkData(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
try:
|
||||
ipAddresses = subprocess.check_output(
|
||||
"ip -4 addr | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'", shell=True).decode().split()
|
||||
ipAddresses.remove("127.0.0.1")
|
||||
regex = re.compile(r'/255$/')
|
||||
filtered = [i for i in ipAddresses if not regex.match(i)]
|
||||
ipAddresses = filtered[0]
|
||||
except:
|
||||
ipAddresses = "Not Connected"
|
||||
try:
|
||||
wifi = subprocess.check_output(
|
||||
"sudo iw dev wlan0 info | grep ssid", shell=True).decode().split()
|
||||
wifi.pop(0)
|
||||
wifiName = " ".join(wifi)
|
||||
except:
|
||||
wifiName = "not connected"
|
||||
self.write_json({
|
||||
'ipAddresses': ipAddresses,
|
||||
'wifi': wifiName
|
||||
})
|
||||
|
||||
class NetworkHandler(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
try:
|
||||
ipAddresses = subprocess.check_output(
|
||||
"ip -4 addr | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'", shell=True).decode().split()
|
||||
ipAddresses.remove("127.0.0.1")
|
||||
regex = re.compile(r'/255$/')
|
||||
filtered = [i for i in ipAddresses if not regex.match(i)]
|
||||
ipAddresses = filtered[0]
|
||||
|
||||
except:
|
||||
ipAddresses = "Not Connected"
|
||||
|
||||
hostname = socket.gethostname()
|
||||
|
||||
try:
|
||||
wifi = json.loads(call_get_output(['config-wifi', '-j']))
|
||||
except:
|
||||
wifi = {'enabled': False}
|
||||
|
||||
try:
|
||||
lines = iw_parse.call_iwlist().decode("utf-8").split("\n")
|
||||
wifi['networks'] = iw_parse.get_parsed_cells(lines)
|
||||
except:
|
||||
wifi['networks'] = []
|
||||
|
||||
self.write_json({
|
||||
'ipAddresses': ipAddresses,
|
||||
'hostname': hostname,
|
||||
'wifi': wifi
|
||||
})
|
||||
|
||||
def put(self):
|
||||
if self.get_ctrl().args.demo:
|
||||
raise HTTPError(400, 'Cannot configure WiFi in demo mode')
|
||||
|
||||
if 'mode' in self.json:
|
||||
cmd = ['config-wifi', '-r']
|
||||
mode = self.json['mode']
|
||||
if not 'wifi' in self.json:
|
||||
raise HTTPError(400, 'Payload is missing wifi config information')
|
||||
|
||||
if mode == 'disabled': cmd += ['-d']
|
||||
elif 'ssid' in self.json:
|
||||
cmd += ['-s', self.json['ssid']]
|
||||
wifi = self.json['wifi']
|
||||
cmd = ['config-wifi', '-r']
|
||||
|
||||
if mode == 'ap':
|
||||
cmd += ['-a']
|
||||
if 'channel' in self.json:
|
||||
cmd += ['-c', self.json['channel']]
|
||||
if not wifi['enabled']:
|
||||
cmd += ['-d']
|
||||
else:
|
||||
if 'ssid' in wifi:
|
||||
cmd += ['-s', wifi['ssid']]
|
||||
|
||||
if 'pass' in self.json:
|
||||
cmd += ['-p', self.json['pass']]
|
||||
if 'password' in wifi:
|
||||
cmd += ['-p', wifi['password']]
|
||||
|
||||
if subprocess.call(cmd) == 0:
|
||||
self.write_json('ok')
|
||||
return
|
||||
if subprocess.call(cmd) == 0:
|
||||
self.write_json('ok')
|
||||
return
|
||||
|
||||
raise HTTPError(400, 'Failed to configure wifi')
|
||||
|
||||
@@ -407,6 +428,103 @@ class JogHandler(bbctrl.APIHandler):
|
||||
self.get_ctrl().mach.jog(self.json)
|
||||
|
||||
|
||||
displayRotatePattern = re.compile(r'display_rotate\s*=\s*(\d)')
|
||||
transformationMatrixPattern = re.compile(
|
||||
r'(\n)(\s+)(MatchIsTouchscreen.*?\n)(\s+Option\s+\"TransformationMatrix\".*?\n)(.*?EndSection)',
|
||||
re.DOTALL)
|
||||
matchIsTouchscreenPattern = re.compile(
|
||||
r'(\n)(\s+)(MatchIsTouchscreen.*?\n)(.*?EndSection)', re.DOTALL)
|
||||
class ScreenRotationHandler(bbctrl.APIHandler):
|
||||
|
||||
@gen.coroutine
|
||||
def get(self):
|
||||
with open("/boot/config.txt", 'rt') as config:
|
||||
lines = config.readlines()
|
||||
for line in lines:
|
||||
if line.startswith('display_rotate'):
|
||||
self.write_json({
|
||||
'rotated':
|
||||
int(displayRotatePattern.search(line).group(1)) != 0
|
||||
})
|
||||
return
|
||||
|
||||
self.write_json({'rotated': False})
|
||||
return
|
||||
|
||||
@gen.coroutine
|
||||
def put_ok(self):
|
||||
rotated = self.json['rotated']
|
||||
|
||||
subprocess.Popen([
|
||||
'/usr/local/bin/edit-boot-config',
|
||||
'display_rotate={}'.format(2 if rotated else 0)
|
||||
])
|
||||
|
||||
with open("/usr/share/X11/xorg.conf.d/40-libinput.conf",
|
||||
'rt') as config:
|
||||
text = config.read()
|
||||
text = transformationMatrixPattern.sub(r'\1\2\3\5', text)
|
||||
if rotated:
|
||||
text = matchIsTouchscreenPattern.sub(
|
||||
r'\1\2\3\2Option "TransformationMatrix" "-1 0 1 0 -1 1 0 0 1"\1\4',
|
||||
text)
|
||||
with open("/usr/share/X11/xorg.conf.d/40-libinput.conf",
|
||||
'wt') as config:
|
||||
config.write(text)
|
||||
|
||||
subprocess.run('reboot')
|
||||
|
||||
|
||||
class TimeHandler(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
timeinfo = call_get_output(['timedatectl'])
|
||||
timezones = call_get_output(
|
||||
['timedatectl', 'list-timezones', '--no-pager'])
|
||||
self.get_log('TimeHandler').info('Time stuff: {}, {}'.format(
|
||||
timeinfo, timezones))
|
||||
|
||||
self.write_json({'timeinfo': timeinfo, 'timezones': timezones})
|
||||
|
||||
def put_ok(self):
|
||||
datetime = self.json['datetime']
|
||||
timezone = self.json['timezone']
|
||||
subprocess.Popen(['timedatectl', 'set-time', datetime])
|
||||
subprocess.Popen(['timedatectl', 'set-timezone', timezone])
|
||||
|
||||
|
||||
class RemoteDiagnosticsHandler(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
code = self.get_query_argument("code", "")
|
||||
command = self.get_query_argument("command", "")
|
||||
|
||||
log = self.get_log('RemoteDiagnostics')
|
||||
|
||||
if command == "disconnect":
|
||||
subprocess.Popen(['killall', 'ngrok'])
|
||||
self.write_json({'message': "Succesfully disconnected"})
|
||||
|
||||
if command == "connect":
|
||||
try:
|
||||
url = 'https://tinyurl.com/1f-remote?code={}'.format(code)
|
||||
with urlopen(url) as response:
|
||||
body = response.read()
|
||||
|
||||
os.makedirs("/tmp/ngrok", exist_ok=True)
|
||||
with open("/tmp/ngrok/1f-ngrok.sh", 'wb') as f:
|
||||
f.write(body)
|
||||
|
||||
subprocess.Popen(['/bin/bash', "/tmp/ngrok/1f-ngrok.sh"])
|
||||
self.write_json({'success': True})
|
||||
except Exception as e:
|
||||
log.info("Failed: {}".format(str(e)))
|
||||
self.write_json({
|
||||
'success': False,
|
||||
'code': e.code or None,
|
||||
'message': e.reason or "Unknown"
|
||||
})
|
||||
|
||||
# Base class for Web Socket connections
|
||||
class ClientConnection(object):
|
||||
def __init__(self, app):
|
||||
@@ -517,7 +635,8 @@ class Web(tornado.web.Application):
|
||||
(r'/api/reboot', RebootHandler),
|
||||
(r'/api/shutdown', ShutdownHandler),
|
||||
(r'/api/hostname', HostnameHandler),
|
||||
(r'/api/wifi', WifiHandler),
|
||||
(r'/api/wifi', NetworkData),
|
||||
(r'/api/network', NetworkHandler),
|
||||
(r'/api/remote/username', UsernameHandler),
|
||||
(r'/api/remote/password', PasswordHandler),
|
||||
(r'/api/config/load', ConfigLoadHandler),
|
||||
@@ -544,6 +663,9 @@ class Web(tornado.web.Application):
|
||||
(r'/api/modbus/write', ModbusWriteHandler),
|
||||
(r'/api/jog', JogHandler),
|
||||
(r'/api/video', bbctrl.VideoHandler),
|
||||
(r'/api/screen-rotation', ScreenRotationHandler),
|
||||
(r'/api/time', TimeHandler),
|
||||
(r'/api/remote-diagnostics', RemoteDiagnosticsHandler),
|
||||
(r'/(.*)', StaticFileHandler,
|
||||
{'path': bbctrl.get_resource('http/'),
|
||||
'default_filename': 'index.html'}),
|
||||
|
||||
@@ -186,10 +186,10 @@ class Plan(object):
|
||||
|
||||
|
||||
def _run(self):
|
||||
start = time.clock()
|
||||
start = time.time()
|
||||
line = 0
|
||||
maxLine = 0
|
||||
maxLineTime = time.clock()
|
||||
maxLineTime = time.time()
|
||||
position = {axis: 0 for axis in 'xyz'}
|
||||
rapid = False
|
||||
|
||||
@@ -242,7 +242,7 @@ class Plan(object):
|
||||
line = cmd['value']
|
||||
if maxLine < line:
|
||||
maxLine = line
|
||||
maxLineTime = time.clock()
|
||||
maxLineTime = time.time()
|
||||
|
||||
elif cmd['name'] == 'speed':
|
||||
s = cmd['value']
|
||||
@@ -250,11 +250,11 @@ class Plan(object):
|
||||
|
||||
elif cmd['type'] == 'dwell': self.time += cmd['seconds']
|
||||
|
||||
if args.max_time < time.clock() - start:
|
||||
if args.max_time < time.time() - start:
|
||||
raise Exception('Max planning time (%d sec) exceeded.' %
|
||||
args.max_time)
|
||||
|
||||
if args.max_loop < time.clock() - maxLineTime:
|
||||
if args.max_loop < time.time() - maxLineTime:
|
||||
raise Exception('Max loop time (%d sec) exceeded.' %
|
||||
args.max_loop)
|
||||
|
||||
|
||||
80
src/py/iw_parse/README.md
Normal file
80
src/py/iw_parse/README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
iw_parse
|
||||
========
|
||||
|
||||
Parse the output of iwlist scan to get the name, address, quality, channel, and encryption type of all networks broadcasting within your Wireless NIC's reach.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* [pip](http://www.pip-installer.org/en/latest/installing.html "pip installation guide") - If you don't have pip installed, followed the link.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```bash
|
||||
pip install iw_parse
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```bash
|
||||
iwlist <INTERFACE_NAME> scan | iw_parse
|
||||
```
|
||||
|
||||
Replace `<INTERFACE_NAME>` with the system name for your wireless NIC. It is usually something like `wlan0`. The command `iwconfig` will list all of your network interfaces.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
iwlist wlan0 scan | iw_parse
|
||||
```
|
||||
|
||||
The result should look something like:
|
||||
|
||||
```
|
||||
Name Address Quality Channel Encryption
|
||||
wireless1 20:AA:4B:34:2C:F5 100 % 11 WEP
|
||||
wireless2 00:26:F2:1E:FC:03 84 % 1 WPA v.1
|
||||
wireless3 00:1D:D3:6A:3C:60 66 % 6 WEP
|
||||
wireless4 20:10:7A:E5:02:98 64 % 1 WEP
|
||||
wireless5 CC:A4:62:B7:D2:B0 54 % 8 WPA v.1
|
||||
wireless6 30:46:9A:53:3C:76 47 % 11 WPA v.1
|
||||
wireless7 A0:21:B7:5F:84:B0 44 % 11 WEP
|
||||
wireless8 04:A1:51:18:E8:E0 41 % 6 WPA v.1
|
||||
```
|
||||
|
||||
Example from Python shell:
|
||||
|
||||
```python
|
||||
>>> import iw_parse
|
||||
>>> networks = iw_parse.get_interfaces(interface='wlan0')
|
||||
>>> print networks
|
||||
[{'Address': 'F8:1E:DF:F9:B0:0B',
|
||||
'Channel': '3',
|
||||
'Encryption': 'WEP',
|
||||
'Name': 'Francis',
|
||||
'Bit Rates': '144 Mb/s',
|
||||
'Signal Level': '42',
|
||||
'Name': 'Francis',
|
||||
'Quality': '100'},
|
||||
{'Address': '86:1B:5E:33:17:D4',
|
||||
'Channel': '6',
|
||||
'Encryption': 'Open',
|
||||
'Bit Rates': '54 Mb/s',
|
||||
'Signal Level': '72',
|
||||
'Name': 'optimumwifi',
|
||||
'Quality': '100'},
|
||||
...
|
||||
```
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
* The vast majority of iw_parse was written by Hugo Chargois.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
iw_parse is free--as in BSD. Hack your heart out, hackers.
|
||||
1
src/py/iw_parse/__init__.py
Normal file
1
src/py/iw_parse/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .iw_parse import *
|
||||
25
src/py/iw_parse/iw_parse
Normal file
25
src/py/iw_parse/iw_parse
Normal file
@@ -0,0 +1,25 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
from iw_parse import get_parsed_cells, print_cells
|
||||
|
||||
def main():
|
||||
""" Pretty prints the output of iwlist scan into a table. """
|
||||
parsed_cells = get_parsed_cells(sys.stdin)
|
||||
|
||||
# You can choose which columns to display here, and most importantly
|
||||
# in what order. Of course, they must exist as keys in the dict rules.
|
||||
columns = [
|
||||
"Name",
|
||||
"Address",
|
||||
"Quality",
|
||||
"Channel",
|
||||
"Signal Level",
|
||||
"Encryption"
|
||||
]
|
||||
|
||||
print_cells(parsed_cells, columns)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
337
src/py/iw_parse/iw_parse.py
Normal file
337
src/py/iw_parse/iw_parse.py
Normal file
@@ -0,0 +1,337 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# Hugo Chargois - 17 jan. 2010 - v.0.1
|
||||
# Parses the output of iwlist scan into a table
|
||||
|
||||
# You can add or change the functions to parse the properties
|
||||
# of each AP (cell) below. They take one argument, the bunch of text
|
||||
# describing one cell in iwlist scan and return a property of that cell.
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
VERSION_RGX = re.compile("version\s+\d+", re.IGNORECASE)
|
||||
|
||||
def get_name(cell):
|
||||
""" Gets the name / essid of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The name / essid of the network.
|
||||
"""
|
||||
|
||||
essid = matching_line(cell, "ESSID:")
|
||||
if not essid:
|
||||
return ""
|
||||
return essid[1:-1]
|
||||
|
||||
def get_quality(cell):
|
||||
""" Gets the quality of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The quality of the network.
|
||||
"""
|
||||
|
||||
quality = matching_line(cell, "Quality=")
|
||||
if quality is None:
|
||||
return ""
|
||||
quality = quality.split()[0].split("/")
|
||||
quality = matching_line(cell, "Quality=").split()[0].split("/")
|
||||
return str(int(round(float(quality[0]) / float(quality[1]) * 100)))
|
||||
|
||||
def get_signal_level(cell):
|
||||
""" Gets the signal level of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The signal level of the network.
|
||||
"""
|
||||
|
||||
signal = matching_line(cell, "Signal level=")
|
||||
if signal is None:
|
||||
return ""
|
||||
signal = signal.split("=")[1].split("/")
|
||||
if len(signal) == 2:
|
||||
return str(int(round(float(signal[0]) / float(signal[1]) * 100)))
|
||||
elif len(signal) == 1:
|
||||
return signal[0].split(' ')[0]
|
||||
else:
|
||||
return ""
|
||||
|
||||
def get_noise_level(cell):
|
||||
""" Gets the noise level of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The noise level of the network.
|
||||
"""
|
||||
|
||||
noise = matching_line(cell, "Noise level=")
|
||||
if noise is None:
|
||||
return ""
|
||||
noise = noise.split("=")[1]
|
||||
return noise.split(' ')[0]
|
||||
|
||||
def get_channel(cell):
|
||||
""" Gets the channel of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The channel of the network.
|
||||
"""
|
||||
|
||||
channel = matching_line(cell, "Channel:")
|
||||
if channel:
|
||||
return channel
|
||||
frequency = matching_line(cell, "Frequency:")
|
||||
channel = re.sub(r".*\(Channel\s(\d{1,3})\).*", r"\1", frequency)
|
||||
return channel
|
||||
|
||||
def get_frequency(cell):
|
||||
""" Gets the frequency of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The frequency of the network.
|
||||
"""
|
||||
|
||||
frequency = matching_line(cell, "Frequency:")
|
||||
if frequency is None:
|
||||
return ""
|
||||
return frequency.split()[0]
|
||||
|
||||
def get_encryption(cell, emit_version=False):
|
||||
""" Gets the encryption type of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The encryption type of the network.
|
||||
"""
|
||||
|
||||
enc = ""
|
||||
if matching_line(cell, "Encryption key:") == "off":
|
||||
enc = "Open"
|
||||
else:
|
||||
for line in cell:
|
||||
matching = match(line,"IE:")
|
||||
if matching == None:
|
||||
continue
|
||||
|
||||
wpa = match(matching,"WPA")
|
||||
if wpa == None:
|
||||
continue
|
||||
|
||||
version_matches = VERSION_RGX.search(wpa)
|
||||
if len(version_matches.regs) == 1:
|
||||
version = version_matches \
|
||||
.group(0) \
|
||||
.lower() \
|
||||
.replace("version", "") \
|
||||
.strip()
|
||||
wpa = wpa.replace(version_matches.group(0), "").strip()
|
||||
if wpa == "":
|
||||
wpa = "WPA"
|
||||
if emit_version:
|
||||
enc = "{0} v.{1}".format(wpa, version)
|
||||
else:
|
||||
enc = wpa
|
||||
if wpa == "WPA2":
|
||||
return enc
|
||||
else:
|
||||
enc = wpa
|
||||
if enc == "":
|
||||
enc = "WEP"
|
||||
return enc
|
||||
|
||||
def get_mode(cell):
|
||||
""" Gets the mode of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The IEEE 802.11 mode of the network.
|
||||
"""
|
||||
|
||||
mode = matching_line(cell, "Extra:ieee_mode=")
|
||||
if mode is None:
|
||||
return ""
|
||||
return mode
|
||||
|
||||
def get_address(cell):
|
||||
""" Gets the address of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The address of the network.
|
||||
"""
|
||||
|
||||
return matching_line(cell, "Address: ")
|
||||
|
||||
def get_bit_rates(cell):
|
||||
""" Gets the bit rate of a network / cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
|
||||
@return string
|
||||
The bit rate of the network.
|
||||
"""
|
||||
|
||||
return matching_line(cell, "Bit Rates:")
|
||||
|
||||
# Here you can choose the way of sorting the table. sortby should be a key of
|
||||
# the dictionary rules.
|
||||
|
||||
def sort_cells(cells):
|
||||
sortby = "Quality"
|
||||
reverse = True
|
||||
cells.sort(key=lambda el:el[sortby], reverse=reverse)
|
||||
|
||||
|
||||
# Below here goes the boring stuff. You shouldn't have to edit anything below
|
||||
# this point
|
||||
|
||||
def matching_line(lines, keyword):
|
||||
""" Returns the first matching line in a list of lines.
|
||||
@see match()
|
||||
"""
|
||||
for line in lines:
|
||||
matching = match(line,keyword)
|
||||
if matching != None:
|
||||
return matching
|
||||
return None
|
||||
|
||||
def match(line, keyword):
|
||||
""" If the first part of line (modulo blanks) matches keyword,
|
||||
returns the end of that line. Otherwise checks if keyword is
|
||||
anywhere in the line and returns that section, else returns None"""
|
||||
|
||||
line = line.lstrip()
|
||||
length = len(keyword)
|
||||
if line[:length] == keyword:
|
||||
return line[length:]
|
||||
else:
|
||||
if keyword in line:
|
||||
return line[line.index(keyword):]
|
||||
else:
|
||||
return None
|
||||
|
||||
def parse_cell(cell, rules):
|
||||
""" Applies the rules to the bunch of text describing a cell.
|
||||
@param string cell
|
||||
A network / cell from iwlist scan.
|
||||
@param dictionary rules
|
||||
A dictionary of parse rules.
|
||||
|
||||
@return dictionary
|
||||
parsed networks. """
|
||||
|
||||
parsed_cell = {}
|
||||
for key in rules:
|
||||
rule = rules[key]
|
||||
parsed_cell.update({key: rule(cell)})
|
||||
return parsed_cell
|
||||
|
||||
def print_table(table):
|
||||
# Functional black magic.
|
||||
widths = list(map(max, map(lambda l: map(len, l), zip(*table))))
|
||||
|
||||
justified_table = []
|
||||
for line in table:
|
||||
justified_line = []
|
||||
for i, el in enumerate(line):
|
||||
justified_line.append(el.ljust(widths[i] + 2))
|
||||
justified_table.append(justified_line)
|
||||
|
||||
for line in justified_table:
|
||||
print("\t".join(line))
|
||||
|
||||
def print_cells(cells, columns):
|
||||
table = [columns]
|
||||
for cell in cells:
|
||||
cell_properties = []
|
||||
for column in columns:
|
||||
if column == 'Quality':
|
||||
# make print nicer
|
||||
cell[column] = cell[column].rjust(3) + " %"
|
||||
cell_properties.append(cell[column])
|
||||
table.append(cell_properties)
|
||||
print_table(table)
|
||||
|
||||
def get_parsed_cells(iw_data, rules=None):
|
||||
""" Parses iwlist output into a list of networks.
|
||||
@param list iw_data
|
||||
Output from iwlist scan.
|
||||
A list of strings.
|
||||
|
||||
@return list
|
||||
properties: Name, Address, Quality, Channel, Frequency, Encryption, Signal Level, Noise Level, Bit Rates, Mode.
|
||||
"""
|
||||
|
||||
# Here's a dictionary of rules that will be applied to the description
|
||||
# of each cell. The key will be the name of the column in the table.
|
||||
# The value is a function defined above.
|
||||
rules = rules or {
|
||||
"Name": get_name,
|
||||
"Quality": get_quality,
|
||||
"Channel": get_channel,
|
||||
"Frequency": get_frequency,
|
||||
"Encryption": get_encryption,
|
||||
"Address": get_address,
|
||||
"Signal Level": get_signal_level,
|
||||
"Noise Level": get_noise_level,
|
||||
"Bit Rates": get_bit_rates,
|
||||
"Mode": get_mode,
|
||||
}
|
||||
|
||||
cells = [[]]
|
||||
parsed_cells = []
|
||||
|
||||
for line in iw_data:
|
||||
cell_line = match(line, "Cell ")
|
||||
if cell_line != None:
|
||||
cells.append([])
|
||||
line = cell_line[-27:]
|
||||
cells[-1].append(line.rstrip())
|
||||
|
||||
cells = cells[1:]
|
||||
|
||||
for cell in cells:
|
||||
parsed_cells.append(parse_cell(cell, rules))
|
||||
|
||||
sort_cells(parsed_cells)
|
||||
return parsed_cells
|
||||
|
||||
def call_iwlist(interface='wlan0'):
|
||||
""" Get iwlist output via subprocess
|
||||
@param string interface
|
||||
interface to scan
|
||||
default is wlan0
|
||||
|
||||
@return string
|
||||
properties: iwlist output
|
||||
"""
|
||||
return subprocess.check_output(['iwlist', interface, 'scanning'])
|
||||
|
||||
def get_interfaces(interface="wlan0"):
|
||||
""" Get parsed iwlist output
|
||||
@param string interface
|
||||
interface to scan
|
||||
default is wlan0
|
||||
|
||||
@param list columns
|
||||
default data attributes to return
|
||||
|
||||
@return dict
|
||||
properties: dictionary of iwlist attributes
|
||||
"""
|
||||
return get_parsed_cells(call_iwlist(interface).split('\n'))
|
||||
|
||||
10
src/py/iw_parse/license.txt
Normal file
10
src/py/iw_parse/license.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Copyright (c) 2013, Cuzzo Yahn
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
"default": "METRIC"
|
||||
},
|
||||
"max-deviation": {
|
||||
"help":
|
||||
"Default allowed deviation from programmed path. Also see G64 & G61.",
|
||||
"help": "Default allowed deviation from programmed path. Also see G64 & G61.",
|
||||
"type": "float",
|
||||
"min": 0.05,
|
||||
"max": 100,
|
||||
@@ -15,8 +14,7 @@
|
||||
"default": 0.1
|
||||
},
|
||||
"junction-accel": {
|
||||
"help":
|
||||
"Higher values will increasing cornering speed but may cause stalls.",
|
||||
"help": "Higher values will increasing cornering speed but may cause stalls.",
|
||||
"type": "float",
|
||||
"min": 10000,
|
||||
"max": 100000000,
|
||||
@@ -34,10 +32,10 @@
|
||||
"type": "list",
|
||||
"index": "0123",
|
||||
"default": [
|
||||
{"axis": "X"},
|
||||
{"axis": "Y"},
|
||||
{"axis": "Z"},
|
||||
{"axis": "A"}
|
||||
{ "axis": "X" },
|
||||
{ "axis": "Y" },
|
||||
{ "axis": "Z" },
|
||||
{ "axis": "A" }
|
||||
],
|
||||
"template": {
|
||||
"general": {
|
||||
@@ -168,7 +166,13 @@
|
||||
"homing": {
|
||||
"homing-mode": {
|
||||
"type": "enum",
|
||||
"values": ["manual", "switch-min", "switch-max","stall-min","stall-max"],
|
||||
"values": [
|
||||
"manual",
|
||||
"switch-min",
|
||||
"switch-max",
|
||||
"stall-min",
|
||||
"stall-max"
|
||||
],
|
||||
"default": "manual",
|
||||
"code": "ho"
|
||||
},
|
||||
@@ -178,7 +182,7 @@
|
||||
"unit": "per full step",
|
||||
"default": 8,
|
||||
"code": "lm",
|
||||
"hmodes": ["stall-min","stall-max"]
|
||||
"hmodes": ["stall-min", "stall-max"]
|
||||
},
|
||||
"search-velocity": {
|
||||
"type": "float",
|
||||
@@ -188,7 +192,7 @@
|
||||
"scale": 0.0254,
|
||||
"default": 0.5,
|
||||
"code": "sv",
|
||||
"hmodes": ["switch-min", "switch-max", "stall-min","stall-max"]
|
||||
"hmodes": ["switch-min", "switch-max", "stall-min", "stall-max"]
|
||||
},
|
||||
"latch-velocity": {
|
||||
"type": "float",
|
||||
@@ -216,7 +220,7 @@
|
||||
"unit": "v",
|
||||
"default": 6,
|
||||
"code": "tv",
|
||||
"hmodes": ["stall-min","stall-max"]
|
||||
"hmodes": ["stall-min", "stall-max"]
|
||||
},
|
||||
"stall-sample-time": {
|
||||
"type": "int",
|
||||
@@ -224,7 +228,7 @@
|
||||
"default": 50,
|
||||
"unit": "µsec",
|
||||
"code": "sp",
|
||||
"hmodes": ["stall-min","stall-max"]
|
||||
"hmodes": ["stall-min", "stall-max"]
|
||||
},
|
||||
"stall-current": {
|
||||
"type": "float",
|
||||
@@ -232,7 +236,7 @@
|
||||
"unit": "amps",
|
||||
"default": 1.5,
|
||||
"code": "tc",
|
||||
"hmodes": ["stall-min","stall-max"]
|
||||
"hmodes": ["stall-min", "stall-max"]
|
||||
},
|
||||
"zero-backoff": {
|
||||
"type": "float",
|
||||
@@ -242,7 +246,7 @@
|
||||
"scale": 25.4,
|
||||
"default": 5,
|
||||
"code": "zb",
|
||||
"hmodes": ["switch-min", "switch-max", "stall-min","stall-max"]
|
||||
"hmodes": ["switch-min", "switch-max", "stall-min", "stall-max"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,7 +270,8 @@
|
||||
"YL600, YL620, YL620-A VFD (Beta)",
|
||||
"FR-D700 (Beta)",
|
||||
"Sunfar E300 (Beta)",
|
||||
"OMRON MX2"
|
||||
"OMRON MX2",
|
||||
"EM60"
|
||||
],
|
||||
"default": "Disabled",
|
||||
"code": "st"
|
||||
@@ -292,16 +297,30 @@
|
||||
},
|
||||
"tool-enable-mode": {
|
||||
"type": "enum",
|
||||
"values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri",
|
||||
"hi-tri"],
|
||||
"values": [
|
||||
"disabled",
|
||||
"lo-hi",
|
||||
"hi-lo",
|
||||
"tri-lo",
|
||||
"tri-hi",
|
||||
"lo-tri",
|
||||
"hi-tri"
|
||||
],
|
||||
"default": "lo-hi",
|
||||
"code": "eom",
|
||||
"pin": 15
|
||||
},
|
||||
"tool-direction-mode": {
|
||||
"type": "enum",
|
||||
"values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri",
|
||||
"hi-tri"],
|
||||
"values": [
|
||||
"disabled",
|
||||
"lo-hi",
|
||||
"hi-lo",
|
||||
"tri-lo",
|
||||
"tri-hi",
|
||||
"lo-tri",
|
||||
"hi-tri"
|
||||
],
|
||||
"default": "lo-hi",
|
||||
"code": "dom",
|
||||
"pin": 16
|
||||
@@ -342,12 +361,20 @@
|
||||
"values": [
|
||||
"disabled",
|
||||
"connect-write",
|
||||
"max-freq-read", "max-freq-fixed",
|
||||
"freq-set", "freq-signed-set",
|
||||
"stop-write", "forward-write", "reverse-write",
|
||||
"freq-read", "freq-signed-read", "freq-actech-read",
|
||||
"max-freq-read",
|
||||
"max-freq-fixed",
|
||||
"freq-set",
|
||||
"freq-signed-set",
|
||||
"freq-scaled-set",
|
||||
"stop-write",
|
||||
"forward-write",
|
||||
"reverse-write",
|
||||
"freq-read",
|
||||
"freq-signed-read",
|
||||
"freq-actech-read",
|
||||
"status-read",
|
||||
"disconnect-write"],
|
||||
"disconnect-write"
|
||||
],
|
||||
"default": "disabled",
|
||||
"code": "vt"
|
||||
},
|
||||
@@ -406,8 +433,7 @@
|
||||
"default": false
|
||||
},
|
||||
"dynamic-power": {
|
||||
"help":
|
||||
"Adjust tool power based on velocity and feed rate. Useful for LASERs.",
|
||||
"help": "Adjust tool power based on velocity and feed rate. Useful for LASERs.",
|
||||
"type": "bool",
|
||||
"default": false,
|
||||
"code": "dp"
|
||||
@@ -452,24 +478,45 @@
|
||||
"outputs": {
|
||||
"load-1": {
|
||||
"type": "enum",
|
||||
"values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri",
|
||||
"hi-tri"],
|
||||
"values": [
|
||||
"disabled",
|
||||
"lo-hi",
|
||||
"hi-lo",
|
||||
"tri-lo",
|
||||
"tri-hi",
|
||||
"lo-tri",
|
||||
"hi-tri"
|
||||
],
|
||||
"default": "lo-hi",
|
||||
"code": "1om",
|
||||
"pin": 2
|
||||
},
|
||||
"load-2": {
|
||||
"type": "enum",
|
||||
"values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri",
|
||||
"hi-tri"],
|
||||
"values": [
|
||||
"disabled",
|
||||
"lo-hi",
|
||||
"hi-lo",
|
||||
"tri-lo",
|
||||
"tri-hi",
|
||||
"lo-tri",
|
||||
"hi-tri"
|
||||
],
|
||||
"default": "lo-hi",
|
||||
"code": "2om",
|
||||
"pin": 1
|
||||
},
|
||||
"fault": {
|
||||
"type": "enum",
|
||||
"values": ["disabled", "lo-hi", "hi-lo", "tri-lo", "tri-hi", "lo-tri",
|
||||
"hi-tri"],
|
||||
"values": [
|
||||
"disabled",
|
||||
"lo-hi",
|
||||
"hi-lo",
|
||||
"tri-lo",
|
||||
"tri-hi",
|
||||
"lo-tri",
|
||||
"hi-tri"
|
||||
],
|
||||
"default": "lo-hi",
|
||||
"code": "fom",
|
||||
"pin": 21
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 250 KiB |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.9",
|
||||
"version": "1.2.1",
|
||||
"settings": {
|
||||
"junction-accel": 200000,
|
||||
"max-deviation": 0.05,
|
||||
|
||||
2
src/static/js/three.min.js
vendored
2
src/static/js/three.min.js
vendored
@@ -172,7 +172,7 @@ c.isSpriteMaterial?(t.diffuse.value=c.color,t.opacity.value=c.opacity,t.rotation
|
||||
b.envMap,a.flipEnvMap.value=b.envMap&&b.envMap.isCubeTexture?-1:1,a.reflectivity.value=b.reflectivity,a.refractionRatio.value=b.refractionRatio,a.maxMipLevel.value=Ca.get(b.envMap).__maxMipLevel);b.lightMap&&(a.lightMap.value=b.lightMap,a.lightMapIntensity.value=b.lightMapIntensity);b.aoMap&&(a.aoMap.value=b.aoMap,a.aoMapIntensity.value=b.aoMapIntensity);if(b.map)var c=b.map;else b.specularMap?c=b.specularMap:b.displacementMap?c=b.displacementMap:b.normalMap?c=b.normalMap:b.bumpMap?c=b.bumpMap:b.roughnessMap?
|
||||
c=b.roughnessMap:b.metalnessMap?c=b.metalnessMap:b.alphaMap?c=b.alphaMap:b.emissiveMap&&(c=b.emissiveMap);void 0!==c&&(c.isWebGLRenderTarget&&(c=c.texture),!0===c.matrixAutoUpdate&&c.updateMatrix(),a.uvTransform.value.copy(c.matrix))}function r(a,b){a.specular.value=b.specular;a.shininess.value=Math.max(b.shininess,1E-4);b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap);b.bumpMap&&(a.bumpMap.value=b.bumpMap,a.bumpScale.value=b.bumpScale,1===b.side&&(a.bumpScale.value*=-1));b.normalMap&&(a.normalMap.value=
|
||||
b.normalMap,a.normalScale.value.copy(b.normalScale),1===b.side&&a.normalScale.value.negate());b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias)}function v(a,b){a.roughness.value=b.roughness;a.metalness.value=b.metalness;b.roughnessMap&&(a.roughnessMap.value=b.roughnessMap);b.metalnessMap&&(a.metalnessMap.value=b.metalnessMap);b.emissiveMap&&(a.emissiveMap.value=b.emissiveMap);b.bumpMap&&(a.bumpMap.value=
|
||||
b.bumpMap,a.bumpScale.value=b.bumpScale,1===b.side&&(a.bumpScale.value*=-1));b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),1===b.side&&a.normalScale.value.negate());b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias);b.envMap&&(a.envMapIntensity.value=b.envMapIntensity)}console.log("THREE.WebGLRenderer","96");a=a||{};var y=void 0!==a.canvas?a.canvas:document.createElementNS("http://www.w3.org/1999/xhtml",
|
||||
b.bumpMap,a.bumpScale.value=b.bumpScale,1===b.side&&(a.bumpScale.value*=-1));b.normalMap&&(a.normalMap.value=b.normalMap,a.normalScale.value.copy(b.normalScale),1===b.side&&a.normalScale.value.negate());b.displacementMap&&(a.displacementMap.value=b.displacementMap,a.displacementScale.value=b.displacementScale,a.displacementBias.value=b.displacementBias);b.envMap&&(a.envMapIntensity.value=b.envMapIntensity)}a=a||{};var y=void 0!==a.canvas?a.canvas:document.createElementNS("http://www.w3.org/1999/xhtml",
|
||||
"canvas"),x=void 0!==a.context?a.context:null,w=void 0!==a.alpha?a.alpha:!1,G=void 0!==a.depth?a.depth:!0,D=void 0!==a.stencil?a.stencil:!0,O=void 0!==a.antialias?a.antialias:!1,S=void 0!==a.premultipliedAlpha?a.premultipliedAlpha:!0,E=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,z=void 0!==a.powerPreference?a.powerPreference:"default",A=null,B=null;this.domElement=y;this.context=null;this.sortObjects=this.autoClearStencil=this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.clippingPlanes=
|
||||
[];this.localClippingEnabled=!1;this.gammaFactor=2;this.physicallyCorrectLights=this.gammaOutput=this.gammaInput=!1;this.toneMappingWhitePoint=this.toneMappingExposure=this.toneMapping=1;this.maxMorphTargets=8;this.maxMorphNormals=4;var P=this,I=!1,F=null,L=null,M=null,Q=-1;var H=b=null;var U=!1;var V=null,Z=null,T=new aa,zc=new aa,Y=null,fa=0,X=y.width,N=y.height,W=1,cb=new aa(0,0,X,N),ha=new aa(0,0,X,N),ra=!1,pa=new od,ba=new Nf,qd=!1,Xd=!1,yc=new J,db=new p;try{w={alpha:w,depth:G,stencil:D,antialias:O,
|
||||
premultipliedAlpha:S,preserveDrawingBuffer:E,powerPreference:z};y.addEventListener("webglcontextlost",d,!1);y.addEventListener("webglcontextrestored",e,!1);var C=x||y.getContext("webgl",w)||y.getContext("experimental-webgl",w);if(null===C){if(null!==y.getContext("webgl"))throw Error("Error creating WebGL context with your selected attributes.");throw Error("Error creating WebGL context.");}void 0===C.getShaderPrecisionFormat&&(C.getShaderPrecisionFormat=function(){return{rangeMin:1,rangeMax:1,precision:1}})}catch(Lg){console.error("THREE.WebGLRenderer: "+
|
||||
|
||||
@@ -394,7 +394,7 @@
|
||||
var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]';
|
||||
|
||||
// detect devtools
|
||||
var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
|
||||
var devtools = false;
|
||||
|
||||
// UA sniffing for working around browser-specific quirks
|
||||
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
|
||||
@@ -9677,14 +9677,5 @@ var template = Object.freeze({
|
||||
|
||||
Vue.version = '1.0.17';
|
||||
|
||||
// devtools global hook
|
||||
/* istanbul ignore next */
|
||||
if (devtools) {
|
||||
devtools.emit('init', Vue);
|
||||
} else if ('development' !== 'production' && inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)) {
|
||||
console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools');
|
||||
}
|
||||
|
||||
return Vue;
|
||||
|
||||
}));
|
||||
@@ -4,6 +4,9 @@ body
|
||||
[v-cloak]
|
||||
display none
|
||||
|
||||
.menu-link
|
||||
z-index unset
|
||||
|
||||
tt
|
||||
color #000
|
||||
background #eee
|
||||
@@ -32,22 +35,63 @@ tt
|
||||
height 140px
|
||||
padding 0
|
||||
|
||||
.header-content
|
||||
max-width 90%
|
||||
margin auto
|
||||
text-align left
|
||||
.nav-header
|
||||
padding-left 60px
|
||||
display flex
|
||||
|
||||
.estop
|
||||
float right
|
||||
margin 5px
|
||||
.brand
|
||||
display flex
|
||||
flex-direction row
|
||||
align-self center
|
||||
white-space nowrap
|
||||
|
||||
img
|
||||
width 300px
|
||||
height 15%
|
||||
|
||||
.version
|
||||
font-size 18pt
|
||||
color #777
|
||||
display flex
|
||||
flex-direction column
|
||||
justify-content space-evenly
|
||||
border-left #777 2px solid;
|
||||
margin-left 15px
|
||||
padding 0px 10px
|
||||
font-weight bold
|
||||
|
||||
.upgrade-link
|
||||
margin-left 20px
|
||||
font-size 16pt
|
||||
align-self center
|
||||
color blue
|
||||
|
||||
.upgrade-attention
|
||||
color red
|
||||
font-size 18pt
|
||||
align-self center
|
||||
margin-left 5px
|
||||
|
||||
.pi-temp-warning
|
||||
align-self center
|
||||
font-size 30pt
|
||||
font-family Audiowide
|
||||
display inline
|
||||
margin 0 30px
|
||||
|
||||
.left
|
||||
color #444
|
||||
.right
|
||||
color #e5aa3d
|
||||
|
||||
.whitespace
|
||||
flex-grow 1
|
||||
|
||||
.video
|
||||
position relative
|
||||
float right
|
||||
width 174px
|
||||
height 130px
|
||||
margin 2px 5px
|
||||
border 2px solid #fff
|
||||
border 2px solid transparent
|
||||
border-radius 5px
|
||||
|
||||
&:hover
|
||||
@@ -87,29 +131,9 @@ tt
|
||||
width 100%
|
||||
height 100%
|
||||
|
||||
.banner
|
||||
float left
|
||||
padding-top 40px
|
||||
white-space nowrap
|
||||
|
||||
img
|
||||
vertical-align top
|
||||
|
||||
.title
|
||||
font-size 30pt
|
||||
font-family Audiowide
|
||||
display inline
|
||||
margin-right 0.5em
|
||||
|
||||
.left
|
||||
color #444
|
||||
.right
|
||||
color #e5aa3d
|
||||
|
||||
.subtitle
|
||||
font-size 18pt
|
||||
font-weight 100
|
||||
color #aaa
|
||||
.estop
|
||||
align-self center
|
||||
margin 0 30px
|
||||
|
||||
.error
|
||||
background red
|
||||
@@ -214,8 +238,6 @@ span.unit
|
||||
.pure-control-group
|
||||
label.units
|
||||
width 6em
|
||||
|
||||
label.units
|
||||
text-align left
|
||||
|
||||
textarea
|
||||
@@ -230,6 +252,7 @@ span.unit
|
||||
padding 0.7em 1em
|
||||
border-radius 3px
|
||||
display inline-block
|
||||
|
||||
|
||||
@keyframes blink
|
||||
50%
|
||||
@@ -267,6 +290,12 @@ span.unit
|
||||
// The jogging buttons, etc.
|
||||
.control-buttons button
|
||||
font-size 150%
|
||||
width 100px
|
||||
height 100px
|
||||
|
||||
.jog-units
|
||||
font-size initial
|
||||
margin-left 5px
|
||||
|
||||
&:first-child
|
||||
margin 0.5em 0
|
||||
@@ -335,6 +364,7 @@ span.unit
|
||||
.axis
|
||||
.name
|
||||
text-transform capitalize
|
||||
vertical-align middle
|
||||
|
||||
.name, .position
|
||||
font-size 24pt
|
||||
@@ -421,7 +451,7 @@ span.unit
|
||||
min-width 8em
|
||||
width 100%
|
||||
|
||||
.mach_units
|
||||
.units
|
||||
padding 0
|
||||
|
||||
select
|
||||
@@ -848,18 +878,6 @@ tt.save
|
||||
text-decoration none
|
||||
|
||||
|
||||
.upgrade-version
|
||||
display inline-block
|
||||
border-radius 4px
|
||||
padding 2px
|
||||
margin-left 0.5em
|
||||
color #555
|
||||
background-color #e5aa3d
|
||||
text-decoration none
|
||||
|
||||
&:hover
|
||||
color #fff
|
||||
|
||||
.modal-mask
|
||||
position fixed
|
||||
z-index 9998
|
||||
|
||||
24
src/svelte-components/.gitignore
vendored
Normal file
24
src/svelte-components/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
48
src/svelte-components/README.md
Normal file
48
src/svelte-components/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Svelte + TS + Vite
|
||||
|
||||
This template should help get you started developing with Svelte and TypeScript in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
|
||||
|
||||
## Need an official Svelte framework?
|
||||
|
||||
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
|
||||
|
||||
## Technical considerations
|
||||
|
||||
**Why use this over SvelteKit?**
|
||||
|
||||
- It brings its own routing solution which might not be preferable for some users.
|
||||
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
|
||||
`vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example.
|
||||
|
||||
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
|
||||
|
||||
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
|
||||
|
||||
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
|
||||
|
||||
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
|
||||
|
||||
**Why include `.vscode/extensions.json`?**
|
||||
|
||||
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
|
||||
|
||||
**Why enable `allowJs` in the TS template?**
|
||||
|
||||
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
|
||||
|
||||
**Why is HMR not preserving my local component state?**
|
||||
|
||||
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
|
||||
|
||||
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
|
||||
|
||||
```ts
|
||||
// store.ts
|
||||
// An extremely simple external store
|
||||
import { writable } from 'svelte/store'
|
||||
export default writable(0)
|
||||
```
|
||||
13
src/svelte-components/index.html
Normal file
13
src/svelte-components/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Svelte + TS + Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
10382
src/svelte-components/package-lock.json
generated
Normal file
10382
src/svelte-components/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
src/svelte-components/package.json
Normal file
31
src/svelte-components/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "svelte-components",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"postbuild": "smui-theme compile dist/smui.css -i src/theme",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/kit": "^1.0.0-next.392",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"node-sass": "^7.0.1",
|
||||
"polyfill-object.fromentries": "^1.0.1",
|
||||
"smui-theme": "^6.0.0-beta.16",
|
||||
"string.prototype.matchall": "^4.0.7",
|
||||
"svelte": "^3.49.0",
|
||||
"svelte-check": "^2.8.0",
|
||||
"svelte-icon": "^1.2.4",
|
||||
"svelte-material-ui": "^6.0.0-beta.16",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"svelte-tiny-virtual-list": "^2.0.5",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^3.0.2"
|
||||
}
|
||||
}
|
||||
BIN
src/svelte-components/public/favicon.ico
Normal file
BIN
src/svelte-components/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
227
src/svelte-components/src/components/AdminNetworkView.svelte
Normal file
227
src/svelte-components/src/components/AdminNetworkView.svelte
Normal file
@@ -0,0 +1,227 @@
|
||||
<script lang="ts">
|
||||
import WifiConnectionDialog from "$dialogs/WifiConnectionDialog.svelte";
|
||||
import ChangeHostnameDialog from "$dialogs/ChangeHostnameDialog.svelte";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import List, { Item, Graphic, Text, Meta } from "@smui/list";
|
||||
import Card from "@smui/card";
|
||||
import { networkInfo, type WifiNetwork } from "$lib/NetworkInfo";
|
||||
import {GET} from "$lib/api"
|
||||
import {processNetworkInfo} from "$lib/NetworkInfo"
|
||||
|
||||
let networkData = GET("network")
|
||||
networkData.then(value=>processNetworkInfo(value))
|
||||
|
||||
let changeHostnameDialog = {
|
||||
open: false,
|
||||
};
|
||||
|
||||
let wifiConnectionDialog = {
|
||||
open: false,
|
||||
network: {} as WifiNetwork,
|
||||
};
|
||||
|
||||
function refreshWifi(){
|
||||
let networkData = GET("network")
|
||||
networkData.then(value=>processNetworkInfo(value))
|
||||
}
|
||||
|
||||
function getWifiStrengthStyle(network: WifiNetwork) {
|
||||
const strength = Math.ceil((Number(network.Quality) / 100) * 4);
|
||||
|
||||
switch (strength) {
|
||||
case 0:
|
||||
return "clip-path: circle(0px at 12.5px 19px);";
|
||||
|
||||
case 1:
|
||||
return "clip-path: circle(4px at 12.5px 19px);";
|
||||
|
||||
case 2:
|
||||
return "clip-path: circle(8px at 12.5px 19px);";
|
||||
|
||||
case 3:
|
||||
return "clip-path: circle(14px at 12.5px 19px);";
|
||||
|
||||
case 4:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeHostname() {
|
||||
changeHostnameDialog = {
|
||||
open: true,
|
||||
};
|
||||
}
|
||||
|
||||
function onNetworkSelected(network: WifiNetwork) {
|
||||
wifiConnectionDialog = {
|
||||
open: true,
|
||||
network,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<WifiConnectionDialog {...wifiConnectionDialog} />
|
||||
<ChangeHostnameDialog {...changeHostnameDialog} />
|
||||
|
||||
<div class="admin-network-view">
|
||||
<div style="display:flex; flex-direction:row; justify-content:space-between" >
|
||||
<h1>Network Info</h1>
|
||||
<div style="text-align: center; padding:20px">
|
||||
<Button on:click={refreshWifi} touch variant="raised">
|
||||
<Label>Refresh WiFi</Label>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-control-group">
|
||||
<label for="hostname">Hostname</label>
|
||||
<Card id="hostname" variant="outlined">
|
||||
<Text id="hostname">
|
||||
{$networkInfo.hostname}
|
||||
</Text>
|
||||
</Card>
|
||||
<Button on:click={onChangeHostname} touch variant="raised">
|
||||
<Label>Change</Label>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-control-group">
|
||||
<label for="ip-addresses">IP Addresses</label>
|
||||
<Card id="ip-addresses" variant="outlined">
|
||||
<!-- {#each $networkInfo.ipAddresses as ipAddress} -->
|
||||
<div>
|
||||
<Text id="hostname">
|
||||
{$networkInfo.ipAddresses}
|
||||
</Text>
|
||||
</div>
|
||||
<!-- {/each} -->
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<div class="pure-control-group">
|
||||
<label for="wifi">Wi-Fi</label>
|
||||
<div class="wifi-networks">
|
||||
<Card id="wifi" variant="outlined">
|
||||
<List>
|
||||
{#if $networkInfo.wifi.networks.length === 0}
|
||||
<Item class="wifi-network">
|
||||
<Text>Scanning...</Text>
|
||||
</Item>
|
||||
{:else}
|
||||
{#each $networkInfo.wifi.networks as network}
|
||||
<Item
|
||||
class="wifi-network"
|
||||
on:SMUI:action={() =>
|
||||
onNetworkSelected(network)}
|
||||
>
|
||||
<Graphic
|
||||
class="strength {$networkInfo.wifi
|
||||
.ssid === network.Name
|
||||
? 'active'
|
||||
: ''}"
|
||||
>
|
||||
<span class="fa fa-wifi background" />
|
||||
<span
|
||||
class="fa fa-wifi"
|
||||
style={getWifiStrengthStyle(
|
||||
network
|
||||
)}
|
||||
/>
|
||||
</Graphic>
|
||||
<Text style="margin-right: 20px;"
|
||||
>{network.Name}</Text
|
||||
>
|
||||
{#if network.Encryption !== "Open"}
|
||||
<Meta>
|
||||
<span class="fa fa-lock" />
|
||||
</Meta>
|
||||
{/if}
|
||||
</Item>
|
||||
{/each}
|
||||
{/if}
|
||||
</List>
|
||||
</Card>
|
||||
<em style="display: block;">
|
||||
Click on a Wi-Fi network to connect or disconnect.
|
||||
</em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
$primary: #0078e7;
|
||||
$very-dark: #555;
|
||||
$text: #777;
|
||||
$grey: #bbb;
|
||||
$light: #ddd;
|
||||
|
||||
:global {
|
||||
.admin-network-view {
|
||||
.pure-form-aligned .pure-control-group label {
|
||||
vertical-align: top;
|
||||
font-size: 15pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mdc-card {
|
||||
width: 400px;
|
||||
min-height: 38px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-bottom: 20px;
|
||||
margin-right: 20px;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
.wifi-networks {
|
||||
display: inline-block;
|
||||
|
||||
.mdc-card {
|
||||
padding: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.wifi-network {
|
||||
.lock {
|
||||
font-size: 20px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.strength {
|
||||
border-radius: 50%;
|
||||
padding: 3px;
|
||||
background-color: $light;
|
||||
color: $very-dark;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
background-color: $primary;
|
||||
color: white;
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
font-size: 22px;
|
||||
|
||||
&.background {
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
195
src/svelte-components/src/components/ConfigTemplatedInput.svelte
Normal file
195
src/svelte-components/src/components/ConfigTemplatedInput.svelte
Normal file
@@ -0,0 +1,195 @@
|
||||
<script lang="ts">
|
||||
import configTemplate from "../../../resources/config-template.json";
|
||||
import { Config, DisplayUnits } from "$lib/ConfigStore";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
type ValueType =
|
||||
| string
|
||||
| number
|
||||
| { title: string; value: string | number };
|
||||
|
||||
type Template = {
|
||||
type?: string;
|
||||
values?: Array<ValueType>;
|
||||
unit?: "string";
|
||||
iunit?: "string";
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
help?: string;
|
||||
default?: string | number;
|
||||
scale?: number;
|
||||
};
|
||||
|
||||
const namesByKey = {
|
||||
"units":"units",
|
||||
"probing-prompts": "Show safety prompts",
|
||||
"probe-xdim": "Probe block width",
|
||||
"probe-ydim": "Probe block length",
|
||||
"probe-zdim": "Probe block height",
|
||||
"probe-fast-seek": "Fast seek speed",
|
||||
"probe-slow-seek": "Slow seek speed",
|
||||
"program-start": "On program start",
|
||||
"tool-change": "On tool change",
|
||||
"program-end": "On program end",
|
||||
"max-deviation": "Maximum deviation",
|
||||
"junction-accel": "Junction acceleration",
|
||||
};
|
||||
|
||||
export let key: string;
|
||||
let keyParts: string[];
|
||||
let template: Template;
|
||||
let name: string;
|
||||
let title: string;
|
||||
let units: string;
|
||||
let value;
|
||||
|
||||
onMount(() => {
|
||||
keyParts = (key || "").split(".");
|
||||
template = getTemplate();
|
||||
title = getTitle();
|
||||
name = keyParts[keyParts.length - 1];
|
||||
name = namesByKey[name] || name;
|
||||
value = getValue();
|
||||
});
|
||||
|
||||
$: metric = $DisplayUnits === "METRIC";
|
||||
$: if (template) {
|
||||
units = metric || !template.iunit ? template.unit : template.iunit;
|
||||
}
|
||||
|
||||
function getTemplate(): Template {
|
||||
let template = configTemplate;
|
||||
for (const part of keyParts) {
|
||||
template = template[part];
|
||||
}
|
||||
|
||||
return template as Template;
|
||||
}
|
||||
|
||||
function getTitle(): string {
|
||||
const help = (!!template.help) ? `${template.help}\n` : "";
|
||||
return `${template.help}\n Default: ${template.default} ${template.unit || ""}`;
|
||||
}
|
||||
|
||||
function getValue(): string | number {
|
||||
let value: any = $Config;
|
||||
for (const part of keyParts) {
|
||||
value = value[part];
|
||||
}
|
||||
|
||||
if (template.scale) {
|
||||
if (metric) {
|
||||
return Number.parseFloat(value.toFixed(3));
|
||||
}
|
||||
|
||||
return Number.parseFloat((value / template.scale).toFixed(4));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function onChange(event) {
|
||||
Config.update((config) => {
|
||||
let target = config;
|
||||
for (const part of keyParts.slice(0, -1)) {
|
||||
target = target[part];
|
||||
}
|
||||
|
||||
const value = getValueFromElement(event.target);
|
||||
target[keyParts[keyParts.length - 1]] = value;
|
||||
|
||||
return config;
|
||||
});
|
||||
|
||||
ControllerMethods.dispatch("config-changed");
|
||||
}
|
||||
|
||||
function getValueFromElement(element) {
|
||||
switch (template.type) {
|
||||
case "float":
|
||||
case "int":
|
||||
return Number(element.value);
|
||||
|
||||
case "bool":
|
||||
return element.checked;
|
||||
|
||||
default:
|
||||
return element.value;
|
||||
}
|
||||
}
|
||||
|
||||
function getOptionValue(opt: ValueType) {
|
||||
switch (typeof opt) {
|
||||
case "object":
|
||||
return opt.value || opt;
|
||||
|
||||
default:
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
|
||||
function getOptionTitle(opt: ValueType) {
|
||||
switch (typeof opt) {
|
||||
case "object":
|
||||
return opt.title || opt;
|
||||
|
||||
default:
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if template}
|
||||
<div class="pure-control-group" {title}>
|
||||
<label for={name}>{name}</label>
|
||||
|
||||
{#if template.values}
|
||||
<select {name} bind:value on:change={onChange}>
|
||||
{#each template.values as opt}
|
||||
<option
|
||||
value={getOptionValue(opt)}
|
||||
disabled={opt === "-----"}
|
||||
>
|
||||
{getOptionTitle(opt)}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else if template.type === "bool"}
|
||||
<input
|
||||
{name}
|
||||
type="checkbox"
|
||||
checked={value}
|
||||
on:change={onChange}
|
||||
/>
|
||||
{:else if template.type === "float"}
|
||||
<input
|
||||
{name}
|
||||
type="number"
|
||||
min={template.min}
|
||||
max={template.max}
|
||||
step={template.step || "any"}
|
||||
bind:value
|
||||
on:keyup={onChange}
|
||||
/>
|
||||
{:else if template.type === "int"}
|
||||
<input
|
||||
{name}
|
||||
type="number"
|
||||
min={template.min}
|
||||
max={template.max}
|
||||
bind:value
|
||||
on:keyup={onChange}
|
||||
/>
|
||||
{:else if template.type === "string"}
|
||||
<input {name} type="text" bind:value on:keyup={onChange} />
|
||||
{:else if template.type == "text"}
|
||||
<textarea {name} bind:value on:keyup={onChange} />
|
||||
{/if}
|
||||
|
||||
<label for="" class="units">{units || ""}</label>
|
||||
|
||||
<slot name="extra" />
|
||||
</div>
|
||||
{/if}
|
||||
62
src/svelte-components/src/components/HelpView.svelte
Normal file
62
src/svelte-components/src/components/HelpView.svelte
Normal file
@@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import RemoteDiagnosticsDialog from "$dialogs/RemoteDiagnosticsDialog.svelte";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
let showRemoteDiagnosticsDialog = false;
|
||||
</script>
|
||||
|
||||
<RemoteDiagnosticsDialog bind:open={showRemoteDiagnosticsDialog} />
|
||||
|
||||
<h2>Support & Contact Info</h2>
|
||||
<p>
|
||||
Please visit
|
||||
<a href="https://onefinitycnc.com/support" target="_blank">
|
||||
onefinitycnc.com/support
|
||||
</a>
|
||||
for a variety of support resources, and to find our contact information.
|
||||
</p>
|
||||
|
||||
<!-- <Button
|
||||
touch
|
||||
variant="raised"
|
||||
on:click={() => (showRemoteDiagnosticsDialog = true)}
|
||||
>
|
||||
<Label>Remote Diagnostics</Label>
|
||||
</Button> -->
|
||||
|
||||
<h2>Discussion Forum</h2>
|
||||
<p>
|
||||
Check out our support and discussion forum at
|
||||
<a href="https://forum.onefinitycnc.com" target="_blank"
|
||||
>forum.onefinitycnc.com</a
|
||||
>. Register on the site and post a message. We can't wait to hear from you.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
We also maintain a list of
|
||||
<a
|
||||
href="https://forum.onefinitycnc.com/t/what-cad-cam-software-can-be-used-to-make-gcode-files-for-the-onefinity-cnc/10253"
|
||||
target="_blank"
|
||||
>
|
||||
recommended software packages
|
||||
</a>
|
||||
on the forum.
|
||||
</p>
|
||||
|
||||
<h2>Credits & Acknowledgements</h2>
|
||||
<h4 style="margin-bottom: 0;">Artwork</h4>
|
||||
<p style="margin-top: 0;">
|
||||
Special thanks to
|
||||
<a href="https://www.instagram.com/fierysquirrelart/" target="_blank">
|
||||
@fierysquirrelart
|
||||
</a>
|
||||
for many of the graphics used in the controller.
|
||||
</p>
|
||||
|
||||
<h4 style="margin-bottom: 0;">Buildbotics</h4>
|
||||
<p style="margin-top: 0;">
|
||||
This controller is based on the
|
||||
<a href="http://buildbotics.com" target="_blank"
|
||||
>Buildbotics CNC Controller</a
|
||||
>.
|
||||
</p>
|
||||
129
src/svelte-components/src/components/SettingsView.svelte
Normal file
129
src/svelte-components/src/components/SettingsView.svelte
Normal file
@@ -0,0 +1,129 @@
|
||||
<script lang="ts">
|
||||
import configTemplate from "../../../resources/config-template.json";
|
||||
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
||||
import ConfigTemplatedInput from "./ConfigTemplatedInput.svelte";
|
||||
import SetTimeDialog from "$dialogs/SetTimeDialog.svelte";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
const gcodeURL = "https://linuxcnc.org/docs/html/gcode/g-code.html";
|
||||
|
||||
let showScreenRotationDialog = false;
|
||||
let showSetTimeDialog = false;
|
||||
</script>
|
||||
|
||||
<ScreenRotationDialog bind:open={showScreenRotationDialog} />
|
||||
<!-- <SetTimeDialog bind:open={showSetTimeDialog} /> -->
|
||||
|
||||
<div class="settings-view">
|
||||
<h1>Settings</h1>
|
||||
|
||||
<div class="pure-form pure-form-aligned">
|
||||
<h2>User Interface</h2>
|
||||
<fieldset>
|
||||
<div class="pure-control-group">
|
||||
<label for="screen-rotation" />
|
||||
<Button
|
||||
name="screen-rotation"
|
||||
touch
|
||||
variant="raised"
|
||||
on:click={() => (showScreenRotationDialog = true)}
|
||||
>
|
||||
<Label>Change Screen Rotation</Label>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- <div class="pure-control-group">
|
||||
<label for="set-time" />
|
||||
<Button
|
||||
name="set-time"
|
||||
touch
|
||||
variant="raised"
|
||||
on:click={() => (showSetTimeDialog = true)}
|
||||
>
|
||||
<Label>Change Time & Timezone</Label>
|
||||
</Button>
|
||||
</div> -->
|
||||
</fieldset>
|
||||
|
||||
<h2>Units</h2>
|
||||
<fieldset>
|
||||
<ConfigTemplatedInput key={`settings.units`} />
|
||||
<div class="tip">
|
||||
Note, units sets both the machine default units and the units used in motor configuration. GCode program-start,
|
||||
set below, may also change the default machine units.
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<h2>Probing</h2>
|
||||
<fieldset>
|
||||
<ConfigTemplatedInput key={`settings.probing-prompts`} />
|
||||
<div class="tip">
|
||||
Onefinity highly recommends that you keep the safety prompts
|
||||
enabled. If you choose to live dangerously, and disable the
|
||||
safety prompts, Onefinity cannot be held responsible.
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
{#each Object.keys(configTemplate.probe) as key}
|
||||
{#if key !== "probe-diameter"}
|
||||
<ConfigTemplatedInput key={`probe.${key}`} />
|
||||
{/if}
|
||||
{/each}
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<h2>GCode</h2>
|
||||
{#each Object.keys(configTemplate.gcode) as key}
|
||||
<ConfigTemplatedInput key={`gcode.${key}`} />
|
||||
{/each}
|
||||
</fieldset>
|
||||
|
||||
<h2>Path Accuracy</h2>
|
||||
<fieldset>
|
||||
<ConfigTemplatedInput key={`settings.max-deviation`} />
|
||||
|
||||
<div class="tip">
|
||||
Lower the maximum deviation to follow the programmed path more
|
||||
precisely but at a slower speed.
|
||||
</div>
|
||||
|
||||
<div class="tip">
|
||||
In order to improve traversal speed, the path planner may merge
|
||||
consecutive moves or round off sharp corners if doing so would
|
||||
deviate from the program path by less than the maximum
|
||||
deviation.
|
||||
</div>
|
||||
|
||||
<div class="tip">
|
||||
GCode commands
|
||||
<a href={`${gcodeURL}#gcode:g61`} target="_blank">G61, G61.1</a>
|
||||
and <a href={`${gcodeURL}#gcode:g64`} target="_blank">G64</a> also
|
||||
affect path planning accuracy.
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<h2>Cornering Speed (Advanced)</h2>
|
||||
<fieldset>
|
||||
<ConfigTemplatedInput key={`settings.junction-accel`} />
|
||||
<div class="tip">
|
||||
Junction acceleration limits the cornering speed the planner
|
||||
will allow. Increasing this value will allow for faster
|
||||
traversal of corners but may cause the planner to violate axis
|
||||
jerk limits and stall the motors. Use with caution.
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.settings-view {
|
||||
.tip {
|
||||
margin-left: 210px;
|
||||
margin-bottom: 15px;
|
||||
font-style: italic;
|
||||
font-size: 90%;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
import TextField from "@smui/textfield";
|
||||
import Icon from "@smui/textfield/icon";
|
||||
import HelperText from "@smui/textfield/helper-text";
|
||||
import MenuSurface, {
|
||||
type MenuSurfaceComponentDev,
|
||||
} from "@smui/menu-surface";
|
||||
import List, { Item, Text } from "@smui/list";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
import { onDestroy } from "svelte";
|
||||
|
||||
let menuSurface: MenuSurfaceComponentDev;
|
||||
let menuTimeout;
|
||||
let optionSelected: boolean = false;
|
||||
|
||||
export let value: string;
|
||||
export let options: string[][];
|
||||
export let valid: boolean;
|
||||
export let helperText: string;
|
||||
|
||||
onDestroy(() => {
|
||||
if (menuTimeout) {
|
||||
clearTimeout(menuTimeout);
|
||||
menuTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
function showMenu(show: boolean) {
|
||||
if (show && optionSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
optionSelected = false;
|
||||
|
||||
if (menuTimeout) {
|
||||
clearTimeout(menuTimeout);
|
||||
}
|
||||
|
||||
// Use a timeout to "debounce" the display of the menu.
|
||||
menuTimeout = setTimeout(() => menuSurface.setOpen(show), 100);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="textfield-with-options">
|
||||
<TextField
|
||||
bind:value
|
||||
on:focusin={() => showMenu(true)}
|
||||
on:focusout={() => showMenu(false)}
|
||||
use={[virtualKeyboardChange((v) => (value = v))]}
|
||||
{...$$restProps}
|
||||
>
|
||||
<div slot="trailingIcon">
|
||||
{#if valid}
|
||||
<Icon class="fa fa-check-circle-o" style="color: green;" />
|
||||
{/if}
|
||||
</div>
|
||||
<HelperText persistent slot="helper">{helperText}</HelperText>
|
||||
</TextField>
|
||||
|
||||
<MenuSurface bind:this={menuSurface} anchorCorner="BOTTOM_LEFT">
|
||||
<div style="display: flex; flex-direction: row;">
|
||||
{#each options as group}
|
||||
<List>
|
||||
{#each group as option}
|
||||
<Item
|
||||
on:SMUI:action={() => {
|
||||
value = option;
|
||||
showMenu(false);
|
||||
|
||||
optionSelected = true;
|
||||
}}
|
||||
>
|
||||
<Text>{option}</Text>
|
||||
</Item>
|
||||
{/each}
|
||||
</List>
|
||||
{/each}
|
||||
</div>
|
||||
</MenuSurface>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
:global {
|
||||
.textfield-with-options {
|
||||
.mdc-deprecated-list-item {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
101
src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte
Normal file
101
src/svelte-components/src/dialogs/ChangeHostnameDialog.svelte
Normal file
@@ -0,0 +1,101 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import TextField from "@smui/textfield";
|
||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||
import * as api from "$lib/api";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
|
||||
// https://man7.org/linux/man-pages/man7/hostname.7.html
|
||||
//
|
||||
// Each element of the hostname must be from 1 to 63 characters long
|
||||
// and the entire hostname, including the dots, can be at most 253
|
||||
// characters long. Valid characters for hostnames are ASCII(7)
|
||||
// letters from a to z, the digits from 0 to 9, and the hyphen (-).
|
||||
// A hostname may not start with a hyphen.
|
||||
|
||||
const pattern = /[a-zA-Z0-9][a-zA-Z0-9-]{0,62}/;
|
||||
|
||||
export let open = false;
|
||||
|
||||
let rebooting = false;
|
||||
let redirectTimeout = 45;
|
||||
let hostname = "";
|
||||
|
||||
$: setTimeout(() => {
|
||||
hostname = (hostname.match(pattern) || [""])[0].toLowerCase();
|
||||
}, 0);
|
||||
|
||||
$: if (open) {
|
||||
hostname = "";
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
rebooting = true;
|
||||
await api.PUT("hostname", { hostname });
|
||||
await api.PUT("reboot");
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (0 < redirectTimeout) {
|
||||
redirectTimeout -= 1;
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
location.hostname = getRedirectTarget();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function getRedirectTarget() {
|
||||
if (location.hostname.endsWith(".local")) {
|
||||
return `${hostname}.local`;
|
||||
}
|
||||
|
||||
if (location.hostname.endsWith(".lan")) {
|
||||
return `${hostname}.lan`;
|
||||
}
|
||||
|
||||
return hostname;
|
||||
}
|
||||
</script>
|
||||
|
||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||
Rebooting to apply the hostname change...
|
||||
</MessageDialog>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="change-hostname-dialog-title"
|
||||
aria-describedby="change-hostname-dialog-content"
|
||||
>
|
||||
<Title id="change-hostname-dialog-title">Change Hostname</Title>
|
||||
|
||||
<Content id="change-hostname-dialog-content">
|
||||
<TextField
|
||||
bind:value={hostname}
|
||||
use={[InitialFocus, virtualKeyboardChange((v) => (hostname = v))]}
|
||||
label="New Hostname"
|
||||
spellcheck="false"
|
||||
variant="filled"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
<Button
|
||||
defaultAction
|
||||
on:click={onConfirm}
|
||||
disabled={hostname.length === 0}
|
||||
>
|
||||
<Label>Confirm & Reboot</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
244
src/svelte-components/src/dialogs/DialogHost.svelte
Normal file
244
src/svelte-components/src/dialogs/DialogHost.svelte
Normal file
@@ -0,0 +1,244 @@
|
||||
<script lang="ts" context="module">
|
||||
import { writable } from "svelte/store";
|
||||
import HomeMachineDialog from "$dialogs/HomeMachineDialog.svelte";
|
||||
import ProbeDialog from "$dialogs/ProbeDialog.svelte";
|
||||
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
|
||||
import UploadDialog from "$dialogs/UploadDialog.svelte";
|
||||
import SetTimeDialog from "./SetTimeDialog.svelte";
|
||||
import ManualHomeAxisDialog from "./ManualHomeAxisDialog.svelte";
|
||||
import SetAxisPositionDialog from "./SetAxisPositionDialog.svelte";
|
||||
import MoveToZeroDialog from "./MoveToZeroDialog.svelte";
|
||||
import ShutdownDialog from "./ShutdownDialog.svelte";
|
||||
import MessageDialog from "./MessageDialog.svelte";
|
||||
|
||||
const HomeMachineDialogProps = writable<HomeMachineDialogPropsType>();
|
||||
type HomeMachineDialogPropsType = {
|
||||
open: boolean;
|
||||
home: () => void;
|
||||
};
|
||||
|
||||
const ProbeDialogProps = writable<ProbeDialogPropsType>();
|
||||
type ProbeDialogPropsType = {
|
||||
open: boolean;
|
||||
probeType: "xyz" | "z";
|
||||
};
|
||||
|
||||
const ScreenRotationDialogProps = writable<ScreenRotationDialogPropsType>();
|
||||
type ScreenRotationDialogPropsType = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
const UploadDialogProps = writable<UploadDialogPropsType>();
|
||||
type UploadDialogPropsType = {
|
||||
open: boolean;
|
||||
file: File;
|
||||
onComplete: () => void;
|
||||
};
|
||||
|
||||
const SetTimeDialogProps = writable<SetTimeDialogPropsType>();
|
||||
type SetTimeDialogPropsType = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
const ManualHomeAxisDialogProps = writable<ManualHomeAxisDialogPropsType>();
|
||||
type ManualHomeAxisDialogPropsType = {
|
||||
open: boolean;
|
||||
axis: string;
|
||||
};
|
||||
|
||||
const SetAxisPositionDialogProps =
|
||||
writable<SetAxisPositionDialogPropsType>();
|
||||
type SetAxisPositionDialogPropsType = {
|
||||
open: boolean;
|
||||
axis: string;
|
||||
};
|
||||
|
||||
const MoveToZeroDialogProps = writable<MoveToZeroDialogPropsType>();
|
||||
type MoveToZeroDialogPropsType = {
|
||||
open: boolean;
|
||||
axes: "xy" | "z";
|
||||
};
|
||||
|
||||
const ShutdownDialogProps = writable<ShutdownDialogPropsType>();
|
||||
type ShutdownDialogPropsType = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
const MessageDialogProps = writable<MessageDialogPropsType>();
|
||||
type MessageDialogPropsType = {
|
||||
open: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
noaction: boolean;
|
||||
};
|
||||
|
||||
export function showDialog(
|
||||
dialog: "HomeMachine",
|
||||
props: Omit<HomeMachineDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "Probe",
|
||||
props: Omit<ProbeDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "ScreenRotation",
|
||||
props: Omit<ScreenRotationDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "Upload",
|
||||
props: Omit<UploadDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "SetTime",
|
||||
props: Omit<SetTimeDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "ManualHomeAxis",
|
||||
props: Omit<ManualHomeAxisDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "SetAxisPosition",
|
||||
props: Omit<SetAxisPositionDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "MoveToZero",
|
||||
props: Omit<MoveToZeroDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "Shutdown",
|
||||
props: Omit<ShutdownDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(
|
||||
dialog: "Message",
|
||||
props: Omit<MessageDialogPropsType, "open">
|
||||
);
|
||||
|
||||
export function showDialog(dialog: string, props: any) {
|
||||
switch (dialog) {
|
||||
case "HomeMachine":
|
||||
HomeMachineDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "Probe":
|
||||
ProbeDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "ScreenRotation":
|
||||
ScreenRotationDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "Upload":
|
||||
UploadDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "SetTime":
|
||||
SetTimeDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "ManualHomeAxis":
|
||||
ManualHomeAxisDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "SetAxisPosition":
|
||||
SetAxisPositionDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "MoveToZero":
|
||||
MoveToZeroDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "Shutdown":
|
||||
ShutdownDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
case "Message":
|
||||
MessageDialogProps.set({ ...props, open: true });
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown dialog '${dialog}'`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
|
||||
let bodyObserver: MutationObserver;
|
||||
let keyboardObserver: MutationObserver;
|
||||
|
||||
onMount(() => {
|
||||
bodyObserver = new MutationObserver(() => {
|
||||
const virtualKeyboard = document.getElementById(
|
||||
"virtualKeyboardChromeExtension"
|
||||
);
|
||||
|
||||
if (virtualKeyboard) {
|
||||
bodyObserver.disconnect();
|
||||
bodyObserver = undefined;
|
||||
|
||||
const virtualKeyboardOverlay = document.getElementById(
|
||||
"virtualKeyboardChromeExtensionOverlayScrollExtend"
|
||||
);
|
||||
|
||||
keyboardObserver = new MutationObserver(() => {
|
||||
const open =
|
||||
virtualKeyboard.getAttribute("_state") === "open";
|
||||
const keyboardHeight = Number.parseFloat(
|
||||
virtualKeyboardOverlay.style.height
|
||||
);
|
||||
|
||||
const dialogContainers =
|
||||
document.querySelectorAll<HTMLDivElement>(
|
||||
".mdc-dialog .mdc-dialog__container"
|
||||
);
|
||||
|
||||
for (let dialogContainer of dialogContainers) {
|
||||
dialogContainer.style["marginBottom"] = open
|
||||
? `${keyboardHeight}px`
|
||||
: "";
|
||||
}
|
||||
});
|
||||
|
||||
keyboardObserver.observe(virtualKeyboard, { attributes: true });
|
||||
}
|
||||
});
|
||||
|
||||
bodyObserver.observe(document.querySelector("body"), {
|
||||
subtree: false,
|
||||
childList: true,
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (bodyObserver) {
|
||||
bodyObserver.disconnect();
|
||||
bodyObserver = undefined;
|
||||
}
|
||||
|
||||
if (keyboardObserver) {
|
||||
keyboardObserver.disconnect();
|
||||
keyboardObserver = undefined;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<HomeMachineDialog {...$HomeMachineDialogProps} />
|
||||
<ProbeDialog {...$ProbeDialogProps} />
|
||||
<ScreenRotationDialog {...$ScreenRotationDialogProps} />
|
||||
<UploadDialog {...$UploadDialogProps} />
|
||||
<SetTimeDialog {...$SetTimeDialogProps} />
|
||||
<ManualHomeAxisDialog {...$ManualHomeAxisDialogProps} />
|
||||
<SetAxisPositionDialog {...$SetAxisPositionDialogProps} />
|
||||
<MoveToZeroDialog {...$MoveToZeroDialogProps} />
|
||||
<ShutdownDialog {...$ShutdownDialogProps} />
|
||||
<MessageDialog {...$MessageDialogProps} />
|
||||
33
src/svelte-components/src/dialogs/HomeMachineDialog.svelte
Normal file
33
src/svelte-components/src/dialogs/HomeMachineDialog.svelte
Normal file
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
export let open;
|
||||
export let home: () => any;
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="home-machine-dialog-title"
|
||||
aria-describedby="home-machine-dialog-content"
|
||||
>
|
||||
<Title id="home-machine-dialog-title">Home Machine</Title>
|
||||
|
||||
<Content id="home-machine-dialog-content">Home the machine?</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button defaultAction use={[InitialFocus]} on:click={home}>
|
||||
<Label>OK</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
@@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import TextField from "@smui/textfield";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
|
||||
export let open: boolean;
|
||||
export let axis = "";
|
||||
|
||||
let value = 0;
|
||||
|
||||
function onConfirm() {
|
||||
ControllerMethods.set_home(axis, value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="manual-home-axis-dialog-title"
|
||||
aria-describedby="manual-home-axis-dialog-content"
|
||||
>
|
||||
<Title id="manual-home-axis-dialog-title">
|
||||
Manually Home {axis.toUpperCase()} Axis
|
||||
</Title>
|
||||
|
||||
<Content id="manual-home-axis-dialog-content">
|
||||
<p>Set axis absolute position</p>
|
||||
|
||||
<TextField
|
||||
label="Absolute"
|
||||
type="number"
|
||||
bind:value
|
||||
use={[InitialFocus, virtualKeyboardChange((v) => (value = v))]}
|
||||
variant="filled"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
<Button defaultAction on:click={onConfirm}>
|
||||
<Label>Set</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
36
src/svelte-components/src/dialogs/MessageDialog.svelte
Normal file
36
src/svelte-components/src/dialogs/MessageDialog.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
|
||||
export let open: boolean;
|
||||
export let title = "";
|
||||
export let message = "";
|
||||
export let noaction = false;
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
escapeKeyAction=""
|
||||
aria-labelledby="message-dialog-title"
|
||||
aria-describedby="message-dialog-content"
|
||||
>
|
||||
<Title id="message-dialog-title">{title}</Title>
|
||||
|
||||
<Content id="message-dialog-content">
|
||||
<slot>{message}</slot>
|
||||
</Content>
|
||||
|
||||
{#if !noaction}
|
||||
<Actions>
|
||||
<Button defaultAction use={[InitialFocus]}>
|
||||
<Label>OK</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
{/if}
|
||||
</Dialog>
|
||||
33
src/svelte-components/src/dialogs/MoveToZeroDialog.svelte
Normal file
33
src/svelte-components/src/dialogs/MoveToZeroDialog.svelte
Normal file
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import Dialog, { Title, Actions, InitialFocus } from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
|
||||
export let open;
|
||||
export let axes: "xy" | "z";
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="move-to-zero-dialog-title"
|
||||
aria-describedby="move-to-zero-dialog-content"
|
||||
>
|
||||
<Title id="move-to-zero-dialog-title">
|
||||
Move to {(axes || "").toUpperCase()} origin?
|
||||
</Title>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
defaultAction
|
||||
use={[InitialFocus]}
|
||||
on:click={() => ControllerMethods.gotoZero(axes)}
|
||||
>
|
||||
<Label>Confirm</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
517
src/svelte-components/src/dialogs/ProbeDialog.svelte
Normal file
517
src/svelte-components/src/dialogs/ProbeDialog.svelte
Normal file
@@ -0,0 +1,517 @@
|
||||
<script type="ts">
|
||||
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import LinearProgress from "@smui/linear-progress";
|
||||
import { waitForChange } from "$lib/StoreHelpers";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
import { Config } from "$lib/ConfigStore";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import {
|
||||
probingActive,
|
||||
probeContacted,
|
||||
probingComplete,
|
||||
probingFailed,
|
||||
probingStarted,
|
||||
} from "$lib/ControllerState";
|
||||
import { numberWithUnit } from "$lib/RegexHelpers";
|
||||
import TextFieldWithOptions from "$components/TextFieldWithOptions.svelte";
|
||||
import Icon from "svelte-icon";
|
||||
import BitDiameter from "../svgs/probe-bit-diameter.svg?raw";
|
||||
import CheckXYZ from "../svgs/probe-check-xyz.svg?raw";
|
||||
import CheckZ from "../svgs/probe-check-z.svg?raw";
|
||||
import PlaceXYZ from "../svgs/probe-place-xyz.svg?raw";
|
||||
import PlaceZ from "../svgs/probe-place-z.svg?raw";
|
||||
import PutAwayXYZ from "../svgs/probe-put-away-xyz.svg?raw";
|
||||
import PutAwayZ from "../svgs/probe-put-away-z.svg?raw";
|
||||
|
||||
const ValidSteps = [
|
||||
"None",
|
||||
"CheckProbe",
|
||||
"BitDimensions",
|
||||
"PlaceProbeBlock",
|
||||
"Probe",
|
||||
"Done",
|
||||
] as const;
|
||||
|
||||
type Step = typeof ValidSteps[number];
|
||||
|
||||
function isStep(str): str is Step {
|
||||
return ValidSteps.includes(str);
|
||||
}
|
||||
|
||||
const stepLabels: Record<Step, string> = {
|
||||
None: "",
|
||||
CheckProbe: "Check probe",
|
||||
BitDimensions: "Bit dimensions",
|
||||
PlaceProbeBlock: "Place probe block",
|
||||
Probe: "Probe",
|
||||
Done: "Done",
|
||||
};
|
||||
|
||||
const cancelled = writable(false);
|
||||
const userAcknowledged = writable(false);
|
||||
|
||||
const imperialBits: `${number}/${number} in`[] = [
|
||||
"1/2 in",
|
||||
"3/8 in",
|
||||
"1/4 in",
|
||||
"1/8 in",
|
||||
"1/16 in",
|
||||
"1/32 in",
|
||||
];
|
||||
|
||||
const metricBits: `${number} mm`[] = [
|
||||
"12 mm",
|
||||
"10 mm",
|
||||
"8 mm",
|
||||
"6 mm",
|
||||
"4 mm",
|
||||
"3 mm",
|
||||
];
|
||||
|
||||
export let open;
|
||||
export let probeType: "xyz" | "z";
|
||||
let currentStep: Step = "None";
|
||||
let cutterDiameterString: string = "";
|
||||
let cutterDiameterMetric: number;
|
||||
let showCancelButton = true;
|
||||
let steps: Step[] = [];
|
||||
let nextButton = {
|
||||
label: "Next",
|
||||
disabled: false,
|
||||
allowClose: false,
|
||||
};
|
||||
|
||||
$: metric = $Config.settings?.units === "METRIC";
|
||||
$: cutterDiameterMetric = numberWithUnit
|
||||
.parse(cutterDiameterString)
|
||||
?.toMetric();
|
||||
|
||||
$: if (open) {
|
||||
cutterDiameterString = localStorage.getItem("cutterDiameter") ?? "";
|
||||
|
||||
// Svelte appears not to like it when you invoke
|
||||
// an async function from a reactive statement, so we
|
||||
// use requestAnimationFrame to call 'begin' at a later moment.
|
||||
requestAnimationFrame(begin);
|
||||
}
|
||||
|
||||
$: if (cutterDiameterString) {
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
async function begin() {
|
||||
try {
|
||||
$probingActive = true;
|
||||
assertValidProbeType();
|
||||
|
||||
$probingFailed = false;
|
||||
|
||||
const enableSafety = $Config.settings["probing-prompts"];
|
||||
|
||||
steps = [
|
||||
enableSafety ? "CheckProbe" : undefined,
|
||||
probeType === "xyz" ? "BitDimensions" : undefined,
|
||||
enableSafety ? "PlaceProbeBlock" : undefined,
|
||||
"Probe",
|
||||
"Done",
|
||||
].filter<Step>(isStep);
|
||||
|
||||
await stepCompleted("CheckProbe", probeContacted);
|
||||
|
||||
if (probeType === "xyz") {
|
||||
await stepCompleted("BitDimensions", userAcknowledged);
|
||||
localStorage.setItem(
|
||||
"cutterDiameter",
|
||||
numberWithUnit.normalize(cutterDiameterString)
|
||||
);
|
||||
}
|
||||
|
||||
await stepCompleted("PlaceProbeBlock", userAcknowledged);
|
||||
await stepCompleted("Probe", probingComplete, probingFailed);
|
||||
await stepCompleted("Done", userAcknowledged);
|
||||
|
||||
if (probeType === "xyz") {
|
||||
ControllerMethods.gotoZero("xy");
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.message !== "cancelled") {
|
||||
console.error("Error during probing:", err);
|
||||
}
|
||||
} finally {
|
||||
$probingActive = false;
|
||||
currentStep = "None";
|
||||
|
||||
if ($probingStarted) {
|
||||
ControllerMethods.stop();
|
||||
}
|
||||
|
||||
clearFlags();
|
||||
}
|
||||
}
|
||||
|
||||
function assertValidProbeType() {
|
||||
switch (probeType) {
|
||||
case "xyz":
|
||||
case "z":
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid probe type: ${probeType}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function stepCompleted(
|
||||
nextStep: Step,
|
||||
...writables: Array<Writable<any>>
|
||||
) {
|
||||
currentStep = nextStep;
|
||||
|
||||
if (!steps.includes(currentStep)) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearFlags();
|
||||
updateButtons();
|
||||
|
||||
if (currentStep === "Probe") {
|
||||
executeProbe();
|
||||
}
|
||||
|
||||
await Promise.race([
|
||||
...writables.map((writable) => waitForChange(writable)),
|
||||
waitForChange(cancelled),
|
||||
]);
|
||||
|
||||
if ($cancelled) {
|
||||
throw new Error("cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
function clearFlags(foo: string = "") {
|
||||
$cancelled = false;
|
||||
$probeContacted = false;
|
||||
$probingStarted = false;
|
||||
$probingComplete = false;
|
||||
$userAcknowledged = false;
|
||||
}
|
||||
|
||||
function updateButtons() {
|
||||
showCancelButton = true;
|
||||
|
||||
nextButton = {
|
||||
label: "Next",
|
||||
disabled: false,
|
||||
allowClose: false,
|
||||
};
|
||||
|
||||
switch (currentStep) {
|
||||
case "CheckProbe":
|
||||
case "Probe":
|
||||
nextButton.disabled = true;
|
||||
break;
|
||||
|
||||
case "BitDimensions":
|
||||
nextButton.disabled = !isFinite(cutterDiameterMetric);
|
||||
break;
|
||||
|
||||
case "Done":
|
||||
showCancelButton = false;
|
||||
nextButton = {
|
||||
disabled: false,
|
||||
label: "Done",
|
||||
allowClose: true,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function executeProbe() {
|
||||
const probeBlockWidth = $Config.probe["probe-xdim"];
|
||||
const probeBlockLength = $Config.probe["probe-ydim"];
|
||||
const probeBlockHeight = $Config.probe["probe-zdim"];
|
||||
const slowSeek = $Config.probe["probe-slow-seek"];
|
||||
const fastSeek = $Config.probe["probe-fast-seek"];
|
||||
|
||||
const cutterLength = 12.7;
|
||||
const zLift = 1;
|
||||
const xOffset = probeBlockWidth + cutterDiameterMetric / 2.0;
|
||||
const yOffset = probeBlockLength + cutterDiameterMetric / 2.0;
|
||||
const zOffset = probeBlockHeight;
|
||||
|
||||
if (probeType === "z") {
|
||||
ControllerMethods.send(`
|
||||
G21
|
||||
G92 Z0
|
||||
|
||||
G38.2 Z -25.4 F${fastSeek}
|
||||
G91 G1 Z 1
|
||||
G38.2 Z -2 F${slowSeek}
|
||||
G92 Z ${zOffset}
|
||||
|
||||
G91 G0 Z 25
|
||||
|
||||
M2
|
||||
`);
|
||||
} else {
|
||||
// After probing Z, we want to drop the bit down:
|
||||
// Ideally, 12.7mm/0.5in
|
||||
// And we don't want to be more than 90% down on the probe block
|
||||
// Also, add zlift to compensate for the fact that we lift after probing Z
|
||||
const plunge = Math.min(cutterLength, zOffset * 0.9) + zLift;
|
||||
|
||||
ControllerMethods.send(`
|
||||
G21
|
||||
G92 X0 Y0 Z0
|
||||
|
||||
G38.2 Z -25 F${fastSeek}
|
||||
G91 G1 Z 1
|
||||
G38.2 Z -2 F${slowSeek}
|
||||
G92 Z ${zOffset}
|
||||
|
||||
G91 G0 Z ${zLift}
|
||||
G91 G0 X 20
|
||||
G91 G0 Z ${-plunge}
|
||||
G38.2 X -20 F${fastSeek}
|
||||
G91 G1 X 1
|
||||
G38.2 X -2 F${slowSeek}
|
||||
G92 X ${xOffset}
|
||||
|
||||
G91 G0 X 1
|
||||
G91 G0 Y 20
|
||||
G91 G0 X -20
|
||||
G38.2 Y -20 F${fastSeek}
|
||||
G91 G1 Y 1
|
||||
G38.2 Y -2 F${slowSeek}
|
||||
G92 Y ${yOffset}
|
||||
|
||||
G91 G0 Y 3
|
||||
G91 G0 Z 25
|
||||
|
||||
M2
|
||||
`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
class="probe-dialog"
|
||||
scrimClickAction=""
|
||||
aria-labelledby="probe-dialog-title"
|
||||
aria-describedby="probe-dialog-content"
|
||||
surface$style="width: 700px; max-width: calc(100vw - 32px);"
|
||||
>
|
||||
<Title id="probe-dialog-title">Probing {probeType?.toUpperCase()}</Title>
|
||||
|
||||
<Content id="probe-dialog-content" style="overflow: visible;">
|
||||
<div class="steps">
|
||||
<p>
|
||||
<b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b>
|
||||
</p>
|
||||
<ul>
|
||||
{#each steps as step}
|
||||
<li class:active={currentStep === step}>
|
||||
{stepLabels[step]}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<div style="width: 100%">
|
||||
{#if currentStep === "CheckProbe"}
|
||||
<p>
|
||||
Attach the probe magnet to the collet, then touch the probe
|
||||
block to the bit.
|
||||
</p>
|
||||
|
||||
<Icon
|
||||
data={probeType === "xyz" ? CheckXYZ : CheckZ}
|
||||
size="300px"
|
||||
class="probe-icon-svg"
|
||||
/>
|
||||
{:else if currentStep === "BitDimensions"}
|
||||
<TextFieldWithOptions
|
||||
label="Cutter diameter"
|
||||
variant="filled"
|
||||
spellcheck="false"
|
||||
style="width: 100%;"
|
||||
bind:value={cutterDiameterString}
|
||||
options={[imperialBits, metricBits]}
|
||||
valid={isFinite(cutterDiameterMetric)}
|
||||
helperText={`Examples: 1/2", 10 mm, 0.25 in`}
|
||||
/>
|
||||
|
||||
<Icon data={BitDiameter} size="150px" class="probe-icon-svg" />
|
||||
{:else if currentStep === "PlaceProbeBlock"}
|
||||
<p>
|
||||
{#if probeType === "xyz"}
|
||||
Place the probe block face up, on the lower-left corner
|
||||
of your workpiece.
|
||||
{:else}
|
||||
Place the probe block face down, with the bit above the
|
||||
recess.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
<Icon
|
||||
data={probeType === "xyz" ? PlaceXYZ : PlaceZ}
|
||||
width="304px"
|
||||
height="129px"
|
||||
class="probe-icon-svg"
|
||||
/>
|
||||
|
||||
<p>
|
||||
The probing procedure will begin as soon as you click
|
||||
'Next'.
|
||||
</p>
|
||||
{:else if currentStep === "Probe"}
|
||||
<p>Probing in progress...</p>
|
||||
|
||||
<LinearProgress indeterminate />
|
||||
{:else if currentStep === "Done"}
|
||||
{#if $probingFailed}
|
||||
<h3>Emergency Stop!</h3>
|
||||
|
||||
<p>Could not find the probe block during probing!</p>
|
||||
|
||||
<p>
|
||||
Make sure the tip of the bit is less than {metric
|
||||
? "25mm"
|
||||
: "1 in"}
|
||||
above the probe block, and try again.
|
||||
</p>
|
||||
{:else}
|
||||
<p>Don't forget to put away the probe!</p>
|
||||
|
||||
<Icon
|
||||
data={probeType === "xyz" ? PutAwayXYZ : PutAwayZ}
|
||||
width="329px"
|
||||
height="256px"
|
||||
class="probe-icon-svg"
|
||||
/>
|
||||
|
||||
{#if probeType === "xyz"}
|
||||
<p>The machine will now move to the XY origin.</p>
|
||||
|
||||
<p>Watch your hands!</p>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
{#if showCancelButton}
|
||||
<Button on:click={() => ($cancelled = true)}>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
defaultAction
|
||||
data-mdc-dialog-action={nextButton.allowClose ? "close" : ""}
|
||||
disabled={nextButton.disabled}
|
||||
on:click={() => ($userAcknowledged = true)}
|
||||
>
|
||||
<Label>
|
||||
{nextButton.label}
|
||||
</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
$primary: #0078e7;
|
||||
$very-dark: #555;
|
||||
$text: #777;
|
||||
$grey: #bbb;
|
||||
$light: #ddd;
|
||||
|
||||
:global {
|
||||
.probe-dialog {
|
||||
.mdc-linear-progress {
|
||||
height: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.mdc-linear-progress__bar-inner {
|
||||
border-top-width: 10px;
|
||||
}
|
||||
|
||||
.probe-icon-svg {
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
#probe-dialog-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.bit-dimensions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.steps {
|
||||
margin-right: 50px;
|
||||
|
||||
ul {
|
||||
margin: 0 auto;
|
||||
list-style-type: none;
|
||||
counter-reset: steps;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
padding: 0 0 12px 30px;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
color: $text;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 0.5px;
|
||||
content: "";
|
||||
border: 2px solid $text;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: 11px;
|
||||
width: 11px;
|
||||
text-align: center;
|
||||
line-height: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
left: 7px;
|
||||
top: 22px;
|
||||
bottom: 0;
|
||||
content: "";
|
||||
width: 0;
|
||||
border-left: 2px solid $text;
|
||||
}
|
||||
|
||||
&:last-of-type:before {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $primary;
|
||||
font-weight: bold;
|
||||
|
||||
&:after {
|
||||
border: 3px solid $primary;
|
||||
top: 2.5px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<script lang="ts">
|
||||
import * as api from "$lib/api";
|
||||
import TextField from "@smui/textfield";
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
|
||||
export let open;
|
||||
|
||||
let code = "";
|
||||
|
||||
async function onContinue() {
|
||||
const url = `remote-diagnostics?command=connect&code=${code}`;
|
||||
const result = await api.GET(url);
|
||||
|
||||
if (result.code === 401) {
|
||||
alert("The 6-digit code you provided was incorrect");
|
||||
} else {
|
||||
alert("Success!");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="remote-diagnostics-dialog-title"
|
||||
aria-describedby="remote-diagnostics-dialog-content"
|
||||
>
|
||||
<Title id="remote-diagnostics-dialog-title">Remote Diagnostics</Title>
|
||||
|
||||
<Content id="remote-diagnostics-dialog-content">
|
||||
<p>
|
||||
This feature enables remote diagnosis of customer issues. It
|
||||
requires a 6-digit code that is provided by Onefinity support during
|
||||
a live support session.
|
||||
</p>
|
||||
|
||||
<TextField
|
||||
bind:value={code}
|
||||
label="6-digit code"
|
||||
type="number"
|
||||
variant="filled"
|
||||
invalid={code?.length !== 6}
|
||||
use={[InitialFocus, virtualKeyboardChange((v) => (code = v))]}
|
||||
/>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
defaultAction
|
||||
on:click={onContinue}
|
||||
disabled={code?.length !== 6}
|
||||
>
|
||||
<Label>Continue</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script type="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import Radio from "@smui/radio";
|
||||
import FormField from "@smui/form-field";
|
||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||
import * as Api from "$lib/api";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
const options = [
|
||||
{ value: 0, label: "Normal" },
|
||||
{ value: 1, label: "Upside-down" },
|
||||
];
|
||||
|
||||
export let open;
|
||||
let currentValue;
|
||||
let value;
|
||||
let rebooting;
|
||||
|
||||
onMount(async () => {
|
||||
const result = await Api.GET("screen-rotation");
|
||||
currentValue = value = result.rotated ? 1 : 0;
|
||||
});
|
||||
|
||||
async function onConfirm() {
|
||||
rebooting = true;
|
||||
|
||||
await Api.PUT("screen-rotation", { rotated: value === 1 });
|
||||
}
|
||||
</script>
|
||||
|
||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||
Rebooting to apply the new screen rotation...
|
||||
</MessageDialog>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="screen-rotation-dialog-title"
|
||||
aria-describedby="screen-rotation-dialog-content"
|
||||
>
|
||||
<Title id="screen-rotation-dialog-title">Screen Rotation</Title>
|
||||
|
||||
<Content id="screen-rotation-dialog-content">
|
||||
{#each options as option}
|
||||
<FormField>
|
||||
<Radio bind:group={value} value={option.value} />
|
||||
<span slot="label">
|
||||
{option.label}
|
||||
</span>
|
||||
</FormField>
|
||||
{/each}
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button use={[InitialFocus]}>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
<Button
|
||||
defaultAction
|
||||
disabled={value === currentValue}
|
||||
on:click={onConfirm}
|
||||
>
|
||||
<Label>Confirm & Reboot</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
:global {
|
||||
#screen-rotation-dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,72 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import TextField from "@smui/textfield";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
|
||||
export let open: boolean;
|
||||
export let axis = "";
|
||||
|
||||
let value = 0;
|
||||
let homed = false;
|
||||
let wasOpen = false;
|
||||
|
||||
$: if (open != wasOpen) {
|
||||
if (open) {
|
||||
homed = ControllerMethods.isAxisHomed(axis);
|
||||
}
|
||||
|
||||
wasOpen = open;
|
||||
}
|
||||
|
||||
function onUnhome() {
|
||||
ControllerMethods.unhome(axis);
|
||||
}
|
||||
|
||||
function onConfirm() {
|
||||
ControllerMethods.set_position(axis, value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="set-axis-position-dialog-title"
|
||||
aria-describedby="set-axis-position-dialog-content"
|
||||
>
|
||||
<Title id="set-axis-position-dialog-title">
|
||||
Set {axis.toUpperCase()} Axis Position
|
||||
</Title>
|
||||
|
||||
<Content id="set-axis-position-dialog-content">
|
||||
<TextField
|
||||
label="Position"
|
||||
type="number"
|
||||
bind:value
|
||||
use={[InitialFocus, virtualKeyboardChange((v) => (value = v))]}
|
||||
spellcheck="false"
|
||||
variant="filled"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
{#if homed}
|
||||
<Button on:click={onUnhome}>
|
||||
<Label>Unhome</Label>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button defaultAction on:click={onConfirm}>
|
||||
<Label>Set</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
290
src/svelte-components/src/dialogs/SetTimeDialog.svelte
Normal file
290
src/svelte-components/src/dialogs/SetTimeDialog.svelte
Normal file
@@ -0,0 +1,290 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import TextField from "@smui/textfield";
|
||||
import Select, { Option } from "@smui/select";
|
||||
import CircularProgress from "@smui/circular-progress";
|
||||
import VirtualList from "svelte-tiny-virtual-list";
|
||||
import * as api from "$lib/api";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
|
||||
const itemHeight = 35;
|
||||
|
||||
type Timezone = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export let open = false;
|
||||
let year = "";
|
||||
let month = "";
|
||||
let day = "";
|
||||
let hour = "";
|
||||
let minute = "";
|
||||
let am = true;
|
||||
let wasOpen = false;
|
||||
let loading = true;
|
||||
let timezones: Timezone[] = [];
|
||||
let currentTimezoneIndex: number;
|
||||
let selectedTimezoneIndex: number;
|
||||
let networkTimeSynchronized: boolean;
|
||||
|
||||
$: if (open != wasOpen) {
|
||||
if (!wasOpen) {
|
||||
loadData();
|
||||
}
|
||||
|
||||
wasOpen = open;
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
loading = true;
|
||||
|
||||
const result = await api.GET("time");
|
||||
|
||||
parseTimezones(result.timezones);
|
||||
parseTimeinfo(result.timeinfo);
|
||||
|
||||
const date = new Date();
|
||||
year = date.getFullYear().toString();
|
||||
month = (date.getMonth() + 1).toString();
|
||||
day = date.getDate().toString();
|
||||
hour = date.getHours().toString();
|
||||
minute = date.getMinutes().toString();
|
||||
am = date.getHours() >= 12;
|
||||
|
||||
loading = false;
|
||||
}
|
||||
|
||||
function parseTimeinfo(str: string) {
|
||||
const matches = Array.from(str.matchAll(/\s*([^:]+):\s+(.+)/gm));
|
||||
|
||||
let currentTimezoneValue;
|
||||
for (const match of matches) {
|
||||
let [, label, value] = match;
|
||||
|
||||
switch (label) {
|
||||
case "Time zone":
|
||||
currentTimezoneValue = value.split(" ")[0];
|
||||
break;
|
||||
|
||||
case "NTP synchronized":
|
||||
networkTimeSynchronized = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
currentTimezoneIndex = timezones.findIndex(
|
||||
(tz) => tz.value === currentTimezoneValue
|
||||
);
|
||||
selectedTimezoneIndex = currentTimezoneIndex;
|
||||
}
|
||||
|
||||
function parseTimezones(str: string) {
|
||||
const matches = Array.from(str.matchAll(/\s*(\S+)\s*/gm));
|
||||
|
||||
timezones = [];
|
||||
for (let [, value] of matches) {
|
||||
timezones.push({
|
||||
label: value.replace(/_/g, " "),
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
// Sort alphabetically, but with the current timezone at the top of the list
|
||||
timezones.sort((a, b) => {
|
||||
switch (true) {
|
||||
case a.value === "UTC":
|
||||
return -1;
|
||||
|
||||
case b.value === "UTC":
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return a.value.localeCompare(b.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
const YY = year.toString().padStart(2, "0");
|
||||
const MM = month.toString().padStart(2, "0");
|
||||
const DD = day.toString().padStart(2, "0");
|
||||
let hh = hour.toString().padStart(2, "0");
|
||||
const mm = minute.toString().padStart(2, "0");
|
||||
|
||||
if (Number(hour) < 12 && !am) {
|
||||
hh = (hour + 12).toString().padStart(2, "0");
|
||||
}
|
||||
|
||||
await api.PUT("time", {
|
||||
datetime: `${YY}-${MM}-${DD} ${hh}:${mm}:00`,
|
||||
timezone: timezones[selectedTimezoneIndex].value,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="set-time-dialog-title"
|
||||
aria-describedby="set-time-dialog-content"
|
||||
>
|
||||
<Title id="set-time-dialog-title">Change Time & Timezone</Title>
|
||||
|
||||
<Content id="set-time-dialog-content">
|
||||
{#if loading}
|
||||
<div style="display: flex; justify-content: center">
|
||||
<CircularProgress
|
||||
style="height: 32px; width: 32px;"
|
||||
indeterminate
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
{#if networkTimeSynchronized}
|
||||
<p>
|
||||
Because this controller is connected to the internet, the
|
||||
time is managed automatically, and cannot be manually set.
|
||||
</p>
|
||||
{:else}
|
||||
<p>
|
||||
Any time the controller is turned off, the time will need to
|
||||
be reset. If you connect the controller to the internet, the
|
||||
time will be managed automatically.
|
||||
</p>
|
||||
|
||||
<TextField
|
||||
bind:value={year}
|
||||
use={[
|
||||
InitialFocus,
|
||||
virtualKeyboardChange((v) => (year = v)),
|
||||
]}
|
||||
label="Year"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
<TextField
|
||||
bind:value={month}
|
||||
use={[virtualKeyboardChange((v) => (month = v))]}
|
||||
label="Month"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
<TextField
|
||||
bind:value={day}
|
||||
use={[virtualKeyboardChange((v) => (day = v))]}
|
||||
label="Day"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
|
||||
<span style="display: inline-block; width: 20px;" />
|
||||
|
||||
<TextField
|
||||
bind:value={hour}
|
||||
use={[virtualKeyboardChange((v) => (hour = v))]}
|
||||
label="Hour"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
<TextField
|
||||
bind:value={minute}
|
||||
use={[virtualKeyboardChange((v) => (minute = v))]}
|
||||
label="Minute"
|
||||
type="number"
|
||||
variant="filled"
|
||||
style="width: 70px;"
|
||||
/>
|
||||
|
||||
<Select
|
||||
label=""
|
||||
bind:value={am}
|
||||
style="width: 90px;"
|
||||
variant="filled"
|
||||
>
|
||||
<Option value={true}>AM</Option>
|
||||
<Option value={false}>PM</Option>
|
||||
</Select>
|
||||
{/if}
|
||||
|
||||
<div class="timezones-container" style="margin-top: 30px;">
|
||||
<VirtualList
|
||||
width="100%"
|
||||
height={itemHeight * 6}
|
||||
itemCount={timezones.length}
|
||||
itemSize={itemHeight}
|
||||
scrollToIndex={currentTimezoneIndex}
|
||||
scrollToAlignment="center"
|
||||
>
|
||||
<div
|
||||
slot="item"
|
||||
let:index
|
||||
let:style
|
||||
{style}
|
||||
class="timezone"
|
||||
class:selected={index === selectedTimezoneIndex}
|
||||
on:click={() => (selectedTimezoneIndex = index)}
|
||||
>
|
||||
{timezones[index].label}
|
||||
</div>
|
||||
</VirtualList>
|
||||
</div>
|
||||
{/if}
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
<Button
|
||||
defaultAction
|
||||
disabled={selectedTimezoneIndex === -1}
|
||||
on:click={onConfirm}
|
||||
>
|
||||
<Label>Confirm</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
|
||||
<style lang="scss">
|
||||
@use "sass:color";
|
||||
|
||||
$primary: #0078e7;
|
||||
$very-dark: #555;
|
||||
$text: #777;
|
||||
$grey: #bbb;
|
||||
$light: #ddd;
|
||||
|
||||
.timezones-container {
|
||||
:global {
|
||||
.virtual-list-wrapper {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
.timezone {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding-left: 10px;
|
||||
|
||||
&.selected {
|
||||
color: $primary;
|
||||
background-color: color.adjust($primary, $lightness: 50%);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
38
src/svelte-components/src/dialogs/ShutdownDialog.svelte
Normal file
38
src/svelte-components/src/dialogs/ShutdownDialog.svelte
Normal file
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import Dialog, { Title, Actions, InitialFocus } from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import * as Api from "$lib/api";
|
||||
|
||||
export let open;
|
||||
|
||||
function shutdown() {
|
||||
Api.PUT("shutdown");
|
||||
}
|
||||
|
||||
function restart() {
|
||||
Api.PUT("reboot");
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="shutdown-dialog-title"
|
||||
aria-describedby="shutdown-dialog-content"
|
||||
>
|
||||
<Title id="shutdown-dialog-title">Confirm Shutdown?</Title>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button on:click={shutdown}>
|
||||
<Label>Shutdown</Label>
|
||||
</Button>
|
||||
|
||||
<Button use={[InitialFocus]} on:click={restart}>
|
||||
<Label>Restart</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
83
src/svelte-components/src/dialogs/UploadDialog.svelte
Normal file
83
src/svelte-components/src/dialogs/UploadDialog.svelte
Normal file
@@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import LinearProgress from "@smui/linear-progress";
|
||||
|
||||
export let open = false;
|
||||
export let file: File;
|
||||
export let onComplete: () => void;
|
||||
|
||||
let wasOpen = false;
|
||||
let xhr;
|
||||
let progress;
|
||||
|
||||
$: if (open != wasOpen) {
|
||||
if (!wasOpen) {
|
||||
beginUpload();
|
||||
}
|
||||
|
||||
wasOpen = open;
|
||||
}
|
||||
|
||||
$: if (!open) {
|
||||
xhr = undefined;
|
||||
}
|
||||
|
||||
async function beginUpload() {
|
||||
progress = 0;
|
||||
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.upload.onload = () => {
|
||||
open = false;
|
||||
if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
};
|
||||
|
||||
xhr.upload.onerror = () => {
|
||||
open = false;
|
||||
alert("Upload failed.");
|
||||
};
|
||||
|
||||
xhr.upload.onabort = () => {
|
||||
open = false;
|
||||
};
|
||||
|
||||
xhr.upload.onprogress = (event) => {
|
||||
progress = event.loaded / event.total;
|
||||
};
|
||||
|
||||
xhr.open("PUT", `/api/file/${encodeURIComponent(file.name)}`);
|
||||
xhr.send(file);
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
xhr.abort();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="upload-dialog-title"
|
||||
aria-describedby="upload-dialog-content"
|
||||
>
|
||||
<Title id="upload-dialog-title">
|
||||
Uploading {#if file}{file.name}...{/if}
|
||||
</Title>
|
||||
|
||||
<Content id="upload-dialog-content">
|
||||
<LinearProgress {progress} />
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button on:click={onCancel} use={[InitialFocus]}>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
112
src/svelte-components/src/dialogs/WifiConnectionDialog.svelte
Normal file
112
src/svelte-components/src/dialogs/WifiConnectionDialog.svelte
Normal file
@@ -0,0 +1,112 @@
|
||||
<script lang="ts">
|
||||
import Dialog, {
|
||||
Title,
|
||||
Content,
|
||||
Actions,
|
||||
InitialFocus,
|
||||
} from "@smui/dialog";
|
||||
import Button, { Label } from "@smui/button";
|
||||
import TextField from "@smui/textfield";
|
||||
import Icon from "@smui/textfield/icon";
|
||||
import HelperText from "@smui/textfield/helper-text";
|
||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||
import type { WifiNetwork } from "$lib/NetworkInfo";
|
||||
import * as api from "$lib/api";
|
||||
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||
|
||||
export let open = false;
|
||||
export let network: WifiNetwork;
|
||||
|
||||
let rebooting = false;
|
||||
let password = "";
|
||||
let showPassword = false;
|
||||
|
||||
$: needPassword = !network?.active && network?.Encryption !== "Open";
|
||||
$: connectOrDisconnect = network?.active ? "Disconnect" : "Connect";
|
||||
$: connectToOrDisconnectFrom = network?.active
|
||||
? "Disconnect from"
|
||||
: "Connect to";
|
||||
|
||||
$: if (open) {
|
||||
password = "";
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
rebooting = true;
|
||||
|
||||
await api.PUT("network", {
|
||||
wifi: {
|
||||
enabled: !network.active,
|
||||
ssid: network.Name,
|
||||
password,
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||
Rebooting to apply Wifi changes...
|
||||
</MessageDialog>
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="wifi-connection-dialog-title"
|
||||
aria-describedby="wifi-connection-dialog-content"
|
||||
>
|
||||
<Title id="wifi-connection-dialog-title">
|
||||
{connectToOrDisconnectFrom}
|
||||
{network.Name}
|
||||
</Title>
|
||||
|
||||
<Content id="wifi-connection-dialog-content">
|
||||
{#if needPassword}
|
||||
<TextField
|
||||
bind:value={password}
|
||||
use={[
|
||||
InitialFocus,
|
||||
virtualKeyboardChange((v) => (password = v)),
|
||||
]}
|
||||
label="Password"
|
||||
spellcheck="false"
|
||||
variant="filled"
|
||||
type={showPassword ? "text" : "password"}
|
||||
style="width: 100%;"
|
||||
>
|
||||
<div
|
||||
slot="trailingIcon"
|
||||
on:click={() => (showPassword = !showPassword)}
|
||||
>
|
||||
<Icon
|
||||
class={`fa ${showPassword ? "fa-eye-slash" : "fa-eye"}`}
|
||||
/>
|
||||
</div>
|
||||
<HelperText persistent slot="helper">
|
||||
Wifi passwords must be 8 to 128 characters
|
||||
</HelperText>
|
||||
</TextField>
|
||||
{/if}
|
||||
|
||||
<p>
|
||||
<em>
|
||||
Clicking {connectOrDisconnect} will reboot the controller to apply
|
||||
the changes.
|
||||
</em>
|
||||
</p>
|
||||
</Content>
|
||||
|
||||
<Actions>
|
||||
<Button>
|
||||
<Label>Cancel</Label>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
defaultAction
|
||||
on:click={onConfirm}
|
||||
disabled={needPassword &&
|
||||
(password.length < 8 || password.length > 128)}
|
||||
>
|
||||
<Label>{connectOrDisconnect} & Reboot</Label>
|
||||
</Button>
|
||||
</Actions>
|
||||
</Dialog>
|
||||
14
src/svelte-components/src/lib/ConfigStore.ts
Normal file
14
src/svelte-components/src/lib/ConfigStore.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
type DisplayUnits = "METRIC" | "IMPERIAL";
|
||||
|
||||
export const Config = writable<Record<string, any>>({});
|
||||
export const DisplayUnits = writable<DisplayUnits>();
|
||||
|
||||
export function handleConfigUpdate(config: Record<string, any>) {
|
||||
Config.set(config);
|
||||
}
|
||||
|
||||
export function setDisplayUnits(value: DisplayUnits) {
|
||||
DisplayUnits.set(value);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user