Merge pull request #72 from dacarley/6
7 - Improved bit selection during probing
This commit is contained in:
15
.devcontainer/Dockerfile
Normal file
15
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# 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}
|
||||||
|
|
||||||
|
RUN apt update \
|
||||||
|
&& apt upgrade -y \
|
||||||
|
&& apt install -y \
|
||||||
|
build-essential git wget binfmt-support qemu gcc-9 \
|
||||||
|
parted gcc-avr avr-libc avrdude python3 python3-pip python3-tornado \
|
||||||
|
curl unzip python3-setuptools gcc-arm-linux-gnueabihf bc vim sudo \
|
||||||
|
&& update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9 \
|
||||||
|
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
|
||||||
|
&& apt install -y nodejs
|
||||||
26
.devcontainer/devcontainer.json
Normal file
26
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// 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": ["--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" ],
|
||||||
|
|
||||||
|
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||||
|
// "remoteUser": "vscode",
|
||||||
|
|
||||||
|
"features": {
|
||||||
|
"sshd": "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,12 +8,18 @@ Debian Linux are not supported.
|
|||||||
|
|
||||||
On a Debian Linux (9.6.0 stable) system install the required packages:
|
On a Debian Linux (9.6.0 stable) system install the required packages:
|
||||||
|
|
||||||
sudo apt-get update
|
apt update
|
||||||
sudo apt-get install -y build-essential git wget binfmt-support qemu \
|
apt upgrade -y
|
||||||
parted gcc-avr avr-libc avrdude pylint3 python3 python3-tornado curl \
|
apt install -y \
|
||||||
unzip python3-setuptools gcc-arm-linux-gnueabihf bc sudo
|
build-essential git wget binfmt-support qemu gcc-9 \
|
||||||
curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
|
parted gcc-avr avr-libc avrdude python3 python3-tornado curl \
|
||||||
sudo apt-get install -y nodejs
|
unzip python3-setuptools gcc-arm-linux-gnueabihf bc sudo
|
||||||
|
|
||||||
|
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9
|
||||||
|
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
|
||||||
|
|
||||||
|
apt install -y nodejs
|
||||||
|
|
||||||
## Getting the Source Code
|
## Getting the Source Code
|
||||||
|
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bbctrl",
|
"name": "bbctrl",
|
||||||
"version": "1.0.10b7",
|
"version": "1.0.10b10",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bbctrl",
|
"name": "bbctrl",
|
||||||
"version": "1.0.10b7",
|
"version": "1.0.10b10",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "GPL-3.0+",
|
"license": "GPL-3.0+",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bbctrl",
|
"name": "bbctrl",
|
||||||
"version": "1.0.10b7",
|
"version": "1.0.10b10",
|
||||||
"homepage": "https://onefinitycnc.com/",
|
"homepage": "https://onefinitycnc.com/",
|
||||||
"repository": "https://github.com/OneFinityCNC/onefinity",
|
"repository": "https://github.com/OneFinityCNC/onefinity",
|
||||||
"license": "GPL-3.0+",
|
"license": "GPL-3.0+",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/bin/bash -ex
|
#!/bin/bash -ex
|
||||||
|
|
||||||
ROOT="$PWD/rpi-root"
|
ROOT="$PWD/rpi-root"
|
||||||
LOOP=12
|
|
||||||
|
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then
|
||||||
echo "Usage: $0 <image> <exec>"
|
echo "Usage: $0 <image> <exec>"
|
||||||
@@ -9,7 +8,8 @@ if [ $# -lt 1 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
IMAGE="$1"
|
IMAGE="$1"
|
||||||
LOOP_DEV=/dev/loop${LOOP}
|
LOOP_BOOT=
|
||||||
|
LOOP_ROOT=
|
||||||
EXEC=
|
EXEC=
|
||||||
|
|
||||||
if [ $# -gt 1 ]; then
|
if [ $# -gt 1 ]; then
|
||||||
@@ -26,25 +26,28 @@ fi
|
|||||||
# Clean up on EXIT
|
# Clean up on EXIT
|
||||||
function cleanup {
|
function cleanup {
|
||||||
umount "$ROOT"/{dev/pts,dev,sys,proc,boot,mnt/host,} 2>/dev/null || true
|
umount "$ROOT"/{dev/pts,dev,sys,proc,boot,mnt/host,} 2>/dev/null || true
|
||||||
losetup -d $LOOP_DEV 2>/dev/null || true
|
losetup -d $LOOP_BOOT 2>/dev/null || true
|
||||||
|
losetup -d $LOOP_ROOT 2>/dev/null || true
|
||||||
rmdir "$ROOT" 2>/dev/null || true
|
rmdir "$ROOT" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
# set up image as loop device
|
LOOP_BOOT=`losetup -f`
|
||||||
losetup $LOOP_DEV "$IMAGE"
|
losetup -o 4194304 $LOOP_BOOT "$IMAGE"
|
||||||
partprobe $LOOP_DEV
|
|
||||||
|
LOOP_ROOT=`losetup -f`
|
||||||
|
losetup -o 48234496 $LOOP_ROOT "$IMAGE"
|
||||||
|
|
||||||
# check and fix filesystems
|
# check and fix filesystems
|
||||||
fsck -f ${LOOP_DEV}p1
|
fsck -f $LOOP_BOOT
|
||||||
fsck -f ${LOOP_DEV}p2
|
fsck -f $LOOP_ROOT
|
||||||
|
|
||||||
# make dir
|
# make dir
|
||||||
mkdir -p "$ROOT"
|
mkdir -p "$ROOT"
|
||||||
|
|
||||||
# mount partition
|
# mount partition
|
||||||
mount -o rw ${LOOP_DEV}p2 -t ext4 "$ROOT"
|
mount -o rw $LOOP_ROOT -t ext4 "$ROOT"
|
||||||
mount -o rw ${LOOP_DEV}p1 "$ROOT/boot"
|
mount -o rw $LOOP_BOOT "$ROOT/boot"
|
||||||
|
|
||||||
# mount binds
|
# mount binds
|
||||||
mount --bind /dev "$ROOT/dev/"
|
mount --bind /dev "$ROOT/dev/"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ ccflags-y:=-std=gnu99 -Wno-declaration-after-statement
|
|||||||
|
|
||||||
KPKG=raspberrypi-kernel_1.20171029-1.tar.gz
|
KPKG=raspberrypi-kernel_1.20171029-1.tar.gz
|
||||||
KURL=https://github.com/dbrgn/linux-rpi/archive/$(KPKG)
|
KURL=https://github.com/dbrgn/linux-rpi/archive/$(KPKG)
|
||||||
KDIR=linux-rpi-raspberrypi-kernel_1.20171029-1
|
KDIR=/tmp/rpi-kernel
|
||||||
export KERNEL=kernel7
|
export KERNEL=kernel7
|
||||||
|
|
||||||
KOPTS=ARCH=arm CROSS_COMPILE=$(CROSS) -C $(KDIR)
|
KOPTS=ARCH=arm CROSS_COMPILE=$(CROSS) -C $(KDIR)
|
||||||
@@ -14,7 +14,9 @@ all: $(KDIR)
|
|||||||
$(MAKE) $(KOPTS) M=$(DIR) modules
|
$(MAKE) $(KOPTS) M=$(DIR) modules
|
||||||
|
|
||||||
$(KDIR): $(KPKG)
|
$(KDIR): $(KPKG)
|
||||||
tar xf $(KPKG)
|
rm -rf $(KDIR)
|
||||||
|
mkdir -p $(KDIR)
|
||||||
|
tar xf $(KPKG) -C $(KDIR) --strip-components 1
|
||||||
$(MAKE) $(KOPTS) bcm2709_defconfig
|
$(MAKE) $(KOPTS) bcm2709_defconfig
|
||||||
$(MAKE) $(KOPTS) modules_prepare
|
$(MAKE) $(KOPTS) modules_prepare
|
||||||
|
|
||||||
|
|||||||
@@ -46,9 +46,7 @@ module.exports = {
|
|||||||
|
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
configRestored: false,
|
|
||||||
confirmReset: false,
|
confirmReset: false,
|
||||||
configReset: false,
|
|
||||||
autoCheckUpgrade: true,
|
autoCheckUpgrade: true,
|
||||||
reset_variant: ''
|
reset_variant: ''
|
||||||
}
|
}
|
||||||
@@ -71,29 +69,31 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
restore: function (e) {
|
restore: function (e) {
|
||||||
var files = e.target.files || e.dataTransfer.files;
|
const files = e.target.files || e.dataTransfer.files;
|
||||||
if (!files.length) return;
|
if (!files.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var fr = new FileReader();
|
const fileReader = new FileReader();
|
||||||
fr.onload = function (e) {
|
fileReader.onload = async ({ target }) => {
|
||||||
var config;
|
let config;
|
||||||
try {
|
try {
|
||||||
config = JSON.parse(e.target.result);
|
config = JSON.parse(target.result);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
api.alert("Invalid config file");
|
api.alert("Invalid config file");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
api.put('config/save', config).done(function (data) {
|
try {
|
||||||
|
await api.put('config/save', config);
|
||||||
this.$dispatch('update');
|
this.$dispatch('update');
|
||||||
this.configRestored = true;
|
SvelteComponents.showDialog("Message", { title: "Success", message: "Configuration restored" })
|
||||||
|
} catch (error) {
|
||||||
}.bind(this)).fail(function (error) {
|
|
||||||
api.alert('Restore failed', error);
|
api.alert('Restore failed', error);
|
||||||
})
|
}
|
||||||
}.bind(this);
|
}
|
||||||
|
|
||||||
fr.readAsText(files[0]);
|
fileReader.readAsText(files[0]);
|
||||||
},
|
},
|
||||||
|
|
||||||
reset: async function () {
|
reset: async function () {
|
||||||
@@ -107,7 +107,7 @@ module.exports = {
|
|||||||
await api.put('config/save', config)
|
await api.put('config/save', config)
|
||||||
this.confirmReset = false;
|
this.confirmReset = false;
|
||||||
this.$dispatch('update');
|
this.$dispatch('update');
|
||||||
this.configRestored = true;
|
SvelteComponents.showDialog("Message", { title: "Success", message: "Configuration restored" })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
api.alert('Restore failed');
|
api.alert('Restore failed');
|
||||||
console.error('Restore failed', err);
|
console.error('Restore failed', err);
|
||||||
|
|||||||
@@ -106,8 +106,7 @@ module.exports = new Vue({
|
|||||||
firmwareUpgrading: false,
|
firmwareUpgrading: false,
|
||||||
checkedUpgrade: false,
|
checkedUpgrade: false,
|
||||||
firmwareName: "",
|
firmwareName: "",
|
||||||
latestVersion: "",
|
latestVersion: ""
|
||||||
confirmShutdown: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -283,10 +282,17 @@ module.exports = new Vue({
|
|||||||
},
|
},
|
||||||
|
|
||||||
show_upgrade: function () {
|
show_upgrade: function () {
|
||||||
if (!this.latestVersion) return false;
|
if (!this.latestVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return is_newer_version(this.config.version, this.latestVersion);
|
return is_newer_version(this.config.version, this.latestVersion);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showShutdownDialog: function () {
|
||||||
|
SvelteComponents.showDialog("Shutdown");
|
||||||
|
},
|
||||||
|
|
||||||
update: async function () {
|
update: async function () {
|
||||||
const config = await api.get("config/load");
|
const config = await api.get("config/load");
|
||||||
|
|
||||||
@@ -303,16 +309,6 @@ module.exports = new Vue({
|
|||||||
SvelteComponents.handleConfigUpdate(this.config);
|
SvelteComponents.handleConfigUpdate(this.config);
|
||||||
},
|
},
|
||||||
|
|
||||||
shutdown: function () {
|
|
||||||
this.confirmShutdown = false;
|
|
||||||
api.put("shutdown");
|
|
||||||
},
|
|
||||||
|
|
||||||
reboot: function () {
|
|
||||||
this.confirmShutdown = false;
|
|
||||||
api.put("reboot");
|
|
||||||
},
|
|
||||||
|
|
||||||
connect: function () {
|
connect: function () {
|
||||||
this.sock = new Sock(`//${location.host}/sockjs`);
|
this.sock = new Sock(`//${location.host}/sockjs`);
|
||||||
|
|
||||||
|
|||||||
@@ -39,17 +39,7 @@ module.exports = {
|
|||||||
jog_adjust: parseInt(cookie.get('jog-adjust', 2)),
|
jog_adjust: parseInt(cookie.get('jog-adjust', 2)),
|
||||||
deleteGCode: false,
|
deleteGCode: false,
|
||||||
tab: 'auto',
|
tab: 'auto',
|
||||||
toolpath_msg: {
|
|
||||||
x: false,
|
|
||||||
y: false,
|
|
||||||
z: false,
|
|
||||||
a: false,
|
|
||||||
b: false,
|
|
||||||
c: false
|
|
||||||
},
|
|
||||||
ask_home: true,
|
ask_home: true,
|
||||||
ask_zero_xy_msg: false,
|
|
||||||
ask_zero_z_msg: false,
|
|
||||||
showGcodeMessage: false
|
showGcodeMessage: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -244,7 +234,6 @@ module.exports = {
|
|||||||
SvelteComponents.registerControllerMethods({
|
SvelteComponents.registerControllerMethods({
|
||||||
stop: (...args) => this.stop(...args),
|
stop: (...args) => this.stop(...args),
|
||||||
send: (...args) => this.send(...args),
|
send: (...args) => this.send(...args),
|
||||||
goto_zero: (...args) => this.goto_zero(...args),
|
|
||||||
isAxisHomed: (axis) => this[axis].homed,
|
isAxisHomed: (axis) => this[axis].homed,
|
||||||
unhome: (...args) => this.unhome(...args),
|
unhome: (...args) => this.unhome(...args),
|
||||||
set_position: (...args) => this.set_position(...args),
|
set_position: (...args) => this.set_position(...args),
|
||||||
@@ -253,18 +242,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
goto_zero(zero_x, zero_y, zero_z, zero_a) {
|
|
||||||
const xcmd = zero_x ? "X0" : "";
|
|
||||||
const ycmd = zero_y ? "Y0" : "";
|
|
||||||
const zcmd = zero_z ? "Z0" : "";
|
|
||||||
const acmd = zero_a ? "A0" : "";
|
|
||||||
|
|
||||||
this.ask_zero_xy_msg = false;
|
|
||||||
this.ask_zero_z_msg = false;
|
|
||||||
|
|
||||||
this.send('G90\nG0' + xcmd + ycmd + zcmd + acmd + '\n');
|
|
||||||
},
|
|
||||||
|
|
||||||
getJogIncrStyle(value) {
|
getJogIncrStyle(value) {
|
||||||
const weight = `font-weight:${this.jog_incr === value ? 'bold' : 'normal'}`;
|
const weight = `font-weight:${this.jog_incr === value ? 'bold' : 'normal'}`;
|
||||||
const color = this.jog_incr === value ? "color:#0078e7" : "";
|
const color = this.jog_incr === value ? "color:#0078e7" : "";
|
||||||
@@ -436,8 +413,12 @@ module.exports = {
|
|||||||
SvelteComponents.showDialog("SetAxisPosition", { axis });
|
SvelteComponents.showDialog("SetAxisPosition", { axis });
|
||||||
},
|
},
|
||||||
|
|
||||||
show_toolpath_msg: function (axis) {
|
showMoveToZeroDialog: function (axes) {
|
||||||
this.toolpath_msg[axis] = true;
|
SvelteComponents.showDialog("MoveToZero", { axes });
|
||||||
|
},
|
||||||
|
|
||||||
|
showToolpathMessageDialog: function (axis) {
|
||||||
|
SvelteComponents.showDialog("Message", { title: this[axis].toolmsg });
|
||||||
},
|
},
|
||||||
|
|
||||||
set_position: function (axis, position) {
|
set_position: function (axis, position) {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ function uuid(length) {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
if (typeof cookie_get('client-id') == 'undefined') {
|
if (typeof cookie_get('client-id') == 'undefined') {
|
||||||
cookie_set('client-id', uuid(), 10000);
|
cookie_set('client-id', uuid(), 10000);
|
||||||
|
|||||||
@@ -65,16 +65,8 @@ html(lang="en")
|
|||||||
li.pure-menu-heading
|
li.pure-menu-heading
|
||||||
a.pure-menu-link(href="#help") Help
|
a.pure-menu-link(href="#help") Help
|
||||||
|
|
||||||
button.pure-button.pure-button-primary(@click="confirmShutdown = true", style="width: 100%")
|
button.pure-button.pure-button-primary(@click="showShutdownDialog", style="width: 100%")
|
||||||
.fa.fa-power-off
|
.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
|
|
||||||
|
|
||||||
|
|
||||||
#main
|
#main
|
||||||
.header
|
.header
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ script#admin-general-view-template(type="text/x-template")
|
|||||||
label.pure-button.pure-button-primary(@click="restore_config") Restore
|
label.pure-button.pure-button-primary(@click="restore_config") Restore
|
||||||
form.restore-config.file-upload
|
form.restore-config.file-upload
|
||||||
input(type="file", accept=".json", @change="restore")
|
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
|
button.pure-button.pure-button-primary(@click="confirmReset = true") Reset
|
||||||
message(:show.sync="confirmReset")
|
message(:show.sync="confirmReset")
|
||||||
@@ -70,10 +67,6 @@ script#admin-general-view-template(type="text/x-template")
|
|||||||
button.pure-button(@click="confirmReset = false") Cancel
|
button.pure-button(@click="confirmReset = false") Cancel
|
||||||
button.pure-button.pure-button-primary(@click="reset") Reset
|
button.pure-button.pure-button-primary(@click="reset") Reset
|
||||||
|
|
||||||
message(:show.sync="configReset")
|
|
||||||
h3(slot="header") Success
|
|
||||||
p(slot="body") Configuration reset.
|
|
||||||
|
|
||||||
h2 Debugging
|
h2 Debugging
|
||||||
a(href="/api/log", target="_blank")
|
a(href="/api/log", target="_blank")
|
||||||
button.pure-button.pure-button-primary View Log
|
button.pure-button.pure-button-primary View Log
|
||||||
|
|||||||
@@ -10,32 +10,6 @@ script#control-view-template(type="text/x-template")
|
|||||||
div(slot="footer")
|
div(slot="footer")
|
||||||
label Simulating {{(toolpath_progress || 0) | percent}}
|
label Simulating {{(toolpath_progress || 0) | percent}}
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
table(style="table-layout: fixed; width: 100%;")
|
table(style="table-layout: fixed; width: 100%;")
|
||||||
tr(style="height: fit-content;")
|
tr(style="height: fit-content;")
|
||||||
td(style="white-space: nowrap; width: 410px;", rowspan="2")
|
td(style="white-space: nowrap; width: 410px;", rowspan="2")
|
||||||
@@ -60,12 +34,12 @@ script#control-view-template(type="text/x-template")
|
|||||||
td(style="height:100px",align="center")
|
td(style="height:100px",align="center")
|
||||||
button(@click="jog_fn(-1,0,0,0)") X-
|
button(@click="jog_fn(-1,0,0,0)") X-
|
||||||
td(style="height:100px",align="center")
|
td(style="height:100px",align="center")
|
||||||
button(@click="ask_zero_xy_msg = true")
|
button(@click="showMoveToZeroDialog('xy')")
|
||||||
.fa.fa-bullseye(style="font-size: 173%")
|
.fa.fa-bullseye(style="font-size: 173%")
|
||||||
td(style="height:100px",align="center")
|
td(style="height:100px",align="center")
|
||||||
button(@click="jog_fn(1,0,0,0)") X+
|
button(@click="jog_fn(1,0,0,0)") X+
|
||||||
td(style="height:100px",align="center")
|
td(style="height:100px",align="center")
|
||||||
button(@click='ask_zero_z_msg = true') Z0
|
button(@click="showMoveToZeroDialog('z')") Z0
|
||||||
tr
|
tr
|
||||||
td(style="height:100px",align="center")
|
td(style="height:100px",align="center")
|
||||||
button(@click="jog_fn(-1,-1,0,0)")
|
button(@click="jog_fn(-1,-1,0,0)")
|
||||||
@@ -133,20 +107,10 @@ script#control-view-template(type="text/x-template")
|
|||||||
td.state
|
td.state
|
||||||
.fa(:class=`'fa-' + ${axis}.icon`)
|
.fa(:class=`'fa-' + ${axis}.icon`)
|
||||||
| {{#{axis}.state}}
|
| {{#{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`)
|
.fa(:class=`'fa-' + ${axis}.ticon`)
|
||||||
| {{#{axis}.tstate}}
|
| {{#{axis}.tstate}}
|
||||||
|
|
||||||
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
|
th.actions
|
||||||
button.pure-button(:disabled="!can_set_axis",
|
button.pure-button(:disabled="!can_set_axis",
|
||||||
title=`Set {{'${axis}' | upper}} axis position.`,
|
title=`Set {{'${axis}' | upper}} axis position.`,
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
script#settings-view-template(type="text/x-template")
|
script#settings-view-template(type="text/x-template")
|
||||||
#settings
|
#settings
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import glob
|
|||||||
import tornado
|
import tornado
|
||||||
from tornado import gen
|
from tornado import gen
|
||||||
from tornado.web import HTTPError
|
from tornado.web import HTTPError
|
||||||
|
from tornado.escape import url_unescape;
|
||||||
|
|
||||||
def safe_remove(path):
|
def safe_remove(path):
|
||||||
try:
|
try:
|
||||||
@@ -20,7 +20,8 @@ class FileHandler(bbctrl.APIHandler):
|
|||||||
if self.request.method == 'PUT':
|
if self.request.method == 'PUT':
|
||||||
self.request.connection.set_max_body_size(2 ** 30)
|
self.request.connection.set_max_body_size(2 ** 30)
|
||||||
|
|
||||||
self.uploadFilename = self.request.path.split('/')[-1] \
|
filename = self.request.path.split('/')[-1]
|
||||||
|
self.uploadFilename = url_unescape(filename) \
|
||||||
.replace('\\', '/') \
|
.replace('\\', '/') \
|
||||||
.replace('#', '-') \
|
.replace('#', '-') \
|
||||||
.replace('?', '-')
|
.replace('?', '-')
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class Jog(inevent.JogHandler):
|
|||||||
|
|
||||||
config = {
|
config = {
|
||||||
"Logitech Logitech RumblePad 2 USB": {
|
"Logitech Logitech RumblePad 2 USB": {
|
||||||
"deadband": 0.1,
|
"deadband": 0.15,
|
||||||
"axes": [ABS_X, ABS_Y, ABS_RZ, ABS_Z],
|
"axes": [ABS_X, ABS_Y, ABS_RZ, ABS_Z],
|
||||||
"dir": [1, -1, -1, 1],
|
"dir": [1, -1, -1, 1],
|
||||||
"arrows": [ABS_HAT0X, ABS_HAT0Y],
|
"arrows": [ABS_HAT0X, ABS_HAT0Y],
|
||||||
@@ -46,7 +46,7 @@ class Jog(inevent.JogHandler):
|
|||||||
},
|
},
|
||||||
|
|
||||||
"default": {
|
"default": {
|
||||||
"deadband": 0.1,
|
"deadband": 0.15,
|
||||||
"axes": [ABS_X, ABS_Y, ABS_RY, ABS_RX],
|
"axes": [ABS_X, ABS_Y, ABS_RY, ABS_RX],
|
||||||
"dir": [1, -1, -1, 1],
|
"dir": [1, -1, -1, 1],
|
||||||
"arrows": [ABS_HAT0X, ABS_HAT0Y],
|
"arrows": [ABS_HAT0X, ABS_HAT0Y],
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import json
|
|||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import bbctrl
|
|
||||||
import iw_parse
|
import iw_parse
|
||||||
from tornado import gen
|
import threading
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
from watchdog.events import FileSystemEventHandler
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ def call_get_output(cmd):
|
|||||||
|
|
||||||
|
|
||||||
class UploadChangeHandler(FileSystemEventHandler):
|
class UploadChangeHandler(FileSystemEventHandler):
|
||||||
|
|
||||||
def __init__(self, state):
|
def __init__(self, state):
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
@@ -28,7 +30,9 @@ class UploadChangeHandler(FileSystemEventHandler):
|
|||||||
|
|
||||||
|
|
||||||
class State(object):
|
class State(object):
|
||||||
|
|
||||||
def __init__(self, ctrl):
|
def __init__(self, ctrl):
|
||||||
|
self.lock = threading.Lock()
|
||||||
self.ctrl = ctrl
|
self.ctrl = ctrl
|
||||||
self.log = ctrl.log.get('State')
|
self.log = ctrl.log.get('State')
|
||||||
|
|
||||||
@@ -72,39 +76,40 @@ class State(object):
|
|||||||
self.load_files()
|
self.load_files()
|
||||||
|
|
||||||
observer = Observer()
|
observer = Observer()
|
||||||
observer.schedule(UploadChangeHandler(
|
observer.schedule(UploadChangeHandler(self),
|
||||||
self), self.ctrl.get_upload(), recursive=True)
|
self.ctrl.get_upload(),
|
||||||
|
recursive=True)
|
||||||
observer.start()
|
observer.start()
|
||||||
|
|
||||||
self._updateNetworkInfo()
|
threading.Thread(target=self._updateNetworkInfo, daemon=True).start()
|
||||||
|
|
||||||
@gen.coroutine
|
|
||||||
def _updateNetworkInfo(self):
|
def _updateNetworkInfo(self):
|
||||||
try:
|
while True:
|
||||||
ipAddresses = call_get_output(['hostname', '-I']).split()
|
try:
|
||||||
except:
|
ipAddresses = call_get_output(['hostname', '-I']).split()
|
||||||
ipAddresses = ""
|
except:
|
||||||
|
ipAddresses = ""
|
||||||
|
|
||||||
hostname = socket.gethostname()
|
hostname = socket.gethostname()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
wifi = json.loads(call_get_output(['config-wifi', '-j']))
|
wifi = json.loads(call_get_output(['config-wifi', '-j']))
|
||||||
except:
|
except:
|
||||||
wifi = {'enabled': False}
|
wifi = {'enabled': False}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
lines = iw_parse.call_iwlist().decode("utf-8").split("\n")
|
lines = iw_parse.call_iwlist().decode("utf-8").split("\n")
|
||||||
wifi['networks'] = iw_parse.get_parsed_cells(lines)
|
wifi['networks'] = iw_parse.get_parsed_cells(lines)
|
||||||
except:
|
except:
|
||||||
wifi['networks'] = []
|
wifi['networks'] = []
|
||||||
|
|
||||||
self.set('networkInfo', {
|
self.set('networkInfo', {
|
||||||
'ipAddresses': ipAddresses,
|
'ipAddresses': ipAddresses,
|
||||||
'hostname': hostname,
|
'hostname': hostname,
|
||||||
'wifi': wifi
|
'wifi': wifi
|
||||||
})
|
})
|
||||||
|
|
||||||
self.timeout = self.ctrl.ioloop.call_later(5, self._updateNetworkInfo)
|
time.sleep(5)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
# Unhome all motors
|
# Unhome all motors
|
||||||
@@ -217,15 +222,20 @@ class State(object):
|
|||||||
self.callbacks[self.resolve(name)] = cb
|
self.callbacks[self.resolve(name)] = cb
|
||||||
|
|
||||||
def set(self, name, value):
|
def set(self, name, value):
|
||||||
name = self.resolve(name)
|
self.lock.acquire()
|
||||||
|
try:
|
||||||
|
name = self.resolve(name)
|
||||||
|
|
||||||
if not name in self.vars or self.vars[name] != value:
|
if not name in self.vars or self.vars[name] != value:
|
||||||
self.vars[name] = value
|
self.vars[name] = value
|
||||||
self.changes[name] = value
|
self.changes[name] = value
|
||||||
|
|
||||||
# Trigger listener notify
|
# Trigger listener notify
|
||||||
if self.timeout is None:
|
if self.timeout is None:
|
||||||
self.timeout = self.ctrl.ioloop.call_later(0.25, self._notify)
|
self.timeout = self.ctrl.ioloop.call_later(
|
||||||
|
0.25, self._notify)
|
||||||
|
finally:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
def update(self, update):
|
def update(self, update):
|
||||||
for name, value in update.items():
|
for name, value in update.items():
|
||||||
@@ -377,7 +387,7 @@ class State(object):
|
|||||||
softMax = int(self.get(axis + '_tm', 0))
|
softMax = int(self.get(axis + '_tm', 0))
|
||||||
if softMax <= softMin + 1:
|
if softMax <= softMin + 1:
|
||||||
return 'max-soft-limit must be at least 1mm greater ' \
|
return 'max-soft-limit must be at least 1mm greater ' \
|
||||||
'than min-soft-limit'
|
'than min-soft-limit'
|
||||||
|
|
||||||
def motor_enabled(self, motor):
|
def motor_enabled(self, motor):
|
||||||
return bool(int(self.vars.get('%dme' % motor, 0)))
|
return bool(int(self.vars.get('%dme' % motor, 0)))
|
||||||
|
|||||||
@@ -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 logging
|
import logging
|
||||||
|
|
||||||
from inevent.Constants import *
|
from inevent.Constants import *
|
||||||
@@ -97,7 +70,31 @@ class JogHandler:
|
|||||||
|
|
||||||
# Process event
|
# Process event
|
||||||
if event.type == EV_ABS and event.code in config['axes']:
|
if event.type == EV_ABS and event.code in config['axes']:
|
||||||
pass
|
old_axes = list(self.axes)
|
||||||
|
deadband = config['deadband']
|
||||||
|
axis = config['axes'].index(event.code)
|
||||||
|
|
||||||
|
self.axes[axis] = event.stream.state.abs[event.code]
|
||||||
|
self.axes[axis] *= config['dir'][axis]
|
||||||
|
|
||||||
|
value = abs(self.axes[axis])
|
||||||
|
if value >= deadband:
|
||||||
|
sign = -1 if self.axes[axis] < 0 else 1
|
||||||
|
delta = value - deadband
|
||||||
|
range = 1 - deadband
|
||||||
|
|
||||||
|
# Scale the new value to the available range (full range, minus the deadband)
|
||||||
|
self.axes[axis] = (delta * sign) / range
|
||||||
|
else:
|
||||||
|
self.axes[axis] = 0
|
||||||
|
|
||||||
|
if self.horizontal_lock and axis not in [0, 3]:
|
||||||
|
self.axes[axis] = 0
|
||||||
|
|
||||||
|
if self.vertical_lock and axis not in [1, 2]:
|
||||||
|
self.axes[axis] = 0
|
||||||
|
|
||||||
|
if old_axes[axis] != self.axes[axis]: changed = True
|
||||||
|
|
||||||
elif event.type == EV_KEY and event.code in config['speed']:
|
elif event.type == EV_KEY and event.code in config['speed']:
|
||||||
old_speed = self.speed
|
old_speed = self.speed
|
||||||
@@ -115,22 +112,4 @@ class JogHandler:
|
|||||||
|
|
||||||
log.debug(event_to_string(event, state))
|
log.debug(event_to_string(event, state))
|
||||||
|
|
||||||
# Update axes
|
|
||||||
old_axes = list(self.axes)
|
|
||||||
|
|
||||||
for axis in range(4):
|
|
||||||
self.axes[axis] = event.stream.state.abs[config['axes'][axis]]
|
|
||||||
self.axes[axis] *= config['dir'][axis]
|
|
||||||
|
|
||||||
if abs(self.axes[axis]) < config['deadband']:
|
|
||||||
self.axes[axis] = 0
|
|
||||||
|
|
||||||
if self.horizontal_lock and axis not in [0, 3]:
|
|
||||||
self.axes[axis] = 0
|
|
||||||
|
|
||||||
if self.vertical_lock and axis not in [1, 2]:
|
|
||||||
self.axes[axis] = 0
|
|
||||||
|
|
||||||
if old_axes != self.axes: changed = True
|
|
||||||
|
|
||||||
if changed: self.changed()
|
if changed: self.changed()
|
||||||
|
|||||||
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?
|
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=
|
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.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=
|
"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,
|
[];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: "+
|
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]';
|
var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]';
|
||||||
|
|
||||||
// detect devtools
|
// detect devtools
|
||||||
var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
|
var devtools = false;
|
||||||
|
|
||||||
// UA sniffing for working around browser-specific quirks
|
// UA sniffing for working around browser-specific quirks
|
||||||
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
|
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
|
||||||
@@ -9677,14 +9677,5 @@ var template = Object.freeze({
|
|||||||
|
|
||||||
Vue.version = '1.0.17';
|
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;
|
return Vue;
|
||||||
|
|
||||||
}));
|
}));
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import TextField from "@smui/textfield";
|
|
||||||
import Select, { Option } from "@smui/select";
|
|
||||||
import type { MenuComponentDev } from "@smui/menu";
|
|
||||||
import Menu from "@smui/menu";
|
|
||||||
import List, { Item, Text } from "@smui/list";
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import { set_input_value } from "svelte/internal";
|
|
||||||
|
|
||||||
let menu: MenuComponentDev;
|
|
||||||
|
|
||||||
type Option = {
|
|
||||||
value: number;
|
|
||||||
label: string;
|
|
||||||
metric: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export let label: string;
|
|
||||||
export let value: number;
|
|
||||||
export let metric: boolean;
|
|
||||||
export let options: Option[];
|
|
||||||
|
|
||||||
let textValue = "";
|
|
||||||
|
|
||||||
$: if (textValue) {
|
|
||||||
value = Number.parseFloat(textValue) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
textValue = value?.toString() || "";
|
|
||||||
});
|
|
||||||
|
|
||||||
function onOptionSelected(option: Option) {
|
|
||||||
textValue = option.value.toString();
|
|
||||||
metric = option.metric;
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterKeys(event) {
|
|
||||||
const input = event.target;
|
|
||||||
|
|
||||||
if (input.value.match(/[^0-9.]/)) {
|
|
||||||
input.value = input.value.replace(/[^0-9.]/g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.value.match(/\.[^.]*\./)) {
|
|
||||||
input.value = input.value.replace(/(\.[^.]*)\./g, "$1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="value-and-unit">
|
|
||||||
<TextField
|
|
||||||
{label}
|
|
||||||
on:keypress={filterKeys}
|
|
||||||
on:keyup={filterKeys}
|
|
||||||
bind:value={textValue}
|
|
||||||
on:click={() => menu.setOpen(true)}
|
|
||||||
/>
|
|
||||||
<Select bind:value={metric}>
|
|
||||||
<Option value={true}>mm</Option>
|
|
||||||
<Option value={false}>in</Option>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<Menu bind:this={menu} anchorCorner="BOTTOM_LEFT">
|
|
||||||
<List>
|
|
||||||
{#each options as option}
|
|
||||||
<Item on:SMUI:action={() => onOptionSelected(option)}>
|
|
||||||
<Text>{option.label}</Text>
|
|
||||||
</Item>
|
|
||||||
{/each}
|
|
||||||
</List>
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
:global {
|
|
||||||
.value-and-unit {
|
|
||||||
display: flex;
|
|
||||||
column-gap: 10px;
|
|
||||||
|
|
||||||
.mdc-select {
|
|
||||||
max-width: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smui-select--standard .mdc-select__dropdown-icon {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</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, (newValue) => (value = newValue)]]}
|
||||||
|
{...$$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: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
import TextField from "@smui/textfield";
|
import TextField from "@smui/textfield";
|
||||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||||
import * as api from "$lib/api";
|
import * as api from "$lib/api";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
// https://man7.org/linux/man-pages/man7/hostname.7.html
|
// https://man7.org/linux/man-pages/man7/hostname.7.html
|
||||||
//
|
//
|
||||||
@@ -57,7 +58,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<MessageDialog open={rebooting} title="Rebooting">
|
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||||
Rebooting to apply the hostname change...
|
Rebooting to apply the hostname change...
|
||||||
</MessageDialog>
|
</MessageDialog>
|
||||||
|
|
||||||
@@ -72,15 +73,12 @@
|
|||||||
<Content id="change-hostname-dialog-content">
|
<Content id="change-hostname-dialog-content">
|
||||||
<TextField
|
<TextField
|
||||||
bind:value={hostname}
|
bind:value={hostname}
|
||||||
|
use={[[virtualKeyboardChange, (newValue) => (hostname = newValue)]]}
|
||||||
label="New Hostname"
|
label="New Hostname"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p>
|
|
||||||
<em>Clicking Confirm will reboot the controller to apply the change.</em>
|
|
||||||
</p>
|
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<Actions>
|
<Actions>
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
import SetTimeDialog from "./SetTimeDialog.svelte";
|
import SetTimeDialog from "./SetTimeDialog.svelte";
|
||||||
import ManualHomeAxisDialog from "./ManualHomeAxisDialog.svelte";
|
import ManualHomeAxisDialog from "./ManualHomeAxisDialog.svelte";
|
||||||
import SetAxisPositionDialog from "./SetAxisPositionDialog.svelte";
|
import SetAxisPositionDialog from "./SetAxisPositionDialog.svelte";
|
||||||
|
import MoveToZeroDialog from "./MoveToZeroDialog.svelte";
|
||||||
|
import ShutdownDialog from "./ShutdownDialog.svelte";
|
||||||
|
import MessageDialog from "./MessageDialog.svelte";
|
||||||
|
|
||||||
const HomeMachineDialogProps = writable<HomeMachineDialogPropsType>();
|
const HomeMachineDialogProps = writable<HomeMachineDialogPropsType>();
|
||||||
type HomeMachineDialogPropsType = {
|
type HomeMachineDialogPropsType = {
|
||||||
@@ -49,6 +52,25 @@
|
|||||||
axis: string;
|
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(
|
export function showDialog(
|
||||||
dialog: "HomeMachine",
|
dialog: "HomeMachine",
|
||||||
props: Omit<HomeMachineDialogPropsType, "open">
|
props: Omit<HomeMachineDialogPropsType, "open">
|
||||||
@@ -84,6 +106,21 @@
|
|||||||
props: Omit<SetAxisPositionDialogPropsType, "open">
|
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) {
|
export function showDialog(dialog: string, props: any) {
|
||||||
switch (dialog) {
|
switch (dialog) {
|
||||||
case "HomeMachine":
|
case "HomeMachine":
|
||||||
@@ -114,12 +151,84 @@
|
|||||||
SetAxisPositionDialogProps.set({ ...props, open: true });
|
SetAxisPositionDialogProps.set({ ...props, open: true });
|
||||||
break;
|
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:
|
default:
|
||||||
throw new Error(`Unknown dialog '${dialog}`);
|
throw new Error(`Unknown dialog '${dialog}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</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} />
|
<HomeMachineDialog {...$HomeMachineDialogProps} />
|
||||||
<ProbeDialog {...$ProbeDialogProps} />
|
<ProbeDialog {...$ProbeDialogProps} />
|
||||||
<ScreenRotationDialog {...$ScreenRotationDialogProps} />
|
<ScreenRotationDialog {...$ScreenRotationDialogProps} />
|
||||||
@@ -127,3 +236,6 @@
|
|||||||
<SetTimeDialog {...$SetTimeDialogProps} />
|
<SetTimeDialog {...$SetTimeDialogProps} />
|
||||||
<ManualHomeAxisDialog {...$ManualHomeAxisDialogProps} />
|
<ManualHomeAxisDialog {...$ManualHomeAxisDialogProps} />
|
||||||
<SetAxisPositionDialog {...$SetAxisPositionDialogProps} />
|
<SetAxisPositionDialog {...$SetAxisPositionDialogProps} />
|
||||||
|
<MoveToZeroDialog {...$MoveToZeroDialogProps} />
|
||||||
|
<ShutdownDialog {...$ShutdownDialogProps} />
|
||||||
|
<MessageDialog {...$MessageDialogProps} />
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import TextField from "@smui/textfield";
|
import TextField from "@smui/textfield";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open: boolean;
|
export let open: boolean;
|
||||||
export let axis = "";
|
export let axis = "";
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
label="Absolute"
|
label="Absolute"
|
||||||
type="number"
|
type="number"
|
||||||
bind:value
|
bind:value
|
||||||
|
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
||||||
variant="filled"
|
variant="filled"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Dialog, { Title, Content } from "@smui/dialog";
|
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
||||||
|
import Button, { Label } from "@smui/button";
|
||||||
|
|
||||||
export let open: boolean;
|
export let open: boolean;
|
||||||
export let title: string;
|
export let title = "";
|
||||||
|
export let message = "";
|
||||||
|
export let noaction = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
@@ -13,7 +16,16 @@
|
|||||||
aria-describedby="message-dialog-content"
|
aria-describedby="message-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="message-dialog-title">{title}</Title>
|
<Title id="message-dialog-title">{title}</Title>
|
||||||
|
|
||||||
<Content id="message-dialog-content">
|
<Content id="message-dialog-content">
|
||||||
<slot />
|
<slot>{message}</slot>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
|
{#if !noaction}
|
||||||
|
<Actions>
|
||||||
|
<Button defaultAction>
|
||||||
|
<Label>OK</Label>
|
||||||
|
</Button>
|
||||||
|
</Actions>
|
||||||
|
{/if}
|
||||||
</Dialog>
|
</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>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
<script type="ts">
|
<script type="ts">
|
||||||
import DimensionInput from "$components/DimensionInput.svelte";
|
|
||||||
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
import { waitForChange } from "$lib/StoreHelpers";
|
import { waitForChange } from "$lib/StoreHelpers";
|
||||||
@@ -13,14 +12,23 @@
|
|||||||
probingFailed,
|
probingFailed,
|
||||||
probingStarted,
|
probingStarted,
|
||||||
} from "$lib/ControllerState";
|
} from "$lib/ControllerState";
|
||||||
|
import { numberWithUnit } from "$lib/RegexHelpers";
|
||||||
|
import TextFieldWithOptions from "$components/TextFieldWithOptions.svelte";
|
||||||
|
|
||||||
type Step =
|
const ValidSteps = [
|
||||||
| "None"
|
"None",
|
||||||
| "CheckProbe"
|
"CheckProbe",
|
||||||
| "BitDimensions"
|
"BitDimensions",
|
||||||
| "PlaceProbeBlock"
|
"PlaceProbeBlock",
|
||||||
| "Probe"
|
"Probe",
|
||||||
| "Done";
|
"Done",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
type Step = typeof ValidSteps[number];
|
||||||
|
|
||||||
|
function isStep(str): str is Step {
|
||||||
|
return ValidSteps.includes(str);
|
||||||
|
}
|
||||||
|
|
||||||
const stepLabels: Record<Step, string> = {
|
const stepLabels: Record<Step, string> = {
|
||||||
None: "",
|
None: "",
|
||||||
@@ -34,21 +42,16 @@
|
|||||||
const cancelled = writable(false);
|
const cancelled = writable(false);
|
||||||
const userAcknowledged = writable(false);
|
const userAcknowledged = writable(false);
|
||||||
|
|
||||||
const cutterDiameterOptions = [
|
const imperialBits = ["1/2 in", "1/4 in", "1/8 in", "1/16", "1/32"];
|
||||||
{ value: 0.5, label: '1/2 "', metric: false },
|
const metricBits = ["10 mm", "8 mm", "6 mm", "3 mm"];
|
||||||
{ value: 0.25, label: '1/4 "', metric: false },
|
|
||||||
{ value: 0.125, label: '1/8 "', metric: false },
|
|
||||||
{ value: 10, label: "10 mm", metric: true },
|
|
||||||
{ value: 6, label: "6 mm", metric: true },
|
|
||||||
{ value: 3, label: "10 mm", metric: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
export let open;
|
export let open;
|
||||||
export let probeType: "xyz" | "z";
|
export let probeType: "xyz" | "z";
|
||||||
let currentStep: Step = "None";
|
let currentStep: Step = "None";
|
||||||
let cutterDiameter: number;
|
let cutterDiameterString: string = "";
|
||||||
|
let cutterDiameterMetric: number;
|
||||||
let showCancelButton = true;
|
let showCancelButton = true;
|
||||||
let steps: Array<Step> = [];
|
let steps: Step[] = [];
|
||||||
let nextButton = {
|
let nextButton = {
|
||||||
label: "Next",
|
label: "Next",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
@@ -56,10 +59,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
$: metric = $Config.settings?.units === "METRIC";
|
$: metric = $Config.settings?.units === "METRIC";
|
||||||
|
$: cutterDiameterMetric = numberWithUnit
|
||||||
|
.parse(cutterDiameterString)
|
||||||
|
?.toMetric();
|
||||||
|
|
||||||
$: if (open) {
|
$: if (open) {
|
||||||
cutterDiameter =
|
cutterDiameterString = localStorage.getItem("cutterDiameter") ?? "";
|
||||||
Number.parseFloat(localStorage.getItem("cutterDiameter")) || null;
|
|
||||||
|
|
||||||
// Svelte appears not to like it when you invoke
|
// Svelte appears not to like it when you invoke
|
||||||
// an async function from a reactive statement, so we
|
// an async function from a reactive statement, so we
|
||||||
@@ -67,32 +72,31 @@
|
|||||||
requestAnimationFrame(begin);
|
requestAnimationFrame(begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if (cutterDiameter) {
|
$: if (cutterDiameterString) {
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeSkippedSteps(steps: Step[]): Step[] {
|
|
||||||
return steps.filter((x) => x);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function begin() {
|
async function begin() {
|
||||||
try {
|
try {
|
||||||
$probingActive = true;
|
$probingActive = true;
|
||||||
assertValidProbeType();
|
assertValidProbeType();
|
||||||
|
|
||||||
steps = removeSkippedSteps([
|
steps = [
|
||||||
"CheckProbe",
|
"CheckProbe",
|
||||||
probeType === "xyz" ? "BitDimensions" : undefined,
|
probeType === "xyz" ? "BitDimensions" : undefined,
|
||||||
"PlaceProbeBlock",
|
"PlaceProbeBlock",
|
||||||
"Probe",
|
"Probe",
|
||||||
"Done",
|
"Done",
|
||||||
]);
|
].filter<Step>(isStep);
|
||||||
|
|
||||||
await stepCompleted("CheckProbe", probeContacted);
|
await stepCompleted("CheckProbe", probeContacted);
|
||||||
|
|
||||||
if (probeType === "xyz") {
|
if (probeType === "xyz") {
|
||||||
await stepCompleted("BitDimensions", userAcknowledged);
|
await stepCompleted("BitDimensions", userAcknowledged);
|
||||||
localStorage.setItem("cutterDiameter", cutterDiameter.toString());
|
localStorage.setItem(
|
||||||
|
"cutterDiameter",
|
||||||
|
numberWithUnit.normalize(cutterDiameterString)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await stepCompleted("PlaceProbeBlock", userAcknowledged);
|
await stepCompleted("PlaceProbeBlock", userAcknowledged);
|
||||||
@@ -100,7 +104,7 @@
|
|||||||
await stepCompleted("Done", userAcknowledged);
|
await stepCompleted("Done", userAcknowledged);
|
||||||
|
|
||||||
if (probeType === "xyz") {
|
if (probeType === "xyz") {
|
||||||
ControllerMethods.goto_zero(1, 1, 0, 0);
|
ControllerMethods.gotoZero("xy");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message !== "cancelled") {
|
if (err.message !== "cancelled") {
|
||||||
@@ -177,11 +181,7 @@
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "BitDimensions":
|
case "BitDimensions":
|
||||||
nextButton.disabled = !(
|
nextButton.disabled = !isFinite(cutterDiameterMetric);
|
||||||
cutterDiameter !== null &&
|
|
||||||
cutterDiameter !== 0 &&
|
|
||||||
isFinite(cutterDiameter)
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Done":
|
case "Done":
|
||||||
@@ -204,8 +204,8 @@
|
|||||||
|
|
||||||
const cutterLength = 12.7;
|
const cutterLength = 12.7;
|
||||||
const zLift = 1;
|
const zLift = 1;
|
||||||
const xOffset = probeBlockWidth + cutterDiameter / 2.0;
|
const xOffset = probeBlockWidth + cutterDiameterMetric / 2.0;
|
||||||
const yOffset = probeBlockLength + cutterDiameter / 2.0;
|
const yOffset = probeBlockLength + cutterDiameterMetric / 2.0;
|
||||||
const zOffset = probeBlockHeight;
|
const zOffset = probeBlockHeight;
|
||||||
|
|
||||||
if (probeType === "z") {
|
if (probeType === "z") {
|
||||||
@@ -269,11 +269,11 @@
|
|||||||
scrimClickAction=""
|
scrimClickAction=""
|
||||||
aria-labelledby="probe-dialog-title"
|
aria-labelledby="probe-dialog-title"
|
||||||
aria-describedby="probe-dialog-content"
|
aria-describedby="probe-dialog-content"
|
||||||
surface$style="width: 700px; height: 400px; max-width: calc(100vw - 32px); overflow: visible;"
|
surface$style="width: 700px; max-width: calc(100vw - 32px);"
|
||||||
>
|
>
|
||||||
<Title id="probe-dialog-title">Probing {probeType?.toUpperCase()}</Title>
|
<Title id="probe-dialog-title">Probing {probeType?.toUpperCase()}</Title>
|
||||||
|
|
||||||
<Content id="probe-dialog-content" style="overflow: visible;">
|
<Content id="probe-dialog-content">
|
||||||
<div class="steps">
|
<div class="steps">
|
||||||
<p><b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b></p>
|
<p><b>Step {steps.indexOf(currentStep) + 1} of {steps.length}</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -282,48 +282,49 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="current-step">
|
<p>
|
||||||
<p>
|
{#if currentStep === "CheckProbe"}
|
||||||
{#if currentStep === "CheckProbe"}
|
Attach the probe magnet to the collet, then touch the probe block to the
|
||||||
Attach the probe magnet to the collet, then touch the probe block to
|
bit.
|
||||||
the bit.
|
{:else if currentStep === "BitDimensions"}
|
||||||
{:else if currentStep === "BitDimensions"}
|
<TextFieldWithOptions
|
||||||
<DimensionInput
|
label="Cutter diameter"
|
||||||
label="Cutter diameter"
|
variant="filled"
|
||||||
options={cutterDiameterOptions}
|
spellcheck="false"
|
||||||
bind:value={cutterDiameter}
|
style="width: 100%;"
|
||||||
{metric}
|
bind:value={cutterDiameterString}
|
||||||
/>
|
options={[imperialBits, metricBits]}
|
||||||
{:else if currentStep === "PlaceProbeBlock"}
|
valid={isFinite(cutterDiameterMetric)}
|
||||||
|
helperText={`Examples: 1/2", 10 mm, 0.25 in`}
|
||||||
|
/>
|
||||||
|
{:else if currentStep === "PlaceProbeBlock"}
|
||||||
|
{#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}
|
||||||
|
{:else if currentStep === "Probe"}
|
||||||
|
Probing in progress...
|
||||||
|
{:else if currentStep === "Done"}
|
||||||
|
{#if $probingFailed}
|
||||||
|
Could not find the probe block during probing!
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Make sure the tip of the bit is less than {metric ? "25mm" : "1 in"}
|
||||||
|
above the probe block, and try again.
|
||||||
|
</p>
|
||||||
|
{:else}
|
||||||
|
Don't forget to put away the probe!
|
||||||
|
|
||||||
{#if probeType === "xyz"}
|
{#if probeType === "xyz"}
|
||||||
Place the probe block face up, on the lower-left corner of your
|
<p>The machine will now move to the XY origin.</p>
|
||||||
workpiece.
|
|
||||||
{:else}
|
|
||||||
Place the probe block face down, with the bit above the recess.
|
|
||||||
{/if}
|
|
||||||
{:else if currentStep === "Probe"}
|
|
||||||
Probing in progress...
|
|
||||||
{:else if currentStep === "Done"}
|
|
||||||
{#if $probingFailed}
|
|
||||||
Could not find the probe block during probing!
|
|
||||||
|
|
||||||
<p>
|
<p>Watch your hands!</p>
|
||||||
Make sure the tip of the bit is less than {metric
|
|
||||||
? "25mm"
|
|
||||||
: "1 in"} above the probe block, and try again.
|
|
||||||
</p>
|
|
||||||
{:else}
|
|
||||||
Don't forget to put away the probe!
|
|
||||||
|
|
||||||
{#if probeType === "xyz"}
|
|
||||||
<p>The machine will now move to the XY origin.</p>
|
|
||||||
|
|
||||||
<p>Watch your hands!</p>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
{/if}
|
||||||
</div>
|
</p>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
<Actions>
|
<Actions>
|
||||||
@@ -358,12 +359,9 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.current-step {
|
.bit-dimensions {
|
||||||
flex-grow: 1;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
.mdc-text-field {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.steps {
|
.steps {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<MessageDialog open={rebooting} title="Rebooting">
|
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||||
Rebooting to apply the new screen rotation...
|
Rebooting to apply the new screen rotation...
|
||||||
</MessageDialog>
|
</MessageDialog>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import TextField from "@smui/textfield";
|
import TextField from "@smui/textfield";
|
||||||
import Button, { Label } from "@smui/button";
|
import Button, { Label } from "@smui/button";
|
||||||
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
import { ControllerMethods } from "$lib/RegisterControllerMethods";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open: boolean;
|
export let open: boolean;
|
||||||
export let axis = "";
|
export let axis = "";
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
label="Position"
|
label="Position"
|
||||||
type="number"
|
type="number"
|
||||||
bind:value
|
bind:value
|
||||||
|
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import CircularProgress from "@smui/circular-progress";
|
import CircularProgress from "@smui/circular-progress";
|
||||||
import VirtualList from "svelte-tiny-virtual-list";
|
import VirtualList from "svelte-tiny-virtual-list";
|
||||||
import * as api from "$lib/api";
|
import * as api from "$lib/api";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
const itemHeight = 35;
|
const itemHeight = 35;
|
||||||
|
|
||||||
@@ -153,6 +154,7 @@
|
|||||||
<Label>Date & Time</Label>
|
<Label>Date & Time</Label>
|
||||||
<TextField
|
<TextField
|
||||||
bind:value
|
bind:value
|
||||||
|
use={[[virtualKeyboardChange, (newValue) => (value = newValue)]]}
|
||||||
label="Time"
|
label="Time"
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
|
|||||||
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>
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
import MessageDialog from "$dialogs/MessageDialog.svelte";
|
||||||
import type { WifiNetwork } from "$lib/NetworkInfo";
|
import type { WifiNetwork } from "$lib/NetworkInfo";
|
||||||
import * as api from "$lib/api";
|
import * as api from "$lib/api";
|
||||||
|
import { virtualKeyboardChange } from "$lib/CustomActions";
|
||||||
|
|
||||||
export let open = false;
|
export let open = false;
|
||||||
export let network: WifiNetwork;
|
export let network: WifiNetwork;
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<MessageDialog open={rebooting} title="Rebooting">
|
<MessageDialog open={rebooting} title="Rebooting" noaction>
|
||||||
Rebooting to apply Wifi changes...
|
Rebooting to apply Wifi changes...
|
||||||
</MessageDialog>
|
</MessageDialog>
|
||||||
|
|
||||||
@@ -48,14 +49,16 @@
|
|||||||
aria-labelledby="wifi-connection-dialog-title"
|
aria-labelledby="wifi-connection-dialog-title"
|
||||||
aria-describedby="wifi-connection-dialog-content"
|
aria-describedby="wifi-connection-dialog-content"
|
||||||
>
|
>
|
||||||
<Title id="wifi-connection-dialog-title"
|
<Title id="wifi-connection-dialog-title">
|
||||||
>{connectToOrDisconnectFrom} {network.Name}</Title
|
{connectToOrDisconnectFrom}
|
||||||
>
|
{network.Name}
|
||||||
|
</Title>
|
||||||
|
|
||||||
<Content id="wifi-connection-dialog-content">
|
<Content id="wifi-connection-dialog-content">
|
||||||
{#if needPassword}
|
{#if needPassword}
|
||||||
<TextField
|
<TextField
|
||||||
bind:value={password}
|
bind:value={password}
|
||||||
|
use={[[virtualKeyboardChange, (newValue) => (password = newValue)]]}
|
||||||
label="Password"
|
label="Password"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
|
|||||||
9
src/svelte-components/src/lib/CustomActions.ts
Normal file
9
src/svelte-components/src/lib/CustomActions.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export function virtualKeyboardChange(node: HTMLElement, cb: (value: any) => void) {
|
||||||
|
const input = node.querySelector("input");
|
||||||
|
if (!input) {
|
||||||
|
console.error("Could not find the textfield's <input>:", node);
|
||||||
|
throw new Error("Could not find the textfield's <input>");
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addEventListener("keyup", () => cb(input.value));
|
||||||
|
}
|
||||||
90
src/svelte-components/src/lib/RegexHelpers.ts
Normal file
90
src/svelte-components/src/lib/RegexHelpers.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
type NumberWithUnit = {
|
||||||
|
value: number,
|
||||||
|
metric: boolean,
|
||||||
|
toMetric: () => number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberWithUnitToMetric() {
|
||||||
|
return this.metric ? this.value : this.value * 25.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPojo(value) {
|
||||||
|
if (value === null || typeof value !== "object") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.getPrototypeOf(value) === Object.prototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fractions = [
|
||||||
|
{ value: 0.75, formatted: "3/4" },
|
||||||
|
{ value: 0.625, formatted: "5/8" },
|
||||||
|
{ value: 0.5, formatted: "1/2" },
|
||||||
|
{ value: 0.375, formatted: "3/8" },
|
||||||
|
{ value: 0.25, formatted: "1/4" },
|
||||||
|
{ value: 0.1875, formatted: "3/16" },
|
||||||
|
{ value: 0.125, formatted: "1/8" },
|
||||||
|
{ value: 0.09375, formatted: "3/32" },
|
||||||
|
{ value: 0.0625, formatted: "1/16" },
|
||||||
|
{ value: 0.03125, formatted: "1/32" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function formatFraction(value: number) {
|
||||||
|
const fraction = fractions.find(f => f.value === value);
|
||||||
|
|
||||||
|
return fraction ? fraction.formatted : value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const numberWithUnit = {
|
||||||
|
regex: /^\s*(?:(\d+)\s*\/\s*(\d+)|(\d*\.\d+)|(\d+(?:\.\d+)?))\s*("|in|inch|inches|mm|millimeters)\s*$/,
|
||||||
|
parse: function (str: string) {
|
||||||
|
let [, numerator, denominator, decimal1, decimal2, unit]: any = str?.match(numberWithUnit.regex) ?? [];
|
||||||
|
|
||||||
|
numerator = Number.parseFloat(numerator)
|
||||||
|
denominator = Number.parseFloat(denominator)
|
||||||
|
decimal1 = Number.parseFloat(decimal1)
|
||||||
|
decimal2 = Number.parseFloat(decimal2)
|
||||||
|
|
||||||
|
const metric = (unit ?? "").includes("m");
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case isFinite(numerator) && isFinite(denominator):
|
||||||
|
return {
|
||||||
|
value: numerator / denominator,
|
||||||
|
metric,
|
||||||
|
toMetric: numberWithUnitToMetric
|
||||||
|
};
|
||||||
|
|
||||||
|
case isFinite(decimal1) && decimal1 !== 0:
|
||||||
|
return {
|
||||||
|
value: decimal1,
|
||||||
|
metric,
|
||||||
|
toMetric: numberWithUnitToMetric
|
||||||
|
};
|
||||||
|
|
||||||
|
case isFinite(decimal2) && decimal2 !== 0:
|
||||||
|
return {
|
||||||
|
value: decimal2,
|
||||||
|
metric,
|
||||||
|
toMetric: numberWithUnitToMetric
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
normalize: function (str) {
|
||||||
|
const value = this.parse(str);
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case !isPojo(value):
|
||||||
|
return "";
|
||||||
|
|
||||||
|
case value.metric:
|
||||||
|
return `${value.value} mm`
|
||||||
|
|
||||||
|
default:
|
||||||
|
return `${formatFraction(value.value)} in`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
type ControllerMethods = {
|
interface RegisterableControllerMethods {
|
||||||
stop: () => void;
|
stop: () => void;
|
||||||
send: (gcode: string) => void;
|
send: (gcode: string) => void;
|
||||||
goto_zero: (x: number, y: number, z: number, a: number) => void;
|
|
||||||
dispatch: (event: string, ...args: any[]) => void;
|
dispatch: (event: string, ...args: any[]) => void;
|
||||||
isAxisHomed: (axis: string) => boolean;
|
isAxisHomed: (axis: string) => boolean;
|
||||||
unhome: (axis: string) => void;
|
unhome: (axis: string) => void;
|
||||||
@@ -9,11 +8,37 @@ type ControllerMethods = {
|
|||||||
set_home: (axis: string, value: number) => void;
|
set_home: (axis: string, value: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ControllerMethods extends RegisterableControllerMethods {
|
||||||
|
gotoZero: (axes: "xy" | "z") => void;
|
||||||
|
}
|
||||||
|
|
||||||
export let ControllerMethods: ControllerMethods;
|
export let ControllerMethods: ControllerMethods;
|
||||||
|
|
||||||
export function registerControllerMethods(methods: Partial<ControllerMethods>) {
|
export function registerControllerMethods(methods: Partial<RegisterableControllerMethods>) {
|
||||||
ControllerMethods = {
|
ControllerMethods = {
|
||||||
...ControllerMethods,
|
...ControllerMethods,
|
||||||
...methods
|
...methods,
|
||||||
|
gotoZero
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function gotoZero(axes: "xy" | "z") {
|
||||||
|
let axesClause = "";
|
||||||
|
switch (axes.toLowerCase()) {
|
||||||
|
case "xy":
|
||||||
|
axesClause = "X0Y0";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "z":
|
||||||
|
axesClause = "Z0";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Invalid axes: ${axes}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerMethods.send(`
|
||||||
|
G90
|
||||||
|
G0 ${axesClause}
|
||||||
|
`);
|
||||||
|
}
|
||||||
@@ -20,6 +20,10 @@
|
|||||||
:root {
|
:root {
|
||||||
--mdc-theme-text-primary-on-background: #777;
|
--mdc-theme-text-primary-on-background: #777;
|
||||||
|
|
||||||
|
.mdc-dialog .mdc-dialog__container {
|
||||||
|
transition: margin-bottom 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
.mdc-dialog .mdc-dialog__content {
|
.mdc-dialog .mdc-dialog__content {
|
||||||
color: #777;
|
color: #777;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
minify: false,
|
|
||||||
target: "chrome60",
|
target: "chrome60",
|
||||||
lib: {
|
lib: {
|
||||||
entry: resolve(__dirname, 'src/main.ts'),
|
entry: resolve(__dirname, 'src/main.ts'),
|
||||||
|
|||||||
Reference in New Issue
Block a user