Merge pull request #73 from OneFinityCNC/1.0.10-devel

Merge "1.0.10 devel"
This commit is contained in:
David A. Carley
2022-09-09 12:38:49 -07:00
committed by GitHub
199 changed files with 22913 additions and 19658 deletions

7
.devcontainer/Dockerfile Normal file
View 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

View 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
View 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
View 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
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View 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
%

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View 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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

5593
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -1 +1,2 @@
watchdog==0.10.6
evdev==1.6.0

View File

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

View File

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

View File

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

View File

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

View File

@@ -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" "$@"

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -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/"

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
Section "ServerFlags"
Option "DontVTSwitch" "on"
EndSection

View File

@@ -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',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,65 +1,37 @@
/******************************************************************************\
This file is part of the Buildbotics firmware.
Copyright (c) 2015 - 2018, Buildbotics LLC
All rights reserved.
This file ("the software") is free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License,
version 2 as published by the Free Software Foundation. You should
have received a copy of the GNU General Public License, version 2
along with the software. If not, see <http://www.gnu.org/licenses/>.
The software is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the software. If not, see
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
"use strict";
module.exports = {
template: '#axis-control-template',
props: ['axes', 'colors', 'enabled', 'adjust', 'step'],
template: "#axis-control-template",
props: [ "axes", "colors", "enabled", "adjust", "step" ],
methods: {
jog: function (axis, ring, direction) {
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 ? "" : "%");
}
}
}
};

View File

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

View File

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

View File

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

View File

@@ -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 = {
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
'/': '&#x2F;', '`': '&#x60;', '=': '&#x3D;'}
"use strict";
const entityMap = {
"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;",
"/": "&#x2F;", "`": "&#x60;", "=": "&#x3D;" };
function escapeHTML(s) {
return s.replace(/[&<>"'`=\/]/g, function (c) {return entityMap[c]})
return s.replace(/[&<>"'`=\\/]/g, function(c) {
return entityMap[c];
});
}
module.exports = {
template: '#gcode-viewer-template',
template: "#gcode-viewer-template",
data: function () {
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);
}
}
}
};

View File

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

View File

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

View File

@@ -1,42 +1,13 @@
/******************************************************************************\
This file is part of the Buildbotics firmware.
Copyright (c) 2015 - 2018, Buildbotics LLC
All rights reserved.
This file ("the software") is free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License,
version 2 as published by the Free Software Foundation. You should
have received a copy of the GNU General Public License, version 2
along with the software. If not, see <http://www.gnu.org/licenses/>.
The software is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the software. If not, see
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
"use strict";
module.exports = {
template: '#io-view-template',
props: ['config', 'template', 'state'],
template: "#io-view-template",
props: [ "config", "template", "state" ],
events: {
'input-changed': function() {
this.$dispatch('config-changed');
"input-changed": function() {
this.$dispatch("config-changed");
return false;
}
}
}
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,42 +1,14 @@
/******************************************************************************\
This file is part of the Buildbotics firmware.
Copyright (c) 2015 - 2018, Buildbotics LLC
All rights reserved.
This file ("the software") is free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License,
version 2 as published by the Free Software Foundation. You should
have received a copy of the GNU General Public License, version 2
along with the software. If not, see <http://www.gnu.org/licenses/>.
The software is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the software. If not, see
<http://www.gnu.org/licenses/>.
For information regarding this software email:
"Joseph Coffland" <joseph@buildbotics.com>
\******************************************************************************/
'use strict'
module.exports = {
template: '#settings-view-template',
props: ['config', 'template'],
template: "#settings-view-template",
attached: function() {
this.svelteComponent = SvelteComponents.createComponent(
"SettingsView",
document.getElementById("settings")
);
},
events: {
'input-changed': function() {
this.$dispatch('config-changed');
return false;
detached: function() {
this.svelteComponent.$destroy();
}
}
}
};

View File

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

View File

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

View File

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

View File

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