├── .gitignore ├── docs ├── CNAME ├── img │ ├── icon.png │ ├── favicon.ico │ ├── common │ │ ├── zip.png │ │ ├── os_mac.png │ │ ├── os_linux.png │ │ ├── os_linux_arm.png │ │ ├── os_windows.png │ │ ├── bad.code.dialog.png │ │ └── bad.code.setting.png │ ├── guide │ │ ├── languages.png │ │ ├── platform │ │ │ ├── run.png │ │ │ ├── work_coord.gif │ │ │ ├── setup_wizard-1.png │ │ │ ├── setup_wizard-2.png │ │ │ ├── setup_wizard-3.png │ │ │ ├── setup_wizard-4.png │ │ │ ├── setup_wizard-5.png │ │ │ ├── setup_wizard-6.png │ │ │ ├── setup_wizard-7.png │ │ │ ├── setup_wizard-8.png │ │ │ ├── setup_wizard-9.png │ │ │ ├── connect_firmware.png │ │ │ ├── controller_state.png │ │ │ ├── overrides_window.png │ │ │ ├── connect_serial_port.png │ │ │ ├── connect_serial_port.svg │ │ │ └── controller_state.svg │ │ ├── languages_platform.png │ │ ├── languages_classic_select.png │ │ └── languages_classic_preferences.png │ ├── platform │ │ ├── editor.png │ │ ├── screenshot.png │ │ ├── about_popup.png │ │ ├── click_to_jog.png │ │ ├── keybindings.png │ │ ├── Digital_readout.png │ │ ├── zoom_to_region.png │ │ └── Overrides_window.png │ ├── screenshots │ │ ├── sending.png │ │ ├── designer.png │ │ ├── finished.png │ │ ├── visualizer.png │ │ └── advanced_machine_control.png │ ├── sponsors │ │ ├── bobs_cnc.jpg │ │ ├── cloudbees.png │ │ ├── poeditor.png │ │ └── artifactory.png │ ├── dev │ │ └── UGSPlatform_open.png │ ├── tutorials │ │ ├── workflow_plugin │ │ │ ├── 07.Demo.gif │ │ │ ├── 00.Design.png │ │ │ ├── 05.GUI_Builder.png │ │ │ ├── 01.Create_Module.png │ │ │ ├── 06.GUI_Builder.02.png │ │ │ ├── 02.New_Module_Wizard_01.png │ │ │ ├── 03.Project_Properties.png │ │ │ └── 04.Create_Window_Class.png │ │ └── gcode_processor_plugin │ │ │ ├── 3.edit.png │ │ │ ├── 1.update.png │ │ │ └── 2.settings.png │ ├── shields │ │ ├── License-GPLv3-blue.svg │ │ └── Language-Java-brightgreen.svg │ └── ugs.svg ├── usage.md ├── css │ └── style.css ├── installing.md ├── dev │ ├── getting_started.md │ ├── frontend_development.md │ ├── backend_development.md │ ├── gcode_processor.md │ └── plugin.md ├── contributing.md ├── download.md └── index.md ├── requirements.txt ├── mkdocs.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | site/ 2 | *.iml 3 | .idea 4 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | universalgcodesender.com 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-bootswatch 3 | -------------------------------------------------------------------------------- /docs/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/icon.png -------------------------------------------------------------------------------- /docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/favicon.ico -------------------------------------------------------------------------------- /docs/img/common/zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/common/zip.png -------------------------------------------------------------------------------- /docs/img/common/os_mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/common/os_mac.png -------------------------------------------------------------------------------- /docs/img/common/os_linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/common/os_linux.png -------------------------------------------------------------------------------- /docs/img/guide/languages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/languages.png -------------------------------------------------------------------------------- /docs/img/platform/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/platform/editor.png -------------------------------------------------------------------------------- /docs/img/common/os_linux_arm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/common/os_linux_arm.png -------------------------------------------------------------------------------- /docs/img/common/os_windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/common/os_windows.png -------------------------------------------------------------------------------- /docs/img/guide/platform/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/run.png -------------------------------------------------------------------------------- /docs/img/platform/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/platform/screenshot.png -------------------------------------------------------------------------------- /docs/img/screenshots/sending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/screenshots/sending.png -------------------------------------------------------------------------------- /docs/img/sponsors/bobs_cnc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/sponsors/bobs_cnc.jpg -------------------------------------------------------------------------------- /docs/img/sponsors/cloudbees.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/sponsors/cloudbees.png -------------------------------------------------------------------------------- /docs/img/sponsors/poeditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/sponsors/poeditor.png -------------------------------------------------------------------------------- /docs/img/dev/UGSPlatform_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/dev/UGSPlatform_open.png -------------------------------------------------------------------------------- /docs/img/platform/about_popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/platform/about_popup.png -------------------------------------------------------------------------------- /docs/img/platform/click_to_jog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/platform/click_to_jog.png -------------------------------------------------------------------------------- /docs/img/platform/keybindings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/platform/keybindings.png -------------------------------------------------------------------------------- /docs/img/screenshots/designer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/screenshots/designer.png -------------------------------------------------------------------------------- /docs/img/screenshots/finished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/screenshots/finished.png -------------------------------------------------------------------------------- /docs/img/sponsors/artifactory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/sponsors/artifactory.png -------------------------------------------------------------------------------- /docs/img/common/bad.code.dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/common/bad.code.dialog.png -------------------------------------------------------------------------------- /docs/img/common/bad.code.setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/common/bad.code.setting.png -------------------------------------------------------------------------------- /docs/img/guide/languages_platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/languages_platform.png -------------------------------------------------------------------------------- /docs/img/platform/Digital_readout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/platform/Digital_readout.png -------------------------------------------------------------------------------- /docs/img/platform/zoom_to_region.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/platform/zoom_to_region.png -------------------------------------------------------------------------------- /docs/img/screenshots/visualizer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/screenshots/visualizer.png -------------------------------------------------------------------------------- /docs/img/guide/platform/work_coord.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/work_coord.gif -------------------------------------------------------------------------------- /docs/img/platform/Overrides_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/platform/Overrides_window.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-1.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-2.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-3.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-4.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-5.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-6.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-7.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-8.png -------------------------------------------------------------------------------- /docs/img/guide/platform/setup_wizard-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/setup_wizard-9.png -------------------------------------------------------------------------------- /docs/img/guide/languages_classic_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/languages_classic_select.png -------------------------------------------------------------------------------- /docs/img/guide/platform/connect_firmware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/connect_firmware.png -------------------------------------------------------------------------------- /docs/img/guide/platform/controller_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/controller_state.png -------------------------------------------------------------------------------- /docs/img/guide/platform/overrides_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/overrides_window.png -------------------------------------------------------------------------------- /docs/img/guide/platform/connect_serial_port.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/platform/connect_serial_port.png -------------------------------------------------------------------------------- /docs/img/tutorials/workflow_plugin/07.Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/workflow_plugin/07.Demo.gif -------------------------------------------------------------------------------- /docs/img/guide/languages_classic_preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/guide/languages_classic_preferences.png -------------------------------------------------------------------------------- /docs/img/screenshots/advanced_machine_control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/screenshots/advanced_machine_control.png -------------------------------------------------------------------------------- /docs/img/tutorials/workflow_plugin/00.Design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/workflow_plugin/00.Design.png -------------------------------------------------------------------------------- /docs/img/tutorials/gcode_processor_plugin/3.edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/gcode_processor_plugin/3.edit.png -------------------------------------------------------------------------------- /docs/img/tutorials/gcode_processor_plugin/1.update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/gcode_processor_plugin/1.update.png -------------------------------------------------------------------------------- /docs/img/tutorials/workflow_plugin/05.GUI_Builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/workflow_plugin/05.GUI_Builder.png -------------------------------------------------------------------------------- /docs/img/tutorials/gcode_processor_plugin/2.settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/gcode_processor_plugin/2.settings.png -------------------------------------------------------------------------------- /docs/img/tutorials/workflow_plugin/01.Create_Module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/workflow_plugin/01.Create_Module.png -------------------------------------------------------------------------------- /docs/img/tutorials/workflow_plugin/06.GUI_Builder.02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/workflow_plugin/06.GUI_Builder.02.png -------------------------------------------------------------------------------- /docs/img/tutorials/workflow_plugin/02.New_Module_Wizard_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/workflow_plugin/02.New_Module_Wizard_01.png -------------------------------------------------------------------------------- /docs/img/tutorials/workflow_plugin/03.Project_Properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/workflow_plugin/03.Project_Properties.png -------------------------------------------------------------------------------- /docs/img/tutorials/workflow_plugin/04.Create_Window_Class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/winder/ugs_website/HEAD/docs/img/tutorials/workflow_plugin/04.Create_Window_Class.png -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: UGS 2 | theme: 3 | logo: assets/icon.png 4 | name: cosmo 5 | nav_style: dark 6 | 7 | markdown_extensions: 8 | - admonition 9 | 10 | site_author: Will Winder 11 | site_description: "Cross platform G-Code sender with advanced features for GRBL based CNC machines." 12 | google_analytics: ['UA-73554112-1', 'auto'] 13 | extra_css: 14 | - css/style.css 15 | nav: 16 | - 'Home': 'index.md' 17 | - 'Download': 'download.md' 18 | - 'Installing': 'installing.md' 19 | - 'Usage': 'usage.md' 20 | - 'Contributing': 'contributing.md' 21 | 22 | -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | Check out our wiki for instructions on how to use Universal Gcode Sender 4 | 5 | * Connecting to the controller 6 | * Configuration 7 | * Usage 8 | * FAQ and Troubleshooting -------------------------------------------------------------------------------- /docs/img/shields/License-GPLv3-blue.svg: -------------------------------------------------------------------------------- 1 | LicenseLicenseGPLv3GPLv3 -------------------------------------------------------------------------------- /docs/img/shields/Language-Java-brightgreen.svg: -------------------------------------------------------------------------------- 1 | LanguageLanguageJavaJava -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Website for [Universal Gcode Sender](http://winder.github.io/ugs_website/). The site is made using the [MkDocs](http://www.mkdocs.org/) static site generator, which allows you to build a static webpage using a series of markdown files. 2 | 3 | See the `docs` folder for markdown files. If you install the `mkdocs` utility on your machine you can test changes by running `mkdocs serve` and navigating to the location specified int he console output. 4 | 5 | To make a new page simply register its location in the `mkdocs.yml`, and create a new `.md` file at the location specified. 6 | 7 | If there's anything in particular you'd like to see feel free to open a ticket and ask. 8 | 9 | For more information about how to contribute documentation see the mkdocs documentation: 10 | http://www.mkdocs.org/user-guide/writing-your-docs/ 11 | 12 | ### Setup 13 | 14 | - `pip install -r requirements.txt` 15 | -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- 1 | h1, h2, h3, h4 { 2 | margin-top: 1em; 3 | margin-bottom: 0.5em; 4 | padding: 0; 5 | } 6 | 7 | .ms-md-auto, .mx-auto { 8 | margin-left: auto !important; 9 | } 10 | 11 | .bs-sidebar .nav > .active > a, 12 | .bs-sidebar .nav > .active:hover > a, 13 | .bs-sidebar .nav > .active:focus > a { 14 | background-color: #e6e6e6; 15 | border-right: 2px solid; 16 | font-weight: normal; 17 | } 18 | 19 | .navitem.active, 20 | .dropdown.active { 21 | font-weight: bold; 22 | } 23 | 24 | .navbar-dark .navbar-nav .nav-link { 25 | color: white !important; 26 | } 27 | 28 | hr { 29 | margin-top: 2em; 30 | margin-bottom: 2em; 31 | } 32 | 33 | li > img { 34 | margin-top: 0.8em; 35 | margin-bottom: 0.8em; 36 | } 37 | 38 | .admonition, details { 39 | /*padding: 15px; 40 | margin-bottom: 20px;*/ 41 | border: 1px solid transparent; 42 | border-radius: 4px; 43 | text-align: left; 44 | } 45 | 46 | .admonition.note, .admonition.note > .admonition-title { 47 | border-color: #1a6dca; 48 | padding-bottom: 0; 49 | } 50 | 51 | .admonition.note > .admonition-title { 52 | background-color: transparent; 53 | border-bottom: none; 54 | color: #2e6b89; 55 | font-weight: bold; 56 | } 57 | 58 | .admonition.note, details.note { 59 | color: #2e6b89; 60 | background-color: #e2f0f7; 61 | border-color: #bce8f1; 62 | } -------------------------------------------------------------------------------- /docs/installing.md: -------------------------------------------------------------------------------- 1 | # How to install 2 | To install Universal G-Code Sender, download the correct package for your operating system, unpack and run. See the videos below for detailed instructions. 3 | 4 | !!! note 5 | If you have recently upgraded UGS and it is not working properly, you may need to remove cached files. The Cache is kept in a user directory called ugsplatform and deleting this directory will force UGS to recreate it. The location of the directory is as the following: 6 | 7 | * Windows: `C:\Users\[your username>]\AppData\Local\ugsplatform\Cache` 8 | * Linux: `~/.cache/ugsplatform` 9 | * MacOSX: `~/Library/Application Support/ugsplatform` 10 | 11 | ## Windows 12 | 13 | 14 | ## MacOSX 15 | 16 | 17 | ## Linux 18 | 19 | -------------------------------------------------------------------------------- /docs/dev/getting_started.md: -------------------------------------------------------------------------------- 1 | # Project Organization 2 | 3 | Universal Gcode Sender uses [Maven](http://maven.apache.org/) to build the 4 | project. It is using maven modules to separate the core library / classic GUI 5 | and the UGS Platform project. At the top level a `UGS` target defines the 6 | `ugs-core` and `ugs-platform-parent` modules which can be built separately or 7 | all at once. 8 | 9 | The classic gui is part of the core project in the `ugs-core` module. The 10 | `maven-shade-plugin` and `maven-assembly-plugin` are generate the 11 | self-executing JAR and distribution zip. 12 | 13 | UGS Platform is built on the [NetBeans Platform](https://netbeans.org/features/platform/). 14 | It is also using maven. 15 | 16 | Development is done using NetBeans, but most of the code can be edited using 17 | any IDE that supports Maven. 18 | 19 | # Development with an IDE 20 | ---------------- 21 | 22 | Any IDE supporting Maven should be able to open the UGS project directory. Once 23 | opened it should show you the `ugs-core` and `ugs-platform-parent` modules 24 | which correspond to the Classic and Platform interfaces. 25 | 26 | Development is done using NetBeans, and for some project development NetBeans 27 | is almost required. But for tweaking the UI and experimenting with the backend 28 | any IDE which supports maven can be used. 29 | 30 | ## Classic GUI 31 | ---------------- 32 | In the `ugs-core` module, you can run the `MainWindow.java` file to start the 33 | Classic GUI. 34 | 35 | ## UGS Platform 36 | ---------------- 37 | The platform build has a number of submodules. Load the suite of modules by 38 | running the ugs-platform-app module. 39 | 40 | # Development with the Command Line 41 | ---------------- 42 | 43 | The UGS Classic and Platform interfaces can also be run from the command line. 44 | These commands should be run from the root directory. 45 | 46 | ## Classic GUI 47 | ---------------- 48 | There is a helper script named `run_classic.sh`, or you can use the commands below. 49 | 50 | ### Running the UI 51 | ``` 52 | mvn install 53 | mvn exec:java -Dexec.mainClass="com.willwinder.universalgcodesender.MainWindow" -pl ugs-core 54 | ``` 55 | 56 | ### Executing tests 57 | ``` 58 | mvn install 59 | mvn test -pl ugs-core 60 | ``` 61 | 62 | ### Building the self-executing JAR 63 | ``` 64 | mvn install 65 | mvn package -pl ugs-core 66 | ``` 67 | 68 | ### Building a UniversalGcodeSender.zip release file 69 | ``` 70 | mvn package assembly:assembly 71 | ``` 72 | 73 | ## UGS Platform 74 | ---------------- 75 | There is a helper script named `run_platform.sh`, or you can use the commands below. 76 | 77 | ### Running the UI 78 | ``` 79 | mvn install 80 | mvn nbm:run-platform -pl ugs-platform/application 81 | ``` 82 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | If you are interested in contributing to make UGS even more awesome, there are 4 | many places you can pitch in. 5 | 6 | Browse through the already reported [issues][github_issues_link] or check out 7 | the [discussion forum][discussion_forum_link]. You might know how to code, have 8 | ideas on how to improve the documentation or want to translate the software to 9 | your language. 10 | 11 | 12 | ## Code 13 | 14 | Pull requests are welcome! Is there a feature you would like to see, or a bug 15 | thats been bothering you? Feel free to dig in. Not sure where to start? Ask on 16 | github, use an existing ticket or create a new one. 17 | 18 | If you're planning to make a lot of changes please create an issue to discuss 19 | implementation details. A lot of effort has gone into the current design so we 20 | want to make sure everything will to work together. 21 | 22 | Checkout the [Developing wiki page for more details](https://github.com/winder/Universal-G-Code-Sender/wiki/Developing) 23 | 24 | 25 | ## Documentation 26 | 27 | If you find any errors in the [documentation](https://github.com/winder/Universal-G-Code-Sender/wiki/) or if there are missing pages, you are welcome to contribute! 28 | 29 | 30 | ## Translations 31 | 32 | We are currently using [POEditor](https://poeditor.com/join/project/2J2hB5I41Z) to manage localization. 33 | If you would like to help please consider signing up to contribute through this service. 34 | 35 | To join the project sign up here: [https://poeditor.com/join/project/2J2hB5I41Z](https://poeditor.com/join/project/2J2hB5I41Z) 36 | 37 | ### Adding a new language 38 | 39 | You can add a new language from POEditor and start translating. 40 | 41 | If you want to stop here, create a ticket on GitHub and someone will update the project. To finish the job completely you'll need to know how to use [git](https://git-scm.com). 42 | 43 | * Create an empty property file for your language in `ugs-core/src/resources`. 44 | * Open `src/com/willwinder/universalgcodesender/i18n/AvailableLanguages.java` 45 | * Add your new translation to the `availableLanguages` object. 46 | * Update the file in `update_languages.py` with a mapping between the POEditor key and your new file. 47 | * Run `update_languages.py`, see README in scripts directory for configuration detail. Only commit the new file even if others were updated. 48 | * [Create a GitHub pull request](https://help.github.com/articles/using-pull-requests/). 49 | 50 | Future language syncs will be done periodically with the `update_languages.py` script. 51 | 52 | [github_issues_link]: https://github.com/winder/Universal-G-Code-Sender 53 | [discussion_forum_link]: https://groups.google.com/forum/#!forum/universal-gcode-sender 54 | -------------------------------------------------------------------------------- /docs/dev/frontend_development.md: -------------------------------------------------------------------------------- 1 | # Front-end Architecture 2 | 3 | UGS uses a Model-View-Presenter architecture. What this means is that at a high 4 | level there are three layers which each serve different purposes. A **Model** 5 | for all backend logic, a **View** displayed to the user and a **Presenter** 6 | which serves as a buffer between the model and one or more views. 7 | 8 | # Model 9 | 10 | The model contains all backend logic. Things like opening a connection, listing 11 | serial ports, streaming a file, and handling firmware specific nuances. All of 12 | this is hidden from the front end as much as possible. 13 | 14 | # View 15 | 16 | The view only has access to the presenter. It is responsible for all user 17 | interaction and feedback. The main logic in here should be things like 18 | enabling or disabling components based on the current state of the model. 19 | 20 | ## Classic GUI 21 | 22 | The **Classic GUI** is built using NetBeans. There are a number of custom Swing 23 | components, and they are all initialized with the NetBeans GUI builder. The 24 | vast majority of the **Classic GUI** code is contained in `MainWindow.java`. 25 | There isn't a lot to expand on here, this front end has grown organically over 26 | the years and is fairly rigid. The `Visualizer` component is a standalone 27 | JOGL window which is updated using events from the backend (it was a model for 28 | many of the improvements in the current applications architecture). 29 | 30 | ## UGS Platform 31 | 32 | The **UGS Platform** build is also built using NetBeans. It is a built ontop of 33 | the NetBeans Platform which provides it a robust set of tools like flexible 34 | windows, a plugin framework, and a suite of tools for module communications. At 35 | the core of this is a module named `UGSLib` which is a simple wrapper to the 36 | standard UGS JAR file. There is a suite of modules named `UGSCore` which 37 | provides many of the standard UI elements seen in the **Classic GUI**, in 38 | addition there are other modules that provide new functionality. 39 | 40 | Extending the GUI is now a matter of creating a new plugin, for details on how 41 | to do this see the [Plugin Tutorial](plugin.md). 42 | 43 | # Presenter 44 | 45 | The presenter serves as an API for the model. All the heavy lifting needed for 46 | the GUI should happen here. For example the controller model object knows how 47 | to stream a processed file, but it doesn't know how to process the file. So the 48 | presenter will pass data to the gcode processor and generate a processed object 49 | which can be passed to the controller. 50 | 51 | Similarly, all notifications from the model are reinterpreted for the view with 52 | a simpler message strategy. 53 | 54 | In this way, all updates to the backend code can be leveraged by all front ends 55 | which utilize UGS. 56 | 57 | ## BackendAPI 58 | 59 | In UGS interfaces named `BackendAPI` and `BackendAPIReadOnly` provide the 60 | presenter layer. The read only methods are split off into a sub-interface in 61 | case a developer wants to be sure they don't change any state. For instance a 62 | widget that displays the current machine location probably has no need for 63 | pausing a stream. 64 | 65 | These APIs are used by all front ends (**Classic GUI**, **PendantUI** and 66 | **UGS Platform**). 67 | -------------------------------------------------------------------------------- /docs/download.md: -------------------------------------------------------------------------------- 1 | # Download 2 | 3 | This is the latest release of UGS. For source code, nightly builds or older releases please [visit github](https://github.com/winder/Universal-G-Code-Sender). 4 | 5 | ### UGS Platform 6 | The next generation platform-based interface. 7 | 8 | | Version 2.1.17 | Description | 9 | |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| 10 | | [![Windows x64](../img/common/os_windows.png) Windows 64-bit](https://github.com/winder/Universal-G-Code-Sender/releases/download/v2.1.17/win64-ugs-platform-app-2.1.17.zip) | Windows 64-bit version with bundled Java | 11 | | [![MacOSX x64](../img/common/os_mac.png) MacOSX (Intel)](https://github.com/winder/Universal-G-Code-Sender/releases/download/v2.1.17/macosx-x64-ugs-platform-app-2.1.17.dmg) | MacOSX version with bundled Java | 12 | | [![MacOSX ARM64](../img/common/os_mac.png) MacOSX (Silicon)](https://github.com/winder/Universal-G-Code-Sender/releases/download/v2.1.17/macosx-aarch64-ugs-platform-app-2.1.17.dmg) | MacOSX ARM64 version (for Apple Silicon M1/M2) with bundled Java | 13 | | [![Linux x64](../img/common/os_linux.png) Linux](https://github.com/winder/Universal-G-Code-Sender/releases/download/v2.1.17/linux-x64-ugs-platform-app-2.1.17.tar.gz) | Linux version with bundled Java | 14 | | [![Linux ARM](../img/common/os_linux_arm.png) Linux ARM](https://github.com/winder/Universal-G-Code-Sender/releases/download/v2.1.17/linux-arm-ugs-platform-app-2.1.17.tar.gz) | Linux ARM version with bundled Java. Can be used with Raspberry Pi OS 32-bit | 15 | | [![Linux ARM64](../img/common/os_linux_arm.png) Linux ARM64](https://github.com/winder/Universal-G-Code-Sender/releases/download/v2.1.17/linux-aarch64-ugs-platform-app-2.1.17.tar.gz) | Linux ARM64 version with bundled Java. Can be used with Raspberry Pi OS 64-bit | 16 | | [![All platforms](../img/common/zip.png) All platforms](https://github.com/winder/Universal-G-Code-Sender/releases/download/v2.1.17/ugs-platform-app-2.1.17.zip) | A generic package without [Java][java_link] which needs to be installed separately | 17 | 18 |
19 | 20 | ### UGS Classic 21 | The classic UGS interface with slightly less features but with the same robust backend as the Platform edition. 22 | 23 | | Download | Description | 24 | |--------------------------------------------------------------------------------------------------------------------------------------------------------------| ----------- | 25 | | [![All platforms](../img/common/zip.png) All platforms](https://github.com/winder/Universal-G-Code-Sender/releases/download/v2.1.17/UniversalGcodeSender.zip) | A generic package without [Java][java_link] which needs to be installed separately | 26 | 27 |
28 | 29 | [java_link]: https://java.com/en/download/manual.jsp 30 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | Universal Gcode Sender 4 |
5 | 6 | A free and full featured gcode platform used for interfacing with advanced CNC controllers like [GRBL](https://github.com/grbl/grbl) 7 | , [FluidNC](http://wiki.fluidnc.com/), [TinyG](https://github.com/synthetos/TinyG), [g2core](https://github.com/synthetos/g2) and [Smoothieware](http://smoothieware.org/). 8 | Universal Gcode Sender is a self-contained Java application which includes all external dependencies and can be used on most computers running Windows, MacOSX or Linux. 9 |
10 | 11 |
12 | 13 | [![Language][java_version_img]][java_version_link] 14 | [![License][license_img]][license_link] 15 | [![Build Status][travis_img]][travis_link] 16 | [![GitHub Release Date][release_img]][release_link] 17 | 23 |
24 | 25 | ------------- 26 | 27 | ## Features 28 | * Cross platform, can run on Windows, MacOSX, Linux, and Raspberry Pi 29 | * Configurable user interface 30 | * 3D Gcode Visualizer with color coded line segments and real time tool position feedback 31 | * Real time overrides 32 | * G-code editor 33 | * Designer editor (with laser engraver support) 34 | * Configuratble gcode optimization: 35 | * Remove comments 36 | * Truncate decimal precision to configurable amount 37 | * Convert arcs (G2/G3) to line segments 38 | * Remove whitespace 39 | * Support for Gamepads and Joysticks 40 | * Web pendant interface 41 | 42 | # Screenshots 43 | *Fully modular GUI, reconfigure windows to suite your needs.* 44 | Screenshot 45 | 46 | *Built in gcode editor with line highlighter and syntax highlighting* 47 | 48 | Gcode editor 49 | 50 | *Built in designer with support for importing SVG/DXF/PNG/JPG* 51 | 52 | Designer tool 53 | 54 | *Customizable keybindings.* 55 | 56 | Key bindings 57 | 58 | *Zoom to selection with command and drag.* 59 | 60 | Zoom 61 | 62 | *Right click in the visualizer to jog to a specific XY location.* 63 | 64 | Click to jog 65 | 66 | ------------- 67 | 68 | # Sponsors 69 | 70 |
71 | 72 | Bob's CNC 73 | 74 | 75 | 76 | POEditor 77 | 78 |
79 | 80 | ------------- 81 | 82 | # Donations 83 | 84 | Universal Gcode Sender is free software developed and maintained in my free time for the hobby cnc community. If you would like to make a monetary donation, all proceeds will be used to try convincing my wife that it is worth my time. 85 | 86 | 95 | 96 |
97 |
98 | 99 | $1 100 | $5 101 | $10 102 |
103 | $25 104 | Custom amount 105 |
106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |
118 |
119 | 120 | [java_version_img]: img/shields/Language-Java-brightgreen.svg 121 | [java_version_link]: https://java.com/en/download/manual.jsp 122 | [license_img]: img/shields/License-GPLv3-blue.svg 123 | [license_link]: http://www.gnu.org/licenses/quick-guide-gplv3.en.html 124 | [travis_img]: https://travis-ci.org/winder/Universal-G-Code-Sender.svg?branch=master 125 | [travis_link]: https://travis-ci.org/winder/Universal-G-Code-Sender 126 | [github_img]: https://img.shields.io/badge/Star-123-green.svg?style=social 127 | [github_link]: https://github.com/winder/Universal-G-Code-Sender 128 | [release_img]: https://img.shields.io/github/release/winder/Universal-G-Code-Sender 129 | [release_link]: download/ 130 | -------------------------------------------------------------------------------- /docs/dev/backend_development.md: -------------------------------------------------------------------------------- 1 | # Backend architecture 2 | 3 | Similar to the front-end there are more layers on the backend to help with 4 | supporting differences between different gcode controllers and the different 5 | ways to communicate with these controllers. Because UGS depends on serial 6 | events from CNC devices, the communication between layers is also event driven. 7 | This is implemented using a series of **Listener** classes which pass messages 8 | from the lower levels to the upper levels whenever data is detected on the 9 | serial port (USB). 10 | 11 | ## Controller 12 | 13 | A controller is primarily responsible for implementing controller-specific 14 | features. Different features can be things like what happens when a 15 | `Perform Homing` command is requested, or how to issue status requests and 16 | parse their results. `GRBL` and `TinyG` are both supported, they share a 17 | lot of code with the **AbstractController.java** abstract class. 18 | 19 | Internally the **AbstractController** class implements several important 20 | things. It manages the stream lifecycle, keeping track of which commands have 21 | been sent, which have been completed and in some cases which are queued for 22 | sending. The controller also figures out when the stream has finished. Finally 23 | the **AbstractController** implements the `SerialCommunicatorListener`, which 24 | how its able to detect all of this state information (and allows commands to 25 | be sent to the CNC controller). 26 | 27 | The controller provides a `ControllerListener` interface which is used to 28 | provide real time status. 29 | 30 | Finally, the **AbstractController** defines a number of abstract methods which 31 | can be used by device specific controllers as needed to hook into important 32 | lifecycle events: 33 | ```java 34 | abstract protected void closeCommBeforeEvent(); 35 | abstract protected void closeCommAfterEvent(); 36 | protected void openCommAfterEvent() throws Exception {} 37 | abstract protected void cancelSendBeforeEvent(); 38 | abstract protected void cancelSendAfterEvent(); 39 | abstract protected void pauseStreamingEvent() throws Exception; 40 | abstract protected void resumeStreamingEvent() throws Exception; 41 | abstract protected void isReadyToSendCommandsEvent() throws Exception; 42 | abstract protected void statusUpdatesEnabledValueChanged(boolean enabled); 43 | abstract protected void statusUpdatesRateValueChanged(int rate); 44 | 45 | // This one is special, because it is responsible for parsing device 46 | // responses, such as a command complete, status string, or parsing a 47 | // status event. In the case of a command complete, it must call 48 | // `commandComplete` to push the stream lifecycle along. 49 | abstract protected void rawResponseHandler(String response); 50 | ``` 51 | 52 | Here is the public interface which controlles conform to: 53 | ```java 54 | public interface IController { 55 | /* 56 | Observable 57 | */ 58 | public void addListener(ControllerListener cl); 59 | 60 | /* 61 | Actions 62 | */ 63 | public void performHomingCycle() throws Exception; 64 | public void returnToHome() throws Exception; 65 | public void resetCoordinatesToZero() throws Exception; 66 | public void resetCoordinateToZero(final char coord) throws Exception; 67 | public void killAlarmLock() throws Exception; 68 | public void toggleCheckMode() throws Exception; 69 | public void viewParserState() throws Exception; 70 | public void issueSoftReset() throws Exception; 71 | 72 | /* 73 | Behavior 74 | */ 75 | public void setSingleStepMode(boolean enabled); 76 | public boolean getSingleStepMode(); 77 | 78 | public void setStatusUpdatesEnabled(boolean enabled); 79 | public boolean getStatusUpdatesEnabled(); 80 | 81 | public void setStatusUpdateRate(int rate); 82 | public int getStatusUpdateRate(); 83 | 84 | public GcodeCommandCreator getCommandCreator(); 85 | public long getJobLengthEstimate(File gcodeFile); 86 | 87 | /* 88 | Serial 89 | */ 90 | public Boolean openCommPort(String port, int portRate) throws Exception; 91 | public Boolean closeCommPort() throws Exception; 92 | public Boolean isCommOpen(); 93 | 94 | /* 95 | Stream information 96 | */ 97 | public Boolean isReadyToStreamFile() throws Exception; 98 | public Boolean isStreamingFile(); 99 | public long getSendDuration(); 100 | public int rowsInSend(); 101 | public int rowsSent(); 102 | public int rowsRemaining(); 103 | 104 | /* 105 | Stream control 106 | */ 107 | public void beginStreaming() throws Exception; 108 | public void pauseStreaming() throws Exception; 109 | public void resumeStreaming() throws Exception; 110 | public void cancelSend(); 111 | 112 | /* 113 | Stream content 114 | */ 115 | public GcodeCommand createCommand(String gcode) throws Exception; 116 | public void sendCommandImmediately(GcodeCommand cmd) throws Exception; 117 | public void queueCommand(GcodeCommand cmd) throws Exception; 118 | public void queueStream(GcodeStreamReader r); 119 | public void queueRawStream(Reader r); 120 | } 121 | ``` 122 | 123 | 124 | ## Communicator 125 | 126 | A communicator handles all levels of sending data to the device. Raw responses 127 | are returned to any listeners via the `SerialCommunicatorListener`. 128 | 129 | The `AbstractCommunicator` implements several listener utilities which are used 130 | by implementing classes. 131 | 132 | The `BufferedCommunicator` abstract class handles the process of buffering 133 | multiple commands at once in order to keep a constant stream of commands 134 | available to the CNC device. It does this in the **streamCommands** method by 135 | maintaining a list of active commands, and the current size of those commands. A 136 | method named `processedCommand` must be implemented in a subclass to determine 137 | whether a raw response indicates a command has completed. This notifies the 138 | **BufferedCommunicator** that it should attempt to send more commands. 139 | 140 | `GrblCommunicator` and `TinyGCommunicator` are two concrete implementations of 141 | the **BufferedCommunicator**. 142 | 143 | ## Connection 144 | 145 | This is a very thin layer which provides a way to write and receive data: 146 | ```java 147 | abstract public boolean openPort(String name, int baud) throws Exception; 148 | abstract public void closePort() throws Exception; 149 | abstract public boolean isOpen(); 150 | abstract public void sendByteImmediately(byte b) throws Exception; 151 | abstract public void sendStringToComm(String command) throws Exception; 152 | ``` 153 | 154 | # Streaming strategy 155 | 156 | UGS attempts to use a fixed amount of memory when streaming a file. In this way 157 | it can send gcode files of any size. Files are preprocessed at the `BackendAPI` 158 | level using the `GcodeStreamWriter` class. This will serialize all the required 159 | metadata into a file. Later on that file can be opened with the 160 | `GcodeStreamReader` class, the **Controller** and **Communicator** classes use 161 | this. Using the reader, the **Communicator** class can pull out commands one at 162 | a time and send them to the **Connection**. 163 | -------------------------------------------------------------------------------- /docs/dev/gcode_processor.md: -------------------------------------------------------------------------------- 1 | # Gcode Processor Development 2 | 3 | The UGS core library has a flexible gcode processor plugin system. It is designed 4 | as a processing pipeline to convert one line of code at a time by passing it 5 | through multiple **Command Processor** plugins. Some advanced features in UGS, 6 | like the Auto Leveler, take advantage of this feature to inject a special 7 | processor module into the gcode processing pipeline. Other processors are simpler, 8 | such as the **M30Processor** which simply removes unwanted **M30** commands, or the 9 | **CommandLengthProcessor** which causes an error if the final processed line has 10 | too much data for your controller. 11 | 12 | This process is configured using a JSON file which holds processor configuration, 13 | order in which processors should appear in the pipeline, and whether or not they 14 | are enabled. All of this is configurable in UGS and UGP in a **Gcode Processor 15 | Configuration** menu. 16 | 17 | ## Anatomy of a CommandProcessor 18 | 19 | The processor interface is simple. One command goes in along with the current state 20 | and a list of output commands come out. A CommandProcessor might discover invalid 21 | input, in which case a GcodeParserException can be thrown for the GcodeParser to 22 | handle. 23 | 24 | ```java 25 | public interface CommandProcessor { 26 | /** 27 | * Given a command and the current state of a program returns a replacement 28 | * list of commands. 29 | * @param command Input gcode. 30 | * @param state State of the gcode parser when the command will run. 31 | * @return One or more gcode commands to replace the original command with. 32 | */ 33 | public List processCommand(String command, GcodeState state) throws GcodeParserException; 34 | 35 | /** 36 | * Returns information about the current command and its configuration. 37 | * @return 38 | */ 39 | public String getHelp(); 40 | } 41 | ``` 42 | 43 | ## Simple Example 44 | 45 | The **CommandLengthProcessor** is one of the simplest examples of a 46 | CommandProcessor. One thing of interest is that it accepts a length parameter 47 | during configuration. By adding a CommandLengthProcessor to the GcodeParser, 48 | you can ensure the maximum length of commands. 49 | 50 | ```java 51 | public class CommandLengthProcessor implements CommandProcessor { 52 | final private int length; 53 | public CommandLengthProcessor(int length) { 54 | this.length = length; 55 | } 56 | 57 | @Override 58 | public String getHelp() { 59 | // Global localization helpers are used for the help message. 60 | return Localization.getString("sender.help.command.length") + "\n" + 61 | Localization.getString("sender.command.length") 62 | + ": " + length; 63 | } 64 | 65 | @Override 66 | public List processCommand(String command, GcodeState state) throws GcodeParserException { 67 | if (command.length() > length) 68 | throw new GcodeParserException("Command '" + command + "' is longer than " + length + " characters."); 69 | 70 | return Collections.singletonList(command); 71 | } 72 | } 73 | ``` 74 | 75 | ## More Complex Examples 76 | The following examples can be found in GitHub, they wont have the full code 77 | included. 78 | 79 | ### DecimalProcessor 80 | This is only slightly more complicated than the CommandLengthProcessor. It adds 81 | in some config validation by throwing a RuntimeException in the constructor, 82 | which is handled by code which configures the GcodeParser. The **processCommand** 83 | method is then able to truncate any decimals using simple string manipulation. 84 | 85 | ### FeedOverrideProcessor 86 | Like the **DecimalProcessor**, the **FeedOverrideProcessor** is able to modifie any 87 | **F-Commands** with simple string manipulation. 88 | 89 | ### LineSplitter 90 | The **LineSplitter** CommandProcessor will actually modify commands by parsing 91 | the gcode and rewriting it. There are several utilities used here to help: 92 | 93 | * **GcodeParser.processCommand** - Converts the command string into something 94 | easier to work with. 95 | * **GcodePreprocessorUtils.extractMotion** - Helper to extract all words associated 96 | to movement commands, the remainder should still be sent but may not be sent in 97 | the context of the rewritten command. 98 | 99 | The remaining logic checks the length of any `G0` or `G1` commands and converts 100 | them, or returns the original command unmodified. 101 | 102 | ## Tutorial: Creating a New CommandProcessor 103 | 104 | Creating a fully integrated and configurable **CommandProcessor** is simple, but 105 | does touch a number of different files. This tutorial will go over those pieces 106 | in the context of creating a processor named **M3Dweller**. 107 | 108 | ### Goal 109 | Create a processor which inserts a **Dwell** command (short delay) whenever the 110 | spindle is enabled. This allows any potentially slow setups such as VFD's to 111 | come up to speed. It should be configurable via the **Gcode Processor 112 | Configuration** menu. 113 | 114 | ### Creating the processor 115 | First you'll notice that the constructor initializes the command which should 116 | be added after our spindle start **M3** command. The locale is set to make sure 117 | comma is not used as a decimal separator: 118 | ```java 119 | private final String dwellCommand; 120 | 121 | public M3Dweller(double dwellDuration) { 122 | this.dwellCommand = String.format(Locale.ROOT, "G4P%.2f", dwellDuration); 123 | } 124 | ``` 125 | 126 | The help method simply adds some comments for the settings GUI: 127 | ```java 128 | @Override 129 | public String getHelp() { 130 | return "Add a delay after enabling the spindle with \"M3\" commands. \"M3\" must be the only command on the line."; 131 | } 132 | ``` 133 | 134 | Everything is then pulled together in the **processCommand** method to return an 135 | extra dwell command when **M3** is detected: 136 | ```java 137 | // Contains an M3 not followed by another digit (i.e. M30) 138 | Pattern m3Pattern = Pattern.compile(".*[mM]3(?!\\d)(\\D.*)?"); 139 | 140 | @Override 141 | public List processCommand(String command, GcodeState state) throws GcodeParserException { 142 | if (m3Pattern.matcher(command).matches()) { 143 | return Arrays.asList(command, dwellCommand); 144 | } 145 | return Collections.singletonList(command); 146 | } 147 | ``` 148 | 149 | All at once now: 150 | ```java 151 | public class M3Dweller implements CommandProcessor { 152 | private final String dwellCommand; 153 | 154 | // Contains an M3 not followed by another digit (i.e. M30) 155 | Pattern m3Pattern = Pattern.compile(".*[mM]3(?!\\d)(\\D.*)?"); 156 | 157 | public M3Dweller(double dwellDuration) { 158 | this.dwellCommand = String.format(Locale.ROOT, "G4P%.2f", dwellDuration); 159 | } 160 | 161 | @Override 162 | public List processCommand(String command, GcodeState state) throws GcodeParserException { 163 | String noComments = GcodePreprocessorUtils.removeComment(command); 164 | if (m3Pattern.matcher(noComments).matches()) { 165 | return Arrays.asList(command, dwellCommand); 166 | } 167 | return Collections.singletonList(command); 168 | } 169 | 170 | @Override 171 | public String getHelp() { 172 | return "Add a delay after enabling the spindle with \"M3\" commands. \"M3\" must be the only command on the line."; 173 | } 174 | } 175 | ``` 176 | 177 | ### Hooking up the JSON 178 | 179 | The json files are stored in `ugs-core/src/resources/firmware_config`, each of 180 | these will be updated to include our new processor. Notice that we have an 181 | argument named duration, and that it is disabled by default: 182 | ```json 183 | "name": "M3Dweller", 184 | "enabled": false, 185 | "optional": true, 186 | "args": { 187 | "duration": 2.5 188 | } 189 | },{ 190 | ``` 191 | 192 | We also increment the version string, which will prompt users that there is a 193 | new configuration file available and they may need to revisit their settings: 194 | ```diff 195 | - "Version": 3, 196 | + "Version": 4, 197 | ``` 198 | 199 | ### JSON Loader 200 | The last step is to update the **CommandProcessorLoader** to include logic that 201 | creates the new M3Dweller when needed. I used one of the other processors as an 202 | example and made sure to make a corresponding entry for the **M3Dweller** in each 203 | of the corresponding code and comment locations: 204 | ```java 205 | case "M3Dweller": 206 | double duration = pc.args.get("duration").getAsDouble(); 207 | p = new M3Dweller(duration); 208 | break; 209 | ``` 210 | 211 | There is also a test which should be updated to make sure everything is working: 212 | ```diff 213 | + args = new JsonObject(); 214 | + args.addProperty("duration", 2.5); 215 | + object = new JsonObject(); 216 | + object.addProperty("name", "M3Dweller"); 217 | + object.add("args", args); 218 | + array.add(object); 219 | + 220 | String jsonConfig = array.toString(); 221 | List processors = CommandProcessorLoader.initializeWithProcessors(jsonConfig); 222 | 223 | - assertEquals(8, processors.size()); 224 | + assertEquals(9, processors.size()); 225 | assertEquals(ArcExpander.class, processors.get(0).getClass()); 226 | assertEquals(CommentProcessor.class, processors.get(1).getClass()); 227 | assertEquals(DecimalProcessor.class, processors.get(2).getClass()); 228 | assertEquals(FeedOverrideProcessor.class, processors.get(3).getClass()); 229 | assertEquals(M30Processor.class, processors.get(4).getClass()); 230 | assertEquals(PatternRemover.class, processors.get(5).getClass()); 231 | assertEquals(CommandLengthProcessor.class, processors.get(6).getClass()); 232 | assertEquals(WhitespaceProcessor.class, processors.get(7).getClass()); 233 | + assertEquals(M3Dweller.class, processors.get(8).getClass()); 234 | ``` 235 | 236 | ### Testing 237 | The command processors lend themselves to thorough testing, so we create a new 238 | `M3DwellerTest.java` file in the associated test package and write some tests: 239 | ```java 240 | @Test 241 | public void testReplaces() throws Exception { 242 | M3Dweller dweller = new M3Dweller(2.5); 243 | String command; 244 | 245 | command = "M3"; 246 | Assertions.assertThat(dweller.processCommand(command, null)).containsExactly(command,"G4P2.50"); 247 | 248 | command = "m3"; 249 | Assertions.assertThat(dweller.processCommand(command, null)).containsExactly(command,"G4P2.50"); 250 | 251 | command = "M3 S1000"; 252 | Assertions.assertThat(dweller.processCommand(command, null)).containsExactly(command,"G4P2.50"); 253 | 254 | command = "m3 S1000"; 255 | Assertions.assertThat(dweller.processCommand(command, null)).containsExactly(command,"G4P2.50"); 256 | 257 | command = "(this is ignored) M3 S1000"; 258 | Assertions.assertThat(dweller.processCommand(command, null)).containsExactly(command,"G4P2.50"); 259 | } 260 | 261 | @Test 262 | public void testNoOp() throws Exception { 263 | M3Dweller dweller = new M3Dweller(2.5); 264 | String command; 265 | command = "anything else"; 266 | Assertions.assertThat(dweller.processCommand(command, null)).containsExactly(command); 267 | 268 | command = "M30"; 269 | Assertions.assertThat(dweller.processCommand(command, null)).containsExactly(command); 270 | 271 | command = "G0 X0 Y0 (definitely not ready to start the spindle with an M3 yet)"; 272 | Assertions.assertThat(dweller.processCommand(command, null)).containsExactly(command); 273 | } 274 | ``` 275 | 276 | ### Localizing 277 | 278 | In addition to the help message which may use the global **Localization** helper, 279 | the settings menu will attempt to find something with the same name as in the 280 | JSON file. So we add an entry to 281 | `./ugs-core/src/resources/MessagesBundle_en_US.properties`, which will later be 282 | pulled into a separate localization service to be localized in other languages. 283 | ```diff 284 | WhitespaceProcessor = Whitespace Remover 285 | +M3Dweller = Spindle start delay 286 | controller.exception.smoothie.booting = Smoothie has not finished booting. 287 | ``` 288 | 289 | ### Conclusion 290 | 291 | We have now fully integrated our **M3Dweller** processor into the UGS framework 292 | and users of UGS and UGP can both make use of it. 293 | 294 | The raw commit for this feature is found on [github here](https://github.com/winder/Universal-G-Code-Sender/commit/edc9f08e6eb908706f4985bdbcbbb6ffa831b72a). 295 |
296 |
297 |
298 | -------------------------------------------------------------------------------- /docs/dev/plugin.md: -------------------------------------------------------------------------------- 1 | # Note: The UGS Platform has been updated to use maven. Some parts of this document need to be updated to reflect that change. 2 | 3 |
4 |
5 | 6 | # Plugin development 7 | 8 | The UGS Platform is built ontop of the NetBeans Platform. This gives us powerful 9 | tools to work with, including a robust plugin system. The heart of the UGS 10 | Platform is a module which wraps and exposes the Universal Gcode Sender JAR 11 | file - the same jar you could execute to run the Classic GUI! Other than using 12 | the UGSLib module, developing a plugin for the UGS Platform is exactly the same 13 | as developing any other NetBeans Platform plugin. And there is lots of great 14 | documentation for that, here is the [NetBeans Platform Plugin Quick Start](https://platform.netbeans.org/tutorials/nbm-google.html) 15 | guide. 16 | 17 | # Workflow Plugin Tutorial 18 | 19 | In this tutorial we're going to build a window to help manage jobs that use 20 | multiple tools which are split into multiple files. The rough design idea will 21 | have a central table with four columns containing: 22 | * File name 23 | * Tool name (editable) 24 | * Finished flag 25 | 26 | There will be a pair of buttons to add and remove files from the table, and 27 | we will also hook up a UGS event listener to detect when files are opened from 28 | other areas of the interface as well. 29 | 30 | Lastly, we'll add another pair of buttons to move rows around in the table, so 31 | that we can reorganize the workflow if files were added out of order the order. 32 | 33 | Here is a sketch of what we're building: 34 |
35 | 36 | 37 | ## Create and configure project 38 | Universal Gcode Sender is developed with NetBeans, and plugins are no exception. 39 | Once you've cloned the Universal Gcode Sender project you should be able to open 40 | the UGSPlatform folder with NetBeans and it will discover a project that you can open. 41 | To start building your module expand the UGSPlatform section, right-click the 42 | modules directory and select `Add New...`. 43 | 44 |
45 | 46 | This will open up a wizard where you name the module, and declare the source 47 | path. For this example the module is named WorkflowModuleTutorial and the source 48 | path is com.willwinder.ugs.nbp.workflowmoduletutorial which is the convention 49 | used in the core modules. 50 | 51 |
52 | 53 | ## Add UGS dependencies 54 | 55 | Your module should now be listed in the Modules section. If it doesn't you may 56 | need to restart NetBeans. Before we dive into the code there are a couple helper 57 | classes to import which will give you full access to the UGS API. Double click 58 | your module from the Modules section to open the code, then right-click the top 59 | level item which appeared and select the properties menu. 60 | 61 | Select `Add Dependency...`, here you should search for `UGSLib` and 62 | `CentralLookup` then add them to your plugin. 63 | 64 |
65 | 66 | ## Create window class 67 | 68 | Now we're ready to build the module. In this tutorial we're building a window 69 | to manage a multi-job workflow, so we'll start by adding a window to customize. 70 | 71 | 1. Open the new module and right click the new package, in the context menu go to 72 | `New` -> `Window...`. To bring up the new window wizard. 73 | 2. In the first screen of the wizard choose the default location your window 74 | will appear. Custom locations have been designed for UGS Platform, the largest 75 | is named `visualizer` because it is the Visualizer's default location. We'll 76 | use this location for our plugin. This means that when our plugin opens it will 77 | be tabbed with the Visualizer module. 78 | 3. Click next and choose a class name for your module, for this tutorial I'm 79 | going to call it `WorkflowModuleTutorial`. 80 | 81 |
82 | 83 | ## Build the GUI 84 | 85 | The NetBeans GUI builder makes it easy to make a custom user interface without 86 | writing a single line of code (which is the main reason UGS uses NetBeans!). 87 | Using the GUI builder we'll add some buttons and a table. This step can be as 88 | elaborate as you want. If you're a seasoned swing developer and prefer not to 89 | use the magic GUI builder, no worries, you can create the UI programatically as 90 | well - but that is a different tutorial. 91 | 92 | * Take a look at the screenshot below. The `[TopComponent] - Navigator - Editor` 93 | window shows all the objects that have been added with the GUI builder. 94 | * There are four JButtons, a JTable nested inside a JScrollPane and a JPanel 95 | which I used to make alignment a little easier (The GUI Builder is powerful, 96 | but it can also be a bit quirky). 97 | * Putting the JTable inside a JScrollPane makes it so that if too many items 98 | get added to the table it will scroll rather than dissapear off the bottom. 99 | * **Note:** The name given to these components will be used in the code, so 100 | be sure to use the names shown in the screenshot. 101 | 102 |
103 | 104 | The JTable is going to be the trickiest part of build the GUI. To configure the 105 | table right-click the JTable object from the component navigator and select 106 | `Table Contents...`. Here you can add our 3 columns and specify that the data 107 | types. You can also specify which columns are editable, in this example we want 108 | the user to be able to type in what type of tool should be used. 109 | 110 |
111 | 112 | ## Autogenerated code 113 | 114 | Before writing any code, lets take a look at what has already been automatically 115 | generated for us. 116 | 117 | 1. Just above the class there are a number of annotations. These are used by 118 | the NetBeans platform, most of them were setup according to how you filled 119 | in the Wizards earlier. They can also configure things like keyboard 120 | shortcuts, and where things are put in the dropdown menus. 121 | 122 | 2. Within the class there are several grayed out sections. This is code 123 | generated by NetBeans which the IDE prevents you from modifying outside the 124 | GUI builder or in some cases component properties. For example if you wanted 125 | to use a custom JTable, you would configure the table in the GUI builder by 126 | adding a custom constructor. 127 | 128 | 3. At the end of the file is `componentOpened` and `componentClosed`, these are 129 | lifecycle events that are called when the window has been opened or closed. 130 | 131 | 4. Also at the end of the file is `writeProperties` and `readProperties`, these 132 | are used to save the window state between runs. 133 | 134 | ## Annotated code 135 | 136 | This is the longest section because it will explain every line of code added to 137 | the `WorkflowModuleTutorial` class. The most complicated code deals with Swing 138 | component manipulation, with just a smattering of UGS lifecycle events to push 139 | things along. 140 | 141 | ### Class signature 142 | 143 | * First there are a few class state object we'll need and two Listeners we'll 144 | be implementing. 145 | ```java 146 | /** 147 | * UGSEventListener - this is how a plugin can listen to UGS lifecycle events. 148 | * ListSelectionListener - listen for table selections. 149 | */ 150 | public final class WorkflowWindowTutorialTopComponent 151 | extends TopComponent 152 | implements UGSEventListener, ListSelectionListener { 153 | 154 | // These are the UGS backend objects for interacting with the backend. 155 | private final Settings settings; 156 | private final BackendAPI backend; 157 | 158 | // This is used to identify when a stream has completed. 159 | private boolean wasSending; 160 | 161 | // This is used in most methods, so cache it here. 162 | DefaultTableModel model; 163 | ``` 164 | 165 | ### Constructor 166 | 167 | * In the constructor we register the class with the UGS backend and also set 168 | the class as a listener to table selection events. 169 | ``` java 170 | public WorkflowWindowTopComponent() { 171 | initComponents(); 172 | setName(Bundle.CTL_WorkflowWindowTopComponent()); 173 | setToolTipText(Bundle.HINT_WorkflowWindowTopComponent()); 174 | 175 | // This is how to access the UGS backend and register the listener. 176 | // CentralLookup is used to get singleton instances of the UGS 177 | // Settings and BackendAPI objects. 178 | settings = CentralLookup.getDefault().lookup(Settings.class); 179 | backend = CentralLookup.getDefault().lookup(BackendAPI.class); 180 | backend.addUGSEventListener(this); 181 | 182 | // Allow contiguous ranges of selections and register a listener. 183 | this.fileTable.setSelectionMode( 184 | ListSelectionModel.SINGLE_INTERVAL_SELECTION); 185 | ListSelectionModel cellSelectionModel = 186 | this.fileTable.getSelectionModel(); 187 | cellSelectionModel.addListSelectionListener(this); 188 | 189 | // Cache the model object. 190 | model = (DefaultTableModel)this.fileTable.getModel(); 191 | } 192 | ``` 193 | 194 | ### UGS Event Listener 195 | 196 | * This is the event sent from the UGS Backend, when a file is loaded or the 197 | state changes a notification will be sent. 198 | * If the state switches from `COMM_SENDING` to `COMM_IDLE` we'll run a 199 | `completeFile` method. 200 | * If a file is loaded, we add it to the table. 201 | ```java 202 | @Override 203 | public void UGSEvent(UGSEvent cse) { 204 | if (cse.isStateChangeEvent()) { 205 | if (wasSending && cse.getControlState() == ControlState.COMM_IDLE) 206 | this.completeFile(backend.getGcodeFile()); 207 | wasSending = backend.isSending(); 208 | } 209 | if (cse.isFileChangeEvent()) { 210 | this.addFileToWorkflow(backend.getGcodeFile()); 211 | } 212 | 213 | } 214 | ``` 215 | 216 | ### File Complete Handler 217 | 218 | * When a command is complete we'll update the JTable, select the next file that 219 | needs to be sent and popup a notification informing the user what they should 220 | do next. The selection event will be sent and handled in the selection 221 | handler. 222 | ```java 223 | public void completeFile(File gcodeFile) { 224 | if (gcodeFile == null) return; 225 | 226 | // Make sure the file is loaded in the table. 227 | int fileIndex = findFileIndex(gcodeFile); 228 | if (fileIndex < 0) return; 229 | 230 | // Mark that it has been completed. 231 | model.setValueAt(true, fileIndex, 2); 232 | 233 | fileIndex++; 234 | String message; 235 | 236 | // Make sure there is another command left. 237 | if (fileIndex < fileTable.getRowCount()) { 238 | String nextTool = (String) model.getValueAt(fileIndex, 1); 239 | String messageTemplate = 240 | "Finished sending '%s'.\n" 241 | + "The next file uses tool '%s'\n" 242 | + "Load tool and move machine to its zero location\n" 243 | + "and click send to continue this workflow."; 244 | message = String.format( 245 | messageTemplate, gcodeFile.getName(), nextTool); 246 | 247 | // Select the next row, this will trigger a selection event. 248 | fileTable.setRowSelectionInterval(fileIndex, fileIndex); 249 | 250 | // Use a different message if we're finished. 251 | } else { 252 | message = "Finished sending the last file!"; 253 | } 254 | 255 | // Display a notification. 256 | java.awt.EventQueue.invokeLater(() -> { 257 | JOptionPane.showMessageDialog(new JFrame(), message, 258 | "Workflow Event", JOptionPane.PLAIN_MESSAGE); 259 | }); 260 | } 261 | ``` 262 | 263 | ### JTable Selection Listener 264 | 265 | * This is the selection listener, when a file is selected load it in the backend. 266 | ```java 267 | @Override 268 | public void valueChanged(ListSelectionEvent e) { 269 | int[] selectedRow = fileTable.getSelectedRows(); 270 | // Only load files when there is a single selection. 271 | if (selectedRow.length == 1) { 272 | // Pull the file out of the table and set it in the backend. 273 | String file = (String) model.getValueAt(selectedRow[0], 0); 274 | try { 275 | backend.setGcodeFile(new File(file)); 276 | } catch (Exception ex) { 277 | Exceptions.printStackTrace(ex); 278 | } 279 | } 280 | } 281 | ``` 282 | 283 | ### JTable Helper 284 | 285 | * Helper method to add a file to the JTable, first making sure that it isn't 286 | already in the table. 287 | ```java 288 | public void addFileToWorkflow(File gcodeFile) { 289 | if (gcodeFile == null) { 290 | return; 291 | } 292 | 293 | int fileIndex = findFileIndex(gcodeFile); 294 | // Don't re-add a file. 295 | if (fileIndex >= 0) { 296 | return; 297 | } 298 | 299 | model.addRow(new Object[]{ 300 | gcodeFile.getAbsolutePath(), 301 | "default", 302 | false 303 | }); 304 | 305 | // Fire off the selection event to load the file. 306 | int lastRow = fileTable.getRowCount() - 1; 307 | fileTable.setRowSelectionInterval(lastRow, lastRow); 308 | } 309 | ``` 310 | 311 | ### Add/Remove Button Action Handlers 312 | 313 | * Now we implement the button event methods. They are generated by double 314 | clicking the buttons in the GUI Builder. This generates the swing code that 315 | attaches the `ActionPerformed` events to the button click callbacks. 316 | * `addButtonActionPerformed` simply displays a file chooser (using some UGS 317 | library built ins) and calls the `addFileToWorkflow` method defined earlier. 318 | * `removeButtonActoinPerformed` is even simpler, it uses standard JTable 319 | functionality to remove any selected rows. The only thing clever here is that 320 | rows are removed starting from the end to avoid having the index of later 321 | selections change while deleting rows one at a time. 322 | ```java 323 | private void addButtonActionPerformed(ActionEvent evt) { 324 | // Open a file chooser pointing at the last opened directory. 325 | JFileChooser fileChooser = GcodeFileTypeFilter.getGcodeFileChooser( 326 | settings.getLastOpenedFilename()); 327 | 328 | int returnVal = fileChooser.showOpenDialog(this); 329 | if (returnVal == JFileChooser.APPROVE_OPTION) { 330 | File gcodeFile = fileChooser.getSelectedFile(); 331 | 332 | // Save the new directory! 333 | settings.setLastOpenedFilename(gcodeFile.getParent()); 334 | 335 | addFileToWorkflow(gcodeFile); 336 | } 337 | } 338 | 339 | private void removeButtonActionPerformed(ActionEvent evt) { 340 | int[] selectedRows = fileTable.getSelectedRows(); 341 | if (selectedRows.length == 0) return; 342 | 343 | Arrays.sort(selectedRows); 344 | for (int i = selectedRows.length - 1; i >= 0; i--) { 345 | int row = selectedRows[i] 346 | this.model.removeRow(row); 347 | this.model.fireTableRowsDeleted(row, row); 348 | } 349 | } 350 | ``` 351 | 352 | ### Up / Down Button Action Handlers 353 | 354 | * The up and down action buttons are pure java code. They don't do anything 355 | you wouldn't do with any other Swing application. The code here deals strictly 356 | with moving selections around. Although a little tricky, and not totally 357 | relevant to UGS, they are included because the feature wouldn't be complete 358 | without them. 359 | ```java 360 | private void upButtonActionPerformed(ActionEvent evt) { 361 | int[] selectedRows = fileTable.getSelectedRows(); 362 | 363 | // Exit early if nothing is selected. 364 | if (selectedRows.length == 0) return; 365 | 366 | Arrays.sort(selectedRows); 367 | 368 | // Exit early if the selected range can't move. 369 | if (selectedRows[0] == 0) return; 370 | 371 | for (int i = 0; i < selectedRows.length; i++) { 372 | selectedRows[i] = this.moveRow(selectedRows[i], -1); 373 | } 374 | 375 | int first = selectedRows[0]; 376 | int last = selectedRows[selectedRows.length-1]; 377 | fileTable.setRowSelectionInterval(first, last); 378 | } 379 | 380 | private void downButtonActionPerformed(ActionEvent evt) { 381 | int[] selectedRows = fileTable.getSelectedRows(); 382 | 383 | // Exit early if nothing is selected. 384 | if (selectedRows.length == 0) return; 385 | 386 | Arrays.sort(selectedRows); 387 | 388 | // Exit early if the selected range can't move. 389 | if (selectedRows[selectedRows.length-1] 390 | == fileTable.getRowCount()) return; 391 | 392 | for (int i = selectedRows.length - 1; i >= 0; i--) { 393 | selectedRows[i] = this.moveRow(selectedRows[i], 1); 394 | } 395 | 396 | int first = selectedRows[0]; 397 | int last = selectedRows[selectedRows.length-1]; 398 | fileTable.setRowSelectionInterval(first, last); 399 | } 400 | ``` 401 | 402 | ### NetBeans Platform Component Lifecycle Code 403 | 404 | * Of the automatically generated methods, `componentOpened` is the only one 405 | which needed some custom code. In case the component had been closed earlier 406 | or wasn't loaded until after a file stream started, grab the `wasSending` 407 | state and save it for later. 408 | ```java 409 | @Override 410 | public void componentOpened() { 411 | this.wasSending = backend.isSending(); 412 | } 413 | 414 | @Override 415 | public void componentClosed() { 416 | // No special close handling. 417 | } 418 | 419 | void writeProperties(java.util.Properties p) { 420 | // better to version settings since initial version as advocated at 421 | // http://wiki.apidesign.org/wiki/PropertyFiles 422 | p.setProperty("version", "1.0"); 423 | 424 | // We could save the loaded files here 425 | } 426 | 427 | void readProperties(java.util.Properties p) { 428 | String version = p.getProperty("version"); 429 | 430 | // We could load previously loaded files here 431 | } 432 | ``` 433 | 434 | ### Helper Methods 435 | 436 | * Finally, here are the helper methods used above. 437 | ```java 438 | /** 439 | * Look for the provided file in the file table. 440 | */ 441 | private int findFileIndex(File gcodeFile) { 442 | if (gcodeFile == null) return -1; 443 | 444 | for (int i = 0; i < model.getRowCount(); i++) { 445 | String file = (String) model.getValueAt(i, 0); 446 | if (file != null && gcodeFile.getAbsolutePath().equals(file)) { 447 | return i; 448 | } 449 | } 450 | 451 | return -1; 452 | } 453 | 454 | /** 455 | * Move a given row by some offset. If the offset would move the row outside 456 | * of the current table size, the row is not moved. 457 | */ 458 | private int moveRow(int row, int offset) { 459 | int dest = row + offset; 460 | if (dest < 0 || dest >= model.getRowCount()) { 461 | return row; 462 | } 463 | 464 | model.moveRow(row, row, dest); 465 | return dest; 466 | } 467 | ``` 468 | 469 | ## Conclusion 470 | 471 | Here is a quick screencast of what this plugin does for us. In the GUI builder I swapped in some up/down arrows compared to the tutorial. 472 | 473 |
474 | 475 | -------------------------------------------------------------------------------- /docs/img/guide/platform/connect_serial_port.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 33 | 38 | 39 | 47 | 52 | 53 | 54 | 73 | 75 | 76 | 78 | image/svg+xml 79 | 81 | 82 | 83 | 84 | 85 | 90 | 451 | 457 | 458 | 459 | -------------------------------------------------------------------------------- /docs/img/guide/platform/controller_state.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 33 | 38 | 39 | 47 | 52 | 53 | 61 | 66 | 67 | 75 | 80 | 81 | 89 | 94 | 95 | 103 | 108 | 109 | 118 | 123 | 124 | 132 | 138 | 139 | 147 | 153 | 154 | 162 | 168 | 169 | 177 | 183 | 184 | 185 | 203 | 205 | 206 | 208 | image/svg+xml 209 | 211 | 212 | 213 | 214 | 215 | 220 | 228 | Axis reset buttons 240 | 251 | Work coordinates 263 | Machine coordinates 275 | Feed rate and Spindle speed 292 | GCode states 304 | Machine state 316 | Limit switches state 328 | 334 | 340 | 346 | 352 | 358 | 364 | 370 | 371 | 372 | -------------------------------------------------------------------------------- /docs/img/ugs.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | 23 | 36 | 49 | 67 | 85 | 97 | 111 | 121 | 145 | 169 | 172 | 176 | 180 | 181 | 184 | 188 | 192 | 193 | 196 | 200 | 204 | 205 | 208 | 212 | 216 | 217 | 220 | 224 | 228 | 229 | 232 | 236 | 240 | 241 | 244 | 248 | 253 | 258 | 263 | 264 | 265 | 274 | 283 | 285 | 289 | 293 | 294 | 296 | 300 | 304 | 305 | 307 | 311 | 315 | 319 | 320 | 322 | 326 | 330 | 331 | 342 | 353 | 364 | 375 | 386 | 397 | 400 | 404 | 409 | 414 | 419 | 420 | 421 | 430 | 439 | 449 | 459 | 469 | 479 | 490 | 500 | 503 | 507 | 512 | 517 | 522 | 523 | 524 | 525 | 548 | 550 | 551 | 553 | image/svg+xml 554 | 556 | 557 | 558 | 559 | 564 | 569 | 574 | 579 | 584 | 590 | 594 | 598 | 602 | 606 | 610 | 614 | 618 | 622 | 626 | 630 | 634 | 638 | 642 | 646 | 650 | 654 | 658 | 662 | 666 | 670 | 674 | 675 | 676 | 681 | 686 | 691 | 696 | 701 | 702 | 940 | 974 | 975 | --------------------------------------------------------------------------------