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/
|
||||
.sconsign.dblite
|
||||
/build
|
||||
/dist
|
||||
/crap
|
||||
/pkg
|
||||
/mnt
|
||||
/demo
|
||||
/bbctrl-*
|
||||
node_modules
|
||||
*~
|
||||
\#*
|
||||
*.pyc
|
||||
__pycache__
|
||||
*.egg-info
|
||||
/upload
|
||||
/*.img
|
||||
*.so
|
||||
*.deb
|
||||
*.zip
|
||||
/rpi-share
|
||||
/rpi-root
|
||||
/src/bbserial/linux-rpi-raspberrypi-kernel*
|
||||
/src/bbserial/raspberrypi-kernel*
|
||||
.vscode
|
||||
*.egg-info
|
||||
*.elf
|
||||
*.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
|
||||
graft scripts
|
||||
graft python-packages
|
||||
graft installer
|
||||
include scripts/install.sh
|
||||
graft src/py/bbctrl/http
|
||||
graft src/py/camotics
|
||||
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)
|
||||
PKG_NAME := bbctrl-$(VERSION)
|
||||
PUB_PATH := root@buildbotics.com:/var/www/buildbotics.com/bbctrl
|
||||
BETA_VERSION := $(VERSION)-rc$(shell ./scripts/next-rc)
|
||||
BETA_PKG_NAME := bbctrl-$(BETA_VERSION)
|
||||
|
||||
SUBPROJECTS := avr boot pwr jig
|
||||
WATCH := src/pug src/pug/templates src/stylus src/js src/resources src/svelte-components src/static Makefile
|
||||
|
||||
ifndef HOST
|
||||
HOST=onefinity
|
||||
@@ -37,16 +33,12 @@ ifndef PASSWORD
|
||||
PASSWORD=onefinity
|
||||
endif
|
||||
|
||||
|
||||
all: $(HTML) $(RESOURCES)
|
||||
@for SUB in $(SUBPROJECTS); do $(MAKE) -C src/$$SUB; done
|
||||
|
||||
pkg: all $(AVR_FIRMWARE) bbserial
|
||||
./setup.py sdist
|
||||
|
||||
beta-pkg: pkg
|
||||
cp dist/$(PKG_NAME).tar.bz2 dist/$(BETA_PKG_NAME).tar.bz2
|
||||
|
||||
bbserial:
|
||||
$(MAKE) -C src/bbserial
|
||||
|
||||
@@ -57,12 +49,6 @@ $(GPLAN_TARGET): $(GPLAN_MOD)
|
||||
|
||||
$(GPLAN_MOD): $(GPLAN_IMG)
|
||||
./scripts/gplan-init-build.sh
|
||||
git -C rpi-share/cbang fetch
|
||||
git -C rpi-share/cbang reset --hard FETCH_HEAD
|
||||
git -C rpi-share/cbang checkout 18f1e963107ef26abe750c023355a5c40dd07853
|
||||
git -C rpi-share/camotics fetch
|
||||
git -C rpi-share/camotics reset --hard FETCH_HEAD
|
||||
git -C rpi-share/camotics checkout ec876c80d20fc19837133087cef0c447df5a939d
|
||||
cp ./scripts/gplan-build.sh rpi-share/
|
||||
chmod +x rpi-share/gplan-build.sh
|
||||
sudo ./scripts/rpi-chroot.sh $(GPLAN_IMG) /mnt/host/gplan-build.sh
|
||||
@@ -74,15 +60,6 @@ $(GPLAN_IMG):
|
||||
$(AVR_FIRMWARE):
|
||||
$(MAKE) -C src/avr
|
||||
|
||||
publish: pkg
|
||||
echo -n $(VERSION) > dist/latest.txt
|
||||
rsync $(RSYNC_OPTS) dist/$(PKG_NAME).tar.bz2 dist/latest.txt $(PUB_PATH)/
|
||||
|
||||
publish-beta: beta-pkg
|
||||
echo -n $(BETA_VERSION) > dist/latest-beta.txt
|
||||
rsync $(RSYNC_OPTS) dist/$(BETA_PKG_NAME).tar.bz2 dist/latest-beta.txt \
|
||||
$(PUB_PATH)/
|
||||
|
||||
update: pkg
|
||||
http_proxy= curl -i -X PUT -H "Content-Type: multipart/form-data" \
|
||||
-F "firmware=@dist/$(PKG_NAME).tar.bz2" -F "password=$(PASSWORD)" \
|
||||
@@ -122,34 +99,8 @@ $(TARGET_DIR)/%.html: src/pug/%.pug node_modules FORCE
|
||||
@mkdir -p $(TARGET_DIR)
|
||||
$(PUG) -O pug-opts.js -P $< -o $(TARGET_DIR) || (rm -f $@; exit 1)
|
||||
|
||||
pylint:
|
||||
pylint3 -E $(shell find src/py -name \*.py | grep -v flycheck_)
|
||||
|
||||
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
|
||||
clean:
|
||||
rm -rf rpi-share
|
||||
git clean -fxd
|
||||
|
||||
.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:
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential git wget binfmt-support qemu \
|
||||
parted gcc-avr avr-libc avrdude pylint3 python3 python3-tornado curl \
|
||||
apt update
|
||||
apt upgrade -y
|
||||
apt install -y \
|
||||
build-essential git wget binfmt-support qemu gcc-9 \
|
||||
parted gcc-avr avr-libc avrdude python3 python3-tornado curl \
|
||||
unzip python3-setuptools gcc-arm-linux-gnueabihf bc sudo
|
||||
curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
/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
|
||||
cd /home/pi
|
||||
sudo -u pi startx
|
||||
@@ -15,8 +15,7 @@ while true; do
|
||||
xrdb /home/pi/.Xresources
|
||||
|
||||
# Start browser
|
||||
/usr/local/bin/browser --no-first-run --disable-infobars \
|
||||
--noerrdialogs --disable-3d-apis http://localhost/
|
||||
/usr/local/bin/browser --no-first-run --disable-infobars --noerrdialogs --disable-3d-apis http://localhost/
|
||||
fi
|
||||
|
||||
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
|
||||
FAIL_REASON="Root partition runs past the end of device"
|
||||
echo $FAIL_REASON
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -60,18 +59,9 @@ if should_resize_root_partition; then
|
||||
# Remove itself from /boot/cmdline.txt
|
||||
# Reboot the machine
|
||||
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
|
||||
|
||||
# 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
|
||||
else
|
||||
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();
|
||||
screenH = Window.GetHeight();
|
||||
|
||||
image = Image("splash.png");
|
||||
image = Image("boot.png");
|
||||
|
||||
if (Plymouth.GetMode() == "shutdown") {
|
||||
image = Image("shutdown.png");
|
||||
}
|
||||
|
||||
imageW = image.GetWidth();
|
||||
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 |
5593
package-lock.json
generated
5593
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@@ -1,17 +1,28 @@
|
||||
{
|
||||
"name": "bbctrl",
|
||||
"version": "1.0.10b1",
|
||||
"version": "1.1.0",
|
||||
"homepage": "https://onefinitycnc.com/",
|
||||
"repository": "https://github.com/OneFinityCNC/onefinity",
|
||||
"license": "GPL-3.0+",
|
||||
"dependencies": {
|
||||
"scripts": {
|
||||
"postinstall": "cd src/svelte-components && npm i"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
||||
"@typescript-eslint/parser": "^5.36.1",
|
||||
"browserify": "^17.0.0",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-standard-with-typescript": "^22.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.2.5",
|
||||
"eslint-plugin-promise": "^6.0.1",
|
||||
"glob": "^8.0.3",
|
||||
"inquirer": "^8.2.4",
|
||||
"jshint": "^2.13.4",
|
||||
"jstransformer-escape-html": "^1.1.0",
|
||||
"jstransformer-scss": "^2.0.0",
|
||||
"jstransformer-stylus": "^1.5.0",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.omit": "^4.5.0",
|
||||
"pug-cli": "^1.0.0-alpha6"
|
||||
}
|
||||
}
|
||||
12
pug-opts.js
12
pug-opts.js
@@ -1,11 +1,11 @@
|
||||
{
|
||||
module.exports = {
|
||||
pretty: true,
|
||||
filters: {
|
||||
browserify: function (text, options) {
|
||||
return require('child_process').execSync(
|
||||
browserify: function(text, _options) {
|
||||
return require("child_process").execSync(
|
||||
`./node_modules/.bin/browserify - --basedir src/js`,
|
||||
{input: text}
|
||||
).toString()
|
||||
{ input: text }
|
||||
).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
if [ ! -e rpi-share/cbang ]; then
|
||||
if [ "$CBANG_HOME" != "" ]; then
|
||||
fetch_local_repo rpi-share/cbang "$CBANG_HOME" master
|
||||
else
|
||||
git clone https://github.com/CauldronDevelopmentLLC/cbang \
|
||||
rpi-share/cbang
|
||||
fi
|
||||
mkdir -p rpi-share/cbang
|
||||
git -C rpi-share/cbang init
|
||||
git -C rpi-share/cbang remote add origin https://github.com/CauldronDevelopmentLLC/cbang
|
||||
git -C rpi-share/cbang fetch --depth 1 origin 18f1e963107ef26abe750c023355a5c40dd07853
|
||||
git -C rpi-share/cbang reset --hard FETCH_HEAD
|
||||
fi
|
||||
|
||||
if [ ! -e rpi-share/camotics ]; then
|
||||
if [ "$CAMOTICS_HOME" != "" ]; then
|
||||
fetch_local_repo rpi-share/camotics "$CAMOTICS_HOME" master
|
||||
else
|
||||
git clone https://github.com/CauldronDevelopmentLLC/camotics \
|
||||
rpi-share/camotics
|
||||
fi
|
||||
mkdir -p rpi-share/camotics
|
||||
git -C rpi-share/camotics init
|
||||
git -C rpi-share/camotics remote add origin https://github.com/CauldronDevelopmentLLC/camotics
|
||||
git -C rpi-share/camotics fetch --depth 1 origin ec876c80d20fc19837133087cef0c447df5a939d
|
||||
git -C rpi-share/camotics reset --hard FETCH_HEAD
|
||||
fi
|
||||
|
||||
@@ -12,45 +12,46 @@ while [ $# -gt 0 ]; do
|
||||
shift 1
|
||||
done
|
||||
|
||||
|
||||
if $UPDATE_PY; then
|
||||
systemctl stop bbctrl
|
||||
|
||||
# Update service
|
||||
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 enable bbctrl
|
||||
fi
|
||||
|
||||
if $UPDATE_AVR; then
|
||||
chmod +x ./scripts/avr109-flash.py
|
||||
./scripts/avr109-flash.py src/avr/bbctrl-avr-firmware.hex
|
||||
chmod +x ./installer/scripts/avr109-flash.py
|
||||
./installer/scripts/avr109-flash.py src/avr/bbctrl-avr-firmware.hex
|
||||
fi
|
||||
|
||||
# Update config.txt
|
||||
./scripts/edit-boot-config max_usb_current=1
|
||||
./scripts/edit-boot-config config_hdmi_boost=8
|
||||
./installer/scripts/edit-boot-config \
|
||||
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
|
||||
#./scripts/edit-boot-config dtoverlay=vc4-kms-v3d
|
||||
#./scripts/edit-boot-config gpu_mem=16
|
||||
#./installer/scripts/edit-boot-config \
|
||||
# dtoverlay=vc4-kms-v3d \
|
||||
# gpu_mem=16
|
||||
#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
|
||||
grep dwc_otg.fiq_fsm_mask /boot/cmdline.txt >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
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
|
||||
REBOOT=true
|
||||
fi
|
||||
@@ -59,55 +60,92 @@ fi
|
||||
grep cgroup_memory /boot/cmdline.txt >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
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
|
||||
REBOOT=true
|
||||
fi
|
||||
|
||||
# Remove Hawkeye
|
||||
if [ -e /etc/init.d/hawkeye ]; then
|
||||
apt-get remove --purge -y hawkeye
|
||||
apt-get purge -y hawkeye
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
# Change to US keyboard layout
|
||||
sed -i 's/^XKBLAYOUT="gb"$/XKBLAYOUT="us" # Comment stops change on upgrade/' \
|
||||
/etc/default/keyboard
|
||||
sed -i -E 's/^XKBLAYOUT="gb"$/XKBLAYOUT="us" # Comment stops change on upgrade/' /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
|
||||
diff ./scripts/11-automount.rules /etc/udev/rules.d/11-automount.rules \
|
||||
>/dev/null
|
||||
diff ./installer/config/11-automount.rules /etc/udev/rules.d/11-automount.rules >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
cp ./scripts/11-automount.rules /etc/udev/rules.d/
|
||||
sed -i 's/^\(MountFlags=slave\)/#\1/' \
|
||||
cp ./installer/config/11-automount.rules /etc/udev/rules.d/
|
||||
sed -i -E 's/^(MountFlags=slave)/#\1/' \
|
||||
/lib/systemd/system/systemd-udevd.service
|
||||
REBOOT=true
|
||||
fi
|
||||
|
||||
# Increase swap
|
||||
grep 'CONF_SWAPSIZE=1000' /etc/dphys-swapfile >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
sed -i 's/^CONF_SWAPSIZE=.*$/CONF_SWAPSIZE=1000/' /etc/dphys-swapfile
|
||||
REBOOT=true
|
||||
# Disable disk-based swap
|
||||
if [ -e /etc/dphys-swapfile ]; then
|
||||
apt-get purge -y dphys-swapfile
|
||||
rm -f /var/swap
|
||||
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
|
||||
|
||||
# Install .Xresources & .xinitrc
|
||||
cp scripts/Xresources ~pi/.Xresources
|
||||
cp ./installer/config/Xresources ~pi/.Xresources
|
||||
chown pi:pi ~pi/.Xresources
|
||||
cp scripts/xinitrc ~pi/.xinitrc
|
||||
cp ./installer/config/xinitrc ~pi/.xinitrc
|
||||
chmod +x ~pi/.xinitrc
|
||||
chown pi:pi ~pi/.xinitrc
|
||||
|
||||
#Configure the "ratpoison" window manager
|
||||
# Configure the "ratpoison" window manager
|
||||
if [ ! -e ~pi/.ratpoisonrc ]; then
|
||||
cp scripts/ratpoisonrc ~pi/.ratpoisonrc
|
||||
cp ./installer/config/ratpoisonrc ~pi/.ratpoisonrc
|
||||
chmod 644 ~pi/.ratpoisonrc
|
||||
chown pi:pi ~pi/.ratpoisonrc
|
||||
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
|
||||
MODSRC=src/bbserial/bbserial.ko
|
||||
MODDST=/lib/modules/$(uname -r)/kernel/drivers/tty/serial/bbserial.ko
|
||||
@@ -119,46 +157,51 @@ if [ $? -ne 0 ]; then
|
||||
fi
|
||||
|
||||
# Install rc.local
|
||||
cp scripts/rc.local /etc/
|
||||
cp ./installer/config/rc.local /etc/
|
||||
|
||||
# Install bbctrl
|
||||
if $UPDATE_PY; then
|
||||
service bbctrl stop
|
||||
systemctl stop bbctrl
|
||||
|
||||
rm -rf /usr/local/lib/python*/dist-packages/bbctrl-*
|
||||
|
||||
# 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
|
||||
|
||||
HTTP_DIR=$(find /usr/local/lib/ -type d -name "http")
|
||||
chmod 777 $HTTP_DIR
|
||||
|
||||
service bbctrl restart
|
||||
systemctl restart bbctrl
|
||||
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
|
||||
chmod +x ./scripts/resize_root_fs.sh
|
||||
./scripts/resize_root_fs.sh
|
||||
chmod +x ./installer/scripts/resize_root_fs.sh
|
||||
./installer/scripts/resize_root_fs.sh
|
||||
if [ $? -eq 0 ]; then
|
||||
REBOOT=true
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
# Ensure logrotate runs on every boot (for systems with no network, thus bad clock)
|
||||
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
|
||||
cp ./scripts/cron_reboot_logrotate /etc/cron.reboot/logrotate
|
||||
cp ./installer/config/cron_reboot_logrotate /etc/cron.reboot/logrotate
|
||||
fi
|
||||
|
||||
# Delete some cookies that were left behind in older images
|
||||
chmod +x ./scripts/delete-cookies.py
|
||||
./scripts/delete-cookies.py
|
||||
chmod +x ./installer/scripts/delete-cookies.py
|
||||
./installer/scripts/delete-cookies.py
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
ROOT="$PWD/rpi-root"
|
||||
LOOP=12
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <image> <exec>"
|
||||
@@ -9,7 +8,8 @@ if [ $# -lt 1 ]; then
|
||||
fi
|
||||
|
||||
IMAGE="$1"
|
||||
LOOP_DEV=/dev/loop${LOOP}
|
||||
LOOP_BOOT=
|
||||
LOOP_ROOT=
|
||||
EXEC=
|
||||
|
||||
if [ $# -gt 1 ]; then
|
||||
@@ -26,25 +26,28 @@ fi
|
||||
# Clean up on EXIT
|
||||
function cleanup {
|
||||
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
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# set up image as loop device
|
||||
losetup $LOOP_DEV "$IMAGE"
|
||||
partprobe $LOOP_DEV
|
||||
LOOP_BOOT=`losetup -f`
|
||||
losetup -o 4194304 $LOOP_BOOT "$IMAGE"
|
||||
|
||||
LOOP_ROOT=`losetup -f`
|
||||
losetup -o 48234496 $LOOP_ROOT "$IMAGE"
|
||||
|
||||
# check and fix filesystems
|
||||
fsck -f ${LOOP_DEV}p1
|
||||
fsck -f ${LOOP_DEV}p2
|
||||
fsck -f $LOOP_BOOT
|
||||
fsck -f $LOOP_ROOT
|
||||
|
||||
# make dir
|
||||
mkdir -p "$ROOT"
|
||||
|
||||
# mount partition
|
||||
mount -o rw ${LOOP_DEV}p2 -t ext4 "$ROOT"
|
||||
mount -o rw ${LOOP_DEV}p1 "$ROOT/boot"
|
||||
mount -o rw $LOOP_ROOT -t ext4 "$ROOT"
|
||||
mount -o rw $LOOP_BOOT "$ROOT/boot"
|
||||
|
||||
# mount binds
|
||||
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'},
|
||||
packages=[
|
||||
'bbctrl',
|
||||
'inevent',
|
||||
'lcd',
|
||||
'camotics',
|
||||
'iw_parse'
|
||||
],
|
||||
@@ -30,15 +28,13 @@ setup(
|
||||
]
|
||||
},
|
||||
scripts=[
|
||||
'scripts/update-bbctrl',
|
||||
'scripts/upgrade-bbctrl',
|
||||
'scripts/sethostname',
|
||||
'scripts/reset-video',
|
||||
'scripts/config-wifi',
|
||||
'scripts/config-screen',
|
||||
'scripts/edit-config',
|
||||
'scripts/edit-boot-config',
|
||||
'scripts/browser',
|
||||
'installer/scripts/update-bbctrl',
|
||||
'installer/scripts/upgrade-bbctrl',
|
||||
'installer/scripts/sethostname',
|
||||
'installer/scripts/config-wifi',
|
||||
'installer/scripts/edit-config',
|
||||
'installer/scripts/edit-boot-config',
|
||||
'installer/scripts/browser',
|
||||
],
|
||||
install_requires=[
|
||||
'tornado',
|
||||
|
||||
@@ -201,7 +201,7 @@ enum {
|
||||
#define I2C_DEV TWIC
|
||||
#define I2C_ISR TWIC_TWIS_vect
|
||||
#define I2C_ADDR 0x2b
|
||||
#define I2C_MAX_DATA 8
|
||||
#define I2C_MAX_DATA 16
|
||||
|
||||
|
||||
// Motor
|
||||
|
||||
@@ -62,7 +62,9 @@ static void _i2c_end_command() {
|
||||
|
||||
|
||||
static void _i2c_command_byte(uint8_t 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;
|
||||
|
||||
int section;
|
||||
int seg;
|
||||
uint32_t seg;
|
||||
|
||||
float iD; // Initial section distance
|
||||
float iV; // Initial section velocity
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "switch.h"
|
||||
#include "analog.h"
|
||||
#include "motor.h"
|
||||
#include "lcd.h"
|
||||
#include "vfd_spindle.h"
|
||||
|
||||
#include <avr/io.h>
|
||||
@@ -46,7 +45,6 @@ static uint32_t ticks;
|
||||
ISR(RTC_OVF_vect) {
|
||||
ticks++;
|
||||
|
||||
lcd_rtc_callback();
|
||||
switch_rtc_callback();
|
||||
analog_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
|
||||
KURL=https://github.com/dbrgn/linux-rpi/archive/$(KPKG)
|
||||
KDIR=linux-rpi-raspberrypi-kernel_1.20171029-1
|
||||
KDIR=/tmp/rpi-kernel
|
||||
export KERNEL=kernel7
|
||||
|
||||
KOPTS=ARCH=arm CROSS_COMPILE=$(CROSS) -C $(KDIR)
|
||||
@@ -14,7 +14,9 @@ all: $(KDIR)
|
||||
$(MAKE) $(KOPTS) M=$(DIR) modules
|
||||
|
||||
$(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) modules_prepare
|
||||
|
||||
|
||||
@@ -1,32 +1,7 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
"use strict";
|
||||
|
||||
const api = require("./api");
|
||||
const utils = require("./utils");
|
||||
const merge = require("lodash.merge");
|
||||
|
||||
const config_defaults = require("../resources/onefinity_defaults.json");
|
||||
@@ -38,65 +13,65 @@ const variant_defaults = {
|
||||
journeyman_x50: require("../resources/onefinity_journeyman_x50_defaults.json")
|
||||
};
|
||||
|
||||
const api = require('./api');
|
||||
|
||||
module.exports = {
|
||||
template: '#admin-general-view-template',
|
||||
props: ['config', 'state'],
|
||||
template: "#admin-general-view-template",
|
||||
props: [ "config", "state" ],
|
||||
|
||||
data: function () {
|
||||
data: function() {
|
||||
return {
|
||||
configRestored: false,
|
||||
confirmReset: false,
|
||||
configReset: false,
|
||||
autoCheckUpgrade: true,
|
||||
reset_variant: ''
|
||||
}
|
||||
reset_variant: ""
|
||||
};
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
this.autoCheckUpgrade = this.config.admin['auto-check-upgrade']
|
||||
ready: function() {
|
||||
this.autoCheckUpgrade = this.config.admin["auto-check-upgrade"];
|
||||
},
|
||||
|
||||
methods: {
|
||||
backup: function () {
|
||||
document.getElementById('download-target').src = '/api/config/download';
|
||||
backup: function() {
|
||||
document.getElementById("download-target").src = "/api/config/download";
|
||||
},
|
||||
|
||||
restore_config: function () {
|
||||
// If we don't reset the form the browser may cache file if name is same
|
||||
// even if contents have changed
|
||||
$('.restore-config')[0].reset();
|
||||
$('.restore-config input').click();
|
||||
restore_config: function() {
|
||||
utils.clickFileInput("restore-config");
|
||||
},
|
||||
|
||||
restore: function (e) {
|
||||
var files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length) return;
|
||||
|
||||
var fr = new FileReader();
|
||||
fr.onload = function (e) {
|
||||
var config;
|
||||
try {
|
||||
config = JSON.parse(e.target.result);
|
||||
} catch (ex) {
|
||||
api.alert("Invalid config file");
|
||||
restore: function(e) {
|
||||
const files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
api.put('config/save', config).done(function (data) {
|
||||
this.$dispatch('update');
|
||||
this.configRestored = true;
|
||||
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;
|
||||
}
|
||||
|
||||
}.bind(this)).fail(function (error) {
|
||||
api.alert('Restore failed', error);
|
||||
})
|
||||
}.bind(this);
|
||||
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");
|
||||
}
|
||||
};
|
||||
|
||||
fr.readAsText(files[0]);
|
||||
fileReader.readAsText(files[0]);
|
||||
},
|
||||
|
||||
reset: async function () {
|
||||
reset: async function() {
|
||||
const config = merge(
|
||||
{},
|
||||
config_defaults,
|
||||
@@ -104,40 +79,42 @@ module.exports = {
|
||||
);
|
||||
|
||||
try {
|
||||
await api.put('config/save', config)
|
||||
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);
|
||||
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')
|
||||
check: function() {
|
||||
this.$dispatch("check");
|
||||
},
|
||||
|
||||
upgrade: function () {
|
||||
this.$dispatch('upgrade')
|
||||
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_firmware: function() {
|
||||
utils.clickFileInput("upload-firmware");
|
||||
},
|
||||
|
||||
upload: function (e) {
|
||||
var files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length) return;
|
||||
this.$dispatch('upload', files[0]);
|
||||
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');
|
||||
change_auto_check_upgrade: function() {
|
||||
this.config.admin["auto-check-upgrade"] = this.autoCheckUpgrade;
|
||||
this.$dispatch("config-changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module.exports = {
|
||||
template: "#admin-network-view-template",
|
||||
|
||||
attached: function () {
|
||||
this.svelteComponent = SvelteComponents.create(
|
||||
attached: function() {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"AdminNetworkView",
|
||||
document.getElementById("svelte-root")
|
||||
document.getElementById("admin-network")
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -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) {
|
||||
config = $.extend({
|
||||
type: method,
|
||||
url: '/api/' + url,
|
||||
dataType: 'json',
|
||||
cache: false
|
||||
}, config);
|
||||
|
||||
if (typeof data == 'object') {
|
||||
config.data = JSON.stringify(data);
|
||||
config.contentType = 'application/json; charset=utf-8';
|
||||
if (data) {
|
||||
if (data instanceof FormData) {
|
||||
body = data;
|
||||
} else {
|
||||
headers["Content-Type"] = "application/json; charset=utf-8";
|
||||
body = JSON.stringify(data);
|
||||
}
|
||||
}
|
||||
|
||||
var d = $.Deferred();
|
||||
|
||||
$.ajax(config).success(function (data, status, xhr) {
|
||||
d.resolve(data, status, xhr);
|
||||
|
||||
}).error(function (xhr, status, error) {
|
||||
var text = xhr.responseText;
|
||||
try {text = $.parseJSON(xhr.responseText)} catch(e) {}
|
||||
if (!text) text = error;
|
||||
|
||||
d.reject(text, xhr, status, error);
|
||||
console.debug('API Error: ' + url + ': ' + text);
|
||||
const response = await fetch(`/api/${url}`, {
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
cache: "no-cache",
|
||||
});
|
||||
|
||||
return d.promise();
|
||||
}
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
throw new Error(await response.text());
|
||||
} catch (error) {
|
||||
console.debug(`API Error: ${url}: ${error}`);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
get: function (url, config) {
|
||||
return api_cb('GET', url, undefined, config);
|
||||
get: function(url) {
|
||||
return callApi("GET", url);
|
||||
},
|
||||
|
||||
|
||||
put: function(url, data, config) {
|
||||
return api_cb('PUT', url, data, config);
|
||||
put: function(url, body = undefined) {
|
||||
return callApi("PUT", url, body);
|
||||
},
|
||||
|
||||
|
||||
post: function(url, data, config) {
|
||||
return api_cb('POST', url, data, config);
|
||||
},
|
||||
|
||||
|
||||
upload: function(url, data, config) {
|
||||
config = $.extend({
|
||||
processData: false,
|
||||
contentType: false,
|
||||
cache: false,
|
||||
data: data
|
||||
}, config);
|
||||
|
||||
return api_cb('PUT', url, undefined, config);
|
||||
},
|
||||
|
||||
|
||||
'delete': function (url, config) {
|
||||
return api_cb('DELETE', url, undefined, config);
|
||||
},
|
||||
|
||||
|
||||
alert: function (msg, error) {
|
||||
if (typeof error != 'undefined') {
|
||||
if (typeof error.message != 'undefined')
|
||||
msg += '\n' + error.message;
|
||||
else msg += '\n' + JSON.stringify(error);
|
||||
delete: function(url) {
|
||||
return callApi("DELETE", url);
|
||||
}
|
||||
|
||||
alert(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
266
src/js/app.js
266
src/js/app.js
@@ -3,9 +3,10 @@
|
||||
const api = require("./api");
|
||||
const cookie = require("./cookie")("bbctrl-");
|
||||
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) {
|
||||
const pattern = /(\d+)\.(\d+)\.(\d+)(.*)/;
|
||||
@@ -22,8 +23,7 @@ function is_newer_version(current, latest) {
|
||||
const patch = latestParts[3] - currentParts[3];
|
||||
|
||||
// If current is a pre-release, and latest is a release
|
||||
const betaToRelease =
|
||||
latestParts[4].length === 0 && currentParts[4].length > 0;
|
||||
const betaToRelease = latestParts[4].length === 0 && currentParts[4].length > 0;
|
||||
|
||||
switch (true) {
|
||||
case major > 0:
|
||||
@@ -40,24 +40,36 @@ function is_newer_version(current, latest) {
|
||||
function is_object(o) {
|
||||
return o !== null && typeof o == "object";
|
||||
}
|
||||
|
||||
function is_array(o) {
|
||||
return Array.isArray(o);
|
||||
}
|
||||
|
||||
function update_array(dst, src) {
|
||||
while (dst.length) dst.pop();
|
||||
for (var i = 0; i < src.length; i++) Vue.set(dst, i, src[i]);
|
||||
while (dst.length) {
|
||||
dst.pop();
|
||||
}
|
||||
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
Vue.set(dst, i, src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function hasOwnProperty(obj, key) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
|
||||
function update_object(dst, src, remove) {
|
||||
var props, index, key, value;
|
||||
let props, index, key, value;
|
||||
|
||||
if (remove) {
|
||||
props = Object.getOwnPropertyNames(dst);
|
||||
|
||||
for (index in props) {
|
||||
key = props[index];
|
||||
if (!src.hasOwnProperty(key)) Vue.delete(dst, key);
|
||||
if (!hasOwnProperty(src, key)) {
|
||||
Vue.delete(dst, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,36 +78,35 @@ function update_object(dst, src, remove) {
|
||||
key = props[index];
|
||||
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);
|
||||
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);
|
||||
else Vue.set(dst, key, value);
|
||||
} else {
|
||||
Vue.set(dst, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Vue({
|
||||
el: "body",
|
||||
|
||||
data: function () {
|
||||
data: function() {
|
||||
return {
|
||||
status: "connecting",
|
||||
currentView: "loading",
|
||||
display_units: localStorage.getItem("display_units") || "METRIC",
|
||||
index: -1,
|
||||
modified: false,
|
||||
template: require("../resources/config-template.json"),
|
||||
config: {
|
||||
settings: { units: "METRIC" },
|
||||
motors: [{}, {}, {}, {}],
|
||||
motors: [ {}, {}, {}, {} ],
|
||||
version: "<loading>",
|
||||
full_version: "<loading>",
|
||||
},
|
||||
state: {
|
||||
messages: [],
|
||||
probing_active: false,
|
||||
wait_for_probing_complete: false,
|
||||
show_probe_complete_modal: false,
|
||||
show_probe_failed_modal: false,
|
||||
},
|
||||
video_size: cookie.get("video-size", "small"),
|
||||
crosshair: cookie.get("crosshair", "false") != "false",
|
||||
@@ -108,8 +119,7 @@ module.exports = new Vue({
|
||||
firmwareUpgrading: false,
|
||||
checkedUpgrade: false,
|
||||
firmwareName: "",
|
||||
latestVersion: "",
|
||||
confirmShutdown: false,
|
||||
latestVersion: ""
|
||||
};
|
||||
},
|
||||
|
||||
@@ -126,33 +136,41 @@ module.exports = new Vue({
|
||||
"help-view": { template: "#help-view-template" },
|
||||
"cheat-sheet-view": {
|
||||
template: "#cheat-sheet-view-template",
|
||||
data: function () {
|
||||
return { showUnimplemented: false };
|
||||
data: function() {
|
||||
return {
|
||||
showUnimplemented: false
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
display_units: function(value) {
|
||||
localStorage.setItem("display_units", value);
|
||||
SvelteComponents.setDisplayUnits(value);
|
||||
},
|
||||
},
|
||||
|
||||
events: {
|
||||
"config-changed": function () {
|
||||
"config-changed": function() {
|
||||
this.modified = true;
|
||||
},
|
||||
|
||||
send: function (msg) {
|
||||
send: function(msg) {
|
||||
if (this.status == "connected") {
|
||||
console.debug(">", msg);
|
||||
this.sock.send(msg);
|
||||
}
|
||||
},
|
||||
|
||||
connected: function () {
|
||||
connected: function() {
|
||||
this.update();
|
||||
},
|
||||
|
||||
update: function () {
|
||||
update: function() {
|
||||
this.update();
|
||||
},
|
||||
|
||||
check: async function () {
|
||||
check: async function() {
|
||||
try {
|
||||
const response = await fetch("https://raw.githubusercontent.com/OneFinityCNC/onefinity-release/master/latest.txt", {
|
||||
cache: "no-cache"
|
||||
@@ -164,20 +182,21 @@ module.exports = new Vue({
|
||||
}
|
||||
},
|
||||
|
||||
upgrade: function () {
|
||||
upgrade: function() {
|
||||
this.confirmUpgrade = true;
|
||||
},
|
||||
|
||||
upload: function (firmware) {
|
||||
upload: function(firmware) {
|
||||
this.firmware = firmware;
|
||||
this.firmwareName = firmware.name;
|
||||
this.confirmUpload = true;
|
||||
},
|
||||
|
||||
error: function (msg) {
|
||||
error: function(msg) {
|
||||
// Honor user error blocking
|
||||
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000)
|
||||
if (Date.now() - this.errorTimeoutStart < this.errorTimeout * 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait at least 1 sec to pop up repeated errors
|
||||
if (1 < msg.repeat && Date.now() - msg.ts < 1000) {
|
||||
@@ -191,7 +210,7 @@ module.exports = new Vue({
|
||||
},
|
||||
|
||||
computed: {
|
||||
popupMessages: function () {
|
||||
popupMessages: function() {
|
||||
const msgs = [];
|
||||
|
||||
for (let i = 0; i < this.state.messages.length; i++) {
|
||||
@@ -205,83 +224,84 @@ module.exports = new Vue({
|
||||
},
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
$(window).on("hashchange", this.parse_hash);
|
||||
ready: function() {
|
||||
window.onhashchange = () => this.parse_hash();
|
||||
this.connect();
|
||||
|
||||
SvelteComponents.registerControllerMethods({
|
||||
dispatch: (...args) => this.$dispatch(...args)
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
metric: function () {
|
||||
return this.config.settings.units != "IMPERIAL";
|
||||
},
|
||||
|
||||
block_error_dialog: function () {
|
||||
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";
|
||||
toggle_video: function() {
|
||||
if (this.video_size == "small") {
|
||||
this.video_size = "large";
|
||||
} else if (this.video_size == "large") {
|
||||
this.video_size = "small";
|
||||
}
|
||||
cookie.set("video-size", this.video_size);
|
||||
},
|
||||
|
||||
toggle_crosshair: function (e) {
|
||||
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");
|
||||
estop: function() {
|
||||
if (this.state.xx == "ESTOPPED") {
|
||||
api.put("clear");
|
||||
} else {
|
||||
api.put("estop");
|
||||
}
|
||||
},
|
||||
|
||||
upgrade_confirmed: async function () {
|
||||
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);
|
||||
} catch (error) {
|
||||
console.error("Error during upgrade:", error);
|
||||
alert("Error during upgrade");
|
||||
}
|
||||
},
|
||||
|
||||
upload_confirmed: function () {
|
||||
upload_confirmed: async 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 () {
|
||||
try {
|
||||
await api.put("firmware/update", form);
|
||||
this.firmwareUpgrading = true;
|
||||
}.bind(this)
|
||||
)
|
||||
.error(
|
||||
function (err) {
|
||||
api.alert("Firmware update failed");
|
||||
console.error("Firmware update failed", err);
|
||||
}.bind(this)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Firmware update failed:", error);
|
||||
alert("Firmware update failed");
|
||||
}
|
||||
},
|
||||
|
||||
show_upgrade: function () {
|
||||
if (!this.latestVersion) return false;
|
||||
show_upgrade: function() {
|
||||
if (!this.latestVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_newer_version(this.config.version, this.latestVersion);
|
||||
},
|
||||
|
||||
update: async function () {
|
||||
showShutdownDialog: function() {
|
||||
SvelteComponents.showDialog("Shutdown");
|
||||
},
|
||||
|
||||
update: async function() {
|
||||
const config = await api.get("config/load");
|
||||
|
||||
update_object(this.config, config, true);
|
||||
@@ -290,23 +310,16 @@ module.exports = new Vue({
|
||||
if (!this.checkedUpgrade) {
|
||||
this.checkedUpgrade = true;
|
||||
|
||||
var check = this.config.admin["auto-check-upgrade"];
|
||||
if (typeof check == "undefined" || check) this.$emit("check");
|
||||
const check = this.config.admin["auto-check-upgrade"];
|
||||
if (typeof check == "undefined" || check) {
|
||||
this.$emit("check");
|
||||
}
|
||||
}
|
||||
|
||||
SvelteComponents.handleConfigUpdate(this.config);
|
||||
},
|
||||
|
||||
shutdown: function () {
|
||||
this.confirmShutdown = false;
|
||||
api.put("shutdown");
|
||||
},
|
||||
|
||||
reboot: function () {
|
||||
this.confirmShutdown = false;
|
||||
api.put("reboot");
|
||||
},
|
||||
|
||||
connect: function () {
|
||||
connect: function() {
|
||||
this.sock = new Sock(`//${location.host}/sockjs`);
|
||||
|
||||
this.sock.onmessage = (e) => {
|
||||
@@ -315,13 +328,9 @@ module.exports = new Vue({
|
||||
}
|
||||
|
||||
if ("log" in e.data) {
|
||||
if (e.data.log.msg === "Switch not found") {
|
||||
this.$broadcast("probing_failed");
|
||||
} else {
|
||||
if (e.data.log.msg !== "Switch not found") {
|
||||
this.$broadcast("log", e.data.log);
|
||||
}
|
||||
|
||||
delete e.data.log;
|
||||
}
|
||||
|
||||
// Check for session ID change on controller
|
||||
@@ -329,10 +338,7 @@ module.exports = new Vue({
|
||||
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"
|
||||
) {
|
||||
if (this.hostname && location.hostname !== "localhost") {
|
||||
location.hostname = this.hostname;
|
||||
}
|
||||
|
||||
@@ -340,37 +346,11 @@ module.exports = new Vue({
|
||||
}
|
||||
}
|
||||
|
||||
// Set this to true to get console output of changes to the state
|
||||
const debugStateChanges = false;
|
||||
if (debugStateChanges) {
|
||||
const data = omit(e.data, [
|
||||
"vdd",
|
||||
"vin",
|
||||
"vout",
|
||||
"motor",
|
||||
"temp",
|
||||
"heartbeat",
|
||||
"load1",
|
||||
"load2",
|
||||
"rpi_temp",
|
||||
]);
|
||||
if (Object.keys(data).length > 0) {
|
||||
console.log(JSON.stringify(data, null, 4));
|
||||
}
|
||||
}
|
||||
|
||||
update_object(this.state, e.data, false);
|
||||
|
||||
if (this.state.pw === 0) {
|
||||
Vue.set(this.state, "saw_probe_connected", true);
|
||||
}
|
||||
SvelteComponents.handleControllerStateUpdate(this.state);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
delete this.state.log;
|
||||
|
||||
this.$broadcast("update");
|
||||
};
|
||||
@@ -388,22 +368,24 @@ module.exports = new Vue({
|
||||
};
|
||||
},
|
||||
|
||||
parse_hash: function () {
|
||||
var hash = location.hash.substr(1);
|
||||
parse_hash: function() {
|
||||
const hash = location.hash.substr(1);
|
||||
|
||||
if (!hash.trim().length) {
|
||||
location.hash = "control";
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = hash.split(":");
|
||||
const parts = hash.split(":");
|
||||
|
||||
if (parts.length == 2) this.index = parts[1];
|
||||
if (parts.length == 2) {
|
||||
this.index = parts[1];
|
||||
}
|
||||
|
||||
this.currentView = parts[0];
|
||||
},
|
||||
|
||||
save: function () {
|
||||
save: async function() {
|
||||
const selected_tool = this.config.tool["selected-tool"];
|
||||
const saveModbus =
|
||||
selected_tool !== "pwm" &&
|
||||
@@ -420,26 +402,28 @@ module.exports = new Vue({
|
||||
|
||||
this.config["selected-tool-settings"][selected_tool] = settings;
|
||||
|
||||
api
|
||||
.put("config/save", this.config)
|
||||
.done(
|
||||
function (data) {
|
||||
try {
|
||||
await api.put("config/save", this.config);
|
||||
this.modified = false;
|
||||
}.bind(this)
|
||||
)
|
||||
.fail(function (error) {
|
||||
api.alert("Save failed", error);
|
||||
});
|
||||
} 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");
|
||||
close_messages: function(action) {
|
||||
if (action == "stop") {
|
||||
api.put("stop");
|
||||
}
|
||||
|
||||
if (action == "continue") {
|
||||
api.put("unpause");
|
||||
}
|
||||
|
||||
// Acknowledge messages
|
||||
if (this.state.messages.length) {
|
||||
var id = this.state.messages.slice(-1)[0].id;
|
||||
api.put("message/" + id + "/ack");
|
||||
const id = this.state.messages.slice(-1)[0].id;
|
||||
api.put(`message/${id}/ack`);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,65 +1,37 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#axis-control-template',
|
||||
props: ['axes', 'colors', 'enabled', 'adjust', 'step'],
|
||||
|
||||
template: "#axis-control-template",
|
||||
props: [ "axes", "colors", "enabled", "adjust", "step" ],
|
||||
|
||||
methods: {
|
||||
jog: function (axis, ring, direction) {
|
||||
var value = direction * this.value(ring);
|
||||
this.$dispatch(this.step ? 'step' : 'jog', this.axes[axis], value);
|
||||
jog: function(axis, ring, direction) {
|
||||
const value = direction * this.value(ring);
|
||||
this.$dispatch(this.step ? "step" : "jog", this.axes[axis], value);
|
||||
},
|
||||
|
||||
back2zero: function(axis0,axis1) {
|
||||
this.$dispatch('back2zero',this.axes[axis0],this.axes[axis1])
|
||||
this.$dispatch("back2zero",this.axes[axis0],this.axes[axis1]);
|
||||
},
|
||||
|
||||
|
||||
release: function (axis) {
|
||||
if (!this.step) this.$dispatch('jog', this.axes[axis], 0)
|
||||
release: function(axis) {
|
||||
if (!this.step) {
|
||||
this.$dispatch("jog", this.axes[axis], 0);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
value: function (ring) {
|
||||
var adjust = [0.01, 0.1, 1][this.adjust];
|
||||
if (this.step) return adjust * [0.1, 1, 10, 100][ring];
|
||||
return adjust * [0.1, 0.25, 0.5, 1][ring];
|
||||
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];
|
||||
},
|
||||
|
||||
|
||||
text: function (ring) {
|
||||
var value = this.value(ring) * (this.step ? 1 : 100);
|
||||
text: function(ring) {
|
||||
let value = this.value(ring) * (this.step ? 1 : 100);
|
||||
value = parseFloat(value.toFixed(3));
|
||||
return value + (this.step ? '' : '%');
|
||||
return value + (this.step ? "" : "%");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,159 +1,162 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
function is_defined(x) {return typeof x != 'undefined'}
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
props: ['state', 'config'],
|
||||
|
||||
props: [ "state", "config" ],
|
||||
|
||||
computed: {
|
||||
x: function () {return this._compute_axis('x')},
|
||||
y: function () {return this._compute_axis('y')},
|
||||
z: function () {return this._compute_axis('z')},
|
||||
a: function () {return this._compute_axis('a')},
|
||||
b: function () {return this._compute_axis('b')},
|
||||
c: function () {return this._compute_axis('c')},
|
||||
axes: function () {return this._compute_axes()}
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
x: function() {
|
||||
return this._compute_axis("x");
|
||||
},
|
||||
|
||||
y: function() {
|
||||
return this._compute_axis("y");
|
||||
},
|
||||
|
||||
z: function() {
|
||||
return this._compute_axis("z");
|
||||
},
|
||||
|
||||
a: function() {
|
||||
return this._compute_axis("a");
|
||||
},
|
||||
|
||||
b: function() {
|
||||
return this._compute_axis("b");
|
||||
},
|
||||
|
||||
c: function() {
|
||||
return this._compute_axis("c");
|
||||
},
|
||||
|
||||
axes: function() {
|
||||
return this._compute_axes();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
_convert_length: function (value) {
|
||||
return this.state.imperial ? value / 25.4 : value;
|
||||
_convert_length: function(value) {
|
||||
return this.metric
|
||||
? value
|
||||
: value / 25.4;
|
||||
},
|
||||
|
||||
|
||||
_length_str: function (value) {
|
||||
return this._convert_length(value).toLocaleString() +
|
||||
(this.state.imperial ? ' in' : ' mm');
|
||||
_length_str: function(value) {
|
||||
return this._convert_length(value).toLocaleString() + (this.metric ? " mm" : " in");
|
||||
},
|
||||
|
||||
|
||||
_compute_axis: function (axis) {
|
||||
var abs = this.state[axis + 'p'] || 0;
|
||||
var off = this.state['offset_' + axis];
|
||||
var motor_id = this._get_motor_id(axis);
|
||||
var motor = motor_id == -1 ? {} : this.config.motors[motor_id];
|
||||
var enabled = typeof motor.enabled != 'undefined' && motor.enabled;
|
||||
var homingMode = motor['homing-mode']
|
||||
var homed = this.state[motor_id + 'homed'];
|
||||
var min = this.state[motor_id + 'tn'];
|
||||
var max = this.state[motor_id + 'tm'];
|
||||
var dim = max - min;
|
||||
var pathMin = this.state['path_min_' + axis];
|
||||
var pathMax = this.state['path_max_' + axis];
|
||||
var pathDim = pathMax - pathMin;
|
||||
var under = pathMin + off < min;
|
||||
var over = max < pathMax + off;
|
||||
var klass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis;
|
||||
var state = 'UNHOMED';
|
||||
var icon = 'question-circle';
|
||||
var fault = this.state[motor_id + 'df'] & 0x1f;
|
||||
var shutdown = this.state.power_shutdown;
|
||||
var title;
|
||||
var ticon = 'question-circle';
|
||||
var tstate = 'NO FILE';
|
||||
var toolmsg;
|
||||
var tklass = (homed ? 'homed' : 'unhomed') + ' axis-' + axis;
|
||||
_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';
|
||||
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';
|
||||
|
||||
tstate = "NO FIT";
|
||||
tklass += " error";
|
||||
ticon = "ban";
|
||||
} else {
|
||||
|
||||
if (over || under) {
|
||||
tstate = over ? 'OVER' : 'UNDER';
|
||||
tklass += ' warn';
|
||||
ticon = 'exclamation-circle';
|
||||
tstate = over ? "OVER" : "UNDER";
|
||||
tklass += " warn";
|
||||
ticon = "exclamation-circle";
|
||||
} else {
|
||||
tstate = 'OK';
|
||||
ticon = 'check-circle';
|
||||
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.';
|
||||
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.`,
|
||||
].join(" ");
|
||||
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.`
|
||||
].join(" ");
|
||||
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.';
|
||||
switch (tstate) {
|
||||
case "OVER":
|
||||
toolmsg = [
|
||||
`Caution: The current tool path file would move`,
|
||||
`${this._length_str(pathMax + off - max)}`,
|
||||
`above axis limit with the current offset.`
|
||||
].join(" ");
|
||||
break;
|
||||
|
||||
case 'UNDER':
|
||||
toolmsg = 'Caution: The current tool path file would move ' +
|
||||
this._length_str(min - pathMin - off) + ' below limit with the current offset.';
|
||||
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) + '.';
|
||||
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.';
|
||||
toolmsg = `Tool path ${axis} dimensions OK.`;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
pos: abs - off,
|
||||
abs: abs,
|
||||
@@ -176,57 +179,76 @@ module.exports = {
|
||||
tstate: tstate,
|
||||
toolmsg: toolmsg,
|
||||
tklass: tklass
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
_get_motor_id: function (axis) {
|
||||
for (var i = 0; i < this.config.motors.length; i++) {
|
||||
var motor = this.config.motors[i];
|
||||
if (motor.axis.toLowerCase() == axis) return i;
|
||||
_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;
|
||||
|
||||
_compute_axes: function () {
|
||||
var homed = false;
|
||||
for (const name of "xyzabc") {
|
||||
const axis = this[name];
|
||||
|
||||
for (var name of 'xyzabc') {
|
||||
var axis = this[name];
|
||||
if (!axis.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!axis.homed) {
|
||||
homed = false; break;
|
||||
}
|
||||
|
||||
if (!axis.enabled) continue
|
||||
if (!axis.homed) {homed = false; break}
|
||||
homed = true;
|
||||
}
|
||||
|
||||
var error = false;
|
||||
var warn = false;
|
||||
let error = false;
|
||||
let warn = false;
|
||||
|
||||
if (homed)
|
||||
for (name of 'xyzabc') {
|
||||
axis = this[name];
|
||||
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;
|
||||
if (!axis.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var klass = homed ? 'homed' : 'unhomed';
|
||||
if (error) klass += ' error';
|
||||
else if (warn) klass += ' warn';
|
||||
if (axis.klass.indexOf("error") != -1) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
if(!homed && this.ask_home)
|
||||
{
|
||||
this.ask_home_msg = 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,87 +1,72 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
function _msg_equal(a, b) {
|
||||
return a.level == b.level && a.source == b.source && a.where == b.where &&
|
||||
a.msg == b.msg;
|
||||
return a.level == b.level
|
||||
&& a.source == b.source
|
||||
&& a.where == b.where
|
||||
&& a.msg == b.msg;
|
||||
}
|
||||
|
||||
|
||||
// Shared among all instances
|
||||
var messages = [];
|
||||
|
||||
const messages = [];
|
||||
|
||||
module.exports = {
|
||||
template: '#console-template',
|
||||
template: "#console-template",
|
||||
|
||||
|
||||
data: function () {
|
||||
return {messages: messages}
|
||||
data: function() {
|
||||
return {
|
||||
messages
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
events: {
|
||||
log: function (msg) {
|
||||
log: function(msg) {
|
||||
// There may be multiple instances of this module so ignore messages
|
||||
// that have already been processed.
|
||||
if (msg.logged) return;
|
||||
if (msg.logged) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.logged = true;
|
||||
|
||||
// Make sure we have a message level
|
||||
msg.level = msg.level || 'info';
|
||||
msg.level = msg.level || "info";
|
||||
|
||||
// Add to message log and count and collapse repeats
|
||||
var repeat = messages.length && _msg_equal(msg, messages[0]);
|
||||
if (repeat) messages[0].repeat++;
|
||||
else {
|
||||
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();
|
||||
while (256 < messages.length) {
|
||||
messages.pop();
|
||||
}
|
||||
}
|
||||
msg.ts = Date.now();
|
||||
|
||||
// Write message to browser console for debugging
|
||||
var text = JSON.stringify(msg);
|
||||
if (msg.level == 'error' || msg.level == 'critical') console.error(text);
|
||||
else if (msg.level == 'warning') console.warn(text);
|
||||
else if (msg.level == 'debug' && console.debug) console.debug(text);
|
||||
else console.log(text);
|
||||
const text = JSON.stringify(msg);
|
||||
if (msg.level == "error" || msg.level == "critical") {
|
||||
console.error(text);
|
||||
} else if (msg.level == "warning") {
|
||||
console.warn(text);
|
||||
} else if (msg.level == "debug" && console.debug) {
|
||||
console.debug(text);
|
||||
} else {
|
||||
console.log(text);
|
||||
}
|
||||
|
||||
// Event on errors
|
||||
if (msg.level == 'error' || msg.level == 'critical')
|
||||
this.$dispatch('error', msg);
|
||||
if (msg.level == "error" || msg.level == "critical") {
|
||||
this.$dispatch("error", msg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
clear: function () {messages.splice(0, messages.length);},
|
||||
clear: function() {
|
||||
messages.splice(0, messages.length);
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,69 +1,49 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
Copyright 2018. Buildbotics LLC
|
||||
All Rights Reserved.
|
||||
module.exports = function(prefix) {
|
||||
if (typeof prefix == "undefined") {
|
||||
prefix = "";
|
||||
}
|
||||
|
||||
For information regarding this software email:
|
||||
Joseph Coffland
|
||||
joseph@buildbotics.com
|
||||
const cookie = {
|
||||
get: function(name, defaultValue) {
|
||||
const decodedCookie = decodeURIComponent(document.cookie);
|
||||
const ca = decodedCookie.split(";");
|
||||
name = `${prefix + name}=`;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
set: function (name, value, days) {
|
||||
var offset = 2147483647; // Max value
|
||||
if (typeof days != 'undefined') offset = days * 24 * 60 * 60 * 1000;
|
||||
var d = new Date();
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + offset);
|
||||
var expires = 'expires=' + d.toUTCString();
|
||||
document.cookie = prefix + name + '=' + value + ';' + expires + ';path=/';
|
||||
const expires = `expires=${d.toUTCString()}`;
|
||||
document.cookie = `${prefix}${name}=${value};${expires};path=/`;
|
||||
},
|
||||
|
||||
|
||||
set_bool: function (name, value) {
|
||||
cookie.set(name, value ? 'true' : 'false');
|
||||
set_bool: function(name, value) {
|
||||
cookie.set(name, value ? "true" : "false");
|
||||
},
|
||||
|
||||
|
||||
get_bool: function (name, defaultValue) {
|
||||
return cookie.get(name, defaultValue ? 'true' : 'false') == 'true';
|
||||
}
|
||||
get_bool: function(name, defaultValue) {
|
||||
return cookie.get(name, defaultValue ? "true" : "false") == "true";
|
||||
}
|
||||
};
|
||||
|
||||
return cookie;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,97 +1,75 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
var api = require('./api');
|
||||
|
||||
|
||||
var entityMap = {
|
||||
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
||||
'/': '/', '`': '`', '=': '='}
|
||||
"use strict";
|
||||
|
||||
const entityMap = {
|
||||
"&": "&", "<": "<", ">": ">", '"': """, "'": "'",
|
||||
"/": "/", "`": "`", "=": "=" };
|
||||
|
||||
function escapeHTML(s) {
|
||||
return s.replace(/[&<>"'`=\/]/g, function (c) {return entityMap[c]})
|
||||
return s.replace(/[&<>"'`=\\/]/g, function(c) {
|
||||
return entityMap[c];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
template: '#gcode-viewer-template',
|
||||
template: "#gcode-viewer-template",
|
||||
|
||||
|
||||
data: function () {
|
||||
data: function() {
|
||||
return {
|
||||
empty: true,
|
||||
file: '',
|
||||
line: -1,
|
||||
scrolling: false
|
||||
file: "",
|
||||
line: -1
|
||||
};
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
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 () {
|
||||
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}
|
||||
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')
|
||||
attached: function() {
|
||||
if (typeof this.clusterize != "undefined") {
|
||||
this.clusterize.refresh(true);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
load: async function(file) {
|
||||
if (file == this.file) return;
|
||||
if (file == this.file) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clear();
|
||||
this.file = file;
|
||||
|
||||
if (!file) return;
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/file/${file}?${Math.random()}`);
|
||||
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']);
|
||||
this.clusterize.update([ "File is large - gcode view disabled" ]);
|
||||
} else {
|
||||
const lines = escapeHTML(text.trimRight())
|
||||
.split(/[\r\n]/)
|
||||
@@ -105,64 +83,72 @@ module.exports = {
|
||||
Vue.nextTick(this.update_line);
|
||||
},
|
||||
|
||||
|
||||
clear: function () {
|
||||
clear: function() {
|
||||
this.empty = true;
|
||||
this.file = '';
|
||||
this.file = "";
|
||||
this.line = -1;
|
||||
this.clusterize.clear();
|
||||
},
|
||||
|
||||
reload: function(file) {
|
||||
if (file != this.file) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(" ");
|
||||
}
|
||||
|
||||
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');
|
||||
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;
|
||||
if (typeof line != "undefined") {
|
||||
if (this.line == line) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.line = line;
|
||||
} else {
|
||||
line = this.line;
|
||||
}
|
||||
|
||||
} else line = this.line;
|
||||
const totalLines = this.clusterize.getRowsAmount();
|
||||
|
||||
var totalLines = this.clusterize.getRowsAmount();
|
||||
if (line <= 0) {
|
||||
line = 1;
|
||||
}
|
||||
|
||||
if (line <= 0) line = 1;
|
||||
if (totalLines < line) line = totalLines;
|
||||
if (totalLines < line) {
|
||||
line = totalLines;
|
||||
}
|
||||
|
||||
var e = $(this.$el).find('.clusterize-scroll');
|
||||
const scroll = this.$el.querySelector(".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);
|
||||
const lineHeight = scroll.scrollHeight / totalLines;
|
||||
const linesPerPage = Math.floor(scroll.clientHeight / lineHeight);
|
||||
const 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)
|
||||
})
|
||||
}
|
||||
}
|
||||
scroll.scrollTop = target * lineHeight;
|
||||
|
||||
Vue.nextTick(this.highlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,66 +1,45 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
var modbus = require('./modbus.js');
|
||||
"use strict";
|
||||
|
||||
const modbus = require("./modbus.js");
|
||||
|
||||
module.exports = {
|
||||
template: '#indicators-template',
|
||||
props: ['state'],
|
||||
|
||||
template: "#indicators-template",
|
||||
props: [ "state" ],
|
||||
|
||||
computed: {
|
||||
modbus_status: function () {return modbus.status_to_string(this.state.mx)},
|
||||
modbus_status: function() {
|
||||
return modbus.status_to_string(this.state.mx);
|
||||
},
|
||||
|
||||
sense_error: function() {
|
||||
let error = "";
|
||||
|
||||
sense_error: function () {
|
||||
var 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';
|
||||
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";
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
is_motor_enabled: function (motor) {
|
||||
return typeof this.state[motor + 'me'] != 'undefined' &&
|
||||
this.state[motor + 'me'];
|
||||
is_motor_enabled: function(motor) {
|
||||
return typeof this.state[`${motor}me`] != "undefined" && this.state[`${motor}me`];
|
||||
},
|
||||
|
||||
|
||||
get_min_pin: function (motor) {
|
||||
get_min_pin: function(motor) {
|
||||
switch (motor) {
|
||||
case 0: return 3;
|
||||
case 1: return 5;
|
||||
@@ -69,8 +48,7 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
get_max_pin: function (motor) {
|
||||
get_max_pin: function(motor) {
|
||||
switch (motor) {
|
||||
case 0: return 4;
|
||||
case 1: return 8;
|
||||
@@ -79,29 +57,38 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
motor_fault_class: function(motor, bit) {
|
||||
if (typeof motor == "undefined") {
|
||||
const status = this.state["fa"];
|
||||
|
||||
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')
|
||||
if (typeof status == "undefined") {
|
||||
return "fa-question";
|
||||
}
|
||||
|
||||
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';
|
||||
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`;
|
||||
}
|
||||
|
||||
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');
|
||||
this.$dispatch("send", cmd);
|
||||
} else {
|
||||
this.$dispatch("send", `\\$${motor}df=0`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,177 +1,155 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: "#io-indicator-template",
|
||||
props: ['name', 'state'],
|
||||
|
||||
props: [ "name", "state" ],
|
||||
|
||||
computed: {
|
||||
klass: function () {
|
||||
if (this.name == 'min-switch-0') return this.get_motor_min_class(0);
|
||||
if (this.name == 'min-switch-1') return this.get_motor_min_class(1);
|
||||
if (this.name == 'min-switch-2') return this.get_motor_min_class(2);
|
||||
if (this.name == 'min-switch-3') return this.get_motor_min_class(3);
|
||||
if (this.name == 'max-switch-0') return this.get_motor_max_class(0);
|
||||
if (this.name == 'max-switch-1') return this.get_motor_max_class(1);
|
||||
if (this.name == 'max-switch-2') return this.get_motor_max_class(2);
|
||||
if (this.name == 'max-switch-3') return this.get_motor_max_class(3);
|
||||
if (this.name == 'estop') return this.get_input_class('ew', 'et');
|
||||
if (this.name == 'probe') return this.get_input_class('pw', 'pt');
|
||||
if (this.name == 'load-1') return this.get_output_class('1');
|
||||
if (this.name == 'load-2') return this.get_output_class('2');
|
||||
if (this.name == 'fault') return this.get_output_class('f');
|
||||
if (this.name == 'tool-enable-mode') return this.get_output_class('e');
|
||||
if (this.name == 'tool-direction-mode') return this.get_output_class('d');
|
||||
},
|
||||
|
||||
|
||||
tooltip: function () {
|
||||
if (this.name == 'min-switch-0') return this.get_motor_min_tooltip(0);
|
||||
if (this.name == 'min-switch-1') return this.get_motor_min_tooltip(1);
|
||||
if (this.name == 'min-switch-2') return this.get_motor_min_tooltip(2);
|
||||
if (this.name == 'min-switch-3') return this.get_motor_min_tooltip(3);
|
||||
if (this.name == 'max-switch-0') return this.get_motor_max_tooltip(0);
|
||||
if (this.name == 'max-switch-1') return this.get_motor_max_tooltip(1);
|
||||
if (this.name == 'max-switch-2') return this.get_motor_max_tooltip(2);
|
||||
if (this.name == 'max-switch-3') return this.get_motor_max_tooltip(3);
|
||||
if (this.name == 'estop') return this.get_input_tooltip('ew', 'et');
|
||||
if (this.name == 'probe') return this.get_input_tooltip('pw', 'pt');
|
||||
if (this.name == 'load-1') return this.get_output_tooltip('1');
|
||||
if (this.name == 'load-2') return this.get_output_tooltip('2');
|
||||
if (this.name == 'fault') return this.get_output_tooltip('f');
|
||||
if (this.name == 'tool-direction-mode')
|
||||
return this.get_output_tooltip('d');
|
||||
if (this.name == 'tool-enable-mode')
|
||||
return this.get_output_tooltip('e');
|
||||
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");
|
||||
}
|
||||
},
|
||||
|
||||
tooltip: function() {
|
||||
switch (this.name) {
|
||||
case "min-switch-0": return this.get_motor_min_tooltip(0);
|
||||
case "min-switch-1": return this.get_motor_min_tooltip(1);
|
||||
case "min-switch-2": return this.get_motor_min_tooltip(2);
|
||||
case "min-switch-3": return this.get_motor_min_tooltip(3);
|
||||
case "max-switch-0": return this.get_motor_max_tooltip(0);
|
||||
case "max-switch-1": return this.get_motor_max_tooltip(1);
|
||||
case "max-switch-2": return this.get_motor_max_tooltip(2);
|
||||
case "max-switch-3": return this.get_motor_max_tooltip(3);
|
||||
case "estop": return this.get_input_tooltip("ew", "et");
|
||||
case "probe": return this.get_input_tooltip("pw", "pt");
|
||||
case "load-1": return this.get_output_tooltip("1");
|
||||
case "load-2": return this.get_output_tooltip("2");
|
||||
case "fault": return this.get_output_tooltip("f");
|
||||
case "tool-direction-mode": return this.get_output_tooltip("d");
|
||||
case "tool-enable-mode": return this.get_output_tooltip("e");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
get_io_state_class: function (active, state) {
|
||||
if (typeof active == 'undefined' || typeof state == 'undefined')
|
||||
return 'fa-exclamation-triangle warn';
|
||||
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';
|
||||
if (state == 2) {
|
||||
return "fa-circle-o";
|
||||
}
|
||||
|
||||
return (state ? 'fa-plus-circle' : 'fa-minus-circle') + ' ' +
|
||||
(active ? 'active' : 'inactive');
|
||||
const icon = state ? "fa-plus-circle" : "fa-minus-circle";
|
||||
return `${icon} ${active ? "active" : "inactive"}`;
|
||||
},
|
||||
|
||||
get_input_active: function(stateCode, typeCode) {
|
||||
const type = this.state[typeCode];
|
||||
const state = this.state[stateCode];
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if (type == 1) return !state; // Normally open
|
||||
else if (type == 2) return state; // Normally closed
|
||||
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
get_input_class: function (stateCode, typeCode) {
|
||||
return this.get_io_state_class(this.get_input_active(stateCode, typeCode),
|
||||
this.state[stateCode]);
|
||||
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_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_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_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";
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
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;
|
||||
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";
|
||||
}
|
||||
|
||||
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];
|
||||
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`];
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
var active = this.state[output + 'oa'];
|
||||
var state = this.state[output + 'os'];
|
||||
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_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');
|
||||
get_motor_max_tooltip: function(motor) {
|
||||
return this.get_input_tooltip(`${motor}xw`, `${motor}xs`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,42 +1,13 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#io-view-template',
|
||||
props: ['config', 'template', 'state'],
|
||||
|
||||
template: "#io-view-template",
|
||||
props: [ "config", "template", "state" ],
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
this.$dispatch('config-changed');
|
||||
"input-changed": function() {
|
||||
this.$dispatch("config-changed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
185
src/js/main.js
185
src/js/main.js
@@ -1,120 +1,118 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict';
|
||||
|
||||
"use strict";
|
||||
|
||||
function cookie_get(name) {
|
||||
var decodedCookie = decodeURIComponent(document.cookie);
|
||||
var ca = decodedCookie.split(';');
|
||||
name = name + '=';
|
||||
const decodedCookie = decodeURIComponent(document.cookie);
|
||||
const ca = decodedCookie.split(";");
|
||||
name = `${name}=`;
|
||||
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1);
|
||||
if (!c.indexOf(name)) return c.substring(name.length, c.length);
|
||||
for (let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == " ") {
|
||||
c = c.substring(1);
|
||||
}
|
||||
|
||||
if (!c.indexOf(name)) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function cookie_set(name, value, days) {
|
||||
var d = new Date();
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000);
|
||||
var expires = 'expires=' + d.toUTCString();
|
||||
document.cookie = name + '=' + value + ';' + expires + ';path=/';
|
||||
const expires = `expires=${d.toUTCString()}`;
|
||||
document.cookie = `${name}=${value};${expires};path=/`;
|
||||
}
|
||||
|
||||
|
||||
var uuid_chars =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+';
|
||||
|
||||
const uuid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+";
|
||||
|
||||
function uuid(length) {
|
||||
if (typeof length == 'undefined') length = 52;
|
||||
if (typeof length == "undefined") {
|
||||
length = 52;
|
||||
}
|
||||
|
||||
var s = '';
|
||||
for (var i = 0; i < length; i++)
|
||||
let s = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
s += uuid_chars[Math.floor(Math.random() * uuid_chars.length)];
|
||||
}
|
||||
|
||||
return s
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
$(function() {
|
||||
if (typeof cookie_get('client-id') == 'undefined')
|
||||
cookie_set('client-id', uuid(), 10000);
|
||||
|
||||
// Vue debugging
|
||||
Vue.config.debug = true;
|
||||
//Vue.util.warn = function (msg) {console.debug('[Vue warn]: ' + msg)}
|
||||
window.onload = function() {
|
||||
if (typeof cookie_get("client-id") == "undefined") {
|
||||
cookie_set("client-id", uuid(), 10000);
|
||||
}
|
||||
|
||||
// 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.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";
|
||||
}
|
||||
|
||||
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("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("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("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("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;
|
||||
Vue.filter("time", function(value, precision) {
|
||||
if (isNaN(value)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var MIN = 60;
|
||||
var HR = MIN * 60;
|
||||
var DAY = HR * 24;
|
||||
var parts = [];
|
||||
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));
|
||||
@@ -129,19 +127,22 @@ $(function() {
|
||||
if (MIN <= value) {
|
||||
parts.push(Math.floor(value / MIN));
|
||||
value %= MIN;
|
||||
|
||||
} else parts.push(0);
|
||||
} else {
|
||||
parts.push(0);
|
||||
}
|
||||
|
||||
parts.push(value);
|
||||
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
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];
|
||||
if (i && parts[i] < 10) {
|
||||
parts[i] = `0${parts[i]}`;
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join(':');
|
||||
return parts.join(":");
|
||||
});
|
||||
|
||||
// Vue app
|
||||
require('./app');
|
||||
});
|
||||
require("./app");
|
||||
};
|
||||
|
||||
@@ -1,35 +1,7 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#message-template',
|
||||
template: "#message-template",
|
||||
|
||||
props: {
|
||||
show: {
|
||||
@@ -44,4 +16,4 @@ module.exports = {
|
||||
twoWay: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,48 +1,20 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '#modbus-reg-view-template',
|
||||
props: ['index', 'model', 'template', 'enable'],
|
||||
|
||||
template: "#modbus-reg-view-template",
|
||||
props: [ "index", "model", "template", "enable" ],
|
||||
|
||||
computed: {
|
||||
has_user_value: function () {
|
||||
var type = this.model['reg-type'];
|
||||
return type.indexOf('write') != -1 || type.indexOf('fixed') != -1;
|
||||
has_user_value: function() {
|
||||
const type = this.model["reg-type"];
|
||||
return type.indexOf("write") != -1 || type.indexOf("fixed") != -1;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
change: function () {this.$dispatch('input-changed')}
|
||||
change: function() {
|
||||
this.$dispatch("input-changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,35 +1,7 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
// Must match modbus.c
|
||||
var exports = {
|
||||
const constants = {
|
||||
DISCONNECTED: 0,
|
||||
OK: 1,
|
||||
CRC: 2,
|
||||
@@ -37,15 +9,15 @@ var exports = {
|
||||
TIMEDOUT: 4
|
||||
};
|
||||
|
||||
|
||||
exports.status_to_string =
|
||||
function (status) {
|
||||
if (status == exports.OK) return 'Ok';
|
||||
if (status == exports.CRC) return 'CRC error';
|
||||
if (status == exports.INVALID) return 'Invalid response';
|
||||
if (status == exports.TIMEDOUT) return 'Timedout';
|
||||
return 'Disconnected';
|
||||
module.exports = {
|
||||
...constants,
|
||||
status_to_string: function(status) {
|
||||
switch (status) {
|
||||
case constants.OK: return "Ok";
|
||||
case constants.CRC: return "CRC error";
|
||||
case constants.INVALID: return "Invalid response";
|
||||
case constants.TIMEDOUT: return "Timedout";
|
||||
default: return "Disconnected";
|
||||
}
|
||||
|
||||
|
||||
module.exports = exports;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,127 +1,108 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
template: '#motor-view-template',
|
||||
props: ['index', 'config', 'template', 'state'],
|
||||
|
||||
template: "#motor-view-template",
|
||||
props: [ "index", "config", "template", "state" ],
|
||||
|
||||
computed: {
|
||||
metric: function () {return this.$root.metric()},
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
|
||||
is_slave: function () {
|
||||
for (var i = 0; i < this.index; i++)
|
||||
if (this.motor.axis == this.config.motors[i].axis)
|
||||
is_slave: function() {
|
||||
for (let i = 0; i < this.index; i++) {
|
||||
if (this.motor.axis == this.config.motors[i].axis) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
motor: function () {return this.config.motors[this.index]},
|
||||
|
||||
|
||||
invalidMaxVelocity: function () {
|
||||
return this.maxMaxVelocity < this.motor['max-velocity'];
|
||||
motor: function() {
|
||||
return this.config.motors[this.index];
|
||||
},
|
||||
|
||||
|
||||
maxMaxVelocity: function () {
|
||||
return 1 * (15 * this.umPerStep / this.motor['microsteps']).toFixed(3);
|
||||
invalidMaxVelocity: function() {
|
||||
return this.maxMaxVelocity < this.motor["max-velocity"];
|
||||
},
|
||||
|
||||
|
||||
ustepPerSec: function () {
|
||||
return this.rpm * this.stepsPerRev * this.motor['microsteps'] / 60;
|
||||
maxMaxVelocity: function() {
|
||||
return 1 * (15 * this.umPerStep / this.motor["microsteps"]).toFixed(3);
|
||||
},
|
||||
|
||||
|
||||
rpm: function () {
|
||||
return 1000 * this.motor['max-velocity'] / this.motor['travel-per-rev'];
|
||||
ustepPerSec: function() {
|
||||
return this.rpm * this.stepsPerRev * this.motor["microsteps"] / 60;
|
||||
},
|
||||
|
||||
|
||||
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
|
||||
rpm: function() {
|
||||
return 1000 * this.motor["max-velocity"] / this.motor["travel-per-rev"];
|
||||
},
|
||||
|
||||
gForce: function() {
|
||||
return this.motor["max-accel"] * 0.0283254504;
|
||||
},
|
||||
|
||||
milPerStep: function () {return this.umPerStep / 25.4},
|
||||
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'];
|
||||
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'];
|
||||
const 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;
|
||||
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() {
|
||||
var ustep = this.motor['stall-microstep'];
|
||||
const ustep = this.motor["stall-microstep"];
|
||||
return this.stallRPM * this.stepsPerRev * ustep / 60;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
Vue.nextTick(function () {
|
||||
"input-changed": function() {
|
||||
Vue.nextTick(function() {
|
||||
// Limit max-velocity
|
||||
if (this.invalidMaxVelocity)
|
||||
if (this.invalidMaxVelocity) {
|
||||
this.$set('motor["max-velocity"]', this.maxMaxVelocity);
|
||||
}
|
||||
|
||||
//Limit stall-velocity
|
||||
if(this.invalidStallVelocity)
|
||||
if (this.invalidStallVelocity) {
|
||||
this.$set('motor["search-velocity"]', this.maxStallVelocity);
|
||||
}
|
||||
|
||||
this.$dispatch('config-changed');
|
||||
}.bind(this))
|
||||
this.$dispatch("config-changed");
|
||||
}.bind(this));
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -129,8 +110,11 @@ module.exports = {
|
||||
|
||||
methods: {
|
||||
show: function(name, templ) {
|
||||
if(templ.hmodes == undefined) return true;
|
||||
return templ.hmodes.indexOf(this.motor['homing-mode']) != -1;
|
||||
if (templ.hmodes == undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return templ.hmodes.indexOf(this.motor["homing-mode"]) != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
431
src/js/orbit.js
431
src/js/orbit.js
@@ -7,7 +7,7 @@
|
||||
* @author jcoffland / https://buildbotics.com/
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
"use strict";
|
||||
|
||||
// This set of controls performs orbiting, dollying (zooming), and panning.
|
||||
// Unlike TrackballControls, it maintains the "up" direction object.up
|
||||
@@ -17,8 +17,42 @@
|
||||
// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
|
||||
// Pan - right mouse, or arrow keys / touch: two-finger move
|
||||
|
||||
const OrbitControls = function(object, domElement) {
|
||||
// internals
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const scope = this;
|
||||
|
||||
const changeEvent = { type: "change" };
|
||||
const startEvent = { type: "start" };
|
||||
const endEvent = { type: "end" };
|
||||
|
||||
const STATE = {
|
||||
NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4
|
||||
};
|
||||
|
||||
let state = STATE.NONE;
|
||||
const EPS = 0.000001;
|
||||
|
||||
// current position in spherical coordinates
|
||||
const spherical = new THREE.Spherical();
|
||||
const sphericalDelta = new THREE.Spherical();
|
||||
|
||||
let scale = 1;
|
||||
const panOffset = new THREE.Vector3();
|
||||
let zoomChanged = false;
|
||||
|
||||
const rotateStart = new THREE.Vector2();
|
||||
const rotateEnd = new THREE.Vector2();
|
||||
const rotateDelta = new THREE.Vector2();
|
||||
|
||||
const panStart = new THREE.Vector2();
|
||||
const panEnd = new THREE.Vector2();
|
||||
const panDelta = new THREE.Vector2();
|
||||
|
||||
const dollyStart = new THREE.Vector2();
|
||||
const dollyEnd = new THREE.Vector2();
|
||||
const dollyDelta = new THREE.Vector2();
|
||||
|
||||
var OrbitControls = function (object, domElement) {
|
||||
this.object = object;
|
||||
this.domElement = domElement != undefined ? domElement : document;
|
||||
|
||||
@@ -72,7 +106,7 @@ var OrbitControls = function (object, domElement) {
|
||||
this.enableKeys = true;
|
||||
|
||||
// The four arrow keys
|
||||
this.keys = {LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40};
|
||||
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
|
||||
|
||||
// Mouse buttons
|
||||
this.mouseButtons = {
|
||||
@@ -85,18 +119,21 @@ var OrbitControls = function (object, domElement) {
|
||||
this.zoom0 = this.object.zoom;
|
||||
|
||||
// public methods
|
||||
this.getPolarAngle = function () {return spherical.phi}
|
||||
this.getAzimuthalAngle = function () {return spherical.theta}
|
||||
this.getPolarAngle = function() {
|
||||
return spherical.phi;
|
||||
};
|
||||
|
||||
this.getAzimuthalAngle = function() {
|
||||
return spherical.theta;
|
||||
};
|
||||
|
||||
this.saveState = function () {
|
||||
this.saveState = function() {
|
||||
scope.target0.copy(scope.target);
|
||||
scope.position0.copy(scope.object.position);
|
||||
scope.zoom0 = scope.object.zoom;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.reset = function () {
|
||||
this.reset = function() {
|
||||
scope.target.copy(scope.target0);
|
||||
scope.object.position.copy(scope.position0);
|
||||
scope.object.zoom = scope.zoom0;
|
||||
@@ -106,22 +143,20 @@ var OrbitControls = function (object, domElement) {
|
||||
scope.update();
|
||||
|
||||
state = STATE.NONE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.update = function () {
|
||||
var offset = new THREE.Vector3();
|
||||
this.update = function() {
|
||||
const offset = new THREE.Vector3();
|
||||
|
||||
// so camera.up is the orbit axis
|
||||
var quat = new THREE.Quaternion()
|
||||
.setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0));
|
||||
var quatInverse = quat.clone().inverse();
|
||||
const quat = new THREE.Quaternion().setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0));
|
||||
const quatInverse = quat.clone().inverse();
|
||||
|
||||
var lastPosition = new THREE.Vector3();
|
||||
var lastQuaternion = new THREE.Quaternion();
|
||||
const lastPosition = new THREE.Vector3();
|
||||
const lastQuaternion = new THREE.Quaternion();
|
||||
|
||||
return function update() {
|
||||
var position = scope.object.position;
|
||||
const position = scope.object.position;
|
||||
|
||||
offset.copy(position).sub(scope.target);
|
||||
|
||||
@@ -131,28 +166,24 @@ var OrbitControls = function (object, domElement) {
|
||||
// angle from z-axis around y-axis
|
||||
spherical.setFromVector3(offset);
|
||||
|
||||
if (scope.autoRotate && state == STATE.NONE)
|
||||
if (scope.autoRotate && state == STATE.NONE) {
|
||||
rotateLeft(getAutoRotationAngle());
|
||||
}
|
||||
|
||||
spherical.theta += sphericalDelta.theta;
|
||||
spherical.phi += sphericalDelta.phi;
|
||||
|
||||
// restrict theta to be between desired limits
|
||||
spherical.theta =
|
||||
Math.max(scope.minAzimuthAngle,
|
||||
Math.min(scope.maxAzimuthAngle, spherical.theta));
|
||||
spherical.theta = Math.max(scope.minAzimuthAngle, Math.min(scope.maxAzimuthAngle, spherical.theta));
|
||||
|
||||
// restrict phi to be between desired limits
|
||||
spherical.phi =
|
||||
Math.max(scope.minPolarAngle,
|
||||
Math.min(scope.maxPolarAngle, spherical.phi));
|
||||
spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi));
|
||||
|
||||
spherical.makeSafe();
|
||||
spherical.radius *= scale;
|
||||
|
||||
// restrict radius to be between desired limits
|
||||
spherical.radius =
|
||||
Math.max(10, Math.min(scope.object.far * 0.8, spherical.radius));
|
||||
spherical.radius = Math.max(10, Math.min(scope.object.far * 0.8, spherical.radius));
|
||||
|
||||
// move target to panned location
|
||||
scope.target.add(panOffset);
|
||||
@@ -178,9 +209,10 @@ var OrbitControls = function (object, domElement) {
|
||||
// update condition is:
|
||||
// min(camera displacement, camera rotation in radians)^2 > EPS
|
||||
// using small-angle approximation cos(x/2) = 1 - x^2 / 8
|
||||
if (zoomChanged || scale != 1 ||
|
||||
lastPosition.distanceToSquared(scope.object.position) > EPS ||
|
||||
8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) {
|
||||
if (zoomChanged
|
||||
|| scale != 1
|
||||
|| lastPosition.distanceToSquared(scope.object.position) > EPS
|
||||
|| 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) {
|
||||
|
||||
scope.dispatchEvent(changeEvent);
|
||||
|
||||
@@ -193,187 +225,150 @@ var OrbitControls = function (object, domElement) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
this.dispose = function () {
|
||||
scope.domElement.removeEventListener('contextmenu', onContextMenu, false);
|
||||
scope.domElement.removeEventListener('mousedown', onMouseDown, false);
|
||||
scope.domElement.removeEventListener('wheel', onMouseWheel, false);
|
||||
scope.domElement.removeEventListener('touchstart', onTouchStart, false);
|
||||
scope.domElement.removeEventListener('touchend', onTouchEnd, false);
|
||||
scope.domElement.removeEventListener('touchmove', onTouchMove, false);
|
||||
document.removeEventListener('mousemove', onMouseMove, false);
|
||||
document.removeEventListener('mouseup', onMouseUp, false);
|
||||
window.removeEventListener('keydown', onKeyDown, false);
|
||||
}
|
||||
|
||||
|
||||
// internals
|
||||
var scope = this;
|
||||
|
||||
var changeEvent = {type: 'change'};
|
||||
var startEvent = {type: 'start'};
|
||||
var endEvent = {type: 'end'};
|
||||
|
||||
var STATE = {
|
||||
NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY_PAN: 4
|
||||
};
|
||||
}();
|
||||
|
||||
var state = STATE.NONE;
|
||||
var EPS = 0.000001;
|
||||
|
||||
// current position in spherical coordinates
|
||||
var spherical = new THREE.Spherical();
|
||||
var sphericalDelta = new THREE.Spherical();
|
||||
|
||||
var scale = 1;
|
||||
var panOffset = new THREE.Vector3();
|
||||
var zoomChanged = false;
|
||||
|
||||
var rotateStart = new THREE.Vector2();
|
||||
var rotateEnd = new THREE.Vector2();
|
||||
var rotateDelta = new THREE.Vector2();
|
||||
|
||||
var panStart = new THREE.Vector2();
|
||||
var panEnd = new THREE.Vector2();
|
||||
var panDelta = new THREE.Vector2();
|
||||
|
||||
var dollyStart = new THREE.Vector2();
|
||||
var dollyEnd = new THREE.Vector2();
|
||||
var dollyDelta = new THREE.Vector2();
|
||||
|
||||
this.dispose = function() {
|
||||
scope.domElement.removeEventListener("contextmenu", onContextMenu, false);
|
||||
scope.domElement.removeEventListener("mousedown", onMouseDown, false);
|
||||
scope.domElement.removeEventListener("wheel", onMouseWheel, false);
|
||||
scope.domElement.removeEventListener("touchstart", onTouchStart, false);
|
||||
scope.domElement.removeEventListener("touchend", onTouchEnd, false);
|
||||
scope.domElement.removeEventListener("touchmove", onTouchMove, false);
|
||||
document.removeEventListener("mousemove", onMouseMove, false);
|
||||
document.removeEventListener("mouseup", onMouseUp, false);
|
||||
window.removeEventListener("keydown", onKeyDown, false);
|
||||
};
|
||||
|
||||
function getAutoRotationAngle() {
|
||||
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
|
||||
}
|
||||
|
||||
function getZoomScale() {
|
||||
return Math.pow(0.95, scope.zoomSpeed);
|
||||
}
|
||||
|
||||
function getZoomScale() {return Math.pow(0.95, scope.zoomSpeed)}
|
||||
function rotateLeft(angle) {sphericalDelta.theta -= angle}
|
||||
function rotateUp(angle) {sphericalDelta.phi -= angle}
|
||||
function rotateLeft(angle) {
|
||||
sphericalDelta.theta -= angle;
|
||||
}
|
||||
|
||||
function rotateUp(angle) {
|
||||
sphericalDelta.phi -= angle;
|
||||
}
|
||||
|
||||
var panLeft = function () {
|
||||
var v = new THREE.Vector3();
|
||||
const panLeft = function() {
|
||||
const v = new THREE.Vector3();
|
||||
|
||||
return function panLeft(distance, objectMatrix) {
|
||||
v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix
|
||||
v.multiplyScalar(-distance);
|
||||
panOffset.add(v);
|
||||
}
|
||||
}()
|
||||
};
|
||||
}();
|
||||
|
||||
|
||||
var panUp = function () {
|
||||
var v = new THREE.Vector3();
|
||||
const panUp = function() {
|
||||
const v = new THREE.Vector3();
|
||||
|
||||
return function panUp(distance, objectMatrix) {
|
||||
if (scope.screenSpacePanning) v.setFromMatrixColumn(objectMatrix, 1);
|
||||
else {
|
||||
if (scope.screenSpacePanning) {
|
||||
v.setFromMatrixColumn(objectMatrix, 1);
|
||||
} else {
|
||||
v.setFromMatrixColumn(objectMatrix, 0);
|
||||
v.crossVectors(scope.object.up, v);
|
||||
}
|
||||
|
||||
v.multiplyScalar(distance);
|
||||
panOffset.add(v);
|
||||
}
|
||||
}()
|
||||
|
||||
};
|
||||
}();
|
||||
|
||||
function unknownCamera() {
|
||||
console.warn('WARNING: OrbitControls.js encountered an unknown camera ' +
|
||||
'type - pan & zoom disabled.');
|
||||
console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan & zoom disabled.");
|
||||
scope.enablePan = false;
|
||||
scope.enableZoom = false;
|
||||
}
|
||||
|
||||
|
||||
// deltaX and deltaY are in pixels; right and down are positive
|
||||
var pan = function () {
|
||||
var offset = new THREE.Vector3();
|
||||
const pan = function() {
|
||||
const offset = new THREE.Vector3();
|
||||
|
||||
return function pan(deltaX, deltaY) {
|
||||
var element = scope.domElement === document ?
|
||||
scope.domElement.body : scope.domElement;
|
||||
const element = scope.domElement === document
|
||||
? scope.domElement.body
|
||||
: scope.domElement;
|
||||
|
||||
if (scope.object.isPerspectiveCamera) {
|
||||
// perspective
|
||||
offset.copy(scope.object.position).sub(scope.target);
|
||||
var targetDistance = offset.length();
|
||||
let targetDistance = offset.length();
|
||||
|
||||
// half of the fov is center to top of screen
|
||||
targetDistance *= Math.tan((scope.object.fov / 2) * Math.PI / 180.0);
|
||||
|
||||
// we use only clientHeight here so aspect ratio does not distort speed
|
||||
panLeft(2 * deltaX * targetDistance / element.clientHeight,
|
||||
scope.object.matrix);
|
||||
panUp(2 * deltaY * targetDistance / element.clientHeight,
|
||||
scope.object.matrix);
|
||||
panLeft(2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix);
|
||||
panUp(2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix);
|
||||
|
||||
} else if (scope.object.isOrthographicCamera) {
|
||||
// orthographic
|
||||
panLeft(deltaX * (scope.object.right - scope.object.left) /
|
||||
scope.object.zoom / element.clientWidth, scope.object.matrix);
|
||||
panUp(deltaY * (scope.object.top - scope.object.bottom) /
|
||||
scope.object.zoom / element.clientHeight, scope.object.matrix);
|
||||
panLeft(deltaX * (scope.object.right - scope.object.left) / scope.object.zoom / element.clientWidth, scope.object.matrix);
|
||||
panUp(deltaY * (scope.object.top - scope.object.bottom) / scope.object.zoom / element.clientHeight, scope.object.matrix);
|
||||
|
||||
} else unknownCamera();
|
||||
} else {
|
||||
unknownCamera();
|
||||
}
|
||||
}()
|
||||
|
||||
};
|
||||
}();
|
||||
|
||||
function dollyIn(dollyScale) {
|
||||
if (scope.object.isPerspectiveCamera) scale /= dollyScale;
|
||||
|
||||
else if (scope.object.isOrthographicCamera) {
|
||||
if (scope.object.isPerspectiveCamera) {
|
||||
scale /= dollyScale;
|
||||
} else if (scope.object.isOrthographicCamera) {
|
||||
scope.object.zoom =
|
||||
Math.max(scope.minZoom,
|
||||
Math.min(scope.maxZoom, scope.object.zoom * dollyScale));
|
||||
scope.object.updateProjectionMatrix();
|
||||
zoomChanged = true;
|
||||
|
||||
} else unknownCamera();
|
||||
} else {
|
||||
unknownCamera();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function dollyOut(dollyScale) {
|
||||
if (scope.object.isPerspectiveCamera) scale *= dollyScale;
|
||||
|
||||
else if (scope.object.isOrthographicCamera) {
|
||||
if (scope.object.isPerspectiveCamera) {
|
||||
scale *= dollyScale;
|
||||
} else if (scope.object.isOrthographicCamera) {
|
||||
scope.object.zoom =
|
||||
Math.max(scope.minZoom,
|
||||
Math.min(scope.maxZoom, scope.object.zoom / dollyScale));
|
||||
scope.object.updateProjectionMatrix();
|
||||
zoomChanged = true;
|
||||
|
||||
} else unknownCamera();
|
||||
} else {
|
||||
unknownCamera();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// event callbacks - update the object state
|
||||
function handleMouseDownRotate(event) {
|
||||
rotateStart.set(event.clientX, event.clientY);
|
||||
}
|
||||
|
||||
|
||||
function handleMouseDownDolly(event) {
|
||||
dollyStart.set(event.clientX, event.clientY);
|
||||
}
|
||||
|
||||
|
||||
function handleMouseDownPan(event) {
|
||||
panStart.set(event.clientX, event.clientY);
|
||||
}
|
||||
|
||||
|
||||
function handleMouseMoveRotate(event) {
|
||||
rotateEnd.set(event.clientX, event.clientY);
|
||||
rotateDelta.subVectors(rotateEnd, rotateStart)
|
||||
.multiplyScalar(scope.rotateSpeed);
|
||||
|
||||
var element = scope.domElement === document ?
|
||||
const element = scope.domElement === document ?
|
||||
scope.domElement.body : scope.domElement;
|
||||
|
||||
// yes, height
|
||||
@@ -385,19 +380,20 @@ var OrbitControls = function (object, domElement) {
|
||||
scope.update();
|
||||
}
|
||||
|
||||
|
||||
function handleMouseMoveDolly(event) {
|
||||
dollyEnd.set(event.clientX, event.clientY);
|
||||
dollyDelta.subVectors(dollyEnd, dollyStart);
|
||||
|
||||
if (dollyDelta.y > 0) dollyIn(getZoomScale());
|
||||
else if (dollyDelta.y < 0) dollyOut(getZoomScale());
|
||||
if (dollyDelta.y > 0) {
|
||||
dollyIn(getZoomScale());
|
||||
} else if (dollyDelta.y < 0) {
|
||||
dollyOut(getZoomScale());
|
||||
}
|
||||
|
||||
dollyStart.copy(dollyEnd);
|
||||
scope.update();
|
||||
}
|
||||
|
||||
|
||||
function handleMouseMovePan(event) {
|
||||
panEnd.set(event.clientX, event.clientY);
|
||||
panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed);
|
||||
@@ -406,18 +402,16 @@ var OrbitControls = function (object, domElement) {
|
||||
scope.update();
|
||||
}
|
||||
|
||||
|
||||
function handleMouseUp(event) {}
|
||||
|
||||
|
||||
function handleMouseWheel(event) {
|
||||
if (event.deltaY < 0) dollyOut(getZoomScale());
|
||||
else if (event.deltaY > 0) dollyIn(getZoomScale());
|
||||
if (event.deltaY < 0) {
|
||||
dollyOut(getZoomScale());
|
||||
} else if (event.deltaY > 0) {
|
||||
dollyIn(getZoomScale());
|
||||
}
|
||||
|
||||
scope.update();
|
||||
}
|
||||
|
||||
|
||||
function handleKeyDown(event) {
|
||||
switch (event.keyCode) {
|
||||
case scope.keys.UP:
|
||||
@@ -442,35 +436,32 @@ var OrbitControls = function (object, domElement) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function handleTouchStartRotate(event) {
|
||||
rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
|
||||
}
|
||||
|
||||
|
||||
function handleTouchStartDollyPan(event) {
|
||||
if (scope.enableZoom) {
|
||||
var dx = event.touches[0].pageX - event.touches[1].pageX;
|
||||
var dy = event.touches[0].pageY - event.touches[1].pageY;
|
||||
var distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const dx = event.touches[0].pageX - event.touches[1].pageX;
|
||||
const dy = event.touches[0].pageY - event.touches[1].pageY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
dollyStart.set(0, distance);
|
||||
}
|
||||
|
||||
if (scope.enablePan) {
|
||||
var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
|
||||
var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
|
||||
const x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
|
||||
const y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
|
||||
panStart.set(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function handleTouchMoveRotate(event) {
|
||||
rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
|
||||
rotateDelta.subVectors(rotateEnd, rotateStart)
|
||||
.multiplyScalar(scope.rotateSpeed);
|
||||
|
||||
var element = scope.domElement === document ?
|
||||
const element = scope.domElement === document ?
|
||||
scope.domElement.body : scope.domElement;
|
||||
|
||||
// yes, height
|
||||
@@ -480,12 +471,11 @@ var OrbitControls = function (object, domElement) {
|
||||
scope.update();
|
||||
}
|
||||
|
||||
|
||||
function handleTouchMoveDollyPan(event) {
|
||||
if (scope.enableZoom) {
|
||||
var dx = event.touches[0].pageX - event.touches[1].pageX;
|
||||
var dy = event.touches[0].pageY - event.touches[1].pageY;
|
||||
var distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const dx = event.touches[0].pageX - event.touches[1].pageX;
|
||||
const dy = event.touches[0].pageY - event.touches[1].pageY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
dollyEnd.set(0, distance);
|
||||
dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed));
|
||||
@@ -493,10 +483,9 @@ var OrbitControls = function (object, domElement) {
|
||||
dollyStart.copy(dollyEnd);
|
||||
}
|
||||
|
||||
|
||||
if (scope.enablePan) {
|
||||
var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
|
||||
var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
|
||||
const x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
|
||||
const y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
|
||||
|
||||
panEnd.set(x, y);
|
||||
panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed);
|
||||
@@ -507,82 +496,94 @@ var OrbitControls = function (object, domElement) {
|
||||
scope.update();
|
||||
}
|
||||
|
||||
|
||||
function handleTouchEnd(event) {}
|
||||
|
||||
|
||||
// event handlers - listen for events and reset state
|
||||
function onMouseDown(event) {
|
||||
if (!scope.enabled) return;
|
||||
if (!scope.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
switch (event.button) {
|
||||
case scope.mouseButtons.ORBIT:
|
||||
if (!scope.enableRotate) return;
|
||||
if (!scope.enableRotate) {
|
||||
return;
|
||||
}
|
||||
handleMouseDownRotate(event);
|
||||
state = STATE.ROTATE;
|
||||
break;
|
||||
|
||||
case scope.mouseButtons.ZOOM:
|
||||
if (!scope.enableZoom) return;
|
||||
if (!scope.enableZoom) {
|
||||
return;
|
||||
}
|
||||
handleMouseDownDolly(event);
|
||||
state = STATE.DOLLY;
|
||||
break;
|
||||
|
||||
case scope.mouseButtons.PAN:
|
||||
if (!scope.enablePan) return;
|
||||
if (!scope.enablePan) {
|
||||
return;
|
||||
}
|
||||
handleMouseDownPan(event);
|
||||
state = STATE.PAN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (state != STATE.NONE) {
|
||||
document.addEventListener('mousemove', onMouseMove, false);
|
||||
document.addEventListener('mouseup', onMouseUp, false);
|
||||
document.addEventListener("mousemove", onMouseMove, false);
|
||||
document.addEventListener("mouseup", onMouseUp, false);
|
||||
scope.dispatchEvent(startEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onMouseMove(event) {
|
||||
if (!scope.enabled) return;
|
||||
if (!scope.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
switch (state) {
|
||||
case STATE.ROTATE:
|
||||
if (!scope.enableRotate) return;
|
||||
if (!scope.enableRotate) {
|
||||
return;
|
||||
}
|
||||
handleMouseMoveRotate(event);
|
||||
break;
|
||||
|
||||
case STATE.DOLLY:
|
||||
if (!scope.enableZoom) return;
|
||||
if (!scope.enableZoom) {
|
||||
return;
|
||||
}
|
||||
handleMouseMoveDolly(event);
|
||||
break;
|
||||
|
||||
case STATE.PAN:
|
||||
if (!scope.enablePan) return;
|
||||
if (!scope.enablePan) {
|
||||
return;
|
||||
}
|
||||
handleMouseMovePan(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseUp() {
|
||||
if (!scope.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
function onMouseUp(event) {
|
||||
if (!scope.enabled) return;
|
||||
|
||||
handleMouseUp(event);
|
||||
document.removeEventListener('mousemove', onMouseMove, false);
|
||||
document.removeEventListener('mouseup', onMouseUp, false);
|
||||
document.removeEventListener("mousemove", onMouseMove, false);
|
||||
document.removeEventListener("mouseup", onMouseUp, false);
|
||||
scope.dispatchEvent(endEvent);
|
||||
state = STATE.NONE;
|
||||
}
|
||||
|
||||
|
||||
function onMouseWheel(event) {
|
||||
if (!scope.enabled || !scope.enableZoom ||
|
||||
(state != STATE.NONE && state != STATE.ROTATE)) return;
|
||||
(state != STATE.NONE && state != STATE.ROTATE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@@ -591,28 +592,34 @@ var OrbitControls = function (object, domElement) {
|
||||
scope.dispatchEvent(endEvent);
|
||||
}
|
||||
|
||||
|
||||
function onKeyDown(event) {
|
||||
if (!scope.enabled || !scope.enableKeys || !scope.enablePan) return;
|
||||
if (!scope.enabled || !scope.enableKeys || !scope.enablePan) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleKeyDown(event);
|
||||
}
|
||||
|
||||
|
||||
function onTouchStart(event) {
|
||||
if (!scope.enabled) return;
|
||||
if (!scope.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
switch (event.touches.length) {
|
||||
case 1: // one-fingered touch: rotate
|
||||
if (!scope.enableRotate) return;
|
||||
if (!scope.enableRotate) {
|
||||
return;
|
||||
}
|
||||
handleTouchStartRotate(event);
|
||||
state = STATE.TOUCH_ROTATE;
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly-pan
|
||||
if (!scope.enableZoom && !scope.enablePan) return;
|
||||
if (!scope.enableZoom && !scope.enablePan) {
|
||||
return;
|
||||
}
|
||||
handleTouchStartDollyPan(event);
|
||||
state = STATE.TOUCH_DOLLY_PAN;
|
||||
break;
|
||||
@@ -620,27 +627,38 @@ var OrbitControls = function (object, domElement) {
|
||||
default: state = STATE.NONE;
|
||||
}
|
||||
|
||||
if (state != STATE.NONE) scope.dispatchEvent(startEvent);
|
||||
if (state != STATE.NONE) {
|
||||
scope.dispatchEvent(startEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onTouchMove(event) {
|
||||
if (!scope.enabled) return;
|
||||
if (!scope.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
switch (event.touches.length) {
|
||||
case 1: // one-fingered touch: rotate
|
||||
if (!scope.enableRotate) return;
|
||||
if (state != STATE.TOUCH_ROTATE) return; // is this needed?
|
||||
if (!scope.enableRotate) {
|
||||
return;
|
||||
}
|
||||
if (state != STATE.TOUCH_ROTATE) {
|
||||
return;
|
||||
} // is this needed?
|
||||
|
||||
handleTouchMoveRotate(event);
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly-pan
|
||||
if (!scope.enableZoom && !scope.enablePan) return;
|
||||
if (state != STATE.TOUCH_DOLLY_PAN) return; // is this needed?
|
||||
if (!scope.enableZoom && !scope.enablePan) {
|
||||
return;
|
||||
}
|
||||
if (state != STATE.TOUCH_DOLLY_PAN) {
|
||||
return;
|
||||
} // is this needed?
|
||||
|
||||
handleTouchMoveDollyPan(event);
|
||||
break;
|
||||
@@ -649,33 +667,32 @@ var OrbitControls = function (object, domElement) {
|
||||
}
|
||||
}
|
||||
|
||||
function onTouchEnd() {
|
||||
if (!scope.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
function onTouchEnd(event) {
|
||||
if (!scope.enabled) return;
|
||||
|
||||
handleTouchEnd(event);
|
||||
scope.dispatchEvent(endEvent);
|
||||
state = STATE.NONE;
|
||||
}
|
||||
|
||||
|
||||
function onContextMenu(event) {
|
||||
if (!scope.enabled) return;
|
||||
if (!scope.enabled) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
scope.domElement.addEventListener('contextmenu', onContextMenu, false);
|
||||
scope.domElement.addEventListener('mousedown', onMouseDown, false);
|
||||
scope.domElement.addEventListener('wheel', onMouseWheel, false);
|
||||
scope.domElement.addEventListener('touchstart', onTouchStart, false);
|
||||
scope.domElement.addEventListener('touchend', onTouchEnd, false);
|
||||
scope.domElement.addEventListener('touchmove', onTouchMove, false);
|
||||
window .addEventListener('keydown', onKeyDown, false);
|
||||
scope.domElement.addEventListener("contextmenu", onContextMenu, false);
|
||||
scope.domElement.addEventListener("mousedown", onMouseDown, false);
|
||||
scope.domElement.addEventListener("wheel", onMouseWheel, false);
|
||||
scope.domElement.addEventListener("touchstart", onTouchStart, false);
|
||||
scope.domElement.addEventListener("touchend", onTouchEnd, false);
|
||||
scope.domElement.addEventListener("touchmove", onTouchMove, false);
|
||||
window.addEventListener("keydown", onKeyDown, false);
|
||||
|
||||
this.update(); // force an update at start
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype);
|
||||
OrbitControls.prototype.constructor = OrbitControls;
|
||||
|
||||
@@ -1,58 +1,32 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
Copyright 2018. Buildbotics LLC
|
||||
All Rights Reserved.
|
||||
|
||||
For information regarding this software email:
|
||||
Joseph Coffland
|
||||
joseph@buildbotics.com
|
||||
|
||||
This software is free software: you clan redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public License
|
||||
as published by the Free Software Foundation, either version 2.1 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This software is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the C! library. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
var orbit = require('./orbit');
|
||||
var cookie = require('./cookie')('bbctrl-');
|
||||
var font = require('./helvetiker_regular.typeface.json')
|
||||
const orbit = require("./orbit");
|
||||
const cookie = require("./cookie")("bbctrl-");
|
||||
const font = require("./helvetiker_regular.typeface.json");
|
||||
|
||||
module.exports = {
|
||||
template: '#path-viewer-template',
|
||||
props: ['toolpath'],
|
||||
template: "#path-viewer-template",
|
||||
props: [ "toolpath" ],
|
||||
|
||||
data: function () {
|
||||
data: function() {
|
||||
return {
|
||||
enabled: false,
|
||||
loading: false,
|
||||
dirty: true,
|
||||
snapView: cookie.get('snap-view', 'isometric'),
|
||||
small: cookie.get_bool('small-path-view', true),
|
||||
surfaceMode: 'cut',
|
||||
showPath: cookie.get_bool('show-path', true),
|
||||
showTool: cookie.get_bool('show-tool', true),
|
||||
showBBox: cookie.get_bool('show-bbox', true),
|
||||
showAxes: cookie.get_bool('show-axes', true),
|
||||
showIntensity: cookie.get_bool('show-intensity', false)
|
||||
}
|
||||
snapView: cookie.get("snap-view", "isometric"),
|
||||
small: cookie.get_bool("small-path-view", true),
|
||||
surfaceMode: "cut",
|
||||
showPath: cookie.get_bool("show-path", true),
|
||||
showTool: cookie.get_bool("show-tool", true),
|
||||
showBBox: cookie.get_bool("show-bbox", true),
|
||||
showAxes: cookie.get_bool("show-axes", true),
|
||||
showIntensity: cookie.get_bool("show-intensity", false)
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
computed: {
|
||||
target: function () {
|
||||
return $(this.$el).find('.path-viewer-content')[0]
|
||||
target: function() {
|
||||
return this.$el.querySelector(".path-viewer-content");
|
||||
},
|
||||
|
||||
webglAvailable: function() {
|
||||
@@ -69,63 +43,66 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
watch: {
|
||||
toolpath: function () {Vue.nextTick(this.update)},
|
||||
surfaceMode: function (mode) {this.update_surface_mode(mode)},
|
||||
|
||||
|
||||
small: function (enable) {
|
||||
cookie.set_bool('small-path-view', enable);
|
||||
Vue.nextTick(this.update_view)
|
||||
toolpath: function() {
|
||||
Vue.nextTick(this.update);
|
||||
},
|
||||
|
||||
|
||||
showPath: function (enable) {
|
||||
cookie.set_bool('show-path', enable);
|
||||
this.set_visible(this.pathView, enable)
|
||||
surfaceMode: function(mode) {
|
||||
this.update_surface_mode(mode);
|
||||
},
|
||||
|
||||
|
||||
showTool: function (enable) {
|
||||
cookie.set_bool('show-tool', enable);
|
||||
this.set_visible(this.toolView, enable)
|
||||
small: function(enable) {
|
||||
cookie.set_bool("small-path-view", enable);
|
||||
Vue.nextTick(this.update_view);
|
||||
},
|
||||
|
||||
|
||||
showAxes: function (enable) {
|
||||
cookie.set_bool('show-axes', enable);
|
||||
this.set_visible(this.axesView, enable)
|
||||
showPath: function(enable) {
|
||||
cookie.set_bool("show-path", enable);
|
||||
this.set_visible(this.pathView, enable);
|
||||
},
|
||||
|
||||
|
||||
showIntensity: function (enable) {
|
||||
cookie.set_bool('show-intensity', enable);
|
||||
Vue.nextTick(this.update)
|
||||
showTool: function(enable) {
|
||||
cookie.set_bool("show-tool", enable);
|
||||
this.set_visible(this.toolView, enable);
|
||||
},
|
||||
|
||||
showAxes: function(enable) {
|
||||
cookie.set_bool("show-axes", enable);
|
||||
this.set_visible(this.axesView, enable);
|
||||
},
|
||||
|
||||
showBBox: function (enable) {
|
||||
cookie.set_bool('show-bbox', enable);
|
||||
showIntensity: function(enable) {
|
||||
cookie.set_bool("show-intensity", enable);
|
||||
Vue.nextTick(this.update);
|
||||
},
|
||||
|
||||
showBBox: function(enable) {
|
||||
cookie.set_bool("show-bbox", enable);
|
||||
this.set_visible(this.bboxView, enable);
|
||||
this.set_visible(this.envelopeView, enable);
|
||||
},
|
||||
|
||||
|
||||
x: function () {this.axis_changed()},
|
||||
y: function () {this.axis_changed()},
|
||||
z: function () {this.axis_changed()}
|
||||
x: function() {
|
||||
this.axis_changed();
|
||||
},
|
||||
|
||||
y: function() {
|
||||
this.axis_changed();
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
z: function() {
|
||||
this.axis_changed();
|
||||
}
|
||||
},
|
||||
|
||||
ready: function() {
|
||||
this.graphics();
|
||||
Vue.nextTick(this.update);
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
update: async function () {
|
||||
update: async function() {
|
||||
if (!this.webglAvailable) {
|
||||
return;
|
||||
}
|
||||
@@ -140,21 +117,23 @@ module.exports = {
|
||||
this.draw_loading();
|
||||
}
|
||||
|
||||
if (!this.enabled || !this.toolpath.filename) return;
|
||||
if (!this.enabled || !this.toolpath.filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
async function get(url) {
|
||||
const response = await fetch(`${url}?${Math.random()}`);
|
||||
const response = await fetch(`${url}`, { cache: "no-cache" });
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
|
||||
return new Float32Array(arrayBuffer);
|
||||
}
|
||||
|
||||
const [positions, speeds] = await Promise.all([
|
||||
get('/api/path/' + this.toolpath.filename + '/positions'),
|
||||
get('/api/path/' + this.toolpath.filename + '/speeds')
|
||||
const [ positions, speeds ] = await Promise.all([
|
||||
get(`/api/path/${this.toolpath.filename}/positions`),
|
||||
get(`/api/path/${this.toolpath.filename}/speeds`)
|
||||
]);
|
||||
|
||||
this.positions = positions
|
||||
this.positions = positions;
|
||||
this.speeds = speeds;
|
||||
this.loading = false;
|
||||
|
||||
@@ -166,22 +145,22 @@ module.exports = {
|
||||
this.update_view();
|
||||
},
|
||||
|
||||
update_surface_mode: function(mode) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_surface_mode: function (mode) {
|
||||
if (!this.enabled) return;
|
||||
|
||||
if (typeof this.surfaceMaterial != 'undefined') {
|
||||
this.surfaceMaterial.wireframe = mode == 'wire';
|
||||
if (typeof this.surfaceMaterial != "undefined") {
|
||||
this.surfaceMaterial.wireframe = mode == "wire";
|
||||
this.surfaceMaterial.needsUpdate = true;
|
||||
}
|
||||
|
||||
this.set_visible(this.surfaceMesh, mode == 'cut' || mode == 'wire');
|
||||
this.set_visible(this.workpieceMesh, mode == 'solid');
|
||||
this.set_visible(this.surfaceMesh, mode == "cut" || mode == "wire");
|
||||
this.set_visible(this.workpieceMesh, mode == "solid");
|
||||
},
|
||||
|
||||
|
||||
load_surface: function (surface) {
|
||||
if (typeof surface == 'undefined') {
|
||||
load_surface: function(surface) {
|
||||
if (typeof surface == "undefined") {
|
||||
this.vertices = undefined;
|
||||
this.normals = undefined;
|
||||
return;
|
||||
@@ -191,30 +170,37 @@ module.exports = {
|
||||
|
||||
// Expand normals
|
||||
this.normals = [];
|
||||
for (var i = 0; i < surface.normals.length / 3; i++)
|
||||
for (var j = 0; j < 3; j++)
|
||||
for (var k = 0; k < 3; k++)
|
||||
for (let i = 0; i < surface.normals.length / 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
for (let k = 0; k < 3; k++) {
|
||||
this.normals.push(surface.normals[i * 3 + k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
set_visible: function (target, visible) {
|
||||
if (typeof target != 'undefined') target.visible = visible;
|
||||
set_visible: function(target, visible) {
|
||||
if (typeof target != "undefined") {
|
||||
target.visible = visible;
|
||||
}
|
||||
this.dirty = true;
|
||||
},
|
||||
|
||||
get_dims: function() {
|
||||
const computedStyle = window.getComputedStyle(this.target);
|
||||
|
||||
get_dims: function () {
|
||||
var t = $(this.target);
|
||||
var width = t.innerWidth();
|
||||
var height = t.innerHeight();
|
||||
return {width: width, height: height};
|
||||
return {
|
||||
width: parseInt(computedStyle.width),
|
||||
height: parseInt(computedStyle.height)
|
||||
};
|
||||
},
|
||||
|
||||
update_view: function() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_view: function () {
|
||||
if (!this.enabled) return;
|
||||
var dims = this.get_dims();
|
||||
const dims = this.get_dims();
|
||||
|
||||
this.camera.aspect = dims.width / dims.height;
|
||||
this.camera.updateProjectionMatrix();
|
||||
@@ -229,61 +215,73 @@ module.exports = {
|
||||
this.dirty = true;
|
||||
},
|
||||
|
||||
update_tool: function(tool) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof tool == "undefined") {
|
||||
tool = this.toolView;
|
||||
}
|
||||
|
||||
if (typeof tool == "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
update_tool: function (tool) {
|
||||
if (!this.enabled) return;
|
||||
if (typeof tool == 'undefined') tool = this.toolView;
|
||||
if (typeof tool == 'undefined') return;
|
||||
tool.position.x = this.x.pos;
|
||||
tool.position.y = this.y.pos;
|
||||
tool.position.z = this.z.pos;
|
||||
},
|
||||
|
||||
update_envelope: function(envelope) {
|
||||
if (!this.enabled || !this.axes.homed) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_envelope: function (envelope) {
|
||||
if (!this.enabled || !this.axes.homed) return;
|
||||
if (typeof envelope == 'undefined') envelope = this.envelopeView;
|
||||
if (typeof envelope == 'undefined') return;
|
||||
if (typeof envelope == "undefined") {
|
||||
envelope = this.envelopeView;
|
||||
}
|
||||
|
||||
var min = new THREE.Vector3();
|
||||
var max = new THREE.Vector3();
|
||||
if (typeof envelope == "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var axis of 'xyz') {
|
||||
const min = new THREE.Vector3();
|
||||
const max = new THREE.Vector3();
|
||||
|
||||
for (const axis of "xyz") {
|
||||
min[axis] = this[axis].min - this[axis].off;
|
||||
max[axis] = this[axis].max - this[axis].off;
|
||||
}
|
||||
|
||||
var bounds = new THREE.Box3(min, max);
|
||||
const bounds = new THREE.Box3(min, max);
|
||||
if (bounds.isEmpty()) {
|
||||
envelope.geometry = this.create_empty_geom();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
envelope.geometry = this.create_bbox_geom(bounds);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
axis_changed: function () {
|
||||
axis_changed: function() {
|
||||
this.update_tool();
|
||||
this.update_envelope();
|
||||
this.dirty = true;
|
||||
},
|
||||
|
||||
|
||||
graphics: function () {
|
||||
graphics: function() {
|
||||
if (!this.webglAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Renderer
|
||||
this.renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
|
||||
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||
this.renderer.setClearColor(0, 0);
|
||||
this.target.appendChild(this.renderer.domElement);
|
||||
|
||||
} catch (e) {
|
||||
console.log('WebGL not supported: ', e);
|
||||
console.log("WebGL not supported: ", e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -295,15 +293,13 @@ module.exports = {
|
||||
// Lighting
|
||||
this.ambient = new THREE.AmbientLight(0xffffff, 0.5);
|
||||
|
||||
var keyLight = new THREE.DirectionalLight
|
||||
(new THREE.Color('hsl(30, 100%, 75%)'), 0.75);
|
||||
const keyLight = new THREE.DirectionalLight(new THREE.Color("hsl(30, 100%, 75%)"), 0.75);
|
||||
keyLight.position.set(-100, 0, 100);
|
||||
|
||||
var fillLight = new THREE.DirectionalLight
|
||||
(new THREE.Color('hsl(240, 100%, 75%)'), 0.25);
|
||||
const fillLight = new THREE.DirectionalLight(new THREE.Color("hsl(240, 100%, 75%)"), 0.25);
|
||||
fillLight.position.set(100, 0, 100);
|
||||
|
||||
var backLight = new THREE.DirectionalLight(0xffffff, 0.5);
|
||||
const backLight = new THREE.DirectionalLight(0xffffff, 0.5);
|
||||
backLight.position.set(100, 0, -100).normalize();
|
||||
|
||||
this.lights = new THREE.Group();
|
||||
@@ -322,8 +318,8 @@ module.exports = {
|
||||
this.controls.enableZoom = true;
|
||||
|
||||
// Move lights with scene
|
||||
this.controls.addEventListener('change', function (scope) {
|
||||
return function () {
|
||||
this.controls.addEventListener("change", function(scope) {
|
||||
return function() {
|
||||
keyLight.position.copy(scope.camera.position);
|
||||
fillLight.position.copy(scope.camera.position);
|
||||
backLight.position.copy(scope.camera.position);
|
||||
@@ -331,18 +327,17 @@ module.exports = {
|
||||
fillLight.lookAt(scope.controls.target);
|
||||
backLight.lookAt(scope.controls.target);
|
||||
scope.dirty = true;
|
||||
}
|
||||
}(this))
|
||||
};
|
||||
}(this));
|
||||
|
||||
// Events
|
||||
window.addEventListener('resize', this.update_view, false);
|
||||
window.addEventListener("resize", this.update_view, false);
|
||||
|
||||
// Start it
|
||||
this.render();
|
||||
},
|
||||
|
||||
|
||||
create_surface_material: function () {
|
||||
create_surface_material: function() {
|
||||
return new THREE.MeshPhongMaterial({
|
||||
specular: 0x111111,
|
||||
shininess: 10,
|
||||
@@ -351,11 +346,10 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
draw_loading: function () {
|
||||
draw_loading: function() {
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
var geometry = new THREE.TextGeometry('Loading 3D View...', {
|
||||
const geometry = new THREE.TextGeometry("Loading 3D View...", {
|
||||
font: new THREE.Font(font),
|
||||
size: 40,
|
||||
height: 5,
|
||||
@@ -367,8 +361,7 @@ module.exports = {
|
||||
});
|
||||
geometry.computeBoundingBox();
|
||||
|
||||
var center = geometry.center();
|
||||
var mesh = new THREE.Mesh(geometry, this.surfaceMaterial);
|
||||
const mesh = new THREE.Mesh(geometry, this.surfaceMaterial);
|
||||
|
||||
this.scene.add(mesh);
|
||||
this.scene.add(this.ambient);
|
||||
@@ -376,21 +369,22 @@ module.exports = {
|
||||
this.update_view();
|
||||
},
|
||||
|
||||
draw_workpiece: function(scene, material) {
|
||||
if (typeof this.workpiece == "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
draw_workpiece: function (scene, material) {
|
||||
if (typeof this.workpiece == 'undefined') return;
|
||||
|
||||
var min = this.workpiece.min;
|
||||
var max = this.workpiece.max;
|
||||
let min = this.workpiece.min;
|
||||
let max = this.workpiece.max;
|
||||
|
||||
min = new THREE.Vector3(min[0], min[1], min[2]);
|
||||
max = new THREE.Vector3(max[0], max[1], max[2]);
|
||||
var dims = max.clone().sub(min);
|
||||
const dims = max.clone().sub(min);
|
||||
|
||||
var geometry = new THREE.BoxGeometry(dims.x, dims.y, dims.z)
|
||||
var mesh = new THREE.Mesh(geometry, material);
|
||||
const geometry = new THREE.BoxGeometry(dims.x, dims.y, dims.z);
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
var offset = dims.clone();
|
||||
const offset = dims.clone();
|
||||
offset.divideScalar(2);
|
||||
offset.add(min);
|
||||
|
||||
@@ -403,16 +397,15 @@ module.exports = {
|
||||
return mesh;
|
||||
},
|
||||
|
||||
draw_surface: function(scene, material) {
|
||||
if (typeof this.vertices == "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
draw_surface: function (scene, material) {
|
||||
if (typeof this.vertices == 'undefined') return;
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
|
||||
var geometry = new THREE.BufferGeometry();
|
||||
|
||||
geometry.addAttribute
|
||||
('position', new THREE.Float32BufferAttribute(this.vertices, 3));
|
||||
geometry.addAttribute
|
||||
('normal', new THREE.Float32BufferAttribute(this.normals, 3));
|
||||
geometry.addAttribute("position", new THREE.Float32BufferAttribute(this.vertices, 3));
|
||||
geometry.addAttribute("normal", new THREE.Float32BufferAttribute(this.normals, 3));
|
||||
|
||||
geometry.computeBoundingSphere();
|
||||
geometry.computeBoundingBox();
|
||||
@@ -420,15 +413,16 @@ module.exports = {
|
||||
return new THREE.Mesh(geometry, material);
|
||||
},
|
||||
|
||||
|
||||
draw_tool: function (scene, bbox) {
|
||||
draw_tool: function(scene, bbox) {
|
||||
// Tool size is relative to bounds
|
||||
var size = bbox.getSize(new THREE.Vector3());
|
||||
var length = (size.x + size.y + size.z) / 24;
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
let length = (size.x + size.y + size.z) / 24;
|
||||
|
||||
if (length < 1) length = 1;
|
||||
if (length < 1) {
|
||||
length = 1;
|
||||
}
|
||||
|
||||
var material = new THREE.MeshPhongMaterial({
|
||||
const material = new THREE.MeshPhongMaterial({
|
||||
transparent: true,
|
||||
opacity: 0.75,
|
||||
specular: 0x161616,
|
||||
@@ -436,30 +430,33 @@ module.exports = {
|
||||
color: 0xffa500 // Orange
|
||||
});
|
||||
|
||||
var geometry = new THREE.CylinderGeometry(length / 2, 0, length, 128);
|
||||
const geometry = new THREE.CylinderGeometry(length / 2, 0, length, 128);
|
||||
geometry.translate(0, length / 2, 0);
|
||||
geometry.rotateX(0.5 * Math.PI);
|
||||
|
||||
var mesh = new THREE.Mesh(geometry, material);
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
this.update_tool(mesh);
|
||||
mesh.visible = this.showTool;
|
||||
scene.add(mesh);
|
||||
return mesh;
|
||||
},
|
||||
|
||||
draw_axis: function(axis, up, length, radius) {
|
||||
let color;
|
||||
|
||||
draw_axis: function (axis, up, length, radius) {
|
||||
var color;
|
||||
if (axis == 0) {
|
||||
color = 0xff0000;
|
||||
} else if (axis == 1) {
|
||||
color = 0x00ff00;
|
||||
} else if (axis == 2) {
|
||||
color = 0x0000ff;
|
||||
}
|
||||
|
||||
if (axis == 0) color = 0xff0000; // Red
|
||||
else if (axis == 1) color = 0x00ff00; // Green
|
||||
else if (axis == 2) color = 0x0000ff; // Blue
|
||||
|
||||
var group = new THREE.Group();
|
||||
var material = new THREE.MeshPhongMaterial({
|
||||
const group = new THREE.Group();
|
||||
const material = new THREE.MeshPhongMaterial({
|
||||
specular: 0x161616, shininess: 10, color: color
|
||||
});
|
||||
var geometry = new THREE.CylinderGeometry(radius, radius, length, 128);
|
||||
let geometry = new THREE.CylinderGeometry(radius, radius, length, 128);
|
||||
geometry.translate(0, -length / 2, 0);
|
||||
group.add(new THREE.Mesh(geometry, material));
|
||||
|
||||
@@ -467,28 +464,35 @@ module.exports = {
|
||||
geometry.translate(0, -length - radius, 0);
|
||||
group.add(new THREE.Mesh(geometry, material));
|
||||
|
||||
if (axis == 0) group.rotateZ((up ? 0.5 : 1.5) * Math.PI);
|
||||
else if (axis == 1) group.rotateX((up ? 0 : 1 ) * Math.PI);
|
||||
else if (axis == 2) group.rotateX((up ? 1.5 : 0.5) * Math.PI);
|
||||
if (axis == 0) {
|
||||
group.rotateZ((up ? 0.5 : 1.5) * Math.PI);
|
||||
} else if (axis == 1) {
|
||||
group.rotateX((up ? 0 : 1 ) * Math.PI);
|
||||
} else if (axis == 2) {
|
||||
group.rotateX((up ? 1.5 : 0.5) * Math.PI);
|
||||
}
|
||||
|
||||
return group;
|
||||
},
|
||||
|
||||
|
||||
draw_axes: function (scene, bbox) {
|
||||
var size = bbox.getSize(new THREE.Vector3());
|
||||
var length = (size.x + size.y + size.z) / 3;
|
||||
draw_axes: function(scene, bbox) {
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
let length = (size.x + size.y + size.z) / 3;
|
||||
length /= 10;
|
||||
|
||||
if (length < 1) length = 1;
|
||||
if (length < 1) {
|
||||
length = 1;
|
||||
}
|
||||
|
||||
var radius = length / 20;
|
||||
const radius = length / 20;
|
||||
|
||||
var group = new THREE.Group();
|
||||
const group = new THREE.Group();
|
||||
|
||||
for (var axis = 0; axis < 3; axis++)
|
||||
for (var up = 0; up < 2; up++)
|
||||
for (let axis = 0; axis < 3; axis++) {
|
||||
for (let up = 0; up < 2; up++) {
|
||||
group.add(this.draw_axis(axis, up, length, radius));
|
||||
}
|
||||
}
|
||||
|
||||
group.visible = this.showAxes;
|
||||
scene.add(group);
|
||||
@@ -496,40 +500,42 @@ module.exports = {
|
||||
return group;
|
||||
},
|
||||
|
||||
get_color: function(speed) {
|
||||
if (isNaN(speed)) {
|
||||
return [ 255, 0, 0 ];
|
||||
} // Rapid
|
||||
|
||||
get_color: function (speed) {
|
||||
if (isNaN(speed)) return [255, 0, 0]; // Rapid
|
||||
let intensity = speed / this.toolpath.maxSpeed;
|
||||
if (typeof speed == "undefined" || !this.showIntensity) {
|
||||
intensity = 1;
|
||||
}
|
||||
|
||||
var intensity = speed / this.toolpath.maxSpeed;
|
||||
if (typeof speed == 'undefined' || !this.showIntensity) intensity = 1;
|
||||
return [0, 255 * intensity, 127 * (1 - intensity)];
|
||||
return [ 0, 255 * intensity, 127 * (1 - intensity) ];
|
||||
},
|
||||
|
||||
|
||||
draw_path: function (scene) {
|
||||
var geometry = new THREE.BufferGeometry();
|
||||
var material =
|
||||
new THREE.LineBasicMaterial({
|
||||
draw_path: function(scene) {
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
const material = new THREE.LineBasicMaterial({
|
||||
vertexColors: THREE.VertexColors,
|
||||
linewidth: 1.5
|
||||
});
|
||||
|
||||
var positions = new THREE.Float32BufferAttribute(this.positions, 3);
|
||||
geometry.addAttribute('position', positions);
|
||||
const positions = new THREE.Float32BufferAttribute(this.positions, 3);
|
||||
geometry.addAttribute("position", positions);
|
||||
|
||||
var colors = [];
|
||||
for (var i = 0; i < this.speeds.length; i++) {
|
||||
var color = this.get_color(this.speeds[i]);
|
||||
let colors = [];
|
||||
for (let i = 0; i < this.speeds.length; i++) {
|
||||
const color = this.get_color(this.speeds[i]);
|
||||
Array.prototype.push.apply(colors, color);
|
||||
}
|
||||
|
||||
colors = new THREE.Uint8BufferAttribute(colors, 3, true);
|
||||
geometry.addAttribute('color', colors);
|
||||
geometry.addAttribute("color", colors);
|
||||
|
||||
geometry.computeBoundingSphere();
|
||||
geometry.computeBoundingBox();
|
||||
|
||||
var line = new THREE.Line(geometry, material);
|
||||
const line = new THREE.Line(geometry, material);
|
||||
|
||||
line.visible = this.showPath;
|
||||
scene.add(line);
|
||||
@@ -537,17 +543,15 @@ module.exports = {
|
||||
return line;
|
||||
},
|
||||
|
||||
|
||||
create_empty_geom: function () {
|
||||
var geometry = new THREE.BufferGeometry();
|
||||
geometry.addAttribute('position',
|
||||
create_empty_geom: function() {
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.addAttribute("position",
|
||||
new THREE.Float32BufferAttribute([], 3));
|
||||
return geometry;
|
||||
},
|
||||
|
||||
|
||||
create_bbox_geom: function (bbox) {
|
||||
var vertices = [];
|
||||
create_bbox_geom: function(bbox) {
|
||||
const vertices = [];
|
||||
|
||||
if (!bbox.isEmpty()) {
|
||||
// Top
|
||||
@@ -581,19 +585,18 @@ module.exports = {
|
||||
vertices.push(bbox.min.x, bbox.max.y, bbox.max.z);
|
||||
}
|
||||
|
||||
var geometry = new THREE.BufferGeometry();
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
|
||||
geometry.addAttribute('position',
|
||||
geometry.addAttribute("position",
|
||||
new THREE.Float32BufferAttribute(vertices, 3));
|
||||
|
||||
return geometry;
|
||||
},
|
||||
|
||||
|
||||
draw_bbox: function (scene, bbox) {
|
||||
var geometry = this.create_bbox_geom(bbox);
|
||||
var material = new THREE.LineBasicMaterial({color: 0xffffff});
|
||||
var line = new THREE.LineSegments(geometry, material);
|
||||
draw_bbox: function(scene, bbox) {
|
||||
const geometry = this.create_bbox_geom(bbox);
|
||||
const material = new THREE.LineBasicMaterial({ color: 0xffffff });
|
||||
const line = new THREE.LineSegments(geometry, material);
|
||||
|
||||
line.visible = this.showBBox;
|
||||
|
||||
@@ -602,11 +605,10 @@ module.exports = {
|
||||
return line;
|
||||
},
|
||||
|
||||
|
||||
draw_envelope: function (scene) {
|
||||
var geometry = this.create_empty_geom();
|
||||
var material = new THREE.LineBasicMaterial({color: 0x00f7ff});
|
||||
var line = new THREE.LineSegments(geometry, material);
|
||||
draw_envelope: function(scene) {
|
||||
const geometry = this.create_empty_geom();
|
||||
const material = new THREE.LineBasicMaterial({ color: 0x00f7ff });
|
||||
const line = new THREE.LineSegments(geometry, material);
|
||||
|
||||
line.visible = this.showBBox;
|
||||
|
||||
@@ -616,8 +618,7 @@ module.exports = {
|
||||
return line;
|
||||
},
|
||||
|
||||
|
||||
draw: function (scene) {
|
||||
draw: function(scene) {
|
||||
// Lights
|
||||
scene.add(this.ambient);
|
||||
scene.add(this.lights);
|
||||
@@ -629,7 +630,7 @@ module.exports = {
|
||||
this.update_surface_mode(this.surfaceMode);
|
||||
|
||||
// Compute bounding box
|
||||
var bbox = this.get_model_bounds();
|
||||
const bbox = this.get_model_bounds();
|
||||
|
||||
// Tool, axes & bounds
|
||||
this.toolView = this.draw_tool(scene, bbox);
|
||||
@@ -638,10 +639,12 @@ module.exports = {
|
||||
this.envelopeView = this.draw_envelope(scene);
|
||||
},
|
||||
|
||||
|
||||
render: function () {
|
||||
render: function() {
|
||||
window.requestAnimationFrame(this.render);
|
||||
if (typeof this.scene == 'undefined') return;
|
||||
|
||||
if (typeof this.scene == "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.controls.update() || this.dirty) {
|
||||
this.dirty = false;
|
||||
@@ -649,14 +652,13 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
get_model_bounds: function () {
|
||||
var bbox = new THREE.Box3(new THREE.Vector3(0, 0, 0),
|
||||
get_model_bounds: function() {
|
||||
const bbox = new THREE.Box3(new THREE.Vector3(0, 0, 0),
|
||||
new THREE.Vector3(0.00001, 0.00001, 0.00001));
|
||||
|
||||
function add(o) {
|
||||
if (typeof o != 'undefined') {
|
||||
var oBBox = new THREE.Box3();
|
||||
if (typeof o != "undefined") {
|
||||
const oBBox = new THREE.Box3();
|
||||
oBBox.setFromObject(o);
|
||||
bbox.union(oBBox);
|
||||
}
|
||||
@@ -669,45 +671,53 @@ module.exports = {
|
||||
return bbox;
|
||||
},
|
||||
|
||||
|
||||
snap: function (view) {
|
||||
if (this.loading) return;
|
||||
if (view != this.snapView) {
|
||||
this.snapView = view;
|
||||
cookie.set('snap-view', view);
|
||||
snap: function(view) {
|
||||
if (this.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bbox = this.get_model_bounds();
|
||||
if (view != this.snapView) {
|
||||
this.snapView = view;
|
||||
cookie.set("snap-view", view);
|
||||
}
|
||||
|
||||
const bbox = this.get_model_bounds();
|
||||
this.controls.reset();
|
||||
bbox.getCenter(this.controls.target);
|
||||
this.update_view();
|
||||
|
||||
// Compute new camera position
|
||||
var center = bbox.getCenter(new THREE.Vector3());
|
||||
var offset = new THREE.Vector3();
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
const offset = new THREE.Vector3();
|
||||
|
||||
switch (view) {
|
||||
case "isometric": offset.y -= 1; offset.z += 1; break;
|
||||
case "front": offset.y -= 1; break;
|
||||
case "back": offset.y += 1; break;
|
||||
case "left": offset.x -= 1; break;
|
||||
case "right": offset.x += 1; break;
|
||||
case "top": offset.z += 1; break;
|
||||
case "bottom": offset.z -= 1; break;
|
||||
}
|
||||
|
||||
if (view == 'isometric') {offset.y -= 1; offset.z += 1;}
|
||||
if (view == 'front') offset.y -= 1;
|
||||
if (view == 'back') offset.y += 1;
|
||||
if (view == 'left') offset.x -= 1;
|
||||
if (view == 'right') offset.x += 1;
|
||||
if (view == 'top') offset.z += 1;
|
||||
if (view == 'bottom') offset.z -= 1;
|
||||
offset.normalize();
|
||||
|
||||
// Initial camera position
|
||||
var position = new THREE.Vector3().copy(center).add(offset);
|
||||
const position = new THREE.Vector3().copy(center).add(offset);
|
||||
this.camera.position.copy(position);
|
||||
this.camera.lookAt(center); // Get correct camera orientation
|
||||
|
||||
var theta = this.camera.fov / 180 * Math.PI; // View angle
|
||||
var cameraLine = new THREE.Line3(center, position);
|
||||
var cameraUp = new THREE.Vector3().copy(this.camera.up)
|
||||
const theta = this.camera.fov / 180 * Math.PI; // View angle
|
||||
const cameraLine = new THREE.Line3(center, position);
|
||||
const cameraUp = new THREE.Vector3()
|
||||
.copy(this.camera.up)
|
||||
.applyQuaternion(this.camera.quaternion);
|
||||
var cameraLeft =
|
||||
new THREE.Vector3().copy(offset).cross(cameraUp).normalize();
|
||||
const cameraLeft = new THREE.Vector3()
|
||||
.copy(offset)
|
||||
.cross(cameraUp)
|
||||
.normalize();
|
||||
|
||||
var corners = [
|
||||
const corners = [
|
||||
new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z),
|
||||
new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.max.z),
|
||||
new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.min.z),
|
||||
@@ -716,39 +726,37 @@ module.exports = {
|
||||
new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z),
|
||||
new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.min.z),
|
||||
new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.max.z),
|
||||
]
|
||||
];
|
||||
|
||||
var dist = this.camera.near; // Min camera dist
|
||||
let dist = this.camera.near; // Min camera dist
|
||||
|
||||
for (var i = 0; i < corners.length; i++) {
|
||||
for (let i = 0; i < corners.length; i++) {
|
||||
// Project on to camera line
|
||||
var p1 = cameraLine
|
||||
.closestPointToPoint(corners[i], false, new THREE.Vector3());
|
||||
const p1 = cameraLine.closestPointToPoint(corners[i], false, new THREE.Vector3());
|
||||
|
||||
// Compute distance from projection to center
|
||||
var d = p1.distanceTo(center);
|
||||
if (cameraLine.closestPointToPointParameter(p1, false) < 0) d = -d;
|
||||
let d = p1.distanceTo(center);
|
||||
if (cameraLine.closestPointToPointParameter(p1, false) < 0) {
|
||||
d = -d;
|
||||
}
|
||||
|
||||
// Compute up line
|
||||
var up =
|
||||
new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraUp));
|
||||
const up = new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraUp));
|
||||
|
||||
// Project on to up line
|
||||
var p2 = up.closestPointToPoint(corners[i], false, new THREE.Vector3());
|
||||
const p2 = up.closestPointToPoint(corners[i], false, new THREE.Vector3());
|
||||
|
||||
// Compute length
|
||||
var l = p1.distanceTo(p2);
|
||||
let l = p1.distanceTo(p2);
|
||||
|
||||
// Update min camera distance
|
||||
dist = Math.max(dist, d + l / Math.tan(theta / 2));
|
||||
|
||||
// Compute left line
|
||||
var left =
|
||||
new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraLeft));
|
||||
const left = new THREE.Line3(p1, new THREE.Vector3().copy(p1).add(cameraLeft));
|
||||
|
||||
// Project on to left line
|
||||
var p3 =
|
||||
left.closestPointToPoint(corners[i], false, new THREE.Vector3());
|
||||
const p3 = left.closestPointToPoint(corners[i], false, new THREE.Vector3());
|
||||
|
||||
// Compute length
|
||||
l = p1.distanceTo(p3);
|
||||
@@ -761,6 +769,5 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
mixins: [require('./axis-vars')]
|
||||
}
|
||||
mixins: [ require("./axis-vars") ]
|
||||
};
|
||||
|
||||
@@ -1,42 +1,14 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
|
||||
module.exports = {
|
||||
template: '#settings-view-template',
|
||||
props: ['config', 'template'],
|
||||
template: "#settings-view-template",
|
||||
|
||||
attached: function() {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"SettingsView",
|
||||
document.getElementById("settings")
|
||||
);
|
||||
},
|
||||
|
||||
events: {
|
||||
'input-changed': function() {
|
||||
this.$dispatch('config-changed');
|
||||
return false;
|
||||
detached: function() {
|
||||
this.svelteComponent.$destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
129
src/js/sock.js
129
src/js/sock.js
@@ -1,38 +1,16 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
const Sock = function(url, retry, timeout) {
|
||||
if (!(this instanceof Sock)) {
|
||||
return new Sock(url, retry);
|
||||
}
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
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 Sock = function (url, retry, timeout) {
|
||||
if (!(this instanceof Sock)) return new Sock(url, retry);
|
||||
|
||||
if (typeof retry == 'undefined') retry = 2000;
|
||||
if (typeof timeout == 'undefined') timeout = 16000;
|
||||
if (typeof retry == "undefined") {
|
||||
retry = 2000;
|
||||
}
|
||||
if (typeof timeout == "undefined") {
|
||||
timeout = 16000;
|
||||
}
|
||||
|
||||
this.url = url;
|
||||
this.retry = retry;
|
||||
@@ -41,87 +19,88 @@ var Sock = function (url, retry, timeout) {
|
||||
this.count = 0;
|
||||
|
||||
this.connect();
|
||||
}
|
||||
};
|
||||
|
||||
Sock.prototype.onmessage = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
Sock.prototype.onmessage = function () {}
|
||||
Sock.prototype.onopen = function () {}
|
||||
Sock.prototype.onclose = function () {}
|
||||
Sock.prototype.onopen = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
Sock.prototype.onclose = function() {
|
||||
// Ignore
|
||||
};
|
||||
|
||||
Sock.prototype.connect = function () {
|
||||
console.debug('connecting to', this.url);
|
||||
Sock.prototype.connect = function() {
|
||||
console.debug("connecting to", this.url);
|
||||
this.close();
|
||||
|
||||
this._sock = new SockJS(this.url);
|
||||
|
||||
this._sock.onmessage = function (e) {
|
||||
console.debug('msg:', e.data);
|
||||
this.heartbeat('msg');
|
||||
this._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._sock.onopen = function() {
|
||||
console.debug("connected");
|
||||
this.heartbeat("open");
|
||||
this.onopen();
|
||||
}.bind(this);
|
||||
|
||||
|
||||
this._sock.onclose = function () {
|
||||
console.debug('disconnected');
|
||||
this._sock.onclose = function() {
|
||||
console.debug("disconnected");
|
||||
this._cancel_timeout();
|
||||
|
||||
this.onclose();
|
||||
if (typeof this._sock != 'undefined')
|
||||
if (typeof this._sock != "undefined") {
|
||||
setTimeout(this.connect.bind(this), this.retry);
|
||||
}
|
||||
}.bind(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Sock.prototype._timedout = function () {
|
||||
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');
|
||||
console.debug("connection timedout");
|
||||
this._timeout = undefined;
|
||||
this._sock.close();
|
||||
|
||||
} else this._set_timeout();
|
||||
}
|
||||
} else {
|
||||
this._set_timeout();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Sock.prototype._cancel_timeout = function () {
|
||||
Sock.prototype._cancel_timeout = function() {
|
||||
clearTimeout(this._timeout);
|
||||
this._timeout = undefined;
|
||||
this.count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Sock.prototype._set_timeout = function () {
|
||||
Sock.prototype._set_timeout = function() {
|
||||
this._timeout = setTimeout(this._timedout.bind(this),
|
||||
this.timeout / this.divisions);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Sock.prototype.heartbeat = function (msg) {
|
||||
//console.debug('heartbeat ' + new Date().toLocaleTimeString() + ' ' + msg);
|
||||
Sock.prototype.heartbeat = function() {
|
||||
this._cancel_timeout();
|
||||
this._set_timeout();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Sock.prototype.close = function () {
|
||||
if (typeof this._sock != 'undefined') {
|
||||
var sock = this._sock;
|
||||
Sock.prototype.close = function() {
|
||||
if (typeof this._sock != "undefined") {
|
||||
const sock = this._sock;
|
||||
this._sock = undefined;
|
||||
sock.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Sock.prototype.send = function(msg) {
|
||||
this._sock.send(msg);
|
||||
};
|
||||
|
||||
Sock.prototype.send = function (msg) {this._sock.send(msg)}
|
||||
|
||||
|
||||
module.exports = Sock
|
||||
module.exports = Sock;
|
||||
|
||||
@@ -1,49 +1,24 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '#templated-input-template',
|
||||
props: ['name', 'model', 'template'],
|
||||
|
||||
|
||||
data: function () {return {view: ''}},
|
||||
template: "#templated-input-template",
|
||||
props: [ "name", "model", "template" ],
|
||||
|
||||
data: function() {
|
||||
return { view: "" };
|
||||
},
|
||||
|
||||
computed: {
|
||||
metric: function () {return this.$root.metric()},
|
||||
metric: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
},
|
||||
|
||||
|
||||
_view: function () {
|
||||
_view: function() {
|
||||
if (this.template.scale) {
|
||||
if (this.metric) return 1 * this.model.toFixed(3);
|
||||
if (this.metric) {
|
||||
return 1 * this.model.toFixed(3);
|
||||
}
|
||||
|
||||
return 1 * (this.model / this.template.scale).toFixed(4);
|
||||
}
|
||||
@@ -51,39 +26,44 @@ module.exports = {
|
||||
return this.model;
|
||||
},
|
||||
|
||||
|
||||
units: function () {
|
||||
return (this.metric || !this.template.iunit) ?
|
||||
this.template.unit : this.template.iunit;
|
||||
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}`;
|
||||
}
|
||||
|
||||
title: function () {
|
||||
var s = 'Default ' + this.template.default + ' ' +
|
||||
(this.template.unit || '');
|
||||
if (typeof this.template.help != 'undefined')
|
||||
s = this.template.help + '\n' + s;
|
||||
return s;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
watch: {
|
||||
_view: function () {this.view = this._view},
|
||||
_view: function() {
|
||||
this.view = this._view;
|
||||
},
|
||||
|
||||
|
||||
view: function () {
|
||||
if (this.template.scale && !this.metric)
|
||||
view: function() {
|
||||
if (this.template.scale && !this.metric) {
|
||||
this.model = this.view * this.template.scale;
|
||||
else this.model = this.view;
|
||||
} else {
|
||||
this.model = this.view;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
ready: function () {this.view = this._view},
|
||||
|
||||
ready: function() {
|
||||
this.view = this._view;
|
||||
},
|
||||
|
||||
methods: {
|
||||
change: function () {this.$dispatch('input-changed')}
|
||||
change: function() {
|
||||
this.$dispatch("input-changed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,41 +1,14 @@
|
||||
/******************************************************************************\
|
||||
"use strict";
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict';
|
||||
|
||||
const api = require('./api');
|
||||
const modbus = require('./modbus.js');
|
||||
const api = require("./api");
|
||||
const modbus = require("./modbus.js");
|
||||
const merge = require("lodash.merge");
|
||||
|
||||
module.exports = {
|
||||
template: '#tool-view-template',
|
||||
props: ['config', 'template', 'state'],
|
||||
template: "#tool-view-template",
|
||||
props: [ "config", "template", "state" ],
|
||||
|
||||
data: function () {
|
||||
data: function() {
|
||||
return {
|
||||
address: 0,
|
||||
value: 0,
|
||||
@@ -110,47 +83,49 @@ module.exports = {
|
||||
unsupported: true
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
components: {
|
||||
'modbus-reg': require('./modbus-reg.js')
|
||||
"modbus-reg": require("./modbus-reg.js")
|
||||
},
|
||||
|
||||
watch: {
|
||||
'state.mr': function () { this.value = this.state.mr }
|
||||
"state.mr": function() {
|
||||
this.value = this.state.mr;
|
||||
}
|
||||
},
|
||||
|
||||
events: {
|
||||
'input-changed': function () {
|
||||
this.$dispatch('config-changed');
|
||||
"input-changed": function() {
|
||||
this.$dispatch("config-changed");
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
ready: function () {
|
||||
ready: function() {
|
||||
this.value = this.state.mr;
|
||||
},
|
||||
|
||||
computed: {
|
||||
regs_tmpl: function () {
|
||||
return this.template['modbus-spindle'].regs;
|
||||
regs_tmpl: function() {
|
||||
return this.template["modbus-spindle"].regs;
|
||||
},
|
||||
|
||||
tool_type: function () {
|
||||
return this.config.tool['tool-type'].toUpperCase();
|
||||
tool_type: function() {
|
||||
return this.config.tool["tool-type"].toUpperCase();
|
||||
},
|
||||
|
||||
selected_tool: function () {
|
||||
return this.config.tool['selected-tool'];
|
||||
selected_tool: function() {
|
||||
return this.config.tool["selected-tool"];
|
||||
},
|
||||
|
||||
is_pwm_spindle: function () {
|
||||
return this.selected_tool == 'pwm';
|
||||
is_pwm_spindle: function() {
|
||||
return this.selected_tool == "pwm";
|
||||
},
|
||||
|
||||
is_modbus: function () {
|
||||
is_modbus: function() {
|
||||
switch (this.selected_tool) {
|
||||
case "disabled":
|
||||
case "laser":
|
||||
@@ -163,26 +138,26 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
modbus_status: function () {
|
||||
modbus_status: function() {
|
||||
return modbus.status_to_string(this.state.mx);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
change_selected_tool: function () {
|
||||
const selectedToolSettings = this.config['selected-tool-settings'] || {};
|
||||
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']);
|
||||
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']);
|
||||
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) {
|
||||
show_tool_settings: function(key) {
|
||||
switch (true) {
|
||||
case key === "tool-type":
|
||||
case key === "selected-tool":
|
||||
@@ -206,72 +181,72 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
get_reg_type: function (reg) {
|
||||
return this.regs_tmpl.template['reg-type'].values[this.state[reg + 'vt']];
|
||||
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_addr: function(reg) {
|
||||
return this.state[`${reg}va`];
|
||||
},
|
||||
|
||||
get_reg_value: function (reg) {
|
||||
return this.state[reg + 'vv'];
|
||||
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;
|
||||
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');
|
||||
show_modbus_field: function(key) {
|
||||
return key != "regs" && (key != "multi-write" || this.tool_type == "CUSTOM MODBUS VFD");
|
||||
},
|
||||
|
||||
read: function (e) {
|
||||
read: function(e) {
|
||||
e.preventDefault();
|
||||
api.put('modbus/read', { address: this.address });
|
||||
api.put("modbus/read", { address: this.address });
|
||||
},
|
||||
|
||||
write: function (e) {
|
||||
write: function(e) {
|
||||
e.preventDefault();
|
||||
api.put('modbus/write', { address: this.address, value: this.value });
|
||||
api.put("modbus/write", { address: this.address, value: this.value });
|
||||
},
|
||||
|
||||
customize: function (e) {
|
||||
customize: function(e) {
|
||||
e.preventDefault();
|
||||
this.config.tool['tool-type'] = 'Custom Modbus VFD';
|
||||
this.config.tool["tool-type"] = "Custom Modbus VFD";
|
||||
|
||||
const regs = this.config['modbus-spindle'].regs;
|
||||
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);
|
||||
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');
|
||||
this.$dispatch("config-changed");
|
||||
},
|
||||
|
||||
clear: function (e) {
|
||||
clear: function(e) {
|
||||
e.preventDefault();
|
||||
this.config.tool['tool-type'] = 'Custom Modbus VFD';
|
||||
this.config.tool["tool-type"] = "Custom Modbus VFD";
|
||||
|
||||
const regs = this.config['modbus-spindle'].regs;
|
||||
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;
|
||||
regs[i]["reg-type"] = "disabled";
|
||||
regs[i]["reg-addr"] = 0;
|
||||
regs[i]["reg-value"] = 0;
|
||||
}
|
||||
|
||||
this.$dispatch('config-changed');
|
||||
this.$dispatch("config-changed");
|
||||
},
|
||||
|
||||
reset_failures: function (e) {
|
||||
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');
|
||||
const regs = this.config["modbus-spindle"].regs;
|
||||
for (let reg = 0; reg < regs.length; reg++) {
|
||||
this.$dispatch("send", `$${reg}vr=0`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,58 +1,47 @@
|
||||
/******************************************************************************\
|
||||
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
|
||||
\******************************************************************************/
|
||||
|
||||
'use strict'
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
replace: true,
|
||||
template: '{{text}}<span class="unit">{{metric ? unit : iunit}}</span>',
|
||||
props: ['value', 'precision', 'unit', 'iunit', 'scale'],
|
||||
|
||||
props: [ "value", "precision", "unit", "iunit", "scale" ],
|
||||
|
||||
computed: {
|
||||
metric: function () {return !this.$root.state.imperial},
|
||||
metric: {
|
||||
cache: false,
|
||||
get: function() {
|
||||
return this.$root.display_units === "METRIC";
|
||||
}
|
||||
},
|
||||
|
||||
text: function() {
|
||||
let value = this.value;
|
||||
if (typeof value == "undefined") {
|
||||
return "";
|
||||
}
|
||||
|
||||
text: function () {
|
||||
var value = this.value;
|
||||
if (typeof value == 'undefined') return '';
|
||||
|
||||
if (!this.metric) value /= this.scale;
|
||||
if (!this.metric) {
|
||||
value /= this.scale;
|
||||
}
|
||||
|
||||
return (1 * value.toFixed(this.precision)).toLocaleString();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
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;
|
||||
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