Merge pull request #71 from dacarley/5
6 - Improved uploading, date/time/timezone setting, etc.
This commit is contained in:
466
CHANGELOG.md
466
CHANGELOG.md
@@ -1,466 +0,0 @@
|
||||
OneFinity CNC Controller Firmware Changelog
|
||||
===========================================
|
||||
|
||||
## v1.0.8
|
||||
- Fixed chatter and lost steps issues (most commonly seen by Fusion users), re-enabled support for G61, G61.1, G64.
|
||||
- Fixed 3d preview on Safari-based web browsers (MacOS & iOS)
|
||||
- Made it less likely for a user to upload a non-gcode file
|
||||
- Fixed problems with UI freezing when attempting to process a non-gcode file
|
||||
- Simplified error popup to make it less confusing
|
||||
- Improved error messages for most internal errors
|
||||
|
||||
## v1.0.7
|
||||
- Cleaned up UI layout a bit on the main screen
|
||||
- Enabled auto expansion of partition and file system to fill SD card on first boot
|
||||
- Added probe continuity test before probing (pop-up dialog)
|
||||
- Added a popup after probing, reminding the user to put away the probe
|
||||
- Lowered default probe seek speed
|
||||
- Fixed file drop-down menu showing old files
|
||||
- Added file system watcher for uploaded files
|
||||
- Improved support for large gcode files, improves performance on Raspi
|
||||
- Fixed a bug where the UI could become unresponsive at boot, or when uploading the first gcode file
|
||||
- Changed default max-deviation to 0.001 to reduce issues with chattering while cutting arcs and circles
|
||||
- Hides ratpoison (window manager) message during boot up
|
||||
- Disabled G61, G61.1, G64 gcodes until the root cause of circle chatter is identified and fixed
|
||||
- Increased icon size on the main GUI
|
||||
- Improved version comparison logic for handling public beta releases in the future
|
||||
- Changed default max-jerk to 1000 for X/Y axes
|
||||
- Changed default idle current to 1A
|
||||
- Force-set some critical configuration values to help with reliability
|
||||
- Metric units only for junction-accel and max-deviation
|
||||
- Rewrote the homing procedure to be more consistent
|
||||
- Small improvements to the stall homing procedure
|
||||
- Fixed error messages in the console (webgl)
|
||||
- Fixed styling bug with error dialog
|
||||
- Added a setting to allow enable/disable of probing safety prompts
|
||||
|
||||
## v1.0.6
|
||||
- Tweaked stall homing procedure to clear stepper stall condition before homing
|
||||
- Modified motor homing parameters (8 microsteps, 1.688 m/min search velocity, 2 stall volts, 1 stall current, 1.5mm zero backoff)
|
||||
- Added pop up message while loading/simulating file on upload
|
||||
- Decreased max velocity in default settings for X and Y axes
|
||||
- Restored jerk to 1000 on all axes
|
||||
- Fixed issue with default units not displaying on Control page (thanks to Robin Goldstone)
|
||||
|
||||
## v1.0.5
|
||||
- Changed the jog commands so that save/restore modal states are not used to avoid situation where spindle/loads could
|
||||
turn back on if the stop button was used instead of the M5/M9 gcode commands.
|
||||
- Added tool path status to control page
|
||||
- Moved over/under/no-fit warnings to tool status from machine status
|
||||
- Added "home machine" pop up on start if the machine is not homed
|
||||
- Added confirmation for X0Y0 and Z0 buttons
|
||||
|
||||
## v1.0.4
|
||||
- Fixed text sticking out of some dialog boxes
|
||||
- Added changes to PWR microcontroller to support new precharge circuitry on V3 of the PCB
|
||||
- Added a Shutdown button under the menu
|
||||
- Added confirmation dialog to shutdown
|
||||
- Fixed Reset Defaults for OneFinity settings
|
||||
- Probe buttons now turn green when probe input is active
|
||||
- Added defaults for both machinist and woodworker machines
|
||||
- Re-activated "Upgrade" button under Admin to grab updates directly from the web
|
||||
|
||||
## v1.0.3
|
||||
- Changed upgrade scripts to point at OneFinity github repository
|
||||
- Upgrade function not yet fully implemented
|
||||
|
||||
## v1.0.2
|
||||
- Initial release of customized OneFinity firmware
|
||||
- Includes GUI customizations, stall homing
|
||||
|
||||
Note: This firmware was forked from version 0.4.14 of the Buildbotics firmware
|
||||
|
||||
Buildbotics CNC Controller Firmware Changelog
|
||||
=============================================
|
||||
|
||||
## v0.4.14
|
||||
- Handle file uploads with '#' or '?' in the name.
|
||||
- Added "step mode" to Web based jogging.
|
||||
- Fixed touch screen Web jogging.
|
||||
|
||||
## v0.4.13
|
||||
- Support for OMRON MX2 VFD.
|
||||
- Better error handling in WiFi configuration.
|
||||
- Fix open WiFi access.
|
||||
- Improved video camera performance.
|
||||
- Allow up to 4 camera clients at once.
|
||||
- Add axis bounds GCode variables ``#<_x_min>``, ``#<_x_max>``, etc.
|
||||
- Expose ``junction-accel`` planning parameter.
|
||||
- Fixed problem with manual firmware upload on OSX.
|
||||
- Ignore cameras that do not support MJPEG format video.
|
||||
|
||||
## v0.4.12
|
||||
- Segments straddle arc in linearization.
|
||||
- Control max-arc-error with GCode var.
|
||||
- Implemented path modes G61, G61.1 & G64 with naive CAM and basic blending.
|
||||
- Log GCode messages to "Messages" tab.
|
||||
- Acknowledging a message on one browser clears it for all.
|
||||
- Automatically reload Web view when file changes.
|
||||
- Added ``config-screen`` script. Web based screen config to come later.
|
||||
- Suppress message popup with (MSG,# No popup message).
|
||||
- Show latest GCode message in ``Message`` field on CONTROL page.
|
||||
- Marked several GCodes supported in cheat sheet.
|
||||
- Solved planner lookahead failure for most reasonable cases.
|
||||
- Prevent cutting off distant parts of 3D path view.
|
||||
- Raised default ``latch-backoff`` to 100mm and ``zero-backoff`` to 5mm.
|
||||
- Added ``max-deviation`` option.
|
||||
- Fixed problem with GCode boolean expression parsing. #232.
|
||||
- Ensure 2uS step pulse width.
|
||||
|
||||
## v0.4.11
|
||||
- Don't reset global offsets on M2.
|
||||
- Test shunt and show error on failure.
|
||||
- Report spindle status codes from Modbus.
|
||||
- Save more log files in bug report.
|
||||
- Fixed indicators low-side units.
|
||||
- Support for YL600, YL620 & YL620-A VFDs.
|
||||
- Move Modbus indicators to tool page.
|
||||
- Support for Sunfar E300 VFD.
|
||||
- Set GCODE_SCRIPT_PATH to support GCode file routines.
|
||||
- Fix pause bug introduced in v0.4.10.
|
||||
|
||||
## v0.4.10
|
||||
- Fix demo password check
|
||||
- Fix bug were fast clicks could cause jog commands to arrive out of order.
|
||||
- Fix bug where planner position may not sync after jog.
|
||||
- Show power shutdown on indicators page.
|
||||
- Show all motors in shutdown when in power shutdown.
|
||||
- Improved GCode error messages.
|
||||
- Put controller into estop when in power shutdown.
|
||||
|
||||
## v0.4.9
|
||||
- Enforce 6A per motor channel peak current limit.
|
||||
- Adjust config values above max or below min instead of resetting to default.
|
||||
|
||||
## v0.4.8
|
||||
- Fixed log rotating.
|
||||
- Use systemd service instead of init.d.
|
||||
- Fix planner terminate.
|
||||
- Changed AVR serial interrupt priorites.
|
||||
- Increased AVR serial and command buffers.
|
||||
- Boost HDMI signal.
|
||||
- Rewrote RPi serial driver.
|
||||
- Automatically scale max CPU speed to reduce RPi temp.
|
||||
- Disable USB camera if RPi temperature above 80°C, back on at 75°C.
|
||||
- Respect offsets in canned cycle moves. #219
|
||||
- Fixed G53 warning.
|
||||
- Fixed delayed offset update after M2 or M30 end of program.
|
||||
- Handle multiple consecutive config resets correctly.
|
||||
- Fixed log CPU usage problem introduced in v0.4.6.
|
||||
- Show RPi temp on indicators page.
|
||||
- Show red thermometer if RPi temp exceeds 80°C.
|
||||
|
||||
## v0.4.7
|
||||
- Fix homing switch to motor channel mapping with non-standard axis order.
|
||||
- Added ``switch-debounce`` and ``switch-lockout`` config options.
|
||||
- Handle corrupt GCode simulation data correctly.
|
||||
- Fixes for exception logging.
|
||||
- Always limit motor max-velocity. #209
|
||||
- Sync GCode and planner files to disk after write.
|
||||
- Added warning about reliability in a noisy environment on WiFi config page.
|
||||
- EStop on motor fault.
|
||||
- Fixed ETA line wrapping on Web interface.
|
||||
- Fixed zeroing with non-zero offset when unhomed. #211
|
||||
- Handle file paths uploaded from Windows correctly. #212
|
||||
- Don't retain estop state through reboot.
|
||||
- Log when RPi gets hot.
|
||||
- Support Modbus multi-write mode.
|
||||
- Added support for Nowforever VFDs.
|
||||
|
||||
## v0.4.6
|
||||
- Fixed a rare ``Negative s-curve time`` error.
|
||||
- Don't allow manual axis homing when soft limits are not set.
|
||||
- Right click to enable camera crosshair.
|
||||
- Demo mode.
|
||||
- Limit idle-current to 2A.
|
||||
- Removed dangerous ``power-mode`` in favor of simpler ``enabled`` option.
|
||||
- Fixed bug where motor driver could fail to disabled during estop.
|
||||
- Restored estop text.
|
||||
|
||||
## v0.4.5
|
||||
- Fix for random errors while running VFD.
|
||||
- Fix bug where planner would not continue after optional pause (M1).
|
||||
- Fix lockup on invalid no move probe G38.x. #183
|
||||
- Fix zeroing homed axis after jog.
|
||||
- Fix VFD communication at higher baud rates (> 9600). #184
|
||||
|
||||
## v0.4.4
|
||||
- Write version to log file.
|
||||
- Write time to log file periodically.
|
||||
- Show simulation progress with or with out 3D view.
|
||||
- Synchronize file list between browsers.
|
||||
- Increased max simulation time to 24hrs.
|
||||
- Added button to download current GCode file.
|
||||
- Blink play button to indicate pause.
|
||||
- Many layout tweaks/improvements.
|
||||
- Don't abort simulations when system time changes.
|
||||
- Only allow one camera stream at a time.
|
||||
|
||||
## v0.4.2
|
||||
- Suppress ``Auto-creating missing tool`` warning.
|
||||
- Prevent ``Stream is closed`` error.
|
||||
- Suppress ``WebGL not supported`` warning.
|
||||
- Fixed Web disconnect during simulation of large GCode.
|
||||
- Disable outputs on estop.
|
||||
- Improved switch debouncing for better homing.
|
||||
- Handle zero length dwell correctly.
|
||||
- Fixed problem with cached GCode file upload when file changed on disk.
|
||||
- Run simulation at low process priority.
|
||||
- Added ``Bug Report`` button to ``Admin`` -> ``General``.
|
||||
- Only render 3D view as needed to save CPU.
|
||||
- Prevent lockup due to browser causing out of memory condition.
|
||||
- Show error message when too large GCode upload fails.
|
||||
- Much faster 3D view loading.
|
||||
|
||||
## v0.4.1
|
||||
- Fix toolpath view axes bug.
|
||||
- Added LASER intensity view.
|
||||
- Fixed reverse path planner bug.
|
||||
- Video size and path view controls persistent over browser reload.
|
||||
- Fixed time and progress bugs.
|
||||
- Added PWM rapid auto off feature for LASER/Plasma.
|
||||
- Added dynamic PWM for LASER/Plasma.
|
||||
- Added motor faults table to indicators page.
|
||||
- Emit error and indicate FAULT on axis for motor driver faults.
|
||||
- Display axis motor FAULT on LCD.
|
||||
- Fixed bug with rapid repeated unpause.
|
||||
|
||||
## v0.4.0
|
||||
- Increased display precision of position and motor config.
|
||||
- Added support for 256 microstepping.
|
||||
- Smoother operation at 250k step rate by doubling clock as needed.
|
||||
- Indicators tab improvements.
|
||||
- Much improved camera support.
|
||||
- Camera hotpluging.
|
||||
- Move camera video to header.
|
||||
- Click to switch video size.
|
||||
- Automount/unmount USB drives.
|
||||
- Automatically install ``buildbotics.gc`` when no other GCode exists.
|
||||
- Preplan GCode and check for errors.
|
||||
- Display 3D view of program tool paths in browser.
|
||||
- Display accurate time remaining, ETA and progress during run.
|
||||
- Automatically collapse moves in planner which are too short in time.
|
||||
- Show IO status indicators on configuration pages.
|
||||
- Check that axis dimensions fit path plan dimensions.
|
||||
- Show machine working envelope in path plan viewer.
|
||||
- Don't reload browser view on reconnect unless controller has reloaded.
|
||||
- Increased max switch backoff search distance.
|
||||
- Major improvements for LASER raster GCodes.
|
||||
- Fixed major bug in command queuing.
|
||||
- Ignore Program Number O-Codes.
|
||||
- Improved planning of collinear line segments.
|
||||
- Allow PWM output up to 320kHz and no slower than 8Hz.
|
||||
|
||||
## v0.3.28
|
||||
- Show step rate on motor configuration page.
|
||||
- Limit motor max-velocity such that step rate cannot exceed 250k.
|
||||
- Fixed deceleration bug at full 250k step rate.
|
||||
|
||||
## v0.3.27
|
||||
- Fixed homing in imperial mode.
|
||||
|
||||
## v0.3.26
|
||||
- Removed VFD test.
|
||||
- Show VFD status on configuration page.
|
||||
- Show VFD commands fail counts.
|
||||
- Marked some VFD types as beta.
|
||||
|
||||
## v0.3.25
|
||||
- Error on home if max-soft-limit <= min-soft-limit + 1. #139
|
||||
- Decrease boot time networking delay.
|
||||
- Default to US keyboard layout. #145
|
||||
- Added configuration option to show metric or imperial units in browser. #74
|
||||
- Implemented fine jogging control in Web interface. #147
|
||||
|
||||
## v0.3.24
|
||||
- Added unhome button on axis position popup.
|
||||
- Ignore soft limits of max <= min.
|
||||
- Fixed problem with restarting program in imperial units mode.
|
||||
- Handle GCode with infinite or very long loops correctly.
|
||||
- Fixed Huanyang spindle restart after stop.
|
||||
|
||||
## v0.3.23
|
||||
- Fix for modbus read operation.
|
||||
- Finalized AC-Tech VFD support.
|
||||
- Preliminary FR-D700 VFD support.
|
||||
- Ignore leading zeros in modbus messages.
|
||||
- Handle older PWR firmwares.
|
||||
|
||||
## v0.3.22
|
||||
- Fix position loss after program pause. #130
|
||||
- Correctly handle disabled axes.
|
||||
- Fixed config checkbox not displaying defaulted enabled correctly.
|
||||
- Added Custom Modbus VFD programming.
|
||||
|
||||
## v0.3.21
|
||||
- Implemented M70-M73 modal state save/restore.
|
||||
- Added support for modbus VFDs.
|
||||
- Start Huanyang spindle with out first pressing Start button on VFD.
|
||||
- Faster switching of large GCode files in Web.
|
||||
- Fixed reported gcode line off by one.
|
||||
- Disable MDI input while running.
|
||||
- Stabilized direction pin output during slow moves.
|
||||
|
||||
## v0.3.20
|
||||
- Eliminated drift caused by miscounting half microsteps.
|
||||
- Fixed disappearing GCode in Web.
|
||||
- More efficient GCode scrolling with very large files.
|
||||
- Fully functional soft-limited jogging.
|
||||
- Added client and access-point Wifi configuration.
|
||||
- Fixed broken hostname Web redirect after change.
|
||||
- Split admin page -> General & Network.
|
||||
- Improved calculation of junction velocity limits.
|
||||
|
||||
## v0.3.19
|
||||
- Fixed stopping problems. #127
|
||||
- Fixed ``Negative s-curve time`` error.
|
||||
- Improved jogging with soft limits.
|
||||
- Added site favicon.
|
||||
- Fixed problems with offsets and imperial units.
|
||||
- Fixed ``All zero s-curve times`` caused by extremely short, non-zero moves.
|
||||
- Fixed position drift.
|
||||
|
||||
## v0.3.18
|
||||
- Don't enable any tool by default.
|
||||
|
||||
## v0.3.17
|
||||
- Fixed pausing fail near end of run bug.
|
||||
- Show "Upgrading firmware" when upgrading.
|
||||
- Log excessive pwr communication failures as errors.
|
||||
- Ensure we can still get out of non-idle cycles when there are errors.
|
||||
- Less frequent pwr variable updates.
|
||||
- Stop cancels seek and subsequent estop.
|
||||
- Fixed bug in AVR/Planner command synchronization.
|
||||
- Consistently display HOMING state during homing operation.
|
||||
- Homing zeros axis global offset.
|
||||
- Added zero all button. #126
|
||||
- Separate "Auto" and "MDI" play/pause & stop buttons. #126
|
||||
- Moved home all button. #126
|
||||
- Display "Video camera not found." instead of broken image icon.
|
||||
- Show offset positions not absolute on LCD.
|
||||
- Don't change gcode lines while homing.
|
||||
- Don't change button states while homing.
|
||||
- Adding warning about power cyclying during an upgrade.
|
||||
- Reset planner on AVR errors.
|
||||
- Fixed pausing with short moves.
|
||||
- Corrected s-curve accel increasing jogging velocities.
|
||||
|
||||
## v0.3.16
|
||||
- Fixed switch debounce bug.
|
||||
|
||||
## v0.3.15
|
||||
- Suppress warning missing config.json warning after config reset.
|
||||
- Fixed EStop reboot loop.
|
||||
- Removed AVR unexpected reboot error.
|
||||
|
||||
## v0.3.14
|
||||
- Fixed: Config fails silently after web disconnect #112
|
||||
- Always reload the page after a disconnect.
|
||||
- Honor soft limits #111 (but not when jogging)
|
||||
- Limit switch going active while moving causes estop. #54
|
||||
- Added more links to help page.
|
||||
- Fixed axis display on LCD. #122
|
||||
- Added GCode cheat sheet.
|
||||
- Fixed LCD boot splash screen. #121
|
||||
- Implemented tool change procedures and pause message box. #81
|
||||
- Implemented program start and end procedures.
|
||||
|
||||
## v0.3.13
|
||||
- Disable spindle and loads on stop.
|
||||
- Fixed several state transition (stop, pause, estop, etc.) problems.
|
||||
|
||||
## v0.3.12
|
||||
- Updated DB25 M2 breakout diagram.
|
||||
- Enabled AVR watchdog.
|
||||
- Fixed problem with selecting newly uploaded file.
|
||||
- More thorough shutdown of stepper driver in estop.
|
||||
- Fixed spindle type specific options.
|
||||
- No more ``Unexpected AVR firmware reboot`` errors on estop clear.
|
||||
- Downgraded ``Machine alarmed - Command not processed`` errors to warnings.
|
||||
- Suppress unnecessary axis homing warnings.
|
||||
- More details for axis homing errors.
|
||||
- Support GCode messages e.g. (MSG, Hello World!)
|
||||
- Support programmed pauses. i.e. M0
|
||||
|
||||
## v0.3.11
|
||||
- Suppressed ``firmware rebooted`` warning.
|
||||
- Error on unexpected AVR reboot.
|
||||
- Fixed pin fault output.
|
||||
- No longer using interrupts for switch inputs. Debouncing on clock tick.
|
||||
|
||||
## v0.3.10
|
||||
- Fixed "Flood" display, changed to "Load 1" and "Load 2". #108
|
||||
- Highlight loads when on.
|
||||
- Fixed axis zeroing.
|
||||
- Fixed bug in home position set after successful home. #109
|
||||
- Fixed ugly Web error dumps.
|
||||
- Allow access to log file from Web.
|
||||
- Rotate log so it does not grow too big.
|
||||
- Keep same GCode file through browser reload. #20
|
||||
|
||||
## v0.3.9
|
||||
- Fixed bug in move exec that was causing bumping between moves.
|
||||
- Fixed planner bug which could create negative s-curve times.
|
||||
- Hide step and optional pause buttons until they are implemented.
|
||||
- Fixed pausing problems.
|
||||
- Limit number of console messages.
|
||||
- Scrollbar on console view.
|
||||
- Log debug messages to console in developer mode.
|
||||
- Fixed AVR log message source.
|
||||
- Fixed step correction.
|
||||
- JOGGING, HOMMING and MDI states.
|
||||
- Fixed position problem with rapid MDI entry.
|
||||
|
||||
## v0.3.8
|
||||
- Fixed pwr flags display
|
||||
- Added pwr fault flags to indicators
|
||||
|
||||
## v0.3.7
|
||||
- Allow blocking error dialog for a period of time
|
||||
- Show actual error message on planner errors
|
||||
- Reset planner on serious error
|
||||
- Fixed console clear
|
||||
- Added helpful info to Video tab
|
||||
- Changed "Console" tab to "Messages"
|
||||
- Removed spin up/down velocity options, they don't do anything
|
||||
- Allow RS485 to work when wires are swapped
|
||||
- Allow setting VFD ID
|
||||
- Only show relevant spindle config items
|
||||
- More robust video camera reset
|
||||
- Added help page
|
||||
- Allow upgrade with out Internet
|
||||
- Limit power fault reporting
|
||||
- Added load over temp, load limiting and motor overload power faults
|
||||
|
||||
## v0.3.6
|
||||
- Set max_usb_current=1 in /boot/config.txt from installer #103
|
||||
|
||||
## v0.3.5
|
||||
- Fixed dwell (G4)
|
||||
- Always show limit switch indicators regardless of motor enable
|
||||
- Fixed feed rate display
|
||||
- Added current GCode unit display
|
||||
- Fixed homed axis zeroing
|
||||
- Fixed probe pin input
|
||||
- Added reload button to video tab
|
||||
- Don't open error dialog on repeat messages
|
||||
- Handle large GCode files in browser
|
||||
- Added max lookahead limit to planner
|
||||
- Fixed GCode stopping/pausing where ramp down needs more than is in the queue
|
||||
- Added breakout box diagram to indicators
|
||||
- Initialize axes offsets to zero on startup
|
||||
- Fixed conflict between ``x`` state variable and ``x`` axis variable
|
||||
- Don't show ipv6 addresses on LCD. They don't fit.
|
||||
|
||||
## v0.3.4
|
||||
- Added alternate units for motor parameters
|
||||
- Automatic config file upgrading
|
||||
- Fixed planner/jog sync
|
||||
- Fixed planner limits config
|
||||
- Accel units mm/min² -> m/min²
|
||||
- Search and latch velocity mm/min -> m/min
|
||||
- Fixed password update (broken in last version)
|
||||
- Start Web server earlier in case of Python coding errors
|
||||
|
||||
|
||||
Changelog not maintained in previous versions. See git commit log.
|
||||
22
CODE_TAG
22
CODE_TAG
@@ -1,22 +0,0 @@
|
||||
This file is part of the Buildbotics firmware.
|
||||
|
||||
Copyright (c) 2015 - 2018, Buildbotics LLC
|
||||
All rights reserved.
|
||||
|
||||
This file ("the software") is free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License,
|
||||
version 2 as published by the Free Software Foundation. You should
|
||||
have received a copy of the GNU General Public License, version 2
|
||||
along with the software. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The software is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the software. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
For information regarding this software email:
|
||||
"Joseph Coffland" <joseph@buildbotics.com>
|
||||
@@ -1,6 +1,6 @@
|
||||
include package.json README.md requirements.txt
|
||||
graft scripts
|
||||
graft python-packages
|
||||
graft installer
|
||||
include scripts/install.sh
|
||||
graft src/py/bbctrl/http
|
||||
graft src/py/camotics
|
||||
include src/avr/bbctrl-avr-firmware.hex
|
||||
|
||||
47
Makefile
47
Makefile
@@ -22,12 +22,8 @@ RSYNC_OPTS := $(RSYNC_EXCLUDE) -rv --no-g --delete --force
|
||||
|
||||
VERSION := $(shell sed -n 's/^.*"version": "\([^"]*\)",.*$$/\1/p' package.json)
|
||||
PKG_NAME := bbctrl-$(VERSION)
|
||||
PUB_PATH := root@buildbotics.com:/var/www/buildbotics.com/bbctrl
|
||||
BETA_VERSION := $(VERSION)-rc$(shell ./scripts/next-rc)
|
||||
BETA_PKG_NAME := bbctrl-$(BETA_VERSION)
|
||||
|
||||
SUBPROJECTS := avr boot pwr jig
|
||||
WATCH := src/pug src/pug/templates src/stylus src/js src/resources src/svelte-components src/static Makefile
|
||||
|
||||
ifndef HOST
|
||||
HOST=onefinity
|
||||
@@ -37,16 +33,12 @@ ifndef PASSWORD
|
||||
PASSWORD=onefinity
|
||||
endif
|
||||
|
||||
|
||||
all: $(HTML) $(RESOURCES)
|
||||
@for SUB in $(SUBPROJECTS); do $(MAKE) -C src/$$SUB; done
|
||||
|
||||
pkg: all $(AVR_FIRMWARE) bbserial
|
||||
./setup.py sdist
|
||||
|
||||
beta-pkg: pkg
|
||||
cp dist/$(PKG_NAME).tar.bz2 dist/$(BETA_PKG_NAME).tar.bz2
|
||||
|
||||
bbserial:
|
||||
$(MAKE) -C src/bbserial
|
||||
|
||||
@@ -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
|
||||
|
||||
659
installer/Team Onefinity.ngc
Normal file
659
installer/Team Onefinity.ngc
Normal file
@@ -0,0 +1,659 @@
|
||||
%
|
||||
G21 G40 G49 M6 T1
|
||||
G17
|
||||
M7
|
||||
G0Z20.320
|
||||
G0X0.000Y0.000S16000M3
|
||||
G0X26.732Y12.863Z5.080
|
||||
G1Z-3.175F1270.0
|
||||
G1X21.606Y17.989F2540.0
|
||||
G1Y12.042
|
||||
G1X25.676Y12.773
|
||||
G1X26.732Y12.863
|
||||
G1X34.154Y9.033
|
||||
G1X19.066Y24.121
|
||||
G1Y9.006
|
||||
G1X26.125Y10.273
|
||||
G2X27.247Y10.273I0.561J-3.125
|
||||
G1X34.154Y9.033
|
||||
G0Z5.080
|
||||
G0X31.766Y33.993
|
||||
G1Z-3.175F1270.0
|
||||
G1X21.606Y44.153F2540.0
|
||||
G1Y43.784
|
||||
G1X31.766Y33.624
|
||||
G1Y33.993
|
||||
G1X34.306Y35.045
|
||||
G1X19.066Y50.285
|
||||
G1Y42.732
|
||||
G1X34.306Y27.492
|
||||
G1Y35.045
|
||||
G0Z5.080
|
||||
G0X31.766Y60.159
|
||||
G1Z-3.175F1270.0
|
||||
G1X21.606Y70.318F2540.0
|
||||
G1Y69.948
|
||||
G1X31.766Y59.789
|
||||
G1Y60.159
|
||||
G1X34.306Y61.211
|
||||
G1X19.066Y76.450
|
||||
G1Y68.896
|
||||
G1X34.306Y53.657
|
||||
G1Y61.211
|
||||
G0Z5.080
|
||||
G0X31.766Y90.231
|
||||
G1Z-3.175F1270.0
|
||||
G1X27.488F2540.0
|
||||
G1X31.766Y85.954
|
||||
G1Y90.231
|
||||
G1X34.306Y92.771
|
||||
G1X21.356
|
||||
G1X34.306Y79.822
|
||||
G1Y92.771
|
||||
G0Z5.080
|
||||
G0X61.691Y55.036
|
||||
G1Z-3.175F1270.0
|
||||
G1X63.248F2540.0
|
||||
G1Y88.425
|
||||
G2X66.423Y91.600I3.175J0.000
|
||||
G1X77.157
|
||||
G1Y91.866
|
||||
G1X47.750
|
||||
G1Y91.600
|
||||
G1X58.516
|
||||
G2X61.691Y88.425I0.000J-3.175
|
||||
G1Y55.036
|
||||
G0Z5.080
|
||||
G0X88.590
|
||||
G1Z-3.175F1270.0
|
||||
G1X111.381F2540.0
|
||||
G1Y55.301
|
||||
G1X93.321
|
||||
G2X90.146Y58.476I0.000J3.175
|
||||
G1Y70.643
|
||||
G2X93.321Y73.818I3.175J0.000
|
||||
G1X107.380
|
||||
G1Y74.084
|
||||
G1X93.321
|
||||
G2X90.146Y77.259I0.000J3.175
|
||||
G1Y88.425
|
||||
G2X93.321Y91.600I3.175J0.000
|
||||
G1X110.220
|
||||
G1Y91.866
|
||||
G1X88.590
|
||||
G1Y55.036
|
||||
G0Z5.080
|
||||
G0X122.153
|
||||
G1Z-3.175F1270.0
|
||||
G1X123.719F2540.0
|
||||
G1X127.166Y64.004
|
||||
G2X130.130Y66.040I2.964J-1.139
|
||||
G1X146.391
|
||||
G2X149.354Y64.004I0.000J-3.175
|
||||
G1X152.802Y55.036
|
||||
G1X154.303
|
||||
G1X139.687Y91.866
|
||||
G1X136.769
|
||||
G1X122.153Y55.036
|
||||
G0Z5.080
|
||||
G0X132.680Y66.306
|
||||
G1Z-3.175F1270.0
|
||||
G2X129.716Y70.620I0.000J3.175F2540.0
|
||||
G1X132.098Y76.845
|
||||
G1X133.063Y79.498
|
||||
G1X134.092Y82.435
|
||||
G1X135.189Y85.669
|
||||
G2X138.196Y87.824I3.007J-1.020
|
||||
G1X138.325
|
||||
G2X141.328Y85.679I0.000J-3.175
|
||||
G1X142.433Y82.457
|
||||
G1X143.467Y79.518
|
||||
G1X144.433Y76.851
|
||||
G1X145.333Y74.451
|
||||
G1X146.805Y70.620
|
||||
G2X143.841Y66.306I-2.964J-1.138
|
||||
G1X132.680
|
||||
G0Z5.080
|
||||
G0X167.414Y55.036
|
||||
G1Z-3.175F1270.0
|
||||
G1X168.971F2540.0
|
||||
G1Y73.759
|
||||
G1X168.947Y76.349
|
||||
G1X168.876Y79.029
|
||||
G1X168.757Y81.813
|
||||
G1X168.589Y84.724
|
||||
G2X171.759Y88.082I3.170J0.183
|
||||
G1X172.049
|
||||
G2X175.085Y85.836I0.000J-3.175
|
||||
G1X175.677Y83.901
|
||||
G1X176.359Y81.760
|
||||
G1X177.102Y79.531
|
||||
G1X177.877Y77.332
|
||||
G1X182.870Y63.705
|
||||
G1X182.369Y65.444
|
||||
G1X181.521Y68.194
|
||||
G1X180.648Y70.844
|
||||
G1X179.742Y73.414
|
||||
G1X173.050Y91.866
|
||||
G1X167.414
|
||||
G1Y55.036
|
||||
G0Z5.080
|
||||
G0X186.047
|
||||
G1Z-3.175F1270.0
|
||||
G1X186.709F2540.0
|
||||
G1X189.243Y61.950
|
||||
G2X186.442Y60.271I-2.800J1.496
|
||||
G1X186.249
|
||||
G2X183.614Y61.675I0.000J3.175
|
||||
G1X186.047Y55.036
|
||||
G0Z5.080
|
||||
G0X203.785
|
||||
G1Z-3.175F1270.0
|
||||
G1X205.342F2540.0
|
||||
G1Y91.866
|
||||
G1X199.644
|
||||
G1X192.952Y73.323
|
||||
G1X191.959Y70.488
|
||||
G1X191.055Y67.759
|
||||
G1X190.236Y65.128
|
||||
G1X189.586Y62.886
|
||||
G1X194.879Y77.332
|
||||
G1X195.654Y79.531
|
||||
G1X196.397Y81.760
|
||||
G1X197.079Y83.901
|
||||
G1X197.670Y85.836
|
||||
G2X200.707Y88.082I3.036J-0.928
|
||||
G1X200.997
|
||||
G2X204.167Y84.733I0.000J-3.175
|
||||
G1X203.999Y81.672
|
||||
G1X203.880Y78.828
|
||||
G1X203.809Y76.184
|
||||
G1X203.785Y73.726
|
||||
G1Y55.036
|
||||
G0Z5.080
|
||||
G0X186.055Y18.115
|
||||
G1Z-3.175F1270.0
|
||||
G1X186.009Y18.105F2540.0
|
||||
G1X186.055Y17.999
|
||||
G1Y18.115
|
||||
G1X185.846Y20.662
|
||||
G2X183.925Y20.635I-1.003J3.012
|
||||
G1X183.850Y20.658
|
||||
G2X182.792Y21.213I0.918J3.039
|
||||
G1X182.707Y21.280
|
||||
G1X182.260Y21.714
|
||||
G1X182.051Y21.961
|
||||
G1X181.761Y22.394
|
||||
G1X181.394Y23.076
|
||||
G1X181.055Y23.830
|
||||
G1X173.477Y41.419
|
||||
G1X172.282
|
||||
G1Y8.016
|
||||
G1X172.558
|
||||
G1Y24.599
|
||||
G1X172.580Y25.408
|
||||
G1X172.663Y26.179
|
||||
G1X172.774Y26.696
|
||||
G1X172.878Y27.007
|
||||
G1X173.108Y27.530
|
||||
G1X173.164Y27.632
|
||||
G2X173.748Y28.390I2.780J-1.535
|
||||
G1X173.813Y28.452
|
||||
G2X175.480Y29.290I2.196J-2.293
|
||||
G1X175.555Y29.303
|
||||
G2X177.475Y29.026I0.528J-3.131
|
||||
G1X177.562Y28.984
|
||||
G2X178.399Y28.391I-1.391J-2.854
|
||||
G1X178.498Y28.294
|
||||
G1X178.838Y27.899
|
||||
G1X179.037Y27.625
|
||||
G1X179.285Y27.206
|
||||
G1X179.505Y26.777
|
||||
G1X187.630Y8.016
|
||||
G1X188.570
|
||||
G1Y41.419
|
||||
G1X188.294
|
||||
G1Y25.010
|
||||
G1X188.271Y24.216
|
||||
G1X188.177Y23.433
|
||||
G1X188.086Y23.078
|
||||
G1X187.917Y22.590
|
||||
G1X187.870Y22.482
|
||||
G2X187.362Y21.681I-2.906J1.279
|
||||
G1X187.306Y21.616
|
||||
G2X185.911Y20.684I-2.399J2.080
|
||||
G1X185.846Y20.662
|
||||
G0Z5.080
|
||||
G0X174.797Y31.718
|
||||
G1Z-3.175F1270.0
|
||||
G1X174.912Y31.742F2540.0
|
||||
G1X174.797Y32.011
|
||||
G1Y31.718
|
||||
G0Z5.080
|
||||
G0X201.208Y8.016
|
||||
G1Z-3.175F1270.0
|
||||
G1X201.483F2540.0
|
||||
G1Y41.419
|
||||
G1X201.208
|
||||
G1Y8.016
|
||||
G0Z5.080
|
||||
G0X221.852
|
||||
G1Z-3.175F1270.0
|
||||
G1X222.128F2540.0
|
||||
G1Y35.585
|
||||
G1X222.152Y36.556
|
||||
G1X222.239Y37.441
|
||||
G1X222.338Y37.967
|
||||
G1X222.494Y38.513
|
||||
G1X222.648Y38.864
|
||||
G1X222.990Y39.459
|
||||
G1X223.068Y39.565
|
||||
G2X223.987Y40.409I2.564J-1.872
|
||||
G1X224.094Y40.473
|
||||
G1X224.660Y40.744
|
||||
G1X224.991Y40.863
|
||||
G1X225.495Y40.981
|
||||
G1X225.981Y41.055
|
||||
G1X226.840Y41.124
|
||||
G1X227.808Y41.143
|
||||
G1X229.857
|
||||
G1Y41.419
|
||||
G1X214.122
|
||||
G1Y41.143
|
||||
G1X217.128Y41.132
|
||||
G1X217.865Y41.093
|
||||
G1X218.540Y41.009
|
||||
G1X219.037Y40.895
|
||||
G1X219.329Y40.787
|
||||
G1X219.958Y40.472
|
||||
G1X220.109Y40.375
|
||||
G1X220.711Y39.869
|
||||
G1X220.827Y39.744
|
||||
G1X221.305Y39.070
|
||||
G1X221.385Y38.918
|
||||
G1X221.658Y38.205
|
||||
G1X221.703Y38.027
|
||||
G1X221.790Y37.485
|
||||
G1X221.837Y36.840
|
||||
G1X221.852Y35.846
|
||||
G1Y8.016
|
||||
G0Z5.080
|
||||
G0X250.225
|
||||
G1Z-3.175F1270.0
|
||||
G1X250.501F2540.0
|
||||
G1Y16.797
|
||||
G1X250.516Y17.761
|
||||
G1X250.567Y18.756
|
||||
G1X250.681Y19.487
|
||||
G1X250.826Y20.159
|
||||
G1X251.009Y20.832
|
||||
G1X257.512Y41.419
|
||||
G1X257.222
|
||||
G1X253.849Y30.875
|
||||
G1X253.398Y29.763
|
||||
G1X253.228Y29.421
|
||||
G1X252.892Y28.886
|
||||
G1X252.817Y28.790
|
||||
G2X250.547Y27.571I-2.508J1.947
|
||||
G1X250.489Y27.567
|
||||
G2X248.506Y28.080I-0.238J3.166
|
||||
G1X248.444Y28.121
|
||||
G2X247.655Y28.861I1.745J2.653
|
||||
G1X247.590Y28.947
|
||||
G1X247.304Y29.401
|
||||
G1X247.150Y29.698
|
||||
G1X246.888Y30.394
|
||||
G1X246.668Y31.132
|
||||
G1X243.420Y41.419
|
||||
G1X243.133
|
||||
G1X249.500Y21.400
|
||||
G1X249.680Y20.918
|
||||
G1X249.880Y20.269
|
||||
G1X250.036Y19.612
|
||||
G1X250.149Y18.936
|
||||
G1X250.190Y18.463
|
||||
G1X250.211Y17.912
|
||||
G1X250.225Y16.513
|
||||
G1Y8.016
|
||||
G0Z5.080
|
||||
G0X159.368
|
||||
G1Z-3.175F1270.0
|
||||
G1X159.644F2540.0
|
||||
G1Y41.419
|
||||
G1X159.368
|
||||
G1Y8.016
|
||||
G0Z5.080
|
||||
G0X133.203
|
||||
G1Z-3.175F1270.0
|
||||
G1X133.478F2540.0
|
||||
G1Y19.288
|
||||
G1X133.495Y20.123
|
||||
G1X133.556Y20.925
|
||||
G1X133.697Y21.712
|
||||
G1X133.822Y22.074
|
||||
G1X134.078Y22.633
|
||||
G1X134.130Y22.723
|
||||
G2X135.032Y23.713I2.746J-1.594
|
||||
G1X135.122Y23.778
|
||||
G1X135.738Y24.121
|
||||
G1X136.070Y24.260
|
||||
G1X136.588Y24.399
|
||||
G1X137.076Y24.485
|
||||
G1X137.885Y24.559
|
||||
G1X138.771Y24.580
|
||||
G1X145.626
|
||||
G1Y24.855
|
||||
G1X139.094
|
||||
G1X138.075Y24.883
|
||||
G1X137.539Y24.929
|
||||
G1X137.029Y24.999
|
||||
G1X136.489Y25.115
|
||||
G1X136.122Y25.236
|
||||
G1X135.630Y25.447
|
||||
G1X135.480Y25.527
|
||||
G1X134.777Y26.035
|
||||
G1X134.681Y26.127
|
||||
G1X134.136Y26.817
|
||||
G1X134.084Y26.906
|
||||
G1X133.828Y27.457
|
||||
G1X133.702Y27.818
|
||||
G1X133.557Y28.600
|
||||
G1X133.495Y29.391
|
||||
G1X133.478Y30.207
|
||||
G1Y35.748
|
||||
G1X133.502Y36.665
|
||||
G1X133.541Y37.153
|
||||
G1X133.601Y37.619
|
||||
G1X133.699Y38.112
|
||||
G1X133.873Y38.681
|
||||
G1X134.045Y39.039
|
||||
G1X134.470Y39.698
|
||||
G1X134.548Y39.791
|
||||
G2X135.430Y40.523I2.436J-2.037
|
||||
G1X135.520Y40.574
|
||||
G1X136.072Y40.818
|
||||
G1X136.429Y40.936
|
||||
G1X137.204Y41.070
|
||||
G1X137.999Y41.128
|
||||
G1X138.833Y41.143
|
||||
G1X146.730
|
||||
G1Y41.419
|
||||
G1X133.203
|
||||
G1Y8.016
|
||||
G0Z5.080
|
||||
G0X107.038
|
||||
G1Z-3.175F1270.0
|
||||
G1X120.565F2540.0
|
||||
G1Y8.291
|
||||
G1X112.934
|
||||
G1X111.953Y8.315
|
||||
G1X111.446Y8.353
|
||||
G1X110.966Y8.412
|
||||
G1X110.462Y8.507
|
||||
G1X109.883Y8.676
|
||||
G1X109.516Y8.848
|
||||
G1X108.829Y9.284
|
||||
G1X108.734Y9.363
|
||||
G2X108.068Y10.127I2.031J2.441
|
||||
G1X108.003Y10.231
|
||||
G1X107.733Y10.777
|
||||
G1X107.609Y11.102
|
||||
G1X107.485Y11.599
|
||||
G1X107.405Y12.080
|
||||
G1X107.334Y12.901
|
||||
G1X107.313Y13.808
|
||||
G1Y19.288
|
||||
G1X107.330Y20.123
|
||||
G1X107.391Y20.925
|
||||
G1X107.532Y21.712
|
||||
G1X107.657Y22.074
|
||||
G1X107.913Y22.633
|
||||
G1X107.965Y22.723
|
||||
G2X108.867Y23.713I2.746J-1.594
|
||||
G1X108.957Y23.778
|
||||
G1X109.573Y24.121
|
||||
G1X109.905Y24.260
|
||||
G1X110.423Y24.399
|
||||
G1X110.911Y24.485
|
||||
G1X111.720Y24.559
|
||||
G1X112.606Y24.580
|
||||
G1X119.461
|
||||
G1Y24.855
|
||||
G1X112.929
|
||||
G1X111.910Y24.883
|
||||
G1X111.374Y24.929
|
||||
G1X110.863Y24.999
|
||||
G1X110.323Y25.115
|
||||
G1X109.957Y25.236
|
||||
G1X109.464Y25.447
|
||||
G1X109.315Y25.527
|
||||
G1X108.612Y26.035
|
||||
G1X108.516Y26.127
|
||||
G1X107.971Y26.817
|
||||
G1X107.919Y26.906
|
||||
G1X107.663Y27.457
|
||||
G1X107.536Y27.818
|
||||
G1X107.392Y28.600
|
||||
G1X107.330Y29.391
|
||||
G1X107.313Y30.207
|
||||
G1Y35.748
|
||||
G1X107.337Y36.665
|
||||
G1X107.376Y37.153
|
||||
G1X107.436Y37.619
|
||||
G1X107.534Y38.112
|
||||
G1X107.708Y38.681
|
||||
G1X107.880Y39.039
|
||||
G1X108.305Y39.698
|
||||
G1X108.383Y39.791
|
||||
G2X109.265Y40.523I2.436J-2.037
|
||||
G1X109.355Y40.573
|
||||
G1X109.907Y40.817
|
||||
G1X110.263Y40.936
|
||||
G1X111.039Y41.070
|
||||
G1X111.834Y41.128
|
||||
G1X112.668Y41.143
|
||||
G1X120.565
|
||||
G1Y41.419
|
||||
G1X107.038
|
||||
G1Y8.016
|
||||
G0Z5.080
|
||||
G0X91.885Y18.115
|
||||
G1Z-3.175F1270.0
|
||||
G1X91.839Y18.105F2540.0
|
||||
G1X91.885Y17.998
|
||||
G1Y18.115
|
||||
G1X91.677Y20.662
|
||||
G2X89.754Y20.635I-1.004J3.012
|
||||
G1X89.680Y20.658
|
||||
G2X88.622Y21.213I0.918J3.039
|
||||
G1X88.537Y21.280
|
||||
G1X88.090Y21.713
|
||||
G1X87.881Y21.961
|
||||
G1X87.591Y22.393
|
||||
G1X87.224Y23.076
|
||||
G1X86.885Y23.829
|
||||
G1X79.307Y41.419
|
||||
G1X78.112
|
||||
G1Y8.016
|
||||
G1X78.388
|
||||
G1Y24.599
|
||||
G1X78.409Y25.408
|
||||
G1X78.493Y26.180
|
||||
G1X78.604Y26.697
|
||||
G1X78.709Y27.008
|
||||
G1X78.939Y27.531
|
||||
G1X78.995Y27.633
|
||||
G2X79.578Y28.391I2.779J-1.535
|
||||
G1X79.643Y28.453
|
||||
G2X81.310Y29.290I2.196J-2.293
|
||||
G1X81.385Y29.302
|
||||
G2X83.305Y29.026I0.529J-3.131
|
||||
G1X83.392Y28.983
|
||||
G2X84.229Y28.391I-1.392J-2.854
|
||||
G1X84.328Y28.294
|
||||
G1X84.668Y27.899
|
||||
G1X84.867Y27.625
|
||||
G1X85.115Y27.206
|
||||
G1X85.335Y26.777
|
||||
G1X93.460Y8.016
|
||||
G1X94.400
|
||||
G1Y41.419
|
||||
G1X94.124
|
||||
G1Y25.009
|
||||
G1X94.101Y24.216
|
||||
G1X94.007Y23.432
|
||||
G1X93.916Y23.078
|
||||
G1X93.747Y22.590
|
||||
G1X93.700Y22.482
|
||||
G2X93.193Y21.681I-2.906J1.279
|
||||
G1X93.137Y21.617
|
||||
G2X91.742Y20.684I-2.399J2.079
|
||||
G1X91.677Y20.662
|
||||
G0Z5.080
|
||||
G0X80.627Y31.718
|
||||
G1Z-3.175F1270.0
|
||||
G1X80.742Y31.742F2540.0
|
||||
G1X80.627Y32.010
|
||||
G1Y31.718
|
||||
G0Z5.080
|
||||
G0X56.689Y8.295
|
||||
G1Z-3.175F1270.0
|
||||
G1X57.314Y8.143F2540.0
|
||||
G1X57.985Y8.049
|
||||
G1X58.710Y8.016
|
||||
G1X59.435Y8.049
|
||||
G1X60.106Y8.143
|
||||
G1X60.731Y8.295
|
||||
G1X61.320Y8.505
|
||||
G1X61.883Y8.775
|
||||
G1X62.429Y9.109
|
||||
G1X62.964Y9.516
|
||||
G1X63.493Y10.004
|
||||
G1X63.980Y10.541
|
||||
G1X64.387Y11.085
|
||||
G1X64.721Y11.636
|
||||
G1X64.989Y12.201
|
||||
G1X65.197Y12.788
|
||||
G1X65.348Y13.407
|
||||
G1X65.441Y14.069
|
||||
G1X65.474Y14.815
|
||||
G1X65.465Y35.027
|
||||
G1X65.401Y35.727
|
||||
G1X65.278Y36.377
|
||||
G1X65.098Y36.986
|
||||
G1X64.861Y37.562
|
||||
G1X64.563Y38.116
|
||||
G1X64.197Y38.656
|
||||
G1X63.755Y39.187
|
||||
G1X63.241Y39.701
|
||||
G1X62.710Y40.142
|
||||
G1X62.170Y40.508
|
||||
G1X61.616Y40.807
|
||||
G1X61.040Y41.044
|
||||
G1X60.431Y41.224
|
||||
G1X59.781Y41.347
|
||||
G1X59.081Y41.410
|
||||
G1X58.339
|
||||
G1X57.639Y41.347
|
||||
G1X56.989Y41.224
|
||||
G1X56.381Y41.044
|
||||
G1X55.804Y40.807
|
||||
G1X55.250Y40.508
|
||||
G1X54.710Y40.142
|
||||
G1X54.179Y39.701
|
||||
G1X53.665Y39.187
|
||||
G1X53.224Y38.656
|
||||
G1X52.858Y38.116
|
||||
G1X52.559Y37.562
|
||||
G1X52.322Y36.986
|
||||
G1X52.142Y36.377
|
||||
G1X52.019Y35.727
|
||||
G1X51.956Y35.027
|
||||
G1X51.956Y14.417
|
||||
G1X52.018Y13.732
|
||||
G1X52.140Y13.093
|
||||
G1X52.320Y12.491
|
||||
G1X52.558Y11.916
|
||||
G1X52.858Y11.359
|
||||
G1X53.228Y10.812
|
||||
G1X53.674Y10.272
|
||||
G1X54.191Y9.750
|
||||
G1X54.722Y9.303
|
||||
G1X55.262Y8.933
|
||||
G1X55.816Y8.632
|
||||
G1X56.391Y8.393
|
||||
G1X56.689Y8.295
|
||||
G0Z5.080
|
||||
G0X56.804Y8.566
|
||||
G1Z-3.175F1270.0
|
||||
G1X56.190Y8.789F2540.0
|
||||
G1X55.603Y9.076
|
||||
G1X55.053Y9.420
|
||||
G1X54.545Y9.814
|
||||
G1X54.085Y10.247
|
||||
G1X53.673Y10.711
|
||||
G1X53.303Y11.209
|
||||
G1X52.978Y11.746
|
||||
G1X52.705Y12.317
|
||||
G1X52.490Y12.915
|
||||
G1X52.338Y13.530
|
||||
G1X52.248Y14.154
|
||||
G1X52.222Y14.705
|
||||
G1Y34.727
|
||||
G1X52.247Y35.285
|
||||
G1X52.335Y35.913
|
||||
G1X52.485Y36.536
|
||||
G1X52.699Y37.143
|
||||
G1X52.975Y37.726
|
||||
G1X53.308Y38.275
|
||||
G1X53.691Y38.784
|
||||
G1X54.116Y39.250
|
||||
G1X54.583Y39.676
|
||||
G1X55.091Y40.058
|
||||
G1X55.640Y40.391
|
||||
G1X56.223Y40.667
|
||||
G1X56.831Y40.881
|
||||
G1X57.453Y41.031
|
||||
G1X58.082Y41.119
|
||||
G1X58.710Y41.147
|
||||
G1X59.339Y41.119
|
||||
G1X59.967Y41.031
|
||||
G1X60.590Y40.881
|
||||
G1X61.197Y40.667
|
||||
G1X61.780Y40.391
|
||||
G1X62.329Y40.058
|
||||
G1X62.837Y39.676
|
||||
G1X63.304Y39.250
|
||||
G1X63.730Y38.784
|
||||
G1X64.112Y38.275
|
||||
G1X64.445Y37.726
|
||||
G1X64.721Y37.143
|
||||
G1X64.935Y36.536
|
||||
G1X65.086Y35.913
|
||||
G1X65.173Y35.285
|
||||
G1X65.198Y34.727
|
||||
G1Y14.705
|
||||
G1X65.172Y14.154
|
||||
G1X65.083Y13.530
|
||||
G1X64.930Y12.915
|
||||
G1X64.716Y12.317
|
||||
G1X64.443Y11.746
|
||||
G1X64.118Y11.209
|
||||
G1X63.748Y10.711
|
||||
G1X63.335Y10.247
|
||||
G1X62.875Y9.814
|
||||
G1X62.367Y9.420
|
||||
G1X61.817Y9.076
|
||||
G1X61.230Y8.789
|
||||
G1X60.616Y8.566
|
||||
G1X59.985Y8.409
|
||||
G1X59.348Y8.317
|
||||
G1X58.710Y8.288
|
||||
G1X58.072Y8.317
|
||||
G1X57.435Y8.409
|
||||
G1X56.804Y8.566
|
||||
G0Z5.080
|
||||
G0Z20.320
|
||||
G0X0.000Y0.000
|
||||
M2
|
||||
%
|
||||
@@ -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
BIN
installer/splash/boot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
8
installer/splash/onefinity.plymouth
Normal file
8
installer/splash/onefinity.plymouth
Normal file
@@ -0,0 +1,8 @@
|
||||
[Plymouth Theme]
|
||||
Name=onefinity
|
||||
Description=Onefinity
|
||||
ModuleName=script
|
||||
|
||||
[script]
|
||||
ImageDir=/usr/share/plymouth/themes/onefinity
|
||||
ScriptFile=/usr/share/plymouth/themes/onefinity/onefinity.script
|
||||
@@ -1,7 +1,12 @@
|
||||
screenW = Window.GetWidth();
|
||||
screenH = Window.GetHeight();
|
||||
|
||||
image = Image("splash.png");
|
||||
image = Image("boot.png");
|
||||
|
||||
if (Plymouth.GetMode() == "shutdown") {
|
||||
image = Image("shutdown.png");
|
||||
}
|
||||
|
||||
imageW = image.GetWidth();
|
||||
imageH = image.GetHeight();
|
||||
|
||||
BIN
installer/splash/shutdown.png
Normal file
BIN
installer/splash/shutdown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
5
package-lock.json
generated
5
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
'''Check that the configuration variable template used on the RPi matches the
|
||||
variables used in the AVR'''
|
||||
|
||||
import sys
|
||||
import json
|
||||
|
||||
templ = json.load(open('src/resources/config-template.json', 'r'))
|
||||
vars = json.load(open('avr/build/vars.json', 'r'))
|
||||
|
||||
|
||||
def check(section):
|
||||
errors = 0
|
||||
|
||||
for name, entry in section.items():
|
||||
if 'type' in entry:
|
||||
ok = False
|
||||
|
||||
# TODO check that defaults are valid
|
||||
# TODO check that types match
|
||||
|
||||
if 'code' in entry and not entry['code'] in vars:
|
||||
print('"%s" with code "%s" not found' % (name, entry['code']))
|
||||
|
||||
else: ok = True
|
||||
|
||||
if not ok: errors += 1
|
||||
|
||||
else: errors += check(entry)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
errors = check(templ)
|
||||
print('\n%d errors' % errors)
|
||||
sys.exit(errors != 0)
|
||||
@@ -1,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
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
ROOT="$PWD/demo"
|
||||
|
||||
# Clean up on EXIT
|
||||
function cleanup {
|
||||
umount "$ROOT"/{dev/pts,dev,sys,proc} 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# mount binds
|
||||
mount --bind /dev "$ROOT/dev/"
|
||||
mount --bind /sys "$ROOT/sys/"
|
||||
mount --bind /proc "$ROOT/proc/"
|
||||
mount --bind /dev/pts "$ROOT/dev/pts"
|
||||
|
||||
chroot "$ROOT" "$@"
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
pip3 download -d python-packages -r requirements.txt
|
||||
pip3 download -d installer/python-packages -r requirements.txt
|
||||
|
||||
@@ -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
|
||||
# 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
|
||||
|
||||
@@ -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
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
import os.path
|
||||
import time
|
||||
|
||||
if not os.path.exists('/dev/video0'):
|
||||
print('/dev/video0 not found')
|
||||
sys.exit(1)
|
||||
|
||||
p = subprocess.Popen('udevadm info -q path /dev/video0'.split(),
|
||||
stdout = subprocess.PIPE)
|
||||
s = p.communicate()[0].decode('utf-8')
|
||||
dev = s.split('/')[7]
|
||||
|
||||
with open('/sys/bus/usb/drivers/usb/unbind', 'w') as f:
|
||||
f.write(dev)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
with open('/sys/bus/usb/drivers/usb/bind', 'w') as f:
|
||||
f.write(dev)
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
USER=bbmc
|
||||
HOST=bbctrl.local
|
||||
|
||||
if [ $# -eq 1 ]; then
|
||||
if [[ "$1" = *@ ]]; then
|
||||
LOGIN="$1"
|
||||
else
|
||||
LOGIN=$USER@"$1"
|
||||
fi
|
||||
else
|
||||
LOGIN=$USER@$HOST
|
||||
fi
|
||||
|
||||
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$LOGIN"
|
||||
@@ -1,64 +0,0 @@
|
||||
function convertToAbsolute(path) {
|
||||
var x0, y0, x1, y1, x2, y2, segs = path.pathSegList;
|
||||
|
||||
for (var x = 0, y = 0, i = 0, len = segs.numberOfItems; i < len; i++) {
|
||||
var seg = segs.getItem(i), c = seg.pathSegTypeAsLetter;
|
||||
|
||||
if (/[MLHVCSQTA]/.test(c)){
|
||||
if ('x' in seg) x = seg.x;
|
||||
if ('y' in seg) y = seg.y;
|
||||
|
||||
} else {
|
||||
if ('x1' in seg) x1 = x + seg.x1;
|
||||
if ('x2' in seg) x2 = x + seg.x2;
|
||||
if ('y1' in seg) y1 = y + seg.y1;
|
||||
if ('y2' in seg) y2 = y + seg.y2;
|
||||
if ('x' in seg) x += seg.x;
|
||||
if ('y' in seg) y += seg.y;
|
||||
|
||||
switch(c) {
|
||||
case 'm':
|
||||
segs.replaceItem(path.createSVGPathSegMovetoAbs(x, y), i);
|
||||
break;
|
||||
case 'l':
|
||||
segs.replaceItem(path.createSVGPathSegLinetoAbs(x, y), i);
|
||||
break;
|
||||
case 'h':
|
||||
segs.replaceItem(path.createSVGPathSegLinetoHorizontalAbs(x), i);
|
||||
break;
|
||||
case 'v':
|
||||
segs.replaceItem(path.createSVGPathSegLinetoVerticalAbs(y), i);
|
||||
break;
|
||||
case 'c':
|
||||
segs.replaceItem(
|
||||
path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i);
|
||||
break;
|
||||
case 's':
|
||||
segs.replaceItem(
|
||||
path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i);
|
||||
break;
|
||||
case 'q':
|
||||
segs.replaceItem(
|
||||
path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i);
|
||||
break;
|
||||
case 't':
|
||||
segs.replaceItem(
|
||||
path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i);
|
||||
break;
|
||||
case 'a':
|
||||
segs.replaceItem(
|
||||
path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle,
|
||||
seg.largeArcFlag, seg.sweepFlag), i);
|
||||
break;
|
||||
|
||||
case 'z': case 'Z':
|
||||
x = x0;
|
||||
y = y0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the start of a subpath
|
||||
if (c == 'M' || c == 'm') x0 = x, y0 = y;
|
||||
}
|
||||
}
|
||||
15
setup.py
15
setup.py
@@ -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',
|
||||
|
||||
@@ -4,7 +4,7 @@ module.exports = {
|
||||
attached: function () {
|
||||
this.svelteComponent = SvelteComponents.createComponent(
|
||||
"AdminNetworkView",
|
||||
document.getElementById("svelte-root")
|
||||
document.getElementById("admin-network")
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
this.last_file_time = undefined; // Force reload
|
||||
this.$broadcast('gcode-reload', file.name);
|
||||
} catch (err) {
|
||||
api.alert('Upload failed', err)
|
||||
}
|
||||
SvelteComponents.showDialog("Upload", {
|
||||
file,
|
||||
onComplete: () => {
|
||||
this.last_file_time = undefined; // Force reload
|
||||
this.$broadcast('gcode-reload', file.name);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
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) });
|
||||
},
|
||||
|
||||
@@ -530,7 +513,7 @@ module.exports = {
|
||||
this.send(JSON.stringify(data));
|
||||
},
|
||||
|
||||
showProbeDialog: function(probeType) {
|
||||
showProbeDialog: function (probeType) {
|
||||
SvelteComponents.showDialog("Probe", { probeType });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@ html(lang="en")
|
||||
|
||||
body(v-cloak)
|
||||
#svelte-dialog-host
|
||||
#svelte-devmode-host
|
||||
|
||||
#overlay(v-if="status != 'connected'")
|
||||
span {{status}}
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
script#admin-network-view-template(type="text/x-template")
|
||||
#admin-network
|
||||
#svelte-root
|
||||
#admin-network
|
||||
@@ -150,60 +150,18 @@ script#control-view-template(type="text/x-template")
|
||||
th.actions
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title=`Set {{'${axis}' | upper}} axis position.`,
|
||||
@click=`show_set_position('${axis}')`,style="height:60px;width:60px")
|
||||
@click=`show_set_position('${axis}')`, style="height:60px;width:60px")
|
||||
.fa.fa-cog
|
||||
|
||||
button.pure-button(:disabled="!can_set_axis",
|
||||
title=`Zero {{'${axis}' | upper}} axis offset.`,
|
||||
@click=`zero('${axis}')`,style="height:60px;width:60px")
|
||||
@click=`zero('${axis}')`, style="height:60px;width:60px")
|
||||
.fa.fa-map-marker
|
||||
|
||||
button.pure-button(:disabled="!is_idle", @click=`home('${axis}')`,
|
||||
title=`Home {{'${axis}' | upper}} axis.`,style="height:60px;width:60px")
|
||||
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")
|
||||
| of {{toolpath.lines | number}}
|
||||
|
||||
tr
|
||||
th Progress
|
||||
td.progress
|
||||
|
||||
@@ -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.
|
||||
#settings
|
||||
@@ -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
|
||||
@@ -25,10 +25,7 @@ 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.avr = bbctrl.AVR(self)
|
||||
|
||||
self.i2c = bbctrl.I2C(args.i2c_port, args.demo)
|
||||
self.mach = bbctrl.Mach(self, self.avr)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -69,7 +39,7 @@ def get_resource(path):
|
||||
return resource_filename(Requirement.parse('bbctrl'), 'bbctrl/' + path)
|
||||
|
||||
|
||||
def on_exit(sig = 0, func = None):
|
||||
def on_exit(sig=0, func=None):
|
||||
global ctrl
|
||||
|
||||
print('Exit handler triggered: signal = %d', sig)
|
||||
@@ -85,76 +55,46 @@ 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')
|
||||
description='Buildbotics Machine Controller')
|
||||
|
||||
parser.add_argument('-p', '--port', default = 80,
|
||||
type = int, help = 'HTTP port')
|
||||
parser.add_argument('-a', '--addr', metavar = 'IP', default = '0.0.0.0',
|
||||
help = 'HTTP address to bind')
|
||||
parser.add_argument('-s', '--serial', default = '/dev/ttyAMA0',
|
||||
help = 'Serial device')
|
||||
parser.add_argument('-b', '--baud', default = 230400, type = int,
|
||||
help = 'Serial baud rate')
|
||||
parser.add_argument('--i2c-port', default = 1, type = int,
|
||||
help = 'I2C port')
|
||||
parser.add_argument('--avr-addr', default = 0x2b, type = int,
|
||||
help = 'AVR I2C address')
|
||||
parser.add_argument('--pwr-addr', default = 0x60, type = int,
|
||||
help = 'Power AVR I2C address')
|
||||
parser.add_argument('-v', '--verbose', action = 'store_true',
|
||||
help = 'Verbose output')
|
||||
parser.add_argument('-l', '--log', metavar = "FILE",
|
||||
help = 'Set a log file')
|
||||
parser.add_argument('--disable-camera', action = 'store_true',
|
||||
help = 'Disable the camera')
|
||||
parser.add_argument('--width', default = 640, type = int,
|
||||
help = 'Camera width')
|
||||
parser.add_argument('--height', default = 480, type = int,
|
||||
help = 'Camera height')
|
||||
parser.add_argument('--fps', default = 15, type = int,
|
||||
help = 'Camera frames per second')
|
||||
parser.add_argument('--camera-clients', default = 4,
|
||||
help = 'Maximum simultaneous camera clients')
|
||||
parser.add_argument('--demo', action = 'store_true',
|
||||
help = 'Enter demo mode')
|
||||
parser.add_argument('--debug', default = 0, type = int,
|
||||
help = 'Enable debug mode and set frequency in seconds')
|
||||
parser.add_argument('--fast-emu', action = 'store_true',
|
||||
help = 'Enter demo mode')
|
||||
parser.add_argument('--client-timeout', default = 5 * 60, type = int,
|
||||
help = 'Demo client timeout in seconds')
|
||||
parser.add_argument('-p', '--port', default=80,
|
||||
type=int, help='HTTP port')
|
||||
parser.add_argument('-a', '--addr', metavar='IP', default='0.0.0.0',
|
||||
help='HTTP address to bind')
|
||||
parser.add_argument('-s', '--serial', default='/dev/ttyAMA0',
|
||||
help='Serial device')
|
||||
parser.add_argument('-b', '--baud', default=230400, type=int,
|
||||
help='Serial baud rate')
|
||||
parser.add_argument('--i2c-port', default=1, type=int,
|
||||
help='I2C port')
|
||||
parser.add_argument('--avr-addr', default=0x2b, type=int,
|
||||
help='AVR I2C address')
|
||||
parser.add_argument('--pwr-addr', default=0x60, type=int,
|
||||
help='Power AVR I2C address')
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='Verbose output')
|
||||
parser.add_argument('-l', '--log', metavar="FILE",
|
||||
help='Set a log file')
|
||||
parser.add_argument('--disable-camera', action='store_true',
|
||||
help='Disable the camera')
|
||||
parser.add_argument('--width', default=640, type=int,
|
||||
help='Camera width')
|
||||
parser.add_argument('--height', default=480, type=int,
|
||||
help='Camera height')
|
||||
parser.add_argument('--fps', default=15, type=int,
|
||||
help='Camera frames per second')
|
||||
parser.add_argument('--camera-clients', default=4,
|
||||
help='Maximum simultaneous camera clients')
|
||||
parser.add_argument('--demo', action='store_true',
|
||||
help='Enter demo mode')
|
||||
parser.add_argument('--debug', default=0, type=int,
|
||||
help='Enable debug mode and set frequency in seconds')
|
||||
parser.add_argument('--fast-emu', action='store_true',
|
||||
help='Enter demo mode')
|
||||
parser.add_argument('--client-timeout', default=5 * 60, type=int,
|
||||
help='Demo client timeout in seconds')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
1394
src/svelte-components/package-lock.json
generated
1394
src/svelte-components/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
131
src/svelte-components/src/components/ConfigTemplatedInput.svelte
Normal file
131
src/svelte-components/src/components/ConfigTemplatedInput.svelte
Normal 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}
|
||||
@@ -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>
|
||||
97
src/svelte-components/src/components/SettingsView.svelte
Normal file
97
src/svelte-components/src/components/SettingsView.svelte
Normal 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>
|
||||
@@ -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} />
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<Dialog
|
||||
bind:open
|
||||
scrimClickAction=""
|
||||
aria-labelledby="home-machine-dialog-title"
|
||||
aria-describedby="home-machine-dialog-content"
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
@@ -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 },
|
||||
|
||||
@@ -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>
|
||||
240
src/svelte-components/src/dialogs/SetTimeDialog.svelte
Normal file
240
src/svelte-components/src/dialogs/SetTimeDialog.svelte
Normal 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>
|
||||
78
src/svelte-components/src/dialogs/UploadDialog.svelte
Normal file
78
src/svelte-components/src/dialogs/UploadDialog.svelte
Normal 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>
|
||||
@@ -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);
|
||||
}
|
||||
36
src/svelte-components/src/lib/ControllerState.ts
Normal file
36
src/svelte-components/src/lib/ControllerState.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,60 +33,43 @@ 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() {
|
||||
const networksByName: Record<string, WifiNetwork> = {}
|
||||
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) {
|
||||
if (network.Name) {
|
||||
network.lastSeen = now;
|
||||
network.active = networkInfo.wifi.ssid === network.Name;
|
||||
networksByName[network.Name] = network;
|
||||
}
|
||||
}
|
||||
|
||||
for (let network of Object.values(networksByName)) {
|
||||
if (network.lastSeen - now > 30000) {
|
||||
delete networksByName[network.Name];
|
||||
}
|
||||
}
|
||||
|
||||
set({
|
||||
ipAddresses: networkInfo.ipAddresses,
|
||||
hostname: networkInfo.hostname,
|
||||
wifi: {
|
||||
ssid: networkInfo.wifi.ssid,
|
||||
networks: Object.values(networksByName).sort((a, b) => {
|
||||
switch (true) {
|
||||
case a.active:
|
||||
return -1;
|
||||
|
||||
case b.active:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return a.Name.localeCompare(b.Name);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.debug("Failed to fetch network info", error);
|
||||
for (let network of rawNetworkInfo.wifi.networks) {
|
||||
if (network.Name) {
|
||||
network.lastSeen = now;
|
||||
network.active = rawNetworkInfo.wifi.ssid === network.Name;
|
||||
networksByName[network.Name] = network;
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearInterval(networkInfoIntervalId);
|
||||
for (let network of Object.values(networksByName)) {
|
||||
if (network.lastSeen - now > 30000) {
|
||||
delete networksByName[network.Name];
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export function init() {
|
||||
return networkInfo.subscribe(() => ({}));
|
||||
networkInfo.set({
|
||||
ipAddresses: rawNetworkInfo.ipAddresses,
|
||||
hostname: rawNetworkInfo.hostname,
|
||||
wifi: {
|
||||
ssid: rawNetworkInfo.wifi.ssid,
|
||||
networks: Object.values(networksByName).sort((a, b) => {
|
||||
switch (true) {
|
||||
case a.active:
|
||||
return -1;
|
||||
|
||||
case b.active:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return a.Name.localeCompare(b.Name);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
build: {
|
||||
minify: false,
|
||||
target: "chrome60",
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/main.ts'),
|
||||
|
||||
Reference in New Issue
Block a user