Merge pull request #71 from dacarley/5

6 - Improved uploading, date/time/timezone setting, etc.
This commit is contained in:
David A. Carley
2022-08-23 15:33:31 -07:00
committed by GitHub
81 changed files with 2931 additions and 3657 deletions

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
@@ -74,15 +66,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 +105,4 @@ $(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
.PHONY: all install clean tidy pkg gplan lint pylint jshint bbserial

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
%

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

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

5
package-lock.json generated
View File

@@ -1,12 +1,13 @@
{
"name": "bbctrl",
"version": "1.0.10b4",
"version": "1.0.10b7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "bbctrl",
"version": "1.0.10b4",
"version": "1.0.10b7",
"hasInstallScript": true,
"license": "GPL-3.0+",
"dependencies": {
"browserify": "^17.0.0",

View File

@@ -1,9 +1,12 @@
{
"name": "bbctrl",
"version": "1.0.10b4",
"version": "1.0.10b7",
"homepage": "https://onefinitycnc.com/",
"repository": "https://github.com/OneFinityCNC/onefinity",
"license": "GPL-3.0+",
"scripts": {
"postinstall": "cd src/svelte-components && npm i"
},
"dependencies": {
"browserify": "^17.0.0",
"jshint": "^2.13.4",

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

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,3 @@
#!/bin/bash -ex
pip3 download -d python-packages -r requirements.txt
pip3 download -d installer/python-packages -r requirements.txt

View File

@@ -12,39 +12,41 @@ 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 config_hdmi_boost=8 hdmi_force_hotplug=1 hdmi_group=2 hdmi_mode=82
./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
@@ -73,14 +75,20 @@ sed -i '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 '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/
cp ./installer/config/11-automount.rules /etc/udev/rules.d/
sed -i 's/^\(MountFlags=slave\)/#\1/' \
/lib/systemd/system/systemd-udevd.service
REBOOT=true
@@ -94,19 +102,30 @@ if [ $? -ne 0 ]; then
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
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
if [ ! -e /usr/share/plymouth/themes/onefinity/onefinity.plymouth ]; then
mkdir -p /usr/share/plymouth/themes/onefinity/
cp -av installer/splash/* /usr/share/plymouth/themes/onefinity/
plymouth-set-default-theme -R onefinity
fi
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
@@ -118,7 +137,7 @@ 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
@@ -127,7 +146,7 @@ if $UPDATE_PY; then
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
@@ -138,26 +157,26 @@ if $UPDATE_PY; then
fi
# 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

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

View File

@@ -29,14 +29,13 @@ setup(
]
},
scripts=[
'scripts/update-bbctrl',
'scripts/upgrade-bbctrl',
'scripts/sethostname',
'scripts/reset-video',
'scripts/config-wifi',
'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

@@ -4,7 +4,7 @@ module.exports = {
attached: function () {
this.svelteComponent = SvelteComponents.createComponent(
"AdminNetworkView",
document.getElementById("svelte-root")
document.getElementById("admin-network")
);
},

View File

@@ -4,7 +4,6 @@ const api = require("./api");
const cookie = require("./cookie")("bbctrl-");
const Sock = require("./sock");
SvelteComponents.initNetworkInfo();
SvelteComponents.createComponent("DialogHost",
document.getElementById("svelte-dialog-host")
);
@@ -136,6 +135,7 @@ module.exports = new Vue({
watch: {
display_units: function (value) {
localStorage.setItem("display_units", value);
SvelteComponents.setDisplayUnits(value);
},
},
@@ -214,6 +214,10 @@ module.exports = new Vue({
ready: function () {
$(window).on("hashchange", this.parse_hash);
this.connect();
SvelteComponents.registerControllerMethods({
dispatch: (...args) => this.$dispatch(...args)
});
},
methods: {
@@ -289,15 +293,6 @@ module.exports = new Vue({
update_object(this.config, config, true);
this.parse_hash();
if (!this.devModChecked) {
this.devModChecked = true;
if (this.config.devmode) {
SvelteComponents.createComponent("Devmode",
document.getElementById("svelte-devmode-host")
);
}
}
if (!this.checkedUpgrade) {
this.checkedUpgrade = true;

View File

@@ -9,6 +9,7 @@ module.exports = {
data: function () {
return {
current_time: "",
mach_units: this.$root.state.metric ? "METRIC" : "IMPERIAL",
mdi: '',
last_file: undefined,
@@ -19,22 +20,6 @@ module.exports = {
history: [],
speed_override: 1,
feed_override: 1,
manual_home: {
x: false,
y: false,
z: false,
a: false,
b: false,
c: false
},
position_msg: {
x: false,
y: false,
z: false,
a: false,
b: false,
c: false
},
jog_incr_amounts: {
"METRIC": {
fine: 0.1,
@@ -49,7 +34,6 @@ module.exports = {
large: 5,
}
},
axis_position: 0,
jog_incr: localStorage.getItem("jog_incr") || 'small',
jog_step: cookie.get_bool('jog-step'),
jog_adjust: parseInt(cookie.get('jog-adjust', 2)),
@@ -253,10 +237,18 @@ module.exports = {
ready: function () {
this.load();
setInterval(() => {
this.current_time = new Date().toLocaleTimeString();
}, 1000);
SvelteComponents.registerControllerMethods({
stop: (...args) => this.stop(...args),
send: (...args) => this.send(...args),
goto_zero: (...args) => this.goto_zero(...args)
goto_zero: (...args) => this.goto_zero(...args),
isAxisHomed: (axis) => this[axis].homed,
unhome: (...args) => this.unhome(...args),
set_position: (...args) => this.set_position(...args),
set_home: (...args) => this.set_home(...args)
});
},
@@ -398,18 +390,13 @@ module.exports = {
return;
}
const fd = new FormData();
fd.append('gcode', file);
try {
await api.upload('file', fd);
SvelteComponents.showDialog("Upload", {
file,
onComplete: () => {
this.last_file_time = undefined; // Force reload
this.$broadcast('gcode-reload', file.name);
} catch (err) {
api.alert('Upload failed', err)
}
});
},
delete_current: function () {
@@ -433,23 +420,20 @@ module.exports = {
} else if (this[axis].homingMode != 'manual') {
api.put('home/' + axis);
} else {
this.manual_home[axis] = true;
SvelteComponents.showDialog("ManualHomeAxis", { axis });
}
},
set_home: function (axis, position) {
this.manual_home[axis] = false;
api.put('home/' + axis + '/set', { position: parseFloat(position) });
},
unhome: function (axis) {
this.position_msg[axis] = false;
api.put('home/' + axis + '/clear');
},
show_set_position: function (axis) {
this.axis_position = 0;
this.position_msg[axis] = true;
SvelteComponents.showDialog("SetAxisPosition", { axis });
},
show_toolpath_msg: function (axis) {
@@ -457,7 +441,6 @@ module.exports = {
},
set_position: function (axis, position) {
this.position_msg[axis] = false;
api.put('position/' + axis, { 'position': parseFloat(position) });
},

View File

@@ -1,31 +1,14 @@
'use strict'
module.exports = {
template: '#settings-view-template',
props: ['config', 'template'],
template: "#settings-view-template",
computed: {
display_units: {
cache: false,
get: function () {
return this.$root.display_units;
},
set: function (value) {
this.$root.display_units = value;
}
},
attached: function () {
this.svelteComponent = SvelteComponents.createComponent(
"SettingsView",
document.getElementById("settings")
);
},
events: {
'input-changed': function () {
this.$dispatch('config-changed');
return false;
}
},
methods: {
showScreenRotationDialog: function () {
SvelteComponents.showDialog("ScreenRotation");
}
}
detached: function() {
this.svelteComponent.$destroy();
}
};

View File

@@ -19,7 +19,6 @@ html(lang="en")
body(v-cloak)
#svelte-dialog-host
#svelte-devmode-host
#overlay(v-if="status != 'connected'")
span {{status}}

View File

@@ -1,3 +1,2 @@
script#admin-network-view-template(type="text/x-template")
#admin-network
#svelte-root

View File

@@ -162,48 +162,6 @@ script#control-view-template(type="text/x-template")
title=`Home {{'${axis}' | upper}} axis.`, style="height:60px;width:60px")
.fa.fa-home
message(:show.sync=`position_msg['${axis}']`)
h3(slot="header") Set {{'#{axis}' | upper}} axis position
div(slot="body")
.pure-form
.pure-control-group
label Position
input(v-model="axis_position",
@keyup.enter=`set_position('${axis}', axis_position)`)
p
div(slot="footer")
button.pure-button(@click=`position_msg['${axis}'] = false`)
| Cancel
button.pure-button(v-if=`${axis}.homed`,
@click=`unhome('${axis}')`) Unhome
button.pure-button.button-success(
@click=`set_position('${axis}', axis_position)`) Set
message(:show.sync=`manual_home['${axis}']`)
h3(slot="header") Manually home {{'#{axis}' | upper}} axis
div(slot="body")
p Set axis absolute position.
.pure-form
.pure-control-group
label Absolute
input(v-model="axis_position",
@keyup.enter=`set_home('${axis}', axis_position)`)
p
div(slot="footer")
button.pure-button(@click=`manual_home['${axis}'] = false`)
| Cancel
button.pure-button.button-success(
title=`Home {{'${axis}' | upper}} axis.`,
@click=`set_home('${axis}', axis_position)`) Set
tr(style="vertical-align: top;")
td
@@ -265,6 +223,11 @@ script#control-view-template(type="text/x-template")
td
table.info
tr
th Current Time
td
span {{current_time}}
tr
th Remaining
td(title="Total run time (days:hours:mins:secs)").
@@ -273,12 +236,7 @@ script#control-view-template(type="text/x-template")
tr
th ETA
td.eta {{eta}}
tr
th Line
td
| {{0 <= state.line ? state.line : 0 | number}}
span(v-if="toolpath.lines")
| &nbsp;of {{toolpath.lines | number}}
tr
th Progress
td.progress

View File

@@ -1,53 +1,2 @@
script#settings-view-template(type="text/x-template")
#settings
h1 Settings
.pure-form.pure-form-aligned
fieldset
h2 Screen
.pure-control-group
label(for="screen-rotation")
button.pure-button(name="screen-rotation", @click="showScreenRotationDialog") Change Screen Rotation
fieldset
h2 Probe Dimensions
templated-input(v-for="templ in template.probe", v-if="$key !== 'probe-diameter'", :name="$key"
:model.sync="config.probe[$key]", :template="templ")
fieldset
h2 GCode
templated-input(v-for="templ in template.gcode", :name="$key",
:model.sync="config.gcode[$key]", :template="templ")
fieldset
h2 Path Accuracy
templated-input(name="max-deviation",
:model.sync="config.settings['max-deviation']",
:template="template.settings['max-deviation']")
p.
Lower #[tt max-deviation] to follow the programmed path more precisely
but at a slower speed.
p.
In order to improve traversal speed, the path planner may merge
consecutive moves or round off sharp corners if doing so would deviate
from the program path by less than #[tt max-deviation].
- var base = '//linuxcnc.org/docs/html/gcode/g-code.html'
p.
GCode commands
#[a(href=base + "#gcode:g61", target="_blank") G61, G61.1] and
#[a(href=base + "#gcode:g64", target="_blank") G64] also affect path
planning accuracy.
h2 Cornering Speed (Advanced)
templated-input(name="junction-accel",
:model.sync="config.settings['junction-accel']",
:template="template.settings['junction-accel']")
p.
Junction acceleration limits the cornering speed the planner will
allow. Increasing this value will allow for faster traversal of
corners but may cause the planner to violate axis jerk limits and
stall the motors. Use with caution.

View File

@@ -1,207 +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> #
# #
################################################################################
import os
import sys
import traceback
import signal
import bbctrl
import bbctrl.Cmd as Cmd
class AVREmu(object):
def __init__(self, ctrl):
self.ctrl = ctrl
self.log = ctrl.log.get('AVREmu')
self.avrOut = None
self.avrIn = None
self.i2cOut = None
self.read_cb = None
self.write_cb = None
self.pid = None
def close(self):
# Close pipes
def _close(fd, withHandle):
if fd is None: return
try:
if withHandle: self.ctrl.ioloop.remove_handler(fd)
except: pass
try:
os.close(fd)
except: pass
_close(self.avrOut, True)
_close(self.avrIn, True)
_close(self.i2cOut, False)
self.avrOut, self.avrIn, self.i2cOut = None, None, None
# Kill process and wait for it
if self.pid is not None:
os.kill(self.pid, signal.SIGKILL)
os.waitpid(self.pid, 0)
self.pid = None
def _start(self):
try:
self.close()
# Create pipes
stdinFDs = os.pipe()
stdoutFDs = os.pipe()
i2cFDs = os.pipe()
self.pid = os.fork()
if not self.pid:
# Dup child ends
os.dup2(stdinFDs[0], 0)
os.dup2(stdoutFDs[1], 1)
os.dup2(i2cFDs[0], 3)
# Close orig fds
os.close(stdinFDs[0])
os.close(stdoutFDs[1])
os.close(i2cFDs[0])
# Close parent ends
os.close(stdinFDs[1])
os.close(stdoutFDs[0])
os.close(i2cFDs[1])
cmd = ['bbemu']
if self.ctrl.args.fast_emu: cmd.append('--fast')
os.execvp(cmd[0], cmd)
os._exit(1) # In case of failure
# Parent, close child ends
os.close(stdinFDs[0])
os.close(stdoutFDs[1])
os.close(i2cFDs[0])
# Non-blocking IO
os.set_blocking(stdinFDs[1], False)
os.set_blocking(stdoutFDs[0], False)
os.set_blocking(i2cFDs[1], False)
self.avrOut = stdinFDs[1]
self.avrIn = stdoutFDs[0]
self.i2cOut = i2cFDs[1]
ioloop = self.ctrl.ioloop
ioloop.add_handler(self.avrOut, self._avr_write_handler,
ioloop.WRITE | ioloop.ERROR)
ioloop.add_handler(self.avrIn, self._avr_read_handler,
ioloop.READ | ioloop.ERROR)
self.write_enabled = True
except Exception:
self.close()
self.log.exception('Internal error: Failed to start bbemu')
def set_handlers(self, read_cb, write_cb):
if self.read_cb is not None or self.write_cb is not None:
raise Exception('AVR handler already set')
self.read_cb = read_cb
self.write_cb = write_cb
self._start()
def enable_write(self, enable):
if self.avrOut is None: return
flags = self.ctrl.ioloop.WRITE if enable else 0
self.ctrl.ioloop.update_handler(self.avrOut, flags)
self.write_enabled = enable
def _avr_write(self, data):
try:
length = os.write(self.avrOut, data)
self.continue_write = length and length == len(data)
return length
except BlockingIOError: pass
except BrokenPipeError: pass
return 0
def _avr_write_handler(self, fd, events):
if self.avrOut is None: return
if events & self.ctrl.ioloop.ERROR:
self._start()
return
try:
while True:
self.continue_write = False
self.write_cb(self._avr_write)
if not self.continue_write: break
except Exception as e:
self.log.warning('AVR write handler error: %s',
traceback.format_exc())
def _avr_read_handler(self, fd, events):
if self.avrIn is None: return
if events & self.ctrl.ioloop.ERROR:
self._start()
return
try:
data = os.read(self.avrIn, 4096)
if data is not None: self.read_cb(data)
except Exception as e:
self.log.warning('AVR read handler error: %s %s' %
(data, traceback.format_exc()))
def i2c_command(self, cmd, byte = None, word = None, block = None):
if byte is not None: data = chr(byte)
elif word is not None: data = word
elif block is not None: data = block
else: data = ''
try:
if self.i2cOut is not None:
os.write(self.i2cOut, bytes(cmd + data + '\n', 'utf-8'))
except BrokenPipeError: pass

View File

@@ -25,9 +25,6 @@ class Ctrl(object):
self.log.get('Ctrl').info('Starting %s' % self.id)
try:
if args.demo:
self.avr = bbctrl.AVREmu(self)
else:
self.avr = bbctrl.AVR(self)
self.i2c = bbctrl.I2C(args.i2c_port, args.demo)

View File

@@ -1,34 +1,8 @@
################################################################################
# #
# This file is part of the Buildbotics firmware. #
# #
# Copyright (c) 2015 - 2018, Buildbotics LLC #
# All rights reserved. #
# #
# This file ("the software") is free software: you can redistribute it #
# and/or modify it under the terms of the GNU General Public License, #
# version 2 as published by the Free Software Foundation. You should #
# have received a copy of the GNU General Public License, version 2 #
# along with the software. If not, see <http://www.gnu.org/licenses/>. #
# #
# The software is distributed in the hope that it will be useful, but #
# WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
# Lesser General Public License for more details. #
# #
# You should have received a copy of the GNU Lesser General Public #
# License along with the software. If not, see #
# <http://www.gnu.org/licenses/>. #
# #
# For information regarding this software email: #
# "Joseph Coffland" <joseph@buildbotics.com> #
# #
################################################################################
import os
import tempfile
import bbctrl
import glob
import html
import tornado
from tornado import gen
from tornado.web import HTTPError
@@ -36,17 +10,32 @@ from tornado.web import HTTPError
def safe_remove(path):
try:
os.unlink(path)
except OSError: pass
except OSError:
pass
@tornado.web.stream_request_body
class FileHandler(bbctrl.APIHandler):
def prepare(self): pass
def prepare(self):
if self.request.method == 'PUT':
self.request.connection.set_max_body_size(2 ** 30)
self.uploadFilename = self.request.path.split('/')[-1] \
.replace('\\', '/') \
.replace('#', '-') \
.replace('?', '-')
self.uploadFile = tempfile.NamedTemporaryFile("wb")
def data_received(self, data):
if self.request.method == 'PUT':
self.uploadFile.write(data)
def delete_ok(self, filename):
if not filename:
# Delete everything
for path in glob.glob(self.get_upload('*')): safe_remove(path)
for path in glob.glob(self.get_upload('*')):
safe_remove(path)
self.get_ctrl().preplanner.delete_all_plans()
self.get_ctrl().state.clear_files()
@@ -57,26 +46,29 @@ class FileHandler(bbctrl.APIHandler):
self.get_ctrl().preplanner.delete_plans(filename)
self.get_ctrl().state.remove_file(filename)
def put_ok(self, *args):
gcode = self.request.files['gcode'][0]
filename = os.path.basename(gcode['filename'].replace('\\', '/'))
filename = filename.replace('#', '-').replace('?', '-')
if not os.path.exists(self.get_upload()):
os.mkdir(self.get_upload())
if not os.path.exists(self.get_upload()): os.mkdir(self.get_upload())
filename = self.get_upload(self.uploadFilename).encode('utf8')
safe_remove(filename)
os.link(self.uploadFile.name, filename)
with open(self.get_upload(filename).encode('utf8'), 'wb') as f:
f.write(gcode['body'])
os.sync()
self.uploadFile.close()
self.get_ctrl().preplanner.invalidate(filename)
self.get_ctrl().state.add_file(filename)
self.get_log('FileHandler').info('GCode received: ' + filename)
del(self.uploadFile)
self.get_ctrl().preplanner.invalidate(self.uploadFilename)
self.get_ctrl().state.add_file(self.uploadFilename)
self.get_log('FileHandler').info(
'GCode received: ' + self.uploadFilename)
del(self.uploadFilename)
@gen.coroutine
def get(self, filename):
if not filename: raise HTTPError(400, 'Missing filename')
if not filename:
raise HTTPError(400, 'Missing filename')
filename = os.path.basename(filename)
try:
@@ -84,6 +76,7 @@ class FileHandler(bbctrl.APIHandler):
self.write(f.read())
except Exception:
self.get_ctrl().state.select_file('')
raise HTTPError(400, "Unable to read file - doesn't appear to be GCode.")
raise HTTPError(
400, "Unable to read file - doesn't appear to be GCode.")
self.get_ctrl().state.select_file(filename)

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,24 @@
import traceback
import copy
import json
import uuid
import os
import socket
import bbctrl
import iw_parse
from tornado import gen
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
def call_get_output(cmd):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
s = p.communicate()[0].decode('utf-8')
if p.returncode:
raise HTTPError(400, 'Command failed')
return s
class UploadChangeHandler(FileSystemEventHandler):
def __init__(self, state):
self.state = state
@@ -64,6 +76,36 @@ class State(object):
self), self.ctrl.get_upload(), recursive=True)
observer.start()
self._updateNetworkInfo()
@gen.coroutine
def _updateNetworkInfo(self):
try:
ipAddresses = call_get_output(['hostname', '-I']).split()
except:
ipAddresses = ""
hostname = socket.gethostname()
try:
wifi = json.loads(call_get_output(['config-wifi', '-j']))
except:
wifi = {'enabled': False}
try:
lines = iw_parse.call_iwlist().decode("utf-8").split("\n")
wifi['networks'] = iw_parse.get_parsed_cells(lines)
except:
wifi['networks'] = []
self.set('networkInfo', {
'ipAddresses': ipAddresses,
'hostname': hostname,
'wifi': wifi
})
self.timeout = self.ctrl.ioloop.call_later(5, self._updateNetworkInfo)
def reset(self):
# Unhome all motors
for i in range(4):
@@ -81,8 +123,6 @@ class State(object):
if not os.path.exists(upload):
os.mkdir(upload)
from shutil import copy
copy(bbctrl.get_resource('http/buildbotics.nc'), upload)
for path in os.listdir(upload):
if os.path.isfile(upload + '/' + path):
@@ -170,8 +210,11 @@ class State(object):
return name
def has(self, name): return self.resolve(name) in self.vars
def set_callback(self, name, cb): self.callbacks[self.resolve(name)] = cb
def has(self, name):
return self.resolve(name) in self.vars
def set_callback(self, name, cb):
self.callbacks[self.resolve(name)] = cb
def set(self, name, value):
name = self.resolve(name)
@@ -233,7 +276,8 @@ class State(object):
self.listeners.append(listener)
listener(self.vars)
def remove_listener(self, listener): self.listeners.remove(listener)
def remove_listener(self, listener):
self.listeners.remove(listener)
def set_machine_vars(self, vars):
# Record all machine vars, indexed or otherwise
@@ -284,7 +328,8 @@ class State(object):
if motor_axis == axis.lower() and self.vars.get('%dme' % motor, 0):
return motor
def is_axis_homed(self, axis): return self.get('%s_homed' % axis, False)
def is_axis_homed(self, axis):
return self.get('%s_homed' % axis, False)
def is_axis_enabled(self, axis):
motor = self.find_motor(axis)

View File

@@ -1,6 +1,5 @@
import os
import re
import json
import tornado
import sockjs.tornado
import datetime
@@ -10,7 +9,6 @@ from tornado.web import HTTPError
from tornado import gen
import bbctrl
import iw_parse
def call_get_output(cmd):
@@ -23,11 +21,17 @@ def call_get_output(cmd):
class RebootHandler(bbctrl.APIHandler):
def put_ok(self):
subprocess.Popen('reboot')
subprocess.Popen(['plymouth', 'show-splash'])
subprocess.Popen(['plymouth', 'change-mode', '--shutdown'])
subprocess.Popen(['killall', 'xinit'])
subprocess.Popen(['reboot'])
class ShutdownHandler(bbctrl.APIHandler):
def put_ok(self):
subprocess.Popen(['plymouth', 'show-splash'])
subprocess.Popen(['plymouth', 'change-mode', '--shutdown'])
subprocess.Popen(['killall', 'xinit'])
subprocess.Popen(['shutdown', '-h', 'now'])
@@ -101,31 +105,6 @@ class HostnameHandler(bbctrl.APIHandler):
class NetworkHandler(bbctrl.APIHandler):
def get(self):
try:
ipAddresses = call_get_output(['hostname', '-I']).split()
except:
ipAddresses = ""
hostname = socket.gethostname()
try:
wifi = json.loads(call_get_output(['config-wifi', '-j']))
except:
wifi = {'enabled': False}
try:
lines = iw_parse.call_iwlist().decode("utf-8").split("\n")
wifi['networks'] = iw_parse.get_parsed_cells(lines)
except:
wifi['networks'] = []
self.write_json({
'ipAddresses': ipAddresses,
'hostname': hostname,
'wifi': wifi
})
def put(self):
if self.get_ctrl().args.demo:
raise HTTPError(400, 'Cannot configure WiFi in demo mode')
@@ -267,14 +246,6 @@ class HomeHandler(bbctrl.APIHandler):
self.get_ctrl().mach.home(axis)
class DevmodeHandler(bbctrl.APIHandler):
def put_ok(self, command, *args):
if command == "/probe":
self.get_ctrl().mach.fake_probe_contact()
else:
raise HTTPError(400, 'Not implemented')
class StartHandler(bbctrl.APIHandler):
def put_ok(self): self.get_ctrl().mach.start()
@@ -383,13 +354,34 @@ class ScreenRotationHandler(bbctrl.APIHandler):
text = config.read()
text = transformationMatrixPattern.sub(r'\1\2\3\5', text)
if rotated:
text = matchIsTouchscreenPattern.sub(r'\1\2\3\2Option "TransformationMatrix" "-1 0 1 0 -1 1 0 0 1"\1\4', text)
text = matchIsTouchscreenPattern.sub(
r'\1\2\3\2Option "TransformationMatrix" "-1 0 1 0 -1 1 0 0 1"\1\4', text)
with open("/usr/share/X11/xorg.conf.d/40-libinput.conf", 'wt') as config:
config.write(text)
subprocess.run('reboot')
class TimeHandler(bbctrl.APIHandler):
def get(self):
timeinfo = call_get_output(['timedatectl'])
timezones = call_get_output(
['timedatectl', 'list-timezones', '--no-pager'])
self.get_log('TimeHandler').info(
'Time stuff: {}, {}'.format(timeinfo, timezones))
self.write_json({
'timeinfo': timeinfo,
'timezones': timezones
})
def put_ok(self):
datetime = self.json['datetime']
timezone = self.json['timezone']
subprocess.Popen(['timedatectl', 'set-time', datetime])
subprocess.Popen(['timedatectl', 'set-timezone', timezone])
# Base class for Web Socket connections
class ClientConnection(object):
def __init__(self, app):
@@ -509,7 +501,6 @@ class Web(tornado.web.Application):
(r'/api/file(/[^/]+)?', bbctrl.FileHandler),
(r'/api/path/([^/]+)((/positions)|(/speeds))?', PathHandler),
(r'/api/home(/[xyzabcXYZABC]((/set)|(/clear))?)?', HomeHandler),
(r'/api/devmode((/probe))?', DevmodeHandler),
(r'/api/start', StartHandler),
(r'/api/estop', EStopHandler),
(r'/api/clear', ClearHandler),
@@ -526,6 +517,7 @@ class Web(tornado.web.Application):
(r'/api/jog', JogHandler),
(r'/api/video', bbctrl.VideoHandler),
(r'/api/screen-rotation', ScreenRotationHandler),
(r'/api/time', TimeHandler),
(r'/(.*)', StaticFileHandler,
{'path': bbctrl.get_resource('http/'),
'default_filename': 'index.html'}),
@@ -545,7 +537,8 @@ class Web(tornado.web.Application):
print('Listening on http://%s:%d/' % (args.addr, args.port))
def opened(self, ctrl): ctrl.clear_timeout()
def opened(self, ctrl):
ctrl.clear_timeout()
def closed(self, ctrl):
# Time out clients in demo mode

View File

@@ -1,33 +1,5 @@
#!/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 os
import sys
import signal
import tornado
@@ -53,13 +25,11 @@ from bbctrl.Comm import Comm
from bbctrl.CommandQueue import CommandQueue
from bbctrl.Camera import Camera, VideoHandler
from bbctrl.AVR import AVR
from bbctrl.AVREmu import AVREmu
from bbctrl.IOLoop import IOLoop
from bbctrl.MonitorTemp import MonitorTemp
import bbctrl.Cmd as Cmd
import bbctrl.v4l2 as v4l2
import bbctrl.Log as log
import bbctrl.ObjGraph as ObjGraph
ctrl = None
@@ -85,36 +55,6 @@ def time_str():
return datetime.datetime.now().strftime('%Y%m%d-%H:%M:%S')
class Debugger:
def __init__(self, ioloop, freq = 60 * 15, depth = 100):
self.ioloop = ioloop
self.freq = freq
self.depth = depth
self._callback()
def _callback(self):
with open('bbctrl-debug-%s.log' % time_str(), 'w') as log:
def line(name):
log.write('==== ' + name + ' ' + '=' * (74 - len(name)) + '\n')
line('Common')
ObjGraph.show_most_common_types(limit = self.depth, file = log)
log.write('\n')
line('Growth')
ObjGraph.show_growth(limit = self.depth, file = log)
log.write('\n')
line('New IDs')
ObjGraph.get_new_ids(limit = self.depth, file = log)
log.flush()
self.ioloop.call_later(self.freq, self._callback)
def parse_args():
parser = argparse.ArgumentParser(
description='Buildbotics Machine Controller')
@@ -170,16 +110,15 @@ def run():
# Create ioloop
ioloop = tornado.ioloop.IOLoop.current()
# Set ObjGraph signal handler
if args.debug: Debugger(ioloop, args.debug)
# Start server
web = Web(args, ioloop)
try:
ioloop.start()
except KeyboardInterrupt: on_exit()
except KeyboardInterrupt:
on_exit()
if __name__ == '__main__': run()
if __name__ == '__main__':
run()

View File

@@ -1,403 +0,0 @@
G21
(File: 'buildbotics_logo.tpl')
G0 Z3
F1600
M3 S10000
M6 T2
G0 X59.25 Y5.85
G1 Z-1.5
G1 X61.68 Y6.7
G1 X63.86 Y8.07
G1 X65.68 Y9.89
G1 X67.05 Y12.07
G1 X67.9 Y14.5
G1 X68.2 Y17.09
G1 Y56.6
G1 X67.73 Y59.04
G1 X50.8
G1 Y34.9
G1 X50.65 Y34.55
G1 X50.3 Y34.4
G1 X23.46
G1 X23.1 Y34.55
G1 X22.96 Y34.9
G1 X22.98 Y49.88
G1 X22.96 Y59.05
G1 X22.41
G1 X19.26
G1 X6.04
G1 X5.56 Y56.53
G1 Y17.09
G1 X5.85 Y14.5
G1 X6.7 Y12.07
G1 X8.07 Y9.89
G1 X9.89 Y8.07
G1 X12.07 Y6.7
G1 X14.5 Y5.85
G1 X17.09 Y5.56
G1 X56.67
G1 X59.25 Y5.85
G0 Z3
G0 X64.26 Y64.72
G1 Z-1.5
G1 X61.78 Y66.52
G1 X58.91 Y67.68
G1 X56.54 Y68.08
G1 X17.22
G1 X14.84 Y67.68
G1 X11.97 Y66.52
G1 X9.49 Y64.72
G1 X8.08 Y63.16
G1 X27.35
G1 X27.89 Y63.45
G1 X27.96 Y63.48
G1 X31.48 Y64.75
G1 X31.52 Y64.76
G1 X31.56 Y64.77
G1 X35.19 Y65.41
G1 X35.26
G1 X35.97 Y65.44
G1 X36.04 Y65.45
G1 X36.07
G1 X36.82 Y65.44
G1 X36.83
G1 X36.89
G1 X36.95
G1 X36.97
G1 X37.72 Y65.43
G1 X37.74
G1 X37.8
G1 X37.81
G1 X37.88
G1 X37.89
G1 X38.65 Y65.38
G1 X38.68
G1 X38.75 Y65.37
G1 X39.38 Y65.32
G1 X39.44 Y65.31
G1 X42.68 Y64.64
G1 X42.76 Y64.62
G1 X45.87 Y63.44
G1 X45.93 Y63.41
G1 X46.4 Y63.16
G1 X65.67
G1 X64.26 Y64.72
G0 Z3
G0 X36.88 Y9.4
G1 Z-1.5
G1 X37.31 Y9.64
G1 X39.58 Y13.48
G1 X39.63 Y13.6
G1 X39.65 Y13.73
G1 Y27.54
G1 X41.67
G1 Y25.39
G1 X41.75 Y25.12
G1 X41.97 Y24.93
G1 X46.41 Y22.92
G1 Y19.97
G1 X45.44
G1 X45.08 Y19.82
G1 X44.94 Y19.47
G1 Y13.73
G1 X45.08 Y13.38
G1 X45.44 Y13.23
G1 X49.94 Y13.24
G1 X50.29 Y13.39
G1 X50.44 Y13.74
G1 Y19.47
G1 X50.29 Y19.83
G1 X49.93 Y19.97
G1 X48.92
G1 Y23.61
G1 X48.84 Y23.88
G1 X48.63 Y24.06
G1 X44.19 Y26.12
G1 Y27.54
G1 X49.22
G1 X49.33 Y27.56
G1 X49.44 Y27.6
G1 X50.13 Y27.94
G1 X50.25 Y28.02
G1 X50.34 Y28.13
G1 X50.73 Y28.77
G1 X50.78 Y28.89
G1 X50.8 Y29.03
G1 Y33.05
G1 Y34.25
G1 Y34.65
G1 X50.66 Y35.01
G1 X50.3 Y35.15
G1 X23.46
G1 X23.1 Y35.01
G1 X22.96 Y34.65
G1 Y29.07
G1 X22.97 Y28.94
G1 X23.02 Y28.82
G1 X23.4 Y28.17
G1 X23.49 Y28.06
G1 X23.6 Y27.98
G1 X24.29 Y27.6
G1 X24.4 Y27.56
G1 X24.52 Y27.54
G1 X25.55
G1 Y26.4
G1 X23.4 Y25.52
G1 X23.17 Y25.33
G1 X23.09 Y25.06
G1 Y17.54
G1 X23.23 Y17.19
G1 X23.59 Y17.04
G1 X24.6
G1 Y10.36
G1 X24.62 Y10.23
G1 X24.66 Y10.11
G1 X24.8 Y9.88
G1 X24.88 Y9.77
G1 X24.99 Y9.68
G1 X25.25 Y9.54
G1 X25.37 Y9.5
G1 X25.49 Y9.48
G1 X26.53
G1 X26.65 Y9.49
G1 X26.76 Y9.54
G1 X27.01 Y9.66
G1 X27.12 Y9.74
G1 X27.21 Y9.85
G1 X27.35 Y10.09
G1 X27.41 Y10.22
G1 X27.43 Y10.35
G1 Y10.43
G1 X27.47 Y17.04
G1 X28.57
G1 X28.92 Y17.19
G1 X29.07 Y17.54
G1 Y24.64
G1 X30.72 Y25.3
G1 X30.95 Y25.49
G1 X31.03 Y25.77
G1 X31.02 Y27.54
G1 X34.03
G1 Y13.73
G1 X34.05 Y13.59
G1 X34.1 Y13.47
G1 X36.45 Y9.64
G1 X36.88 Y9.4
G0 Z3
G0 X49.94 Y10.82
G1 Z-1.5
G1 X50.29 Y10.97
G1 X50.44 Y11.32
G1 Y13.34
G1 X50.29 Y13.69
G1 X49.94 Y13.84
G1 X45.44
G1 X45.08 Y13.69
G1 X44.94 Y13.34
G1 Y11.32
G1 X45.08 Y10.97
G1 X45.44 Y10.82
G1 X49.94
G0 Z3
G0 X48.46 Y9.7
G1 Z-1.5
G1 X48.59 Y9.72
G1 X48.71 Y9.77
G1 X50.03 Y10.53
G1 X50.21 Y10.71
G1 X50.28 Y10.96
G1 X50.14 Y11.31
G1 X49.78 Y11.46
G1 X45.62
G1 X45.14 Y11.09
G1 X45.37 Y10.53
G1 X46.69 Y9.77
G1 X46.81 Y9.72
G1 X46.94 Y9.7
G1 X48.46
G0 Z3
G0 X50.3 Y34.4
G1 Z-1.5
G1 X50.66 Y34.55
G1 X50.8 Y34.9
G1 Y49.88
G1 X50.79 Y59.52
G1 X50.76 Y59.69
G1 X50.67 Y59.84
G1 X50.09 Y60.52
G1 X50.06 Y60.55
G1 X50.02 Y60.58
G1 X47.89 Y62.26
G1 X47.85 Y62.28
G1 X47.81 Y62.31
G1 X44.46 Y64.03
G1 X44.41 Y64.05
G1 X44.37 Y64.06
G1 X40.69 Y65.1
G1 X40.62 Y65.12
G1 X37.86 Y65.45
G1 X37.8 Y65.46
G1 X36.89 Y65.44
G1 X36.83
G1 X36.82
G1 X36.07 Y65.45
G1 X36.04 Y65.44
G1 X35.97
G1 X35.1 Y65.41
G1 X35.04 Y65.4
G1 X32.44 Y64.99
G1 X32.37 Y64.97
G1 X28.94 Y63.9
G1 X28.89 Y63.88
G1 X28.85 Y63.86
G1 X25.74 Y62.2
G1 X25.7 Y62.18
G1 X25.66 Y62.15
G1 X23.68 Y60.56
G1 X23.65 Y60.53
G1 X23.62 Y60.5
G1 X23.08 Y59.87
G1 X22.99 Y59.71
G1 X22.96 Y59.54
G1 X22.98 Y49.88
G1 X22.96 Y34.9
G1 X23.1 Y34.55
G1 X23.46 Y34.4
G1 X50.3
G0 Z3
G0 X55.2 Y43.67
G1 Z-1.5
G1 Y51.34
G1 X55.11 Y51.94
G1 X54.83 Y52.88
G1 X54.39 Y53.88
G1 X53.8 Y54.85
G1 X53.09 Y55.74
G1 X52.28 Y56.47
G1 X51.41 Y56.98
G1 X51.07 Y57.09
G1 Y43.67
G1 X55.2
G0 Z3
G0 X22.69 Y43.63
G1 Z-1.5
G1 Y57.09
G1 X22.35 Y56.98
G1 X21.47 Y56.47
G1 X20.67 Y55.74
G1 X19.95 Y54.85
G1 X19.36 Y53.88
G1 X18.92 Y52.88
G1 X18.64 Y51.94
G1 X18.55 Y51.34
G1 Y43.63
G1 X22.69
G0 Z3
G0 X28.55 Y35.84
G1 Z-0.99
G1 X30.11 Y36.15
G1 X31.43 Y37.03
G1 X32.32 Y38.35
G1 X32.63 Y39.91
G1 X32.32 Y41.47
G1 X31.43 Y42.79
G1 X30.11 Y43.68
G1 X28.55 Y43.99
G1 X26.99 Y43.68
G1 X25.67 Y42.79
G1 X24.79 Y41.47
G1 X24.48 Y39.91
G1 X24.79 Y38.35
G1 X25.67 Y37.03
G1 X26.99 Y36.15
G1 X28.55 Y35.84
G0 Z3
G0 X45.33 Y35.93
G1 Z-0.99
G1 X46.88 Y36.24
G1 X48.21 Y37.12
G1 X49.09 Y38.45
G1 X49.4 Y40
G1 X49.09 Y41.56
G1 X48.21 Y42.88
G1 X46.88 Y43.77
G1 X45.33 Y44.08
G1 X43.77 Y43.77
G1 X42.45 Y42.88
G1 X41.56 Y41.56
G1 X41.25 Y40
G1 X41.56 Y38.45
G1 X42.45 Y37.12
G1 X43.77 Y36.24
G1 X45.33 Y35.93
G0 Z3
G0 X45.2 Y39.12
G1 Z-0.99
G1 X45.7 Y39.19
G1 X46.07 Y39.52
G1 X46.22 Y40
G1 X46.07 Y40.49
G1 X45.7 Y40.81
G1 X45.2 Y40.89
G1 X44.74 Y40.68
G1 X44.47 Y40.26
G1 Y39.75
G1 X44.74 Y39.33
G1 X45.2 Y39.12
G0 Z3
G0 X28.43 Y39.03
G1 Z-0.99
G1 X28.92 Y39.1
G1 X29.3 Y39.43
G1 X29.44 Y39.91
G1 X29.3 Y40.4
G1 X28.92 Y40.72
G1 X28.43 Y40.8
G1 X27.97 Y40.59
G1 X27.7 Y40.16
G1 Y39.66
G1 X27.97 Y39.24
G1 X28.43 Y39.03
G0 Z3
G0 X55.76 Y0
G1 Z-1.5
G1 X59.27 Y0.35
G1 X62.65 Y1.37
G1 X65.76 Y3.03
G1 X68.49 Y5.27
G1 X70.73 Y8
G1 X72.39 Y11.11
G1 X73.42 Y14.49
G1 X73.76 Y18
G1 Y55.69
G1 X73.42 Y59.2
G1 X72.39 Y62.57
G1 X70.73 Y65.69
G1 X68.49 Y68.41
G1 X65.76 Y70.65
G1 X62.65 Y72.31
G1 X59.27 Y73.34
G1 X55.76 Y73.69
G1 X18
G1 X14.49 Y73.34
G1 X11.11 Y72.31
G1 X8 Y70.65
G1 X5.27 Y68.41
G1 X3.03 Y65.69
G1 X1.37 Y62.57
G1 X0.35 Y59.2
G1 X0 Y55.69
G1 Y18
G1 X0.35 Y14.49
G1 X1.37 Y11.11
G1 X3.03 Y8
G1 X5.27 Y5.27
G1 X8 Y3.03
G1 X11.11 Y1.37
G1 X14.49 Y0.35
G1 X18 Y0
G1 X55.76
G0 Z3
M5
G0 X40 Y75
M2

View File

@@ -502,14 +502,14 @@
},
"probe-fast-seek": {
"type": "float",
"unit": "mm/m",
"unit": "mm/min",
"min": 0,
"max": 1000,
"default": 200
},
"probe-slow-seek": {
"type": "float",
"unit": "mm/m",
"unit": "mm/min",
"min": 0,
"max": 1000,
"default": 25

View File

@@ -1,8 +0,0 @@
[Plymouth Theme]
Name=buildbotics
Description=Buildbotics boot splash
ModuleName=script
[script]
ImageDir=/usr/share/plymouth/themes/buildbotics
ScriptFile=/usr/share/plymouth/themes/buildbotics/buildbotics.script

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -11,18 +11,20 @@
"check": "svelte-check --tsconfig ./tsconfig.json"
},
"devDependencies": {
"@sveltejs/kit": "^1.0.0-next.357",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.49",
"@sveltejs/kit": "^1.0.0-next.392",
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"@tsconfig/svelte": "^3.0.0",
"node-sass": "^7.0.1",
"polyfill-object.fromentries": "^1.0.1",
"smui-theme": "^6.0.0-beta.16",
"svelte": "^3.48.0",
"string.prototype.matchall": "^4.0.7",
"svelte": "^3.49.0",
"svelte-check": "^2.8.0",
"svelte-material-ui": "^6.0.0-beta.16",
"svelte-preprocess": "^4.10.7",
"svelte-tiny-virtual-list": "^2.0.5",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"vite": "^2.9.13"
"vite": "^3.0.2"
}
}

View File

@@ -0,0 +1,131 @@
<script lang="ts">
import configTemplate from "../../../resources/config-template.json";
import { Config, DisplayUnits } from "$lib/ConfigStore";
import { ControllerMethods } from "$lib/RegisterControllerMethods";
import { onMount } from "svelte";
type Template = {
type?: string;
values?: (string | number)[];
unit?: "string";
iunit?: "string";
min?: number;
max?: number;
step?: number;
help?: string;
default?: string | number;
scale?: number;
};
export let key: string;
let keyParts: string[];
let template: Template;
let name: string;
let title: string;
let units: string;
let value;
onMount(() => {
keyParts = (key || "").split(".");
template = getTemplate();
name = keyParts[keyParts.length - 1];
title = getTitle();
value = getValue();
});
$: metric = $DisplayUnits === "METRIC";
$: if (template) {
units = metric || !template.iunit ? template.unit : template.iunit;
}
function getTemplate(): Template {
let template = configTemplate;
for (const part of keyParts) {
template = template[part];
}
return template as Template;
}
function getTitle(): string {
const help = template.help ? `${template.help}\n` : "";
return `${help}Default: ${template.default} ${template.unit || ""}`;
}
function getValue(): string | number {
let value: any = $Config;
for (const part of keyParts) {
value = value[part];
}
if (template.scale) {
if (metric) {
return Number.parseFloat(value.toFixed(3));
}
return Number.parseFloat((value / template.scale).toFixed(4));
}
return value;
}
function onChange() {
Config.update((config) => {
let target = config;
for (const part of keyParts.slice(0, -1)) {
target = target[part];
}
target[keyParts[keyParts.length - 1]] = value;
return config;
});
ControllerMethods.dispatch("config-changed");
}
</script>
{#if template}
<div class="pure-control-group" {title}>
<label for={name}>{name}</label>
{#if template.values}
<select {name} bind:value on:change={onChange}>
{#each template.values as opt}
<option value={opt} disabled={opt === "-----"}>
{opt}
</option>
{/each}
</select>
{:else if template.type === "bool"}
<input {name} type="checkbox" bind:value on:input={onChange} />
{:else if template.type === "float"}
<input
{name}
type="number"
min={template.min}
max={template.max}
step={template.step || "any"}
bind:value
on:input={onChange}
/>
{:else if template.type === "int"}
<input
{name}
type="number"
min={template.min}
max={template.max}
bind:value
on:input={onChange}
/>
{:else if template.type === "string"}
<input {name} type="text" bind:value on:input={onChange} />
{:else if template.type == "text"}
<textarea {name} bind:value on:input={onChange} />
{/if}
<label for="" class="units">{units || ""}</label>
<slot name="extra" />
</div>
{/if}

View File

@@ -1,33 +0,0 @@
<script lang="ts">
import * as api from "$lib/api";
import { onMount } from "svelte";
onMount(() => {
document.body.style.backgroundColor = "black";
const layout = document.querySelector("#layout") as HTMLElement;
layout.style.backgroundColor = "white";
layout.style.width = "1280px";
layout.style.height = "720px";
layout.style.overflowY = "scroll";
});
</script>
<div class="devmode">
<button on:click={() => api.PUT("devmode/probe")}> Probe Contact </button>
</div>
<style lang="scss">
.devmode {
background-color: greenyellow;
z-index: 20000000;
position: absolute;
top: 10px;
left: 400px;
width: 500px;
height: 100px;
padding: 6px;
display: flex;
align-items: flex-start;
justify-items: space-between;
}
</style>

View File

@@ -0,0 +1,97 @@
<script lang="ts">
import configTemplate from "../../../resources/config-template.json";
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
import ConfigTemplatedInput from "./ConfigTemplatedInput.svelte";
import SetTimeDialog from "$dialogs/SetTimeDialog.svelte";
import Button, { Label } from "@smui/button";
const gcodeURL = "https://linuxcnc.org/docs/html/gcode/g-code.html";
let showScreenRotationDialog = false;
let showSetTimeDialog = false;
</script>
<ScreenRotationDialog bind:open={showScreenRotationDialog} />
<SetTimeDialog bind:open={showSetTimeDialog} />
<h1>Settings</h1>
<div class="pure-form pure-form-aligned">
<h2>User Interface</h2>
<fieldset>
<div class="pure-control-group">
<label for="screen-rotation" />
<Button
name="screen-rotation"
touch
variant="raised"
on:click={() => (showScreenRotationDialog = true)}
>
<Label>Change Screen Rotation</Label>
</Button>
</div>
<div class="pure-control-group">
<label for="set-time" />
<Button
name="set-time"
touch
variant="raised"
on:click={() => (showSetTimeDialog = true)}
>
<Label>Change Time & Timezone</Label>
</Button>
</div>
</fieldset>
<fieldset>
<h2>Probe Dimensions</h2>
{#each Object.keys(configTemplate.probe) as key}
{#if key !== "probe-diameter"}
<ConfigTemplatedInput key={`probe.${key}`} />
{/if}
{/each}
</fieldset>
<fieldset>
<h2>GCode</h2>
{#each Object.keys(configTemplate.gcode) as key}
<ConfigTemplatedInput key={`gcode.${key}`} />
{/each}
</fieldset>
<h2>Path Accuracy</h2>
<fieldset>
<ConfigTemplatedInput key={`settings.max-deviation`} />
</fieldset>
<p>
Lower <tt>max-deviation</tt> to follow the programmed path more precisely but
at a slower speed.
</p>
<p>
In order to improve traversal speed, the path planner may merge consecutive
moves or round off sharp corners if doing so would deviate from the program
path by less than <tt>max-deviation</tt>.
</p>
<p>
GCode commands
<a href={`${gcodeURL}#gcode:g61`} target="_blank">G61, G61.1</a>
and <a href={`${gcodeURL}#gcode:g64`} target="_blank"> G64</a> also affect path
planning accuracy.
</p>
<h2>Cornering Speed (Advanced)</h2>
<fieldset>
<ConfigTemplatedInput key={`settings.junction-accel`} />
</fieldset>
<p>
Junction acceleration limits the cornering speed the planner will allow.
Increasing this value will allow for faster traversal of corners but may
cause the planner to violate axis jerk limits and stall the motors. Use with
caution.
</p>
</div>

View File

@@ -3,6 +3,10 @@
import HomeMachineDialog from "$dialogs/HomeMachineDialog.svelte";
import ProbeDialog from "$dialogs/ProbeDialog.svelte";
import ScreenRotationDialog from "$dialogs/ScreenRotationDialog.svelte";
import UploadDialog from "$dialogs/UploadDialog.svelte";
import SetTimeDialog from "./SetTimeDialog.svelte";
import ManualHomeAxisDialog from "./ManualHomeAxisDialog.svelte";
import SetAxisPositionDialog from "./SetAxisPositionDialog.svelte";
const HomeMachineDialogProps = writable<HomeMachineDialogPropsType>();
type HomeMachineDialogPropsType = {
@@ -16,11 +20,35 @@
probeType: "xyz" | "z";
};
const ScreenRotationDialogProps = writable<ProbeDialogPropsType>();
const ScreenRotationDialogProps = writable<ScreenRotationDialogPropsType>();
type ScreenRotationDialogPropsType = {
open: boolean;
};
const UploadDialogProps = writable<UploadDialogPropsType>();
type UploadDialogPropsType = {
open: boolean;
file: File;
onComplete: () => void;
};
const SetTimeDialogProps = writable<SetTimeDialogPropsType>();
type SetTimeDialogPropsType = {
open: boolean;
};
const ManualHomeAxisDialogProps = writable<ManualHomeAxisDialogPropsType>();
type ManualHomeAxisDialogPropsType = {
open: boolean;
axis: string;
};
const SetAxisPositionDialogProps = writable<SetAxisPositionDialogPropsType>();
type SetAxisPositionDialogPropsType = {
open: boolean;
axis: string;
};
export function showDialog(
dialog: "HomeMachine",
props: Omit<HomeMachineDialogPropsType, "open">
@@ -31,6 +59,31 @@
props: Omit<ProbeDialogPropsType, "open">
);
export function showDialog(
dialog: "ScreenRotation",
props: Omit<ScreenRotationDialogPropsType, "open">
);
export function showDialog(
dialog: "Upload",
props: Omit<UploadDialogPropsType, "open">
);
export function showDialog(
dialog: "SetTime",
props: Omit<SetTimeDialogPropsType, "open">
);
export function showDialog(
dialog: "ManualHomeAxis",
props: Omit<ManualHomeAxisDialogPropsType, "open">
);
export function showDialog(
dialog: "SetAxisPosition",
props: Omit<SetAxisPositionDialogPropsType, "open">
);
export function showDialog(dialog: string, props: any) {
switch (dialog) {
case "HomeMachine":
@@ -45,6 +98,22 @@
ScreenRotationDialogProps.set({ ...props, open: true });
break;
case "Upload":
UploadDialogProps.set({ ...props, open: true });
break;
case "SetTime":
SetTimeDialogProps.set({ ...props, open: true });
break;
case "ManualHomeAxis":
ManualHomeAxisDialogProps.set({ ...props, open: true });
break;
case "SetAxisPosition":
SetAxisPositionDialogProps.set({ ...props, open: true });
break;
default:
throw new Error(`Unknown dialog '${dialog}`);
}
@@ -54,3 +123,7 @@
<HomeMachineDialog {...$HomeMachineDialogProps} />
<ProbeDialog {...$ProbeDialogProps} />
<ScreenRotationDialog {...$ScreenRotationDialogProps} />
<UploadDialog {...$UploadDialogProps} />
<SetTimeDialog {...$SetTimeDialogProps} />
<ManualHomeAxisDialog {...$ManualHomeAxisDialogProps} />
<SetAxisPositionDialog {...$SetAxisPositionDialogProps} />

View File

@@ -8,6 +8,7 @@
<Dialog
bind:open
scrimClickAction=""
aria-labelledby="home-machine-dialog-title"
aria-describedby="home-machine-dialog-content"
>

View File

@@ -0,0 +1,46 @@
<script lang="ts">
import Dialog, { Title, Content, Actions } from "@smui/dialog";
import TextField from "@smui/textfield";
import Button, { Label } from "@smui/button";
import { ControllerMethods } from "$lib/RegisterControllerMethods";
export let open: boolean;
export let axis = "";
let value = 0;
function onConfirm() {
ControllerMethods.set_home(axis, value);
}
</script>
<Dialog
bind:open
scrimClickAction=""
aria-labelledby="manual-home-axis-dialog-title"
aria-describedby="manual-home-axis-dialog-content"
>
<Title id="manual-home-axis-dialog-title"
>Manually Home {axis.toUpperCase()} Axis</Title
>
<Content id="manual-home-axis-dialog-content">
<p>Set axis absolute position</p>
<TextField
label="Absolute"
type="number"
bind:value
variant="filled"
style="width: 100%;"
/>
</Content>
<Actions>
<Button>
<Label>Cancel</Label>
</Button>
<Button defaultAction on:click={onConfirm}>
<Label>Set</Label>
</Button>
</Actions>
</Dialog>

View File

@@ -1,5 +1,18 @@
<script type="ts" context="module">
import { get, writable, type Writable } from "svelte/store";
<script type="ts">
import DimensionInput from "$components/DimensionInput.svelte";
import Dialog, { Title, Content, Actions } from "@smui/dialog";
import Button, { Label } from "@smui/button";
import { waitForChange } from "$lib/StoreHelpers";
import { ControllerMethods } from "$lib/RegisterControllerMethods";
import { Config } from "$lib/ConfigStore";
import { writable, type Writable } from "svelte/store";
import {
probingActive,
probeContacted,
probingComplete,
probingFailed,
probingStarted,
} from "$lib/ControllerState";
type Step =
| "None"
@@ -19,49 +32,8 @@
};
const cancelled = writable(false);
const probingActive = writable(false);
const probeContacted = writable(false);
const probingStarted = writable(false);
const probingFailed = writable(false);
const probingComplete = writable(false);
const userAcknowledged = writable(false);
export function handleControllerStateUpdate(state: Record<string, any>) {
if (!get(probingActive)) {
return;
}
switch (true) {
case state.pw === 0:
probeContacted.set(true);
break;
case state.log?.msg === "Switch not found":
probingFailed.set(true);
break;
case state.cycle !== "idle":
probingStarted.set(true);
break;
case state.cycle === "idle":
if (get(probingStarted)) {
probingStarted.set(false);
probingComplete.set(true);
}
break;
}
}
</script>
<script type="ts">
import DimensionInput from "$components/DimensionInput.svelte";
import Dialog, { Title, Content, Actions } from "@smui/dialog";
import Button, { Label } from "@smui/button";
import { waitForChange } from "$lib/StoreHelpers";
import { ControllerMethods } from "$lib/RegisterControllerMethods";
import { Config } from "$lib/ConfigStore";
const cutterDiameterOptions = [
{ value: 0.5, label: '1/2 "', metric: false },
{ value: 0.25, label: '1/4 "', metric: false },

View File

@@ -0,0 +1,64 @@
<script lang="ts">
import Dialog, { Title, Content, Actions } from "@smui/dialog";
import TextField from "@smui/textfield";
import Button, { Label } from "@smui/button";
import { ControllerMethods } from "$lib/RegisterControllerMethods";
export let open: boolean;
export let axis = "";
let value = 0;
let homed = false;
let wasOpen = false;
$: if (open != wasOpen) {
if (open) {
homed = ControllerMethods.isAxisHomed(axis);
}
wasOpen = open;
}
function onUnhome() {
ControllerMethods.unhome(axis);
}
function onConfirm() {
ControllerMethods.set_position(axis, value);
}
</script>
<Dialog
bind:open
scrimClickAction=""
aria-labelledby="set-axis-position-dialog-title"
aria-describedby="set-axis-position-dialog-content"
>
<Title id="set-axis-position-dialog-title"
>Set {axis.toUpperCase()} Axis Position</Title
>
<Content id="set-axis-position-dialog-content">
<TextField
label="Position"
type="number"
bind:value
spellcheck="false"
variant="filled"
style="width: 100%;"
/>
</Content>
<Actions>
<Button>
<Label>Cancel</Label>
</Button>
{#if homed}
<Button on:click={onUnhome}>
<Label>Unhome</Label>
</Button>
{/if}
<Button defaultAction on:click={onConfirm}>
<Label>Set</Label>
</Button>
</Actions>
</Dialog>

View File

@@ -0,0 +1,240 @@
<script lang="ts">
import Dialog, { Title, Content, Actions } from "@smui/dialog";
import Button, { Label } from "@smui/button";
import TextField from "@smui/textfield";
import CircularProgress from "@smui/circular-progress";
import VirtualList from "svelte-tiny-virtual-list";
import * as api from "$lib/api";
const itemHeight = 35;
type Timezone = {
label: string;
value: string;
};
export let open = false;
let value = "";
let wasOpen = false;
let loading = true;
let timezones: Timezone[] = [];
let currentTimezoneIndex: number;
let selectedTimezoneIndex: number;
let networkTimeSynchronized: boolean;
$: if (open != wasOpen) {
if (!wasOpen) {
loadData();
}
wasOpen = open;
}
async function loadData() {
loading = true;
const result = await api.GET("time");
parseTimezones(result.timezones);
parseTimeinfo(result.timeinfo);
value = getDateTimeValueString();
loading = false;
}
function parseTimeinfo(str: string) {
const matches = Array.from(str.matchAll(/\s*([^:]+):\s+(.+)/gm));
let currentTimezoneValue;
for (const match of matches) {
let [, label, value] = match;
switch (label) {
case "Time zone":
currentTimezoneValue = value.split(" ")[0];
break;
case "NTP synchronized":
networkTimeSynchronized = value === "yes";
break;
}
}
currentTimezoneIndex = timezones.findIndex(
(tz) => tz.value === currentTimezoneValue
);
selectedTimezoneIndex = currentTimezoneIndex;
}
function parseTimezones(str: string) {
const matches = Array.from(str.matchAll(/\s*(\S+)\s*/gm));
timezones = [];
for (let [, value] of matches) {
timezones.push({
label: value.replace(/_/g, " "),
value,
});
}
// Sort alphabetically, but with the current timezone at the top of the list
timezones.sort((a, b) => {
switch (true) {
case a.value === "UTC":
return -1;
case b.value === "UTC":
return 1;
default:
return a.value.localeCompare(b.value);
}
});
}
function getDateTimeValueString() {
const date = new Date();
const year = date.getFullYear().toString().padStart(2, "0");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
const hour = date.getHours().toString().padStart(2, "0");
const minute = date.getMinutes().toString().padStart(2, "0");
return `${year}-${month}-${day}T${hour}:${minute}:00`;
}
async function onConfirm() {
const date = new Date(value);
const year = date.getFullYear().toString().padStart(2, "0");
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const day = date.getDate().toString().padStart(2, "0");
const hour = date.getHours().toString().padStart(2, "0");
const minute = date.getMinutes().toString().padStart(2, "0");
await api.PUT("time", {
datetime: `${year}-${month}-${day} ${hour}:${minute}:00`,
timezone: timezones[selectedTimezoneIndex].value,
});
}
</script>
<Dialog
bind:open
scrimClickAction=""
aria-labelledby="set-time-dialog-title"
aria-describedby="set-time-dialog-content"
>
<Title id="set-time-dialog-title">Adjust Clock & Timezone</Title>
<Content id="set-time-dialog-content">
{#if loading}
<div style="display: flex; justify-content: center">
<CircularProgress style="height: 32px; width: 32px;" indeterminate />
</div>
{:else}
{#if networkTimeSynchronized}
<p>
Because this controller is connected to the internet, the time is
managed automatically, and cannot be manually set.
</p>
{:else}
<p>
Because this controller is not connected to the internet, you can
manually set the time.
</p>
<p>
Note: any time the controller is turned off, the time will need to be
reset. If you connect the controller to the internet, the time will be
managed automatically.
</p>
<Label>Date & Time</Label>
<TextField
bind:value
label="Time"
type="datetime-local"
variant="filled"
style="width: 100%;"
/>
{/if}
<p>
To display your local time correctly, the controller must know what
timezone it is in.
</p>
<div class="timezones-container" style="margin-top: 20px;">
<Label>Select your timezone</Label>
<VirtualList
width="100%"
height={itemHeight * 6}
itemCount={timezones.length}
itemSize={itemHeight}
scrollToIndex={currentTimezoneIndex}
scrollToAlignment="center"
>
<div
slot="item"
let:index
let:style
{style}
class="timezone"
class:selected={index === selectedTimezoneIndex}
on:click={() => (selectedTimezoneIndex = index)}
>
{timezones[index].label}
</div>
</VirtualList>
</div>
{/if}
</Content>
<Actions>
<Button>
<Label>Cancel</Label>
</Button>
<Button
defaultAction
disabled={selectedTimezoneIndex === -1}
on:click={onConfirm}
>
<Label>Confirm</Label>
</Button>
</Actions>
</Dialog>
<style lang="scss">
@use "sass:color";
$primary: #0078e7;
$very-dark: #555;
$text: #777;
$grey: #bbb;
$light: #ddd;
.timezones-container {
:global {
.virtual-list-wrapper {
border: 1px solid #ccc;
border-radius: 3px;
overflow-x: hidden;
overflow-y: scroll;
}
}
.timezone {
font-size: 14px;
display: flex;
align-items: center;
margin: 0;
padding-left: 10px;
&.selected {
color: $primary;
background-color: color.adjust($primary, $lightness: 50%);
font-weight: bold;
}
}
}
</style>

View File

@@ -0,0 +1,78 @@
<script lang="ts">
import Dialog, { Title, Content, Actions } from "@smui/dialog";
import Button, { Label } from "@smui/button";
import LinearProgress from "@smui/linear-progress";
export let open = false;
export let file: File;
export let onComplete: () => void;
let wasOpen = false;
let xhr;
let progress;
$: if (open != wasOpen) {
if (!wasOpen) {
beginUpload();
}
wasOpen = open;
}
$: if (!open) {
xhr = undefined;
}
async function beginUpload() {
progress = 0;
xhr = new XMLHttpRequest();
xhr.upload.onload = () => {
open = false;
if (onComplete) {
onComplete();
}
};
xhr.upload.onerror = () => {
open = false;
alert("Upload failed.");
};
xhr.upload.onabort = () => {
open = false;
};
xhr.upload.onprogress = (event) => {
progress = event.loaded / event.total;
};
xhr.open("PUT", `/api/file/${encodeURIComponent(file.name)}`);
xhr.send(file);
}
function onCancel() {
xhr.abort();
}
</script>
<Dialog
bind:open
scrimClickAction=""
aria-labelledby="upload-dialog-title"
aria-describedby="upload-dialog-content"
>
<Title id="upload-dialog-title">
Uploading {#if file}{file.name}...{/if}
</Title>
<Content id="upload-dialog-content">
<LinearProgress {progress} />
</Content>
<Actions>
<Button on:click={onCancel}>
<Label>Cancel</Label>
</Button>
</Actions>
</Dialog>

View File

@@ -1,7 +1,14 @@
import { writable } from "svelte/store";
type DisplayUnits = "METRIC" | "IMPERIAL";
export const Config = writable<Record<string, any>>({});
export const DisplayUnits = writable<DisplayUnits>();
export function handleConfigUpdate(config: Record<string, any>) {
Config.set(config);
}
export function setDisplayUnits(value: DisplayUnits) {
DisplayUnits.set(value);
}

View File

@@ -0,0 +1,36 @@
import { get, writable } from "svelte/store";
import { processNetworkInfo } from "./NetworkInfo";
export const networkInfo = writable({});
export const probingActive = writable(false);
export const probeContacted = writable(false);
export const probingStarted = writable(false);
export const probingFailed = writable(false);
export const probingComplete = writable(false);
export function handleControllerStateUpdate(state: Record<string, any>) {
if (state.networkInfo) {
processNetworkInfo(state.networkInfo);
delete state.networkInfo;
}
if (get(probingActive)) {
if (state.pw === 0) {
probeContacted.set(true);
}
if (state.log?.msg === "Switch not found") {
probingFailed.set(true);
}
if (state.cycle !== "idle") {
probingStarted.set(true);
}
if (state.cycle === "idle" && get(probingStarted)) {
probingStarted.set(false);
probingComplete.set(true);
}
}
}

View File

@@ -1,5 +1,4 @@
import { readable } from "svelte/store";
import * as api from "$lib/api";
import { writable } from "svelte/store";
export type WifiNetwork = {
Quality: string;
@@ -34,21 +33,16 @@ const empty: NetworkInfo = {
}
}
export const networkInfo = readable<NetworkInfo>(empty, (set) => {
getNetworkInfo();
const networkInfoIntervalId = setInterval(getNetworkInfo, 5000);
export const networkInfo = writable<NetworkInfo>(empty);
async function getNetworkInfo() {
export function processNetworkInfo(rawNetworkInfo: NetworkInfo) {
const now = Date.now();
const networksByName: Record<string, WifiNetwork> = {}
try {
const networkInfo: NetworkInfo = await api.GET("network");
const now = Date.now();
for (let network of networkInfo.wifi.networks) {
for (let network of rawNetworkInfo.wifi.networks) {
if (network.Name) {
network.lastSeen = now;
network.active = networkInfo.wifi.ssid === network.Name;
network.active = rawNetworkInfo.wifi.ssid === network.Name;
networksByName[network.Name] = network;
}
}
@@ -59,11 +53,11 @@ export const networkInfo = readable<NetworkInfo>(empty, (set) => {
}
}
set({
ipAddresses: networkInfo.ipAddresses,
hostname: networkInfo.hostname,
networkInfo.set({
ipAddresses: rawNetworkInfo.ipAddresses,
hostname: rawNetworkInfo.hostname,
wifi: {
ssid: networkInfo.wifi.ssid,
ssid: rawNetworkInfo.wifi.ssid,
networks: Object.values(networksByName).sort((a, b) => {
switch (true) {
case a.active:
@@ -78,16 +72,4 @@ export const networkInfo = readable<NetworkInfo>(empty, (set) => {
})
}
});
} catch (error) {
console.debug("Failed to fetch network info", error);
}
}
return () => {
clearInterval(networkInfoIntervalId);
}
})
export function init() {
return networkInfo.subscribe(() => ({}));
}

View File

@@ -2,10 +2,18 @@ type ControllerMethods = {
stop: () => void;
send: (gcode: string) => void;
goto_zero: (x: number, y: number, z: number, a: number) => void;
dispatch: (event: string, ...args: any[]) => void;
isAxisHomed: (axis: string) => boolean;
unhome: (axis: string) => void;
set_position: (axis: string, value: number) => void;
set_home: (axis: string, value: number) => void;
}
export let ControllerMethods: ControllerMethods;
export function registerControllerMethods(methods: ControllerMethods) {
ControllerMethods = methods;
export function registerControllerMethods(methods: Partial<ControllerMethods>) {
ControllerMethods = {
...ControllerMethods,
...methods
};
}

View File

@@ -1,11 +1,13 @@
import 'polyfill-object.fromentries';
import matchAll from "string.prototype.matchall";
matchAll.shim();
import AdminNetworkView from '$components/AdminNetworkView.svelte';
import SettingsView from '$components/SettingsView.svelte';
import DialogHost, { showDialog } from "$dialogs/DialogHost.svelte";
import Devmode from "$components/Devmode.svelte";
import { handleConfigUpdate } from '$lib/ConfigStore';
import { init as initNetworkInfo } from '$lib/NetworkInfo';
import { handleControllerStateUpdate } from "$dialogs/ProbeDialog.svelte";
import { handleConfigUpdate, setDisplayUnits } from '$lib/ConfigStore';
import { handleControllerStateUpdate } from "$lib/ControllerState";
import { registerControllerMethods } from "$lib/RegisterControllerMethods";
export function createComponent(component: string, target: HTMLElement, props: Record<string, any>) {
@@ -13,21 +15,21 @@ export function createComponent(component: string, target: HTMLElement, props: R
case "AdminNetworkView":
return new AdminNetworkView({ target, props });
case "SettingsView":
return new SettingsView({ target, props });
case "DialogHost":
return new DialogHost({ target, props });
case "Devmode":
return new Devmode({target, props});
default:
throw new Error("Unknown component");
}
}
export {
initNetworkInfo,
showDialog,
handleControllerStateUpdate,
handleConfigUpdate,
registerControllerMethods,
setDisplayUnits
};

View File

@@ -15,6 +15,7 @@ export default defineConfig({
}
},
build: {
minify: false,
target: "chrome60",
lib: {
entry: resolve(__dirname, 'src/main.ts'),