Merge pull request #73 from OneFinityCNC/1.0.10-devel
Merge "1.0.10 devel"
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
Executable file
47
.devcontainer/install_tools.sh
Executable 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
|
||||||
107
.eslintrc.yml
Normal file
107
.eslintrc.yml
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
env:
|
||||||
|
es2021: true
|
||||||
|
node: true
|
||||||
|
browser: true
|
||||||
|
extends:
|
||||||
|
- eslint:recommended
|
||||||
|
- plugin:@typescript-eslint/recommended
|
||||||
|
overrides: []
|
||||||
|
parser: "@typescript-eslint/parser"
|
||||||
|
parserOptions:
|
||||||
|
ecmaVersion: latest
|
||||||
|
sourceType: module
|
||||||
|
tsconfigRootDir: ./src/svelte-components
|
||||||
|
plugins:
|
||||||
|
- "@typescript-eslint"
|
||||||
|
globals:
|
||||||
|
Vue: readonly
|
||||||
|
THREE: readonly
|
||||||
|
SvelteComponents: readonly
|
||||||
|
Clusterize: readonly
|
||||||
|
SockJS: readonly
|
||||||
|
ignorePatterns:
|
||||||
|
- /src/svelte-components/dist
|
||||||
|
- /src/static
|
||||||
|
- /build
|
||||||
|
- /dist
|
||||||
|
- /rpi-share
|
||||||
|
- /src/py/bbctrl/http
|
||||||
|
rules:
|
||||||
|
indent:
|
||||||
|
- off
|
||||||
|
"@typescript-eslint/indent":
|
||||||
|
- error
|
||||||
|
- 4
|
||||||
|
linebreak-style:
|
||||||
|
- error
|
||||||
|
- unix
|
||||||
|
quotes:
|
||||||
|
- error
|
||||||
|
- double
|
||||||
|
- allowTemplateLiterals: true
|
||||||
|
avoidEscape: true
|
||||||
|
semi:
|
||||||
|
- error
|
||||||
|
- always
|
||||||
|
"@typescript-eslint/no-explicit-any":
|
||||||
|
- off
|
||||||
|
"@typescript-eslint/no-unused-vars":
|
||||||
|
- error
|
||||||
|
- argsIgnorePattern: _.*
|
||||||
|
no-unused-vars:
|
||||||
|
- error
|
||||||
|
- argsIgnorePattern: _.*
|
||||||
|
no-trailing-spaces:
|
||||||
|
- error
|
||||||
|
key-spacing:
|
||||||
|
- error
|
||||||
|
space-before-blocks:
|
||||||
|
- error
|
||||||
|
block-spacing:
|
||||||
|
- error
|
||||||
|
brace-style:
|
||||||
|
- error
|
||||||
|
curly:
|
||||||
|
- error
|
||||||
|
keyword-spacing:
|
||||||
|
- error
|
||||||
|
no-multi-spaces:
|
||||||
|
- error
|
||||||
|
"@typescript-eslint/no-var-requires":
|
||||||
|
- off
|
||||||
|
no-multiple-empty-lines:
|
||||||
|
- error
|
||||||
|
- max: 1
|
||||||
|
func-call-spacing:
|
||||||
|
- error
|
||||||
|
- never
|
||||||
|
padding-line-between-statements:
|
||||||
|
- error
|
||||||
|
- blankLine: always
|
||||||
|
prev: function
|
||||||
|
next: function
|
||||||
|
no-var:
|
||||||
|
- error
|
||||||
|
no-unused-expressions:
|
||||||
|
- error
|
||||||
|
prefer-const:
|
||||||
|
- error
|
||||||
|
prefer-template:
|
||||||
|
- error
|
||||||
|
object-curly-spacing:
|
||||||
|
- error
|
||||||
|
- always
|
||||||
|
array-bracket-spacing:
|
||||||
|
- error
|
||||||
|
- always
|
||||||
|
template-curly-spacing:
|
||||||
|
- error
|
||||||
|
require-await:
|
||||||
|
- error
|
||||||
|
space-infix-ops:
|
||||||
|
- error
|
||||||
|
space-before-function-paren:
|
||||||
|
- error
|
||||||
|
- anonymous: never
|
||||||
|
named: never
|
||||||
|
asyncArrow: always
|
||||||
43
.gitignore
vendored
43
.gitignore
vendored
@@ -1,27 +1,26 @@
|
|||||||
|
.DS_Store
|
||||||
.sconf_temp/
|
.sconf_temp/
|
||||||
.sconsign.dblite
|
.sconsign.dblite
|
||||||
/build
|
|
||||||
/dist
|
|
||||||
/crap
|
|
||||||
/pkg
|
|
||||||
/mnt
|
|
||||||
/demo
|
|
||||||
/bbctrl-*
|
|
||||||
node_modules
|
|
||||||
*~
|
|
||||||
\#*
|
|
||||||
*.pyc
|
|
||||||
__pycache__
|
|
||||||
*.egg-info
|
|
||||||
/upload
|
|
||||||
/*.img
|
|
||||||
*.so
|
|
||||||
*.deb
|
*.deb
|
||||||
*.zip
|
*.egg-info
|
||||||
/rpi-share
|
|
||||||
/rpi-root
|
|
||||||
/src/bbserial/linux-rpi-raspberrypi-kernel*
|
|
||||||
/src/bbserial/raspberrypi-kernel*
|
|
||||||
.vscode
|
|
||||||
*.elf
|
*.elf
|
||||||
*.hex
|
*.hex
|
||||||
|
*.img
|
||||||
|
*.img.bz2
|
||||||
|
*.img.gzip
|
||||||
|
*.img.xz
|
||||||
|
*.pyc
|
||||||
|
*.so
|
||||||
|
*.tmp
|
||||||
|
*.zip
|
||||||
|
**/__pycache__
|
||||||
|
*~
|
||||||
|
\#*
|
||||||
|
build
|
||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
null.d
|
||||||
|
rpi-root
|
||||||
|
rpi-share
|
||||||
|
src/bbserial/linux-rpi-raspberrypi-kernel*
|
||||||
|
src/bbserial/raspberrypi-kernel*
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
466
CHANGELOG.md
466
CHANGELOG.md
@@ -1,466 +0,0 @@
|
|||||||
OneFinity CNC Controller Firmware Changelog
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
## v1.0.8
|
|
||||||
- Fixed chatter and lost steps issues (most commonly seen by Fusion users), re-enabled support for G61, G61.1, G64.
|
|
||||||
- Fixed 3d preview on Safari-based web browsers (MacOS & iOS)
|
|
||||||
- Made it less likely for a user to upload a non-gcode file
|
|
||||||
- Fixed problems with UI freezing when attempting to process a non-gcode file
|
|
||||||
- Simplified error popup to make it less confusing
|
|
||||||
- Improved error messages for most internal errors
|
|
||||||
|
|
||||||
## v1.0.7
|
|
||||||
- Cleaned up UI layout a bit on the main screen
|
|
||||||
- Enabled auto expansion of partition and file system to fill SD card on first boot
|
|
||||||
- Added probe continuity test before probing (pop-up dialog)
|
|
||||||
- Added a popup after probing, reminding the user to put away the probe
|
|
||||||
- Lowered default probe seek speed
|
|
||||||
- Fixed file drop-down menu showing old files
|
|
||||||
- Added file system watcher for uploaded files
|
|
||||||
- Improved support for large gcode files, improves performance on Raspi
|
|
||||||
- Fixed a bug where the UI could become unresponsive at boot, or when uploading the first gcode file
|
|
||||||
- Changed default max-deviation to 0.001 to reduce issues with chattering while cutting arcs and circles
|
|
||||||
- Hides ratpoison (window manager) message during boot up
|
|
||||||
- Disabled G61, G61.1, G64 gcodes until the root cause of circle chatter is identified and fixed
|
|
||||||
- Increased icon size on the main GUI
|
|
||||||
- Improved version comparison logic for handling public beta releases in the future
|
|
||||||
- Changed default max-jerk to 1000 for X/Y axes
|
|
||||||
- Changed default idle current to 1A
|
|
||||||
- Force-set some critical configuration values to help with reliability
|
|
||||||
- Metric units only for junction-accel and max-deviation
|
|
||||||
- Rewrote the homing procedure to be more consistent
|
|
||||||
- Small improvements to the stall homing procedure
|
|
||||||
- Fixed error messages in the console (webgl)
|
|
||||||
- Fixed styling bug with error dialog
|
|
||||||
- Added a setting to allow enable/disable of probing safety prompts
|
|
||||||
|
|
||||||
## v1.0.6
|
|
||||||
- Tweaked stall homing procedure to clear stepper stall condition before homing
|
|
||||||
- Modified motor homing parameters (8 microsteps, 1.688 m/min search velocity, 2 stall volts, 1 stall current, 1.5mm zero backoff)
|
|
||||||
- Added pop up message while loading/simulating file on upload
|
|
||||||
- Decreased max velocity in default settings for X and Y axes
|
|
||||||
- Restored jerk to 1000 on all axes
|
|
||||||
- Fixed issue with default units not displaying on Control page (thanks to Robin Goldstone)
|
|
||||||
|
|
||||||
## v1.0.5
|
|
||||||
- Changed the jog commands so that save/restore modal states are not used to avoid situation where spindle/loads could
|
|
||||||
turn back on if the stop button was used instead of the M5/M9 gcode commands.
|
|
||||||
- Added tool path status to control page
|
|
||||||
- Moved over/under/no-fit warnings to tool status from machine status
|
|
||||||
- Added "home machine" pop up on start if the machine is not homed
|
|
||||||
- Added confirmation for X0Y0 and Z0 buttons
|
|
||||||
|
|
||||||
## v1.0.4
|
|
||||||
- Fixed text sticking out of some dialog boxes
|
|
||||||
- Added changes to PWR microcontroller to support new precharge circuitry on V3 of the PCB
|
|
||||||
- Added a Shutdown button under the menu
|
|
||||||
- Added confirmation dialog to shutdown
|
|
||||||
- Fixed Reset Defaults for OneFinity settings
|
|
||||||
- Probe buttons now turn green when probe input is active
|
|
||||||
- Added defaults for both machinist and woodworker machines
|
|
||||||
- Re-activated "Upgrade" button under Admin to grab updates directly from the web
|
|
||||||
|
|
||||||
## v1.0.3
|
|
||||||
- Changed upgrade scripts to point at OneFinity github repository
|
|
||||||
- Upgrade function not yet fully implemented
|
|
||||||
|
|
||||||
## v1.0.2
|
|
||||||
- Initial release of customized OneFinity firmware
|
|
||||||
- Includes GUI customizations, stall homing
|
|
||||||
|
|
||||||
Note: This firmware was forked from version 0.4.14 of the Buildbotics firmware
|
|
||||||
|
|
||||||
Buildbotics CNC Controller Firmware Changelog
|
|
||||||
=============================================
|
|
||||||
|
|
||||||
## v0.4.14
|
|
||||||
- Handle file uploads with '#' or '?' in the name.
|
|
||||||
- Added "step mode" to Web based jogging.
|
|
||||||
- Fixed touch screen Web jogging.
|
|
||||||
|
|
||||||
## v0.4.13
|
|
||||||
- Support for OMRON MX2 VFD.
|
|
||||||
- Better error handling in WiFi configuration.
|
|
||||||
- Fix open WiFi access.
|
|
||||||
- Improved video camera performance.
|
|
||||||
- Allow up to 4 camera clients at once.
|
|
||||||
- Add axis bounds GCode variables ``#<_x_min>``, ``#<_x_max>``, etc.
|
|
||||||
- Expose ``junction-accel`` planning parameter.
|
|
||||||
- Fixed problem with manual firmware upload on OSX.
|
|
||||||
- Ignore cameras that do not support MJPEG format video.
|
|
||||||
|
|
||||||
## v0.4.12
|
|
||||||
- Segments straddle arc in linearization.
|
|
||||||
- Control max-arc-error with GCode var.
|
|
||||||
- Implemented path modes G61, G61.1 & G64 with naive CAM and basic blending.
|
|
||||||
- Log GCode messages to "Messages" tab.
|
|
||||||
- Acknowledging a message on one browser clears it for all.
|
|
||||||
- Automatically reload Web view when file changes.
|
|
||||||
- Added ``config-screen`` script. Web based screen config to come later.
|
|
||||||
- Suppress message popup with (MSG,# No popup message).
|
|
||||||
- Show latest GCode message in ``Message`` field on CONTROL page.
|
|
||||||
- Marked several GCodes supported in cheat sheet.
|
|
||||||
- Solved planner lookahead failure for most reasonable cases.
|
|
||||||
- Prevent cutting off distant parts of 3D path view.
|
|
||||||
- Raised default ``latch-backoff`` to 100mm and ``zero-backoff`` to 5mm.
|
|
||||||
- Added ``max-deviation`` option.
|
|
||||||
- Fixed problem with GCode boolean expression parsing. #232.
|
|
||||||
- Ensure 2uS step pulse width.
|
|
||||||
|
|
||||||
## v0.4.11
|
|
||||||
- Don't reset global offsets on M2.
|
|
||||||
- Test shunt and show error on failure.
|
|
||||||
- Report spindle status codes from Modbus.
|
|
||||||
- Save more log files in bug report.
|
|
||||||
- Fixed indicators low-side units.
|
|
||||||
- Support for YL600, YL620 & YL620-A VFDs.
|
|
||||||
- Move Modbus indicators to tool page.
|
|
||||||
- Support for Sunfar E300 VFD.
|
|
||||||
- Set GCODE_SCRIPT_PATH to support GCode file routines.
|
|
||||||
- Fix pause bug introduced in v0.4.10.
|
|
||||||
|
|
||||||
## v0.4.10
|
|
||||||
- Fix demo password check
|
|
||||||
- Fix bug were fast clicks could cause jog commands to arrive out of order.
|
|
||||||
- Fix bug where planner position may not sync after jog.
|
|
||||||
- Show power shutdown on indicators page.
|
|
||||||
- Show all motors in shutdown when in power shutdown.
|
|
||||||
- Improved GCode error messages.
|
|
||||||
- Put controller into estop when in power shutdown.
|
|
||||||
|
|
||||||
## v0.4.9
|
|
||||||
- Enforce 6A per motor channel peak current limit.
|
|
||||||
- Adjust config values above max or below min instead of resetting to default.
|
|
||||||
|
|
||||||
## v0.4.8
|
|
||||||
- Fixed log rotating.
|
|
||||||
- Use systemd service instead of init.d.
|
|
||||||
- Fix planner terminate.
|
|
||||||
- Changed AVR serial interrupt priorites.
|
|
||||||
- Increased AVR serial and command buffers.
|
|
||||||
- Boost HDMI signal.
|
|
||||||
- Rewrote RPi serial driver.
|
|
||||||
- Automatically scale max CPU speed to reduce RPi temp.
|
|
||||||
- Disable USB camera if RPi temperature above 80°C, back on at 75°C.
|
|
||||||
- Respect offsets in canned cycle moves. #219
|
|
||||||
- Fixed G53 warning.
|
|
||||||
- Fixed delayed offset update after M2 or M30 end of program.
|
|
||||||
- Handle multiple consecutive config resets correctly.
|
|
||||||
- Fixed log CPU usage problem introduced in v0.4.6.
|
|
||||||
- Show RPi temp on indicators page.
|
|
||||||
- Show red thermometer if RPi temp exceeds 80°C.
|
|
||||||
|
|
||||||
## v0.4.7
|
|
||||||
- Fix homing switch to motor channel mapping with non-standard axis order.
|
|
||||||
- Added ``switch-debounce`` and ``switch-lockout`` config options.
|
|
||||||
- Handle corrupt GCode simulation data correctly.
|
|
||||||
- Fixes for exception logging.
|
|
||||||
- Always limit motor max-velocity. #209
|
|
||||||
- Sync GCode and planner files to disk after write.
|
|
||||||
- Added warning about reliability in a noisy environment on WiFi config page.
|
|
||||||
- EStop on motor fault.
|
|
||||||
- Fixed ETA line wrapping on Web interface.
|
|
||||||
- Fixed zeroing with non-zero offset when unhomed. #211
|
|
||||||
- Handle file paths uploaded from Windows correctly. #212
|
|
||||||
- Don't retain estop state through reboot.
|
|
||||||
- Log when RPi gets hot.
|
|
||||||
- Support Modbus multi-write mode.
|
|
||||||
- Added support for Nowforever VFDs.
|
|
||||||
|
|
||||||
## v0.4.6
|
|
||||||
- Fixed a rare ``Negative s-curve time`` error.
|
|
||||||
- Don't allow manual axis homing when soft limits are not set.
|
|
||||||
- Right click to enable camera crosshair.
|
|
||||||
- Demo mode.
|
|
||||||
- Limit idle-current to 2A.
|
|
||||||
- Removed dangerous ``power-mode`` in favor of simpler ``enabled`` option.
|
|
||||||
- Fixed bug where motor driver could fail to disabled during estop.
|
|
||||||
- Restored estop text.
|
|
||||||
|
|
||||||
## v0.4.5
|
|
||||||
- Fix for random errors while running VFD.
|
|
||||||
- Fix bug where planner would not continue after optional pause (M1).
|
|
||||||
- Fix lockup on invalid no move probe G38.x. #183
|
|
||||||
- Fix zeroing homed axis after jog.
|
|
||||||
- Fix VFD communication at higher baud rates (> 9600). #184
|
|
||||||
|
|
||||||
## v0.4.4
|
|
||||||
- Write version to log file.
|
|
||||||
- Write time to log file periodically.
|
|
||||||
- Show simulation progress with or with out 3D view.
|
|
||||||
- Synchronize file list between browsers.
|
|
||||||
- Increased max simulation time to 24hrs.
|
|
||||||
- Added button to download current GCode file.
|
|
||||||
- Blink play button to indicate pause.
|
|
||||||
- Many layout tweaks/improvements.
|
|
||||||
- Don't abort simulations when system time changes.
|
|
||||||
- Only allow one camera stream at a time.
|
|
||||||
|
|
||||||
## v0.4.2
|
|
||||||
- Suppress ``Auto-creating missing tool`` warning.
|
|
||||||
- Prevent ``Stream is closed`` error.
|
|
||||||
- Suppress ``WebGL not supported`` warning.
|
|
||||||
- Fixed Web disconnect during simulation of large GCode.
|
|
||||||
- Disable outputs on estop.
|
|
||||||
- Improved switch debouncing for better homing.
|
|
||||||
- Handle zero length dwell correctly.
|
|
||||||
- Fixed problem with cached GCode file upload when file changed on disk.
|
|
||||||
- Run simulation at low process priority.
|
|
||||||
- Added ``Bug Report`` button to ``Admin`` -> ``General``.
|
|
||||||
- Only render 3D view as needed to save CPU.
|
|
||||||
- Prevent lockup due to browser causing out of memory condition.
|
|
||||||
- Show error message when too large GCode upload fails.
|
|
||||||
- Much faster 3D view loading.
|
|
||||||
|
|
||||||
## v0.4.1
|
|
||||||
- Fix toolpath view axes bug.
|
|
||||||
- Added LASER intensity view.
|
|
||||||
- Fixed reverse path planner bug.
|
|
||||||
- Video size and path view controls persistent over browser reload.
|
|
||||||
- Fixed time and progress bugs.
|
|
||||||
- Added PWM rapid auto off feature for LASER/Plasma.
|
|
||||||
- Added dynamic PWM for LASER/Plasma.
|
|
||||||
- Added motor faults table to indicators page.
|
|
||||||
- Emit error and indicate FAULT on axis for motor driver faults.
|
|
||||||
- Display axis motor FAULT on LCD.
|
|
||||||
- Fixed bug with rapid repeated unpause.
|
|
||||||
|
|
||||||
## v0.4.0
|
|
||||||
- Increased display precision of position and motor config.
|
|
||||||
- Added support for 256 microstepping.
|
|
||||||
- Smoother operation at 250k step rate by doubling clock as needed.
|
|
||||||
- Indicators tab improvements.
|
|
||||||
- Much improved camera support.
|
|
||||||
- Camera hotpluging.
|
|
||||||
- Move camera video to header.
|
|
||||||
- Click to switch video size.
|
|
||||||
- Automount/unmount USB drives.
|
|
||||||
- Automatically install ``buildbotics.gc`` when no other GCode exists.
|
|
||||||
- Preplan GCode and check for errors.
|
|
||||||
- Display 3D view of program tool paths in browser.
|
|
||||||
- Display accurate time remaining, ETA and progress during run.
|
|
||||||
- Automatically collapse moves in planner which are too short in time.
|
|
||||||
- Show IO status indicators on configuration pages.
|
|
||||||
- Check that axis dimensions fit path plan dimensions.
|
|
||||||
- Show machine working envelope in path plan viewer.
|
|
||||||
- Don't reload browser view on reconnect unless controller has reloaded.
|
|
||||||
- Increased max switch backoff search distance.
|
|
||||||
- Major improvements for LASER raster GCodes.
|
|
||||||
- Fixed major bug in command queuing.
|
|
||||||
- Ignore Program Number O-Codes.
|
|
||||||
- Improved planning of collinear line segments.
|
|
||||||
- Allow PWM output up to 320kHz and no slower than 8Hz.
|
|
||||||
|
|
||||||
## v0.3.28
|
|
||||||
- Show step rate on motor configuration page.
|
|
||||||
- Limit motor max-velocity such that step rate cannot exceed 250k.
|
|
||||||
- Fixed deceleration bug at full 250k step rate.
|
|
||||||
|
|
||||||
## v0.3.27
|
|
||||||
- Fixed homing in imperial mode.
|
|
||||||
|
|
||||||
## v0.3.26
|
|
||||||
- Removed VFD test.
|
|
||||||
- Show VFD status on configuration page.
|
|
||||||
- Show VFD commands fail counts.
|
|
||||||
- Marked some VFD types as beta.
|
|
||||||
|
|
||||||
## v0.3.25
|
|
||||||
- Error on home if max-soft-limit <= min-soft-limit + 1. #139
|
|
||||||
- Decrease boot time networking delay.
|
|
||||||
- Default to US keyboard layout. #145
|
|
||||||
- Added configuration option to show metric or imperial units in browser. #74
|
|
||||||
- Implemented fine jogging control in Web interface. #147
|
|
||||||
|
|
||||||
## v0.3.24
|
|
||||||
- Added unhome button on axis position popup.
|
|
||||||
- Ignore soft limits of max <= min.
|
|
||||||
- Fixed problem with restarting program in imperial units mode.
|
|
||||||
- Handle GCode with infinite or very long loops correctly.
|
|
||||||
- Fixed Huanyang spindle restart after stop.
|
|
||||||
|
|
||||||
## v0.3.23
|
|
||||||
- Fix for modbus read operation.
|
|
||||||
- Finalized AC-Tech VFD support.
|
|
||||||
- Preliminary FR-D700 VFD support.
|
|
||||||
- Ignore leading zeros in modbus messages.
|
|
||||||
- Handle older PWR firmwares.
|
|
||||||
|
|
||||||
## v0.3.22
|
|
||||||
- Fix position loss after program pause. #130
|
|
||||||
- Correctly handle disabled axes.
|
|
||||||
- Fixed config checkbox not displaying defaulted enabled correctly.
|
|
||||||
- Added Custom Modbus VFD programming.
|
|
||||||
|
|
||||||
## v0.3.21
|
|
||||||
- Implemented M70-M73 modal state save/restore.
|
|
||||||
- Added support for modbus VFDs.
|
|
||||||
- Start Huanyang spindle with out first pressing Start button on VFD.
|
|
||||||
- Faster switching of large GCode files in Web.
|
|
||||||
- Fixed reported gcode line off by one.
|
|
||||||
- Disable MDI input while running.
|
|
||||||
- Stabilized direction pin output during slow moves.
|
|
||||||
|
|
||||||
## v0.3.20
|
|
||||||
- Eliminated drift caused by miscounting half microsteps.
|
|
||||||
- Fixed disappearing GCode in Web.
|
|
||||||
- More efficient GCode scrolling with very large files.
|
|
||||||
- Fully functional soft-limited jogging.
|
|
||||||
- Added client and access-point Wifi configuration.
|
|
||||||
- Fixed broken hostname Web redirect after change.
|
|
||||||
- Split admin page -> General & Network.
|
|
||||||
- Improved calculation of junction velocity limits.
|
|
||||||
|
|
||||||
## v0.3.19
|
|
||||||
- Fixed stopping problems. #127
|
|
||||||
- Fixed ``Negative s-curve time`` error.
|
|
||||||
- Improved jogging with soft limits.
|
|
||||||
- Added site favicon.
|
|
||||||
- Fixed problems with offsets and imperial units.
|
|
||||||
- Fixed ``All zero s-curve times`` caused by extremely short, non-zero moves.
|
|
||||||
- Fixed position drift.
|
|
||||||
|
|
||||||
## v0.3.18
|
|
||||||
- Don't enable any tool by default.
|
|
||||||
|
|
||||||
## v0.3.17
|
|
||||||
- Fixed pausing fail near end of run bug.
|
|
||||||
- Show "Upgrading firmware" when upgrading.
|
|
||||||
- Log excessive pwr communication failures as errors.
|
|
||||||
- Ensure we can still get out of non-idle cycles when there are errors.
|
|
||||||
- Less frequent pwr variable updates.
|
|
||||||
- Stop cancels seek and subsequent estop.
|
|
||||||
- Fixed bug in AVR/Planner command synchronization.
|
|
||||||
- Consistently display HOMING state during homing operation.
|
|
||||||
- Homing zeros axis global offset.
|
|
||||||
- Added zero all button. #126
|
|
||||||
- Separate "Auto" and "MDI" play/pause & stop buttons. #126
|
|
||||||
- Moved home all button. #126
|
|
||||||
- Display "Video camera not found." instead of broken image icon.
|
|
||||||
- Show offset positions not absolute on LCD.
|
|
||||||
- Don't change gcode lines while homing.
|
|
||||||
- Don't change button states while homing.
|
|
||||||
- Adding warning about power cyclying during an upgrade.
|
|
||||||
- Reset planner on AVR errors.
|
|
||||||
- Fixed pausing with short moves.
|
|
||||||
- Corrected s-curve accel increasing jogging velocities.
|
|
||||||
|
|
||||||
## v0.3.16
|
|
||||||
- Fixed switch debounce bug.
|
|
||||||
|
|
||||||
## v0.3.15
|
|
||||||
- Suppress warning missing config.json warning after config reset.
|
|
||||||
- Fixed EStop reboot loop.
|
|
||||||
- Removed AVR unexpected reboot error.
|
|
||||||
|
|
||||||
## v0.3.14
|
|
||||||
- Fixed: Config fails silently after web disconnect #112
|
|
||||||
- Always reload the page after a disconnect.
|
|
||||||
- Honor soft limits #111 (but not when jogging)
|
|
||||||
- Limit switch going active while moving causes estop. #54
|
|
||||||
- Added more links to help page.
|
|
||||||
- Fixed axis display on LCD. #122
|
|
||||||
- Added GCode cheat sheet.
|
|
||||||
- Fixed LCD boot splash screen. #121
|
|
||||||
- Implemented tool change procedures and pause message box. #81
|
|
||||||
- Implemented program start and end procedures.
|
|
||||||
|
|
||||||
## v0.3.13
|
|
||||||
- Disable spindle and loads on stop.
|
|
||||||
- Fixed several state transition (stop, pause, estop, etc.) problems.
|
|
||||||
|
|
||||||
## v0.3.12
|
|
||||||
- Updated DB25 M2 breakout diagram.
|
|
||||||
- Enabled AVR watchdog.
|
|
||||||
- Fixed problem with selecting newly uploaded file.
|
|
||||||
- More thorough shutdown of stepper driver in estop.
|
|
||||||
- Fixed spindle type specific options.
|
|
||||||
- No more ``Unexpected AVR firmware reboot`` errors on estop clear.
|
|
||||||
- Downgraded ``Machine alarmed - Command not processed`` errors to warnings.
|
|
||||||
- Suppress unnecessary axis homing warnings.
|
|
||||||
- More details for axis homing errors.
|
|
||||||
- Support GCode messages e.g. (MSG, Hello World!)
|
|
||||||
- Support programmed pauses. i.e. M0
|
|
||||||
|
|
||||||
## v0.3.11
|
|
||||||
- Suppressed ``firmware rebooted`` warning.
|
|
||||||
- Error on unexpected AVR reboot.
|
|
||||||
- Fixed pin fault output.
|
|
||||||
- No longer using interrupts for switch inputs. Debouncing on clock tick.
|
|
||||||
|
|
||||||
## v0.3.10
|
|
||||||
- Fixed "Flood" display, changed to "Load 1" and "Load 2". #108
|
|
||||||
- Highlight loads when on.
|
|
||||||
- Fixed axis zeroing.
|
|
||||||
- Fixed bug in home position set after successful home. #109
|
|
||||||
- Fixed ugly Web error dumps.
|
|
||||||
- Allow access to log file from Web.
|
|
||||||
- Rotate log so it does not grow too big.
|
|
||||||
- Keep same GCode file through browser reload. #20
|
|
||||||
|
|
||||||
## v0.3.9
|
|
||||||
- Fixed bug in move exec that was causing bumping between moves.
|
|
||||||
- Fixed planner bug which could create negative s-curve times.
|
|
||||||
- Hide step and optional pause buttons until they are implemented.
|
|
||||||
- Fixed pausing problems.
|
|
||||||
- Limit number of console messages.
|
|
||||||
- Scrollbar on console view.
|
|
||||||
- Log debug messages to console in developer mode.
|
|
||||||
- Fixed AVR log message source.
|
|
||||||
- Fixed step correction.
|
|
||||||
- JOGGING, HOMMING and MDI states.
|
|
||||||
- Fixed position problem with rapid MDI entry.
|
|
||||||
|
|
||||||
## v0.3.8
|
|
||||||
- Fixed pwr flags display
|
|
||||||
- Added pwr fault flags to indicators
|
|
||||||
|
|
||||||
## v0.3.7
|
|
||||||
- Allow blocking error dialog for a period of time
|
|
||||||
- Show actual error message on planner errors
|
|
||||||
- Reset planner on serious error
|
|
||||||
- Fixed console clear
|
|
||||||
- Added helpful info to Video tab
|
|
||||||
- Changed "Console" tab to "Messages"
|
|
||||||
- Removed spin up/down velocity options, they don't do anything
|
|
||||||
- Allow RS485 to work when wires are swapped
|
|
||||||
- Allow setting VFD ID
|
|
||||||
- Only show relevant spindle config items
|
|
||||||
- More robust video camera reset
|
|
||||||
- Added help page
|
|
||||||
- Allow upgrade with out Internet
|
|
||||||
- Limit power fault reporting
|
|
||||||
- Added load over temp, load limiting and motor overload power faults
|
|
||||||
|
|
||||||
## v0.3.6
|
|
||||||
- Set max_usb_current=1 in /boot/config.txt from installer #103
|
|
||||||
|
|
||||||
## v0.3.5
|
|
||||||
- Fixed dwell (G4)
|
|
||||||
- Always show limit switch indicators regardless of motor enable
|
|
||||||
- Fixed feed rate display
|
|
||||||
- Added current GCode unit display
|
|
||||||
- Fixed homed axis zeroing
|
|
||||||
- Fixed probe pin input
|
|
||||||
- Added reload button to video tab
|
|
||||||
- Don't open error dialog on repeat messages
|
|
||||||
- Handle large GCode files in browser
|
|
||||||
- Added max lookahead limit to planner
|
|
||||||
- Fixed GCode stopping/pausing where ramp down needs more than is in the queue
|
|
||||||
- Added breakout box diagram to indicators
|
|
||||||
- Initialize axes offsets to zero on startup
|
|
||||||
- Fixed conflict between ``x`` state variable and ``x`` axis variable
|
|
||||||
- Don't show ipv6 addresses on LCD. They don't fit.
|
|
||||||
|
|
||||||
## v0.3.4
|
|
||||||
- Added alternate units for motor parameters
|
|
||||||
- Automatic config file upgrading
|
|
||||||
- Fixed planner/jog sync
|
|
||||||
- Fixed planner limits config
|
|
||||||
- Accel units mm/min² -> m/min²
|
|
||||||
- Search and latch velocity mm/min -> m/min
|
|
||||||
- Fixed password update (broken in last version)
|
|
||||||
- Start Web server earlier in case of Python coding errors
|
|
||||||
|
|
||||||
|
|
||||||
Changelog not maintained in previous versions. See git commit log.
|
|
||||||
22
CODE_TAG
22
CODE_TAG
@@ -1,22 +0,0 @@
|
|||||||
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>
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
include package.json README.md requirements.txt
|
include package.json README.md requirements.txt
|
||||||
graft scripts
|
graft installer
|
||||||
graft python-packages
|
include scripts/install.sh
|
||||||
graft src/py/bbctrl/http
|
graft src/py/bbctrl/http
|
||||||
graft src/py/camotics
|
graft src/py/camotics
|
||||||
include src/avr/bbctrl-avr-firmware.hex
|
include src/avr/bbctrl-avr-firmware.hex
|
||||||
|
|||||||
55
Makefile
55
Makefile
@@ -22,12 +22,8 @@ RSYNC_OPTS := $(RSYNC_EXCLUDE) -rv --no-g --delete --force
|
|||||||
|
|
||||||
VERSION := $(shell sed -n 's/^.*"version": "\([^"]*\)",.*$$/\1/p' package.json)
|
VERSION := $(shell sed -n 's/^.*"version": "\([^"]*\)",.*$$/\1/p' package.json)
|
||||||
PKG_NAME := bbctrl-$(VERSION)
|
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
|
SUBPROJECTS := avr boot pwr jig
|
||||||
WATCH := src/pug src/pug/templates src/stylus src/js src/resources src/svelte-components src/static Makefile
|
|
||||||
|
|
||||||
ifndef HOST
|
ifndef HOST
|
||||||
HOST=onefinity
|
HOST=onefinity
|
||||||
@@ -37,16 +33,12 @@ ifndef PASSWORD
|
|||||||
PASSWORD=onefinity
|
PASSWORD=onefinity
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
all: $(HTML) $(RESOURCES)
|
all: $(HTML) $(RESOURCES)
|
||||||
@for SUB in $(SUBPROJECTS); do $(MAKE) -C src/$$SUB; done
|
@for SUB in $(SUBPROJECTS); do $(MAKE) -C src/$$SUB; done
|
||||||
|
|
||||||
pkg: all $(AVR_FIRMWARE) bbserial
|
pkg: all $(AVR_FIRMWARE) bbserial
|
||||||
./setup.py sdist
|
./setup.py sdist
|
||||||
|
|
||||||
beta-pkg: pkg
|
|
||||||
cp dist/$(PKG_NAME).tar.bz2 dist/$(BETA_PKG_NAME).tar.bz2
|
|
||||||
|
|
||||||
bbserial:
|
bbserial:
|
||||||
$(MAKE) -C src/bbserial
|
$(MAKE) -C src/bbserial
|
||||||
|
|
||||||
@@ -57,12 +49,6 @@ $(GPLAN_TARGET): $(GPLAN_MOD)
|
|||||||
|
|
||||||
$(GPLAN_MOD): $(GPLAN_IMG)
|
$(GPLAN_MOD): $(GPLAN_IMG)
|
||||||
./scripts/gplan-init-build.sh
|
./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/
|
cp ./scripts/gplan-build.sh rpi-share/
|
||||||
chmod +x rpi-share/gplan-build.sh
|
chmod +x rpi-share/gplan-build.sh
|
||||||
sudo ./scripts/rpi-chroot.sh $(GPLAN_IMG) /mnt/host/gplan-build.sh
|
sudo ./scripts/rpi-chroot.sh $(GPLAN_IMG) /mnt/host/gplan-build.sh
|
||||||
@@ -74,15 +60,6 @@ $(GPLAN_IMG):
|
|||||||
$(AVR_FIRMWARE):
|
$(AVR_FIRMWARE):
|
||||||
$(MAKE) -C src/avr
|
$(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
|
update: pkg
|
||||||
http_proxy= curl -i -X PUT -H "Content-Type: multipart/form-data" \
|
http_proxy= curl -i -X PUT -H "Content-Type: multipart/form-data" \
|
||||||
-F "firmware=@dist/$(PKG_NAME).tar.bz2" -F "password=$(PASSWORD)" \
|
-F "firmware=@dist/$(PKG_NAME).tar.bz2" -F "password=$(PASSWORD)" \
|
||||||
@@ -122,34 +99,8 @@ $(TARGET_DIR)/%.html: src/pug/%.pug node_modules FORCE
|
|||||||
@mkdir -p $(TARGET_DIR)
|
@mkdir -p $(TARGET_DIR)
|
||||||
$(PUG) -O pug-opts.js -P $< -o $(TARGET_DIR) || (rm -f $@; exit 1)
|
$(PUG) -O pug-opts.js -P $< -o $(TARGET_DIR) || (rm -f $@; exit 1)
|
||||||
|
|
||||||
pylint:
|
clean:
|
||||||
pylint3 -E $(shell find src/py -name \*.py | grep -v flycheck_)
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
15
installer/config/bbctrl-poweroff.service
Normal file
15
installer/config/bbctrl-poweroff.service
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Turn off HDMI at powerdown
|
||||||
|
DefaultDependencies=no
|
||||||
|
Before=umount.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/bin/sh -c ' \
|
||||||
|
killall xinit \
|
||||||
|
&& plymouth change-mode --shutdown show-splash \
|
||||||
|
&& sleep 10s \
|
||||||
|
&& /usr/bin/vcgencmd display_power 0 '
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=poweroff.target
|
||||||
@@ -23,9 +23,6 @@ fi
|
|||||||
# Reload udev
|
# Reload udev
|
||||||
/etc/init.d/udev restart
|
/etc/init.d/udev restart
|
||||||
|
|
||||||
# Stop boot splash so it doesn't interfere with X if GPU enabled and to save CPU
|
|
||||||
plymouth quit
|
|
||||||
|
|
||||||
# Start X in /home/pi
|
# Start X in /home/pi
|
||||||
cd /home/pi
|
cd /home/pi
|
||||||
sudo -u pi startx
|
sudo -u pi startx
|
||||||
@@ -15,8 +15,7 @@ while true; do
|
|||||||
xrdb /home/pi/.Xresources
|
xrdb /home/pi/.Xresources
|
||||||
|
|
||||||
# Start browser
|
# Start browser
|
||||||
/usr/local/bin/browser --no-first-run --disable-infobars \
|
/usr/local/bin/browser --no-first-run --disable-infobars --noerrdialogs --disable-3d-apis http://localhost/
|
||||||
--noerrdialogs --disable-3d-apis http://localhost/
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
659
installer/gcode/Team Onefinity.ngc
Normal file
659
installer/gcode/Team Onefinity.ngc
Normal file
@@ -0,0 +1,659 @@
|
|||||||
|
%
|
||||||
|
G21 G40 G49 M6 T1
|
||||||
|
G17
|
||||||
|
M7
|
||||||
|
G0Z20.320
|
||||||
|
G0X0.000Y0.000S16000M3
|
||||||
|
G0X26.732Y12.863Z5.080
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X21.606Y17.989F2540.0
|
||||||
|
G1Y12.042
|
||||||
|
G1X25.676Y12.773
|
||||||
|
G1X26.732Y12.863
|
||||||
|
G1X34.154Y9.033
|
||||||
|
G1X19.066Y24.121
|
||||||
|
G1Y9.006
|
||||||
|
G1X26.125Y10.273
|
||||||
|
G2X27.247Y10.273I0.561J-3.125
|
||||||
|
G1X34.154Y9.033
|
||||||
|
G0Z5.080
|
||||||
|
G0X31.766Y33.993
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X21.606Y44.153F2540.0
|
||||||
|
G1Y43.784
|
||||||
|
G1X31.766Y33.624
|
||||||
|
G1Y33.993
|
||||||
|
G1X34.306Y35.045
|
||||||
|
G1X19.066Y50.285
|
||||||
|
G1Y42.732
|
||||||
|
G1X34.306Y27.492
|
||||||
|
G1Y35.045
|
||||||
|
G0Z5.080
|
||||||
|
G0X31.766Y60.159
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X21.606Y70.318F2540.0
|
||||||
|
G1Y69.948
|
||||||
|
G1X31.766Y59.789
|
||||||
|
G1Y60.159
|
||||||
|
G1X34.306Y61.211
|
||||||
|
G1X19.066Y76.450
|
||||||
|
G1Y68.896
|
||||||
|
G1X34.306Y53.657
|
||||||
|
G1Y61.211
|
||||||
|
G0Z5.080
|
||||||
|
G0X31.766Y90.231
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X27.488F2540.0
|
||||||
|
G1X31.766Y85.954
|
||||||
|
G1Y90.231
|
||||||
|
G1X34.306Y92.771
|
||||||
|
G1X21.356
|
||||||
|
G1X34.306Y79.822
|
||||||
|
G1Y92.771
|
||||||
|
G0Z5.080
|
||||||
|
G0X61.691Y55.036
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X63.248F2540.0
|
||||||
|
G1Y88.425
|
||||||
|
G2X66.423Y91.600I3.175J0.000
|
||||||
|
G1X77.157
|
||||||
|
G1Y91.866
|
||||||
|
G1X47.750
|
||||||
|
G1Y91.600
|
||||||
|
G1X58.516
|
||||||
|
G2X61.691Y88.425I0.000J-3.175
|
||||||
|
G1Y55.036
|
||||||
|
G0Z5.080
|
||||||
|
G0X88.590
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X111.381F2540.0
|
||||||
|
G1Y55.301
|
||||||
|
G1X93.321
|
||||||
|
G2X90.146Y58.476I0.000J3.175
|
||||||
|
G1Y70.643
|
||||||
|
G2X93.321Y73.818I3.175J0.000
|
||||||
|
G1X107.380
|
||||||
|
G1Y74.084
|
||||||
|
G1X93.321
|
||||||
|
G2X90.146Y77.259I0.000J3.175
|
||||||
|
G1Y88.425
|
||||||
|
G2X93.321Y91.600I3.175J0.000
|
||||||
|
G1X110.220
|
||||||
|
G1Y91.866
|
||||||
|
G1X88.590
|
||||||
|
G1Y55.036
|
||||||
|
G0Z5.080
|
||||||
|
G0X122.153
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X123.719F2540.0
|
||||||
|
G1X127.166Y64.004
|
||||||
|
G2X130.130Y66.040I2.964J-1.139
|
||||||
|
G1X146.391
|
||||||
|
G2X149.354Y64.004I0.000J-3.175
|
||||||
|
G1X152.802Y55.036
|
||||||
|
G1X154.303
|
||||||
|
G1X139.687Y91.866
|
||||||
|
G1X136.769
|
||||||
|
G1X122.153Y55.036
|
||||||
|
G0Z5.080
|
||||||
|
G0X132.680Y66.306
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G2X129.716Y70.620I0.000J3.175F2540.0
|
||||||
|
G1X132.098Y76.845
|
||||||
|
G1X133.063Y79.498
|
||||||
|
G1X134.092Y82.435
|
||||||
|
G1X135.189Y85.669
|
||||||
|
G2X138.196Y87.824I3.007J-1.020
|
||||||
|
G1X138.325
|
||||||
|
G2X141.328Y85.679I0.000J-3.175
|
||||||
|
G1X142.433Y82.457
|
||||||
|
G1X143.467Y79.518
|
||||||
|
G1X144.433Y76.851
|
||||||
|
G1X145.333Y74.451
|
||||||
|
G1X146.805Y70.620
|
||||||
|
G2X143.841Y66.306I-2.964J-1.138
|
||||||
|
G1X132.680
|
||||||
|
G0Z5.080
|
||||||
|
G0X167.414Y55.036
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X168.971F2540.0
|
||||||
|
G1Y73.759
|
||||||
|
G1X168.947Y76.349
|
||||||
|
G1X168.876Y79.029
|
||||||
|
G1X168.757Y81.813
|
||||||
|
G1X168.589Y84.724
|
||||||
|
G2X171.759Y88.082I3.170J0.183
|
||||||
|
G1X172.049
|
||||||
|
G2X175.085Y85.836I0.000J-3.175
|
||||||
|
G1X175.677Y83.901
|
||||||
|
G1X176.359Y81.760
|
||||||
|
G1X177.102Y79.531
|
||||||
|
G1X177.877Y77.332
|
||||||
|
G1X182.870Y63.705
|
||||||
|
G1X182.369Y65.444
|
||||||
|
G1X181.521Y68.194
|
||||||
|
G1X180.648Y70.844
|
||||||
|
G1X179.742Y73.414
|
||||||
|
G1X173.050Y91.866
|
||||||
|
G1X167.414
|
||||||
|
G1Y55.036
|
||||||
|
G0Z5.080
|
||||||
|
G0X186.047
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X186.709F2540.0
|
||||||
|
G1X189.243Y61.950
|
||||||
|
G2X186.442Y60.271I-2.800J1.496
|
||||||
|
G1X186.249
|
||||||
|
G2X183.614Y61.675I0.000J3.175
|
||||||
|
G1X186.047Y55.036
|
||||||
|
G0Z5.080
|
||||||
|
G0X203.785
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X205.342F2540.0
|
||||||
|
G1Y91.866
|
||||||
|
G1X199.644
|
||||||
|
G1X192.952Y73.323
|
||||||
|
G1X191.959Y70.488
|
||||||
|
G1X191.055Y67.759
|
||||||
|
G1X190.236Y65.128
|
||||||
|
G1X189.586Y62.886
|
||||||
|
G1X194.879Y77.332
|
||||||
|
G1X195.654Y79.531
|
||||||
|
G1X196.397Y81.760
|
||||||
|
G1X197.079Y83.901
|
||||||
|
G1X197.670Y85.836
|
||||||
|
G2X200.707Y88.082I3.036J-0.928
|
||||||
|
G1X200.997
|
||||||
|
G2X204.167Y84.733I0.000J-3.175
|
||||||
|
G1X203.999Y81.672
|
||||||
|
G1X203.880Y78.828
|
||||||
|
G1X203.809Y76.184
|
||||||
|
G1X203.785Y73.726
|
||||||
|
G1Y55.036
|
||||||
|
G0Z5.080
|
||||||
|
G0X186.055Y18.115
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X186.009Y18.105F2540.0
|
||||||
|
G1X186.055Y17.999
|
||||||
|
G1Y18.115
|
||||||
|
G1X185.846Y20.662
|
||||||
|
G2X183.925Y20.635I-1.003J3.012
|
||||||
|
G1X183.850Y20.658
|
||||||
|
G2X182.792Y21.213I0.918J3.039
|
||||||
|
G1X182.707Y21.280
|
||||||
|
G1X182.260Y21.714
|
||||||
|
G1X182.051Y21.961
|
||||||
|
G1X181.761Y22.394
|
||||||
|
G1X181.394Y23.076
|
||||||
|
G1X181.055Y23.830
|
||||||
|
G1X173.477Y41.419
|
||||||
|
G1X172.282
|
||||||
|
G1Y8.016
|
||||||
|
G1X172.558
|
||||||
|
G1Y24.599
|
||||||
|
G1X172.580Y25.408
|
||||||
|
G1X172.663Y26.179
|
||||||
|
G1X172.774Y26.696
|
||||||
|
G1X172.878Y27.007
|
||||||
|
G1X173.108Y27.530
|
||||||
|
G1X173.164Y27.632
|
||||||
|
G2X173.748Y28.390I2.780J-1.535
|
||||||
|
G1X173.813Y28.452
|
||||||
|
G2X175.480Y29.290I2.196J-2.293
|
||||||
|
G1X175.555Y29.303
|
||||||
|
G2X177.475Y29.026I0.528J-3.131
|
||||||
|
G1X177.562Y28.984
|
||||||
|
G2X178.399Y28.391I-1.391J-2.854
|
||||||
|
G1X178.498Y28.294
|
||||||
|
G1X178.838Y27.899
|
||||||
|
G1X179.037Y27.625
|
||||||
|
G1X179.285Y27.206
|
||||||
|
G1X179.505Y26.777
|
||||||
|
G1X187.630Y8.016
|
||||||
|
G1X188.570
|
||||||
|
G1Y41.419
|
||||||
|
G1X188.294
|
||||||
|
G1Y25.010
|
||||||
|
G1X188.271Y24.216
|
||||||
|
G1X188.177Y23.433
|
||||||
|
G1X188.086Y23.078
|
||||||
|
G1X187.917Y22.590
|
||||||
|
G1X187.870Y22.482
|
||||||
|
G2X187.362Y21.681I-2.906J1.279
|
||||||
|
G1X187.306Y21.616
|
||||||
|
G2X185.911Y20.684I-2.399J2.080
|
||||||
|
G1X185.846Y20.662
|
||||||
|
G0Z5.080
|
||||||
|
G0X174.797Y31.718
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X174.912Y31.742F2540.0
|
||||||
|
G1X174.797Y32.011
|
||||||
|
G1Y31.718
|
||||||
|
G0Z5.080
|
||||||
|
G0X201.208Y8.016
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X201.483F2540.0
|
||||||
|
G1Y41.419
|
||||||
|
G1X201.208
|
||||||
|
G1Y8.016
|
||||||
|
G0Z5.080
|
||||||
|
G0X221.852
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X222.128F2540.0
|
||||||
|
G1Y35.585
|
||||||
|
G1X222.152Y36.556
|
||||||
|
G1X222.239Y37.441
|
||||||
|
G1X222.338Y37.967
|
||||||
|
G1X222.494Y38.513
|
||||||
|
G1X222.648Y38.864
|
||||||
|
G1X222.990Y39.459
|
||||||
|
G1X223.068Y39.565
|
||||||
|
G2X223.987Y40.409I2.564J-1.872
|
||||||
|
G1X224.094Y40.473
|
||||||
|
G1X224.660Y40.744
|
||||||
|
G1X224.991Y40.863
|
||||||
|
G1X225.495Y40.981
|
||||||
|
G1X225.981Y41.055
|
||||||
|
G1X226.840Y41.124
|
||||||
|
G1X227.808Y41.143
|
||||||
|
G1X229.857
|
||||||
|
G1Y41.419
|
||||||
|
G1X214.122
|
||||||
|
G1Y41.143
|
||||||
|
G1X217.128Y41.132
|
||||||
|
G1X217.865Y41.093
|
||||||
|
G1X218.540Y41.009
|
||||||
|
G1X219.037Y40.895
|
||||||
|
G1X219.329Y40.787
|
||||||
|
G1X219.958Y40.472
|
||||||
|
G1X220.109Y40.375
|
||||||
|
G1X220.711Y39.869
|
||||||
|
G1X220.827Y39.744
|
||||||
|
G1X221.305Y39.070
|
||||||
|
G1X221.385Y38.918
|
||||||
|
G1X221.658Y38.205
|
||||||
|
G1X221.703Y38.027
|
||||||
|
G1X221.790Y37.485
|
||||||
|
G1X221.837Y36.840
|
||||||
|
G1X221.852Y35.846
|
||||||
|
G1Y8.016
|
||||||
|
G0Z5.080
|
||||||
|
G0X250.225
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X250.501F2540.0
|
||||||
|
G1Y16.797
|
||||||
|
G1X250.516Y17.761
|
||||||
|
G1X250.567Y18.756
|
||||||
|
G1X250.681Y19.487
|
||||||
|
G1X250.826Y20.159
|
||||||
|
G1X251.009Y20.832
|
||||||
|
G1X257.512Y41.419
|
||||||
|
G1X257.222
|
||||||
|
G1X253.849Y30.875
|
||||||
|
G1X253.398Y29.763
|
||||||
|
G1X253.228Y29.421
|
||||||
|
G1X252.892Y28.886
|
||||||
|
G1X252.817Y28.790
|
||||||
|
G2X250.547Y27.571I-2.508J1.947
|
||||||
|
G1X250.489Y27.567
|
||||||
|
G2X248.506Y28.080I-0.238J3.166
|
||||||
|
G1X248.444Y28.121
|
||||||
|
G2X247.655Y28.861I1.745J2.653
|
||||||
|
G1X247.590Y28.947
|
||||||
|
G1X247.304Y29.401
|
||||||
|
G1X247.150Y29.698
|
||||||
|
G1X246.888Y30.394
|
||||||
|
G1X246.668Y31.132
|
||||||
|
G1X243.420Y41.419
|
||||||
|
G1X243.133
|
||||||
|
G1X249.500Y21.400
|
||||||
|
G1X249.680Y20.918
|
||||||
|
G1X249.880Y20.269
|
||||||
|
G1X250.036Y19.612
|
||||||
|
G1X250.149Y18.936
|
||||||
|
G1X250.190Y18.463
|
||||||
|
G1X250.211Y17.912
|
||||||
|
G1X250.225Y16.513
|
||||||
|
G1Y8.016
|
||||||
|
G0Z5.080
|
||||||
|
G0X159.368
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X159.644F2540.0
|
||||||
|
G1Y41.419
|
||||||
|
G1X159.368
|
||||||
|
G1Y8.016
|
||||||
|
G0Z5.080
|
||||||
|
G0X133.203
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X133.478F2540.0
|
||||||
|
G1Y19.288
|
||||||
|
G1X133.495Y20.123
|
||||||
|
G1X133.556Y20.925
|
||||||
|
G1X133.697Y21.712
|
||||||
|
G1X133.822Y22.074
|
||||||
|
G1X134.078Y22.633
|
||||||
|
G1X134.130Y22.723
|
||||||
|
G2X135.032Y23.713I2.746J-1.594
|
||||||
|
G1X135.122Y23.778
|
||||||
|
G1X135.738Y24.121
|
||||||
|
G1X136.070Y24.260
|
||||||
|
G1X136.588Y24.399
|
||||||
|
G1X137.076Y24.485
|
||||||
|
G1X137.885Y24.559
|
||||||
|
G1X138.771Y24.580
|
||||||
|
G1X145.626
|
||||||
|
G1Y24.855
|
||||||
|
G1X139.094
|
||||||
|
G1X138.075Y24.883
|
||||||
|
G1X137.539Y24.929
|
||||||
|
G1X137.029Y24.999
|
||||||
|
G1X136.489Y25.115
|
||||||
|
G1X136.122Y25.236
|
||||||
|
G1X135.630Y25.447
|
||||||
|
G1X135.480Y25.527
|
||||||
|
G1X134.777Y26.035
|
||||||
|
G1X134.681Y26.127
|
||||||
|
G1X134.136Y26.817
|
||||||
|
G1X134.084Y26.906
|
||||||
|
G1X133.828Y27.457
|
||||||
|
G1X133.702Y27.818
|
||||||
|
G1X133.557Y28.600
|
||||||
|
G1X133.495Y29.391
|
||||||
|
G1X133.478Y30.207
|
||||||
|
G1Y35.748
|
||||||
|
G1X133.502Y36.665
|
||||||
|
G1X133.541Y37.153
|
||||||
|
G1X133.601Y37.619
|
||||||
|
G1X133.699Y38.112
|
||||||
|
G1X133.873Y38.681
|
||||||
|
G1X134.045Y39.039
|
||||||
|
G1X134.470Y39.698
|
||||||
|
G1X134.548Y39.791
|
||||||
|
G2X135.430Y40.523I2.436J-2.037
|
||||||
|
G1X135.520Y40.574
|
||||||
|
G1X136.072Y40.818
|
||||||
|
G1X136.429Y40.936
|
||||||
|
G1X137.204Y41.070
|
||||||
|
G1X137.999Y41.128
|
||||||
|
G1X138.833Y41.143
|
||||||
|
G1X146.730
|
||||||
|
G1Y41.419
|
||||||
|
G1X133.203
|
||||||
|
G1Y8.016
|
||||||
|
G0Z5.080
|
||||||
|
G0X107.038
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X120.565F2540.0
|
||||||
|
G1Y8.291
|
||||||
|
G1X112.934
|
||||||
|
G1X111.953Y8.315
|
||||||
|
G1X111.446Y8.353
|
||||||
|
G1X110.966Y8.412
|
||||||
|
G1X110.462Y8.507
|
||||||
|
G1X109.883Y8.676
|
||||||
|
G1X109.516Y8.848
|
||||||
|
G1X108.829Y9.284
|
||||||
|
G1X108.734Y9.363
|
||||||
|
G2X108.068Y10.127I2.031J2.441
|
||||||
|
G1X108.003Y10.231
|
||||||
|
G1X107.733Y10.777
|
||||||
|
G1X107.609Y11.102
|
||||||
|
G1X107.485Y11.599
|
||||||
|
G1X107.405Y12.080
|
||||||
|
G1X107.334Y12.901
|
||||||
|
G1X107.313Y13.808
|
||||||
|
G1Y19.288
|
||||||
|
G1X107.330Y20.123
|
||||||
|
G1X107.391Y20.925
|
||||||
|
G1X107.532Y21.712
|
||||||
|
G1X107.657Y22.074
|
||||||
|
G1X107.913Y22.633
|
||||||
|
G1X107.965Y22.723
|
||||||
|
G2X108.867Y23.713I2.746J-1.594
|
||||||
|
G1X108.957Y23.778
|
||||||
|
G1X109.573Y24.121
|
||||||
|
G1X109.905Y24.260
|
||||||
|
G1X110.423Y24.399
|
||||||
|
G1X110.911Y24.485
|
||||||
|
G1X111.720Y24.559
|
||||||
|
G1X112.606Y24.580
|
||||||
|
G1X119.461
|
||||||
|
G1Y24.855
|
||||||
|
G1X112.929
|
||||||
|
G1X111.910Y24.883
|
||||||
|
G1X111.374Y24.929
|
||||||
|
G1X110.863Y24.999
|
||||||
|
G1X110.323Y25.115
|
||||||
|
G1X109.957Y25.236
|
||||||
|
G1X109.464Y25.447
|
||||||
|
G1X109.315Y25.527
|
||||||
|
G1X108.612Y26.035
|
||||||
|
G1X108.516Y26.127
|
||||||
|
G1X107.971Y26.817
|
||||||
|
G1X107.919Y26.906
|
||||||
|
G1X107.663Y27.457
|
||||||
|
G1X107.536Y27.818
|
||||||
|
G1X107.392Y28.600
|
||||||
|
G1X107.330Y29.391
|
||||||
|
G1X107.313Y30.207
|
||||||
|
G1Y35.748
|
||||||
|
G1X107.337Y36.665
|
||||||
|
G1X107.376Y37.153
|
||||||
|
G1X107.436Y37.619
|
||||||
|
G1X107.534Y38.112
|
||||||
|
G1X107.708Y38.681
|
||||||
|
G1X107.880Y39.039
|
||||||
|
G1X108.305Y39.698
|
||||||
|
G1X108.383Y39.791
|
||||||
|
G2X109.265Y40.523I2.436J-2.037
|
||||||
|
G1X109.355Y40.573
|
||||||
|
G1X109.907Y40.817
|
||||||
|
G1X110.263Y40.936
|
||||||
|
G1X111.039Y41.070
|
||||||
|
G1X111.834Y41.128
|
||||||
|
G1X112.668Y41.143
|
||||||
|
G1X120.565
|
||||||
|
G1Y41.419
|
||||||
|
G1X107.038
|
||||||
|
G1Y8.016
|
||||||
|
G0Z5.080
|
||||||
|
G0X91.885Y18.115
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X91.839Y18.105F2540.0
|
||||||
|
G1X91.885Y17.998
|
||||||
|
G1Y18.115
|
||||||
|
G1X91.677Y20.662
|
||||||
|
G2X89.754Y20.635I-1.004J3.012
|
||||||
|
G1X89.680Y20.658
|
||||||
|
G2X88.622Y21.213I0.918J3.039
|
||||||
|
G1X88.537Y21.280
|
||||||
|
G1X88.090Y21.713
|
||||||
|
G1X87.881Y21.961
|
||||||
|
G1X87.591Y22.393
|
||||||
|
G1X87.224Y23.076
|
||||||
|
G1X86.885Y23.829
|
||||||
|
G1X79.307Y41.419
|
||||||
|
G1X78.112
|
||||||
|
G1Y8.016
|
||||||
|
G1X78.388
|
||||||
|
G1Y24.599
|
||||||
|
G1X78.409Y25.408
|
||||||
|
G1X78.493Y26.180
|
||||||
|
G1X78.604Y26.697
|
||||||
|
G1X78.709Y27.008
|
||||||
|
G1X78.939Y27.531
|
||||||
|
G1X78.995Y27.633
|
||||||
|
G2X79.578Y28.391I2.779J-1.535
|
||||||
|
G1X79.643Y28.453
|
||||||
|
G2X81.310Y29.290I2.196J-2.293
|
||||||
|
G1X81.385Y29.302
|
||||||
|
G2X83.305Y29.026I0.529J-3.131
|
||||||
|
G1X83.392Y28.983
|
||||||
|
G2X84.229Y28.391I-1.392J-2.854
|
||||||
|
G1X84.328Y28.294
|
||||||
|
G1X84.668Y27.899
|
||||||
|
G1X84.867Y27.625
|
||||||
|
G1X85.115Y27.206
|
||||||
|
G1X85.335Y26.777
|
||||||
|
G1X93.460Y8.016
|
||||||
|
G1X94.400
|
||||||
|
G1Y41.419
|
||||||
|
G1X94.124
|
||||||
|
G1Y25.009
|
||||||
|
G1X94.101Y24.216
|
||||||
|
G1X94.007Y23.432
|
||||||
|
G1X93.916Y23.078
|
||||||
|
G1X93.747Y22.590
|
||||||
|
G1X93.700Y22.482
|
||||||
|
G2X93.193Y21.681I-2.906J1.279
|
||||||
|
G1X93.137Y21.617
|
||||||
|
G2X91.742Y20.684I-2.399J2.079
|
||||||
|
G1X91.677Y20.662
|
||||||
|
G0Z5.080
|
||||||
|
G0X80.627Y31.718
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X80.742Y31.742F2540.0
|
||||||
|
G1X80.627Y32.010
|
||||||
|
G1Y31.718
|
||||||
|
G0Z5.080
|
||||||
|
G0X56.689Y8.295
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X57.314Y8.143F2540.0
|
||||||
|
G1X57.985Y8.049
|
||||||
|
G1X58.710Y8.016
|
||||||
|
G1X59.435Y8.049
|
||||||
|
G1X60.106Y8.143
|
||||||
|
G1X60.731Y8.295
|
||||||
|
G1X61.320Y8.505
|
||||||
|
G1X61.883Y8.775
|
||||||
|
G1X62.429Y9.109
|
||||||
|
G1X62.964Y9.516
|
||||||
|
G1X63.493Y10.004
|
||||||
|
G1X63.980Y10.541
|
||||||
|
G1X64.387Y11.085
|
||||||
|
G1X64.721Y11.636
|
||||||
|
G1X64.989Y12.201
|
||||||
|
G1X65.197Y12.788
|
||||||
|
G1X65.348Y13.407
|
||||||
|
G1X65.441Y14.069
|
||||||
|
G1X65.474Y14.815
|
||||||
|
G1X65.465Y35.027
|
||||||
|
G1X65.401Y35.727
|
||||||
|
G1X65.278Y36.377
|
||||||
|
G1X65.098Y36.986
|
||||||
|
G1X64.861Y37.562
|
||||||
|
G1X64.563Y38.116
|
||||||
|
G1X64.197Y38.656
|
||||||
|
G1X63.755Y39.187
|
||||||
|
G1X63.241Y39.701
|
||||||
|
G1X62.710Y40.142
|
||||||
|
G1X62.170Y40.508
|
||||||
|
G1X61.616Y40.807
|
||||||
|
G1X61.040Y41.044
|
||||||
|
G1X60.431Y41.224
|
||||||
|
G1X59.781Y41.347
|
||||||
|
G1X59.081Y41.410
|
||||||
|
G1X58.339
|
||||||
|
G1X57.639Y41.347
|
||||||
|
G1X56.989Y41.224
|
||||||
|
G1X56.381Y41.044
|
||||||
|
G1X55.804Y40.807
|
||||||
|
G1X55.250Y40.508
|
||||||
|
G1X54.710Y40.142
|
||||||
|
G1X54.179Y39.701
|
||||||
|
G1X53.665Y39.187
|
||||||
|
G1X53.224Y38.656
|
||||||
|
G1X52.858Y38.116
|
||||||
|
G1X52.559Y37.562
|
||||||
|
G1X52.322Y36.986
|
||||||
|
G1X52.142Y36.377
|
||||||
|
G1X52.019Y35.727
|
||||||
|
G1X51.956Y35.027
|
||||||
|
G1X51.956Y14.417
|
||||||
|
G1X52.018Y13.732
|
||||||
|
G1X52.140Y13.093
|
||||||
|
G1X52.320Y12.491
|
||||||
|
G1X52.558Y11.916
|
||||||
|
G1X52.858Y11.359
|
||||||
|
G1X53.228Y10.812
|
||||||
|
G1X53.674Y10.272
|
||||||
|
G1X54.191Y9.750
|
||||||
|
G1X54.722Y9.303
|
||||||
|
G1X55.262Y8.933
|
||||||
|
G1X55.816Y8.632
|
||||||
|
G1X56.391Y8.393
|
||||||
|
G1X56.689Y8.295
|
||||||
|
G0Z5.080
|
||||||
|
G0X56.804Y8.566
|
||||||
|
G1Z-3.175F1270.0
|
||||||
|
G1X56.190Y8.789F2540.0
|
||||||
|
G1X55.603Y9.076
|
||||||
|
G1X55.053Y9.420
|
||||||
|
G1X54.545Y9.814
|
||||||
|
G1X54.085Y10.247
|
||||||
|
G1X53.673Y10.711
|
||||||
|
G1X53.303Y11.209
|
||||||
|
G1X52.978Y11.746
|
||||||
|
G1X52.705Y12.317
|
||||||
|
G1X52.490Y12.915
|
||||||
|
G1X52.338Y13.530
|
||||||
|
G1X52.248Y14.154
|
||||||
|
G1X52.222Y14.705
|
||||||
|
G1Y34.727
|
||||||
|
G1X52.247Y35.285
|
||||||
|
G1X52.335Y35.913
|
||||||
|
G1X52.485Y36.536
|
||||||
|
G1X52.699Y37.143
|
||||||
|
G1X52.975Y37.726
|
||||||
|
G1X53.308Y38.275
|
||||||
|
G1X53.691Y38.784
|
||||||
|
G1X54.116Y39.250
|
||||||
|
G1X54.583Y39.676
|
||||||
|
G1X55.091Y40.058
|
||||||
|
G1X55.640Y40.391
|
||||||
|
G1X56.223Y40.667
|
||||||
|
G1X56.831Y40.881
|
||||||
|
G1X57.453Y41.031
|
||||||
|
G1X58.082Y41.119
|
||||||
|
G1X58.710Y41.147
|
||||||
|
G1X59.339Y41.119
|
||||||
|
G1X59.967Y41.031
|
||||||
|
G1X60.590Y40.881
|
||||||
|
G1X61.197Y40.667
|
||||||
|
G1X61.780Y40.391
|
||||||
|
G1X62.329Y40.058
|
||||||
|
G1X62.837Y39.676
|
||||||
|
G1X63.304Y39.250
|
||||||
|
G1X63.730Y38.784
|
||||||
|
G1X64.112Y38.275
|
||||||
|
G1X64.445Y37.726
|
||||||
|
G1X64.721Y37.143
|
||||||
|
G1X64.935Y36.536
|
||||||
|
G1X65.086Y35.913
|
||||||
|
G1X65.173Y35.285
|
||||||
|
G1X65.198Y34.727
|
||||||
|
G1Y14.705
|
||||||
|
G1X65.172Y14.154
|
||||||
|
G1X65.083Y13.530
|
||||||
|
G1X64.930Y12.915
|
||||||
|
G1X64.716Y12.317
|
||||||
|
G1X64.443Y11.746
|
||||||
|
G1X64.118Y11.209
|
||||||
|
G1X63.748Y10.711
|
||||||
|
G1X63.335Y10.247
|
||||||
|
G1X62.875Y9.814
|
||||||
|
G1X62.367Y9.420
|
||||||
|
G1X61.817Y9.076
|
||||||
|
G1X61.230Y8.789
|
||||||
|
G1X60.616Y8.566
|
||||||
|
G1X59.985Y8.409
|
||||||
|
G1X59.348Y8.317
|
||||||
|
G1X58.710Y8.288
|
||||||
|
G1X58.072Y8.317
|
||||||
|
G1X57.435Y8.409
|
||||||
|
G1X56.804Y8.566
|
||||||
|
G0Z5.080
|
||||||
|
G0Z20.320
|
||||||
|
G0X0.000Y0.000
|
||||||
|
M2
|
||||||
|
%
|
||||||
BIN
installer/linux-packages/virtualKeyboard.zip
Normal file
BIN
installer/linux-packages/virtualKeyboard.zip
Normal file
Binary file not shown.
BIN
installer/linux-packages/zram-config-main.zip
Normal file
BIN
installer/linux-packages/zram-config-main.zip
Normal file
Binary file not shown.
BIN
installer/python-packages/evdev-1.6.0.tar.gz
Normal file
BIN
installer/python-packages/evdev-1.6.0.tar.gz
Normal file
Binary file not shown.
151
installer/scripts/config-wifi
Normal file
151
installer/scripts/config-wifi
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
#!/bin/bash -e
|
||||||
|
|
||||||
|
DISABLE=false
|
||||||
|
SSID=
|
||||||
|
PASS=
|
||||||
|
REBOOT=false
|
||||||
|
|
||||||
|
WLAN0_CFG=/etc/network/interfaces.d/wlan0
|
||||||
|
DHCPCD_CFG=/etc/dhcpcd.conf
|
||||||
|
WPA_CFG=/etc/wpa_supplicant/wpa_supplicant.conf
|
||||||
|
|
||||||
|
|
||||||
|
function query_config() {
|
||||||
|
SSID=$(grep wpa-ssid $WLAN0_CFG |
|
||||||
|
sed 's/^[[:space:]]*wpa-ssid "\([^"]*\)"/\1/')
|
||||||
|
echo "{\"ssid\": \"$SSID\"}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function disable_wifi() {
|
||||||
|
rm -f $WLAN0_CFG
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function configure_wlan0() {
|
||||||
|
echo "auto wlan0"
|
||||||
|
echo "allow-hotplug wlan0"
|
||||||
|
echo "iface wlan0 inet dhcp"
|
||||||
|
echo " wpa-scan-ssid 1"
|
||||||
|
echo " wpa-ap-scan 1"
|
||||||
|
echo " wpa-key-mgmt WPA-PSK"
|
||||||
|
echo " wpa-proto RSN WPA"
|
||||||
|
echo " wpa-pairwise CCMP TKIP"
|
||||||
|
echo " wpa-group CCMP TKIP"
|
||||||
|
echo " wpa-ssid \"$SSID\""
|
||||||
|
|
||||||
|
if [ ${#PASS} -ne 0 ]; then
|
||||||
|
echo " wpa-psk \"$PASS\""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function configure_wpa() {
|
||||||
|
echo "country=US"
|
||||||
|
echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev"
|
||||||
|
echo "update_config=1"
|
||||||
|
|
||||||
|
if [ ${#PASS} -eq 0 ]; then
|
||||||
|
echo "network={"
|
||||||
|
echo " ssid=\"$SSID\""
|
||||||
|
echo " key_mgmt=NONE"
|
||||||
|
echo "}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function configure_dhcpcd() {
|
||||||
|
echo "hostname"
|
||||||
|
echo "clientid"
|
||||||
|
echo "persistent"
|
||||||
|
echo "option rapid_commit"
|
||||||
|
echo "option domain_name_servers, domain_name, domain_search, host_name"
|
||||||
|
echo "option classless_static_routes"
|
||||||
|
echo "option ntp_servers"
|
||||||
|
echo "option interface_mtu"
|
||||||
|
echo "require dhcp_server_identifier"
|
||||||
|
echo "slaac private"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function configure_wifi() {
|
||||||
|
disable_wifi
|
||||||
|
echo "source-directory /etc/network/interfaces.d" > /etc/network/interfaces
|
||||||
|
configure_wlan0 > $WLAN0_CFG
|
||||||
|
configure_wpa > $WPA_CFG
|
||||||
|
configure_dhcpcd > $DHCPCD_CFG
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
echo "Usage: config-wifi [OPTIONS]"
|
||||||
|
echo
|
||||||
|
echo "Configure wifi as either a client or access point."
|
||||||
|
echo
|
||||||
|
echo "OPTIONS:"
|
||||||
|
echo
|
||||||
|
echo " -d Disable wifi."
|
||||||
|
echo " -r Reboot when done."
|
||||||
|
echo " -s <SSID> Set SSID."
|
||||||
|
echo " -p <PASS> Set password."
|
||||||
|
echo " -j Report wifi config as JSON data."
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Parse args
|
||||||
|
while [ $# -ne 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-d) DISABLE=true ;;
|
||||||
|
-r) REBOOT=true; ;;
|
||||||
|
-s) SSID="$2"; shift ;;
|
||||||
|
-p) PASS="$2"; shift ;;
|
||||||
|
-j) query_config; exit 0 ;;
|
||||||
|
|
||||||
|
-h)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
echo "Unknown argument '$1'"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
if $DISABLE; then
|
||||||
|
disable_wifi
|
||||||
|
else
|
||||||
|
# Check args
|
||||||
|
function clean_str() {
|
||||||
|
echo "$1" | tr -d '\n\r"'
|
||||||
|
}
|
||||||
|
|
||||||
|
SSID=$(clean_str "$SSID")
|
||||||
|
PASS=$(clean_str "$PASS")
|
||||||
|
|
||||||
|
LANG=C LC_ALL=C # For correct string byte length
|
||||||
|
|
||||||
|
if [ ${#SSID} -eq 0 -o 32 -lt ${#SSID} ]; then
|
||||||
|
echo "Invalid or missing SSID '$SSID'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#PASS} -ne 0 ]; then
|
||||||
|
if [ ${#PASS} -lt 8 -o 128 -lt ${#PASS} ]; then
|
||||||
|
echo "Invalid passsword"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
echo "Configuring Wifi"
|
||||||
|
configure_wifi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if $REBOOT; then nohup reboot & fi
|
||||||
@@ -37,7 +37,6 @@ should_resize_root_partition() {
|
|||||||
|
|
||||||
if [ "$ROOT_PART_END" -gt "$TARGET_END" ]; then
|
if [ "$ROOT_PART_END" -gt "$TARGET_END" ]; then
|
||||||
FAIL_REASON="Root partition runs past the end of device"
|
FAIL_REASON="Root partition runs past the end of device"
|
||||||
echo $FAIL_REASON
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -60,18 +59,9 @@ if should_resize_root_partition; then
|
|||||||
# Remove itself from /boot/cmdline.txt
|
# Remove itself from /boot/cmdline.txt
|
||||||
# Reboot the machine
|
# Reboot the machine
|
||||||
sudo mount /boot -o rw,remount
|
sudo mount /boot -o rw,remount
|
||||||
sed -i 's/\(.*\)/\1 init=\/usr\/lib\/raspi-config\/init_resize.sh/' /boot/cmdline.txt
|
sed -i -E 's|(.*)|\1 init=/usr/lib/raspi-config/init_resize.sh|' /boot/cmdline.txt
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# On the first boot after init_resize, resize2fs_once will:
|
|
||||||
# Resize the root fs to fill its partition
|
|
||||||
# Remove itself from the registered systemd services
|
|
||||||
# Delete itself from the filesystem
|
|
||||||
# Therefore, never run again
|
|
||||||
cp scripts/resize2fs_once /etc/init.d/resize2fs_once
|
|
||||||
chmod +x /etc/init.d/resize2fs_once
|
|
||||||
systemctl enable resize2fs_once
|
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
echo "Not resizing root partition: $FAIL_REASON"
|
echo "Not resizing root partition: $FAIL_REASON"
|
||||||
BIN
installer/splash/boot.png
Normal file
BIN
installer/splash/boot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
8
installer/splash/onefinity.plymouth
Normal file
8
installer/splash/onefinity.plymouth
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Plymouth Theme]
|
||||||
|
Name=onefinity
|
||||||
|
Description=Onefinity
|
||||||
|
ModuleName=script
|
||||||
|
|
||||||
|
[script]
|
||||||
|
ImageDir=/usr/share/plymouth/themes/onefinity
|
||||||
|
ScriptFile=/usr/share/plymouth/themes/onefinity/onefinity.script
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
screenW = Window.GetWidth();
|
screenW = Window.GetWidth();
|
||||||
screenH = Window.GetHeight();
|
screenH = Window.GetHeight();
|
||||||
|
|
||||||
image = Image("splash.png");
|
image = Image("boot.png");
|
||||||
|
|
||||||
|
if (Plymouth.GetMode() == "shutdown") {
|
||||||
|
image = Image("shutdown.png");
|
||||||
|
}
|
||||||
|
|
||||||
imageW = image.GetWidth();
|
imageW = image.GetWidth();
|
||||||
imageH = image.GetHeight();
|
imageH = image.GetHeight();
|
||||||
|
|
||||||
BIN
installer/splash/shutdown.png
Normal file
BIN
installer/splash/shutdown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
16199
package-lock.json
generated
16199
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
41
package.json
41
package.json
@@ -1,17 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "bbctrl",
|
"name": "bbctrl",
|
||||||
"version": "1.0.10b1",
|
"version": "1.1.0",
|
||||||
"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+",
|
||||||
"dependencies": {
|
"scripts": {
|
||||||
"browserify": "^17.0.0",
|
"postinstall": "cd src/svelte-components && npm i"
|
||||||
"jshint": "^2.13.4",
|
},
|
||||||
"jstransformer-escape-html": "^1.1.0",
|
"devDependencies": {
|
||||||
"jstransformer-scss": "^2.0.0",
|
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
||||||
"jstransformer-stylus": "^1.5.0",
|
"@typescript-eslint/parser": "^5.36.1",
|
||||||
"lodash.merge": "4.6.2",
|
"browserify": "^17.0.0",
|
||||||
"lodash.omit": "^4.5.0",
|
"eslint": "^8.23.0",
|
||||||
"pug-cli": "^1.0.0-alpha6"
|
"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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
20
pug-opts.js
20
pug-opts.js
@@ -1,11 +1,11 @@
|
|||||||
{
|
module.exports = {
|
||||||
pretty: true,
|
pretty: true,
|
||||||
filters: {
|
filters: {
|
||||||
browserify: function (text, options) {
|
browserify: function(text, _options) {
|
||||||
return require('child_process').execSync(
|
return require("child_process").execSync(
|
||||||
`./node_modules/.bin/browserify - --basedir src/js`,
|
`./node_modules/.bin/browserify - --basedir src/js`,
|
||||||
{input: text}
|
{ input: text }
|
||||||
).toString()
|
).toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
watchdog==0.10.6
|
watchdog==0.10.6
|
||||||
|
evdev==1.6.0
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
'''Check that the configuration variable template used on the RPi matches the
|
|
||||||
variables used in the AVR'''
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
|
|
||||||
templ = json.load(open('src/resources/config-template.json', 'r'))
|
|
||||||
vars = json.load(open('avr/build/vars.json', 'r'))
|
|
||||||
|
|
||||||
|
|
||||||
def check(section):
|
|
||||||
errors = 0
|
|
||||||
|
|
||||||
for name, entry in section.items():
|
|
||||||
if 'type' in entry:
|
|
||||||
ok = False
|
|
||||||
|
|
||||||
# TODO check that defaults are valid
|
|
||||||
# TODO check that types match
|
|
||||||
|
|
||||||
if 'code' in entry and not entry['code'] in vars:
|
|
||||||
print('"%s" with code "%s" not found' % (name, entry['code']))
|
|
||||||
|
|
||||||
else: ok = True
|
|
||||||
|
|
||||||
if not ok: errors += 1
|
|
||||||
|
|
||||||
else: errors += check(entry)
|
|
||||||
|
|
||||||
return errors
|
|
||||||
|
|
||||||
|
|
||||||
errors = check(templ)
|
|
||||||
print('\n%d errors' % errors)
|
|
||||||
sys.exit(errors != 0)
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [ $# != 3 ]; then
|
|
||||||
echo "Usage: $0 <width> <height> <rotation>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
WIDTH="$1"
|
|
||||||
HEIGHT="$2"
|
|
||||||
ROTATION="$3"
|
|
||||||
|
|
||||||
if [[ ! "$WIDTH" =~ ^[0-9]+$ ]]; then
|
|
||||||
echo "Invalid width '$WIDTH'."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! "$HEIGHT" =~ ^[0-9]+$ ]]; then
|
|
||||||
echo "Invalid height '$HEIGHT'."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! "$ROTATION" =~ ^[0-3]$ ]]; then
|
|
||||||
echo "Invalid rotation '$ROTATION'."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
OPTIONS="framebuffer_width=$WIDTH "
|
|
||||||
OPTIONS+="framebuffer_height=$HEIGHT "
|
|
||||||
OPTIONS+="display_rotate=$ROTATION"
|
|
||||||
|
|
||||||
edit-boot-config $OPTIONS
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
AP=false
|
|
||||||
DISABLE=false
|
|
||||||
SSID=
|
|
||||||
PASS=
|
|
||||||
CHANNEL=7
|
|
||||||
REBOOT=false
|
|
||||||
|
|
||||||
WLAN0_CFG=/etc/network/interfaces.d/wlan0
|
|
||||||
HOSTAPD_CFG=/etc/hostapd/hostapd.conf
|
|
||||||
DNSMASQ_CFG=/etc/dnsmasq.conf
|
|
||||||
DHCPCD_CFG=/etc/dhcpcd.conf
|
|
||||||
WPA_CFG=/etc/wpa_supplicant/wpa_supplicant.conf
|
|
||||||
|
|
||||||
|
|
||||||
function query_config() {
|
|
||||||
if [ -e $WLAN0_CFG ]; then
|
|
||||||
SSID=$(grep wpa-ssid $WLAN0_CFG |
|
|
||||||
sed 's/^[[:space:]]*wpa-ssid "\([^"]*\)"/\1/')
|
|
||||||
# echo "{\"ssid\": \"$SSID\", \"mode\": \"client\"}"
|
|
||||||
echo "{\"ssid\": \"$SSID\"}"
|
|
||||||
|
|
||||||
else
|
|
||||||
# if [ -e $HOSTAPD_CFG -a -e /etc/default/hostapd ]; then
|
|
||||||
# SSID=$(grep ^ssid= $HOSTAPD_CFG | sed 's/^ssid=\(.*\)$/\1/')
|
|
||||||
# CHANNEL=$(grep ^channel= $HOSTAPD_CFG |
|
|
||||||
# sed 's/^channel=\(.*\)$/\1/')
|
|
||||||
|
|
||||||
# echo -n "{\"ssid\": \"$SSID\", "
|
|
||||||
# echo "\"channel\": $CHANNEL, \"mode\": \"ap\"}"
|
|
||||||
|
|
||||||
# else
|
|
||||||
# echo "{\"mode\": \"disabled\"}"
|
|
||||||
# fi
|
|
||||||
|
|
||||||
echo "{}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function disable_wifi() {
|
|
||||||
rm -f $WLAN0_CFG $HOSTAPD_CFG /etc/default/hostapd
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_wlan0() {
|
|
||||||
echo "auto wlan0"
|
|
||||||
echo "allow-hotplug wlan0"
|
|
||||||
echo "iface wlan0 inet dhcp"
|
|
||||||
echo " wpa-scan-ssid 1"
|
|
||||||
echo " wpa-ap-scan 1"
|
|
||||||
echo " wpa-key-mgmt WPA-PSK"
|
|
||||||
echo " wpa-proto RSN WPA"
|
|
||||||
echo " wpa-pairwise CCMP TKIP"
|
|
||||||
echo " wpa-group CCMP TKIP"
|
|
||||||
echo " wpa-ssid \"$SSID\""
|
|
||||||
|
|
||||||
if [ ${#PASS} -ne 0 ]; then
|
|
||||||
echo " wpa-psk \"$PASS\""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_wpa() {
|
|
||||||
echo "country=US"
|
|
||||||
echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev"
|
|
||||||
echo "update_config=1"
|
|
||||||
|
|
||||||
if [ ${#PASS} -eq 0 ]; then
|
|
||||||
echo "network={"
|
|
||||||
echo " ssid=\"$SSID\""
|
|
||||||
echo " key_mgmt=NONE"
|
|
||||||
echo "}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_dhcpcd() {
|
|
||||||
echo "hostname"
|
|
||||||
echo "clientid"
|
|
||||||
echo "persistent"
|
|
||||||
echo "option rapid_commit"
|
|
||||||
echo "option domain_name_servers, domain_name, domain_search, host_name"
|
|
||||||
echo "option classless_static_routes"
|
|
||||||
echo "option ntp_servers"
|
|
||||||
echo "option interface_mtu"
|
|
||||||
echo "require dhcp_server_identifier"
|
|
||||||
echo "slaac private"
|
|
||||||
|
|
||||||
if $AP; then
|
|
||||||
echo
|
|
||||||
echo "interface wlan0"
|
|
||||||
echo " static ip_address=192.168.43.1/24"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_wifi() {
|
|
||||||
disable_wifi
|
|
||||||
echo "source-directory /etc/network/interfaces.d" > /etc/network/interfaces
|
|
||||||
configure_wlan0 > $WLAN0_CFG
|
|
||||||
configure_wpa > $WPA_CFG
|
|
||||||
configure_dhcpcd > $DHCPCD_CFG
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_dnsmasq() {
|
|
||||||
echo "interface=wlan0"
|
|
||||||
echo "domain-needed"
|
|
||||||
echo "bogus-priv"
|
|
||||||
echo "dhcp-range=192.168.43.2,192.168.43.20,255.255.255.0,12h"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_hostapd() {
|
|
||||||
echo "interface=wlan0"
|
|
||||||
echo "driver=nl80211"
|
|
||||||
echo "ssid=$SSID"
|
|
||||||
echo "hw_mode=g"
|
|
||||||
echo "channel=$CHANNEL"
|
|
||||||
echo "wmm_enabled=0"
|
|
||||||
echo "macaddr_acl=0"
|
|
||||||
echo "auth_algs=1"
|
|
||||||
echo "ignore_broadcast_ssid=0"
|
|
||||||
echo "wpa=2"
|
|
||||||
echo "wpa_passphrase=$PASS"
|
|
||||||
echo "wpa_key_mgmt=WPA-PSK"
|
|
||||||
echo "wpa_pairwise=TKIP"
|
|
||||||
echo "rsn_pairwise=CCMP"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function is_installed() {
|
|
||||||
dpkg-query -W --showformat='${Status}' $1 |
|
|
||||||
grep "install ok installed" >/dev/null
|
|
||||||
if [ $? -eq 0 ]; then echo true; else echo false; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_ap() {
|
|
||||||
disable_wifi
|
|
||||||
|
|
||||||
# Install packages
|
|
||||||
(
|
|
||||||
$(is_installed dnsmasq) &&
|
|
||||||
$(is_installed hostapd) &&
|
|
||||||
$(is_installed iptables-persistent)
|
|
||||||
|
|
||||||
) || (
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
apt-get update
|
|
||||||
apt-get install -yq dnsmasq hostapd iptables-persistent
|
|
||||||
)
|
|
||||||
|
|
||||||
configure_dhcpcd > $DHCPCD_CFG
|
|
||||||
configure_dnsmasq > $DNSMASQ_CFG
|
|
||||||
configure_hostapd > $HOSTAPD_CFG
|
|
||||||
|
|
||||||
echo "DAEMON_CONF=\"/etc/hostapd/hostapd.conf\"" > /etc/default/hostapd
|
|
||||||
|
|
||||||
# Enable IP forwarding
|
|
||||||
sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
|
|
||||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
||||||
|
|
||||||
# Enable IP masquerading
|
|
||||||
iptables -t nat -F
|
|
||||||
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
|
||||||
iptables-save > /etc/iptables/rules.v4
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
echo "Usage: config-wifi [OPTIONS]"
|
|
||||||
echo
|
|
||||||
echo "Configure wifi as either a client or access point."
|
|
||||||
echo
|
|
||||||
echo "OPTIONS:"
|
|
||||||
echo
|
|
||||||
echo " -a Configure access point."
|
|
||||||
echo " -d Disable wifi."
|
|
||||||
echo " -r Reboot when done."
|
|
||||||
echo " -s <SSID> Set SSID."
|
|
||||||
echo " -p <PASS> Set password."
|
|
||||||
echo " -c <CHANNEL> Set wifi channel."
|
|
||||||
echo " -j Report wifi config as JSON data."
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Parse args
|
|
||||||
while [ $# -ne 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
-a) AP=true ;;
|
|
||||||
-d) DISABLE=true ;;
|
|
||||||
-r) REBOOT=true; ;;
|
|
||||||
-s) SSID="$2"; shift ;;
|
|
||||||
-p) PASS="$2"; shift ;;
|
|
||||||
-c) CHANNEL="$2"; shift ;;
|
|
||||||
-j) query_config; exit 0 ;;
|
|
||||||
|
|
||||||
-h)
|
|
||||||
usage
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
usage
|
|
||||||
echo "Unknown argument '$1'"
|
|
||||||
exit 1
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
if $DISABLE; then
|
|
||||||
disable_wifi
|
|
||||||
|
|
||||||
else
|
|
||||||
# Check args
|
|
||||||
function clean_str() {
|
|
||||||
echo "$1" | tr -d '\n\r"'
|
|
||||||
}
|
|
||||||
|
|
||||||
SSID=$(clean_str "$SSID")
|
|
||||||
PASS=$(clean_str "$PASS")
|
|
||||||
|
|
||||||
LANG=C LC_ALL=C # For correct string byte length
|
|
||||||
|
|
||||||
if [ ${#SSID} -eq 0 -o 32 -lt ${#SSID} ]; then
|
|
||||||
echo "Invalid or missing SSID '$SSID'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ${#PASS} -ne 0 ]; then
|
|
||||||
if [ ${#PASS} -lt 8 -o 128 -lt ${#PASS} ]; then
|
|
||||||
echo "Invalid passsword"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$CHANNEL" | grep '^[0-9]\{1,2\}' > /dev/null
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Invalid channel '$CHANNEL'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Execute
|
|
||||||
if $AP; then
|
|
||||||
echo "Configuring Wifi access point"
|
|
||||||
configure_ap
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "Configuring Wifi"
|
|
||||||
configure_wifi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if $REBOOT; then nohup reboot & fi
|
|
||||||
@@ -1,259 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
AP=false
|
|
||||||
DISABLE=false
|
|
||||||
SSID=
|
|
||||||
PASS=
|
|
||||||
CHANNEL=7
|
|
||||||
REBOOT=false
|
|
||||||
|
|
||||||
WLAN0_CFG=/etc/network/interfaces.d/wlan0
|
|
||||||
HOSTAPD_CFG=/etc/hostapd/hostapd.conf
|
|
||||||
DNSMASQ_CFG=/etc/dnsmasq.conf
|
|
||||||
DHCPCD_CFG=/etc/dhcpcd.conf
|
|
||||||
WPA_CFG=/etc/wpa_supplicant/wpa_supplicant.conf
|
|
||||||
|
|
||||||
|
|
||||||
function query_config() {
|
|
||||||
if [ -e $WLAN0_CFG ]; then
|
|
||||||
SSID=$(grep wpa-ssid $WLAN0_CFG |
|
|
||||||
sed 's/^[[:space:]]*wpa-ssid "\([^"]*\)"/\1/')
|
|
||||||
echo "{\"ssid\": \"$SSID\", \"mode\": \"client\"}"
|
|
||||||
|
|
||||||
else
|
|
||||||
if [ -e $HOSTAPD_CFG -a -e /etc/default/hostapd ]; then
|
|
||||||
SSID=$(grep ^ssid= $HOSTAPD_CFG | sed 's/^ssid=\(.*\)$/\1/')
|
|
||||||
CHANNEL=$(grep ^channel= $HOSTAPD_CFG |
|
|
||||||
sed 's/^channel=\(.*\)$/\1/')
|
|
||||||
|
|
||||||
echo -n "{\"ssid\": \"$SSID\", "
|
|
||||||
echo "\"channel\": $CHANNEL, \"mode\": \"ap\"}"
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "{\"mode\": \"disabled\"}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function disable_wifi() {
|
|
||||||
rm -f $WLAN0_CFG $HOSTAPD_CFG /etc/default/hostapd
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_wlan0() {
|
|
||||||
echo "auto wlan0"
|
|
||||||
echo "allow-hotplug wlan0"
|
|
||||||
echo "iface wlan0 inet dhcp"
|
|
||||||
echo " wpa-scan-ssid 1"
|
|
||||||
echo " wpa-ap-scan 1"
|
|
||||||
echo " wpa-key-mgmt WPA-PSK"
|
|
||||||
echo " wpa-proto RSN WPA"
|
|
||||||
echo " wpa-pairwise CCMP TKIP"
|
|
||||||
echo " wpa-group CCMP TKIP"
|
|
||||||
echo " wpa-ssid \"$SSID\""
|
|
||||||
|
|
||||||
if [ ${#PASS} -ne 0 ]; then
|
|
||||||
echo " wpa-psk \"$PASS\""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_wpa() {
|
|
||||||
echo "country=US"
|
|
||||||
echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev"
|
|
||||||
echo "update_config=1"
|
|
||||||
|
|
||||||
if [ ${#PASS} -eq 0 ]; then
|
|
||||||
echo "network={"
|
|
||||||
echo " ssid=\"$SSID\""
|
|
||||||
echo " key_mgmt=NONE"
|
|
||||||
echo "}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_dhcpcd() {
|
|
||||||
echo "hostname"
|
|
||||||
echo "clientid"
|
|
||||||
echo "persistent"
|
|
||||||
echo "option rapid_commit"
|
|
||||||
echo "option domain_name_servers, domain_name, domain_search, host_name"
|
|
||||||
echo "option classless_static_routes"
|
|
||||||
echo "option ntp_servers"
|
|
||||||
echo "option interface_mtu"
|
|
||||||
echo "require dhcp_server_identifier"
|
|
||||||
echo "slaac private"
|
|
||||||
|
|
||||||
if $AP; then
|
|
||||||
echo
|
|
||||||
echo "interface wlan0"
|
|
||||||
echo " static ip_address=192.168.43.1/24"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_wifi() {
|
|
||||||
disable_wifi
|
|
||||||
echo "source-directory /etc/network/interfaces.d" > /etc/network/interfaces
|
|
||||||
configure_wlan0 > $WLAN0_CFG
|
|
||||||
configure_wpa > $WPA_CFG
|
|
||||||
configure_dhcpcd > $DHCPCD_CFG
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_dnsmasq() {
|
|
||||||
echo "interface=wlan0"
|
|
||||||
echo "domain-needed"
|
|
||||||
echo "bogus-priv"
|
|
||||||
echo "dhcp-range=192.168.43.2,192.168.43.20,255.255.255.0,12h"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_hostapd() {
|
|
||||||
echo "interface=wlan0"
|
|
||||||
echo "driver=nl80211"
|
|
||||||
echo "ssid=$SSID"
|
|
||||||
echo "hw_mode=g"
|
|
||||||
echo "channel=$CHANNEL"
|
|
||||||
echo "wmm_enabled=0"
|
|
||||||
echo "macaddr_acl=0"
|
|
||||||
echo "auth_algs=1"
|
|
||||||
echo "ignore_broadcast_ssid=0"
|
|
||||||
echo "wpa=2"
|
|
||||||
echo "wpa_passphrase=$PASS"
|
|
||||||
echo "wpa_key_mgmt=WPA-PSK"
|
|
||||||
echo "wpa_pairwise=TKIP"
|
|
||||||
echo "rsn_pairwise=CCMP"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function is_installed() {
|
|
||||||
dpkg-query -W --showformat='${Status}' $1 |
|
|
||||||
grep "install ok installed" >/dev/null
|
|
||||||
if [ $? -eq 0 ]; then echo true; else echo false; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function configure_ap() {
|
|
||||||
disable_wifi
|
|
||||||
|
|
||||||
# Install packages
|
|
||||||
(
|
|
||||||
$(is_installed dnsmasq) &&
|
|
||||||
$(is_installed hostapd) &&
|
|
||||||
$(is_installed iptables-persistent)
|
|
||||||
|
|
||||||
) || (
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
apt-get update
|
|
||||||
apt-get install -yq dnsmasq hostapd iptables-persistent
|
|
||||||
)
|
|
||||||
|
|
||||||
configure_dhcpcd > $DHCPCD_CFG
|
|
||||||
configure_dnsmasq > $DNSMASQ_CFG
|
|
||||||
configure_hostapd > $HOSTAPD_CFG
|
|
||||||
|
|
||||||
echo "DAEMON_CONF=\"/etc/hostapd/hostapd.conf\"" > /etc/default/hostapd
|
|
||||||
|
|
||||||
# Enable IP forwarding
|
|
||||||
sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
|
|
||||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
||||||
|
|
||||||
# Enable IP masquerading
|
|
||||||
iptables -t nat -F
|
|
||||||
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
|
||||||
iptables-save > /etc/iptables/rules.v4
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
echo "Usage: config-wifi [OPTIONS]"
|
|
||||||
echo
|
|
||||||
echo "Configure wifi as either a client or access point."
|
|
||||||
echo
|
|
||||||
echo "OPTIONS:"
|
|
||||||
echo
|
|
||||||
echo " -a Configure access point."
|
|
||||||
echo " -d Disable wifi."
|
|
||||||
echo " -r Reboot when done."
|
|
||||||
echo " -s <SSID> Set SSID."
|
|
||||||
echo " -p <PASS> Set password."
|
|
||||||
echo " -c <CHANNEL> Set wifi channel."
|
|
||||||
echo " -j Report wifi config as JSON data."
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Parse args
|
|
||||||
while [ $# -ne 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
-a) AP=true ;;
|
|
||||||
-d) DISABLE=true ;;
|
|
||||||
-r) REBOOT=true; ;;
|
|
||||||
-s) SSID="$2"; shift ;;
|
|
||||||
-p) PASS="$2"; shift ;;
|
|
||||||
-c) CHANNEL="$2"; shift ;;
|
|
||||||
-j) query_config; exit 0 ;;
|
|
||||||
|
|
||||||
-h)
|
|
||||||
usage
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
usage
|
|
||||||
echo "Unknown argument '$1'"
|
|
||||||
exit 1
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
if $DISABLE; then
|
|
||||||
disable_wifi
|
|
||||||
|
|
||||||
else
|
|
||||||
# Check args
|
|
||||||
function clean_str() {
|
|
||||||
echo "$1" | tr -d '\n\r"'
|
|
||||||
}
|
|
||||||
|
|
||||||
SSID=$(clean_str "$SSID")
|
|
||||||
PASS=$(clean_str "$PASS")
|
|
||||||
|
|
||||||
LANG=C LC_ALL=C # For correct string byte length
|
|
||||||
|
|
||||||
if [ ${#SSID} -eq 0 -o 32 -lt ${#SSID} ]; then
|
|
||||||
echo "Invalid or missing SSID '$SSID'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ${#PASS} -ne 0 ]; then
|
|
||||||
if [ ${#PASS} -lt 8 -o 128 -lt ${#PASS} ]; then
|
|
||||||
echo "Invalid passsword"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$CHANNEL" | grep '^[0-9]\{1,2\}' > /dev/null
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Invalid channel '$CHANNEL'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Execute
|
|
||||||
if $AP; then
|
|
||||||
echo "Configuring Wifi access point"
|
|
||||||
configure_ap
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "Configuring Wifi"
|
|
||||||
configure_wifi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if $REBOOT; then nohup reboot & fi
|
|
||||||
42
scripts/create-sdcard-image.js
Executable file
42
scripts/create-sdcard-image.js
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const inquirer = require("inquirer");
|
||||||
|
const { statSync } = require("fs");
|
||||||
|
const { runCommand, initSignalHandlers, assertEffectiveRoot } = require("./util");
|
||||||
|
|
||||||
|
const IMAGE_FILENAME = "onefinity-controller.img";
|
||||||
|
|
||||||
|
initSignalHandlers();
|
||||||
|
main();
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
assertEffectiveRoot();
|
||||||
|
|
||||||
|
const { uid } = statSync(".");
|
||||||
|
|
||||||
|
const devices = runCommand("df -T msdos")
|
||||||
|
.split("\n")
|
||||||
|
.map(line => {
|
||||||
|
const [ disk ] = line.split(/\s+/);
|
||||||
|
return {
|
||||||
|
name: line,
|
||||||
|
value: {
|
||||||
|
disk: disk,
|
||||||
|
device: disk.replace(/s\d$/, "")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const { disk: { disk, device } } = await inquirer.prompt({
|
||||||
|
type: "list",
|
||||||
|
name: "disk",
|
||||||
|
choices: devices,
|
||||||
|
message: `Which device is the sdcard?`
|
||||||
|
});
|
||||||
|
|
||||||
|
runCommand(`diskutil unmount ${disk}`);
|
||||||
|
runCommand(`dd if=${device} of=${IMAGE_FILENAME} status=progress`, {
|
||||||
|
stdio: "inherit"
|
||||||
|
});
|
||||||
|
runCommand(`chown ${uid} 1f.img`);
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/bin/bash -ex
|
|
||||||
|
|
||||||
ROOT="$PWD/demo"
|
|
||||||
|
|
||||||
# Clean up on EXIT
|
|
||||||
function cleanup {
|
|
||||||
umount "$ROOT"/{dev/pts,dev,sys,proc} 2>/dev/null || true
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
# mount binds
|
|
||||||
mount --bind /dev "$ROOT/dev/"
|
|
||||||
mount --bind /sys "$ROOT/sys/"
|
|
||||||
mount --bind /proc "$ROOT/proc/"
|
|
||||||
mount --bind /dev/pts "$ROOT/dev/pts"
|
|
||||||
|
|
||||||
chroot "$ROOT" "$@"
|
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
#!/bin/bash -ex
|
#!/bin/bash -ex
|
||||||
|
|
||||||
pip3 download -d python-packages -r requirements.txt
|
# To use, copy this file, and requirements.txt into a folder on the controller, and run this script.
|
||||||
|
# Then, copy the contents of ./installer/python-packages back to your development environment
|
||||||
|
|
||||||
|
pip3 download -d installer/python-packages -r requirements.txt
|
||||||
|
|||||||
@@ -31,30 +31,20 @@ if [ ! -e $GPLAN_IMG ]; then
|
|||||||
mv $GPLAN_IMG.tmp $GPLAN_IMG
|
mv $GPLAN_IMG.tmp $GPLAN_IMG
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get repos
|
|
||||||
function fetch_local_repo() {
|
|
||||||
mkdir -p $1
|
|
||||||
git -C $1 init
|
|
||||||
git -C $1 fetch -t "$2" $3
|
|
||||||
git -C $1 reset --hard FETCH_HEAD
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir -p rpi-share || true
|
mkdir -p rpi-share || true
|
||||||
|
|
||||||
if [ ! -e rpi-share/cbang ]; then
|
if [ ! -e rpi-share/cbang ]; then
|
||||||
if [ "$CBANG_HOME" != "" ]; then
|
mkdir -p rpi-share/cbang
|
||||||
fetch_local_repo rpi-share/cbang "$CBANG_HOME" master
|
git -C rpi-share/cbang init
|
||||||
else
|
git -C rpi-share/cbang remote add origin https://github.com/CauldronDevelopmentLLC/cbang
|
||||||
git clone https://github.com/CauldronDevelopmentLLC/cbang \
|
git -C rpi-share/cbang fetch --depth 1 origin 18f1e963107ef26abe750c023355a5c40dd07853
|
||||||
rpi-share/cbang
|
git -C rpi-share/cbang reset --hard FETCH_HEAD
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -e rpi-share/camotics ]; then
|
if [ ! -e rpi-share/camotics ]; then
|
||||||
if [ "$CAMOTICS_HOME" != "" ]; then
|
mkdir -p rpi-share/camotics
|
||||||
fetch_local_repo rpi-share/camotics "$CAMOTICS_HOME" master
|
git -C rpi-share/camotics init
|
||||||
else
|
git -C rpi-share/camotics remote add origin https://github.com/CauldronDevelopmentLLC/camotics
|
||||||
git clone https://github.com/CauldronDevelopmentLLC/camotics \
|
git -C rpi-share/camotics fetch --depth 1 origin ec876c80d20fc19837133087cef0c447df5a939d
|
||||||
rpi-share/camotics
|
git -C rpi-share/camotics reset --hard FETCH_HEAD
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,45 +12,46 @@ while [ $# -gt 0 ]; do
|
|||||||
shift 1
|
shift 1
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
if $UPDATE_PY; then
|
if $UPDATE_PY; then
|
||||||
systemctl stop bbctrl
|
systemctl stop bbctrl
|
||||||
|
|
||||||
# Update service
|
# Update service
|
||||||
rm -f /etc/init.d/bbctrl
|
rm -f /etc/init.d/bbctrl
|
||||||
cp scripts/bbctrl.service /etc/systemd/system/
|
cp ./installer/config/bbctrl.service /etc/systemd/system/
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable bbctrl
|
systemctl enable bbctrl
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if $UPDATE_AVR; then
|
if $UPDATE_AVR; then
|
||||||
chmod +x ./scripts/avr109-flash.py
|
chmod +x ./installer/scripts/avr109-flash.py
|
||||||
./scripts/avr109-flash.py src/avr/bbctrl-avr-firmware.hex
|
./installer/scripts/avr109-flash.py src/avr/bbctrl-avr-firmware.hex
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update config.txt
|
# Update config.txt
|
||||||
./scripts/edit-boot-config max_usb_current=1
|
./installer/scripts/edit-boot-config \
|
||||||
./scripts/edit-boot-config config_hdmi_boost=8
|
disable_overscan=1 \
|
||||||
|
framebuffer_width=1280 \
|
||||||
|
framebuffer_height=720 \
|
||||||
|
nohz=on \
|
||||||
|
dtparam=sd_overclock=100 \
|
||||||
|
max_usb_current=1 \
|
||||||
|
config_hdmi_boost=8 \
|
||||||
|
disable_splash=1 \
|
||||||
|
hdmi_force_hotplug=1 \
|
||||||
|
hdmi_group=2 \
|
||||||
|
hdmi_mode=82
|
||||||
|
|
||||||
# TODO Enable GPU
|
# TODO Enable GPU
|
||||||
#./scripts/edit-boot-config dtoverlay=vc4-kms-v3d
|
#./installer/scripts/edit-boot-config \
|
||||||
#./scripts/edit-boot-config gpu_mem=16
|
# dtoverlay=vc4-kms-v3d \
|
||||||
|
# gpu_mem=16
|
||||||
#chmod ug+s /usr/lib/xorg/Xorg
|
#chmod ug+s /usr/lib/xorg/Xorg
|
||||||
|
|
||||||
# Use the full screen resolution
|
|
||||||
# grep "^framebuffer_width=1280$" /boot/config.txt >/dev/null
|
|
||||||
# if [ $? -eq 0 ]; then
|
|
||||||
# mount -o remount,rw /boot &&
|
|
||||||
# sed -i 's/^\(framebuffer_.*\)$/#\1/g' /boot/config.txt
|
|
||||||
# mount -o remount,ro /boot
|
|
||||||
# REBOOT=true
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# Fix camera
|
# Fix camera
|
||||||
grep dwc_otg.fiq_fsm_mask /boot/cmdline.txt >/dev/null
|
grep dwc_otg.fiq_fsm_mask /boot/cmdline.txt >/dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
mount -o remount,rw /boot &&
|
mount -o remount,rw /boot &&
|
||||||
sed -i 's/\(.*\)/\1 dwc_otg.fiq_fsm_mask=0x3/' /boot/cmdline.txt
|
sed -i -E 's/(.*)/\1 dwc_otg.fiq_fsm_mask=0x3/' /boot/cmdline.txt
|
||||||
mount -o remount,ro /boot
|
mount -o remount,ro /boot
|
||||||
REBOOT=true
|
REBOOT=true
|
||||||
fi
|
fi
|
||||||
@@ -59,55 +60,92 @@ fi
|
|||||||
grep cgroup_memory /boot/cmdline.txt >/dev/null
|
grep cgroup_memory /boot/cmdline.txt >/dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
mount -o remount,rw /boot &&
|
mount -o remount,rw /boot &&
|
||||||
sed -i 's/\(.*\)/\1 cgroup_memory=1/' /boot/cmdline.txt
|
sed -i -E 's/(.*)/\1 cgroup_memory=1/' /boot/cmdline.txt
|
||||||
mount -o remount,ro /boot
|
mount -o remount,ro /boot
|
||||||
REBOOT=true
|
REBOOT=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove Hawkeye
|
# Remove Hawkeye
|
||||||
if [ -e /etc/init.d/hawkeye ]; then
|
if [ -e /etc/init.d/hawkeye ]; then
|
||||||
apt-get remove --purge -y hawkeye
|
apt-get purge -y hawkeye
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Decrease boot delay
|
# Decrease boot delay
|
||||||
sed -i 's/^TimeoutStartSec=.*$/TimeoutStartSec=1/' \
|
sed -i -E 's/^TimeoutStartSec=.*$/TimeoutStartSec=1/' \
|
||||||
/etc/systemd/system/network-online.target.wants/networking.service
|
/etc/systemd/system/network-online.target.wants/networking.service
|
||||||
|
|
||||||
# Change to US keyboard layout
|
# Change to US keyboard layout
|
||||||
sed -i 's/^XKBLAYOUT="gb"$/XKBLAYOUT="us" # Comment stops change on upgrade/' \
|
sed -i -E 's/^XKBLAYOUT="gb"$/XKBLAYOUT="us" # Comment stops change on upgrade/' /etc/default/keyboard
|
||||||
/etc/default/keyboard
|
|
||||||
|
# Set the default locale to en_US
|
||||||
|
grep '^en_US.UTF-8 UTF-8' /etc/locale.gen >/dev/null
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
perl -pi -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' /etc/locale.gen
|
||||||
|
locale-gen en_US.UTF-8
|
||||||
|
update-locale en_US.UTF-8
|
||||||
|
fi
|
||||||
|
|
||||||
# Setup USB stick automount
|
# Setup USB stick automount
|
||||||
diff ./scripts/11-automount.rules /etc/udev/rules.d/11-automount.rules \
|
diff ./installer/config/11-automount.rules /etc/udev/rules.d/11-automount.rules >/dev/null
|
||||||
>/dev/null
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
cp ./scripts/11-automount.rules /etc/udev/rules.d/
|
cp ./installer/config/11-automount.rules /etc/udev/rules.d/
|
||||||
sed -i 's/^\(MountFlags=slave\)/#\1/' \
|
sed -i -E 's/^(MountFlags=slave)/#\1/' \
|
||||||
/lib/systemd/system/systemd-udevd.service
|
/lib/systemd/system/systemd-udevd.service
|
||||||
REBOOT=true
|
REBOOT=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase swap
|
# Disable disk-based swap
|
||||||
grep 'CONF_SWAPSIZE=1000' /etc/dphys-swapfile >/dev/null
|
if [ -e /etc/dphys-swapfile ]; then
|
||||||
if [ $? -ne 0 ]; then
|
apt-get purge -y dphys-swapfile
|
||||||
sed -i 's/^CONF_SWAPSIZE=.*$/CONF_SWAPSIZE=1000/' /etc/dphys-swapfile
|
rm -f /var/swap
|
||||||
REBOOT=true
|
fi
|
||||||
|
|
||||||
|
# Enable zram swap
|
||||||
|
# See https://github.com/ecdye/zram-config
|
||||||
|
# The zram-config-main.zip is a downloaded snapshot of the git repository
|
||||||
|
if [ ! -e /usr/local/sbin/zram-config ]; then
|
||||||
|
modprobe -a lz4 zram
|
||||||
|
|
||||||
|
unzip ./installer/linux-packages/zram-config-main.zip -d /tmp
|
||||||
|
/tmp/zram-config-main/install.bash
|
||||||
|
|
||||||
|
sed -i -E 's/^(swap\s+)(lzo-rle)(.*)$/\1lz4\3/' /etc/ztab
|
||||||
|
sed -i -E 's/^(swap.*)150\s*$/\1100/' /etc/ztab
|
||||||
|
sed -i -E 's/^(log.*)$/#\1/' /etc/ztab
|
||||||
|
|
||||||
|
sed -i -E 's/^Type=exec$/Type=simple/' /etc/systemd/system/zram-config.service
|
||||||
|
|
||||||
|
systemctl restart zram-config
|
||||||
|
|
||||||
|
rm -rf /tmp/zram-config
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install .Xresources & .xinitrc
|
# Install .Xresources & .xinitrc
|
||||||
cp scripts/Xresources ~pi/.Xresources
|
cp ./installer/config/Xresources ~pi/.Xresources
|
||||||
chown pi:pi ~pi/.Xresources
|
chown pi:pi ~pi/.Xresources
|
||||||
cp scripts/xinitrc ~pi/.xinitrc
|
cp ./installer/config/xinitrc ~pi/.xinitrc
|
||||||
chmod +x ~pi/.xinitrc
|
chmod +x ~pi/.xinitrc
|
||||||
chown pi:pi ~pi/.xinitrc
|
chown pi:pi ~pi/.xinitrc
|
||||||
|
|
||||||
#Configure the "ratpoison" window manager
|
# Configure the "ratpoison" window manager
|
||||||
if [ ! -e ~pi/.ratpoisonrc ]; then
|
if [ ! -e ~pi/.ratpoisonrc ]; then
|
||||||
cp scripts/ratpoisonrc ~pi/.ratpoisonrc
|
cp ./installer/config/ratpoisonrc ~pi/.ratpoisonrc
|
||||||
chmod 644 ~pi/.ratpoisonrc
|
chmod 644 ~pi/.ratpoisonrc
|
||||||
chown pi:pi ~pi/.ratpoisonrc
|
chown pi:pi ~pi/.ratpoisonrc
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Configure the Plymouth graphical bootloader with the Onefinity theme
|
||||||
|
rm -rf /usr/share/plymouth/themes/buildbotics
|
||||||
|
rm -rf /usr/share/plymouth/themes/onefinity
|
||||||
|
mkdir -p /usr/share/plymouth/themes/onefinity/
|
||||||
|
cp -av ./installer/splash/* /usr/share/plymouth/themes/onefinity/
|
||||||
|
plymouth-set-default-theme -R onefinity
|
||||||
|
|
||||||
|
grep 'quiet splash plymouth.ignore-serial-consoles logo.nologo' /boot/cmdline.txt >/dev/null
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo -n " quiet splash plymouth.ignore-serial-consoles logo.nologo" >> /boot/cmdline.txt
|
||||||
|
fi
|
||||||
|
|
||||||
# Install bbserial
|
# Install bbserial
|
||||||
MODSRC=src/bbserial/bbserial.ko
|
MODSRC=src/bbserial/bbserial.ko
|
||||||
MODDST=/lib/modules/$(uname -r)/kernel/drivers/tty/serial/bbserial.ko
|
MODDST=/lib/modules/$(uname -r)/kernel/drivers/tty/serial/bbserial.ko
|
||||||
@@ -119,46 +157,51 @@ if [ $? -ne 0 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Install rc.local
|
# Install rc.local
|
||||||
cp scripts/rc.local /etc/
|
cp ./installer/config/rc.local /etc/
|
||||||
|
|
||||||
# Install bbctrl
|
# Install bbctrl
|
||||||
if $UPDATE_PY; then
|
if $UPDATE_PY; then
|
||||||
service bbctrl stop
|
systemctl stop bbctrl
|
||||||
|
|
||||||
rm -rf /usr/local/lib/python*/dist-packages/bbctrl-*
|
rm -rf /usr/local/lib/python*/dist-packages/bbctrl-*
|
||||||
|
|
||||||
# Ensure python dependencies are installed
|
# Ensure python dependencies are installed
|
||||||
pip3 install --no-index --find-links python-packages -r requirements.txt
|
pip3 install --no-index --find-links installer/python-packages -r requirements.txt
|
||||||
|
|
||||||
./setup.py install --force
|
./setup.py install --force
|
||||||
|
|
||||||
HTTP_DIR=$(find /usr/local/lib/ -type d -name "http")
|
HTTP_DIR=$(find /usr/local/lib/ -type d -name "http")
|
||||||
chmod 777 $HTTP_DIR
|
chmod 777 $HTTP_DIR
|
||||||
|
|
||||||
service bbctrl restart
|
systemctl restart bbctrl
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Install the service that turns off the screen during shutdown
|
||||||
|
cp ./installer/config/bbctrl-poweroff.service /etc/systemd/system/
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable bbctrl-poweroff
|
||||||
|
|
||||||
# Expand the file system if necessary
|
# Expand the file system if necessary
|
||||||
chmod +x ./scripts/resize_root_fs.sh
|
chmod +x ./installer/scripts/resize_root_fs.sh
|
||||||
./scripts/resize_root_fs.sh
|
./installer/scripts/resize_root_fs.sh
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
REBOOT=true
|
REBOOT=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install our logrotate config
|
# Install our logrotate config
|
||||||
cp ./scripts/bbctrl-logrotate /etc/logrotate.d/bbctrl
|
cp ./installer/config/bbctrl-logrotate /etc/logrotate.d/bbctrl
|
||||||
chown root:root /etc/logrotate.d/bbctrl
|
chown root:root /etc/logrotate.d/bbctrl
|
||||||
|
|
||||||
# Ensure logrotate runs on every boot (for systems with no network, thus bad clock)
|
# Ensure logrotate runs on every boot (for systems with no network, thus bad clock)
|
||||||
if [ ! -e /etc/cron.d/reboot ]; then
|
if [ ! -e /etc/cron.d/reboot ]; then
|
||||||
cp ./scripts/cron_d_reboot /etc/cron.d/reboot
|
cp ./installer/config/cron_d_reboot /etc/cron.d/reboot
|
||||||
mkdir -p /etc/cron.reboot
|
mkdir -p /etc/cron.reboot
|
||||||
cp ./scripts/cron_reboot_logrotate /etc/cron.reboot/logrotate
|
cp ./installer/config/cron_reboot_logrotate /etc/cron.reboot/logrotate
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Delete some cookies that were left behind in older images
|
# Delete some cookies that were left behind in older images
|
||||||
chmod +x ./scripts/delete-cookies.py
|
chmod +x ./installer/scripts/delete-cookies.py
|
||||||
./scripts/delete-cookies.py
|
./installer/scripts/delete-cookies.py
|
||||||
pkill -HUP chromium # Force Chromium to restart, to see the cookie changes
|
pkill -HUP chromium # Force Chromium to restart, to see the cookie changes
|
||||||
|
|
||||||
# Get rid of some old files that were left behind in older images
|
# Get rid of some old files that were left behind in older images
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import json
|
|
||||||
import sys
|
|
||||||
|
|
||||||
p = [0, 0, 0]
|
|
||||||
|
|
||||||
|
|
||||||
print('F400 G21')
|
|
||||||
|
|
||||||
for line in sys.stdin:
|
|
||||||
try:
|
|
||||||
if not line.startswith('I:Comm:> '): continue
|
|
||||||
line = line[9:]
|
|
||||||
|
|
||||||
data = json.loads(line)
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
for axis in range(3):
|
|
||||||
var = 'xyz'[axis] + 'p'
|
|
||||||
if var in data:
|
|
||||||
p[axis] = data[var]
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if changed: print('G1 X%d Y%d Z%d' % tuple(p))
|
|
||||||
|
|
||||||
except json.decoder.JSONDecodeError: pass
|
|
||||||
66
scripts/prep-controller-for-imaging.js
Executable file
66
scripts/prep-controller-for-imaging.js
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const inquirer = require("inquirer");
|
||||||
|
const { runCommand, logErrorAndExit, initSignalHandlers, assertInstalled, info } = require("./util");
|
||||||
|
|
||||||
|
const PACKAGES_TO_PURGE = [
|
||||||
|
"dphys-swapfile",
|
||||||
|
"gdb",
|
||||||
|
"geoip-database",
|
||||||
|
"gnome-icon-theme",
|
||||||
|
"hostapd",
|
||||||
|
"libfreetype6-dev",
|
||||||
|
"libglib2.0-data",
|
||||||
|
"libraspberrypi-doc",
|
||||||
|
"mlocate",
|
||||||
|
"triggerhappy",
|
||||||
|
"zip",
|
||||||
|
];
|
||||||
|
|
||||||
|
let controller;
|
||||||
|
|
||||||
|
initSignalHandlers();
|
||||||
|
main();
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
controller = process.argv[2] ?? "onefinity";
|
||||||
|
|
||||||
|
assertInstalled([ "sshpass", "ssh" ]);
|
||||||
|
|
||||||
|
const { password } = await inquirer.prompt({
|
||||||
|
type: "password",
|
||||||
|
name: "password",
|
||||||
|
message: `What is the password for ${controller}?`
|
||||||
|
});
|
||||||
|
|
||||||
|
process.env.SSHPASS = password;
|
||||||
|
|
||||||
|
ssh("echo sudo access confirmed", {
|
||||||
|
onError: () => {
|
||||||
|
logErrorAndExit([
|
||||||
|
"You must configure the 'bbmc` user for no-password sudo.",
|
||||||
|
"The secret is 'NOPASSWD:ALL'",
|
||||||
|
"See: https://www.cyberciti.biz/faq/linux-unix-running-sudo-command-without-a-password/"
|
||||||
|
].join("\n"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ssh("apt-get update");
|
||||||
|
ssh(`apt-get purge -y ${PACKAGES_TO_PURGE.join(" ")}`);
|
||||||
|
ssh("apt-get autoremove -y");
|
||||||
|
ssh("touch /root/.prep-controller-completed");
|
||||||
|
ssh("sed -i -E 's|NOPASSWD:ALL|ALL|' /etc/sudoers");
|
||||||
|
} catch (error) {
|
||||||
|
logErrorAndExit("An unexpected error occurred", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ssh(command, options) {
|
||||||
|
info(`Running "${command}"`);
|
||||||
|
|
||||||
|
return runCommand(`sshpass -e /usr/bin/ssh ${controller} "sudo ${command}"`, {
|
||||||
|
...options,
|
||||||
|
stdio: "inherit"
|
||||||
|
});
|
||||||
|
}
|
||||||
479
scripts/prep-sd-image.js
Executable file
479
scripts/prep-sd-image.js
Executable file
@@ -0,0 +1,479 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const inquirer = require("inquirer");
|
||||||
|
const merge = require("lodash.merge");
|
||||||
|
const { resolve } = require("path");
|
||||||
|
const {
|
||||||
|
statSync,
|
||||||
|
rmdirSync,
|
||||||
|
copyFileSync,
|
||||||
|
writeFileSync,
|
||||||
|
readFileSync,
|
||||||
|
existsSync,
|
||||||
|
rmSync
|
||||||
|
} = require("fs");
|
||||||
|
const { exit } = require("process");
|
||||||
|
const { glob } = require("glob");
|
||||||
|
const packageJSON = require("../package.json");
|
||||||
|
const config_defaults = require("../src/resources/onefinity_defaults.json");
|
||||||
|
const {
|
||||||
|
info,
|
||||||
|
runCommand,
|
||||||
|
logErrorAndExit,
|
||||||
|
assertOS,
|
||||||
|
assertEffectiveRoot,
|
||||||
|
assertFileExists,
|
||||||
|
assertInstalled,
|
||||||
|
initSignalHandlers,
|
||||||
|
doFinally
|
||||||
|
} = require("./util");
|
||||||
|
|
||||||
|
const variant_defaults = {
|
||||||
|
machinist_x35: require("../src/resources/onefinity_machinist_x35_defaults.json"),
|
||||||
|
woodworker_x35: require("../src/resources/onefinity_woodworker_x35_defaults.json"),
|
||||||
|
woodworker_x50: require("../src/resources/onefinity_woodworker_x50_defaults.json"),
|
||||||
|
journeyman_x50: require("../src/resources/onefinity_journeyman_x50_defaults.json")
|
||||||
|
};
|
||||||
|
|
||||||
|
const ORIGINAL_IMAGE_FILENAME = "onefinity-controller.img";
|
||||||
|
|
||||||
|
const REQUIRED_TOOLS = [
|
||||||
|
"rsync",
|
||||||
|
"parted",
|
||||||
|
"losetup",
|
||||||
|
"tune2fs",
|
||||||
|
"md5sum",
|
||||||
|
"e2fsck",
|
||||||
|
"resize2fs",
|
||||||
|
"xz",
|
||||||
|
"zerofree"
|
||||||
|
];
|
||||||
|
|
||||||
|
const SYSTEM_FILES = [
|
||||||
|
"/tmp/*",
|
||||||
|
"/usr/**/__pycache__",
|
||||||
|
"/usr/**/*.py[co]",
|
||||||
|
"/usr/share/doc/*",
|
||||||
|
"/var/@(cache|backups|log|tmp)/*",
|
||||||
|
"/var/lib/apt/lists/*",
|
||||||
|
"/var/lib/bbctrl/@(config|gamepads).json",
|
||||||
|
"/var/lib/bbctrl/@(firmware|plans|upload)/*",
|
||||||
|
"/var/lib/dhcpcd5/*",
|
||||||
|
"/var/swap",
|
||||||
|
];
|
||||||
|
|
||||||
|
const USER_FILES = [
|
||||||
|
".bash_history",
|
||||||
|
".cache",
|
||||||
|
".config",
|
||||||
|
".lesshst",
|
||||||
|
".local",
|
||||||
|
".nano",
|
||||||
|
".pki",
|
||||||
|
".prep-controller-completed",
|
||||||
|
".ratpoison_history",
|
||||||
|
".viminfo",
|
||||||
|
".wget-hsts",
|
||||||
|
".Xauthority",
|
||||||
|
"Downloads",
|
||||||
|
"splash.png"
|
||||||
|
];
|
||||||
|
|
||||||
|
initSignalHandlers();
|
||||||
|
main();
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let meta;
|
||||||
|
|
||||||
|
await doFinally(async () => {
|
||||||
|
assertOS();
|
||||||
|
assertEffectiveRoot();
|
||||||
|
assertFileExists(ORIGINAL_IMAGE_FILENAME);
|
||||||
|
assertInstalled(REQUIRED_TOOLS);
|
||||||
|
|
||||||
|
const meta = prepareImage();
|
||||||
|
|
||||||
|
await attachToLoopback(meta, "root", async (loopback) => {
|
||||||
|
checkAndRepair(loopback);
|
||||||
|
await prepareFilesystem(loopback);
|
||||||
|
});
|
||||||
|
|
||||||
|
await attachToLoopback(meta, "root", (loopback) => {
|
||||||
|
checkAndRepair(loopback);
|
||||||
|
shrinkFilesystem(loopback);
|
||||||
|
shrinkPartition(loopback, meta);
|
||||||
|
zerofree(loopback);
|
||||||
|
});
|
||||||
|
|
||||||
|
truncateImage(meta);
|
||||||
|
await configureAutoExpand(meta);
|
||||||
|
compress(meta);
|
||||||
|
moveImageFiles(meta);
|
||||||
|
}, () => {
|
||||||
|
if (meta) {
|
||||||
|
if (existsSync(meta.imageFilePath)) {
|
||||||
|
rmSync(meta.imageFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsSync(meta.compressedImageFilePath)) {
|
||||||
|
rmSync(meta.compressedImageFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createImageFileCopy() {
|
||||||
|
const target = runCommand("mktemp --tmpdir --suffix=.img onefinity-raspi-XXXXXXXXXX");
|
||||||
|
|
||||||
|
info(`Copying ${ORIGINAL_IMAGE_FILENAME} to ${target}...`);
|
||||||
|
|
||||||
|
runCommand(`rsync --times --progress ${ORIGINAL_IMAGE_FILENAME} ${target}`, {
|
||||||
|
stdio: "inherit",
|
||||||
|
onError: error => {
|
||||||
|
logErrorAndExit(`Failed to copy ${ORIGINAL_IMAGE_FILENAME} to ${target}`, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function attachToLoopback(meta, partition, cb) {
|
||||||
|
info("Attaching the image to a loopback device...");
|
||||||
|
|
||||||
|
let loopback;
|
||||||
|
await doFinally(
|
||||||
|
async () => {
|
||||||
|
const start = meta.partitions[partition].start;
|
||||||
|
loopback = runCommand(`losetup -f --show -o "${start}" "${meta.imageFilePath}"`);
|
||||||
|
|
||||||
|
await cb(loopback);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (loopback) {
|
||||||
|
runCommand(`losetup -d ${loopback}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareImage() {
|
||||||
|
const imageFilePath = createImageFileCopy();
|
||||||
|
|
||||||
|
info("Gathering info about the image...");
|
||||||
|
const { size: initialImageSize } = statSync(imageFilePath);
|
||||||
|
|
||||||
|
const partedOutput = runCommand(`parted -s "${imageFilePath}" unit B print`, {
|
||||||
|
onError: error => {
|
||||||
|
logErrorAndExit(`
|
||||||
|
Error fetching disk image info.
|
||||||
|
|
||||||
|
'parted' failed with exitcode ${error.status}
|
||||||
|
|
||||||
|
Run 'parted ${imageFilePath} unit B print' manually to investigate
|
||||||
|
`, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [ boot, root ] = partedOutput
|
||||||
|
.split("\n")
|
||||||
|
.slice(-2)
|
||||||
|
.map(line => line
|
||||||
|
.trim()
|
||||||
|
.split(/\s+/)
|
||||||
|
.map(col => parseInt(col) || col)
|
||||||
|
)
|
||||||
|
.map(columns => ({
|
||||||
|
number: columns[0],
|
||||||
|
start: columns[1],
|
||||||
|
end: columns[2],
|
||||||
|
size: columns[3],
|
||||||
|
type: columns[4],
|
||||||
|
filesystem: columns[5],
|
||||||
|
flags: columns[6]
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
initialImageSize,
|
||||||
|
imageFilePath,
|
||||||
|
compressedImageFilePath: getCompressedFilename(imageFilePath),
|
||||||
|
partitions: {
|
||||||
|
boot,
|
||||||
|
root
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAndRepair(loopback) {
|
||||||
|
info("Checking the filesystem...");
|
||||||
|
|
||||||
|
let success = true;
|
||||||
|
|
||||||
|
runCommand(`e2fsck -pf "${loopback}"`, {
|
||||||
|
stdio: "inherit",
|
||||||
|
onError: error => {
|
||||||
|
success = error.status < 4;
|
||||||
|
if (error.status >= 4) {
|
||||||
|
info(`First e2fsck returned '${error.status}'.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
info("Trying harder to fix the image");
|
||||||
|
runCommand(`e2fsck -y "${loopback}"`, {
|
||||||
|
stdio: "inherit",
|
||||||
|
onError: error => {
|
||||||
|
success = error.status < 4;
|
||||||
|
if (error.status >= 4) {
|
||||||
|
info(`Second e2fsck returned '${error.status}'.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
info("The filesystem must be pretty damaged. Trying again with the alternate superblock.");
|
||||||
|
runCommand(`e2fsck -yf -b 32768 "${loopback}"`, {
|
||||||
|
stdio: "inherit",
|
||||||
|
onError: error => {
|
||||||
|
if (error.status >= 4) {
|
||||||
|
logErrorAndExit(`The final e2fsck attempt returned '${error.status}'. Giving up.`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function prepareFilesystem(loopback) {
|
||||||
|
await mountLoopback(loopback, async mountpoint => {
|
||||||
|
info("Removing unnecessary files from the filesystem...");
|
||||||
|
|
||||||
|
if (!existsSync(`${mountpoint}/root/.prep-controller-completed`)) {
|
||||||
|
const { proceed } = await inquirer.prompt({
|
||||||
|
type: "confirm",
|
||||||
|
name: "proceed",
|
||||||
|
message: [
|
||||||
|
"It looks like 'prep-controller-for-imaging.js' has not been run on this image.",
|
||||||
|
"Do you want to proceed anyway?"
|
||||||
|
].join("\n")
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!proceed) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrubFiles(mountpoint, SYSTEM_FILES);
|
||||||
|
scrubUserFiles(mountpoint, "/root");
|
||||||
|
scrubUserFiles(mountpoint, "/home/bbmc");
|
||||||
|
scrubUserFiles(mountpoint, "/home/pi");
|
||||||
|
|
||||||
|
info("Injecting files...");
|
||||||
|
|
||||||
|
copyFileSync(
|
||||||
|
resolve(`${__dirname}/../installer/gcode/Team Onefinity.ngc`),
|
||||||
|
resolve(`${mountpoint}/var/lib/bbctrl/upload/Team Onefinity.ngc`)
|
||||||
|
);
|
||||||
|
|
||||||
|
writeFileSync(`${mountpoint}/var/lib/bbctrl/config.json`,
|
||||||
|
JSON.stringify(merge(
|
||||||
|
{},
|
||||||
|
config_defaults,
|
||||||
|
variant_defaults.woodworker_x35
|
||||||
|
), null, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
const virtualKeyboardZip = resolve(`${__dirname}/../installer/linux-packages/virtualKeyboard.zip`);
|
||||||
|
const userPiHome = resolve(`${mountpoint}/home/pi`);
|
||||||
|
runCommand(`unzip "${virtualKeyboardZip}" -d "${userPiHome}"`);
|
||||||
|
|
||||||
|
runCommand(`chown -R 1000:1000 ${userPiHome}/.config`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function shrinkFilesystem(loopback) {
|
||||||
|
info(`Shrinking the root filesystem`);
|
||||||
|
|
||||||
|
// We run the shrink step multiple times, because
|
||||||
|
// each time, resize2fs can shrink it a little more,
|
||||||
|
// until eventually it can't.
|
||||||
|
//
|
||||||
|
// TODO: Switch to using pipes to both display the output and capture it
|
||||||
|
// We can then look at the output to determine when to stop, rather than
|
||||||
|
// using a fixed count for loop.
|
||||||
|
// See: https://stackoverflow.com/questions/22337446/how-to-wait-for-a-child-process-to-finish-in-node-js
|
||||||
|
for (let i = 0; i < 5; ++i) {
|
||||||
|
runCommand(`resize2fs -p "${loopback}" -M`, {
|
||||||
|
stdio: "inherit",
|
||||||
|
onError: error => {
|
||||||
|
logErrorAndExit("Error while resizing", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shrinkPartition(loopback, meta) {
|
||||||
|
info(`Shrinking the root partition`);
|
||||||
|
|
||||||
|
const tune2fsOutput = runCommand(`tune2fs -l "${loopback}"`, {
|
||||||
|
onError: error => logErrorAndExit("tune2fs failed. Unable to shrink this type of image", error)
|
||||||
|
});
|
||||||
|
|
||||||
|
const root = meta.partitions.root;
|
||||||
|
const [ , currentSize ] = tune2fsOutput.match(/^Block count:\s+(\d+)/m);
|
||||||
|
const [ , blockSize ] = tune2fsOutput.match(/^Block size:\s+(\d+)/m);
|
||||||
|
const newSize = parseInt(currentSize) * parseInt(blockSize);
|
||||||
|
const newEnd = root.start + newSize;
|
||||||
|
|
||||||
|
runCommand(`parted -s -a minimal "${meta.imageFilePath}" rm "${root.number}"`, {
|
||||||
|
onError: error => logErrorAndExit("parted failed while deleting root partition", error)
|
||||||
|
});
|
||||||
|
|
||||||
|
runCommand(`parted -s "${meta.imageFilePath}" unit B mkpart "${root.type}" "${root.start}" "${newEnd}"`, {
|
||||||
|
onError: error => logErrorAndExit("parted failed while recreating the root partition", error)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function zerofree(loopback) {
|
||||||
|
info(`Zeroing out empty blocks for better compression...`);
|
||||||
|
info("(This will take a bit - it can look like it's hung, have patience)");
|
||||||
|
|
||||||
|
runCommand(`zerofree -v "${loopback}"`, {
|
||||||
|
stdio: "inherit"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncateImage(meta) {
|
||||||
|
info(`Shrinking the image`);
|
||||||
|
|
||||||
|
const partedOutput = runCommand(`parted -sm "${meta.imageFilePath}" unit B print free`, {
|
||||||
|
onError: error => logErrorAndExit("parted failed while shrinking the image", error)
|
||||||
|
});
|
||||||
|
|
||||||
|
// The output of the parted command above will look something like this:
|
||||||
|
//
|
||||||
|
// BYT;
|
||||||
|
// image.img:31914983424B:file:512:512:msdos::;
|
||||||
|
// 1:16384B:1048575B:1032192B:free;
|
||||||
|
// 1:1048576B:135266303B:134217728B:fat32::boot;
|
||||||
|
// 2:135266304B:2002096639B:1866830336B:ext4::;
|
||||||
|
// 1:2002096640B:31914983423B:29912886784B:free;
|
||||||
|
//
|
||||||
|
// The format is:
|
||||||
|
// "number":"begin":"end":"size":"filesystem-type":"partition-name":"flags-set";
|
||||||
|
//
|
||||||
|
// We're interested in the last line only, to determine
|
||||||
|
// the start of the free space in the image, after the partitions
|
||||||
|
|
||||||
|
const [ , startOfFreeSpace, , , type ] = partedOutput
|
||||||
|
.split("\n")
|
||||||
|
.at(-1)
|
||||||
|
.replace(/^([^;]+);.*$/, "$1")
|
||||||
|
.split(":")
|
||||||
|
.map(col => parseInt(col) || col);
|
||||||
|
|
||||||
|
if (type !== "free") {
|
||||||
|
info("There is no free space after the root partition, skipping image shrinking.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runCommand(`truncate -s "${startOfFreeSpace}" "${meta.imageFilePath}"`);
|
||||||
|
|
||||||
|
const { size: newSize } = statSync(meta.imageFilePath);
|
||||||
|
info(`Shrank the image from ${meta.initialImageSize} to ${newSize}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function configureAutoExpand(meta) {
|
||||||
|
info("Configuring the root partition to autoexpand on first boot...");
|
||||||
|
|
||||||
|
await attachToLoopback(meta, "boot", async (loopback) => {
|
||||||
|
await mountLoopback(loopback, mountpoint => {
|
||||||
|
let cmdline = readFileSync(`${mountpoint}/cmdline.txt`, { encoding: "utf8" });
|
||||||
|
if (cmdline.match(/init_resize/)) {
|
||||||
|
logErrorAndExit("init_resize is already in /boot/cmdline.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdline = `${cmdline.trim()} init=/usr/lib/raspi-config/init_resize.sh`;
|
||||||
|
writeFileSync(`${mountpoint}/cmdline.txt`, cmdline, { encoding: "utf8" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function compress(meta) {
|
||||||
|
info(`Compressing the image`);
|
||||||
|
|
||||||
|
runCommand(`xz -k9veT0 ${meta.imageFilePath}`, {
|
||||||
|
stdio: "inherit"
|
||||||
|
});
|
||||||
|
|
||||||
|
const { size: oldSize } = statSync(meta.imageFilePath);
|
||||||
|
const compressed = getCompressedFilename(meta.imageFilePath);
|
||||||
|
|
||||||
|
const { size: newSize } = statSync(compressed);
|
||||||
|
info(`Compressed the image from ${oldSize} to ${newSize}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveImageFiles(meta) {
|
||||||
|
info("Finalizing...");
|
||||||
|
|
||||||
|
const finalImageName = `onefinity-raspi-${packageJSON.version}.img`;
|
||||||
|
const finalCompressedImageName = getCompressedFilename(finalImageName);
|
||||||
|
runCommand(`mv ${meta.imageFilePath} ${finalImageName}`);
|
||||||
|
runCommand(`mv ${meta.compressedImageFilePath} ${finalCompressedImageName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCompressedFilename(target) {
|
||||||
|
return `${target}.xz`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mountLoopback(loopback, cb) {
|
||||||
|
let mountpoint;
|
||||||
|
|
||||||
|
await doFinally(async () => {
|
||||||
|
mountpoint = runCommand("mktemp --tmpdir -d onefinity-raspi-root-XXXXXXXXXX");
|
||||||
|
|
||||||
|
runCommand(`mount ${loopback} ${mountpoint}`);
|
||||||
|
await cb(mountpoint);
|
||||||
|
}, () => {
|
||||||
|
if (mountpoint) {
|
||||||
|
runCommand(`umount "${mountpoint}"`);
|
||||||
|
rmdirSync(mountpoint);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrubFiles(mountpoint, patterns) {
|
||||||
|
for (const _pattern of patterns) {
|
||||||
|
const { pattern, ignore } = (typeof _pattern === "string")
|
||||||
|
? { pattern: _pattern }
|
||||||
|
: _pattern;
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
dot: true,
|
||||||
|
cwd: mountpoint,
|
||||||
|
root: mountpoint,
|
||||||
|
ignore
|
||||||
|
};
|
||||||
|
|
||||||
|
const matches = glob.sync(pattern, options);
|
||||||
|
|
||||||
|
for (const match of matches) {
|
||||||
|
runCommand(`rm -rvf "${match}"`, {
|
||||||
|
stdio: "inherit"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrubUserFiles(mountpoint, homedir) {
|
||||||
|
scrubFiles(mountpoint, USER_FILES.map(item => {
|
||||||
|
if (typeof item === "string") {
|
||||||
|
return `${homedir}/${item}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
pattern: `${homedir}/${item.pattern}`,
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
import os.path
|
|
||||||
import time
|
|
||||||
|
|
||||||
if not os.path.exists('/dev/video0'):
|
|
||||||
print('/dev/video0 not found')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
p = subprocess.Popen('udevadm info -q path /dev/video0'.split(),
|
|
||||||
stdout = subprocess.PIPE)
|
|
||||||
s = p.communicate()[0].decode('utf-8')
|
|
||||||
dev = s.split('/')[7]
|
|
||||||
|
|
||||||
with open('/sys/bus/usb/drivers/usb/unbind', 'w') as f:
|
|
||||||
f.write(dev)
|
|
||||||
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
with open('/sys/bus/usb/drivers/usb/bind', 'w') as f:
|
|
||||||
f.write(dev)
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
### BEGIN INIT INFO
|
|
||||||
# Provides: resize2fs_once
|
|
||||||
# Required-Start:
|
|
||||||
# Required-Stop:
|
|
||||||
# Default-Start: 3
|
|
||||||
# Default-Stop:
|
|
||||||
# Short-Description: Resize the root filesystem to fill partition
|
|
||||||
# Description:
|
|
||||||
### END INIT INFO
|
|
||||||
. /lib/lsb/init-functions
|
|
||||||
case "$1" in
|
|
||||||
start)
|
|
||||||
log_daemon_msg "Starting resize2fs_once"
|
|
||||||
ROOT_DEV=$(findmnt / -o source -n) &&
|
|
||||||
resize2fs $ROOT_DEV &&
|
|
||||||
update-rc.d resize2fs_once remove &&
|
|
||||||
rm /etc/init.d/resize2fs_once &&
|
|
||||||
log_end_msg $?
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: $0 start" >&2
|
|
||||||
exit 3
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -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/"
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
USER=bbmc
|
|
||||||
HOST=bbctrl.local
|
|
||||||
|
|
||||||
if [ $# -eq 1 ]; then
|
|
||||||
if [[ "$1" = *@ ]]; then
|
|
||||||
LOGIN="$1"
|
|
||||||
else
|
|
||||||
LOGIN=$USER@"$1"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
LOGIN=$USER@$HOST
|
|
||||||
fi
|
|
||||||
|
|
||||||
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$LOGIN"
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
function convertToAbsolute(path) {
|
|
||||||
var x0, y0, x1, y1, x2, y2, segs = path.pathSegList;
|
|
||||||
|
|
||||||
for (var x = 0, y = 0, i = 0, len = segs.numberOfItems; i < len; i++) {
|
|
||||||
var seg = segs.getItem(i), c = seg.pathSegTypeAsLetter;
|
|
||||||
|
|
||||||
if (/[MLHVCSQTA]/.test(c)){
|
|
||||||
if ('x' in seg) x = seg.x;
|
|
||||||
if ('y' in seg) y = seg.y;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if ('x1' in seg) x1 = x + seg.x1;
|
|
||||||
if ('x2' in seg) x2 = x + seg.x2;
|
|
||||||
if ('y1' in seg) y1 = y + seg.y1;
|
|
||||||
if ('y2' in seg) y2 = y + seg.y2;
|
|
||||||
if ('x' in seg) x += seg.x;
|
|
||||||
if ('y' in seg) y += seg.y;
|
|
||||||
|
|
||||||
switch(c) {
|
|
||||||
case 'm':
|
|
||||||
segs.replaceItem(path.createSVGPathSegMovetoAbs(x, y), i);
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
segs.replaceItem(path.createSVGPathSegLinetoAbs(x, y), i);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
segs.replaceItem(path.createSVGPathSegLinetoHorizontalAbs(x), i);
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
segs.replaceItem(path.createSVGPathSegLinetoVerticalAbs(y), i);
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
segs.replaceItem(
|
|
||||||
path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
segs.replaceItem(
|
|
||||||
path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i);
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
segs.replaceItem(
|
|
||||||
path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i);
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
segs.replaceItem(
|
|
||||||
path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i);
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
segs.replaceItem(
|
|
||||||
path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle,
|
|
||||||
seg.largeArcFlag, seg.sweepFlag), i);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'z': case 'Z':
|
|
||||||
x = x0;
|
|
||||||
y = y0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record the start of a subpath
|
|
||||||
if (c == 'M' || c == 'm') x0 = x, y0 = y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
161
scripts/util.js
Normal file
161
scripts/util.js
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
const { execSync } = require("child_process");
|
||||||
|
const { statSync } = require("fs");
|
||||||
|
const { exit } = require("process");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
info,
|
||||||
|
runCommand,
|
||||||
|
logErrorAndExit,
|
||||||
|
registerSignalHandler,
|
||||||
|
initSignalHandlers,
|
||||||
|
assertOS,
|
||||||
|
assertEffectiveRoot,
|
||||||
|
assertFileExists,
|
||||||
|
assertInstalled,
|
||||||
|
doFinally
|
||||||
|
};
|
||||||
|
|
||||||
|
let signalHandlers = [];
|
||||||
|
|
||||||
|
function registerSignalHandler(cb) {
|
||||||
|
signalHandlers.push(cb);
|
||||||
|
|
||||||
|
return () => (signalHandlers = signalHandlers.filter(h => h === cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSignalHandlers() {
|
||||||
|
process.on("SIGTERM", handle);
|
||||||
|
process.on("SIGINT", handle);
|
||||||
|
process.on("SIGHUP", handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle(signal) {
|
||||||
|
console.log(`Received ${signal}`);
|
||||||
|
|
||||||
|
for (const handler of signalHandlers) {
|
||||||
|
handler(signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function info(msg) {
|
||||||
|
console.log(`\n${msg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCommand(command, _options) {
|
||||||
|
const options = {
|
||||||
|
encoding: "utf8",
|
||||||
|
..._options,
|
||||||
|
shell: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const { onError } = options;
|
||||||
|
delete options["onError"];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = execSync(command, options);
|
||||||
|
|
||||||
|
return (typeof result === "string")
|
||||||
|
? result.trim()
|
||||||
|
: result;
|
||||||
|
} catch (error) {
|
||||||
|
if (onError) {
|
||||||
|
onError(error);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logErrorAndExit(msg, error) {
|
||||||
|
const lines = msg.split("\n");
|
||||||
|
|
||||||
|
// Get rid of leading blank lines
|
||||||
|
while (lines.length) {
|
||||||
|
if (lines[0].trim().length == 0) {
|
||||||
|
lines.shift();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rid of trailing blank lines
|
||||||
|
while (lines.length) {
|
||||||
|
if (lines.at(-1).trim().length == 0) {
|
||||||
|
lines.pop();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [ whitespace ] = lines[0].match(/^\s*/);
|
||||||
|
|
||||||
|
console.error(lines
|
||||||
|
.map(line => line.replace(whitespace, "").trimEnd())
|
||||||
|
.join("\n")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error("Error:", JSON.stringify({
|
||||||
|
name: error.name,
|
||||||
|
code: error.code,
|
||||||
|
status: error.status,
|
||||||
|
signal: error.signal,
|
||||||
|
error: error.error,
|
||||||
|
pid: error.pid,
|
||||||
|
output: error.output,
|
||||||
|
msg: error.msg,
|
||||||
|
message: error.message
|
||||||
|
}, null, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertOS() {
|
||||||
|
if (process.platform !== "linux") {
|
||||||
|
logErrorAndExit("This script requires linux.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEffectiveRoot() {
|
||||||
|
if (process.geteuid() !== 0) {
|
||||||
|
logErrorAndExit("Please run this script as root");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertFileExists(file) {
|
||||||
|
const stats = statSync(file);
|
||||||
|
if (!stats.isFile) {
|
||||||
|
logErrorAndExit(`${file} does not exist`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertInstalled(tools) {
|
||||||
|
const missingTools = [];
|
||||||
|
|
||||||
|
for (const tool of tools) {
|
||||||
|
runCommand(`command -v ${tool}`, {
|
||||||
|
onError: () => missingTools.push(tool)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingTools.length) {
|
||||||
|
logErrorAndExit(`
|
||||||
|
This script requires some tools that are not installed.
|
||||||
|
|
||||||
|
Install them via:
|
||||||
|
apt-get install -y ${missingTools.join(" ")}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doFinally(doHandler, finallyHandler) {
|
||||||
|
const unregister = registerSignalHandler(finallyHandler);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await doHandler();
|
||||||
|
} finally {
|
||||||
|
unregister();
|
||||||
|
await finallyHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Section "ServerFlags"
|
|
||||||
Option "DontVTSwitch" "on"
|
|
||||||
EndSection
|
|
||||||
18
setup.py
18
setup.py
@@ -18,8 +18,6 @@ setup(
|
|||||||
package_dir={'': 'src/py'},
|
package_dir={'': 'src/py'},
|
||||||
packages=[
|
packages=[
|
||||||
'bbctrl',
|
'bbctrl',
|
||||||
'inevent',
|
|
||||||
'lcd',
|
|
||||||
'camotics',
|
'camotics',
|
||||||
'iw_parse'
|
'iw_parse'
|
||||||
],
|
],
|
||||||
@@ -30,15 +28,13 @@ setup(
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
scripts=[
|
scripts=[
|
||||||
'scripts/update-bbctrl',
|
'installer/scripts/update-bbctrl',
|
||||||
'scripts/upgrade-bbctrl',
|
'installer/scripts/upgrade-bbctrl',
|
||||||
'scripts/sethostname',
|
'installer/scripts/sethostname',
|
||||||
'scripts/reset-video',
|
'installer/scripts/config-wifi',
|
||||||
'scripts/config-wifi',
|
'installer/scripts/edit-config',
|
||||||
'scripts/config-screen',
|
'installer/scripts/edit-boot-config',
|
||||||
'scripts/edit-config',
|
'installer/scripts/browser',
|
||||||
'scripts/edit-boot-config',
|
|
||||||
'scripts/browser',
|
|
||||||
],
|
],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'tornado',
|
'tornado',
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ enum {
|
|||||||
#define I2C_DEV TWIC
|
#define I2C_DEV TWIC
|
||||||
#define I2C_ISR TWIC_TWIS_vect
|
#define I2C_ISR TWIC_TWIS_vect
|
||||||
#define I2C_ADDR 0x2b
|
#define I2C_ADDR 0x2b
|
||||||
#define I2C_MAX_DATA 8
|
#define I2C_MAX_DATA 16
|
||||||
|
|
||||||
|
|
||||||
// Motor
|
// Motor
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ static void _i2c_end_command() {
|
|||||||
|
|
||||||
|
|
||||||
static void _i2c_command_byte(uint8_t byte) {
|
static void _i2c_command_byte(uint8_t byte) {
|
||||||
i2c.data[i2c.length++] = byte;
|
if (i2c.length < I2C_MAX_DATA) {
|
||||||
|
i2c.data[i2c.length++] = byte;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
/******************************************************************************\
|
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
#include "lcd.h"
|
|
||||||
#include "rtc.h"
|
|
||||||
#include "hardware.h"
|
|
||||||
#include "command.h"
|
|
||||||
|
|
||||||
#include <avr/io.h>
|
|
||||||
#include <avr/wdt.h>
|
|
||||||
#include <util/delay.h>
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_init(uint8_t addr) {
|
|
||||||
// Enable I2C master
|
|
||||||
TWIC.MASTER.BAUD = F_CPU / 2 / 100000 - 5; // 100 KHz
|
|
||||||
TWIC.MASTER.CTRLA = TWI_MASTER_ENABLE_bm;
|
|
||||||
TWIC.MASTER.CTRLB = TWI_MASTER_TIMEOUT_DISABLED_gc;
|
|
||||||
TWIC.MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc;
|
|
||||||
|
|
||||||
_delay_ms(50);
|
|
||||||
lcd_nibble(addr, 3 << 4); // Home
|
|
||||||
_delay_ms(50);
|
|
||||||
lcd_nibble(addr, 3 << 4); // Home
|
|
||||||
_delay_ms(50);
|
|
||||||
lcd_nibble(addr, 3 << 4); // Home
|
|
||||||
lcd_nibble(addr, 2 << 4); // 4-bit
|
|
||||||
|
|
||||||
lcd_write(addr,
|
|
||||||
LCD_FUNCTION_SET | LCD_2_LINE | LCD_5x8_DOTS | LCD_4_BIT_MODE, 0);
|
|
||||||
lcd_write(addr, LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON, 0);
|
|
||||||
lcd_write(addr, LCD_ENTRY_MODE_SET | LCD_ENTRY_SHIFT_INC, 0);
|
|
||||||
|
|
||||||
lcd_write(addr, LCD_CLEAR_DISPLAY, 0);
|
|
||||||
lcd_write(addr, LCD_RETURN_HOME, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _master_wait() {
|
|
||||||
#ifdef __AVR__
|
|
||||||
while (!(TWIC.MASTER.STATUS & TWI_MASTER_WIF_bm)) continue;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _write_i2c(uint8_t addr, uint8_t data) {
|
|
||||||
data |= BACKLIGHT_BIT;
|
|
||||||
|
|
||||||
TWIC.MASTER.ADDR = addr << 1;
|
|
||||||
_master_wait();
|
|
||||||
|
|
||||||
TWIC.MASTER.DATA = data;
|
|
||||||
_master_wait();
|
|
||||||
|
|
||||||
TWIC.MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
|
|
||||||
|
|
||||||
_delay_us(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_nibble(uint8_t addr, uint8_t data) {
|
|
||||||
_write_i2c(addr, data);
|
|
||||||
_write_i2c(addr, data | ENABLE_BIT);
|
|
||||||
_delay_us(500);
|
|
||||||
_write_i2c(addr, data & ~ENABLE_BIT);
|
|
||||||
_delay_us(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_write(uint8_t addr, uint8_t cmd, uint8_t flags) {
|
|
||||||
lcd_nibble(addr, flags | (cmd & 0xf0));
|
|
||||||
lcd_nibble(addr, flags | ((cmd << 4) & 0xf0));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_goto(uint8_t addr, uint8_t x, uint8_t y) {
|
|
||||||
static uint8_t row[] = {0, 64, 20, 84};
|
|
||||||
lcd_write(addr, LCD_SET_DDRAM_ADDR | (row[y] + x), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_putchar(uint8_t addr, uint8_t c) {
|
|
||||||
lcd_write(addr, c, REG_SELECT_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_pgmstr(uint8_t addr, const char *s) {
|
|
||||||
while (true) {
|
|
||||||
char c = pgm_read_byte(s++);
|
|
||||||
if (!c) break;
|
|
||||||
lcd_putchar(addr, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void _splash(uint8_t addr) {
|
|
||||||
lcd_init(addr);
|
|
||||||
lcd_goto(addr, 1, 1);
|
|
||||||
lcd_pgmstr(addr, PSTR("Controller booting"));
|
|
||||||
lcd_goto(addr, 3, 2);
|
|
||||||
lcd_pgmstr(addr, PSTR("Please wait..."));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_splash() {
|
|
||||||
wdt_disable();
|
|
||||||
_splash(0x27);
|
|
||||||
_splash(0x3f);
|
|
||||||
wdt_enable(WDTO_250MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_rtc_callback() {
|
|
||||||
// Display the splash if we haven't gotten any commands in 1sec since boot
|
|
||||||
if (!command_is_active() && rtc_get_time() == 1000)
|
|
||||||
lcd_splash();
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
/******************************************************************************\
|
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pgmspace.h"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Control flags
|
|
||||||
enum {
|
|
||||||
REG_SELECT_BIT = 1 << 0,
|
|
||||||
READ_BIT = 1 << 1,
|
|
||||||
ENABLE_BIT = 1 << 2,
|
|
||||||
BACKLIGHT_BIT = 1 << 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Commands
|
|
||||||
enum {
|
|
||||||
LCD_CLEAR_DISPLAY = 1 << 0,
|
|
||||||
LCD_RETURN_HOME = 1 << 1,
|
|
||||||
LCD_ENTRY_MODE_SET = 1 << 2,
|
|
||||||
LCD_DISPLAY_CONTROL = 1 << 3,
|
|
||||||
LCD_CURSOR_SHIFT = 1 << 4,
|
|
||||||
LCD_FUNCTION_SET = 1 << 5,
|
|
||||||
LCD_SET_CGRAM_ADDR = 1 << 6,
|
|
||||||
LCD_SET_DDRAM_ADDR = 1 << 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Entry Mode Set flags
|
|
||||||
#define LCD_ENTRY_SHIFT_DISPLAY (1 << 0)
|
|
||||||
#define LCD_ENTRY_SHIFT_INC (1 << 1)
|
|
||||||
#define LCD_ENTRY_SHIFT_DEC (0 << 1)
|
|
||||||
|
|
||||||
|
|
||||||
// Display Control flags
|
|
||||||
#define LCD_BLINK_ON (1 << 0)
|
|
||||||
#define LCD_BLINK_OFF (0 << 0)
|
|
||||||
#define LCD_CURSOR_ON (1 << 1)
|
|
||||||
#define LCD_CURSOR_OFF (0 << 1)
|
|
||||||
#define LCD_DISPLAY_ON (1 << 2)
|
|
||||||
#define LCD_DISPLAY_OFF (0 << 2)
|
|
||||||
|
|
||||||
|
|
||||||
// Cursor Shift flags
|
|
||||||
#define LCD_SHIFT_RIGHT (1 << 2)
|
|
||||||
#define LCD_SHIFT_LEFT (0 << 2)
|
|
||||||
#define LCD_SHIFT_DISPLAY (1 << 3)
|
|
||||||
#define LCD_SHIFT_CURSOR (0 << 3)
|
|
||||||
|
|
||||||
|
|
||||||
// Function Set flags
|
|
||||||
#define LCD_5x11_DOTS (1 << 2)
|
|
||||||
#define LCD_5x8_DOTS (0 << 2)
|
|
||||||
#define LCD_2_LINE (1 << 3)
|
|
||||||
#define LCD_1_LINE (0 << 3)
|
|
||||||
#define LCD_8_BIT_MODE (1 << 4)
|
|
||||||
#define LCD_4_BIT_MODE (0 << 4)
|
|
||||||
|
|
||||||
|
|
||||||
// Text justification flags
|
|
||||||
enum {
|
|
||||||
JUSTIFY_LEFT = 0,
|
|
||||||
JUSTIFY_RIGHT = 1,
|
|
||||||
JUSTIFY_CENTER = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void lcd_init(uint8_t addr);
|
|
||||||
void lcd_nibble(uint8_t addr, uint8_t data);
|
|
||||||
void lcd_write(uint8_t addr, uint8_t cmd, uint8_t flags);
|
|
||||||
void lcd_goto(uint8_t addr, uint8_t x, uint8_t y);
|
|
||||||
void lcd_putchar(uint8_t addr, uint8_t c);
|
|
||||||
void lcd_pgmstr(uint8_t addr, const char *s);
|
|
||||||
void lcd_splash();
|
|
||||||
void lcd_rtc_callback();
|
|
||||||
@@ -54,7 +54,7 @@ static struct {
|
|||||||
line_t line;
|
line_t line;
|
||||||
|
|
||||||
int section;
|
int section;
|
||||||
int seg;
|
uint32_t seg;
|
||||||
|
|
||||||
float iD; // Initial section distance
|
float iD; // Initial section distance
|
||||||
float iV; // Initial section velocity
|
float iV; // Initial section velocity
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
#include "switch.h"
|
#include "switch.h"
|
||||||
#include "analog.h"
|
#include "analog.h"
|
||||||
#include "motor.h"
|
#include "motor.h"
|
||||||
#include "lcd.h"
|
|
||||||
#include "vfd_spindle.h"
|
#include "vfd_spindle.h"
|
||||||
|
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
@@ -46,7 +45,6 @@ static uint32_t ticks;
|
|||||||
ISR(RTC_OVF_vect) {
|
ISR(RTC_OVF_vect) {
|
||||||
ticks++;
|
ticks++;
|
||||||
|
|
||||||
lcd_rtc_callback();
|
|
||||||
switch_rtc_callback();
|
switch_rtc_callback();
|
||||||
analog_rtc_callback();
|
analog_rtc_callback();
|
||||||
vfd_spindle_rtc_callback();
|
vfd_spindle_rtc_callback();
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
# Makefile for Bulidbotics step-test
|
|
||||||
PROJECT = step-test
|
|
||||||
MCU = atxmega192a3u
|
|
||||||
CLOCK = 32000000
|
|
||||||
|
|
||||||
# SRC
|
|
||||||
SRC = usart.c lcd.c pins.c hardware.c
|
|
||||||
SRC := $(wildcard *.c) $(patsubst %,../src/%,$(SRC))
|
|
||||||
|
|
||||||
include ../Makefile.common
|
|
||||||
|
|
||||||
CFLAGS += -I../src -I.
|
|
||||||
|
|
||||||
# Build
|
|
||||||
all: $(PROJECT).hex size
|
|
||||||
|
|
||||||
$(PROJECT).elf: $(SRC)
|
|
||||||
$(CC) $(SRC) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
# #
|
|
||||||
# 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 sys, serial, argparse
|
|
||||||
import numpy as np
|
|
||||||
import math
|
|
||||||
import json
|
|
||||||
from time import sleep
|
|
||||||
from collections import deque
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import matplotlib.animation as animation
|
|
||||||
|
|
||||||
|
|
||||||
MM_PER_STEP = 5 * 1.8 / 360 / 32
|
|
||||||
SAMPLES_PER_MIN = 6000
|
|
||||||
|
|
||||||
|
|
||||||
class Plot:
|
|
||||||
def __init__(self, port, baud, max_len):
|
|
||||||
# Open serial port
|
|
||||||
self.sp = serial.Serial(port, baud)
|
|
||||||
|
|
||||||
# Create data series
|
|
||||||
self.series = []
|
|
||||||
for i in range(5):
|
|
||||||
self.series.append(deque([0.0] * max_len))
|
|
||||||
|
|
||||||
# Init vars
|
|
||||||
self.max_len = max_len
|
|
||||||
self.incoming = ''
|
|
||||||
self.last = [None] * 4
|
|
||||||
|
|
||||||
# Open velocity log
|
|
||||||
ts = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
|
|
||||||
self.log = open('velocity-%s.log' % ts, 'w')
|
|
||||||
|
|
||||||
|
|
||||||
# Add new series data
|
|
||||||
def add(self, i, value):
|
|
||||||
self.series[i].pop()
|
|
||||||
self.series[i].appendleft(value)
|
|
||||||
|
|
||||||
|
|
||||||
def update_text(self, text, vel, data):
|
|
||||||
text[4].set_text('V {0:8,.2f}'.format(vel))
|
|
||||||
|
|
||||||
for i in range(4):
|
|
||||||
text[i].set_text('{} {:11,}'.format('XYZA'[i], int(data[i])))
|
|
||||||
|
|
||||||
|
|
||||||
def update(self, frame, axes, text):
|
|
||||||
# Read new data
|
|
||||||
try:
|
|
||||||
data = self.sp.read(self.sp.in_waiting)
|
|
||||||
self.incoming += data.decode('utf-8')
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return
|
|
||||||
|
|
||||||
while True:
|
|
||||||
# Parse lines
|
|
||||||
i = self.incoming.find('\n')
|
|
||||||
if i == -1: break
|
|
||||||
line = self.incoming[0:i]
|
|
||||||
self.incoming = self.incoming[i + 1:]
|
|
||||||
|
|
||||||
# Handle reset
|
|
||||||
if line.find('RESET') != -1:
|
|
||||||
self.update_text(text, 0, [0] * 4)
|
|
||||||
self.log.write(line + '\n')
|
|
||||||
self.last = [None] * 4
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Parse data
|
|
||||||
try:
|
|
||||||
data = [float(value) for value in line.split(',')]
|
|
||||||
except ValueError: continue
|
|
||||||
|
|
||||||
if len(data) != 4: continue
|
|
||||||
|
|
||||||
# Compute axis velocities
|
|
||||||
v = [] # Axis velocity
|
|
||||||
totalV = 0 # Tool velocity
|
|
||||||
|
|
||||||
for i in range(4):
|
|
||||||
if self.last[i] is not None:
|
|
||||||
delta = self.last[i] - data[i]
|
|
||||||
v.append(delta * MM_PER_STEP * SAMPLES_PER_MIN) # mm/min
|
|
||||||
totalV += math.pow(v[i], 2)
|
|
||||||
|
|
||||||
self.last[i] = data[i]
|
|
||||||
|
|
||||||
# Compute tool velocity
|
|
||||||
totalV = math.sqrt(totalV)
|
|
||||||
|
|
||||||
# Update position and velocity text
|
|
||||||
self.update_text(text, totalV, data)
|
|
||||||
|
|
||||||
# Don't update plots when not moving
|
|
||||||
if totalV == 0: continue
|
|
||||||
|
|
||||||
# Add new data
|
|
||||||
for i in range(4): self.add(i, v[i])
|
|
||||||
self.add(4, totalV)
|
|
||||||
|
|
||||||
# Update plots
|
|
||||||
for i in range(5):
|
|
||||||
axes[i].set_data(range(self.max_len), self.series[i])
|
|
||||||
|
|
||||||
self.log.write(line + '\n')
|
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.sp.flush()
|
|
||||||
self.sp.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# Parse command line arguments
|
|
||||||
description = "Plot velocity data in real-time"
|
|
||||||
parser = argparse.ArgumentParser(description = description)
|
|
||||||
parser.add_argument('-p', '--port', default = '/dev/ttyUSB0')
|
|
||||||
parser.add_argument('-b', '--baud', default = 115200, type = int)
|
|
||||||
parser.add_argument('-m', '--max-width', default = 2000, type = int)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# Create plot
|
|
||||||
plot = Plot(args.port, args.baud, args.max_width)
|
|
||||||
fig = plt.figure()
|
|
||||||
ax = plt.axes(xlim = (0, args.max_width), ylim = (-10000, 10000))
|
|
||||||
axes = []
|
|
||||||
axes_text = []
|
|
||||||
|
|
||||||
# Setup position and velocity text fields
|
|
||||||
font = dict(fontsize = 14, family = 'monospace')
|
|
||||||
|
|
||||||
axes_text.append(plt.text(0, 11700, '', **font))
|
|
||||||
axes_text.append(plt.text(800, 11700, '', **font))
|
|
||||||
axes_text.append(plt.text(0, 10500, '', **font))
|
|
||||||
axes_text.append(plt.text(800, 10500, '', **font))
|
|
||||||
axes_text.append(plt.text(1500, 11700, '', **font))
|
|
||||||
|
|
||||||
# Create axes
|
|
||||||
for i in range(5):
|
|
||||||
axes.append(ax.plot([], [])[0])
|
|
||||||
# Set text color to match axis color
|
|
||||||
axes_text[i].set_color(axes[i].get_color())
|
|
||||||
|
|
||||||
# Initial text views
|
|
||||||
plot.update_text(axes_text, 0, [0] * 4)
|
|
||||||
|
|
||||||
# Set up animation
|
|
||||||
anim = animation.FuncAnimation(fig, plot.update, fargs = [axes, axes_text],
|
|
||||||
interval = 100)
|
|
||||||
|
|
||||||
# Run
|
|
||||||
plt.show()
|
|
||||||
plot.close()
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
/******************************************************************************\
|
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "hardware.h"
|
|
||||||
#include "usart.h"
|
|
||||||
#include "lcd.h"
|
|
||||||
|
|
||||||
#include <avr/interrupt.h>
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define RESET_PIN SPI_MOSI_PIN
|
|
||||||
|
|
||||||
|
|
||||||
void rtc_init() {}
|
|
||||||
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
uint8_t step_pin;
|
|
||||||
uint8_t dir_pin;
|
|
||||||
TC0_t *timer;
|
|
||||||
volatile int16_t high;
|
|
||||||
volatile bool reading;
|
|
||||||
|
|
||||||
} channel[4] = {
|
|
||||||
{STEP_X_PIN, DIR_X_PIN, &TCC0, 0},
|
|
||||||
{STEP_Y_PIN, DIR_Y_PIN, &TCD0, 0},
|
|
||||||
{STEP_Z_PIN, DIR_Z_PIN, &TCE0, 0},
|
|
||||||
{STEP_A_PIN, DIR_A_PIN, &TCF0, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static int reset = 0;
|
|
||||||
|
|
||||||
|
|
||||||
void channel_reset(int i) {channel[i].timer->CNT = channel[i].high = 0;}
|
|
||||||
|
|
||||||
|
|
||||||
#define EVSYS_CHMUX(CH) (&EVSYS_CH0MUX)[CH]
|
|
||||||
#define EVSYS_CHCTRL(CH) (&EVSYS_CH0CTRL)[CH]
|
|
||||||
|
|
||||||
|
|
||||||
void channel_overflow(int i) {
|
|
||||||
if (IN_PIN(channel[i].dir_pin)) channel[i].high--;
|
|
||||||
else channel[i].high++;
|
|
||||||
channel[i].reading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ISR(TCC0_OVF_vect) {channel_overflow(0);}
|
|
||||||
ISR(TCD0_OVF_vect) {channel_overflow(1);}
|
|
||||||
ISR(TCE0_OVF_vect) {channel_overflow(2);}
|
|
||||||
ISR(TCF0_OVF_vect) {channel_overflow(3);}
|
|
||||||
|
|
||||||
|
|
||||||
void channel_update_dir(int i) {
|
|
||||||
if (IN_PIN(channel[i].dir_pin)) channel[i].timer->CTRLFSET = TC0_DIR_bm;
|
|
||||||
else channel[i].timer->CTRLFCLR = TC0_DIR_bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ISR(PORTE_INT0_vect) {for (int i = 0; i < 4; i++) channel_update_dir(i);}
|
|
||||||
|
|
||||||
|
|
||||||
int32_t __attribute__ ((noinline)) _channel_read(int i) {
|
|
||||||
return (int32_t)channel[i].high << 16 | channel[i].timer->CNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int32_t channel_read(int i) {
|
|
||||||
while (true) {
|
|
||||||
channel[i].reading = true;
|
|
||||||
|
|
||||||
int32_t x = _channel_read(i);
|
|
||||||
int32_t y = _channel_read(i);
|
|
||||||
|
|
||||||
if (x != y || !channel[i].reading) continue;
|
|
||||||
|
|
||||||
channel[i].reading = false;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void channel_update_enable(int i) {
|
|
||||||
if (IN_PIN(MOTOR_ENABLE_PIN))
|
|
||||||
channel[i].timer->CTRLA = TC_CLKSEL_EVCH0_gc + i;
|
|
||||||
else channel[i].timer->CTRLA = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ISR(PORTF_INT0_vect) {
|
|
||||||
for (int i = 0; i < 4; i++) channel_update_enable(i);
|
|
||||||
if (!IN_PIN(MOTOR_ENABLE_PIN)) reset = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ISR(PORTC_INT0_vect) {reset = 32;}
|
|
||||||
|
|
||||||
|
|
||||||
ISR(TCC1_OVF_vect) {
|
|
||||||
if (reset) reset--;
|
|
||||||
|
|
||||||
// Report measured steps
|
|
||||||
static int32_t counts[4] = {0, 0, 0, 0};
|
|
||||||
bool moving = false;
|
|
||||||
bool zero = true;
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
int32_t count = channel_read(i);
|
|
||||||
if (count != counts[i]) moving = true;
|
|
||||||
if (count) zero = false;
|
|
||||||
counts[i] = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reset && !zero) {
|
|
||||||
for (int i = 0; i < 4; i++) channel_reset(i);
|
|
||||||
printf("RESET\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moving)
|
|
||||||
printf("%ld,%ld,%ld,%ld\n", counts[0], counts[1], counts[2], counts[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void _splash(uint8_t addr) {
|
|
||||||
lcd_init(addr);
|
|
||||||
lcd_goto(addr, 5, 1);
|
|
||||||
lcd_pgmstr(addr, PSTR("Step Test"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void channel_init(int i) {
|
|
||||||
uint8_t step_pin = channel[i].step_pin;
|
|
||||||
uint8_t dir_pin = channel[i].dir_pin;
|
|
||||||
|
|
||||||
// Configure I/O
|
|
||||||
DIRCLR_PIN(step_pin);
|
|
||||||
DIRCLR_PIN(dir_pin);
|
|
||||||
PINCTRL_PIN(step_pin) = PORT_SRLEN_bm | PORT_ISC_RISING_gc;
|
|
||||||
PINCTRL_PIN(dir_pin) = PORT_SRLEN_bm | PORT_ISC_BOTHEDGES_gc;
|
|
||||||
|
|
||||||
// Dir change interrupt
|
|
||||||
PIN_PORT(dir_pin)->INTCTRL |= PORT_INT0LVL_HI_gc;
|
|
||||||
PIN_PORT(dir_pin)->INT0MASK |= PIN_BM(dir_pin);
|
|
||||||
|
|
||||||
// Events
|
|
||||||
EVSYS_CHMUX(i) = PIN_EVSYS_CHMUX(step_pin);
|
|
||||||
EVSYS_CHCTRL(i) = EVSYS_DIGFILT_8SAMPLES_gc;
|
|
||||||
|
|
||||||
// Clock
|
|
||||||
channel_update_enable(i);
|
|
||||||
channel[i].timer->INTCTRLA = TC_OVFINTLVL_HI_gc;
|
|
||||||
|
|
||||||
// Set initial clock direction
|
|
||||||
channel_update_dir(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void init() {
|
|
||||||
cli();
|
|
||||||
|
|
||||||
hw_init();
|
|
||||||
usart_init();
|
|
||||||
|
|
||||||
// Motor channels
|
|
||||||
for (int i = 0; i < 4; i++) channel_init(i);
|
|
||||||
|
|
||||||
// Motor enable
|
|
||||||
DIRCLR_PIN(MOTOR_ENABLE_PIN);
|
|
||||||
PINCTRL_PIN(MOTOR_ENABLE_PIN) =
|
|
||||||
PORT_SRLEN_bm | PORT_ISC_BOTHEDGES_gc | PORT_OPC_PULLUP_gc;
|
|
||||||
PIN_PORT(MOTOR_ENABLE_PIN)->INTCTRL |= PORT_INT0LVL_HI_gc;
|
|
||||||
PIN_PORT(MOTOR_ENABLE_PIN)->INT0MASK |= PIN_BM(MOTOR_ENABLE_PIN);
|
|
||||||
|
|
||||||
// Configure report clock
|
|
||||||
TCC1.INTCTRLA = TC_OVFINTLVL_LO_gc;
|
|
||||||
TCC1.PER = F_CPU / 256 * 0.01; // 10ms
|
|
||||||
TCC1.CTRLA = TC_CLKSEL_DIV256_gc;
|
|
||||||
|
|
||||||
// Reset switch
|
|
||||||
DIRCLR_PIN(RESET_PIN);
|
|
||||||
PINCTRL_PIN(RESET_PIN) =
|
|
||||||
PORT_SRLEN_bm | PORT_ISC_RISING_gc | PORT_OPC_PULLUP_gc;
|
|
||||||
PIN_PORT(RESET_PIN)->INTCTRL |= PORT_INT0LVL_LO_gc;
|
|
||||||
PIN_PORT(RESET_PIN)->INT0MASK |= PIN_BM(RESET_PIN);
|
|
||||||
|
|
||||||
printf("RESET\n");
|
|
||||||
|
|
||||||
sei();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
init();
|
|
||||||
|
|
||||||
_splash(0x27);
|
|
||||||
_splash(0x3f);
|
|
||||||
|
|
||||||
while (true) continue;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -1,143 +1,120 @@
|
|||||||
/******************************************************************************\
|
"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 utils = require("./utils");
|
||||||
const merge = require("lodash.merge");
|
const merge = require("lodash.merge");
|
||||||
|
|
||||||
const config_defaults = require("../resources/onefinity_defaults.json");
|
const config_defaults = require("../resources/onefinity_defaults.json");
|
||||||
|
|
||||||
const variant_defaults = {
|
const variant_defaults = {
|
||||||
machinist_x35: require("../resources/onefinity_machinist_x35_defaults.json"),
|
machinist_x35: require("../resources/onefinity_machinist_x35_defaults.json"),
|
||||||
woodworker_x35: require("../resources/onefinity_woodworker_x35_defaults.json"),
|
woodworker_x35: require("../resources/onefinity_woodworker_x35_defaults.json"),
|
||||||
woodworker_x50: require("../resources/onefinity_woodworker_x50_defaults.json"),
|
woodworker_x50: require("../resources/onefinity_woodworker_x50_defaults.json"),
|
||||||
journeyman_x50: require("../resources/onefinity_journeyman_x50_defaults.json")
|
journeyman_x50: require("../resources/onefinity_journeyman_x50_defaults.json")
|
||||||
};
|
};
|
||||||
|
|
||||||
const api = require('./api');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#admin-general-view-template',
|
template: "#admin-general-view-template",
|
||||||
props: ['config', 'state'],
|
props: [ "config", "state" ],
|
||||||
|
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
configRestored: false,
|
confirmReset: false,
|
||||||
confirmReset: false,
|
autoCheckUpgrade: true,
|
||||||
configReset: false,
|
reset_variant: ""
|
||||||
autoCheckUpgrade: true,
|
};
|
||||||
reset_variant: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
this.autoCheckUpgrade = this.config.admin['auto-check-upgrade']
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
backup: function () {
|
|
||||||
document.getElementById('download-target').src = '/api/config/download';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
restore_config: function () {
|
ready: function() {
|
||||||
// If we don't reset the form the browser may cache file if name is same
|
this.autoCheckUpgrade = this.config.admin["auto-check-upgrade"];
|
||||||
// even if contents have changed
|
|
||||||
$('.restore-config')[0].reset();
|
|
||||||
$('.restore-config input').click();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
restore: function (e) {
|
methods: {
|
||||||
var files = e.target.files || e.dataTransfer.files;
|
backup: function() {
|
||||||
if (!files.length) return;
|
document.getElementById("download-target").src = "/api/config/download";
|
||||||
|
},
|
||||||
|
|
||||||
var fr = new FileReader();
|
restore_config: function() {
|
||||||
fr.onload = function (e) {
|
utils.clickFileInput("restore-config");
|
||||||
var config;
|
},
|
||||||
try {
|
|
||||||
config = JSON.parse(e.target.result);
|
restore: function(e) {
|
||||||
} catch (ex) {
|
const files = e.target.files || e.dataTransfer.files;
|
||||||
api.alert("Invalid config file");
|
if (!files.length) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
fileReader.onload = async ({ target }) => {
|
||||||
|
let config;
|
||||||
|
try {
|
||||||
|
config = JSON.parse(target.result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Invalid config file:", error);
|
||||||
|
alert("Invalid config file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put("config/save", config);
|
||||||
|
this.$dispatch("update");
|
||||||
|
SvelteComponents.showDialog("Message", {
|
||||||
|
title: "Success",
|
||||||
|
message: "Configuration restored"
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Restore failed:", error);
|
||||||
|
alert("Restore failed");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fileReader.readAsText(files[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: async function() {
|
||||||
|
const config = merge(
|
||||||
|
{},
|
||||||
|
config_defaults,
|
||||||
|
variant_defaults[this.reset_variant]
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put("config/save", config);
|
||||||
|
this.confirmReset = false;
|
||||||
|
this.$dispatch("update");
|
||||||
|
SvelteComponents.showDialog("Message", {
|
||||||
|
title: "Success",
|
||||||
|
message: "Configuration restored"
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Restore failed:", error);
|
||||||
|
alert("Restore failed");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
check: function() {
|
||||||
|
this.$dispatch("check");
|
||||||
|
},
|
||||||
|
|
||||||
|
upgrade: function() {
|
||||||
|
this.$dispatch("upgrade");
|
||||||
|
},
|
||||||
|
|
||||||
|
upload_firmware: function() {
|
||||||
|
utils.clickFileInput("upload-firmware");
|
||||||
|
},
|
||||||
|
|
||||||
|
upload: function(e) {
|
||||||
|
const files = e.target.files || e.dataTransfer.files;
|
||||||
|
if (!files.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$dispatch("upload", files[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
change_auto_check_upgrade: function() {
|
||||||
|
this.config.admin["auto-check-upgrade"] = this.autoCheckUpgrade;
|
||||||
|
this.$dispatch("config-changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
api.put('config/save', config).done(function (data) {
|
|
||||||
this.$dispatch('update');
|
|
||||||
this.configRestored = true;
|
|
||||||
|
|
||||||
}.bind(this)).fail(function (error) {
|
|
||||||
api.alert('Restore failed', error);
|
|
||||||
})
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
fr.readAsText(files[0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
reset: async function () {
|
|
||||||
const config = merge(
|
|
||||||
{},
|
|
||||||
config_defaults,
|
|
||||||
variant_defaults[this.reset_variant]
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await api.put('config/save', config)
|
|
||||||
this.confirmReset = false;
|
|
||||||
this.$dispatch('update');
|
|
||||||
this.configRestored = true;
|
|
||||||
} catch (err) {
|
|
||||||
api.alert('Restore failed');
|
|
||||||
console.error('Restore failed', err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
check: function () {
|
|
||||||
this.$dispatch('check')
|
|
||||||
},
|
|
||||||
|
|
||||||
upgrade: function () {
|
|
||||||
this.$dispatch('upgrade')
|
|
||||||
},
|
|
||||||
|
|
||||||
upload_firmware: function () {
|
|
||||||
// If we don't reset the form the browser may cache file if name is same
|
|
||||||
// even if contents have changed
|
|
||||||
$('.upload-firmware')[0].reset();
|
|
||||||
$('.upload-firmware input').click();
|
|
||||||
},
|
|
||||||
|
|
||||||
upload: function (e) {
|
|
||||||
var files = e.target.files || e.dataTransfer.files;
|
|
||||||
if (!files.length) return;
|
|
||||||
this.$dispatch('upload', files[0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
change_auto_check_upgrade: function () {
|
|
||||||
this.config.admin['auto-check-upgrade'] = this.autoCheckUpgrade;
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
template: "#admin-network-view-template",
|
template: "#admin-network-view-template",
|
||||||
|
|
||||||
attached: function () {
|
attached: function() {
|
||||||
this.svelteComponent = SvelteComponents.create(
|
this.svelteComponent = SvelteComponents.createComponent(
|
||||||
"AdminNetworkView",
|
"AdminNetworkView",
|
||||||
document.getElementById("svelte-root")
|
document.getElementById("admin-network")
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
detached: function() {
|
detached: function() {
|
||||||
this.svelteComponent.$destroy();
|
this.svelteComponent.$destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
101
src/js/api.js
101
src/js/api.js
@@ -1,77 +1,48 @@
|
|||||||
'use strict'
|
"use strict";
|
||||||
|
|
||||||
|
async function callApi(method, url, data) {
|
||||||
|
try {
|
||||||
|
const headers = {};
|
||||||
|
let body = undefined;
|
||||||
|
|
||||||
function api_cb(method, url, data, config) {
|
if (data) {
|
||||||
config = $.extend({
|
if (data instanceof FormData) {
|
||||||
type: method,
|
body = data;
|
||||||
url: '/api/' + url,
|
} else {
|
||||||
dataType: 'json',
|
headers["Content-Type"] = "application/json; charset=utf-8";
|
||||||
cache: false
|
body = JSON.stringify(data);
|
||||||
}, config);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof data == 'object') {
|
const response = await fetch(`/api/${url}`, {
|
||||||
config.data = JSON.stringify(data);
|
method,
|
||||||
config.contentType = 'application/json; charset=utf-8';
|
headers,
|
||||||
}
|
body,
|
||||||
|
cache: "no-cache",
|
||||||
|
});
|
||||||
|
|
||||||
var d = $.Deferred();
|
if (response.ok) {
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
$.ajax(config).success(function (data, status, xhr) {
|
throw new Error(await response.text());
|
||||||
d.resolve(data, status, xhr);
|
} catch (error) {
|
||||||
|
console.debug(`API Error: ${url}: ${error}`);
|
||||||
|
|
||||||
}).error(function (xhr, status, error) {
|
throw error;
|
||||||
var text = xhr.responseText;
|
}
|
||||||
try {text = $.parseJSON(xhr.responseText)} catch(e) {}
|
|
||||||
if (!text) text = error;
|
|
||||||
|
|
||||||
d.reject(text, xhr, status, error);
|
|
||||||
console.debug('API Error: ' + url + ': ' + text);
|
|
||||||
});
|
|
||||||
|
|
||||||
return d.promise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
get: function (url, config) {
|
get: function(url) {
|
||||||
return api_cb('GET', url, undefined, config);
|
return callApi("GET", url);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
put: function(url, body = undefined) {
|
||||||
|
return callApi("PUT", url, body);
|
||||||
|
},
|
||||||
|
|
||||||
put: function(url, data, config) {
|
delete: function(url) {
|
||||||
return api_cb('PUT', url, data, config);
|
return callApi("DELETE", url);
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
post: function(url, data, config) {
|
|
||||||
return api_cb('POST', url, data, config);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
upload: function(url, data, config) {
|
|
||||||
config = $.extend({
|
|
||||||
processData: false,
|
|
||||||
contentType: false,
|
|
||||||
cache: false,
|
|
||||||
data: data
|
|
||||||
}, config);
|
|
||||||
|
|
||||||
return api_cb('PUT', url, undefined, config);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
'delete': function (url, config) {
|
|
||||||
return api_cb('DELETE', url, undefined, config);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
alert: function (msg, error) {
|
|
||||||
if (typeof error != 'undefined') {
|
|
||||||
if (typeof error.message != 'undefined')
|
|
||||||
msg += '\n' + error.message;
|
|
||||||
else msg += '\n' + JSON.stringify(error);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
alert(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
750
src/js/app.js
750
src/js/app.js
@@ -3,444 +3,428 @@
|
|||||||
const api = require("./api");
|
const api = require("./api");
|
||||||
const cookie = require("./cookie")("bbctrl-");
|
const cookie = require("./cookie")("bbctrl-");
|
||||||
const Sock = require("./sock");
|
const Sock = require("./sock");
|
||||||
const omit = require("lodash.omit");
|
|
||||||
|
|
||||||
SvelteComponents.initNetworkInfo();
|
SvelteComponents.createComponent("DialogHost",
|
||||||
|
document.getElementById("svelte-dialog-host")
|
||||||
|
);
|
||||||
|
|
||||||
function is_newer_version(current, latest) {
|
function is_newer_version(current, latest) {
|
||||||
const pattern = /(\d+)\.(\d+)\.(\d+)(.*)/;
|
const pattern = /(\d+)\.(\d+)\.(\d+)(.*)/;
|
||||||
const currentParts = current.match(pattern);
|
const currentParts = current.match(pattern);
|
||||||
const latestParts = latest.match(pattern);
|
const latestParts = latest.match(pattern);
|
||||||
|
|
||||||
if (!currentParts || !latestParts) {
|
if (!currentParts || !latestParts) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal version comparisons
|
// Normal version comparisons
|
||||||
const major = latestParts[1] - currentParts[1];
|
const major = latestParts[1] - currentParts[1];
|
||||||
const minor = latestParts[2] - currentParts[2];
|
const minor = latestParts[2] - currentParts[2];
|
||||||
const patch = latestParts[3] - currentParts[3];
|
const patch = latestParts[3] - currentParts[3];
|
||||||
|
|
||||||
// If current is a pre-release, and latest is a release
|
// If current is a pre-release, and latest is a release
|
||||||
const betaToRelease =
|
const betaToRelease = latestParts[4].length === 0 && currentParts[4].length > 0;
|
||||||
latestParts[4].length === 0 && currentParts[4].length > 0;
|
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case major > 0:
|
case major > 0:
|
||||||
case major === 0 && minor > 0:
|
case major === 0 && minor > 0:
|
||||||
case major === 0 && minor === 0 && patch > 0:
|
case major === 0 && minor === 0 && patch > 0:
|
||||||
case major === 0 && minor === 0 && patch === 0 && betaToRelease:
|
case major === 0 && minor === 0 && patch === 0 && betaToRelease:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_object(o) {
|
function is_object(o) {
|
||||||
return o !== null && typeof o == "object";
|
return o !== null && typeof o == "object";
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_array(o) {
|
function is_array(o) {
|
||||||
return Array.isArray(o);
|
return Array.isArray(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_array(dst, src) {
|
function update_array(dst, src) {
|
||||||
while (dst.length) dst.pop();
|
while (dst.length) {
|
||||||
for (var i = 0; i < src.length; i++) Vue.set(dst, i, src[i]);
|
dst.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < src.length; i++) {
|
||||||
|
Vue.set(dst, i, src[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasOwnProperty(obj, key) {
|
||||||
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_object(dst, src, remove) {
|
function update_object(dst, src, remove) {
|
||||||
var props, index, key, value;
|
let props, index, key, value;
|
||||||
|
|
||||||
if (remove) {
|
if (remove) {
|
||||||
props = Object.getOwnPropertyNames(dst);
|
props = Object.getOwnPropertyNames(dst);
|
||||||
|
|
||||||
for (index in props) {
|
for (index in props) {
|
||||||
key = props[index];
|
key = props[index];
|
||||||
if (!src.hasOwnProperty(key)) Vue.delete(dst, key);
|
if (!hasOwnProperty(src, key)) {
|
||||||
|
Vue.delete(dst, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
props = Object.getOwnPropertyNames(src);
|
props = Object.getOwnPropertyNames(src);
|
||||||
for (index in props) {
|
for (index in props) {
|
||||||
key = props[index];
|
key = props[index];
|
||||||
value = src[key];
|
value = src[key];
|
||||||
|
|
||||||
if (is_array(value) && dst.hasOwnProperty(key) && is_array(dst[key]))
|
if (is_array(value) && hasOwnProperty(dst, key) && is_array(dst[key])) {
|
||||||
update_array(dst[key], value);
|
update_array(dst[key], value);
|
||||||
else if (is_object(value) && dst.hasOwnProperty(key) && is_object(dst[key]))
|
} else if (is_object(value) && hasOwnProperty(dst, key) && is_object(dst[key])) {
|
||||||
update_object(dst[key], value, remove);
|
update_object(dst[key], value, remove);
|
||||||
else Vue.set(dst, key, value);
|
} else {
|
||||||
}
|
Vue.set(dst, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Vue({
|
module.exports = new Vue({
|
||||||
el: "body",
|
el: "body",
|
||||||
|
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
status: "connecting",
|
status: "connecting",
|
||||||
currentView: "loading",
|
currentView: "loading",
|
||||||
index: -1,
|
display_units: localStorage.getItem("display_units") || "METRIC",
|
||||||
modified: false,
|
index: -1,
|
||||||
template: require("../resources/config-template.json"),
|
modified: false,
|
||||||
config: {
|
template: require("../resources/config-template.json"),
|
||||||
settings: { units: "METRIC" },
|
config: {
|
||||||
motors: [{}, {}, {}, {}],
|
settings: { units: "METRIC" },
|
||||||
version: "<loading>",
|
motors: [ {}, {}, {}, {} ],
|
||||||
full_version: "<loading>",
|
version: "<loading>",
|
||||||
},
|
full_version: "<loading>",
|
||||||
state: {
|
},
|
||||||
messages: [],
|
state: {
|
||||||
probing_active: false,
|
messages: [],
|
||||||
wait_for_probing_complete: false,
|
},
|
||||||
show_probe_complete_modal: false,
|
video_size: cookie.get("video-size", "small"),
|
||||||
show_probe_failed_modal: false,
|
crosshair: cookie.get("crosshair", "false") != "false",
|
||||||
},
|
errorTimeout: 30,
|
||||||
video_size: cookie.get("video-size", "small"),
|
errorTimeoutStart: 0,
|
||||||
crosshair: cookie.get("crosshair", "false") != "false",
|
errorShow: false,
|
||||||
errorTimeout: 30,
|
errorMessage: "",
|
||||||
errorTimeoutStart: 0,
|
confirmUpgrade: false,
|
||||||
errorShow: false,
|
confirmUpload: false,
|
||||||
errorMessage: "",
|
firmwareUpgrading: false,
|
||||||
confirmUpgrade: false,
|
checkedUpgrade: false,
|
||||||
confirmUpload: false,
|
firmwareName: "",
|
||||||
firmwareUpgrading: false,
|
latestVersion: ""
|
||||||
checkedUpgrade: false,
|
};
|
||||||
firmwareName: "",
|
|
||||||
latestVersion: "",
|
|
||||||
confirmShutdown: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
send: function (msg) {
|
components: {
|
||||||
if (this.status == "connected") {
|
estop: { template: "#estop-template" },
|
||||||
console.debug(">", msg);
|
"loading-view": { template: "<h1>Loading...</h1>" },
|
||||||
this.sock.send(msg);
|
"control-view": require("./control-view"),
|
||||||
}
|
"settings-view": require("./settings-view"),
|
||||||
|
"motor-view": require("./motor-view"),
|
||||||
|
"tool-view": require("./tool-view"),
|
||||||
|
"io-view": require("./io-view"),
|
||||||
|
"admin-general-view": require("./admin-general-view"),
|
||||||
|
"admin-network-view": require("./admin-network-view"),
|
||||||
|
"help-view": { template: "#help-view-template" },
|
||||||
|
"cheat-sheet-view": {
|
||||||
|
template: "#cheat-sheet-view-template",
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
showUnimplemented: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
connected: function () {
|
watch: {
|
||||||
this.update();
|
display_units: function(value) {
|
||||||
|
localStorage.setItem("display_units", value);
|
||||||
|
SvelteComponents.setDisplayUnits(value);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
update: function () {
|
events: {
|
||||||
this.update();
|
"config-changed": function() {
|
||||||
},
|
this.modified = true;
|
||||||
|
},
|
||||||
|
|
||||||
check: async function () {
|
send: function(msg) {
|
||||||
try {
|
if (this.status == "connected") {
|
||||||
const response = await fetch("https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt", {
|
this.sock.send(msg);
|
||||||
cache: "no-cache"
|
}
|
||||||
});
|
},
|
||||||
|
|
||||||
this.latestVersion = (await response.text()).trim();
|
connected: function() {
|
||||||
} catch (err) {
|
this.update();
|
||||||
this.latestVersion = "";
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
upgrade: function () {
|
update: function() {
|
||||||
this.confirmUpgrade = true;
|
this.update();
|
||||||
},
|
},
|
||||||
|
|
||||||
upload: function (firmware) {
|
check: async function() {
|
||||||
this.firmware = firmware;
|
try {
|
||||||
this.firmwareName = firmware.name;
|
const response = await fetch("https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt", {
|
||||||
this.confirmUpload = true;
|
cache: "no-cache"
|
||||||
},
|
});
|
||||||
|
|
||||||
error: function (msg) {
|
this.latestVersion = (await response.text()).trim();
|
||||||
// Honor user error blocking
|
} catch (err) {
|
||||||
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000)
|
this.latestVersion = "";
|
||||||
return;
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Wait at least 1 sec to pop up repeated errors
|
upgrade: function() {
|
||||||
if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
|
this.confirmUpgrade = true;
|
||||||
return;
|
},
|
||||||
}
|
|
||||||
|
|
||||||
// Popup error dialog
|
upload: function(firmware) {
|
||||||
this.errorShow = true;
|
this.firmware = firmware;
|
||||||
this.errorMessage = msg.msg;
|
this.firmwareName = firmware.name;
|
||||||
},
|
this.confirmUpload = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
error: function(msg) {
|
||||||
popupMessages: function () {
|
// Honor user error blocking
|
||||||
const msgs = [];
|
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000) {
|
||||||
|
return;
|
||||||
for (let i = 0; i < this.state.messages.length; i++) {
|
|
||||||
const text = this.state.messages[i].text;
|
|
||||||
if (!/^#/.test(text)) {
|
|
||||||
msgs.push(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return msgs;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
$(window).on("hashchange", this.parse_hash);
|
|
||||||
this.connect();
|
|
||||||
},
|
|
||||||
|
|
||||||
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: async function () {
|
|
||||||
const config = await api.get("config/load");
|
|
||||||
|
|
||||||
update_object(this.config, config, true);
|
|
||||||
this.parse_hash();
|
|
||||||
|
|
||||||
if (!this.checkedUpgrade) {
|
|
||||||
this.checkedUpgrade = true;
|
|
||||||
|
|
||||||
var check = this.config.admin["auto-check-upgrade"];
|
|
||||||
if (typeof check == "undefined" || check) this.$emit("check");
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Popup error dialog
|
||||||
const debugStateChanges = false;
|
this.errorShow = true;
|
||||||
if (debugStateChanges) {
|
this.errorMessage = msg.msg;
|
||||||
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);
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
parse_hash: function () {
|
computed: {
|
||||||
var hash = location.hash.substr(1);
|
popupMessages: function() {
|
||||||
|
const msgs = [];
|
||||||
|
|
||||||
if (!hash.trim().length) {
|
for (let i = 0; i < this.state.messages.length; i++) {
|
||||||
location.hash = "control";
|
const text = this.state.messages[i].text;
|
||||||
return;
|
if (!/^#/.test(text)) {
|
||||||
}
|
msgs.push(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var parts = hash.split(":");
|
return msgs;
|
||||||
|
},
|
||||||
if (parts.length == 2) this.index = parts[1];
|
|
||||||
|
|
||||||
this.currentView = parts[0];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
save: function () {
|
ready: function() {
|
||||||
const selected_tool = this.config.tool["selected-tool"];
|
window.onhashchange = () => this.parse_hash();
|
||||||
const saveModbus =
|
this.connect();
|
||||||
selected_tool !== "pwm" &&
|
|
||||||
selected_tool !== "laser" &&
|
|
||||||
selected_tool !== "router";
|
|
||||||
const settings = {
|
|
||||||
["tool"]: { ...this.config.tool },
|
|
||||||
["pwm-spindle"]: { ...this.config["pwm-spindle"] },
|
|
||||||
["modbus-spindle"]: saveModbus
|
|
||||||
? { ...this.config["modbus-spindle"] }
|
|
||||||
: undefined,
|
|
||||||
};
|
|
||||||
delete settings.tool["tool-type"];
|
|
||||||
|
|
||||||
this.config["selected-tool-settings"][selected_tool] = settings;
|
SvelteComponents.registerControllerMethods({
|
||||||
|
dispatch: (...args) => this.$dispatch(...args)
|
||||||
api
|
|
||||||
.put("config/save", this.config)
|
|
||||||
.done(
|
|
||||||
function (data) {
|
|
||||||
this.modified = false;
|
|
||||||
}.bind(this)
|
|
||||||
)
|
|
||||||
.fail(function (error) {
|
|
||||||
api.alert("Save failed", error);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
close_messages: function (action) {
|
methods: {
|
||||||
if (action == "stop") api.put("stop");
|
block_error_dialog: function() {
|
||||||
if (action == "continue") api.put("unpause");
|
this.errorTimeoutStart = Date.now();
|
||||||
|
this.errorShow = false;
|
||||||
|
},
|
||||||
|
|
||||||
// Acknowledge messages
|
toggle_video: function() {
|
||||||
if (this.state.messages.length) {
|
if (this.video_size == "small") {
|
||||||
var id = this.state.messages.slice(-1)[0].id;
|
this.video_size = "large";
|
||||||
api.put("message/" + id + "/ack");
|
} else if (this.video_size == "large") {
|
||||||
}
|
this.video_size = "small";
|
||||||
|
}
|
||||||
|
cookie.set("video-size", this.video_size);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggle_crosshair: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.crosshair = !this.crosshair;
|
||||||
|
cookie.set("crosshair", this.crosshair);
|
||||||
|
},
|
||||||
|
|
||||||
|
estop: function() {
|
||||||
|
if (this.state.xx == "ESTOPPED") {
|
||||||
|
api.put("clear");
|
||||||
|
} else {
|
||||||
|
api.put("estop");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
upgrade_confirmed: async function() {
|
||||||
|
this.confirmUpgrade = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put("upgrade");
|
||||||
|
this.firmwareUpgrading = true;
|
||||||
|
} catch (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 is_newer_version(this.config.version, this.latestVersion);
|
||||||
|
},
|
||||||
|
|
||||||
|
showShutdownDialog: function() {
|
||||||
|
SvelteComponents.showDialog("Shutdown");
|
||||||
|
},
|
||||||
|
|
||||||
|
update: async function() {
|
||||||
|
const config = await api.get("config/load");
|
||||||
|
|
||||||
|
update_object(this.config, config, true);
|
||||||
|
this.parse_hash();
|
||||||
|
|
||||||
|
if (!this.checkedUpgrade) {
|
||||||
|
this.checkedUpgrade = true;
|
||||||
|
|
||||||
|
const check = this.config.admin["auto-check-upgrade"];
|
||||||
|
if (typeof check == "undefined" || check) {
|
||||||
|
this.$emit("check");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SvelteComponents.handleConfigUpdate(this.config);
|
||||||
|
},
|
||||||
|
|
||||||
|
connect: function() {
|
||||||
|
this.sock = new Sock(`//${location.host}/sockjs`);
|
||||||
|
|
||||||
|
this.sock.onmessage = (e) => {
|
||||||
|
if (typeof e.data != "object") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("log" in e.data) {
|
||||||
|
if (e.data.log.msg !== "Switch not found") {
|
||||||
|
this.$broadcast("log", e.data.log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for session ID change on controller
|
||||||
|
if ("sid" in e.data) {
|
||||||
|
if (typeof this.sid == "undefined") {
|
||||||
|
this.sid = e.data.sid;
|
||||||
|
} else if (this.sid != e.data.sid) {
|
||||||
|
if (this.hostname && location.hostname !== "localhost") {
|
||||||
|
location.hostname = this.hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_object(this.state, e.data, false);
|
||||||
|
|
||||||
|
SvelteComponents.handleControllerStateUpdate(this.state);
|
||||||
|
|
||||||
|
delete this.state.log;
|
||||||
|
|
||||||
|
this.$broadcast("update");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sock.onopen = () => {
|
||||||
|
this.status = "connected";
|
||||||
|
this.$emit(this.status);
|
||||||
|
this.$broadcast(this.status);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sock.onclose = () => {
|
||||||
|
this.status = "disconnected";
|
||||||
|
this.$emit(this.status);
|
||||||
|
this.$broadcast(this.status);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
parse_hash: function() {
|
||||||
|
const hash = location.hash.substr(1);
|
||||||
|
|
||||||
|
if (!hash.trim().length) {
|
||||||
|
location.hash = "control";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = hash.split(":");
|
||||||
|
|
||||||
|
if (parts.length == 2) {
|
||||||
|
this.index = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentView = parts[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
save: async function() {
|
||||||
|
const selected_tool = this.config.tool["selected-tool"];
|
||||||
|
const saveModbus =
|
||||||
|
selected_tool !== "pwm" &&
|
||||||
|
selected_tool !== "laser" &&
|
||||||
|
selected_tool !== "router";
|
||||||
|
const settings = {
|
||||||
|
["tool"]: { ...this.config.tool },
|
||||||
|
["pwm-spindle"]: { ...this.config["pwm-spindle"] },
|
||||||
|
["modbus-spindle"]: saveModbus
|
||||||
|
? { ...this.config["modbus-spindle"] }
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
delete settings.tool["tool-type"];
|
||||||
|
|
||||||
|
this.config["selected-tool-settings"][selected_tool] = settings;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.put("config/save", this.config);
|
||||||
|
this.modified = false;
|
||||||
|
} catch (error) {
|
||||||
|
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 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#axis-control-template',
|
template: "#axis-control-template",
|
||||||
props: ['axes', 'colors', 'enabled', 'adjust', 'step'],
|
props: [ "axes", "colors", "enabled", "adjust", "step" ],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
jog: function(axis, ring, direction) {
|
||||||
|
const value = direction * this.value(ring);
|
||||||
|
this.$dispatch(this.step ? "step" : "jog", this.axes[axis], value);
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
back2zero: function(axis0,axis1) {
|
||||||
jog: function (axis, ring, direction) {
|
this.$dispatch("back2zero",this.axes[axis0],this.axes[axis1]);
|
||||||
var value = direction * this.value(ring);
|
},
|
||||||
this.$dispatch(this.step ? 'step' : 'jog', this.axes[axis], value);
|
|
||||||
},
|
|
||||||
|
|
||||||
back2zero: function(axis0,axis1) {
|
release: function(axis) {
|
||||||
this.$dispatch('back2zero',this.axes[axis0],this.axes[axis1])
|
if (!this.step) {
|
||||||
},
|
this.$dispatch("jog", this.axes[axis], 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
value: function(ring) {
|
||||||
|
const adjust = [ 0.01, 0.1, 1 ][this.adjust];
|
||||||
|
if (this.step) {
|
||||||
|
return adjust * [ 0.1, 1, 10, 100 ][ring];
|
||||||
|
}
|
||||||
|
return adjust * [ 0.1, 0.25, 0.5, 1 ][ring];
|
||||||
|
},
|
||||||
|
|
||||||
release: function (axis) {
|
text: function(ring) {
|
||||||
if (!this.step) this.$dispatch('jog', this.axes[axis], 0)
|
let value = this.value(ring) * (this.step ? 1 : 100);
|
||||||
},
|
value = parseFloat(value.toFixed(3));
|
||||||
|
return value + (this.step ? "" : "%");
|
||||||
|
}
|
||||||
value: function (ring) {
|
|
||||||
var adjust = [0.01, 0.1, 1][this.adjust];
|
|
||||||
if (this.step) return adjust * [0.1, 1, 10, 100][ring];
|
|
||||||
return adjust * [0.1, 0.25, 0.5, 1][ring];
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
text: function (ring) {
|
|
||||||
var value = this.value(ring) * (this.step ? 1 : 100);
|
|
||||||
value = parseFloat(value.toFixed(3));
|
|
||||||
return value + (this.step ? '' : '%');
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,232 +1,254 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
function is_defined(x) {return typeof x != 'undefined'}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
props: ['state', 'config'],
|
props: [ "state", "config" ],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
metric: function() {
|
||||||
|
return this.$root.display_units === "METRIC";
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
x: function() {
|
||||||
x: function () {return this._compute_axis('x')},
|
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()}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
y: function() {
|
||||||
|
return this._compute_axis("y");
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
z: function() {
|
||||||
_convert_length: function (value) {
|
return this._compute_axis("z");
|
||||||
return this.state.imperial ? value / 25.4 : value;
|
},
|
||||||
},
|
|
||||||
|
|
||||||
|
a: function() {
|
||||||
|
return this._compute_axis("a");
|
||||||
|
},
|
||||||
|
|
||||||
_length_str: function (value) {
|
b: function() {
|
||||||
return this._convert_length(value).toLocaleString() +
|
return this._compute_axis("b");
|
||||||
(this.state.imperial ? ' in' : ' mm');
|
},
|
||||||
},
|
|
||||||
|
|
||||||
|
c: function() {
|
||||||
|
return this._compute_axis("c");
|
||||||
|
},
|
||||||
|
|
||||||
_compute_axis: function (axis) {
|
axes: function() {
|
||||||
var abs = this.state[axis + 'p'] || 0;
|
return this._compute_axes();
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
_length_str: function(value) {
|
||||||
for (var i = 0; i < this.config.motors.length; i++) {
|
return this._convert_length(value).toLocaleString() + (this.metric ? " mm" : " in");
|
||||||
var motor = this.config.motors[i];
|
},
|
||||||
if (motor.axis.toLowerCase() == axis) return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 () {
|
if (0 < dim && dim < pathDim) {
|
||||||
var homed = false;
|
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') {
|
switch (state) {
|
||||||
var axis = this[name];
|
case "UNHOMED":
|
||||||
|
title = "Click the home button to home axis.";
|
||||||
|
break;
|
||||||
|
|
||||||
if (!axis.enabled) continue
|
case "HOMED":
|
||||||
if (!axis.homed) {homed = false; break}
|
title = "Axis successfuly homed.";
|
||||||
homed = true;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
var error = false;
|
case "FAULT":
|
||||||
var warn = false;
|
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)
|
case "SHUTDOWN":
|
||||||
for (name of 'xyzabc') {
|
title = [
|
||||||
axis = this[name];
|
`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;
|
switch (tstate) {
|
||||||
if (axis.klass.indexOf('error') != -1) error = true;
|
case "OVER":
|
||||||
if (axis.klass.indexOf('warn') != -1) warn = true;
|
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,72 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
function _msg_equal(a, b) {
|
function _msg_equal(a, b) {
|
||||||
return a.level == b.level && a.source == b.source && a.where == b.where &&
|
return a.level == b.level
|
||||||
a.msg == b.msg;
|
&& a.source == b.source
|
||||||
|
&& a.where == b.where
|
||||||
|
&& a.msg == b.msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Shared among all instances
|
// Shared among all instances
|
||||||
var messages = [];
|
const messages = [];
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#console-template',
|
template: "#console-template",
|
||||||
|
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
messages
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
data: function () {
|
events: {
|
||||||
return {messages: messages}
|
log: function(msg) {
|
||||||
},
|
// There may be multiple instances of this module so ignore messages
|
||||||
|
// that have already been processed.
|
||||||
|
if (msg.logged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.logged = true;
|
||||||
|
|
||||||
events: {
|
// Make sure we have a message level
|
||||||
log: function (msg) {
|
msg.level = msg.level || "info";
|
||||||
// There may be multiple instances of this module so ignore messages
|
|
||||||
// that have already been processed.
|
|
||||||
if (msg.logged) return;
|
|
||||||
msg.logged = true;
|
|
||||||
|
|
||||||
// Make sure we have a message level
|
// Add to message log and count and collapse repeats
|
||||||
msg.level = msg.level || 'info';
|
const repeat = messages.length && _msg_equal(msg, messages[0]);
|
||||||
|
if (repeat) {
|
||||||
|
messages[0].repeat++;
|
||||||
|
} else {
|
||||||
|
msg.repeat = msg.repeat || 1;
|
||||||
|
messages.unshift(msg);
|
||||||
|
while (256 < messages.length) {
|
||||||
|
messages.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.ts = Date.now();
|
||||||
|
|
||||||
// Add to message log and count and collapse repeats
|
// Write message to browser console for debugging
|
||||||
var repeat = messages.length && _msg_equal(msg, messages[0]);
|
const text = JSON.stringify(msg);
|
||||||
if (repeat) messages[0].repeat++;
|
if (msg.level == "error" || msg.level == "critical") {
|
||||||
else {
|
console.error(text);
|
||||||
msg.repeat = msg.repeat || 1;
|
} else if (msg.level == "warning") {
|
||||||
messages.unshift(msg);
|
console.warn(text);
|
||||||
while (256 < messages.length) messages.pop();
|
} else if (msg.level == "debug" && console.debug) {
|
||||||
}
|
console.debug(text);
|
||||||
msg.ts = Date.now();
|
} else {
|
||||||
|
console.log(text);
|
||||||
|
}
|
||||||
|
|
||||||
// Write message to browser console for debugging
|
// Event on errors
|
||||||
var text = JSON.stringify(msg);
|
if (msg.level == "error" || msg.level == "critical") {
|
||||||
if (msg.level == 'error' || msg.level == 'critical') console.error(text);
|
this.$dispatch("error", msg);
|
||||||
else if (msg.level == 'warning') console.warn(text);
|
}
|
||||||
else if (msg.level == 'debug' && console.debug) console.debug(text);
|
}
|
||||||
else console.log(text);
|
},
|
||||||
|
|
||||||
// Event on errors
|
methods: {
|
||||||
if (msg.level == 'error' || msg.level == 'critical')
|
clear: function() {
|
||||||
this.$dispatch('error', msg);
|
messages.splice(0, messages.length);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
clear: function () {messages.splice(0, messages.length);},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
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
|
module.exports = function(prefix) {
|
||||||
All Rights Reserved.
|
if (typeof prefix == "undefined") {
|
||||||
|
prefix = "";
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return cookie;
|
const cookie = {
|
||||||
}
|
get: function(name, defaultValue) {
|
||||||
|
const decodedCookie = decodeURIComponent(document.cookie);
|
||||||
|
const ca = decodedCookie.split(";");
|
||||||
|
name = `${prefix + name}=`;
|
||||||
|
|
||||||
|
for (let i = 0; i < ca.length; i++) {
|
||||||
|
let c = ca[i];
|
||||||
|
while (c.charAt(0) == " ") {
|
||||||
|
c = c.substring(1);
|
||||||
|
}
|
||||||
|
if (!c.indexOf(name)) {
|
||||||
|
return c.substring(name.length, c.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
},
|
||||||
|
|
||||||
|
set: function(name, value, days) {
|
||||||
|
let offset = 2147483647; // Max value
|
||||||
|
if (typeof days != "undefined") {
|
||||||
|
offset = days * 24 * 60 * 60 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
const d = new Date();
|
||||||
|
d.setTime(d.getTime() + offset);
|
||||||
|
const expires = `expires=${d.toUTCString()}`;
|
||||||
|
document.cookie = `${prefix}${name}=${value};${expires};path=/`;
|
||||||
|
},
|
||||||
|
|
||||||
|
set_bool: function(name, value) {
|
||||||
|
cookie.set(name, value ? "true" : "false");
|
||||||
|
},
|
||||||
|
|
||||||
|
get_bool: function(name, defaultValue) {
|
||||||
|
return cookie.get(name, defaultValue ? "true" : "false") == "true";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return cookie;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,168 +1,154 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
var api = require('./api');
|
|
||||||
|
|
||||||
|
|
||||||
var entityMap = {
|
|
||||||
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
|
||||||
'/': '/', '`': '`', '=': '='}
|
|
||||||
|
|
||||||
|
const entityMap = {
|
||||||
|
"&": "&", "<": "<", ">": ">", '"': """, "'": "'",
|
||||||
|
"/": "/", "`": "`", "=": "=" };
|
||||||
|
|
||||||
function escapeHTML(s) {
|
function escapeHTML(s) {
|
||||||
return s.replace(/[&<>"'`=\/]/g, function (c) {return entityMap[c]})
|
return s.replace(/[&<>"'`=\\/]/g, function(c) {
|
||||||
|
return entityMap[c];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#gcode-viewer-template',
|
template: "#gcode-viewer-template",
|
||||||
|
|
||||||
|
data: function() {
|
||||||
data: function () {
|
return {
|
||||||
return {
|
empty: true,
|
||||||
empty: true,
|
file: "",
|
||||||
file: '',
|
line: -1
|
||||||
line: -1,
|
};
|
||||||
scrolling: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
events: {
|
|
||||||
'gcode-load': function (file) {this.load(file)},
|
|
||||||
'gcode-clear': function () {this.clear()},
|
|
||||||
'gcode-reload': function (file) {this.reload(file)},
|
|
||||||
'gcode-line': function (line) {this.update_line(line)}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
this.clusterize = new Clusterize({
|
|
||||||
rows: [],
|
|
||||||
scrollElem: $(this.$el).find('.clusterize-scroll')[0],
|
|
||||||
contentElem: $(this.$el).find('.clusterize-content')[0],
|
|
||||||
no_data_text: 'GCode view...',
|
|
||||||
callbacks: {clusterChanged: this.highlight}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
attached: function () {
|
|
||||||
if (typeof this.clusterize != 'undefined')
|
|
||||||
this.clusterize.refresh(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
load: async function(file) {
|
|
||||||
if (file == this.file) return;
|
|
||||||
this.clear();
|
|
||||||
this.file = file;
|
|
||||||
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
const response = await fetch(`/api/file/${file}?${Math.random()}`);
|
|
||||||
const text = await response.text();
|
|
||||||
|
|
||||||
if (text.length > 20e6) {
|
|
||||||
this.clusterize.update(['File is large - gcode view disabled']);
|
|
||||||
} else {
|
|
||||||
const lines = escapeHTML(text.trimRight())
|
|
||||||
.split(/[\r\n]/)
|
|
||||||
.map((line, i) => `<li class="ln${i + 1}"><b>${i + 1}</b>${line}</li>`);
|
|
||||||
|
|
||||||
this.clusterize.update(lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.empty = false;
|
|
||||||
|
|
||||||
Vue.nextTick(this.update_line);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
clear: function () {
|
"gcode-load": function(file) {
|
||||||
this.empty = true;
|
this.load(file);
|
||||||
this.file = '';
|
},
|
||||||
this.line = -1;
|
"gcode-clear": function() {
|
||||||
this.clusterize.clear();
|
this.clear();
|
||||||
},
|
},
|
||||||
|
"gcode-reload": function(file) {
|
||||||
|
this.reload(file);
|
||||||
reload: function (file) {
|
},
|
||||||
if (file != this.file) return;
|
"gcode-line": function(line) {
|
||||||
this.clear();
|
this.update_line(line);
|
||||||
this.load(file);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
highlight: function () {
|
|
||||||
var e = $(this.$el).find('.highlight');
|
|
||||||
if (e.length) e.removeClass('highlight');
|
|
||||||
|
|
||||||
e = $(this.$el).find('.ln' + this.line);
|
|
||||||
if (e.length) e.addClass('highlight');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
update_line: function(line) {
|
|
||||||
if (typeof line != 'undefined') {
|
|
||||||
if (this.line == line) return;
|
|
||||||
this.line = line;
|
|
||||||
|
|
||||||
} else line = this.line;
|
|
||||||
|
|
||||||
var totalLines = this.clusterize.getRowsAmount();
|
|
||||||
|
|
||||||
if (line <= 0) line = 1;
|
|
||||||
if (totalLines < line) line = totalLines;
|
|
||||||
|
|
||||||
var e = $(this.$el).find('.clusterize-scroll');
|
|
||||||
|
|
||||||
var lineHeight = e[0].scrollHeight / totalLines;
|
|
||||||
var linesPerPage = Math.floor(e[0].clientHeight / lineHeight);
|
|
||||||
var current = e[0].scrollTop / lineHeight;
|
|
||||||
var target = line - 1 - Math.floor(linesPerPage / 2);
|
|
||||||
|
|
||||||
// Update scroll position
|
|
||||||
if (!this.scrolling) {
|
|
||||||
if (target < current - 20 || current + 20 < target)
|
|
||||||
e[0].scrollTop = target * lineHeight;
|
|
||||||
|
|
||||||
else {
|
|
||||||
this.scrolling = true;
|
|
||||||
e.animate({scrollTop: target * lineHeight}, {
|
|
||||||
complete: function () {this.scrolling = false}.bind(this)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
Vue.nextTick(this.highlight);
|
ready: function() {
|
||||||
|
this.clusterize = new Clusterize({
|
||||||
|
rows: [],
|
||||||
|
scrollElem: this.$el.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,107 +1,94 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
var modbus = require('./modbus.js');
|
|
||||||
|
|
||||||
|
const modbus = require("./modbus.js");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#indicators-template',
|
template: "#indicators-template",
|
||||||
props: ['state'],
|
props: [ "state" ],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
modbus_status: function() {
|
||||||
|
return modbus.status_to_string(this.state.mx);
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
sense_error: function() {
|
||||||
modbus_status: function () {return modbus.status_to_string(this.state.mx)},
|
let error = "";
|
||||||
|
|
||||||
|
if (this.state.motor_voltage_sense_error) {
|
||||||
|
error += "Motor voltage\n";
|
||||||
|
}
|
||||||
|
if (this.state.motor_current_sense_error) {
|
||||||
|
error += "Motor current\n";
|
||||||
|
}
|
||||||
|
if (this.state.load1_sense_error) {
|
||||||
|
error += "Load 1\n";
|
||||||
|
}
|
||||||
|
if (this.state.load2_sense_error) {
|
||||||
|
error += "Load 2\n";
|
||||||
|
}
|
||||||
|
if (this.state.vdd_current_sense_error) {
|
||||||
|
error += "Vdd current\n";
|
||||||
|
}
|
||||||
|
|
||||||
sense_error: function () {
|
return error;
|
||||||
var error = '';
|
}
|
||||||
|
},
|
||||||
|
|
||||||
if (this.state.motor_voltage_sense_error) error += 'Motor voltage\n';
|
methods: {
|
||||||
if (this.state.motor_current_sense_error) error += 'Motor current\n';
|
is_motor_enabled: function(motor) {
|
||||||
if (this.state.load1_sense_error) error += 'Load 1\n';
|
return typeof this.state[`${motor}me`] != "undefined" && this.state[`${motor}me`];
|
||||||
if (this.state.load2_sense_error) error += 'Load 2\n';
|
},
|
||||||
if (this.state.vdd_current_sense_error) error += 'Vdd current\n';
|
|
||||||
|
|
||||||
return error;
|
get_min_pin: function(motor) {
|
||||||
|
switch (motor) {
|
||||||
|
case 0: return 3;
|
||||||
|
case 1: return 5;
|
||||||
|
case 2: return 9;
|
||||||
|
case 3: return 11;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_max_pin: function(motor) {
|
||||||
|
switch (motor) {
|
||||||
|
case 0: return 4;
|
||||||
|
case 1: return 8;
|
||||||
|
case 2: return 10;
|
||||||
|
case 3: return 12;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
motor_fault_class: function(motor, bit) {
|
||||||
|
if (typeof motor == "undefined") {
|
||||||
|
const status = this.state["fa"];
|
||||||
|
|
||||||
|
if (typeof status == "undefined") {
|
||||||
|
return "fa-question";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `fa-thumbs-${status ? "down error" : "up success"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const flags = this.state[`${motor}df`];
|
||||||
|
|
||||||
|
if (typeof flags == "undefined") {
|
||||||
|
return "fa-question";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (flags & (1 << bit)) ? "fa-thumbs-down error" :
|
||||||
|
"fa-thumbs-up success";
|
||||||
|
},
|
||||||
|
|
||||||
|
motor_reset: function(motor) {
|
||||||
|
if (typeof motor == "undefined") {
|
||||||
|
let cmd = "";
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
cmd += `\\$${i}df=0\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$dispatch("send", cmd);
|
||||||
|
} else {
|
||||||
|
this.$dispatch("send", `\\$${motor}df=0`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
is_motor_enabled: function (motor) {
|
|
||||||
return typeof this.state[motor + 'me'] != 'undefined' &&
|
|
||||||
this.state[motor + 'me'];
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_min_pin: function (motor) {
|
|
||||||
switch (motor) {
|
|
||||||
case 0: return 3;
|
|
||||||
case 1: return 5;
|
|
||||||
case 2: return 9;
|
|
||||||
case 3: return 11;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_max_pin: function (motor) {
|
|
||||||
switch (motor) {
|
|
||||||
case 0: return 4;
|
|
||||||
case 1: return 8;
|
|
||||||
case 2: return 10;
|
|
||||||
case 3: return 12;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
motor_fault_class: function (motor, bit) {
|
|
||||||
if (typeof motor == 'undefined') {
|
|
||||||
var status = this.state['fa'];
|
|
||||||
if (typeof status == 'undefined') return 'fa-question';
|
|
||||||
return 'fa-thumbs-' + (status ? 'down error' : 'up success')
|
|
||||||
}
|
|
||||||
|
|
||||||
var flags = this.state[motor + 'df'];
|
|
||||||
if (typeof flags == 'undefined') return 'fa-question';
|
|
||||||
return (flags & (1 << bit)) ? 'fa-thumbs-down error' :
|
|
||||||
'fa-thumbs-up success';
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
motor_reset: function (motor) {
|
|
||||||
if (typeof motor == 'undefined') {
|
|
||||||
var cmd = '';
|
|
||||||
for (var i = 0; i < 4; i++)
|
|
||||||
cmd += '\\$' + i + 'df=0\n';
|
|
||||||
this.$dispatch('send', cmd);
|
|
||||||
|
|
||||||
} else this.$dispatch('send', '\\$' + motor + 'df=0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,177 +1,155 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: "#io-indicator-template",
|
template: "#io-indicator-template",
|
||||||
props: ['name', 'state'],
|
props: [ "name", "state" ],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
klass: function() {
|
||||||
|
switch (this.name) {
|
||||||
|
case "min-switch-0": return this.get_motor_min_class(0);
|
||||||
|
case "min-switch-1": return this.get_motor_min_class(1);
|
||||||
|
case "min-switch-2": return this.get_motor_min_class(2);
|
||||||
|
case "min-switch-3": return this.get_motor_min_class(3);
|
||||||
|
case "max-switch-0": return this.get_motor_max_class(0);
|
||||||
|
case "max-switch-1": return this.get_motor_max_class(1);
|
||||||
|
case "max-switch-2": return this.get_motor_max_class(2);
|
||||||
|
case "max-switch-3": return this.get_motor_max_class(3);
|
||||||
|
case "estop": return this.get_input_class("ew", "et");
|
||||||
|
case "probe": return this.get_input_class("pw", "pt");
|
||||||
|
case "load-1": return this.get_output_class("1");
|
||||||
|
case "load-2": return this.get_output_class("2");
|
||||||
|
case "fault": return this.get_output_class("f");
|
||||||
|
case "tool-enable-mode": return this.get_output_class("e");
|
||||||
|
case "tool-direction-mode": return this.get_output_class("d");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
tooltip: function() {
|
||||||
klass: function () {
|
switch (this.name) {
|
||||||
if (this.name == 'min-switch-0') return this.get_motor_min_class(0);
|
case "min-switch-0": return this.get_motor_min_tooltip(0);
|
||||||
if (this.name == 'min-switch-1') return this.get_motor_min_class(1);
|
case "min-switch-1": return this.get_motor_min_tooltip(1);
|
||||||
if (this.name == 'min-switch-2') return this.get_motor_min_class(2);
|
case "min-switch-2": return this.get_motor_min_tooltip(2);
|
||||||
if (this.name == 'min-switch-3') return this.get_motor_min_class(3);
|
case "min-switch-3": return this.get_motor_min_tooltip(3);
|
||||||
if (this.name == 'max-switch-0') return this.get_motor_max_class(0);
|
case "max-switch-0": return this.get_motor_max_tooltip(0);
|
||||||
if (this.name == 'max-switch-1') return this.get_motor_max_class(1);
|
case "max-switch-1": return this.get_motor_max_tooltip(1);
|
||||||
if (this.name == 'max-switch-2') return this.get_motor_max_class(2);
|
case "max-switch-2": return this.get_motor_max_tooltip(2);
|
||||||
if (this.name == 'max-switch-3') return this.get_motor_max_class(3);
|
case "max-switch-3": return this.get_motor_max_tooltip(3);
|
||||||
if (this.name == 'estop') return this.get_input_class('ew', 'et');
|
case "estop": return this.get_input_tooltip("ew", "et");
|
||||||
if (this.name == 'probe') return this.get_input_class('pw', 'pt');
|
case "probe": return this.get_input_tooltip("pw", "pt");
|
||||||
if (this.name == 'load-1') return this.get_output_class('1');
|
case "load-1": return this.get_output_tooltip("1");
|
||||||
if (this.name == 'load-2') return this.get_output_class('2');
|
case "load-2": return this.get_output_tooltip("2");
|
||||||
if (this.name == 'fault') return this.get_output_class('f');
|
case "fault": return this.get_output_tooltip("f");
|
||||||
if (this.name == 'tool-enable-mode') return this.get_output_class('e');
|
case "tool-direction-mode": return this.get_output_tooltip("d");
|
||||||
if (this.name == 'tool-direction-mode') return this.get_output_class('d');
|
case "tool-enable-mode": return this.get_output_tooltip("e");
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
get_io_state_class: function(active, state) {
|
||||||
|
if (typeof active == "undefined" || typeof state == "undefined") {
|
||||||
|
return "fa-exclamation-triangle warn";
|
||||||
|
}
|
||||||
|
|
||||||
tooltip: function () {
|
if (state == 2) {
|
||||||
if (this.name == 'min-switch-0') return this.get_motor_min_tooltip(0);
|
return "fa-circle-o";
|
||||||
if (this.name == 'min-switch-1') return this.get_motor_min_tooltip(1);
|
}
|
||||||
if (this.name == 'min-switch-2') return this.get_motor_min_tooltip(2);
|
|
||||||
if (this.name == 'min-switch-3') return this.get_motor_min_tooltip(3);
|
const icon = state ? "fa-plus-circle" : "fa-minus-circle";
|
||||||
if (this.name == 'max-switch-0') return this.get_motor_max_tooltip(0);
|
return `${icon} ${active ? "active" : "inactive"}`;
|
||||||
if (this.name == 'max-switch-1') return this.get_motor_max_tooltip(1);
|
},
|
||||||
if (this.name == 'max-switch-2') return this.get_motor_max_tooltip(2);
|
|
||||||
if (this.name == 'max-switch-3') return this.get_motor_max_tooltip(3);
|
get_input_active: function(stateCode, typeCode) {
|
||||||
if (this.name == 'estop') return this.get_input_tooltip('ew', 'et');
|
const type = this.state[typeCode];
|
||||||
if (this.name == 'probe') return this.get_input_tooltip('pw', 'pt');
|
const state = this.state[stateCode];
|
||||||
if (this.name == 'load-1') return this.get_output_tooltip('1');
|
|
||||||
if (this.name == 'load-2') return this.get_output_tooltip('2');
|
if (type == 1) {
|
||||||
if (this.name == 'fault') return this.get_output_tooltip('f');
|
return !state; // Normally open
|
||||||
if (this.name == 'tool-direction-mode')
|
} else if (type == 2) {
|
||||||
return this.get_output_tooltip('d');
|
return state; // Normally closed
|
||||||
if (this.name == 'tool-enable-mode')
|
}
|
||||||
return this.get_output_tooltip('e');
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
get_input_class: function(stateCode, typeCode) {
|
||||||
|
return this.get_io_state_class(this.get_input_active(stateCode, typeCode), this.state[stateCode]);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_output_class: function(output) {
|
||||||
|
return this.get_io_state_class(this.state[`${output}oa`], this.state[`${output}os`]);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_motor_min_class: function(motor) {
|
||||||
|
return this.get_input_class(`${motor}lw`, `${motor}ls`);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_motor_max_class: function(motor) {
|
||||||
|
return this.get_input_class(`${motor}xw`, `${motor}xs`);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_tooltip: function(mode, active, state) {
|
||||||
|
if (typeof mode == "undefined" || typeof active == "undefined" || typeof state == "undefined") {
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == 0) {
|
||||||
|
state = "Lo/Gnd";
|
||||||
|
} else if (state == 1) {
|
||||||
|
state = "Hi/+3.3v";
|
||||||
|
} else if (state == 2) {
|
||||||
|
state = "Tristated";
|
||||||
|
} else {
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Mode: ${mode}\nActive: ${active ? "True" : "False"}\nLevel: ${state}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
get_input_tooltip: function(stateCode, typeCode) {
|
||||||
|
let type = this.state[typeCode];
|
||||||
|
if (type == 0) {
|
||||||
|
return "Disabled";
|
||||||
|
} else if (type == 1) {
|
||||||
|
type = "Normally open";
|
||||||
|
} else if (type == 2) {
|
||||||
|
type = "Normally closed";
|
||||||
|
}
|
||||||
|
|
||||||
|
const active = this.get_input_active(stateCode, typeCode);
|
||||||
|
const state = this.state[stateCode];
|
||||||
|
|
||||||
|
return this.get_tooltip(type, active, state);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_output_tooltip: function(output) {
|
||||||
|
let mode = this.state[`${output}om`];
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case 0: return "Disabled";
|
||||||
|
case 1: mode = "Lo/Hi"; break;
|
||||||
|
case 2: mode = "Hi/Lo"; break;
|
||||||
|
case 3: mode = "Tri/Lo"; break;
|
||||||
|
case 4: mode = "Tri/Hi"; break;
|
||||||
|
case 5: mode = "Lo/Tri"; break;
|
||||||
|
case 6: mode = "Hi/Tri"; break;
|
||||||
|
default:
|
||||||
|
mode = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const active = this.state[`${output}oa`];
|
||||||
|
const state = this.state[`${output}os`];
|
||||||
|
|
||||||
|
return this.get_tooltip(mode, active, state);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_motor_min_tooltip: function(motor) {
|
||||||
|
return this.get_input_tooltip(`${motor}lw`, `${motor}ls`);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_motor_max_tooltip: function(motor) {
|
||||||
|
return this.get_input_tooltip(`${motor}xw`, `${motor}xs`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
get_io_state_class: function (active, state) {
|
|
||||||
if (typeof active == 'undefined' || typeof state == 'undefined')
|
|
||||||
return 'fa-exclamation-triangle warn';
|
|
||||||
|
|
||||||
if (state == 2) return 'fa-circle-o';
|
|
||||||
|
|
||||||
return (state ? 'fa-plus-circle' : 'fa-minus-circle') + ' ' +
|
|
||||||
(active ? 'active' : 'inactive');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_input_active: function (stateCode, typeCode) {
|
|
||||||
var type = this.state[typeCode];
|
|
||||||
var state = this.state[stateCode];
|
|
||||||
|
|
||||||
if (type == 1) return !state; // Normally open
|
|
||||||
else if (type == 2) return state; // Normally closed
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_input_class: function (stateCode, typeCode) {
|
|
||||||
return this.get_io_state_class(this.get_input_active(stateCode, typeCode),
|
|
||||||
this.state[stateCode]);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_output_class: function (output) {
|
|
||||||
return this.get_io_state_class(this.state[output + 'oa'],
|
|
||||||
this.state[output + 'os']);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_motor_min_class: function (motor) {
|
|
||||||
return this.get_input_class(motor + 'lw', motor + 'ls');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_motor_max_class: function (motor) {
|
|
||||||
return this.get_input_class(motor + 'xw', motor + 'xs');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_tooltip: function (mode, active, state) {
|
|
||||||
if (typeof mode == 'undefined' || typeof active == 'undefined' ||
|
|
||||||
typeof state == 'undefined') return 'Invalid';
|
|
||||||
|
|
||||||
if (state == 0) state = 'Lo/Gnd';
|
|
||||||
else if (state == 1) state = 'Hi/+3.3v';
|
|
||||||
else if (state == 2) state = 'Tristated';
|
|
||||||
else return 'Invalid';
|
|
||||||
|
|
||||||
return 'Mode: ' + mode + '\nActive: ' + (active ? 'True' : 'False') +
|
|
||||||
'\nLevel: ' + state;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_input_tooltip: function (stateCode, typeCode) {
|
|
||||||
var type = this.state[typeCode];
|
|
||||||
if (type == 0) return 'Disabled';
|
|
||||||
else if (type == 1) type = 'Normally open';
|
|
||||||
else if (type == 2) type = 'Normally closed';
|
|
||||||
|
|
||||||
var active = this.get_input_active(stateCode, typeCode);
|
|
||||||
var state = this.state[stateCode];
|
|
||||||
|
|
||||||
return this.get_tooltip(type, active, state);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_output_tooltip: function (output) {
|
|
||||||
var mode = this.state[output + 'om'];
|
|
||||||
if (mode == 0) return 'Disabled';
|
|
||||||
else if (mode == 1) mode = 'Lo/Hi';
|
|
||||||
else if (mode == 2) mode = 'Hi/Lo';
|
|
||||||
else if (mode == 3) mode = 'Tri/Lo';
|
|
||||||
else if (mode == 4) mode = 'Tri/Hi';
|
|
||||||
else if (mode == 5) mode = 'Lo/Tri';
|
|
||||||
else if (mode == 6) mode = 'Hi/Tri';
|
|
||||||
else mode = undefined;
|
|
||||||
|
|
||||||
var active = this.state[output + 'oa'];
|
|
||||||
var state = this.state[output + 'os'];
|
|
||||||
|
|
||||||
return this.get_tooltip(mode, active, state);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_motor_min_tooltip: function (motor) {
|
|
||||||
return this.get_input_tooltip(motor + 'lw', motor + 'ls');
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
get_motor_max_tooltip: function (motor) {
|
|
||||||
return this.get_input_tooltip(motor + 'xw', motor + 'xs');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,42 +1,13 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#io-view-template',
|
template: "#io-view-template",
|
||||||
props: ['config', 'template', 'state'],
|
props: [ "config", "template", "state" ],
|
||||||
|
|
||||||
|
events: {
|
||||||
events: {
|
"input-changed": function() {
|
||||||
'input-changed': function() {
|
this.$dispatch("config-changed");
|
||||||
this.$dispatch('config-changed');
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
251
src/js/main.js
251
src/js/main.js
@@ -1,147 +1,148 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
|
|
||||||
function cookie_get(name) {
|
function cookie_get(name) {
|
||||||
var decodedCookie = decodeURIComponent(document.cookie);
|
const decodedCookie = decodeURIComponent(document.cookie);
|
||||||
var ca = decodedCookie.split(';');
|
const ca = decodedCookie.split(";");
|
||||||
name = name + '=';
|
name = `${name}=`;
|
||||||
|
|
||||||
for (var i = 0; i < ca.length; i++) {
|
for (let i = 0; i < ca.length; i++) {
|
||||||
var c = ca[i];
|
let c = ca[i];
|
||||||
while (c.charAt(0) == ' ') c = c.substring(1);
|
while (c.charAt(0) == " ") {
|
||||||
if (!c.indexOf(name)) return c.substring(name.length, c.length);
|
c = c.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!c.indexOf(name)) {
|
||||||
|
return c.substring(name.length, c.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function cookie_set(name, value, days) {
|
function cookie_set(name, value, days) {
|
||||||
var d = new Date();
|
const d = new Date();
|
||||||
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
|
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
|
||||||
var expires = 'expires=' + d.toUTCString();
|
const expires = `expires=${d.toUTCString()}`;
|
||||||
document.cookie = name + '=' + value + ';' + expires + ';path=/';
|
document.cookie = `${name}=${value};${expires};path=/`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uuid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+";
|
||||||
var uuid_chars =
|
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+';
|
|
||||||
|
|
||||||
|
|
||||||
function uuid(length) {
|
function uuid(length) {
|
||||||
if (typeof length == 'undefined') length = 52;
|
if (typeof length == "undefined") {
|
||||||
|
length = 52;
|
||||||
|
}
|
||||||
|
|
||||||
var s = '';
|
let s = "";
|
||||||
for (var i = 0; i < length; i++)
|
for (let i = 0; i < length; i++) {
|
||||||
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
|
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
|
||||||
|
}
|
||||||
|
|
||||||
return s
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.onload = 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);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HR <= value) {
|
// Register global components
|
||||||
parts.push(Math.floor(value / HR));
|
Vue.component("templated-input", require("./templated-input"));
|
||||||
value %= HR;
|
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) {
|
Vue.filter("number", function(value) {
|
||||||
parts.push(Math.floor(value / MIN));
|
if (isNaN(value)) {
|
||||||
value %= MIN;
|
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++) {
|
if (typeof precision == "undefined") {
|
||||||
parts[i] = parts[i].toFixed(i == parts.length - 1 ? precision : 0);
|
precision = 2;
|
||||||
if (i && parts[i] < 10) parts[i] = '0' + parts[i];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join(':');
|
return `${(value * 100.0).toFixed(precision)}%`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Vue app
|
Vue.filter("non_zero_percent", function(value, precision) {
|
||||||
require('./app');
|
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 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#message-template',
|
template: "#message-template",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
twoWay: true
|
twoWay: true
|
||||||
},
|
},
|
||||||
|
|
||||||
class: {
|
class: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
twoWay: false
|
twoWay: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,48 +1,20 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
replace: true,
|
replace: true,
|
||||||
template: '#modbus-reg-view-template',
|
template: "#modbus-reg-view-template",
|
||||||
props: ['index', 'model', 'template', 'enable'],
|
props: [ "index", "model", "template", "enable" ],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
has_user_value: function() {
|
||||||
|
const type = this.model["reg-type"];
|
||||||
|
return type.indexOf("write") != -1 || type.indexOf("fixed") != -1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
methods: {
|
||||||
has_user_value: function () {
|
change: function() {
|
||||||
var type = this.model['reg-type'];
|
this.$dispatch("input-changed");
|
||||||
return type.indexOf('write') != -1 || type.indexOf('fixed') != -1;
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
change: function () {this.$dispatch('input-changed')}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,51 +1,23 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
// Must match modbus.c
|
// Must match modbus.c
|
||||||
var exports = {
|
const constants = {
|
||||||
DISCONNECTED: 0,
|
DISCONNECTED: 0,
|
||||||
OK: 1,
|
OK: 1,
|
||||||
CRC: 2,
|
CRC: 2,
|
||||||
INVALID: 3,
|
INVALID: 3,
|
||||||
TIMEDOUT: 4
|
TIMEDOUT: 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
exports.status_to_string =
|
...constants,
|
||||||
function (status) {
|
status_to_string: function(status) {
|
||||||
if (status == exports.OK) return 'Ok';
|
switch (status) {
|
||||||
if (status == exports.CRC) return 'CRC error';
|
case constants.OK: return "Ok";
|
||||||
if (status == exports.INVALID) return 'Invalid response';
|
case constants.CRC: return "CRC error";
|
||||||
if (status == exports.TIMEDOUT) return 'Timedout';
|
case constants.INVALID: return "Invalid response";
|
||||||
return 'Disconnected';
|
case constants.TIMEDOUT: return "Timedout";
|
||||||
}
|
default: return "Disconnected";
|
||||||
|
}
|
||||||
|
}
|
||||||
module.exports = exports;
|
};
|
||||||
|
|||||||
@@ -1,136 +1,120 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#motor-view-template',
|
template: "#motor-view-template",
|
||||||
props: ['index', 'config', 'template', 'state'],
|
props: [ "index", "config", "template", "state" ],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
metric: function() {
|
||||||
|
return this.$root.display_units === "METRIC";
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
is_slave: function() {
|
||||||
metric: function () {return this.$root.metric()},
|
for (let i = 0; i < this.index; i++) {
|
||||||
|
if (this.motor.axis == this.config.motors[i].axis) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
is_slave: function () {
|
motor: function() {
|
||||||
for (var i = 0; i < this.index; i++)
|
return this.config.motors[this.index];
|
||||||
if (this.motor.axis == this.config.motors[i].axis)
|
},
|
||||||
return true;
|
|
||||||
|
|
||||||
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 false;
|
||||||
return this.maxMaxVelocity < this.motor['max-velocity'];
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
show: function(name, templ) {
|
||||||
|
if (templ.hmodes == undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
maxMaxVelocity: function () {
|
return templ.hmodes.indexOf(this.motor["homing-mode"]) != -1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
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 = {
|
module.exports = {
|
||||||
template: '#settings-view-template',
|
template: "#settings-view-template",
|
||||||
props: ['config', 'template'],
|
|
||||||
|
|
||||||
|
attached: function() {
|
||||||
|
this.svelteComponent = SvelteComponents.createComponent(
|
||||||
|
"SettingsView",
|
||||||
|
document.getElementById("settings")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
events: {
|
detached: function() {
|
||||||
'input-changed': function() {
|
this.svelteComponent.$destroy();
|
||||||
this.$dispatch('config-changed');
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|||||||
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
|
if (typeof retry == "undefined") {
|
||||||
All rights reserved.
|
retry = 2000;
|
||||||
|
}
|
||||||
|
if (typeof timeout == "undefined") {
|
||||||
|
timeout = 16000;
|
||||||
|
}
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
this.url = url;
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
this.retry = retry;
|
||||||
version 2 as published by the Free Software Foundation. You should
|
this.timeout = timeout;
|
||||||
have received a copy of the GNU General Public License, version 2
|
this.divisions = 4;
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
this.count = 0;
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
this.connect();
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
};
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
Sock.prototype.onmessage = function() {
|
||||||
License along with the software. If not, see
|
// Ignore
|
||||||
<http://www.gnu.org/licenses/>.
|
};
|
||||||
|
|
||||||
For information regarding this software email:
|
Sock.prototype.onopen = function() {
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
// Ignore
|
||||||
|
};
|
||||||
|
|
||||||
\******************************************************************************/
|
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) {
|
this._sock.onmessage = function(e) {
|
||||||
if (!(this instanceof Sock)) return new Sock(url, retry);
|
console.debug("msg:", e.data);
|
||||||
|
this.heartbeat("msg");
|
||||||
|
this.onmessage(e);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
if (typeof retry == 'undefined') retry = 2000;
|
this._sock.onopen = function() {
|
||||||
if (typeof timeout == 'undefined') timeout = 16000;
|
console.debug("connected");
|
||||||
|
this.heartbeat("open");
|
||||||
|
this.onopen();
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
this.url = url;
|
this._sock.onclose = function() {
|
||||||
this.retry = retry;
|
console.debug("disconnected");
|
||||||
this.timeout = timeout;
|
this._cancel_timeout();
|
||||||
this.divisions = 4;
|
|
||||||
this.count = 0;
|
|
||||||
|
|
||||||
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 () {}
|
} else {
|
||||||
Sock.prototype.onopen = function () {}
|
this._set_timeout();
|
||||||
Sock.prototype.onclose = function () {}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Sock.prototype._cancel_timeout = function() {
|
||||||
Sock.prototype.connect = function () {
|
clearTimeout(this._timeout);
|
||||||
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');
|
|
||||||
this._timeout = undefined;
|
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 () {
|
Sock.prototype.close = function() {
|
||||||
clearTimeout(this._timeout);
|
if (typeof this._sock != "undefined") {
|
||||||
this._timeout = undefined;
|
const sock = this._sock;
|
||||||
this.count = 0;
|
this._sock = undefined;
|
||||||
}
|
sock.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Sock.prototype.send = function(msg) {
|
||||||
|
this._sock.send(msg);
|
||||||
|
};
|
||||||
|
|
||||||
Sock.prototype._set_timeout = function () {
|
module.exports = Sock;
|
||||||
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
|
|
||||||
|
|||||||
@@ -1,89 +1,69 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
replace: true,
|
replace: true,
|
||||||
template: '#templated-input-template',
|
template: "#templated-input-template",
|
||||||
props: ['name', 'model', 'template'],
|
props: [ "name", "model", "template" ],
|
||||||
|
|
||||||
|
data: function() {
|
||||||
data: function () {return {view: ''}},
|
return { view: "" };
|
||||||
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
metric: function () {return this.$root.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;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
metric: function() {
|
||||||
|
return this.$root.display_units === "METRIC";
|
||||||
|
},
|
||||||
|
|
||||||
units: function () {
|
_view: function() {
|
||||||
return (this.metric || !this.template.iunit) ?
|
if (this.template.scale) {
|
||||||
this.template.unit : this.template.iunit;
|
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 () {
|
view: function() {
|
||||||
var s = 'Default ' + this.template.default + ' ' +
|
if (this.template.scale && !this.metric) {
|
||||||
(this.template.unit || '');
|
this.model = this.view * this.template.scale;
|
||||||
if (typeof this.template.help != 'undefined')
|
} else {
|
||||||
s = this.template.help + '\n' + s;
|
this.model = this.view;
|
||||||
return s;
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ready: function() {
|
||||||
|
this.view = this._view;
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
change: function() {
|
||||||
|
this.$dispatch("input-changed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
_view: function () {this.view = this._view},
|
|
||||||
|
|
||||||
|
|
||||||
view: function () {
|
|
||||||
if (this.template.scale && !this.metric)
|
|
||||||
this.model = this.view * this.template.scale;
|
|
||||||
else this.model = this.view;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
ready: function () {this.view = this._view},
|
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
change: function () {this.$dispatch('input-changed')}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,277 +1,252 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
const api = require("./api");
|
||||||
|
const modbus = require("./modbus.js");
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const api = require('./api');
|
|
||||||
const modbus = require('./modbus.js');
|
|
||||||
const merge = require("lodash.merge");
|
const merge = require("lodash.merge");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
template: '#tool-view-template',
|
template: "#tool-view-template",
|
||||||
props: ['config', 'template', 'state'],
|
props: [ "config", "template", "state" ],
|
||||||
|
|
||||||
data: function () {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
address: 0,
|
address: 0,
|
||||||
value: 0,
|
value: 0,
|
||||||
toolList: [
|
toolList: [
|
||||||
{
|
{
|
||||||
id: "disabled",
|
id: "disabled",
|
||||||
name: "Disabled"
|
name: "Disabled"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "router",
|
id: "router",
|
||||||
type: "PWM Spindle",
|
type: "PWM Spindle",
|
||||||
name: "Router (Makita, etc)"
|
name: "Router (Makita, etc)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "laser",
|
id: "laser",
|
||||||
type: "PWM Spindle",
|
type: "PWM Spindle",
|
||||||
name: "Laser (J Tech, etc)"
|
name: "Laser (J Tech, etc)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "pwm",
|
id: "pwm",
|
||||||
name: "PWM Spindle"
|
name: "PWM Spindle"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "unsupported-separator",
|
id: "unsupported-separator",
|
||||||
name: "Unsupported Tools",
|
name: "Unsupported Tools",
|
||||||
disabled: true,
|
disabled: true,
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "huanyang-vfd",
|
id: "huanyang-vfd",
|
||||||
name: "Huanyang VFD",
|
name: "Huanyang VFD",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "custom-modbus-vfd",
|
id: "custom-modbus-vfd",
|
||||||
name: "Custom Modbus VFD",
|
name: "Custom Modbus VFD",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "ac-tech-vfd",
|
id: "ac-tech-vfd",
|
||||||
name: "AC-Tech VFD",
|
name: "AC-Tech VFD",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "nowforever-vfd",
|
id: "nowforever-vfd",
|
||||||
name: "Nowforever VFD",
|
name: "Nowforever VFD",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "delta-vfd",
|
id: "delta-vfd",
|
||||||
name: "Delta VFD015M21A (Beta)",
|
name: "Delta VFD015M21A (Beta)",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "yl600-vfd",
|
id: "yl600-vfd",
|
||||||
name: "YL600, YL620, YL620-A VFD (Beta)",
|
name: "YL600, YL620, YL620-A VFD (Beta)",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "fr-d700-vfd",
|
id: "fr-d700-vfd",
|
||||||
name: "FR-D700 (Beta)",
|
name: "FR-D700 (Beta)",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "sunfar-e300-vfd",
|
id: "sunfar-e300-vfd",
|
||||||
name: "Sunfar E300 (Beta)",
|
name: "Sunfar E300 (Beta)",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "omron-mx2-vfd",
|
id: "omron-mx2-vfd",
|
||||||
name: "OMRON MX2",
|
name: "OMRON MX2",
|
||||||
unsupported: true
|
unsupported: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
"modbus-reg": require("./modbus-reg.js")
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
"state.mr": function() {
|
||||||
|
this.value = this.state.mr;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
"input-changed": function() {
|
||||||
|
this.$dispatch("config-changed");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ready: function() {
|
||||||
|
this.value = this.state.mr;
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
regs_tmpl: function() {
|
||||||
|
return this.template["modbus-spindle"].regs;
|
||||||
|
},
|
||||||
|
|
||||||
|
tool_type: function() {
|
||||||
|
return this.config.tool["tool-type"].toUpperCase();
|
||||||
|
},
|
||||||
|
|
||||||
|
selected_tool: function() {
|
||||||
|
return this.config.tool["selected-tool"];
|
||||||
|
},
|
||||||
|
|
||||||
|
is_pwm_spindle: function() {
|
||||||
|
return this.selected_tool == "pwm";
|
||||||
|
},
|
||||||
|
|
||||||
|
is_modbus: function() {
|
||||||
|
switch (this.selected_tool) {
|
||||||
|
case "disabled":
|
||||||
|
case "laser":
|
||||||
|
case "router":
|
||||||
|
case "pwm":
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
modbus_status: function() {
|
||||||
|
return modbus.status_to_string(this.state.mx);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
change_selected_tool: function() {
|
||||||
|
const selectedToolSettings = this.config["selected-tool-settings"] || {};
|
||||||
|
const settings = selectedToolSettings[this.selected_tool] || {};
|
||||||
|
this.config.tool = merge({}, this.config.tool, settings["tool"]);
|
||||||
|
this.config["pwm-spindle"] = merge({}, this.config["pwm-spindle"], settings["pwm-spindle"]);
|
||||||
|
this.config["modbus-spindle"] = merge({}, this.config["modbus-spindle"], settings["modbus-spindle"]);
|
||||||
|
|
||||||
|
const tool = this.toolList.find(tool => tool.id == this.config.tool["selected-tool"]);
|
||||||
|
this.config.tool["tool-type"] = tool.type || tool.name;
|
||||||
|
|
||||||
|
this.$dispatch("config-changed");
|
||||||
|
},
|
||||||
|
|
||||||
|
show_tool_settings: function(key) {
|
||||||
|
switch (true) {
|
||||||
|
case key === "tool-type":
|
||||||
|
case key === "selected-tool":
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case this.selected_tool === "disabled":
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case this.selected_tool === "laser":
|
||||||
|
case this.selected_tool === "router":
|
||||||
|
switch (key) {
|
||||||
|
case "tool-enable-mode":
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_reg_type: function(reg) {
|
||||||
|
return this.regs_tmpl.template["reg-type"].values[this.state[`${reg}vt`]];
|
||||||
|
},
|
||||||
|
|
||||||
|
get_reg_addr: function(reg) {
|
||||||
|
return this.state[`${reg}va`];
|
||||||
|
},
|
||||||
|
|
||||||
|
get_reg_value: function(reg) {
|
||||||
|
return this.state[`${reg}vv`];
|
||||||
|
},
|
||||||
|
|
||||||
|
get_reg_fails: function(reg) {
|
||||||
|
const fails = this.state[`${reg}vr`];
|
||||||
|
return fails == 255 ? "Max" : fails;
|
||||||
|
},
|
||||||
|
|
||||||
|
show_modbus_field: function(key) {
|
||||||
|
return key != "regs" && (key != "multi-write" || this.tool_type == "CUSTOM MODBUS VFD");
|
||||||
|
},
|
||||||
|
|
||||||
|
read: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
api.put("modbus/read", { address: this.address });
|
||||||
|
},
|
||||||
|
|
||||||
|
write: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
api.put("modbus/write", { address: this.address, value: this.value });
|
||||||
|
},
|
||||||
|
|
||||||
|
customize: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.config.tool["tool-type"] = "Custom Modbus VFD";
|
||||||
|
|
||||||
|
const regs = this.config["modbus-spindle"].regs;
|
||||||
|
for (let i = 0; i < regs.length; i++) {
|
||||||
|
const reg = this.regs_tmpl.index[i];
|
||||||
|
regs[i]["reg-type"] = this.get_reg_type(reg);
|
||||||
|
regs[i]["reg-addr"] = this.get_reg_addr(reg);
|
||||||
|
regs[i]["reg-value"] = this.get_reg_value(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$dispatch("config-changed");
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.config.tool["tool-type"] = "Custom Modbus VFD";
|
||||||
|
|
||||||
|
const regs = this.config["modbus-spindle"].regs;
|
||||||
|
for (let i = 0; i < regs.length; i++) {
|
||||||
|
regs[i]["reg-type"] = "disabled";
|
||||||
|
regs[i]["reg-addr"] = 0;
|
||||||
|
regs[i]["reg-value"] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$dispatch("config-changed");
|
||||||
|
},
|
||||||
|
|
||||||
|
reset_failures: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const regs = this.config["modbus-spindle"].regs;
|
||||||
|
for (let reg = 0; reg < regs.length; reg++) {
|
||||||
|
this.$dispatch("send", `$${reg}vr=0`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
components: {
|
|
||||||
'modbus-reg': require('./modbus-reg.js')
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
'state.mr': function () { this.value = this.state.mr }
|
|
||||||
},
|
|
||||||
|
|
||||||
events: {
|
|
||||||
'input-changed': function () {
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function () {
|
|
||||||
this.value = this.state.mr;
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
regs_tmpl: function () {
|
|
||||||
return this.template['modbus-spindle'].regs;
|
|
||||||
},
|
|
||||||
|
|
||||||
tool_type: function () {
|
|
||||||
return this.config.tool['tool-type'].toUpperCase();
|
|
||||||
},
|
|
||||||
|
|
||||||
selected_tool: function () {
|
|
||||||
return this.config.tool['selected-tool'];
|
|
||||||
},
|
|
||||||
|
|
||||||
is_pwm_spindle: function () {
|
|
||||||
return this.selected_tool == 'pwm';
|
|
||||||
},
|
|
||||||
|
|
||||||
is_modbus: function () {
|
|
||||||
switch (this.selected_tool) {
|
|
||||||
case "disabled":
|
|
||||||
case "laser":
|
|
||||||
case "router":
|
|
||||||
case "pwm":
|
|
||||||
return false;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
modbus_status: function () {
|
|
||||||
return modbus.status_to_string(this.state.mx);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
change_selected_tool: function () {
|
|
||||||
const selectedToolSettings = this.config['selected-tool-settings'] || {};
|
|
||||||
const settings = selectedToolSettings[this.selected_tool] || {};
|
|
||||||
this.config.tool = merge({}, this.config.tool, settings['tool']);
|
|
||||||
this.config['pwm-spindle'] = merge({}, this.config['pwm-spindle'], settings['pwm-spindle']);
|
|
||||||
this.config['modbus-spindle'] = merge({}, this.config['modbus-spindle'], settings['modbus-spindle']);
|
|
||||||
|
|
||||||
const tool = this.toolList.find(tool => tool.id == this.config.tool['selected-tool']);
|
|
||||||
this.config.tool["tool-type"] = tool.type || tool.name;
|
|
||||||
|
|
||||||
this.$dispatch("config-changed");
|
|
||||||
},
|
|
||||||
|
|
||||||
show_tool_settings: function (key) {
|
|
||||||
switch (true) {
|
|
||||||
case key === "tool-type":
|
|
||||||
case key === "selected-tool":
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case this.selected_tool === "disabled":
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case this.selected_tool === "laser":
|
|
||||||
case this.selected_tool === "router":
|
|
||||||
switch (key) {
|
|
||||||
case "tool-enable-mode":
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
get_reg_type: function (reg) {
|
|
||||||
return this.regs_tmpl.template['reg-type'].values[this.state[reg + 'vt']];
|
|
||||||
},
|
|
||||||
|
|
||||||
get_reg_addr: function (reg) {
|
|
||||||
return this.state[reg + 'va'];
|
|
||||||
},
|
|
||||||
|
|
||||||
get_reg_value: function (reg) {
|
|
||||||
return this.state[reg + 'vv'];
|
|
||||||
},
|
|
||||||
|
|
||||||
get_reg_fails: function (reg) {
|
|
||||||
const fails = this.state[reg + 'vr']
|
|
||||||
return fails == 255 ? 'Max' : fails;
|
|
||||||
},
|
|
||||||
|
|
||||||
show_modbus_field: function (key) {
|
|
||||||
return key != 'regs' &&
|
|
||||||
(key != 'multi-write' || this.tool_type == 'CUSTOM MODBUS VFD');
|
|
||||||
},
|
|
||||||
|
|
||||||
read: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
api.put('modbus/read', { address: this.address });
|
|
||||||
},
|
|
||||||
|
|
||||||
write: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
api.put('modbus/write', { address: this.address, value: this.value });
|
|
||||||
},
|
|
||||||
|
|
||||||
customize: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.config.tool['tool-type'] = 'Custom Modbus VFD';
|
|
||||||
|
|
||||||
const regs = this.config['modbus-spindle'].regs;
|
|
||||||
for (let i = 0; i < regs.length; i++) {
|
|
||||||
const reg = this.regs_tmpl.index[i];
|
|
||||||
regs[i]['reg-type'] = this.get_reg_type(reg);
|
|
||||||
regs[i]['reg-addr'] = this.get_reg_addr(reg);
|
|
||||||
regs[i]['reg-value'] = this.get_reg_value(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
},
|
|
||||||
|
|
||||||
clear: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.config.tool['tool-type'] = 'Custom Modbus VFD';
|
|
||||||
|
|
||||||
const regs = this.config['modbus-spindle'].regs;
|
|
||||||
for (let i = 0; i < regs.length; i++) {
|
|
||||||
regs[i]['reg-type'] = 'disabled';
|
|
||||||
regs[i]['reg-addr'] = 0;
|
|
||||||
regs[i]['reg-value'] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$dispatch('config-changed');
|
|
||||||
},
|
|
||||||
|
|
||||||
reset_failures: function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const regs = this.config['modbus-spindle'].regs;
|
|
||||||
for (let reg = 0; reg < regs.length; reg++)
|
|
||||||
this.$dispatch('send', '\$' + reg + 'vr=0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,58 +1,47 @@
|
|||||||
/******************************************************************************\
|
"use strict";
|
||||||
|
|
||||||
This file is part of the Buildbotics firmware.
|
|
||||||
|
|
||||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This file ("the software") is free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License,
|
|
||||||
version 2 as published by the Free Software Foundation. You should
|
|
||||||
have received a copy of the GNU General Public License, version 2
|
|
||||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The software is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with the software. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
For information regarding this software email:
|
|
||||||
"Joseph Coffland" <joseph@buildbotics.com>
|
|
||||||
|
|
||||||
\******************************************************************************/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
replace: true,
|
replace: true,
|
||||||
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
|
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
|
||||||
props: ['value', 'precision', 'unit', 'iunit', 'scale'],
|
props: [ "value", "precision", "unit", "iunit", "scale" ],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
metric: {
|
||||||
|
cache: false,
|
||||||
|
get: function() {
|
||||||
|
return this.$root.display_units === "METRIC";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
text: function() {
|
||||||
metric: function () {return !this.$root.state.imperial},
|
let value = this.value;
|
||||||
|
if (typeof value == "undefined") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.metric) {
|
||||||
|
value /= this.scale;
|
||||||
|
}
|
||||||
|
|
||||||
text: function () {
|
return (1 * value.toFixed(this.precision)).toLocaleString();
|
||||||
var value = this.value;
|
}
|
||||||
if (typeof value == 'undefined') return '';
|
},
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user