Compare commits

...
Author SHA1 Message Date
Christian 376e4a4ea2 Merge pull request #1117 from qitechgmbh/1100-update-the-main-readme
1100 update the main readme
2026-02-05 21:39:54 +01:00
Robin Krämer 0b30ae9a85 removed excessive logging, simplified socketio task with actual error printed … (#1122)
* removed excessive logging, simplified socketio task with actual error message

* fixed formatting
2026-02-05 17:34:48 +01:00
Christian 528952238d Update issue templates 2026-02-05 14:54:10 +01:00
Robin Krämer 8c32ce2a2b loop now uses Different Cycle time when development-build is set (#1112)
* loop now uses Different Cycle time when development-build is set

* fixed formatting

* hid stop_dnsmasq import behind feature flag

* added feature gate for start_dnsmasq in main
2026-02-05 11:52:56 +01:00
Christian 579ef0d2e2 Enhance video section in README.md with descriptive captions for better user engagement 2026-02-04 19:39:34 +01:00
Christian b6263e90d6 Refactor video section in README.md for improved layout and clarity 2026-02-04 19:32:33 +01:00
Christian f67f098ff3 better wording 2026-02-04 19:30:21 +01:00
Christian 1a3c26b789 Update issue templates 2026-02-04 18:30:07 +01:00
Oshgnacknak 49f0c9f289 Merge pull request #1113 from qitechgmbh/add-issue-templates
Add issue templates
2026-02-04 15:40:33 +01:00
Oshgnacknak e60daed274 Merge pull request #1116 from qitechgmbh/1115-remove-gitattributes-and-gitmodules
1115 Remove .gitattributes and .gitmodules files
2026-02-04 15:35:35 +01:00
Oshgnacknak 9a9120398e Merge pull request #1097 from qitechgmbh/dependabot/cargo/serde_json-1.0.149
Bump serde_json from 1.0.145 to 1.0.149
2026-02-04 15:22:07 +01:00
Christian dec342498c Fix link to Device Examples in README.md for improved navigation 2026-02-04 15:21:06 +01:00
Christian bc8cc5dd3d Update README.md to enhance documentation structure and clarity 2026-02-04 15:18:19 +01:00
Oshgnacknak 4a1f3d125d Merge pull request #1096 from qitechgmbh/dependabot/cargo/syn-2.0.114
Bump syn from 2.0.111 to 2.0.114
2026-02-04 15:07:29 +01:00
dependabot[bot] 1d1eeb3d48 Bump serde_json from 1.0.145 to 1.0.149
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.145 to 1.0.149.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.145...v1.0.149)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-version: 1.0.149
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-04 14:06:09 +00:00
Oshgnacknak c395d4e172 Merge pull request #1094 from qitechgmbh/dependabot/cargo/euclid-0.22.13
Bump euclid from 0.22.11 to 0.22.13
2026-02-04 15:05:33 +01:00
Oshgnacknak 5009c5ca75 Merge pull request #1092 from qitechgmbh/dependabot/cargo/axum-0.8.8
Bump axum from 0.8.7 to 0.8.8
2026-02-04 15:03:41 +01:00
Oshgnacknak 50dbc46b8c Merge pull request #1089 from qitechgmbh/dependabot/cargo/tracing-subscriber-0.3.22
Bump tracing-subscriber from 0.3.20 to 0.3.22
2026-02-04 15:01:40 +01:00
Oshgnacknak 774e7e2f8b Merge pull request #1108 from qitechgmbh/dependabot/cargo/bytes-1.11.1
Bump bytes from 1.11.0 to 1.11.1
2026-02-04 14:59:55 +01:00
Oshgnacknak 270872fe81 Merge pull request #1114 from qitechgmbh/1081-automate-npm-deps-hash-for-electronnix
feat: automate npm deps hash update for Electron
2026-02-04 14:33:01 +01:00
Oshgnacknak a749f59186 Merge pull request #1110 from qitechgmbh/1109-remove-hooks
1109 remove hooks folder
2026-02-04 14:28:37 +01:00
Christian bae1de3c54 Remove .gitattributes and .gitmodules files 2026-02-04 13:52:33 +01:00
Christian 7681dae32c feat: automate npmDepsHash updates for Electron
- Add GitHub Actions workflow to automatically calculate and update npmDepsHash
- Workflow triggers on package-lock.json changes in PRs
- Uses nix prefetch-npm-deps to calculate correct hash
- Automatically commits hash updates to the PR
- Supports manual triggering via workflow_dispatch
2026-02-04 13:40:58 +01:00
ChristianandCopilot 2d98d369e1 Update .github/ISSUE_TEMPLATE/feature_request.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-04 13:04:29 +01:00
Christian 310ac42a88 Update issue templates
Add issue templates for better feature requests or bug reports.
2026-02-04 12:57:35 +01:00
Christian 182df9c569 Merge pull request #1107 from qitechgmbh/electron-deps
electron deps
2026-02-03 22:05:39 +01:00
Christian 153a719d64 remove Git hooks and pre-commit script 2026-02-03 21:56:06 +01:00
Christian 84603d1594 update hash 2026-02-03 21:53:28 +01:00
Christian 17b2e3e297 clear hash 2026-02-03 21:52:02 +01:00
Christian fb1cf33c7f Update package lock 2026-02-03 21:44:19 +01:00
dependabot[bot] ae2d0fa8b1 Bump bytes from 1.11.0 to 1.11.1
Bumps [bytes](https://github.com/tokio-rs/bytes) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/tokio-rs/bytes/releases)
- [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/bytes/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: bytes
  dependency-version: 1.11.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 19:43:19 +00:00
ᴏxᴏ.ᴍɪᴋᴀ ed06b63b77 Merge pull request #1083 from qitechgmbh/1076-minimum-hardware-documentation-links
1076 minimum hardware documentation links
2026-02-03 17:15:21 +01:00
Oshgnacknak 9f0f45d19c Fix hash 2026-02-03 16:26:07 +01:00
dependabot[bot] a75c0ff08a Bump zustand from 5.0.9 to 5.0.11 in /electron
Bumps [zustand](https://github.com/pmndrs/zustand) from 5.0.9 to 5.0.11.
- [Release notes](https://github.com/pmndrs/zustand/releases)
- [Commits](https://github.com/pmndrs/zustand/compare/v5.0.9...v5.0.11)

---
updated-dependencies:
- dependency-name: zustand
  dependency-version: 5.0.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 16:19:11 +01:00
dependabot[bot] 205432c926 Bump vitest from 3.2.4 to 4.0.18 in /electron
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 3.2.4 to 4.0.18.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.18/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-version: 4.0.18
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 16:18:11 +01:00
dependabot[bot] 781783464b Bump eslint from 9.39.1 to 9.39.2 in /electron
Bumps [eslint](https://github.com/eslint/eslint) from 9.39.1 to 9.39.2.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.1...v9.39.2)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.39.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 16:16:43 +01:00
dependabot[bot] 4fa2a1a221 Bump react-i18next from 15.7.4 to 16.5.4 in /electron
Bumps [react-i18next](https://github.com/i18next/react-i18next) from 15.7.4 to 16.5.4.
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v15.7.4...v16.5.4)

---
updated-dependencies:
- dependency-name: react-i18next
  dependency-version: 16.5.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 16:14:36 +01:00
Oshgnacknak b734468741 Fix event names in wago power machine 2026-02-03 16:14:02 +01:00
dependabot[bot] 1ed4ae3d2c Bump socket.io-client from 4.8.1 to 4.8.3 in /electron
Bumps [socket.io-client](https://github.com/socketio/socket.io) from 4.8.1 to 4.8.3.
- [Release notes](https://github.com/socketio/socket.io/releases)
- [Changelog](https://github.com/socketio/socket.io/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io/compare/socket.io-client@4.8.1...socket.io-client@4.8.3)

---
updated-dependencies:
- dependency-name: socket.io-client
  dependency-version: 4.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 15:57:02 +01:00
dependabot[bot] 8095222cad Bump syn from 2.0.111 to 2.0.114
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.111 to 2.0.114.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.111...2.0.114)

---
updated-dependencies:
- dependency-name: syn
  dependency-version: 2.0.114
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 12:37:25 +00:00
dependabot[bot] d19af0923c Bump axum from 0.8.7 to 0.8.8
Bumps [axum](https://github.com/tokio-rs/axum) from 0.8.7 to 0.8.8.
- [Release notes](https://github.com/tokio-rs/axum/releases)
- [Changelog](https://github.com/tokio-rs/axum/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/axum/compare/axum-v0.8.7...axum-v0.8.8)

---
updated-dependencies:
- dependency-name: axum
  dependency-version: 0.8.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 12:37:25 +00:00
Robin Krämer 08ce5a37bd aquapath now works with all the other machines connected (#1103)
* aquapath now works with all the other machines connected

* fixed formatting

* removed unused import

* fixed formatting AGAIN
2026-02-03 13:36:07 +01:00
Robin Krämer 78c7667d96 Merge pull request #1087 from qitechgmbh/fix-wago-init-logic
fixed Wago Initialization Logic
2026-02-02 11:02:44 +01:00
dependabot[bot] bef3389874 Bump euclid from 0.22.11 to 0.22.13
Bumps [euclid](https://github.com/servo/euclid) from 0.22.11 to 0.22.13.
- [Release notes](https://github.com/servo/euclid/releases)
- [Commits](https://github.com/servo/euclid/commits)

---
updated-dependencies:
- dependency-name: euclid
  dependency-version: 0.22.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-01 02:25:36 +00:00
dependabot[bot] 985937c227 Bump tracing-subscriber from 0.3.20 to 0.3.22
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.20 to 0.3.22.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.20...tracing-subscriber-0.3.22)

---
updated-dependencies:
- dependency-name: tracing-subscriber
  dependency-version: 0.3.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-01 02:25:04 +00:00
kraemr 22be4b79f0 fixed linter errors and added logic for 750_430 2026-01-30 16:33:50 +01:00
kraemr be84edf0fd removed unused parameter from ModulePDOMapping 2026-01-30 16:23:22 +01:00
kraemr ff3e787a03 fixed Wago Initialization Logic
fixed formatting
2026-01-30 16:16:10 +01:00
Christian 093663c4e6 Merge pull request #1086 from qitechgmbh/1085-change-heater-power-for-extruder-v3
Update heater power to the real values for extruder v3
2026-01-30 15:14:59 +01:00
Christian 1b2d6df459 Merge pull request #967 from qitechgmbh/Hamids-Branch
BECKHOFF minimal example motor
2026-01-30 13:24:54 +01:00
Christian cd2e15cb29 Update heater power to the real values for extruder v3 2026-01-30 13:23:46 +01:00
Christian f1571c0daa Merge pull request #1082 from qitechgmbh/revert-1077-1076-minimum-hardware-documentation-links
Revert "docs(readme): add documentation links and update machine table"
2026-01-29 14:50:33 +01:00
Christian 96e0fb83ef Revert "docs(readme): add documentation links and update machine table" 2026-01-29 14:50:10 +01:00
Christian 94cffbf717 Merge pull request #1077 from qitechgmbh/1076-minimum-hardware-documentation-links
docs(readme): add documentation links and update machine table
2026-01-29 13:22:14 +01:00
EinsPommes 38b2e907f9 docs(readme): update documentation links and improve machine table clarity 2026-01-28 17:16:10 +01:00
ᴏxᴏ.ᴍɪᴋᴀ 7e5498751a Merge branch 'master' into 1076-minimum-hardware-documentation-links 2026-01-28 17:13:52 +01:00
Christian eef9d63de2 Merge pull request #1074 from qitechgmbh/dependabot/npm_and_yarn/electron/multi-c5fe2c841a
Bump seroval and solid-js in /electron
2026-01-27 11:00:51 +01:00
Christian d9d9b9d344 update hash 2026-01-27 10:47:20 +01:00
Christian 2ff3d8df56 remove deps hash 2026-01-27 10:43:38 +01:00
Christian 462672bb6a Add peer dependency flag to multiple packages in package-lock.json 2026-01-27 10:38:58 +01:00
dependabot[bot] 84d539a4fa Bump seroval and solid-js in /electron
Bumps [seroval](https://github.com/lxsmnsyc/seroval) and [solid-js](https://github.com/solidjs/solid). These dependencies needed to be updated together.

Updates `seroval` from 1.3.2 to 1.5.0
- [Release notes](https://github.com/lxsmnsyc/seroval/releases)
- [Commits](https://github.com/lxsmnsyc/seroval/commits)

Updates `solid-js` from 1.9.10 to 1.9.11
- [Release notes](https://github.com/solidjs/solid/releases)
- [Changelog](https://github.com/solidjs/solid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/solidjs/solid/commits)

---
updated-dependencies:
- dependency-name: seroval
  dependency-version: 1.5.0
  dependency-type: indirect
- dependency-name: solid-js
  dependency-version: 1.9.11
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-27 10:38:58 +01:00
Robin Krämer 850b2d411d Merge pull request #1080 from qitechgmbh/changelog-2.14.1
updated Changelog
2026-01-26 16:37:03 +01:00
kraemr 008efe6ba1 updated Changelog 2026-01-26 16:36:09 +01:00
Robin KrämerandOshgnacknak 3bd248b9fe Don't use half sec interval (#1079)
Co-authored-by: Oshgnacknak <osh@oshgnacknak.de>
2026-01-26 16:29:11 +01:00
Oshgnacknak d73cfe7a02 Don't use half sec interval 2026-01-26 13:19:08 +01:00
EinsPommes 6a6c09e283 docs(readme): add documentation links and update machine table 2026-01-25 22:03:53 +01:00
dependabot[bot] 6d2279d44b Merge pull request #1062 from qitechgmbh/dependabot/npm_and_yarn/electron/seroval-1.4.2
Bump seroval from 1.4.0 to 1.4.2 in /electron
2026-01-25 17:00:04 +01:00
Oshgnacknak 61b9ac8a39 Merge branch 'master' into Hamids-Branch 2026-01-23 16:14:22 +01:00
Oshgnacknak 728dd6eb26 Merge pull request #1066 from qitechgmbh/update_numpad_position
Numpad position now updating on resize
2026-01-23 16:10:00 +01:00
Hans Sebastian Pöhlmann 742fd35d5f Numpad position now updating on resize 2026-01-23 15:56:58 +01:00
Milan von dem Bussche 5e1126e896 Revise machine table in README.md
Updated the machine table format and added version details.
2026-01-23 12:38:14 +01:00
Milan von dem Bussche e91719f902 Update getting started documentation with new sections 2026-01-23 12:37:30 +01:00
Milan von dem Bussche 0efa9bcb07 Revise README structure and add documentation links
Updated README to enhance structure and clarity, added minimal hardware examples section, and included API documentation link.
2026-01-23 12:33:58 +01:00
Milan von dem Bussche 37d897d240 Revise machine table with new entries and videos
Updated machine specifications and added video links for various machines.
2026-01-23 11:38:36 +01:00
Robin Krämer 7488e1d97a updated Change Log (#1069) 2026-01-22 20:35:25 +01:00
Christian bb169e3b33 Fix formatting in registry.rs by removing extra blank line 2026-01-22 14:11:19 +01:00
Christian 3d735f8abd Merge branch 'master' into Hamids-Branch 2026-01-22 13:48:58 +01:00
Christian 0a87e45901 Merge pull request #1058 from qitechgmbh/1041-implement-wago-750-402-terminal
1041 implement wago 750 402 terminal
2026-01-22 13:43:37 +01:00
Renosch 6205189b14 Christians Request Resolved 2026-01-22 13:00:42 +01:00
Renosch 9d7da4f0af Resolved 2026-01-22 12:39:30 +01:00
Renosch 241b24be43 Resolved 2026-01-22 12:06:06 +01:00
Renosch aa77d12879 Documentation with Images 2026-01-22 12:05:16 +01:00
Renosch 78f183a8e5 Immplement Digital Input Machine WAGo 750-402 Final Product 2026-01-22 12:05:16 +01:00
Renosch 9f6fc8184e Working Digital Input Machine 2026-01-22 12:05:16 +01:00
Renosch ec3bdcb1df it now funktional! 2026-01-22 12:05:16 +01:00
Renosch 30292650bc finaly the DigitalInputTestMachines Tab is working 2026-01-22 12:05:16 +01:00
Renosch 1e9bcf9b19 alpha version Frontend and Backend 2026-01-22 12:04:49 +01:00
Robin Krämer 1e88979ced dnsmasq now only runs after ethercat is OP (#1063) 2026-01-21 18:46:06 +01:00
Christian a9a973e336 Merge branch 'master' into Hamids-Branch 2026-01-21 17:11:58 +01:00
Oshgnacknak 1112fbaae2 Merge pull request #990 from qitechgmbh/983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page
983 refactor improve aquapath UI layout and add fan speed configuration page
2026-01-21 15:55:32 +01:00
Oshgnacknak 1abe0bbbdb Fix firewall (#1059)
* Configure firewall to only trust lan interface
2026-01-21 15:53:30 +01:00
Oshgnacknak 3644d16cb5 Merge branch 'master' into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2026-01-21 15:44:59 +01:00
Christian cbd82ee7c3 Merge pull request #1056 from qitechgmbh/959-wago-analog-input-klemme-750-455
959 wago analog input klemme 750 455
2026-01-21 15:19:38 +01:00
Christian b66a59dba9 Fix naming 2026-01-21 14:33:18 +01:00
Christian 9dbc962dbc Update file paths 2026-01-21 14:33:18 +01:00
Christian 93e2558803 Translate comments from German to English 2026-01-21 14:33:18 +01:00
Christian 8cf7480833 Fix path in routes.tsx 2026-01-21 14:33:18 +01:00
Christian 08d655effc Fix naming and folder structure 2026-01-21 14:33:18 +01:00
Christian c1c867fca1 Change minimal motor readme for current implementation 2026-01-21 14:33:18 +01:00
Hamid-el 1378c00e1a added .idea to .gitignore 2026-01-21 14:33:18 +01:00
Hamid-el 85f17befbe removed .idea 2026-01-21 14:33:18 +01:00
Hamid-el f2e764f93a finished stepper motor + doc finished (real version3) 2026-01-21 14:33:18 +01:00
Hamid-el 9adeeda490 finished stepper motor + doc finished (real version2) 2026-01-21 14:33:18 +01:00
Hamid-el 68bf075172 finished stepper motor + doc finished (real version) 2026-01-21 14:33:18 +01:00
Hamid-el 7d23190939 stepper motor doc finished 2026-01-21 14:33:18 +01:00
Hamid-el 5c68e2c78a finished stepper motor 2026-01-21 14:33:18 +01:00
Hamid-el 980e14a96e stepper motor with ethercat beckhoff Ek1100 and EL7031 0030 + UI works 2026-01-21 14:33:18 +01:00
Hamid-el d5fb45e4e3 stepper motor with ethercat beckhoff Ek1100 and EL7031 works 2026-01-21 14:33:18 +01:00
Hamid-el cdb7ab1e0c small adjustment 2026-01-21 14:33:18 +01:00
Hamid-el da934e0bb3 first version of manually running a stepper motor with ethercat beckhoff Ek1100 and EL7031
# Conflicts:
#	machines/src/aquapath1/act.rs
#	machines/src/registry.rs
#	server/src/ethercat/setup.rs
2026-01-21 14:33:18 +01:00
Christian 06ea89d500 Refactor Wago AI Test Machine components: improve layout structure, optimize wiring error display, and clean up code formatting. 2026-01-20 13:22:30 +01:00
Christian 0868a90513 Update minimal example documentation for Wago 750-455: refine hardware requirements, enhance wiring instructions, and clarify testing steps. 2026-01-20 13:17:04 +01:00
Christian 39fb295b02 Remove debug print statements 2026-01-20 13:17:04 +01:00
Christian 85dbae7458 Beginning of the dev doc 455 2026-01-20 13:17:04 +01:00
Christian 6ce2174e25 Add wiring error handling to Wago AI Test Machine interface 2026-01-20 13:17:04 +01:00
Christian 3ccaa1cd26 Fix wiring error detection and adjust raw value range for Wago750_455 analog input 2026-01-20 13:17:04 +01:00
Christian 72fec6dc9e Implement Wago AI Test Machine with analog input support and control interface 2026-01-20 13:17:04 +01:00
Christian 547e4ac2bd Add Wago750_455 analog input device implementation 2026-01-20 13:15:04 +01:00
Robin Krämer 5fbdf7b9ab Merge pull request #1053 from qitechgmbh/fix-ethercat-devices-event
fixed broken schema for ethercat devices event
2026-01-20 13:09:02 +01:00
kraemr 85bd4b0d96 fixed formatting 2026-01-20 12:59:39 +01:00
kraemr dfce760521 fixed validation error 2026-01-20 12:58:15 +01:00
kraemr 30c97bf9aa fixed formatting 2026-01-19 16:00:00 +01:00
Oshgnacknak 2c8373adcf Fix rust enum zod check 2026-01-19 15:54:52 +01:00
kraemr d08972254c fixed broken schema for ethercat devices event 2026-01-19 15:12:43 +01:00
Christian a76d39e0cd Merge pull request #1016 from qitechgmbh/1015-fix-xlsx-vulnerabilities
Update xlsx dependency to version 0.20.3 for improved security
2026-01-19 13:23:34 +01:00
Christian a74d0dbb80 Set npmDepsHash for Electron package 2026-01-18 16:57:30 +01:00
Christian 17ab24739d Remove npmDepsHash for improved compatibility 2026-01-18 15:21:11 +01:00
Christian 09f0ea3454 Updated package-lock 2026-01-18 15:00:50 +01:00
Christian 0dd2494ea0 Update xlsx dependency to version 0.20.3 for improved security 2026-01-17 11:41:48 +01:00
Oshgnacknak 502c6d010e Merge remote-tracking branch 'origin/master' into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2026-01-16 13:43:42 +01:00
Robin Krämer 57c1cda229 Merge pull request #1040 from qitechgmbh/1001-rest-api
Rest API /api/v2
2026-01-15 17:06:34 +01:00
Oshgnacknak 8c2b20b148 Rest API: Docs 2026-01-15 16:57:09 +01:00
Oshgnacknak 12e7f245ce Rest API: Allow posting mutations to each machine 2026-01-15 16:57:09 +01:00
Oshgnacknak 671e36fa0f Rest API: Getting values from all machines 2026-01-15 16:57:09 +01:00
Oshgnacknak 1ffd7eade6 Rest API: Getting values from all mock machines 2026-01-15 16:57:09 +01:00
Oshgnacknak 703bbfc9e6 Rest API: Get mock winder v1 values 2026-01-15 16:57:09 +01:00
Oshgnacknak 057afb0b2e List machines in rest api 2026-01-15 16:57:09 +01:00
Robin Krämer 94bd29c734 Merge pull request #1048 from qitechgmbh/zod4
Upgrade to zod v4
2026-01-15 11:37:41 +01:00
Oshgnacknak d078b7b143 Fix nix hash 2026-01-14 22:03:07 +01:00
dependabot[bot] acbeb98f4f Upgrade to zod v4.
To make this work, I lot if typescript types had to be fixed.
In general, they became a little simpler, so the update is worth it.
Also removes zod-adapter and upgrades zod-validation-error to work with zod v4.

From depbot:

Bumps [zod](https://github.com/colinhacks/zod) from 3.25.76 to 4.3.4.
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v3.25.76...v4.3.4)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.3.4
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-14 21:51:23 +01:00
Robin Krämer cab8bf062d Merge pull request #1046 from qitechgmbh/1038-implement-wago-750-652-driver
added implementation for wago 750-652 serial
2026-01-14 14:45:28 +01:00
kraemr 9e04a7fa33 added implementation for wago 750-652 serial 2026-01-14 14:12:01 +01:00
Robin Krämer c92d4847ca Merge pull request #1045 from qitechgmbh/impl-wago-530-driver
added Wago750_530 implementation
2026-01-14 11:57:57 +01:00
kraemr aceeaa08c2 fixed formatting 2026-01-14 11:09:36 +01:00
kraemr 35946d0c88 added Wago750_530 implementation 2026-01-14 11:03:30 +01:00
Oshgnacknak e864a91caa Merge remote-tracking branch 'origin/master' into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2026-01-13 16:58:54 +01:00
Oshgnacknak a5b9a0b1d5 Merge pull request #1039 from qitechgmbh/883-implement-modbus-tcp-for-wago-2789-9052
Fix modbus discovery
2026-01-11 23:19:10 +01:00
Oshgnacknak 412d625138 Fix modbus discovery 2026-01-09 18:12:33 +01:00
Oshgnacknak 1ec20074e9 Rewrite doc for renderUnitSyntax (#981) 2026-01-08 12:22:28 +01:00
4d796f8422 Implement Wago Power Supply Machine (#915)
* Add timeout to Modbus TCP connection

* Remove machine new trait from Machine trait

Machines should not need to know, how they where created.
Only in the machine registry do we assume we know the constructor.

* Initialize very basic WAGO machine

* Most basic WAGO power page

* Basic machine now known its namespace

* Display values from namespace

* Read live values from wago power supply

* Allow turning power on and off

* Allow setting mode

* Actually set registers correctly

* Show error if mutation fails

* Turn power on/off in UI

* Formatting

* Use actual serial number from device

* Fix loop logic

* Enable dhcp and dns server on lan interface

* Loop until modbus device has been found

* Format

* Fix time series creation

* Convert asserts into Err values

This is to make sure that the device cannot panic our backend.

---------

Co-authored-by: nixos-install.sh <nixos-install.sh@localhost>
Co-authored-by: Robin Krämer <93094049+kraemr@users.noreply.github.com>
2026-01-08 12:01:18 +01:00
Robin Krämer 1406d6c724 Merge pull request #1035 from qitechgmbh/changelog-2.13
added ChangeLog
2026-01-07 18:34:00 +01:00
rob 33441b95a5 added ChangeLog 2026-01-07 18:21:09 +01:00
kraemr 8a3f312675 Revert "Merge pull request #943 from qitechgmbh/892-add-datasheets-to-the-machines-manual-tab"
This reverts commit 40e17ef5f1, reversing
changes made to f65d100cd5.
2026-01-07 18:01:14 +01:00
Robin Krämer 5c327428d6 Merge pull request #1034 from qitechgmbh/1032-fix-metrics-page
1032 fix metrics page
2026-01-07 15:57:12 +01:00
rob 4e4d31cad1 changed metrics for frontend 2026-01-07 15:51:59 +01:00
rob 031fdf25a9 changed minor_faults to per sec in frontend 2026-01-07 15:33:06 +01:00
Oshgnacknak 857cda1455 Merge pull request #1013 from qitechgmbh/Oshgnacknak-patch-2
Update nixos docs
2026-01-07 15:17:21 +01:00
OshgnacknakandThomas Symalla de5f3a5cfc Update docs/nixos/README.md
Co-authored-by: Thomas Symalla <github@thomassymalla.de>
2026-01-07 13:23:47 +01:00
Oshgnacknak 2da7af522c Update nixos docs 2026-01-07 13:23:47 +01:00
Robin Krämer 712284e2f0 Merge pull request #1030 from qitechgmbh/fixes-2.13
Fixes 2.13
2026-01-07 13:16:01 +01:00
rob 402c47a81b removed need for el5152 hack, aquapath now usable with other machines 2026-01-06 16:49:34 +01:00
rob 54a000a280 fixed formatting 2026-01-06 16:49:34 +01:00
rob 41fe118144 removed print again 2026-01-06 16:49:34 +01:00
rob aaa6ff604f fixded unnecessary additional version in machine name 2026-01-06 16:49:34 +01:00
rob a350bb378f added debug print 2026-01-06 16:49:34 +01:00
rob aa2212820e fixed ethercat setup 2026-01-06 16:49:34 +01:00
Robin Krämer 578e70119d Merge pull request #1025 from qitechgmbh/dependabot/cargo/tracing-0.1.44
Bump tracing from 0.1.41 to 0.1.44
2026-01-06 16:06:29 +01:00
dependabot[bot] e8796e00af Bump tracing from 0.1.41 to 0.1.44
Bumps [tracing](https://github.com/tokio-rs/tracing) from 0.1.41 to 0.1.44.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-0.1.41...tracing-0.1.44)

---
updated-dependencies:
- dependency-name: tracing
  dependency-version: 0.1.44
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-05 10:23:54 +01:00
Christian 735f27770a Merge pull request #1019 from qitechgmbh/615-test-nixos-builds-in-ci-cd
Rework github pipeline
2026-01-05 07:17:47 +01:00
Christian 700a255157 Merge pull request #971 from qitechgmbh/946-create-docs-for-xtrem-protocol
Added docs
2026-01-05 07:12:08 +01:00
nixos-install.sh ec06275c08 Split and merge nix stores 2026-01-04 23:02:25 +00:00
nixos-install.sh 11d0c8bb53 Add lock files to nix store cache hash 2026-01-04 22:04:46 +00:00
nixos-install.sh 535f24f1d1 Add derivations to store so they appear in the cache 2026-01-04 20:57:15 +00:00
nixos-install.sh 172913d8ec Fix server.nix dependency caching 2026-01-04 20:16:42 +00:00
nixos-install.sh 0f99c54587 Format Nix Files 2026-01-04 18:34:03 +00:00
nixos-install.sh f89d70c5f6 Build whole nix flake in ci 2026-01-04 18:32:15 +00:00
Oshgnacknak 0fc070be69 Build rust in nix inside ci 2026-01-04 18:27:46 +00:00
Oshgnacknak b93eaf530e Build electron in nix inside ci 2026-01-04 18:27:46 +00:00
Oshgnacknak 76d8504651 Redo electron nix build 2026-01-04 18:27:46 +00:00
Oshgnacknak 06acd43ff9 Replace electron forge with vite-plugin-electron 2026-01-04 18:27:46 +00:00
Oshgnacknak 870090c950 Split rust build jobs.
Also use existing rustup action.
2026-01-04 18:27:46 +00:00
Oshgnacknak 65777f10ab Split electron build jobs 2026-01-04 18:27:46 +00:00
Oshgnacknak 67d145de56 Don't run electron workflow twice.
Only run on master push and on pulls to master.
Also use setup-node's built-in cache.
2026-01-04 18:27:46 +00:00
93094049+kraemr@users.noreply.github.com d45d1ed01a test 2026-01-04 18:27:46 +00:00
93094049+kraemr@users.noreply.github.com 0d6c7bc0a5 fixed incorrect path 2026-01-04 18:27:46 +00:00
93094049+kraemr@users.noreply.github.com 15f013f710 fix path 2026-01-04 18:27:46 +00:00
93094049+kraemr@users.noreply.github.com a32e26a041 stubbed hardware config 2026-01-04 18:27:46 +00:00
93094049+kraemr@users.noreply.github.com 41c7e1f74e ci/cd fix 2026-01-04 18:27:46 +00:00
93094049+kraemr@users.noreply.github.com 5a856e6e2b added --impure flag 2026-01-04 18:27:46 +00:00
93094049+kraemr@users.noreply.github.com 8ca16d447e removed buid from nix workflow 2026-01-04 18:27:46 +00:00
93094049+kraemr@users.noreply.github.com b99549895b added nixos test 2026-01-04 18:27:46 +00:00
Christian 07cbaa4518 Merge pull request #1017 from qitechgmbh/fix-nixos-build
Fix nixos electron build
2026-01-02 18:55:07 +01:00
Oshgnacknak c78f5ff5a0 Fix compile warnings 2026-01-02 11:06:23 +01:00
nixos-install.sh 33946a6fa3 Fix nixos electron build 2026-01-02 11:06:23 +01:00
Milan von dem Bussche 02dc67bb85 Revise QiTech Control main description in README
Expanded description of QiTech Control to highlight its open-source nature and modern software paradigms.
2025-12-31 14:43:12 +01:00
Christian b5ae86b19c Merge pull request #988 from qitechgmbh/982-createtimeseries-is-unreadable
refactor of createTimeSeries
2025-12-28 13:41:42 +01:00
Christian 35567b967f Fix analog input test machine issue after rebasing 2025-12-28 13:16:41 +01:00
Hamid-el c45718e670 refactor 2025-12-28 12:26:05 +01:00
Hamid-el 74dcd027e0 refactor of createTimeSeries 2025-12-28 12:26:05 +01:00
Oshgnacknak c793d3fa50 Merge pull request #1012 from qitechgmbh/980-aqua-path-change-max--temp-to-80
Added Min/Max value for AquaPath target temperature
2025-12-25 23:46:06 +01:00
Snacj 04fdc8a9d1 Added Min/Max value for AquaPath target temperature 2025-12-25 23:32:38 +01:00
Oshgnacknak fd702ff0bc Merge pull request #973 from qitechgmbh/948-change-default-settings-graph-dre
Move roundness into separate graph in DRE 

Fixes #948
2025-12-25 22:17:00 +01:00
Hamid-el f25bf71753 added english comments 2025-12-25 22:10:40 +01:00
Hamid-el 4c33873095 added english comments 2025-12-25 22:10:40 +01:00
Hamid-el 8dbcb5efc3 english comments 2025-12-25 22:10:40 +01:00
Hamid-el 36426aafe0 turned all comments into english ones 2025-12-25 22:10:40 +01:00
Hamid-el 21cf9314be turned all comments into english ones 2025-12-25 22:10:40 +01:00
Hamid-el 4f2af7b90d turned all comments into english ones 2025-12-25 22:10:40 +01:00
Hamid-el 06795dcf78 finished issue "Change Default settings Graph DRE #948
" (version2)
2025-12-25 22:10:40 +01:00
Hamid-el 1241d0a4a7 finished issue "Change Default settings Graph DRE #948
"
2025-12-25 22:10:40 +01:00
Oshgnacknak f36723f435 Merge pull request #1011 from qitechgmbh/1010-move-minimal-getting-started-machines-to-their-own-doc
Refactor minimal examples to be in their own md, link from readme and add to getting started docs
2025-12-24 22:49:04 +01:00
ChristianandCopilot e96854a6bc Update docs/developer-docs/minimal-example-el3021.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-24 22:47:17 +01:00
Christian 04c5a673da Refactor minimal examples to be in their own md, link from readme and add to getting started docs 2025-12-24 22:47:17 +01:00
Oshgnacknak 40e17ef5f1 Merge pull request #943 from qitechgmbh/892-add-datasheets-to-the-machines-manual-tab
892 add datasheets to the machines manual tab
2025-12-24 22:26:10 +01:00
Christian 39200aac12 Update import path for extruder manual to align with new documentation structure 2025-12-24 22:24:03 +01:00
Christian 43a405eec4 Update extruder manual import path to reflect new documentation structure 2025-12-24 22:24:03 +01:00
Christian 0206a4ddca Refactor Markdown components for improved readability and maintainability 2025-12-24 22:24:03 +01:00
Christian 34da7178f8 Change manual markdown location and add an readme to explain where images are stored. 2025-12-24 22:24:03 +01:00
Christian 2726c47280 Update Images of datasheet 2025-12-24 22:24:03 +01:00
Christian 63cf25b3d6 Add extruder datasheet to manual 2025-12-24 22:24:03 +01:00
Christian eae13207e4 Add winder datasheet 2025-12-24 22:24:03 +01:00
Oshgnacknak f65d100cd5 Merge pull request #1008 from ThatsMeGIT/patch-1
Fix typo in README about EtherCAT master software
2025-12-24 22:14:36 +01:00
ThatsMe 13e4ea1ac7 Fix typo in README about EtherCAT master software 2025-12-24 22:13:49 +01:00
ᴏxᴏ.ᴍɪᴋᴀ 49788246c8 Merge pull request #945 from qitechgmbh/916-when-assigning-the-ethercat-terminals-no-keyboard-is-showing-up
916 when assigning the ethercat terminals no keyboard is showing up
2025-12-24 20:53:25 +01:00
Christian d4e849d588 Merge pull request #1009 from qitechgmbh/1006-implement-minmal-machine-für-ip-20-community-8di8do
1006 implement minmal machine for ip 20 community 8di8do
2025-12-24 11:11:50 +01:00
Christian 2689d19928 Refactor import statements for IP20 EC DI8 DO8 module across multiple files 2025-12-23 16:07:07 +01:00
Christian 968e9327f6 Remove unused u16dump and u16print debug functions from machine identification 2025-12-23 16:04:37 +01:00
Christian 3cbb74b3b0 Refactor IP20 EC DI8 DO8 device integration: move implementation to wago_modules and update module imports 2025-12-23 15:55:53 +01:00
Christian 50fcfd6e30 Refactor IP20 Test Machine control page and improve code formatting 2025-12-23 15:50:52 +01:00
Christian ce0f27787c Refactor error handling and logging in IP20EcDi8Do8 implementation 2025-12-23 15:49:48 +01:00
Christian 08ab7fd404 Implement IP20 Test Machine support with control and namespace handling 2025-12-23 15:39:16 +01:00
Christian f138b61a45 Add support for IP20 EC DI8 DO8 device identification 2025-12-23 15:06:18 +01:00
Christian 0ad0a58d46 Add Wago IP20 EC DI8 DO8 device support and initialization logic 2025-12-23 15:01:35 +01:00
MLobsien 2d2415a801 fix: unused imports 2025-12-20 17:48:57 +01:00
MLobsien b4cce1dba4 fix: unued fields in controller 2025-12-20 17:47:45 +01:00
MLobsien f80cdb05d5 fix: cooling state display in UI 2025-12-20 17:43:50 +01:00
MLobsien f24f31259b fix: temperature calculation
- resolves #987
- however, total_energy calculation is incorrect but stable
2025-12-20 17:31:55 +01:00
EinsPommes 3d13e6cee2 Refactor DeviceEepromDialog to simplify state management by updating setOpen handler to a no-argument function, enhancing clarity in dialog closure logic. 2025-12-20 16:11:19 +01:00
EinsPommes 86915c2987 Shorten onOpenChange handler in DeviceEepromDialog 2025-12-20 16:09:42 +01:00
MLobsien 6411f9ff80 feat: display heating energy consumption in UI 2025-12-20 15:50:22 +01:00
EinsPommes ada8d0f12c Refactor numpad handling in DeviceEepromDialog to simplify position updates and improve interaction logic. Removed unnecessary observers and streamlined event handling for opening and closing the numpad. 2025-12-20 15:47:30 +01:00
EinsPommes 735456b6eb Enhance DeviceEepromDialog with improved numpad handling and dialog interactions. Added position tracking for the numpad, preventing dialog closure on outside interactions, and ensuring focus remains on the serial input when the numpad is open. 2025-12-20 15:47:30 +01:00
EinsPommes b46a528adb add aria-label for numpad toggle button in DeviceEepromDialog 2025-12-20 15:47:30 +01:00
EinsPommes 80f2343a60 fix: prettier formatting 2025-12-20 15:47:30 +01:00
EinsPommes e030fbe23e Implement touch numpad for serial input in DeviceEepromDialog 2025-12-20 15:47:30 +01:00
Oshgnacknak 1ef0cb608c Merge pull request #997 from qitechgmbh/996-fix-mock-machines-not-running-under-macos
Fix thread ID retrieval for macOS in start_loop_thread function
2025-12-20 15:46:20 +01:00
Christian 789a84e511 Fix thread ID retrieval for unsupported platforms and windows 2025-12-20 15:22:25 +01:00
ChristianandCopilot 28620b6a23 Update server/src/loop.rs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-20 13:54:26 +01:00
Christian 01609513fb Fix formatting in start_loop_thread function for better readability 2025-12-20 13:48:34 +01:00
Christian 379a4ec417 Fix thread ID retrieval for macOS in start_loop_thread function 2025-12-20 13:47:06 +01:00
MLobsien 8b462d5a1d Fix: target_revolutions -> max_revolutions in controller 2025-12-20 10:58:04 +01:00
JalleliAchraf d8a0039db3 fixed double import 2025-12-19 20:52:01 +01:00
Achraf Jalleli dec8ea481c Merge pull request #994 from qitechgmbh/master
merge
2025-12-19 20:45:18 +01:00
EinsPommes 0c8b8388d0 auto-redirect to setup when machine disconnects 2025-12-19 20:42:11 +01:00
MLobsien d3a56710d6 fix: format 2025-12-19 18:49:24 +01:00
MLobsien f9f67d3309 fix: update fans periodically 2025-12-19 18:10:47 +01:00
MLobsien 66122ad957 fix: rename target_revolutions to max_revolutions 2025-12-19 17:54:35 +01:00
MLobsien 95feaf46bf Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-19 17:51:20 +01:00
MLobsien 88f14e0329 fix: emit max_revolutions
- resolves an issue in the UI where the setting wouldnt update
2025-12-19 17:50:00 +01:00
15i1i-s7v 5bf19af844 fix: cargo fmt issues 2025-12-19 17:38:11 +01:00
MLobsien 19e2a337cf Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-19 17:27:30 +01:00
MLobsien 0fd47cfcb1 fix: adjust fan speed in backend
- fine tuning is still necessary
2025-12-19 17:25:58 +01:00
15i1i-s7v e0548d9a65 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-19 17:14:54 +01:00
15i1i-s7v 259ed30dd5 style(aquapath): improve control layout spacing and remove unused import 2025-12-19 17:10:49 +01:00
MLobsien 2e028f0c0f fix: front and back cooling were switched
- use Badge instead of Status Badge
2025-12-19 16:58:15 +01:00
MLobsien 1328c6ce03 Feat: display heating/cooling state in control page 2025-12-19 16:13:48 +01:00
Robin Krämer 28d898e780 Merge pull request #927 from qitechgmbh/dependabot/cargo/syn-2.0.111
Bump syn from 2.0.110 to 2.0.111
2025-12-19 14:46:02 +01:00
dependabot[bot] daabb170f5 Bump syn from 2.0.110 to 2.0.111
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.110 to 2.0.111.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.110...2.0.111)

---
updated-dependencies:
- dependency-name: syn
  dependency-version: 2.0.111
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-19 14:43:02 +01:00
MLobsien adc06623cd Fix: website layout
- variable naming issues in the frontend namespace
- emit state in backend after request to update UI
2025-12-19 14:29:38 +01:00
Robin Krämer c4803e63dd Merge pull request #993 from qitechgmbh/992-fix-install-script
Revert "Merge pull request #914 from qitechgmbh/bin-directory"
2025-12-19 14:12:58 +01:00
rob 0022d86035 Revert "Merge pull request #914 from qitechgmbh/bin-directory"
This reverts commit aecabfde43, reversing
changes made to b8168c6b43.
2025-12-19 14:10:40 +01:00
MLobsien 172ad0ab28 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-19 13:39:40 +01:00
MLobsien bfabb5aa13 feat: UI for temperature tolerance control
- added ui components
- correct frontend api to match backend
2025-12-19 13:38:14 +01:00
15i1i-s7v 94d5a844cf feat: add heating and cooling tolerance functions for front and back
- Added `setFrontHeatingTolerance` and `setBackHeatingTolerance` functions to update heating tolerance values.
- Added `setFrontCoolingTolerance` and `setBackCoolingTolerance` functions to update cooling tolerance values.
- Updated `fanStateSchema` to use `max_revolutions` instead of `target_revolutions`.
- Introduced mutation hooks for handling tolerance updates for front and back heating/cooling.
2025-12-19 13:36:28 +01:00
MLobsien 75c528a430 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-19 11:53:14 +01:00
MLobsien 36045455db WIP: API for heating/cooling tolerance 2025-12-19 11:52:26 +01:00
15i1i-s7v bb12f96723 style: apply code formatting
- Reformatted `controller.rs` and `mod.rs` for better readability.
- Adjusted indentation and spacing for consistency.
2025-12-19 11:48:38 +01:00
15i1i-s7v 8151c99944 fix: simplify cooling logic and remove unnecessary .clone()
- Used `.abs()` directly on the temperature difference instead of duplicating the operation.
- Updated the condition for activating the cooling relay to depend on `cooling_allowed`.
- Refactored the fan speed control to pass the revolutions directly without converting to `AngularVelocity`.
2025-12-19 11:44:24 +01:00
MLobsien 6408010aff Add heating tolerance for controller 2025-12-19 11:23:21 +01:00
MLobsien c96c96217a Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-19 11:14:36 +01:00
MLobsien 93d179a81d WIP: adjust cooling automatically 2025-12-19 11:13:57 +01:00
15i1i-s7v 0f6f2240fc fix: electron fmt issues 2025-12-19 10:48:40 +01:00
15i1i-s7v 74505c6c22 fix: fmt issues resolved 2025-12-19 10:41:23 +01:00
15i1i-s7v 3dfb879101 fix: clean up imports and destructure variables
- Removed unused `use` import in `useAquapath.ts`.
- Added missing `React` import in `Aquapath1Settings.tsx`.
- Refactored variable destructuring in `useAquapath.ts` for readability.
2025-12-19 10:35:42 +01:00
Hans Sebastian Pöhlmann dc711a820f Analog input test machine (#989)
Adding AnalogInputTestMachine, a minimal demo setup with EK1100 and EL3021. Adding setup instructions to readme.
2025-12-18 16:45:28 +00:00
MLobsien 44d4c650cc Fix: format 2025-12-18 17:45:07 +01:00
MLobsien 3121679205 feat: control aquapath fans via UI
- time series is displayed on control page
- device is properly addressed
2025-12-18 17:34:08 +01:00
MLobsien 9fe5e582b0 Refactor: use EditValue components instead of TouchSlider 2025-12-18 16:52:43 +01:00
MLobsien 3e6b838757 Refactor(aquapath1): rename zod fields to match backend
- 'front/back_fan_rpm' -> 'front/back_revolutions'
- integrate a graph view of the fan speed to the config page
2025-12-18 15:56:57 +01:00
Robin Krämer 59cf2cea2a Merge pull request #849 from qitechgmbh/769-add-an-alert-or-warning-in-the-frontend-for-filament-out-of-spec
769 add an alert or warning in the frontend for filament out of spec
2025-12-18 12:00:52 +01:00
MLobsien 3d7bcfecca Feat: Add aquapath fan control to controllers update function 2025-12-18 11:40:27 +01:00
Snacj da69ad1c3e Removed unused import 2025-12-18 11:22:22 +01:00
Snacj 6ff434fb47 Fixed Formatting 2025-12-18 11:22:22 +01:00
Snacj a63c57a0b9 Global error on Filament out of tolerance. 2025-12-18 11:22:22 +01:00
Snacj 21c43adcb4 Rebase and error fixing 2025-12-18 11:22:22 +01:00
snacj afa23666e2 Resolved small writing and logic errors. 2025-12-18 11:22:22 +01:00
snacj 245ad64426 Fixed Formatting 2025-12-18 11:22:22 +01:00
snacj 9665971150 Fixed 0mm threshold for Laser Diameter Warning 2025-12-18 11:22:22 +01:00
snacj 3b056c8f26 Changed Styling of Checkbox. 2025-12-18 11:22:22 +01:00
snacj 62b35e0237 Added AutoStopWinderOnOutOfTolerance to Laser Presets 2025-12-18 11:22:22 +01:00
snacj 11bc62f40e formatting 2025-12-18 11:22:22 +01:00
snacj 116c76d218 Added AutoStopWinderOnOutOfTolerance 2025-12-18 11:22:22 +01:00
snacj 4dcedb537d Changed Styling of Warning. 2025-12-18 11:22:22 +01:00
snacj 09022c9c86 Global Laser Warning addded 2025-12-18 11:22:22 +01:00
snacj 80cf98b11d Laser Warning works across all laser pages 2025-12-18 11:22:22 +01:00
snacj 644e750353 Added global Laser Alert 2025-12-18 11:22:22 +01:00
Robin Krämer aecabfde43 Merge pull request #914 from qitechgmbh/bin-directory
Move scripts to bin directory with better names
2025-12-18 11:20:28 +01:00
Oshgnacknak 686678a1fe Rename install script in electron app 2025-12-18 11:16:39 +01:00
Oshgnacknak 7ac0b788c2 Move scripts to bin directory with better names 2025-12-18 11:16:39 +01:00
Snacj 3791ee785d Fixed Spelling 2025-12-18 11:15:00 +01:00
snacj 7341dee976 Added docs 2025-12-18 11:15:00 +01:00
MLobsien 63a472a1ba refactor(aquapath1): rename fan RPM to revolutions and add target tracking
- Rename all occurrences of 'rpm' to 'revolutions' to align with physical
  property naming conventions.
- Update FanState and Controller to distinguish between current and
  target revolutions.
- Refactor Controller fields to use the AngularVelocity type for improved
  type safety and unit handling.
- Update MachineApi mutations and event building to reflect the new
  data structure.
2025-12-18 10:56:42 +01:00
MLobsien df6b9e72c0 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page
fix indentation
2025-12-18 10:22:16 +01:00
MLobsien 6ad4b9c8bb WIP: add fan support 2025-12-18 10:19:52 +01:00
15i1i-s7v 4fcc8dfe12 fix: Change fan RPM type from f32 to f64 for precision
- Updated the `front_fan_rpm` and `back_fan_rpm` fields in `LiveValuesEvent` struct from f32 to f64.
- Adjusted the `FanState` struct to use f64 for the `rpm` field.
- Modified the `SetFrontFan` and `SetBackFan` mutation variants to expect f64 values instead of f32.
2025-12-18 10:08:10 +01:00
MLobsien 1da64c9cea WIP: AquaPath settings page can be rendered 2025-12-17 17:25:06 +01:00
MLobsien 9b6912be8d WIP: Added fan rpm to AquaPath API 2025-12-17 17:10:22 +01:00
15i1i-s7v 3beed9478c feat(aquapath): add fan RPM data and related schemas to live values (untested)
- Introduced fan RPM values for both front and back fans to AquaPathV1 live values.
- Updated TypeScript schemas to include fan state and RPM data.
- Added time series for tracking fan RPM.
- Updated event handling to include fan RPM data (untested).
2025-12-17 16:06:40 +01:00
15i1i-s7v 3715728fc6 feat: Add fan RPM data to AquaPathV1 live values
- Included front and back fan RPM values from the cooling controllers.
2025-12-17 15:23:59 +01:00
MLobsien 5beee78cad Add a second slider to AquaPath Config Page
- Both fans should be adjustable individually
- Therefor the second slider for the other fan
2025-12-17 15:05:01 +01:00
MLobsien 32fe774e62 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-17 15:01:22 +01:00
MLobsien dead77b49c Change intern representation of fan rpm to f32 2025-12-17 14:58:43 +01:00
MLobsien d7aa976eb1 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-17 14:53:47 +01:00
15i1i-s7v 6f9e89d616 fix(aquapath): correct SetFrontFan mutation name typo 2025-12-17 14:53:25 +01:00
MLobsien bb3f6c0d5a Feat: Implement fan rpm
- extend 'emit_state' to integrate FanState
- implement fan rpm setter function for AquaPathV1 struct
2025-12-17 14:51:46 +01:00
15i1i-s7v 620974ac59 feat(aquapath): implement fan RPM monitoring and control API
Add support for reading fan speeds and setting target RPM values
for both front and back fans in the Aquapath V1 machine.
2025-12-17 14:47:36 +01:00
15i1i-s7v 6c78ff95e4 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-17 13:30:19 +01:00
15i1i-s7v a814236bfa Add settings page and route for Aquapath1 2025-12-17 13:27:52 +01:00
MLobsien 2bee030410 set max temperature of aquapath to 80°C 2025-12-17 12:11:58 +01:00
Hamid-el 285ad59b08 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of https://github.com/qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-17 11:58:43 +01:00
Hamid-el f8b861cb39 WIP: add configpage for aquapath 2025-12-17 11:57:17 +01:00
MLobsien f6a3aa6458 Refactor: reorganize 'Control' Page of the AquaPath Machine
- data is now displayed per device instead of per measurement type
2025-12-17 11:40:51 +01:00
MLobsien dd5f3e8e73 Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-17 10:36:28 +01:00
EinsPommes f50e32af02 fix: prettier formatting 2025-12-17 10:35:20 +01:00
MLobsien 345e083edd auto-redirect to setup when machine disconnects 2025-12-17 10:35:20 +01:00
MLobsien 5660ddd05f Merge branch '983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page' of github.com:qitechgmbh/control into 983-refactor-improve-aquapath-ui-layout-and-add-fan-speed-configuration-page 2025-12-17 10:17:41 +01:00
rob c4360850af wago modules detected and wrapped as an EtherCatDevice 2025-12-17 10:13:15 +01:00
rob ce2123c88c added logic for initializing wago modules on a 750_354 2025-12-17 10:13:15 +01:00
Christian 520f33bf29 fix: clean up import formatting in registry.rs 2025-12-17 10:13:15 +01:00
Christian c620f1db7f fix mock machines in dev build 2025-12-17 10:13:15 +01:00
Christian d352e88d6f feat: add mock machine build step to CI workflow 2025-12-17 10:13:15 +01:00
EinsPommes cc009b31f3 fix: prettier formatting 2025-12-17 10:13:15 +01:00
EinsPommes b5c189ab49 transform roundness to percentage and update related components 2025-12-17 10:13:15 +01:00
EinsPommes 64f57277f2 fix: prettier formatting 2025-12-17 10:13:15 +01:00
EinsPommes af8ee60fe8 fix: clarify machine existence check in SidebarLayout 2025-12-17 10:13:15 +01:00
EinsPommes beb2e3cc2d remove console.log statement 2025-12-17 10:13:15 +01:00
EinsPommes 0e2bcfd7bc fix: handle machine disconnects properly
The app now detects when a machine disconnects and redirects you
to the setup page. Before, it would just stay on the machine page
even though the machine was gone.

Fixed both backend (USB detection) and frontend (auto-navigation).
Added error handling for parseInt and pathMatch array access.
2025-12-17 10:13:15 +01:00
EinsPommes 417ffb23c2 update: redirect to setup when machine disconnects 2025-12-17 10:13:15 +01:00
EinsPommes a57d4cdf36 auto-redirect to setup when machine disconnects 2025-12-17 10:13:15 +01:00
Robin Krämer b8168c6b43 Merge pull request #976 from qitechgmbh/aquapath-fix-port-selection
Aquapath fix port selection
2025-12-17 10:12:30 +01:00
rob 6026d2a658 ports are now correctly selected for the aquapath 2025-12-17 10:10:18 +01:00
Achraf Jalleli a301f3e228 Merge pull request #979 from qitechgmbh/932-add-metrics
Add compile and runtime metrics
2025-12-16 22:45:54 +01:00
JalleliAchraf 0b6bd68a24 frontend UI improvements + compile metrics script fixes 2025-12-16 17:43:33 +01:00
Christian e7fd390263 Merge pull request #969 from qitechgmbh/968-Add-mock-machines-to-pr-checks
968 add mock machines to pr checks and fix mocks and dev build
2025-12-16 16:09:17 +01:00
JalleliAchraf eab675795a Add compile and runtime metrics 2025-12-16 14:07:03 +01:00
Christian 8c2dc9a507 Merge branch 'master' into 968-Add-mock-machines-to-pr-checks 2025-12-16 10:49:14 +01:00
Robin Krämer f6c0744efa Merge pull request #937 from qitechgmbh/860-wago-coupler-750-354-cant-enter-op
860 wago coupler 750 354 cant enter op
2025-12-16 10:45:25 +01:00
rob 8c7e810359 wago modules detected and wrapped as an EtherCatDevice 2025-12-16 08:25:19 +01:00
Christian f0fd9e8ce6 fix: clean up import formatting in registry.rs 2025-12-15 11:00:49 +01:00
Christian c5579a4471 fix mock machines in dev build 2025-12-15 10:58:07 +01:00
Christian de7a65bf12 feat: add mock machine build step to CI workflow 2025-12-15 10:43:52 +01:00
rob e4d0b3c5be added logic for initializing wago modules on a 750_354 2025-12-12 15:24:59 +01:00
Robin Krämer dad029ab0d Merge pull request #924 from qitechgmbh/905-change-the-laser-graph-roundness-to-display-in-percentage-instead-of-mm
display laser roundness as percentage
2025-12-07 12:00:46 +01:00
EinsPommes 9c780799d1 fix: prettier formatting 2025-12-07 11:59:15 +01:00
EinsPommes 27a33f9def transform roundness to percentage and update related components 2025-12-07 11:59:15 +01:00
Robin Krämer f44cd029f9 Merge pull request #925 from qitechgmbh/891-switch-to-setup-page-when-selected-machine-disconnected
891 switch to setup page when selected machine disconnected
2025-12-07 11:58:58 +01:00
EinsPommes 9abb7629fb fix: prettier formatting 2025-12-07 11:56:41 +01:00
EinsPommes a161d85a63 fix: clarify machine existence check in SidebarLayout 2025-12-07 11:56:41 +01:00
EinsPommes 9df9e6df4a remove console.log statement 2025-12-07 11:56:41 +01:00
EinsPommes 29d465c13e fix: handle machine disconnects properly
The app now detects when a machine disconnects and redirects you
to the setup page. Before, it would just stay on the machine page
even though the machine was gone.

Fixed both backend (USB detection) and frontend (auto-navigation).
Added error handling for parseInt and pathMatch array access.
2025-12-07 11:56:41 +01:00
EinsPommes 622c94f2d9 update: redirect to setup when machine disconnects 2025-12-07 11:56:41 +01:00
EinsPommes f31a2789ca auto-redirect to setup when machine disconnects 2025-12-07 11:56:41 +01:00
Robin Krämer e7632e8d1b Merge pull request #941 from qitechgmbh/936
936-documentation for minimal example
2025-12-07 11:56:25 +01:00
Achraf Jalleli 2f4f3de32a Update schematic asset path
Removed placeholder text for schematic asset path.
2025-12-06 21:23:01 +01:00
Achraf Jalleli c9f49198c7 updated image reference 2025-12-06 19:01:06 +01:00
Achraf Jalleli e2c12a0168 Refinements
Updated instructions for connecting and configuring the EK1100 and EL2004.
2025-12-06 18:59:43 +01:00
Achraf Jalleli cb6e066512 Fix formatting of Acknowledgements section header 2025-12-06 18:50:35 +01:00
Achraf Jalleli 1dd0421f96 documentation for minimal example 2025-12-06 18:48:49 +01:00
Achraf Jalleli 64855f2e7a Minimal Example Documentation added 2025-12-06 18:42:17 +01:00
Achraf Jalleli 855ae7b3cf minimal example docu assets 2025-12-06 18:20:54 +01:00
Robin Krämer 5f9a8605c9 Merge pull request #930 from qitechgmbh/923-create-extruder-v3
923 create extruder v3
2025-12-04 13:55:20 +01:00
rob 567730a8d4 fixed typos in machines 2025-12-04 13:25:07 +01:00
rob 6b2164b88b added frontend for extruder3 and fixed rpm calculation 2025-12-04 11:30:53 +01:00
rob 930ea10409 added extruder2 2025-12-04 11:26:17 +01:00
Robin Krämer 24b703e262 Merge pull request #931 from qitechgmbh/895-small-sample-machine-for-testing
small test machine
2025-12-04 11:23:42 +01:00
JalleliAchraf 152dd3efa0 small test machine 2025-12-04 11:17:24 +01:00
Milan von dem Bussche ebc5bae55f added Minimal Example README.md
Minimal example to explain the Code
2025-12-03 16:19:13 +01:00
Robin Krämer 88214c46a8 Merge pull request #911 from qitechgmbh/909-implement-ecma-error-messages
909 implement ecmy error messages
2025-11-30 00:41:49 +01:00
rob c9485a92e9 fixed formatting 2025-11-30 00:38:34 +01:00
rob 9df42c4d13 fixed server setup 2025-11-30 00:38:34 +01:00
rob fc9a439ae8 removed unused variables 2025-11-30 00:38:34 +01:00
rob e451153406 removed unused variables 2025-11-30 00:38:34 +01:00
rob 500e2fe481 fixed parsing etc 2025-11-30 00:38:34 +01:00
rob a5884d93e7 test 2025-11-30 00:38:34 +01:00
rob d2e32cdd78 added comment 2025-11-30 00:38:34 +01:00
rob f66c6d4f65 Note entirely sure it works ... 2025-11-30 00:38:34 +01:00
Oshgnacknak fb85cc6db7 Merge pull request #896 from qitechgmbh/877-remove-dependencies-that-are-not-necessary-or-bloated
Remove futures crate
2025-11-24 23:13:55 +01:00
Robin Krämer 4e128ded36 Merge pull request #885 from qitechgmbh/699-add-min-and-max-diameter-on-laser-page2
699 add min and max diameter on laser page
2025-11-24 13:02:35 +01:00
Oshgnacknak f50388b141 Just restart failed tasks 2025-11-24 12:54:40 +01:00
ChristianandCopilot 4025a68fe1 Update electron/src/control/MinMaxValue.tsx
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-23 17:58:07 +01:00
ChristianandCopilot 7a05fa206f Update electron/src/control/MinMaxValue.tsx
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-23 17:58:07 +01:00
Christian 8594b06c3b Reorganize shared timeframe selector for clarity and improve layout for diameter range display 2025-11-23 17:58:07 +01:00
Christian 17523574d8 Add shared timeframe selector to Laser1ControlPage and expose TIMEFRAME_OPTIONS 2025-11-23 17:58:07 +01:00
Christian 7971797178 Refactor diameter range display for 2-axis lasers and improve layout 2025-11-23 17:58:07 +01:00
Christian a8b0837e58 Add MinMaxValue component for displaying diameter ranges 2025-11-23 17:58:07 +01:00
Oshgnacknak e419974844 Refactor init logic to work without futures crate 2025-11-22 17:30:37 +01:00
Oshgnacknak 773d0f6c15 Remove tonic 2025-11-22 16:15:48 +01:00
Oshgnacknak afc4e0d8b4 Completely remove opentelemetry and update dependencies 2025-11-22 16:15:47 +01:00
Robin Krämer 5e86988ac4 Merge pull request #907 from qitechgmbh/882-add-nozzle-docs-for-extruder
882 add nozzle docs for extruder
2025-11-19 11:38:07 +01:00
snacj 2d28c18b1a Added Heading for new Mechanical Diagram 2025-11-18 14:04:32 +01:00
snacj b19764281a Added Nozzle docs 2025-11-18 13:58:51 +01:00
snacj c25d63f781 Added Nozzle docs 2025-11-18 13:58:51 +01:00
Robin Krämer d79efc412f Merge pull request #904 from qitechgmbh/898-mock-machine-doesnt-work
898 mock machine doesnt work
2025-11-18 12:53:24 +01:00
rob 7ad74cf089 fixed formatting 2025-11-17 18:00:43 +01:00
rob 6073d37892 fixed remaining compile errors 2025-11-17 18:00:43 +01:00
rob c04937553f machines show up but dont get subscribed to 2025-11-17 18:00:43 +01:00
rob e08bdfa3c7 removed unnecessary and outdated docs 2025-11-17 18:00:43 +01:00
rob d282678b19 fixed compile errors for mock-machines 2025-11-17 18:00:43 +01:00
Robin Krämer 9ff4243350 Merge pull request #902 from qitechgmbh/2.12-releasenotes
updated Changelog
2025-11-17 16:38:48 +01:00
rob 6c79b33752 updated Changelog 2025-11-17 16:36:41 +01:00
rob 861b5da7a3 updated Changelog 2025-11-17 16:35:28 +01:00
Robin Krämer 7dd3d50653 Merge pull request #899 from qitechgmbh/add-debugging-utils-folder
added debugging utils and fixed memory leak
2025-11-14 23:30:55 +01:00
kraemr ba8e04c641 removed unused imports 2025-11-14 23:28:05 +01:00
kraemr cece245a55 removed one_hour caching on backend for apis 2025-11-14 21:26:38 +01:00
kraemr fab4d2810e fixed memory leak and added debugging utilities 2025-11-14 21:18:51 +01:00
Robin Krämer 5bff3ca929 Merge pull request #897 from qitechgmbh/fix-async-yield-timeout
Fix async yield timeout
2025-11-14 16:45:59 +01:00
kraemr afe1d3b80e fixed display of time taken on failing loop 2025-11-14 14:06:32 +01:00
kraemr 6c8005dba9 log cycle time on failure 2025-11-14 13:36:50 +01:00
kraemr 9f399ca6f3 fixed compile error 2025-11-14 13:25:44 +01:00
kraemr a7574dd348 better error handling for copy ethercat outputs and inputs 2025-11-14 13:22:27 +01:00
kraemr b39f3597c8 fixed loop 2025-11-14 12:20:53 +01:00
Robin Krämer bb9a86d52e Merge pull request #893 from qitechgmbh/fix-serial-hotplugging
fixed laser hotplug issue
2025-11-13 13:57:27 +01:00
kraemr e5329e40c6 fixed ci error 2025-11-13 13:55:44 +01:00
kraemr 319611f69d fixed laser hotplug issue 2025-11-13 13:55:44 +01:00
Robin Krämer 4b16253c8c Merge pull request #887 from qitechgmbh/841-remove-all-locking-and-as-much-async-code-as-possible
841 remove all locking and as much async code as possible
2025-11-13 10:01:47 +01:00
kraemr b60423fde6 removed unnecessary variable from shared_state and fixed serial hotplug 2025-11-13 09:33:40 +01:00
kraemr 5d3399c72e fixed formatting electron 2025-11-13 01:03:10 +01:00
kraemr d247bafcbb fixed formatting 2025-11-13 01:00:28 +01:00
kraemr 5227aad4c6 removed all warnings 2025-11-13 00:56:59 +01:00
kraemr da4c54a99e fixed compile errors 2025-11-13 00:04:15 +01:00
kraemr 8d42bd93f7 started refactoring so machines use channels instead of locks
backend works flawlessly now

added machine_connection and a main_thread actor

added back write_machine_device_ident

error handling for machines restored

correctly handles machine connections/disconnections now

removed unused deps
2025-11-12 17:54:11 +01:00
kraemr 37e467272b removed potential "deadlock" 2025-11-12 17:41:03 +01:00
Oshgnacknak ed0b9cd5ae Merge pull request #859 from qitechgmbh/789-create-minimal-set-of-uom-units
Use minimal set of units instead of full uom::si module
2025-11-11 15:47:45 +01:00
Oshgnacknak 8045f799f0 Use minimal set of units instead of full uom::si module 2025-11-10 17:15:51 +01:00
Robin Krämer ca6ea16f89 Merge pull request #881 from qitechgmbh/880-add-nozzle-pid-for-temperature-at-extruder
880 add nozzle pid for temperature at extruder
2025-11-06 12:16:58 +01:00
snacj 452614abf1 Fixed Frontend Formatting 2025-11-06 11:59:52 +01:00
snacj f63c797cd1 Fixed Formatting 2025-11-06 11:57:56 +01:00
snacj de791f6ea8 Added PID for extruder nozzle. 2025-11-06 11:57:34 +01:00
Robin Krämer 024f751ef1 Merge pull request #873 from qitechgmbh/872-add-cycle-target-time
872 add cycle target time
2025-11-04 16:19:15 +01:00
kraemr d4c82bfd7d refactored ethercat input ouput into funcs, and added spin_sleep
with default target time of 300us
2025-11-04 15:23:19 +01:00
Robin Krämer a49f19133d Merge pull request #874 from qitechgmbh/855-decouple-serial-devices-from-ethercat-logic-in-looprs-mainrs
855 decouple serial devices from ethercat logic in looprs mainrs
2025-11-04 15:08:49 +01:00
snacj a94009514c Changed Error message of moved code 2025-11-04 12:46:03 +01:00
Snacj d1d3724f52 Fixed Formatting 2025-11-03 18:49:51 +01:00
Snacj 9f4177aff9 Fixed implementation errors 2025-11-03 18:48:01 +01:00
Snacj e323fd76f8 Moved loop thread out of main loop 2025-11-03 18:45:08 +01:00
Snacj b0d24d8502 fixed mock-machine init after app_state 2025-11-03 18:29:27 +01:00
Snacj 8ae20c2aae Added init_mock back to main.rs and fixed serial devices relying on ethercat interface 2025-11-03 18:27:28 +01:00
Snacj 89843514b8 debugging main.rs 2025-11-03 17:41:26 +01:00
Robin Krämer 1a9e39a49e Merge pull request #817 from qitechgmbh/811-add-a-simple-updating-tab
811 add a simple updating progress bar
2025-11-03 14:23:36 +01:00
Christian f4d5e74c59 Add update step tracking and progress bar component 2025-11-02 14:47:05 +01:00
Oshgnacknak eb1301df99 Merge pull request #650 from qitechgmbh/dependabot/npm_and_yarn/electron/electron-forge/plugin-fuses-7.8.3
Bump @electron-forge/plugin-fuses from 7.8.2 to 7.10.2 in /electron
2025-10-31 19:02:46 +01:00
Oshgnacknak 2885fa91cc Bump electron fuses to 17.10.2 2025-10-31 18:41:38 +01:00
dependabot[bot] d89e4681db Bump @electron-forge/plugin-fuses from 7.8.2 to 7.8.3 in /electron
Bumps [@electron-forge/plugin-fuses](https://github.com/electron/forge) from 7.8.2 to 7.8.3.
- [Release notes](https://github.com/electron/forge/releases)
- [Changelog](https://github.com/electron/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/electron/forge/compare/v7.8.2...v7.8.3)

---
updated-dependencies:
- dependency-name: "@electron-forge/plugin-fuses"
  dependency-version: 7.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-31 17:26:00 +00:00
Oshgnacknak dedcd76241 Merge pull request #761 from qitechgmbh/dependabot/npm_and_yarn/electron/hookform/resolvers-5.2.2
Bump @hookform/resolvers from 4.1.3 to 5.2.2 in /electron
2025-10-31 18:23:33 +01:00
Oshgnacknak e987dd07f9 Fix resolver type argument 2025-10-31 18:22:03 +01:00
dependabot[bot] 6747433100 Bump @hookform/resolvers from 4.1.3 to 5.2.2 in /electron
Bumps [@hookform/resolvers](https://github.com/react-hook-form/resolvers) from 4.1.3 to 5.2.2.
- [Release notes](https://github.com/react-hook-form/resolvers/releases)
- [Commits](https://github.com/react-hook-form/resolvers/compare/v4.1.3...v5.2.2)

---
updated-dependencies:
- dependency-name: "@hookform/resolvers"
  dependency-version: 5.2.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-31 18:22:02 +01:00
Robin Krämer 3ffd67918c Merge pull request #854 from qitechgmbh/853-add-laser-dre-manual-md-to-docs
853 add laser dre manual md to docs
2025-10-31 10:39:33 +01:00
Snacj 3fe6556037 Added Settings options to DRE md 2025-10-31 10:34:42 +01:00
snacj 5ccc65241d Added Markdown for laser DRE 2025-10-31 10:34:42 +01:00
Robin Krämer be92fd0665 Merge pull request #857 from qitechgmbh/856-dre-laser-registers-are-read-wrong-in-modbus-mode
Fixed Register Vec to work with 2 Axis Laser in Modbus mode
2025-10-29 21:32:01 +01:00
Snacj 8a6d4d1385 Fixed Register Vec to work with 2 Axis Laser in Modbus mode 2025-10-29 18:10:52 +01:00
439 changed files with 25874 additions and 12108 deletions

No files matched your search

-3
View File
@@ -1,3 +0,0 @@
.png filter=lfs diff=lfs merge=lfs -text
.jpg filter=lfs diff=lfs merge=lfs -text
.jpeg filter=lfs diff=lfs merge=lfs -text
+40
View File
@@ -0,0 +1,40 @@
---
name: Bug report
about: Create a report to help us improve
title: 'BUG: '
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Navigate to '...'
2. Select/Press '....'
3. Execute operation '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots/Photos**
If applicable, add screenshots from the HMI display or photos of the machine screen to help explain your problem.
**Machine Information (please complete the following):**
- Machine Type: [Winder2, Laser, Aquapath...]
- Control Software Version: [e.g. 2.4.1]
- Settings: [e.g. Different Gear Ratios, PID Settings, …]
**Device Information (if issue occurs on external device):**
- Device Type: [e.g. Tablet, PLC, Remote Control Panel]
- OS: [e.g. Windows 10, Linux, etc.]
- Connection Type: [e.g. Ethernet, USB]
**Error Messages/Codes**
If applicable, provide exact error codes or messages displayed or found in the troubleshooting terminal.
**Additional context**
Add any other context about the problem here (e.g., recent software updates, environmental factors).
+33
View File
@@ -0,0 +1,33 @@
---
name: Feature request
about: Suggest an idea for this project
title: 'Feat: '
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. During operation, I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions, workarounds, or features you've considered.
**Use Case/Application:**
- Machine Type(s): [e.g. All machines, Winder, Aquapath, ...]
- Frequency of Use: [e.g. Daily, During setup, Occasionally]
**Expected Benefits:**
- Efficiency Improvement: [e.g. Reduces setup time by X minutes, Eliminates manual steps]
- Safety Enhancement: [e.g. Prevents operator error, Adds safety checks]
- Production Impact: [e.g. Increases throughput, Reduces downtime, Improves quality]
- User Experience: [e.g. Simplifies workflow, Reduces training time]
**Mockups/Sketches**
If applicable, add sketches, mockups, or photos showing how the feature would appear on the HMI display or integrate with the machine.
**Additional context**
Add any other context, industry standards, or examples from other control systems about the feature request here.
+52 -29
View File
@@ -3,50 +3,73 @@ name: Electron
on:
push:
branches:
- "**"
- master
pull_request:
branches:
- "*"
- master
defaults:
run:
working-directory: electron
jobs:
build-electron:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: electron
steps:
- name: Setup Node.js
- &checkout
name: Checkout
uses: actions/checkout@v4
- &setup-node
name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "18" # or '20' for latest LTS
node-version: 20
cache: "npm"
cache-dependency-path: |
electron/package-lock.json
- uses: actions/checkout@v4
- &install
name: Install dependencies
run: npm ci
- name: Cache NPM dependencies
uses: actions/cache@v3
with:
path: |
~/.npm
node_modules/
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install
run: npm i
- name: Build
- name: Compile
run: tsc
- name: Format
run: npm run format
- name: Build
run: npm run build
- name: Lint
run: npm run lint
test:
runs-on: ubuntu-latest
needs: build
steps:
- *checkout
- *setup-node
- *install
- name: Test
run: npm run test
- name: Package
run: npm run package
format:
runs-on: ubuntu-latest
steps:
- *checkout
- *setup-node
- *install
- name: Format
run: npm run format
lint:
runs-on: ubuntu-latest
steps:
- *checkout
- *setup-node
- *install
- name: Lint
run: npm run lint
+96
View File
@@ -0,0 +1,96 @@
name: Nix CI
on:
push:
branches:
- master
pull_request:
branches:
- master
env:
NIX_PATH: nixpkgs=channel:nixos-25.05
jobs:
electron:
runs-on: ubuntu-latest
steps:
- &checkout
name: Check out repository
uses: actions/checkout@v4
- &nix-install
uses: nixbuild/nix-quick-install-action@v34
with:
nix_conf: |
keep-env-derivations = true
keep-outputs = true
- uses: nix-community/cache-nix-action@v6
with:
primary-key: nix-electron-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', './electron/package-lock.json') }}
restore-prefixes-first-match: nix-${{ runner.os }}-
purge: true
purge-prefixes: nix-electron-${{ runner.os }}-
- name: Build Electron in Nix
run: nix build ".#packages.x86_64-linux.electron"
rust:
runs-on: ubuntu-latest
steps:
- *checkout
- *nix-install
- uses: nix-community/cache-nix-action@v6
with:
primary-key: nix-server-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', './Cargo.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-
purge: true
purge-prefixes: nix-server-${{ runner.os }}-
- name: Build Server in Nix
run: nix build ".#packages.x86_64-linux.server"
build:
runs-on: ubuntu-latest
needs: [electron, rust]
steps:
- *checkout
- *nix-install
- name: Use both Electron and Server Nix Store
uses: nix-community/cache-nix-action@v6
with:
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', './Cargo.lock', './electron/package-lock.json') }}
# if no hit on the primary key, merge individual caches
restore-prefixes-all-matches: |
nix-electron-${{ runner.os }}-
nix-server-${{ runner.os }}-
- name: Build Flake and System Configuration
run: nix build ".#nixosConfigurations.nixos.config.system.build.toplevel" --impure
format:
runs-on: ubuntu-latest
steps:
- *checkout
- *nix-install
- name: Format
run: nix run 'nixpkgs#nixfmt-classic' $(find . nixos -name '*.nix')
check:
runs-on: ubuntu-latest
steps:
- *checkout
- *nix-install
- name: Build Flake and Configuration.nix
run: nix flake check "." --impure
+48 -9
View File
@@ -3,9 +3,10 @@ name: Rust
on:
push:
branches:
- "**"
- master
pull_request:
branches: "*"
branches:
- master
env:
CARGO_TERM_COLOR: always
@@ -16,14 +17,24 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install libudev development package
- &udev-install
name: Install libudev development package
run: sudo apt-get update && sudo apt-get install -y libudev-dev
- uses: actions/checkout@v4
- name: Set Rust version
run: rustup override set ${{ env.RUST_VERSION }}
- name: Install Formatter
run: rustup component add rustfmt
- name: Cache Rust dependencies
- &checkout
uses: actions/checkout@v4
- &setup-rust
name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
profile: minimal
override: true
components: rustfmt
- &cargo-cache
name: Cache Rust dependencies
uses: actions/cache@v3
with:
path: |
@@ -41,8 +52,36 @@ jobs:
RUSTFLAGS: "-D warnings"
run: cargo build --verbose
format:
runs-on: ubuntu-latest
steps:
- *checkout
- *setup-rust
- name: Format
run: cargo fmt --check --verbose
test:
runs-on: ubuntu-latest
steps:
- *udev-install
- *checkout
- *setup-rust
- *cargo-cache
- name: Run tests
run: cargo test --verbose
mock:
runs-on: ubuntu-latest
steps:
- *udev-install
- *checkout
- *setup-rust
- *cargo-cache
- name: Mock Machine
run: cargo build --features "development-build,mock-machine"
+80
View File
@@ -0,0 +1,80 @@
name: Update npm deps hash
on:
pull_request:
paths:
- 'electron/package-lock.json'
- 'electron/package.json'
workflow_dispatch:
env:
NIX_PATH: nixpkgs=channel:nixos-25.05
permissions:
contents: write
pull-requests: write
actions: write
jobs:
update-hash:
runs-on: ubuntu-latest
if: github.event.pull_request.head.repo.full_name == github.repository
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Nix
uses: nixbuild/nix-quick-install-action@v34
with:
nix_conf: |
keep-env-derivations = true
keep-outputs = true
- name: Calculate new npm deps hash
id: calc-hash
run: |
# Calculate the hash using nix run with nixpkgs
NEW_HASH=$(nix run 'nixpkgs#prefetch-npm-deps' electron/package-lock.json)
echo "hash=$NEW_HASH" >> $GITHUB_OUTPUT
echo "Calculated hash: $NEW_HASH"
- name: Update npmDepsHash in electron.nix
run: |
NEW_HASH="${{ steps.calc-hash.outputs.hash }}"
sed -i "s|npmDepsHash = \".*\";|npmDepsHash = \"$NEW_HASH\";|" nixos/packages/electron.nix
- name: Check if hash changed
id: check-changes
run: |
if git diff --quiet nixos/packages/electron.nix; then
echo "changed=false" >> $GITHUB_OUTPUT
echo "No changes needed - hash is already correct"
else
echo "changed=true" >> $GITHUB_OUTPUT
echo "Hash updated in electron.nix"
fi
- name: Commit and push if changed
if: steps.check-changes.outputs.changed == 'true'
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add nixos/packages/electron.nix
git commit -m "chore: update npmDepsHash to ${{ steps.calc-hash.outputs.hash }}"
git push
- name: Comment on PR
if: steps.check-changes.outputs.changed == 'true'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Automatically updated `npmDepsHash` to `${{ steps.calc-hash.outputs.hash }}`\n\n**Next step:** Trigger the checks by pushing an empty commit:\n```bash\ngit commit --allow-empty -m "trigger ci" && git push\n```'
})
+5 -1
View File
@@ -4,4 +4,8 @@ dist/
Cargo.lock
dhat-heap.json
build.sh
shell.nix
node_modules/
runtime_metrics.csv
compile_metrics.csv
.idea/
View File
Whitespace-only changes.
+48
View File
@@ -1,3 +1,51 @@
# `2.14.1`
_26.01.2026_
## Laser
-[#1079](https://github.com/qitechgmbh/control/pull/1079) Fixed Overflowing of buffer for Graphs causing crashes
# `2.14.0`
_22.01.2026_
## General
- [#1040](https://github.com/qitechgmbh/control/pull/1040) Added API for reading Machine Data
# `2.13.0`
_07.01.2026_
## General
- [#925](https://github.com/qitechgmbh/control/pull/925) Switch to Setup when selected machine is disconnected
- [#937](https://github.com/qitechgmbh/control/pull/937) Added minimal Support for WAGO
- [#945](https://github.com/qitechgmbh/control/pull/945) Improvements for On-Screen Keyboard usage in Ethercat Setup Page
## Extruder V3
- [#930](https://github.com/qitechgmbh/control/pull/930) Added Extruder V3
## Laser
- [#924](https://github.com/qitechgmbh/control/pull/924) Roundness is now displayed as a percentage
- [#885](https://github.com/qitechgmbh/control/pull/885) Added Min and Max Diameter
- [#849](https://github.com/qitechgmbh/control/pull/849) Added a Warning if Filament is out of Spec
## Aquapath
- [#976](https://github.com/qitechgmbh/control/pull/976) Improved Stability
# `2.12.0`
_17.11.2025_
## General
- [#847](https://github.com/qitechgmbh/control/pull/847) Improved discovery and initialization
- [#859](https://github.com/qitechgmbh/control/pull/859) Improved update speed
- [#897](https://github.com/qitechgmbh/control/pull/897), [#899](https://github.com/qitechgmbh/control/pull/899), [#852](https://github.com/qitechgmbh/control/pull/852) Improved system stability
- [#817](https://github.com/qitechgmbh/control/pull/817) Added an update bar
## Laser
- [#857](https://github.com/qitechgmbh/control/pull/857) Fixed potential issues with 2 Axis Laser
## Extruder V2
- [#881](https://github.com/qitechgmbh/control/pull/881) Extended Temperature PID Configuration
# `2.11.0`
_21.10.2025_
Generated
+520 -1063
View File
File diff suppressed because it is too large. Load diff
+4 -1
View File
@@ -4,8 +4,11 @@ members = [
"ethercat-hal",
"ethercat-hal-derive",
"control-core",
"ethercat-eeprom-dump",
"machines",
#"ethercat-eeprom-dump",
"control-core-derive",
"units",
"utils",
]
exclude = []
resolver = "2"
+39 -125
View File
@@ -2,153 +2,67 @@
# QiTech Control
QiTech Control is an industrial control panel software for the next generation of QiTech recycling machines built on top of Beckhoff Automation hardware.
QiTech Control is an open-source framework designed to bring modern software development practices to certified industrial hardware.
[![](https://img.youtube.com/vi/KI3YeBwfV-s/maxresdefault.jpg)](https://www.youtube.com/watch?v=KI3YeBwfV-s)
*Click here to watch a video demo of our software.*
It frees developers from proprietary, license-heavy PLC ecosystems and rigid "point-and-click" workflows that are no longer adequate for today's complex automation challenges.
[![](https://img.youtube.com/vi/55egCAkQgyM/maxresdefault.jpg)](https://youtu.be/55egCAkQgyM)
*Click here to watch a full explainer Video of our Software.*
QiTech Control combines the modularity and reliability of standard EtherCAT terminals (e.g., WAGO, Beckhoff) with the power of a modern Rust & React stack.
# Repo Structure
## Documentation
Frontend
**[View Full Documentation Wiki](https://github.com/qitechgmbh/control/wiki)**
- `/electron`: Frontend code for the control software built with React and Electron.
- [Getting Started](https://github.com/qitechgmbh/control/wiki/Getting-Started)
- [Architecture Overview](https://github.com/qitechgmbh/control/wiki/Architecture-Overview)
- [EtherCAT Basics](https://github.com/qitechgmbh/control/wiki/Ethercat-Basics)
- [Device Examples](https://github.com/qitechgmbh/control/wiki/Hardware-Examples)
- [REST API Reference](https://github.com/qitechgmbh/control/wiki/Rest-Api)
Backend
## Videos
- `/server`: Glue between Beckhoff and Electron. Implements machine logic.
- `/ethercat-hal`: Hardware abstraction layer for Beckhoff (and possibly other EtherCat) devices and protocols.
- `/ethercat-hal-derive`: Macros for `ethercat-hal`
- `/control-core`: Core control logic for the server.
| Software Demo | Full Explainer |
|---------------|----------------|
| [![](https://img.youtube.com/vi/KI3YeBwfV-s/maxresdefault.jpg)](https://www.youtube.com/watch?v=KI3YeBwfV-s)<br/>*Click to watch a demo of the control software in action* | [![](https://img.youtube.com/vi/55egCAkQgyM/maxresdefault.jpg)](https://youtu.be/55egCAkQgyM)<br/>*Click to watch a complete overview of QiTech Control* |
Operating System
## Repository Structure
- `/nixos`: Custom Linux with realtime kernel & preconfigured for touchscreens.
- **`/electron`** - React + Electron frontend
- **`/server`** - Rust backend implementing machine logic
- **`/ethercat-hal`** - Hardware abstraction layer for EtherCAT devices
- **`/control-core`** - Core control logic
- **`/nixos`** - Custom Linux OS with realtime kernel
Other
## Technology Stack
- `/docs`: Documentation for the project.
**Backend:** Rust with [Ethercrab](https://github.com/ethercrab-rs/ethercrab) for EtherCAT, [SocketIO](https://socket.io/) for real-time communication, [axum](https://docs.rs/axum/latest/axum/) for REST API
# Technology Choices
**Frontend:** [Electron](https://www.electronjs.org/) + [React](https://react.dev/) with [Shadcn](https://ui.shadcn.com/) components and [Tailwind](https://tailwindcss.com/) styling
## Backend
To interface with Beckhoff and other EtherCAT devices we need an EtherCAT master stoftware. Possibilities are [PySOEM](https://github.com/bnjmnp/pysoem) (Python), [SOEM](https://github.com/OpenEtherCATsociety/SOEM) (C) and [Ethercrab](https://github.com/ethercrab-rs/ethercrab) (Rust). For realtime operation only C and Rust are suitable. We chose Rust because of safety and confidence in the written code.
## Hardware Examples
[SocketIO](https://socket.io/) was chosen for performant event driven communication from the backend to the server. But we still use REST with [axum](https://docs.rs/axum/latest/axum/) for the communication thet benefits from the request/response model.
QiTech Control supports a wide range of EtherCAT hardware from WAGO and Beckhoff. We provide complete step-by-step tutorials with wiring diagrams and software configuration for many common modules.
We use [Smol](https://github.com/smol-rs/smol) for EtherCAT IO in the control loop for it's performance and [Tokio](https://tokio.rs/) for server IO because of it's ecosystem and maturity.
**[View all hardware examples and tutorials on the wiki](https://github.com/qitechgmbh/control/wiki/Hardware-Examples)**
## Frontend
Quick links to popular examples:
- [EL2004 - LED Control](https://github.com/qitechgmbh/control/wiki/Minimal-Example-El2004) (simplest setup for beginners)
- [EL3021 - Analog Input](https://github.com/qitechgmbh/control/wiki/Minimal-Example-El3021)
- [EL7031 - Stepper Motor Control](https://github.com/qitechgmbh/control/wiki/Minimal-Example-El7031-Motor)
- [WAGO 750-402 - Digital Input](https://github.com/qitechgmbh/control/wiki/Minimal-Example-Wago750-402)
- [WAGO 750-455 - Analog Input](https://github.com/qitechgmbh/control/wiki/Minimal-Example-Wago-750-455)
We could combine the code of the frontend and backend using [Doxius](https://dioxuslabs.com/) but it lacks good Linux support. We chose [Electron](https://www.electronjs.org/) with [React](https://react.dev/) for it's maturity and ecosystem. For the UI we use [Shadcn](https://ui.shadcn.com/) components and [Tailwind](https://tailwindcss.com/) for styling. For routing we use [TanStack Router](https://tanstack.com/router/v1).
## QiTech Machines
# Dev Setup
QiTech Control powers 10+ production machines for filament extrusion, winding, measurement, and material processing.
[Developer Documentation](./docs/developer-docs/)
**[View complete machine catalog on the wiki](https://github.com/qitechgmbh/control/wiki/QiTech-Machines)**
## Backend
- Rust stable 1.86^ toolchain (install via [rustup](https://rustup.rs/))
- `rust-analyzer` extension for VSCode
- Set your interface in `server/src/ethercat/init.rs` like `en10`
- Connect a Beckhoff EK1100 to your interface
- run `cd server && cargo run` to start the server (localhost:3001)
## Contributing
## Frontend
This is an open-source project. Contributions are welcome! Please see the [wiki](https://github.com/qitechgmbh/control/wiki) for development guidelines.
- nodejs and npm installed
- run `cd electron && npm i && npm run start` to start the frontend
## License
# Machines
| Machine Type | Version | Release Date | Description | Change to Previous Version | Vendor ID | Machine ID | Implemented | Docs |
| ------------ | ------- | ------------ | --------------------------- | ------------------------------------------------------ | -------------------------- | ---------- | ----------- | ------------------------------- |
| Winder | V1 | ??? | Winding Filaments & Similar | - | 1 (Qitech Industries GmbH) | 1 | Reserved | - |
| Winder | V2 | 2025 | Winding Filaments & Similar | Reengineered Traverse | 1 (Qitech Industries GmbH) | 2 | Yes | [](./docs/machines/winder-1.md) |
| Extruder | V1 | ??? | Single Screw Extruder | - | 1 (Qitech Industries GmbH) | 3 | Reserved | - |
| Extruder | V2 | 2025 | Single Screw Extruder | PT100 Thermometers, Optional Additional Heating Zone 4 | 1 (Qitech Industries GmbH) | 4 | Yes | |
| Waterway | V1 | 2025 | Filament Water Cooling | - | 1 (Qitech Industries GmbH) | 5 | In Progress | |
| Laser | V1 | ??? | Diameter Measuring Laser | - | 1 (Qitech Industries GmbH) | 6 | Yes | |
| Mock | - | ??? | Mock Machine for Testing | - | 1 (Qitech Industries GmbH) | 7 | Yes | - |
# More Docs
- [x] [Architecture & Data Flow](./docs/architecture-overview.md)
- [x] Example Winder V2
- [ ] Electron
- Folder Structure
- Routing with TanStack Router
- Design with Tailwind & Shadcn
- ...
- [ ] Interfacing with Electron/Server
- [ ] SocketIO
- Machine Namespace
- Main Namespace
- [ ] REST
- Machine Mutations
- Write Device Identification
- Server
- [x] [Threading](./docs/control-loop.md)
- [x] [Logging](./docs/logging.md)
- [ ] Control Loop Setup
- Control Loop Thread
- [ ] realtime
- Maindevice
- Group
- Extracting Device Identifications
- Identifying Groups
- Validating Machines
- Run Control Loop
- [x] [Control Loop](./docs/control-loop.md)
- [x] [Machine/Device Identification](./docs/identification.md)
- [ ] Machines
- When to create a new Machine?
- Versioning
- Code sharing
- Creating/Validating a Machine
- Validation
- Configuration
- [ ] Machine Implementation Guide
- Link: How to create a Device
- Link: How to create an Actor
- Link: How to create a Machine
- API (SocketIO + REST)
- Creation/Validation Logic
- Optional/Mandatory Devices
- Validate Devices
- Business Logic
- Link: How to create Machine Abstraction (Like Traverse/Puller/...)
- Forward `act` in winder.
- [ ] Control Core
- [x] [Actors](./docs/actors.md)
- [ ] SocketIO
- Namespaces & Caching
- Joining leaving namespaces
- NamespaceId
- Caching
- Serverside Caching
- Clientside Caching
- [ ] REST
- [x] Ethercat HAL
- [x] [Devices](./docs/devices.md)
- [x] [Configuration (CoE)](./docs/coe.md)
- [x] [IO](./docs/io.md)
- [x] [PDO](./docs/pdo.md)
- [x] [Ethercat Basics](./docs/ethercat-basics.md)
- [x] [NixOS Operating System](./docs/nixos/README.md)
See [LICENSE](LICENSE) file for details.
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
set -euo pipefail
BUILD_DIR="${BUILD_DIR:-target}"
CSV_FILE="${CSV_FILE:-compile_metrics.csv}"
DEFAULT_PKG="server"
if [ "$#" -eq 0 ]; then
set -- cargo build
fi
if [ "${1:-}" = "cargo" ] && [ "${2:-}" = "build" ]; then
cargo clean
BUILD_CMD=(cargo rustc -p "$DEFAULT_PKG")
shift 2
BUILD_CMD+=("$@")
BUILD_CMD+=(-- --emit=llvm-ir -C debuginfo=0)
else
BUILD_CMD=("$@")
fi
compile_start_ns=$(date +%s%N)
"${BUILD_CMD[@]}"
compile_end_ns=$(date +%s%N)
compile_ms=$(( (compile_end_ns - compile_start_ns) / 1000000 ))
if [ -d "$BUILD_DIR" ]; then
llvm_ir_lines=$(
find "$BUILD_DIR" -type f -name '*.ll' -print0 \
| xargs -0 cat \
| wc -l
)
else
llvm_ir_lines=0
fi
bin_path=""
if [ -n "${BINARY_PATH:-}" ] && [ -f "$BINARY_PATH" ]; then
bin_path="$BINARY_PATH"
elif [ -f "./target/release/server" ]; then
bin_path="./target/release/server"
elif [ -f "./target/debug/server" ]; then
bin_path="./target/debug/server"
elif [ -f "./server/target/release/server" ]; then
bin_path="./server/target/release/server"
elif [ -f "./server/target/debug/server" ]; then
bin_path="./server/target/debug/server"
fi
if [ -n "$bin_path" ]; then
binary_size_bytes=$(stat -c '%s' "$bin_path")
else
binary_size_bytes=0
fi
finished_timestamp_ms=$(($(date +%s%N) / 1000000))
finished_datetime=$(date -Iseconds)
if [ ! -f "$CSV_FILE" ]; then
echo "datetime,finished_timestamp_ms,compile_ms,llvm_ir_lines,binary_size_bytes" > "$CSV_FILE"
fi
echo "${finished_datetime},${finished_timestamp_ms},${compile_ms},${llvm_ir_lines},${binary_size_bytes}" \
>> "$CSV_FILE"
echo "Metrics recorded in $CSV_FILE"
+4 -6
View File
@@ -9,21 +9,19 @@ workspace = true
[dependencies]
anyhow = "1.0.100"
axum = "0.8.6"
axum = "0.8.8"
ethercat_hal = { path = "../ethercat-hal" }
ethercrab = "0.6"
units = { path = "../units" }
ethercrab = { git = "https://github.com/v-morlock/ethercrab", branch = "main" }
interfaces = "0.0.9"
serde = "1.0.219"
serde_json = "1.0.143"
serialport = "4.7.3"
smol = "2.0.2"
socketioxide = "0.17.2"
uom = { version = "0.36.0", default-features = false, features = ["f64"] }
bitvec = { version = "1.0.1", features = ["alloc"] }
crc = "3.3.0"
serial = "0.4.0"
tracing = "0.1.41"
tracing = "0.1.44"
erased-serde = "0.4.8"
core_affinity = "0.8.3"
control_core_derive = { version = "0.1.0", path = "../control-core-derive" }
@@ -1,6 +1,6 @@
use std::time::Instant;
use uom::si::{
use units::{
angular_acceleration::radian_per_second_squared,
angular_velocity::radian_per_second,
f64::{AngularAcceleration, AngularVelocity},
@@ -1,6 +1,6 @@
use std::time::Instant;
use uom::si::{
use units::{
acceleration::meter_per_second_squared,
f64::{Acceleration, Velocity},
velocity::meter_per_second,
@@ -1,6 +1,6 @@
use std::time::Instant;
use uom::si::{
use units::{
angle::radian,
angular_acceleration::radian_per_second_squared,
angular_velocity::radian_per_second,
@@ -199,8 +199,8 @@ impl AngularAccelerationPositionController {
/// # Example
/// ```ignore
/// use control_core::controllers::second_degree_motion::angular_acceleration_position_controller::AngularAccelerationPositionController;
/// use uom::si::f64::Angle;
/// use uom::si::angle::degree;
/// use units::f64::Angle;
/// use units::angle::degree;
///
/// let mut controller = AngularAccelerationPositionController::new_simple(
/// Some(Angle::new::<degree>(180.0)),
@@ -1,6 +1,6 @@
use std::time::Instant;
use uom::si::{
use units::{
angular_acceleration::radian_per_second_squared,
angular_jerk::radian_per_second_cubed,
angular_velocity::radian_per_second,
@@ -23,7 +23,7 @@ use super::jerk_speed_controller::JerkSpeedController;
///
/// # Example
/// ```ignore
/// use uom::si::{
/// use units::{
/// angular_velocity::revolution_per_minute,
/// angular_acceleration::revolution_per_minute_per_second,
/// angular_jerk::revolution_per_minute_per_second_squared,
@@ -65,7 +65,7 @@ impl AngularJerkSpeedController {
///
/// # Example
/// ```ignore
/// use uom::si::{
/// use units::{
/// angular_velocity::revolution_per_minute,
/// angular_acceleration::revolution_per_minute_per_second,
/// angular_jerk::revolution_per_minute_per_second_squared,
@@ -127,7 +127,7 @@ impl AngularJerkSpeedController {
///
/// # Example
/// ```ignore
/// use uom::si::{
/// use units::{
/// angular_velocity::revolution_per_minute,
/// angular_acceleration::revolution_per_minute_per_second,
/// angular_jerk::revolution_per_minute_per_second_squared,
@@ -185,7 +185,7 @@ impl AngularJerkSpeedController {
/// # Example
/// ```ignore
/// use std::time::Instant;
/// use uom::si::{angular_velocity::revolution_per_minute, f64::AngularVelocity};
/// use units::{angular_velocity::revolution_per_minute, f64::AngularVelocity};
///
/// let mut controller = AngularJerkSpeedController::new_simple(/* ... */);
/// let target = AngularVelocity::new::<revolution_per_minute>(1500.0);
@@ -1,6 +1,6 @@
use std::time::Instant;
use uom::si::{
use units::{
acceleration::meter_per_second_squared,
f64::{Acceleration, Length, Velocity},
length::meter,
@@ -22,8 +22,8 @@ use super::acceleration_position_controller::{
///
/// # Example
/// ```ignore
/// use uom::si::{length::meter, velocity::meter_per_second, acceleration::meter_per_second_squared};
/// use uom::si::f64::{Length, Velocity, Acceleration};
/// use units::{length::meter, velocity::meter_per_second, acceleration::meter_per_second_squared};
/// use units::f64::{Length, Velocity, Acceleration};
///
/// let position_limit = Length::new::<meter>(10.0);
/// let max_speed = Velocity::new::<meter_per_second>(2.0);
@@ -101,8 +101,8 @@ impl LinearAccelerationPositionController {
///
/// # Example
/// ```ignore
/// use uom::si::{length::meter, velocity::meter_per_second, acceleration::meter_per_second_squared};
/// use uom::si::f64::{Length, Velocity, Acceleration};
/// use units::{length::meter, velocity::meter_per_second, acceleration::meter_per_second_squared};
/// use units::f64::{Length, Velocity, Acceleration};
///
/// let position_limit = Length::new::<meter>(5.0); // Creates [-5.0, +5.0] meter range
/// let max_speed = Velocity::new::<meter_per_second>(1.0); // Creates [-1.0, +1.0] m/s range
@@ -150,7 +150,7 @@ impl LinearAccelerationPositionController {
/// # Example
/// ```ignore
/// use std::time::Instant;
/// use uom::si::{length::meter, f64::Length};
/// use units::{length::meter, f64::Length};
///
/// let mut controller = LinearAccelerationPositionController::new_simple(
/// Some(Length::new::<meter>(10.0)),
@@ -375,7 +375,7 @@ impl LinearAccelerationPositionController {
///
/// # Example
/// ```ignore
/// use uom::si::{length::meter, velocity::meter_per_second, f64::{Length, Velocity}};
/// use units::{length::meter, velocity::meter_per_second, f64::{Length, Velocity}};
///
/// let new_position = Length::new::<meter>(0.0);
///
@@ -1,6 +1,6 @@
use std::time::Instant;
use uom::si::{
use units::{
acceleration::meter_per_second_squared,
f64::{Acceleration, Jerk, Velocity},
jerk::meter_per_second_cubed,
@@ -23,7 +23,7 @@ use super::jerk_speed_controller::JerkSpeedController;
///
/// # Example
/// ```ignore
/// use uom::si::{
/// use units::{
/// velocity::meter_per_second,
/// acceleration::meter_per_second_squared,
/// jerk::meter_per_second_cubed,
@@ -65,7 +65,7 @@ impl LinearJerkSpeedController {
///
/// # Example
/// ```ignore
/// use uom::si::{
/// use units::{
/// velocity::meter_per_second,
/// acceleration::meter_per_second_squared,
/// jerk::meter_per_second_cubed,
@@ -127,7 +127,7 @@ impl LinearJerkSpeedController {
///
/// # Example
/// ```ignore
/// use uom::si::{
/// use units::{
/// velocity::meter_per_second,
/// acceleration::meter_per_second_squared,
/// jerk::meter_per_second_cubed,
@@ -1,4 +1,4 @@
use uom::si::{angle::radian, f64::Angle};
use units::{angle::radian, f64::Angle};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AngleConverter {
@@ -1,4 +1,4 @@
use uom::si::{
use units::{
angle::revolution,
angular_acceleration::radian_per_second_squared,
angular_velocity::revolution_per_second,
@@ -90,7 +90,7 @@ mod tests {
use super::*;
use approx::assert_relative_eq;
use std::f64::EPSILON;
use uom::si::{
use units::{
angle::degree, angular_acceleration::degree_per_second_squared,
angular_velocity::degree_per_second,
};
@@ -14,14 +14,14 @@
//!
//! ```rust
//! use control_core::converters::circular_converter::CircularConverter;
//! use uom::si::{f64::{Length, Velocity}, length::meter, velocity::meter_per_second};
//! use units::{f64::{Length, Velocity}, length::meter, velocity::meter_per_second};
//!
//! let converter = CircularConverter::from_radius(Length::new::<meter>(0.1));
//! let velocity = Velocity::new::<meter_per_second>(1.0);
//! let angular_velocity = converter.linear_to_angular_velocity(velocity);
//! ```
use uom::si::{
use units::{
acceleration::meter_per_second_squared,
angle::radian,
angular_acceleration::radian_per_second_squared,
@@ -199,7 +199,7 @@ impl CircularConverter {
#[cfg(test)]
mod tests {
use super::*;
use uom::si::length::centimeter;
use units::length::centimeter;
#[test]
fn test_constructors() {
@@ -1,4 +1,4 @@
use uom::si::{
use units::{
angle::revolution,
angular_velocity::revolution_per_second,
f64::{Acceleration, Angle, AngularAcceleration, AngularVelocity, Length, Velocity},
@@ -251,7 +251,7 @@ mod tests {
use super::*;
use approx::assert_relative_eq;
use std::f64::EPSILON;
use uom::si::{
use units::{
acceleration::meter_per_second_squared, angular_acceleration::radian_per_second_squared,
length::meter, velocity::meter_per_second,
};
@@ -1,10 +1,9 @@
use std::{process::Command, sync::Arc, time::Duration};
use ethercrab::{
MainDevice, MainDeviceConfig, PduStorage, Timeouts,
std::{ethercat_now, tx_rx_task},
};
use interfaces::Interface;
use std::{process::Command, sync::Arc, time::Duration};
// Constants for EtherCAT configuration
const IFACE_DISCOVERY_MAX_SUBDEVICES: usize = 16; // Must be power of 2 > 1
@@ -92,9 +91,6 @@ pub async fn discover_ethercat_interface() -> Result<String, anyhow::Error> {
let mut interface: Option<&str> = None;
for i in 0..interfaces.len() {
// Avoid NetworkManager hangups
set_interface_managed(&interfaces[i].name, false);
match test_interface(&interfaces[i].name) {
Ok(_) => {
// if interface found with ethercat Exit early, we expect only one interface with ethercat
@@ -107,7 +103,7 @@ pub async fn discover_ethercat_interface() -> Result<String, anyhow::Error> {
for i in 0..interfaces.len() {
if interface.is_some() && interface.unwrap() == &interfaces[i].name {
continue;
set_interface_managed(&interfaces[i].name, false);
} else {
set_interface_managed(&interfaces[i].name, true);
}
-1
View File
@@ -1,2 +1 @@
pub mod eeprom_identification;
pub mod interface_discovery;
+65
View File
@@ -0,0 +1,65 @@
use std::process::Command;
use anyhow::Result;
use interfaces::Interface;
pub mod modbus_tcp_discovery;
/// Returns true if the given network interface is Ethernet.
/// Prevents testing wlan,loopback and other non ethernet devices
fn is_ethernet(interface: &str) -> std::io::Result<bool> {
#[cfg(target_os = "linux")]
{
let base_path = format!("/sys/class/net/{}", interface);
let type_path = std::path::Path::new(&base_path).join("type");
let iface_type = std::fs::read_to_string(&type_path)?.trim().to_string();
// 1 means Ethernet
let reports_as_ethernet = iface_type == "1";
// Double-check that it's not a wireless interface
let uevent_path = std::path::Path::new(&base_path).join("uevent");
let uevent = std::fs::read_to_string(&uevent_path).unwrap_or_default();
// If "DEVTYPE=wlan" appears, it's lying
let actually_wifi = uevent.contains("DEVTYPE=wlan");
Ok(reports_as_ethernet && !actually_wifi)
}
#[cfg(not(target_os = "linux"))]
return Ok(true);
}
pub fn get_interfaces() -> Result<Vec<Interface>> {
let vec = Interface::get_all()
.map_err(|e| anyhow::anyhow!("Failed to get network interfaces: {}", e))?
.into_iter()
.filter(|iface| {
iface.is_up()
&& iface.is_running()
&& !iface.is_loopback()
&& is_ethernet(&iface.name).unwrap_or(false)
&& !iface.name.starts_with("bridge")
&& !iface.name.starts_with("utun") // Exclude tunnel interfaces
&& !iface.name.starts_with("awdl") // Exclude Apple Wireless Direct Link
&& !iface.name.starts_with("anpi") // Exclude Apple Network Privacy Interface
&& !iface.name.starts_with("llw") // Exclude Apple low-latency WLAN
})
.collect::<Vec<_>>();
Ok(vec)
}
/// Sets a network interface to unmanaged by NetworkManager.
/// Returns true if the command succeeded.
pub fn set_interface_managed(interface: &str, managed: bool) -> bool {
let managed_str = match managed {
true => "yes",
false => "no",
};
tracing::info!(
"set_interface_managed for {} managed was set to: {}",
interface,
managed_str
);
let status = Command::new("nmcli")
.args(["dev", "set", interface, "managed", managed_str])
.status();
matches!(status, Ok(s) if s.success())
}
@@ -0,0 +1,78 @@
use crate::ethernet::get_interfaces;
use crate::{futures::FutureIteratorExt, modbus::tcp::ModbusTcpDevice};
use anyhow::{Result, bail};
use interfaces::Interface;
use std::cmp::min;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
pub struct ModbusTcpProbe {
pub addr: SocketAddr,
pub serial: u16,
}
pub async fn probe_modbus_tcp() -> Vec<ModbusTcpProbe> {
let interfaces = match get_interfaces() {
Ok(x) => x,
Err(_) => return vec![],
};
interfaces
.into_iter()
.map(probe_modbus_tcp_addresses)
.join_all()
.await
.into_iter()
.flatten()
.collect()
}
async fn probe_modbus_tcp_addresses(interface: Interface) -> Vec<ModbusTcpProbe> {
let out: Vec<ModbusTcpProbe> = interface
.addresses
.iter()
.filter_map(|addr| {
let a = addr.addr.map(|a| a.ip());
let m = addr.mask.map(|a| a.ip());
match (a, m) {
(Some(IpAddr::V4(addr)), Some(IpAddr::V4(mask))) => Some((addr, mask)),
_ => None,
}
})
.flat_map(|(addr, mask)| {
let a = u32::from(addr);
let m = u32::from(mask);
let network = a & m;
let prefix = min(24, m.count_ones()); // mask → prefix length
let size = 1u32 << (32 - prefix); // number of addresses
(0..size).map(move |i| SocketAddr::new(Ipv4Addr::from(network + i).into(), 502))
})
.map(|addr| smol::spawn(ping_modbus_device(addr)))
.join_all()
.await
.into_iter()
.filter_map(|x| x.ok())
.collect();
out
}
async fn ping_modbus_device(addr: SocketAddr) -> Result<ModbusTcpProbe> {
let mut device = ModbusTcpDevice::new(addr).await?;
let module_number1 = device.get_u32(0x2).await?;
let module_number2 = device.get_u32(0x4).await?;
if module_number1 != 0x2787_2144 || module_number2 != 0x0000_0000 {
bail!("Unknown modbus tcp device!");
}
let serial = device.get_u32(0x000A).await?;
Ok(ModbusTcpProbe {
addr,
serial: serial as u16,
})
}
+26
View File
@@ -0,0 +1,26 @@
use core::future::Future;
// Extension trait for iterators of futures
pub trait FutureIteratorExt<F>: Iterator<Item = F> + Sized
where
F: Future,
{
/// Await all futures and return their outputs
fn join_all(self) -> impl Future<Output = Vec<F::Output>>;
}
impl<I, F> FutureIteratorExt<F> for I
where
I: Iterator<Item = F> + Sized,
F: Future,
{
async fn join_all(self) -> Vec<F::Output> {
let mut out = Vec::new();
for fut in self {
out.push(fut.await);
}
out
}
}
+2 -6
View File
@@ -2,19 +2,15 @@ pub mod controllers;
pub mod converters;
pub mod downcast;
pub mod ethercat;
pub mod ethernet;
pub mod futures;
pub mod helpers;
pub mod irq_handling;
pub mod machines;
pub mod modbus;
pub mod realtime;
pub mod rest;
pub mod serial;
pub mod socketio;
pub mod transmission;
pub mod uom_extensions;
#[cfg(feature = "video-streaming")]
pub mod video_streaming;
#[macro_use]
extern crate uom;
-27
View File
@@ -1,27 +0,0 @@
use serde_json::Value;
use smol::lock::Mutex;
use std::sync::Arc;
use crate::socketio::namespace::Namespace;
pub trait MachineApi {
fn api_mutate(&mut self, value: Value) -> Result<(), anyhow::Error>;
fn api_event_namespace(&mut self) -> Arc<Mutex<Namespace>>;
/// Returns a list of available video stream identifiers for this machine
#[cfg(feature = "video-streaming")]
fn api_video_streams(&self) -> Vec<String> {
Vec::new()
}
/// Get a video stream receiver for the specified stream ID
/// Returns None if the stream doesn't exist
#[cfg(feature = "video-streaming")]
fn api_get_video_stream(
&mut self,
stream_id: &str,
) -> Option<tokio::sync::broadcast::Receiver<Vec<u8>>> {
let _ = stream_id;
None
}
}
-208
View File
@@ -1,208 +0,0 @@
use crate::downcast::Downcast;
use crate::machines::identification::MachineIdentificationUnique;
use crate::machines::manager::MachineManager;
use crate::socketio::{event::GenericEvent, namespace::Namespace};
use serde::Serialize;
use smol::block_on;
use smol::lock::RwLock;
use std::sync::Arc;
use std::sync::Weak;
use super::Machine;
use anyhow::anyhow;
use smol::{channel::Sender, lock::Mutex};
use socketioxide::extract::SocketRef;
#[derive(Debug)]
pub enum MachineConnection<M: Machine + ?Sized>
where
Self: Sized,
{
Error(anyhow::Error),
Disconnected,
Connected(Arc<Mutex<M>>),
}
#[derive(Debug)]
pub struct MachineSlot<M: Machine + ?Sized>
where
Self: Sized,
{
pub machine_connection: MachineConnection<M>,
pub namespace: Arc<Mutex<Namespace>>,
}
pub type MachineConnectionGeneric = MachineConnection<dyn Machine>;
pub type MachineSlotGeneric = MachineSlot<dyn Machine>;
impl<M: Machine> Clone for MachineConnection<M> {
fn clone(&self) -> Self {
match self {
Self::Connected(m) => Self::Connected(m.clone()),
Self::Error(e) => Self::Error(anyhow!(e.to_string())),
Self::Disconnected => Self::Disconnected,
}
}
}
impl<M: Machine + ?Sized> MachineConnection<M> {
pub fn to_error(&self) -> Option<anyhow::Error> {
match self {
MachineConnection::Error(err) => Some(anyhow!(err.to_string())),
Self::Disconnected => Some(anyhow!("Machine is disconnected")),
_ => None,
}
}
pub fn to_machine(&self) -> Option<Arc<Mutex<M>>> {
match self {
Self::Connected(machine) => Some(machine.clone()),
_ => None,
}
}
}
impl<M: Machine + ?Sized> MachineSlot<M> {
pub fn new(socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>) -> Self {
let namespace = Namespace::new(socket_queue_tx);
Self {
machine_connection: MachineConnection::Disconnected,
namespace: Arc::new(Mutex::new(namespace)),
}
}
pub const fn is_connected(&self) -> bool {
matches!(self.machine_connection, MachineConnection::Connected(_))
}
}
pub trait CrossConnectableMachine<
F: CrossConnectableMachine<F, T>,
T: CrossConnectableMachine<T, F>,
>: Machine
{
fn get_cross_connection(&mut self) -> &mut MachineCrossConnection<F, T>;
}
#[derive(Debug)]
pub struct MachineCrossConnection<
F: CrossConnectableMachine<F, T>,
T: CrossConnectableMachine<T, F>,
> {
machine_manager: Weak<RwLock<MachineManager>>,
machine_identification_unique: MachineIdentificationUnique,
connected_machine: Weak<Mutex<MachineSlot<T>>>,
_make_compiler_happy: std::marker::PhantomData<F>, // This marks tells the typechecker, that we
// need F in our type arguemnts. We are ok with
// it being used only in a fake field. Without
// F being fixed, we would not be anble to implement
// reverse connecting and disconnecting properly,
// as the type on the other side would decay
// to an unknown type. See also https://github.com/qitechgmbh/control/pull/625#discussion_r2379566315
}
#[derive(Serialize, Debug, Clone, Default)]
pub struct MachineCrossConnectionState {
/// Connected Machine
pub machine_identification_unique: Option<MachineIdentificationUnique>,
pub is_available: bool,
}
impl<F: CrossConnectableMachine<F, T>, T: CrossConnectableMachine<T, F>>
MachineCrossConnection<F, T>
{
pub fn new(
manager: Weak<RwLock<MachineManager>>,
machine_identification_unique: &MachineIdentificationUnique,
) -> Self {
Self {
machine_manager: manager,
connected_machine: Weak::new(),
machine_identification_unique: machine_identification_unique.clone(),
_make_compiler_happy: std::marker::PhantomData,
}
}
pub fn set_connected_machine(
&mut self,
machine_identification_unique: &MachineIdentificationUnique,
) {
if let Some(machine_manager) = self.machine_manager.upgrade() {
let machine_manager = block_on(machine_manager.read());
let slot: Arc<Mutex<MachineSlotGeneric>> =
match machine_manager.get(&machine_identification_unique) {
Some(c) => c,
None => return,
};
let slot: Arc<Mutex<MachineSlot<T>>> = match Downcast::downcast(&slot) {
Ok(s) => s,
Err(_) => return,
};
self.connected_machine = Arc::downgrade(&slot);
} else {
panic!("The machine manager has died, cannot do anything at this point");
}
}
pub fn reverse_connect(&self) {
if let Some(slot) = self.connected_machine.upgrade() {
let slot = &mut slot.lock_blocking();
if let MachineConnection::Connected(machine) = &slot.machine_connection {
let mut machine = machine.lock_blocking();
let cross_connection = machine.get_cross_connection();
if !cross_connection.is_connected() {
cross_connection.set_connected_machine(&self.machine_identification_unique);
}
};
}
}
pub fn disconnect(&mut self) {
self.connected_machine = Weak::new();
}
pub fn reverse_disconnect(&mut self) {
if let Some(slot) = self.connected_machine.upgrade() {
let slot = slot.lock_blocking();
if let MachineConnection::Connected(machine) = &slot.machine_connection {
let mut machine = machine.lock_blocking();
let cross_connection = machine.get_cross_connection();
cross_connection.disconnect();
};
}
}
pub fn is_connected(&self) -> bool {
let arc = self.connected_machine.upgrade();
arc.is_some()
}
pub fn to_state(&self) -> MachineCrossConnectionState {
if let Some(slot) = self.connected_machine.upgrade() {
let connection = &slot.lock_blocking().machine_connection;
if let MachineConnection::Connected(machine) = connection {
let machine = machine.lock_blocking();
return MachineCrossConnectionState {
machine_identification_unique: Some(
machine.get_machine_identification_unique(),
),
is_available: true,
};
}
}
MachineCrossConnectionState {
machine_identification_unique: None,
is_available: false,
}
}
}
-113
View File
@@ -1,113 +0,0 @@
use std::fmt::Display;
use serde::Deserialize;
use serde::Serialize;
/// Identifies a spacifi machine
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct MachineIdentificationUnique {
pub machine_identification: MachineIdentification,
pub serial: u16,
}
impl MachineIdentificationUnique {
/// Check if values are non-zero
pub const fn is_valid(&self) -> bool {
self.machine_identification.is_valid() && self.serial != 0
}
}
impl Display for MachineIdentificationUnique {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}/{}/{}",
self.machine_identification.vendor, self.machine_identification.machine, self.serial
)
}
}
/// Identifies a machine
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct MachineIdentification {
pub vendor: u16,
pub machine: u16,
}
impl MachineIdentification {
/// Check if values are non-zero
pub const fn is_valid(&self) -> bool {
self.vendor != 0 && self.machine != 0
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeviceMachineIdentification {
pub machine_identification_unique: MachineIdentificationUnique,
pub role: u16,
}
impl DeviceMachineIdentification {
/// Check if values are non-zero
pub const fn is_valid(&self) -> bool {
self.machine_identification_unique.is_valid()
&& self
.machine_identification_unique
.machine_identification
.machine
!= 0
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeviceIdentification {
pub device_machine_identification: Option<DeviceMachineIdentification>,
pub device_hardware_identification: DeviceHardwareIdentification,
}
impl From<DeviceIdentificationIdentified> for DeviceIdentification {
fn from(value: DeviceIdentificationIdentified) -> Self {
Self {
device_machine_identification: Some(value.device_machine_identification),
device_hardware_identification: value.device_hardware_identification,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeviceIdentificationIdentified {
pub device_machine_identification: DeviceMachineIdentification,
pub device_hardware_identification: DeviceHardwareIdentification,
}
impl TryFrom<DeviceIdentification> for DeviceIdentificationIdentified {
type Error = anyhow::Error;
fn try_from(value: DeviceIdentification) -> Result<Self, Self::Error> {
let device_machine_identification =
value.device_machine_identification.ok_or(anyhow::anyhow!(
"[{}::DeviceIdentificationIdentified::try_from] No device machine identification",
module_path!()
))?;
Ok(Self {
device_machine_identification,
device_hardware_identification: value.device_hardware_identification,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum DeviceHardwareIdentification {
Ethercat(DeviceHardwareIdentificationEthercat),
Serial(DeviceHardwareIdentificationSerial),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeviceHardwareIdentificationEthercat {
pub subdevice_index: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeviceHardwareIdentificationSerial {
pub path: String,
}
-297
View File
@@ -1,297 +0,0 @@
use super::{
Machine,
identification::{DeviceIdentification, DeviceIdentificationIdentified},
new::MachineNewHardwareSerial,
registry::MachineRegistry,
};
use smol::{
channel::Sender,
lock::{Mutex, RwLock},
};
use socketioxide::extract::SocketRef;
use crate::{
machines::{
connection::{
MachineConnection, MachineConnectionGeneric, MachineSlot, MachineSlotGeneric,
},
identification::MachineIdentificationUnique,
new::{MachineNewHardware, MachineNewHardwareEthercat, MachineNewParams},
},
serial::SerialDevice,
socketio::event::GenericEvent,
};
use std::{
collections::HashMap,
sync::{Arc, Weak},
};
#[derive(Debug)]
pub struct MachineManager {
pub ethercat_machines: HashMap<MachineIdentificationUnique, Arc<Mutex<MachineSlotGeneric>>>,
pub serial_machines: HashMap<MachineIdentificationUnique, Arc<Mutex<MachineSlotGeneric>>>,
}
impl Default for MachineManager {
fn default() -> Self {
Self::new()
}
}
impl MachineManager {
pub fn new() -> Self {
Self {
ethercat_machines: HashMap::new(),
serial_machines: HashMap::new(),
}
}
pub fn set_ethercat_devices<const MAX_SUBDEVICES: usize, const MAX_PDI: usize>(
&mut self,
device_identifications: &Vec<DeviceIdentification>,
machine_registry: &MachineRegistry,
hardware: &MachineNewHardwareEthercat,
socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
machine_manager: Weak<RwLock<Self>>,
) {
// group devices by machine device identification
let device_grouping_result = group_devices_by_identification(device_identifications);
tracing::info!(
"Device Groups {}",
device_grouping_result.device_groups.len()
);
let machine_new_hardware = MachineNewHardware::Ethercat(hardware);
// iterate over all identified device groups but ignore unaffected machines
for device_group in device_grouping_result.device_groups.iter() {
// get the machine identification
let machine_identification_unique: MachineIdentificationUnique =
match device_group.first() {
Some(device_identification) => device_identification
.device_machine_identification
.machine_identification_unique
.clone(),
None => continue, // Skip this group if empty
};
let slot =
self.get_or_create_slot(socket_queue_tx.clone(), machine_identification_unique);
let mut slot = slot.lock_blocking();
// create the machine
let new_machine = machine_registry.new_machine(&MachineNewParams {
device_group,
hardware: &machine_new_hardware,
socket_queue_tx: socket_queue_tx.clone(),
machine_manager: machine_manager.clone(),
namespace: slot.namespace.clone(),
});
slot.machine_connection = match new_machine {
Err(err) => MachineConnectionGeneric::Error(err),
Ok(machine) => MachineConnectionGeneric::Connected(machine),
};
}
}
fn get_or_create_slot(
&mut self,
socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
machine_identification: MachineIdentificationUnique,
) -> Arc<Mutex<MachineSlotGeneric>> {
if let Some(slot) = self.get(&machine_identification) {
return slot;
}
let slot = Arc::new(Mutex::new(MachineSlot::new(socket_queue_tx)));
self.ethercat_machines
.insert(machine_identification, slot.clone());
return slot;
}
pub fn get(
&self,
machine_identification: &MachineIdentificationUnique,
) -> Option<Arc<Mutex<MachineSlotGeneric>>> {
self.ethercat_machines
.get(machine_identification)
.or_else(|| self.serial_machines.get(machine_identification))
.map(|x| x.clone())
}
pub fn add_serial_device(
&mut self,
device_identification: &DeviceIdentification,
device: Arc<RwLock<dyn SerialDevice>>,
machine_registry: &MachineRegistry,
socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
machine_manager: Weak<RwLock<Self>>,
) {
let hardware = MachineNewHardwareSerial { device };
let device_identification_identified: DeviceIdentificationIdentified =
device_identification
.clone()
.try_into()
.expect("Serial devices always have machine identification");
let machine_identification: MachineIdentificationUnique = device_identification_identified
.device_machine_identification
.machine_identification_unique
.clone();
let slot = self.get_or_create_slot(socket_queue_tx.clone(), machine_identification);
let mut slot = slot.lock_blocking();
let new_machine = machine_registry.new_machine(&MachineNewParams {
device_group: &vec![device_identification_identified.clone()],
hardware: &MachineNewHardware::Serial(&hardware),
socket_queue_tx,
machine_manager: machine_manager.clone(),
namespace: slot.namespace.clone(),
});
slot.machine_connection = match new_machine {
Err(err) => MachineConnectionGeneric::Error(err),
Ok(machine) => MachineConnectionGeneric::Connected(machine),
};
tracing::info!("Adding serial machine {:?}", slot);
}
pub fn remove_serial_device(&mut self, device_identification: &DeviceIdentification) {
let device_identification_identified: DeviceIdentificationIdentified =
device_identification
.clone()
.try_into()
.expect("Serial devices always have machine identification");
tracing::info!(
"Removing serial machine {:?}",
device_identification_identified
);
let machine_identification_unique = &device_identification_identified
.device_machine_identification
.machine_identification_unique;
if let Some(slot) = self.serial_machines.get(machine_identification_unique) {
let mut slot = slot.lock_blocking();
slot.machine_connection = MachineConnection::Disconnected;
}
}
pub fn get_ethercat_weak(
&self,
machine_identification: &MachineIdentificationUnique,
) -> Option<Weak<Mutex<dyn Machine>>> {
self.get_weak(&self.ethercat_machines, machine_identification)
}
pub fn get_serial_weak(
&self,
machine_identification: &MachineIdentificationUnique,
) -> Option<Weak<Mutex<dyn Machine>>> {
self.get_weak(&self.serial_machines, machine_identification)
}
fn get_weak(
&self,
machines: &HashMap<MachineIdentificationUnique, Arc<Mutex<MachineSlotGeneric>>>,
machine_identification: &MachineIdentificationUnique,
) -> Option<Weak<Mutex<dyn Machine>>> {
let slot = machines.get(machine_identification).clone();
return slot.and_then(|slot| {
let connection = &slot.lock_blocking().machine_connection;
match connection {
MachineConnectionGeneric::Error(_) => None,
MachineConnectionGeneric::Disconnected => None,
MachineConnectionGeneric::Connected(machine) => Some(Arc::downgrade(&machine)),
}
});
}
}
/// Groups devices by machine identification
///
/// Returns a DeviceGroupingResult containing:
/// - device_groups: a vector of devices grouped by machine identification
/// - unidentified_devices: a vector of devices that could not be identified
pub fn group_devices_by_identification(
device_identifications: &Vec<DeviceIdentification>,
) -> DeviceGroupingResult {
let mut device_groups: Vec<Vec<DeviceIdentificationIdentified>> = Vec::new();
let mut unidentified_devices: Vec<DeviceIdentification> = Vec::new();
for device_identification in device_identifications {
// if vendor or serial or machine is 0, it is not a valid machine device
if let Some(device_machine_identification) =
device_identification.device_machine_identification.as_ref()
{
if !device_machine_identification.is_valid() {
unidentified_devices.push(device_identification.clone());
continue;
}
} else {
unidentified_devices.push(device_identification.clone());
continue;
}
// scan over all deice groups
// get the first DeviceMachineIdentification
// compare and append to the group
let mut found = false;
for check_group in device_groups.iter_mut() {
// get first device in group
let first_device = check_group.first().expect("group to not be empty");
let first_device_machine_identification = &first_device
.device_machine_identification
.machine_identification_unique;
// chek if it has machine identification
if let Some(device_machine_identification) =
device_identification.device_machine_identification.as_ref()
{
// compare with the current device
if first_device_machine_identification
== &device_machine_identification.machine_identification_unique
{
let device_identification_identified = device_identification
.clone()
.try_into()
.expect("should have Some(DeviceMachineIdentification)");
check_group.push(device_identification_identified);
found = true;
break;
}
}
}
if !found {
let device_identification_identified = device_identification
.clone()
.try_into()
.expect("should have Some(DeviceMachineIdentification)");
device_groups.push(vec![device_identification_identified]);
}
}
DeviceGroupingResult {
device_groups,
unidentified_devices,
}
}
/// Structure to hold the result of grouping devices by identification
#[derive(Debug)]
pub struct DeviceGroupingResult {
/// Devices grouped by machine identification
pub device_groups: Vec<Vec<DeviceIdentificationIdentified>>,
/// Devices that could not be identified
pub unidentified_devices: Vec<DeviceIdentification>,
}
-84
View File
@@ -1,84 +0,0 @@
use std::collections::HashMap;
use std::sync::Arc;
use smol::lock::Mutex;
use crate::machines::connection::MachineSlotGeneric;
use super::{identification::MachineIdentificationUnique, manager::MachineManager};
pub enum MachineManagerIter<'a> {
EthercatMachines {
iter: std::collections::hash_map::Iter<
'a,
MachineIdentificationUnique,
Arc<Mutex<MachineSlotGeneric>>,
>,
},
SerialMachines {
iter: std::collections::hash_map::Iter<
'a,
MachineIdentificationUnique,
Arc<Mutex<MachineSlotGeneric>>,
>,
},
Done,
}
pub struct MachineManagerIterator<'a> {
iter: MachineManagerIter<'a>,
serial_machines: &'a HashMap<MachineIdentificationUnique, Arc<Mutex<MachineSlotGeneric>>>,
}
impl<'a> Iterator for MachineManagerIterator<'a> {
type Item = (
&'a MachineIdentificationUnique,
&'a Arc<Mutex<MachineSlotGeneric>>,
);
fn next(&mut self) -> Option<Self::Item> {
loop {
match &mut self.iter {
MachineManagerIter::EthercatMachines { iter } => {
// Try to get the next item from ethercat_machines
if let Some(item) = iter.next() {
return Some(item);
} else {
// If ethercat_machines is exhausted, move to serial_machines
self.iter = MachineManagerIter::SerialMachines {
iter: self.serial_machines.iter(),
};
// Continue to the next iteration of the loop
continue;
}
}
MachineManagerIter::SerialMachines { iter } => {
// Return the next item from serial_machines or None if exhausted
if let Some(item) = iter.next() {
return Some(item);
} else {
// If serial_machines is also exhausted, we're done
self.iter = MachineManagerIter::Done;
return None;
}
}
MachineManagerIter::Done => {
// We've exhausted both collections
return None;
}
}
}
}
}
impl MachineManager {
// Returns an iterator over all machines (both ethercat and serial)
pub fn iter(&self) -> MachineManagerIterator<'_> {
MachineManagerIterator {
iter: MachineManagerIter::EthercatMachines {
iter: self.ethercat_machines.iter(),
},
serial_machines: &self.serial_machines,
}
}
}
-23
View File
@@ -1,23 +0,0 @@
use new::MachineNewTrait;
use std::any::Any;
use std::fmt::Debug;
use crate::machines::{
api::MachineApi, identification::MachineIdentificationUnique, new::MachineAct,
};
pub mod api;
pub mod connection;
pub mod identification;
pub mod manager;
pub mod manager_iter;
pub mod new;
pub mod registry;
pub trait Machine: MachineAct + MachineNewTrait + MachineApi + Any + Debug + Send + Sync {
fn get_machine_identification_unique(&self) -> MachineIdentificationUnique;
}
pub trait AnyGetters: Any {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
-279
View File
@@ -1,279 +0,0 @@
use crate::{
machines::{identification::MachineIdentificationUnique, manager::MachineManager},
serial::SerialDevice,
socketio::namespace::Namespace,
};
use super::identification::DeviceIdentificationIdentified;
use anyhow::Error;
use ethercat_hal::{
devices::EthercatDevice, helpers::ethercrab_types::EthercrabSubDevicePreoperational,
};
use ethercrab::{SubDevice, SubDeviceRef};
use smol::{
channel::Sender,
lock::{Mutex, RwLock},
};
use socketioxide::extract::SocketRef;
use std::{
sync::{Arc, Weak},
time::Instant,
};
use crate::socketio::event::GenericEvent;
pub trait MachineNewTrait {
fn new(params: &MachineNewParams<'_, '_, '_, '_, '_, '_, '_>) -> Result<Self, Error>
where
Self: Sized;
}
pub trait MachineAct {
fn act(&mut self, now: Instant);
}
pub struct MachineNewParams<
'maindevice,
'subdevices,
'device_identifications_identified,
'ethercat_devices,
'machine_new_hardware_etehrcat,
'machine_new_hardware_serial,
'machine_new_hardware,
> where
'maindevice: 'machine_new_hardware,
'subdevices: 'machine_new_hardware,
'ethercat_devices: 'machine_new_hardware,
'machine_new_hardware_etehrcat: 'machine_new_hardware,
{
pub device_group: &'device_identifications_identified Vec<DeviceIdentificationIdentified>,
pub hardware: &'machine_new_hardware MachineNewHardware<
'maindevice,
'subdevices,
'ethercat_devices,
'machine_new_hardware_etehrcat,
'machine_new_hardware_serial,
>,
pub socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
pub machine_manager: Weak<RwLock<MachineManager>>,
pub namespace: Arc<Mutex<Namespace>>,
}
impl MachineNewParams<'_, '_, '_, '_, '_, '_, '_> {
pub fn get_machine_identification_unique(&self) -> MachineIdentificationUnique {
self.device_group
.first()
.expect("device group must have at least one device")
.device_machine_identification
.machine_identification_unique
.clone()
}
}
pub enum MachineNewHardware<
'maindevice,
'subdevices,
'ethercat_devices,
'machine_new_hardware_etehrcat,
'machine_new_hardware_serial,
> where
'maindevice: 'machine_new_hardware_etehrcat,
'subdevices: 'machine_new_hardware_etehrcat,
'ethercat_devices: 'machine_new_hardware_etehrcat,
{
Ethercat(
&'machine_new_hardware_etehrcat MachineNewHardwareEthercat<
'maindevice,
'subdevices,
'ethercat_devices,
>,
),
Serial(&'machine_new_hardware_serial MachineNewHardwareSerial),
}
pub struct MachineNewHardwareEthercat<'maindevice, 'subdevices, 'ethercat_devices> {
pub subdevices:
&'subdevices Vec<&'subdevices SubDeviceRef<'maindevice, &'subdevices SubDevice>>,
pub ethercat_devices: &'ethercat_devices Vec<Arc<RwLock<dyn EthercatDevice>>>,
}
pub struct MachineNewHardwareSerial {
pub device: Arc<RwLock<dyn SerialDevice>>,
}
// validates that all devices in the group have the same machine identification
pub fn validate_same_machine_identification_unique(
identified_device_group: &Vec<DeviceIdentificationIdentified>,
) -> Result<(), Error> {
let machine_identification_unique = &identified_device_group
.first()
.ok_or_else(|| {
anyhow::anyhow!(
"[{}::validate_same_machine_identification] No devices in group",
module_path!()
)
})?
.device_machine_identification
.machine_identification_unique;
for device in identified_device_group.iter() {
if device
.device_machine_identification
.machine_identification_unique
!= *machine_identification_unique
{
return Err(anyhow::anyhow!(
"[{}::validate_same_machine_identification] Different machine identifications",
module_path!()
));
}
}
Ok(())
}
/// validates that every role is unique
pub fn validate_no_role_dublicates(
identified_device_group: &Vec<DeviceIdentificationIdentified>,
) -> Result<(), Error> {
let mut roles = vec![];
for device in identified_device_group.iter() {
if roles.contains(&device.device_machine_identification.role) {
return Err(anyhow::anyhow!(
"[{}::validate_no_role_dublicates] Role dublicate",
module_path!(),
));
}
roles.push(device.device_machine_identification.role);
}
Ok(())
}
// Inside control_core::machines::new module:
pub fn get_device_identification_by_role(
identified_device_group: &Vec<DeviceIdentificationIdentified>,
role: u16,
) -> Result<&DeviceIdentificationIdentified, Error> {
for device in identified_device_group.iter() {
if device.device_machine_identification.role == role {
return Ok(device);
}
}
Err(anyhow::anyhow!(
"[{}::get_device_identification_by_role] Role {} not found",
module_path!(),
role
))
}
pub fn get_device_by_index<'maindevice>(
devices: &Vec<Arc<RwLock<dyn EthercatDevice>>>,
subdevice_index: usize,
) -> Result<Arc<RwLock<dyn EthercatDevice>>, Error> {
Ok(devices
.get(subdevice_index)
.ok_or(anyhow::anyhow!(
"[{}::get_device_by_index] Index {} out of bounds for devices",
module_path!(),
subdevice_index
))?
.clone())
}
pub fn get_subdevice_by_index<'subdevices, 'maindevice>(
subdevices: &'subdevices Vec<&EthercrabSubDevicePreoperational<'maindevice>>,
subdevice_index: usize,
) -> Result<&'subdevices EthercrabSubDevicePreoperational<'maindevice>, Error> {
Ok(subdevices.get(subdevice_index).ok_or(anyhow::anyhow!(
"Index {} out of bounds for subdevices",
subdevice_index
))?)
}
pub fn get_ethercat_device_by_index<'maindevice>(
ethercat_devices: &Vec<Arc<RwLock<dyn EthercatDevice>>>,
subdevice_index: usize,
) -> Result<Arc<RwLock<dyn EthercatDevice>>, Error> {
Ok(ethercat_devices
.get(subdevice_index)
.ok_or(anyhow::anyhow!(
"[{}::get_ethercat_device_by_index] Index {} out of bounds for ethercat devices",
module_path!(),
subdevice_index
))?
.clone())
}
#[cfg(test)]
mod tests {
use crate::machines::identification::{
DeviceHardwareIdentification, DeviceHardwareIdentificationEthercat,
DeviceMachineIdentification, MachineIdentification, MachineIdentificationUnique,
};
pub use super::*;
#[test]
fn test_get_device_identification_by_role() {
let device_identifications = vec![
// role 0
DeviceIdentificationIdentified {
device_machine_identification: DeviceMachineIdentification {
machine_identification_unique: MachineIdentificationUnique {
machine_identification: MachineIdentification {
vendor: 1,
machine: 1,
},
serial: 1,
},
role: 0,
},
device_hardware_identification: DeviceHardwareIdentification::Ethercat(
DeviceHardwareIdentificationEthercat { subdevice_index: 0 },
),
},
// role 1
DeviceIdentificationIdentified {
device_machine_identification: DeviceMachineIdentification {
machine_identification_unique: MachineIdentificationUnique {
machine_identification: MachineIdentification {
vendor: 2,
machine: 2,
},
serial: 2,
},
role: 2,
},
device_hardware_identification: DeviceHardwareIdentification::Ethercat(
DeviceHardwareIdentificationEthercat { subdevice_index: 1 },
),
},
// role 2
DeviceIdentificationIdentified {
device_machine_identification: DeviceMachineIdentification {
machine_identification_unique: MachineIdentificationUnique {
machine_identification: MachineIdentification {
vendor: 3,
machine: 3,
},
serial: 3,
},
role: 1,
},
device_hardware_identification: DeviceHardwareIdentification::Ethercat(
DeviceHardwareIdentificationEthercat { subdevice_index: 2 },
),
},
];
// search for role 0
let result = get_device_identification_by_role(&device_identifications, 0);
assert_eq!(result.unwrap().device_machine_identification.role, 0,);
// search for role 1
let result = get_device_identification_by_role(&device_identifications, 1);
assert_eq!(result.unwrap().device_machine_identification.role, 1,);
// search for role 2
let result = get_device_identification_by_role(&device_identifications, 2);
assert_eq!(result.unwrap().device_machine_identification.role, 2,);
}
}
-74
View File
@@ -1,74 +0,0 @@
use super::{Machine, identification::MachineIdentification, new::MachineNewParams};
use anyhow::Error;
use smol::lock::Mutex;
use std::{any::TypeId, collections::HashMap, sync::Arc};
pub type MachineNewClosure =
Box<dyn Fn(&MachineNewParams) -> Result<Arc<Mutex<dyn Machine>>, Error> + Send + Sync>;
pub struct MachineRegistry {
type_map: HashMap<TypeId, (MachineIdentification, MachineNewClosure)>,
}
impl Default for MachineRegistry {
fn default() -> Self {
Self::new()
}
}
impl MachineRegistry {
pub fn new() -> Self {
Self {
type_map: HashMap::new(),
}
}
pub fn register<T: Machine + 'static>(
&mut self,
machine_identficiation: MachineIdentification,
) {
self.type_map.insert(
TypeId::of::<T>(),
(
machine_identficiation.clone(),
// create a machine construction closure
Box::new(|machine_new_params| {
Ok(Arc::new(Mutex::new(T::new(machine_new_params)?)))
}),
),
);
}
pub fn new_machine(
&self,
machine_new_params: &MachineNewParams,
) -> Result<Arc<Mutex<dyn Machine>>, anyhow::Error> {
// get machiine identification
let device_identification =
&machine_new_params
.device_group
.first()
.ok_or(anyhow::anyhow!(
"[{}::MachineConstructor::new_machine] No device in group",
module_path!()
))?;
// find machine new function by comparing MachineIdentification
let (_, machine_new_closure) = self
.type_map
.values()
.find(|(mi, _)| {
mi == &device_identification
.device_machine_identification
.machine_identification_unique
.machine_identification
})
.ok_or(anyhow::anyhow!(
"[{}::MachineConstructor::new_machine] Machine not found",
module_path!()
))?;
// call machine new function by reference
(machine_new_closure)(machine_new_params)
}
}
+1
View File
@@ -1,4 +1,5 @@
pub mod modbus_serial_interface;
pub mod tcp;
use anyhow::Error;
use crc::{CRC_16_MODBUS, Crc};
+263
View File
@@ -0,0 +1,263 @@
use std::fmt::Debug;
use std::io::ErrorKind;
use std::net::SocketAddr;
use std::time::Duration;
use anyhow::Context;
use anyhow::Result;
use anyhow::bail;
use smol::Timer;
use smol::future::FutureExt;
use smol::io;
use smol::io::AsyncReadExt;
use smol::io::AsyncWriteExt;
use smol::net::TcpStream;
const PROTOCOL_ID: u16 = 0;
const UNIT_ID: u8 = 0;
const READ_HOLDING_FUNCTION_CODE: u8 = 3;
const WRITE_HOLDING_FUNCTION_CODE: u8 = 16;
const READ_HOLDING_LENGTH: u16 = 6; // Unit ID 00 + Func Code 03 + Start Address XXXX + Quantity XXXX = 6 bytes
const WRITE_HOLDING_LENGTH_WITHOUT_DATA: u16 = 7; // Unit ID 00 + Func Code 16 + Start Address XXXX + Quantity XXXX + Num Data Bytes XX = 7 bytes
struct Packet {
buf: Vec<u8>,
}
impl Packet {
pub const fn new() -> Self {
Self { buf: Vec::new() }
}
pub fn add_u8(&mut self, x: u8) {
self.buf.push(x);
}
pub fn add_u16(&mut self, x: u16) {
let buf = x.to_be_bytes();
self.buf.push(buf[0]);
self.buf.push(buf[1]);
}
pub fn as_bytes(&self) -> &[u8] {
&self.buf
}
}
pub struct ModbusTcpDevice {
stream: TcpStream,
transactions: u16,
}
impl Debug for ModbusTcpDevice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.stream.peer_addr() {
Ok(addr) => write!(f, "ModbusTcpDevice({:?})", addr),
Err(_) => std::fmt::Result::Err(std::fmt::Error),
}
}
}
impl ModbusTcpDevice {
pub async fn new(addr: SocketAddr) -> Result<Self> {
let timeout = async {
Timer::after(Duration::from_millis(10)).await;
Err(io::Error::from(ErrorKind::TimedOut))
};
let stream = TcpStream::connect(addr)
.or(timeout)
.await
.context("Could not connect to modbus device!")?;
Ok(Self {
stream,
transactions: 0,
})
}
async fn read_and_check_responce_header(&mut self, func_code: u8) -> Result<()> {
if self.read_u16().await? != self.transactions {
bail!("Modbus device sent unexpected transaction id!");
}
if self.read_u16().await? != PROTOCOL_ID {
bail!("Modbus device sent unexpected protocol id!");
}
let length = self.read_u16().await?;
if length < 2 {
bail!("Modbus device will send too few bytes to understand the response!");
}
if self.read_u8().await? != UNIT_ID {
bail!("Modbus device sent unexpected unit id!");
}
let func_code_res = self.read_u8().await?;
if func_code != func_code_res {
let error_code = self.read_u8().await?;
bail!(
"Modbus device sent unexpected function code 0x{:x}! It probably encountered an error. Error code: 0x{:x}",
func_code_res,
error_code
);
}
Ok(())
}
async fn send_read_holding_request(&mut self, addr: u16, count: u16) -> Result<u16> {
let mut packet = Packet::new();
self.transactions += 1;
packet.add_u16(self.transactions);
packet.add_u16(PROTOCOL_ID);
packet.add_u16(READ_HOLDING_LENGTH);
packet.add_u8(UNIT_ID);
packet.add_u8(READ_HOLDING_FUNCTION_CODE);
packet.add_u16(addr);
packet.add_u16(count);
self.send(packet).await?;
self.read_and_check_responce_header(READ_HOLDING_FUNCTION_CODE)
.await?;
let num_data_bytes = self.read_u8().await? as u16;
if count * 2 != num_data_bytes {
bail!(
"Modbus device wants to send only a portion of the requested range - UNIMPLEMENTED!"
);
}
Ok(num_data_bytes)
}
pub async fn get_holding_registers(&mut self, addr: u16, count: u16) -> Result<Vec<u16>> {
self.send_read_holding_request(addr, count).await?;
let mut res: Vec<u16> = Vec::new();
while res.len() < count as usize {
res.push(self.read_u16().await?);
}
Ok(res)
}
pub async fn set_holding_registers(&mut self, addr: u16, values: &[u16]) -> Result<()> {
let count = values.len();
let num_bytes = count * 2;
assert!(
count <= 128,
"Cannot send that many bytes to modbus device in one go! Trying to send {} bytes of data.",
num_bytes
);
let mut packet = Packet::new();
self.transactions += 1;
packet.add_u16(self.transactions);
packet.add_u16(PROTOCOL_ID);
packet.add_u16(WRITE_HOLDING_LENGTH_WITHOUT_DATA + num_bytes as u16);
packet.add_u8(UNIT_ID);
packet.add_u8(WRITE_HOLDING_FUNCTION_CODE);
packet.add_u16(addr);
packet.add_u16(count as u16);
packet.add_u8(num_bytes as u8);
for value in values {
packet.add_u16(value.to_owned());
}
self.send(packet).await?;
self.read_and_check_responce_header(WRITE_HOLDING_FUNCTION_CODE)
.await?;
if self.read_u16().await? != addr {
bail!("Modbus device wrote to wrong register address!");
}
if self.read_u16().await? != count as u16 {
bail!("Modbus device wrote wrong number of registers!");
}
Ok(())
}
pub async fn get_string<const N: usize>(&mut self, addr: u16) -> Result<String> {
assert!(N % 2 == 0, "Strings are always of even length!");
assert!(N <= 256, "Cannot read long strings in a single swoop!");
let count = N / 2;
self.send_read_holding_request(addr, count as u16).await?;
let buf = self.read_bytes::<N>().await?;
let s = String::from_utf8_lossy(&buf);
let s = match s.split_once('\0') {
None => s.to_string(),
Some((l, _)) => l.to_string(),
};
Ok(s)
}
pub async fn set_string<const N: usize>(&mut self, _addr: u16, s: &str) -> Result<()> {
assert!(s.len() <= N, "String is too long to fit!");
todo!()
}
pub async fn get_u16(&mut self, addr: u16) -> Result<u16> {
let length = self.send_read_holding_request(addr, 1).await?;
assert_eq!(
length, 2,
"Expected modbus device to send exactly 2 bytes of data!"
);
self.read_u16().await
}
pub async fn get_u32(&mut self, addr: u16) -> Result<u32> {
let length = self.send_read_holding_request(addr, 2).await?;
assert_eq!(
length, 4,
"Expected modbus device to send exactly 4 bytes of data!"
);
let buf = self.read_bytes::<4>().await?;
Ok(u32::from_be_bytes(buf))
}
// We have to send the request in a single packet,
// otherwise the device may fail to respond correctly.
async fn send(&mut self, packet: Packet) -> Result<()> {
self.stream
.write_all(packet.as_bytes())
.await
.context("Could write to modbus device!")
}
async fn read_u8(&mut self) -> Result<u8> {
let buf = self.read_bytes::<1>().await?;
Ok(u8::from_be_bytes(buf))
}
async fn read_u16(&mut self) -> Result<u16> {
let buf = self.read_bytes::<2>().await?;
Ok(u16::from_be_bytes(buf))
}
async fn read_bytes<const N: usize>(&mut self) -> Result<[u8; N]> {
let mut buf = [0; N];
self.stream
.read_exact(&mut buf)
.await
.context("Could not read from modbus device!")?;
Ok(buf)
}
}
-1
View File
@@ -1 +0,0 @@
pub mod mutation;
+2 -2
View File
@@ -1,11 +1,10 @@
use std::{any::Any, sync::Arc};
/*use std::{any::Any, sync::Arc};
use smol::lock::RwLock;
use std::fmt::Debug;
use crate::machines::identification::DeviceIdentification;
pub mod registry;
pub mod serial_detection;
pub trait SerialDevice: Any + Send + Sync + SerialDeviceNew + Debug {}
@@ -31,3 +30,4 @@ pub struct SerialDeviceIdentification {
pub vendor_id: u16,
pub product_id: u16,
}
*/
-1
View File
@@ -1,3 +1,2 @@
pub mod event;
pub mod namespace;
pub mod namespace_id;
+2 -2
View File
@@ -4,7 +4,7 @@ use socketioxide::extract::SocketRef;
use std::{collections::HashMap, sync::Arc, time::Duration};
use tracing::instrument;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Namespace {
pub sockets: Vec<SocketRef>,
pub events: HashMap<String, Vec<Arc<GenericEvent>>>,
@@ -167,7 +167,7 @@ impl Namespace {
impl Drop for Namespace {
fn drop(&mut self) {
self.disconnect_all();
//self.disconnect_all();
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
use uom::si::{
use units::{
angle::radian,
angular_velocity::radian_per_second,
f64::{Angle, AngularVelocity, Length, Velocity},
@@ -1,7 +0,0 @@
unit! {
system: uom::si;
quantity: uom::si::acceleration;
@meter_per_minute_per_second: 0.016_666_666_666_666_67; "m/min/s", "meters per minute per second",
"meters per minute per second";
}
@@ -1,7 +0,0 @@
unit! {
system: uom::si;
quantity: uom::si::angular_acceleration;
@revolution_per_minute_per_second: 0.104_719_755_119_659_77; "rev/min/s", "revolutions per minute per second",
"revolutions per minute per second";
}
@@ -1,7 +0,0 @@
unit! {
system: uom::si;
quantity: uom::si::angular_jerk;
@revolution_per_minute_per_second_squared: 0.104_719_755_119_659_77; "rev/min/s²", "revolution per minute per second squared",
"revolution per minute per second squared";
}
-7
View File
@@ -1,7 +0,0 @@
unit! {
system: uom::si;
quantity: uom::si::jerk;
@meter_per_minute_per_second_squared: 0.016_666_666_666_666_666_67; "m/min/s²", "meters per minute per second squared",
"meters per minute per second squared";
}
-5
View File
@@ -1,5 +0,0 @@
pub mod acceleration;
pub mod angular_acceleration;
pub mod angular_jerk;
pub mod jerk;
pub mod velocity;
@@ -1,7 +0,0 @@
unit! {
system: uom::si;
quantity: uom::si::velocity;
@meter_per_minute: 0.016_666_666_666_666_67; "m/min", "meters per minute",
"meters per minute";
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 557 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

+139
View File
@@ -0,0 +1,139 @@
# XTREM Communication Protocol
**Version:** 3.007
**Device:** XTREM / XTREM-S Weighing Module
**Standard:** OIML R76:2006 / EN45501:2015 compliant
---
## Overview
The **XTREM communication protocol** allows a terminal or host controller to exchange data and commands with the XTREM weighing module.
The device transmits and receives ASCII-coded frames over a serial interface (RS232, RS485, Wi-Fi, or Ethernet, depending on configuration).
Since we are using the Ethernet interface we need to provide some kind of DHCP to assign an IP - Address to each XTREM Device.
They then may be addressed via a **broadcast** IP (e.g.: 192.168.4.255/24).
A **Unicast** communication is applied through the device ID inside the XTREM Frame.
---
## Frame Structure
Each message—both query and response—has the same format:
```
STX ID_O ID_D F D_ADDRESS D_L DATA LRC ETX
```
| Field | Description | Length (bytes) |
| :------------ | :------------------------------------------------ | :------------: |
| **STX** | Start of message (ASCII `02h`) | 1 |
| **ID_O** | Sender address (2 ASCII hex chars) | 2 |
| **ID_D** | Destination address (2 ASCII hex chars) | 2 |
| **F** | Function code (`R`, `W`, `E`, etc.) | 1 |
| **D_ADDRESS** | Data address (register, 4 ASCII hex chars) | 4 |
| **D_L** | Data length (2 ASCII hex chars) | 2 |
| **DATA** | Optional data field | Variable |
| **LRC** | Longitudinal Redundancy Check (2 ASCII hex chars) | 2 |
| **ETX** | End of message (ASCII `03h`) | 1 |
In the default configuration the Frame also requires a CRLF at the end.
**Example:**
```
STX ‘1’ ‘7’ ‘0’ ‘1’ ‘R’ ‘0’ ‘1’ ‘0’ ‘1’ ‘0’ ‘0’ LRC ETX CR LF
```
---
## Function Codes
| Function | ASCII | Hex | Description |
| :------- | :---- | :---| :-------------- |
| **R** | R | 52h | Read request |
| **r** | r | 72h | Read response |
| **W** | W | 57h | Write request |
| **w** | w | 77h | Write response |
| **E** | E | 45h | Execute command |
| **e** | e | 65h | Execute response|
### Write response codes
* `'0'` – OK
* `'1'` – Write denied (sealed)
* `'2'` – Read-only register
* `'3'` – Invalid value / out of range
### Execute response codes
* `'0'` – Executed successfully
* `'1'` – Blocked by seal switch
---
## LRC Calculation
The **Longitudinal Redundancy Check** is a one-byte XOR of all message bytes, excluding:
* `STX`
* `ETX`
* The LRC itself
Example C function:
```c
unsigned char LRC(unsigned char* s) {
unsigned char lrc = 0;
while (*s) {
lrc ^= *(s++);
}
return lrc;
}
```
Example Rust function:
```rust
pub fn compute_lrc(data: &[u8]) -> u8 {
data.iter().fold(0u8, |acc, &b| acc ^ b)
}
```
---
## Key Data Registers
| Address (hex) | Description | Type | Notes |
| :------------ | :------------------------- | :--: | :------------------------------------ |
| **0000h** | Serial number | R | Factory-set |
| **0001h** | Device ID | RW | Network address |
| **0007h** | Hardware version | R | Read-only |
| **0008h** | Software version | R | Read-only |
| **0009h** | Sealing switch status | R | `0=UNLOCK`, `1=LOCK` |
| **0010h** | Baud rate | RW | `0–4` → 9600–115200 baud |
| **0013h** | Stream mode output rate | RW | Time in ms |
| **0100h** | Device state | R | Status bits (power, Wi-Fi, ADC, etc.) |
| **0101h** | Weight value | R | Gross weight |
| **0102h** | Tare value | RE | Read/Execute |
| **0103h** | Net weight | R | Gross − Tare |
| **0110h** | ADC counts (instant) | R | Raw ADC data |
| **0111h** | ADC counts (filtered) | R | Post-filter data |
| **1010h** | Stop stream mode | E | Execute |
| **1011h** | Start stream mode (weight) | E | Continuous output |
| **9999h** | Device reset | E | Software reset |
| **EEEEh** | Factory reset | E | Restores defaults |
---
## Post-Factory Reset Configuration
After performing a factory reset, several registers must be configured to ensure proper operation of the device.
Note: The unit may remain set to 'g' after a factory reset, but the values received may be in 'kg' depending on the device configuration.
The following registers should be reviewed and set as required:
1. **Address: 0022 | Max (Max1) (scale maximum capacity)**
The default value for this register may vary between devices (e.g., 60000 or 6000). Set this value to 6000 to ensure correct operation.
2. **Address: 0023 | e (e1) (scale interval)**
3. **Address: 0026 | Decimal position**
+90
View File
@@ -82,3 +82,93 @@ git push
# Recommended Editor Setup
We recommend you to use an Editor with rust-analyzer support like VSCode to speed up development and detect errors before compiling.
# Minimal Hardware Examples
To get started with actual hardware, check out these step-by-step tutorials:
- **[LED Control with EL2004](./minimal-example-el2004.md)** - Digital output control, the simplest possible hardware setup
- **[Analog Input with EL3021](./minimal-example-el3021.md)** - Reading analog current measurements
- **[Stepper Motor Control with EL7031-0030](./minimal-example-el7031-motor.md)** - Complete motor control integration with velocity control
These examples provide complete hardware wiring diagrams and software setup instructions.
# More Docs
- [x] [Architecture & Data Flow](./docs/architecture-overview.md)
- [x] Example Winder V2
- [ ] Electron
- Folder Structure
- Routing with TanStack Router
- Design with Tailwind & Shadcn
- ...
- [ ] Interfacing with Electron/Server
- [ ] SocketIO
- Machine Namespace
- Main Namespace
- [ ] REST
- Machine Mutations
- Write Device Identification
- Server
- [x] [Threading](./docs/control-loop.md)
- [x] [Logging](./docs/logging.md)
- [ ] Control Loop Setup
- Control Loop Thread
- [ ] realtime
- Maindevice
- Group
- Extracting Device Identifications
- Identifying Groups
- Validating Machines
- Run Control Loop
- [x] [Control Loop](./docs/control-loop.md)
- [x] [Machine/Device Identification](./docs/identification.md)
- [ ] Machines
- When to create a new Machine?
- Versioning
- Code sharing
- Creating/Validating a Machine
- Validation
- Configuration
- [ ] Machine Implementation Guide
- Link: How to create a Device
- Link: How to create an Actor
- Link: How to create a Machine
- API (SocketIO + REST)
- Creation/Validation Logic
- Optional/Mandatory Devices
- Validate Devices
- Business Logic
- Link: How to create Machine Abstraction (Like Traverse/Puller/...)
- Forward `act` in winder.
- [ ] Control Core
- [x] [Actors](./docs/actors.md)
- [ ] SocketIO
- Namespaces & Caching
- Joining leaving namespaces
- NamespaceId
- Caching
- Serverside Caching
- Clientside Caching
- [ ] REST
- [x] Ethercat HAL
- [x] [Devices](./docs/devices.md)
- [x] [Configuration (CoE)](./docs/coe.md)
- [x] [IO](./docs/io.md)
- [x] [PDO](./docs/pdo.md)
- [x] [Ethercat Basics](./docs/ethercat-basics.md)
- [x] [NixOS Operating System](./docs/nixos/README.md)
@@ -0,0 +1,224 @@
# Minimal Example — LED Control on the EL2004
A complete hardware + software walkthrough
---
## Table of Contents
1. [Introduction](#1-introduction)
2. [Requirements](#2-requirements)
3. [Hardware Setup](#3-hardware-setup)
4. [Software Setup](#4-software-setup)
5. [Demo](#5-demo)
6. [References](#6-references)
7. [Acknowledgements](#7-acknowledgements)
---
## 1. Introduction
The EL2004 LED Toggle Example is a minimal demonstration showing how to control digital outputs on a **Beckhoff EL2004 EtherCAT terminal** using the QiTech machine framework.
It represents the simplest possible hardware interaction in the system:
**toggling LED outputs using the QiTech Control Dashboard.**
---
## 2. Requirements
### Hardware
- Beckhoff **EL2004 EtherCAT Terminal** (4-channel digital output)
- Beckhoff **EK1100 EtherCAT Coupler**
- **24 V DC power supply** (AC/DC adapter + DC hollow plug)
- Jumper / bridge wires (0.5–1.5 mm² recommended)
- A **Linux PC** (Ubuntu/Debian recommended)
- Standard Ethernet cable
- Flat screwdriver
### Software
*(Installation steps in Section 4)*
- Rust toolchain
- Node.js + npm
- Git
- QiTech Control repository
- EtherCAT HAL (included inside repo)
---
## 3. Hardware Setup
### 3.1 Schematic
![](../assets/schematic.png)
---
### 3.2 EK1100 Wiring
This wiring configuration powers the EL2004 and prepares it for LED control.
It is not the only possible wiring but is the **simplest functional setup**.
#### ⚠️ Safety Warning
Always disconnect power before wiring.
Working on live EtherCAT terminals can cause serious damage or electrical shock.
---
#### 3.2.1 Safe Wiring Procedure (Beckhoff Recommended)
1. Insert a screwdriver **straight** into the square release hole.
2. Insert the stripped wire into the round opening.
3. Remove the screwdriver — the spring clamp locks the wire.
![](../assets/wiring.png)
---
#### 3.2.2 Wiring Steps (Used in This Example)
We supply power using a **DC hollow-plug adapter**, like this one:
https://www.amazon.de/dp/B093FTFZ8Q
Perform the following wiring on the EK1100:
1. Red wire **(+24 V)** → Terminal **2**
2. Black wire **(0 V)** → Terminal **3**
3. Jumper wire from **Terminal 1 → Terminal 6**
4. Jumper wire from **Terminal 5 → Terminal 7**
After wiring, your module should look like **Figure 1**.
---
#### **Figure 1 — EK1100 Minimal Wiring**
<img src="../assets/ek1100.jpeg" width="400">
---
### 3.3 EL2004 Integration
Slide the EL2004 onto the **right side of the EK1100** until it locks.
The EtherCAT E-Bus and power contacts connect automatically — **no wiring required**.
#### **Figure 2 — EL2004 Terminal**
<img src="../assets/el2004.jpeg" width="400">
---
### 3.4 Final Assembled Setup
#### **Figure 3 — EK1100 + EL2004 Connected**
<img src="../assets/complete.jpeg" width="400">
---
### 3.5 Power & Ethernet
#### Power
Connect the 24 V adapter to the hollow plug used earlier.
**Example AC/DC Adapter (Figure 4):**
<img src="../assets/adapter.jpeg" width="400">
#### Ethernet
Use a standard LAN cable to connect your PC → EK1100.
The final powered up and connected setup should look like this:
<img src="../assets/power.jpeg" width="400">
---
## 4. Software Setup
### 4.1 Installing on Ubuntu/Debian
Paste this into your terminal:
```bash
# Press Enter when prompted
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
sudo apt update
sudo apt install -y npm nodejs git
git clone git@github.com:qitechgmbh/control.git
cd control/electron
npm install
```
### 4.2 Running the Backend
```bash
./cargo_run_linux.sh
```
This script:
- Builds the backend
- Grants required system capabilities (raw sockets)
- Starts EtherCAT communication
Ensure the EK1100 is connected.
### 4.3 Running the Frontend
```bash
cd electron
npm run start
```
This launches the QiTech Control dashboard.
## 5. Demo
### 5.1 Assigning Devices in the Dashboard
Once the backend + frontend are running, you should see:
- EK1100 Coupler
- EL2004 Digital Output Terminal
![](../assets/discovery.png)
Steps:
- Click Assign on the EK1100
- Select TestMachine V1
![](../assets/setmachine.png)
- Enter a serial number (use the same for EK1100 + EL2004)
![](../assets/serserial.png)
- Click Write
- Repeat for the EL2004
### 5.2 Testing LED Control
Navigate to:
Machines → TestMachine
![](../assets/machinedetected.png)
You will see this interface:
![](../assets/machinecontrol.png)
**You can now toggle the four digital outputs of the EL2004.**
## 6. References
This guide incorporates information from official Beckhoff documentation.
All diagrams, product names, and figures belong to Beckhoff Automation GmbH & Co. KG and are used here solely for educational purposes.
Referenced Manuals
[Beckhoff EK1100 Documentation](https://download.beckhoff.com/download/Document/io/ethercat-terminals/ek110x_ek15xx_en.pdf)
[Beckhoff EL2004 Documentation](https://download.beckhoff.com/download/Document/io/ethercat-terminals/el20xx_el2124_de.pdf)
## 7. Acknowledgements
This tutorial is inspired by the clarity and educational quality of Beckhoff manuals.
All wiring illustrations and hardware descriptions in this guide are provided for demonstration purposes only and do not replace official Beckhoff installation guidelines.
Special thanks to the QiTech engineering team for providing the backend architecture, EtherCAT HAL abstraction, and the TestMachine framework that makes this example possible.
@@ -0,0 +1,178 @@
# Minimal Example — Analog Input EL3021
## Table of Contents
1. [Introduction](#1-introduction)
2. [Requirements](#2-requirements)
3. [Hardware Setup](#3-hardware-setup)
4. [Software Setup](#4-software-setup)
5. [Demo](#5-demo)
6. [References](#6-references)
## 1. Introduction
The EL3021 Analog Input example demonstrates how to measure analog current on a **Beckhoff EL3021 EtherCAT terminal** using the QiTech machine framework.
## 2. Requirements
### Hardware
- Beckhoff **EL3021 EtherCAT Terminal** (1-channel analog input)
- Beckhoff **EK1100 EtherCAT Coupler**
- **24 V DC power supply** (AC/DC adapter + DC hollow plug)
- **Adjustable DC power supply** (or any other means of producing 4-20mA DC @ <10V )
- Jumper / bridge wires (0.5–1.5 mm² recommended)
- A **Linux PC** (Ubuntu/Debian recommended)
- Standard Ethernet cable
- Flat screwdriver
## 3. Hardware Setup
### 3.1 Schematic
![](../assets/EL3021_schematic.png)
The 24V supply powers the EL2004, while the adjustable power supply generates the analog input signal for the EL3021.
#### ⚠️ Safety Warning
Always disconnect power before wiring.
Working on live EtherCAT terminals can cause serious damage or electrical shock.
### 3.2.1 Safe Wiring Procedure (Beckhoff Recommended)
1. Insert a screwdriver **straight** into the square release hole.
2. Insert the stripped wire into the round opening.
3. Remove the screwdriver — the spring clamp locks the wire.
![](../assets/wiring.png)
---
### 3.2.2 Wiring Steps (Used in This Example)
We supply power using a **DC hollow-plug adapter**, like this one:
https://www.amazon.de/dp/B093FTFZ8Q
Perform the following wiring on the EK1100:
1. Red wire **(+24 V)** → Terminal **2**
2. Black wire **(0 V)** → Terminal **3**
3. Jumper wire from **Terminal 1 → Terminal 6**
4. Jumper wire from **Terminal 5 → Terminal 7**
After wiring, your module should look like **Figure 1**.
---
#### **Figure 1 — EK1100 Minimal Wiring**
<img src="../assets/ek1100.jpeg" width="400">
---
### 3.3 EL3021 Integration
Slide the EL3021 onto the **right side of the EK1100** until it locks.
The EtherCAT E-Bus and power contacts connect automatically.
Then you need to wire the adjustable power supply to the EL3021.
#### **Figure 2 — EL3021 Terminal**
<img src="../assets/EL3021.jpg" width="400">
1. Wire ⊕ to **Terminal 1**
2. Wire ⊖ to **Terminal 2**
---
### 3.4 Final Assembled Setup
#### **Figure 3 — EK1100 + EL3021 Connected**
<img src="../assets/EK1100+EL3021.jpg" width="400">
---
## 3.5 Power & Ethernet
### 3.5.1 Power
Connect the 24 V adapter to the hollow plug used earlier.
#### Example AC/DC Adapter (Figure 4):
<img src="../assets/adapter.jpeg" width="400">
### 3.5.2 Ethernet
Use a standard LAN cable to connect your PC → EK1100.
The final powered up and connected setup should look like this:
<img src="../assets/EL3021_power+eth.jpg" width="400">
---
## 4. Software Setup
### 4.1 Installing on Ubuntu/Debian
Paste this into your terminal:
```bash
# Press Enter when prompted
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
sudo apt update
sudo apt install -y npm nodejs git
git clone git@github.com:qitechgmbh/control.git
cd control/electron
npm install
```
### 4.2 Running the Backend
```bash
./cargo_run_linux.sh
```
This script:
- Builds the backend
- Grants required system capabilities (raw sockets)
- Starts EtherCAT communication
Ensure the EK1100 is connected.
### 4.3 Running the Frontend
```bash
cd electron
npm run start
```
This launches the QiTech Control dashboard.
## 5. Demo
### 5.1 Assigning Devices in the Dashboard
Once both backend and frontend are running, the Setup -> EtherCat page should look something like this:
![](../assets/EL3021_overview.png)
Listing the EK1100 and the EL3021.
To assign the two devices to a machine, click "Assign" on both devices. This should open the following pop-up modal:
**EK1100**
![](../assets/EL3021_assign.png)
1. Select "AnalogTest V1" under "Machine"
2. Choose a serial number to uniquely identify the machine (1 in this example)
3. Under "Device Role" select "0 - Bus Coupler"
**EL3021**
![](../assets/EL3021_assign2.png)
1. Select "AnalogTest V1" under "Machine"
2. Use the same serial number you chose for the EK1100
3. Under "Device Role" select "1 - EL3021"
---
The machine is now configured.
A new Tab "AnalogTest" should appear in the right sidebar. Clicking that tab should show the machine control interface:
![](../assets/EL3021_control.png)
Results should show the current measured by the analog input - the current set on the adjustable power supply.
## 6. References
This guide incorporates information from official Beckhoff documentation.
All diagrams, product names, and figures belong to Beckhoff Automation GmbH & Co. KG and are used here solely for educational purposes.
Referenced Manuals
[Beckhoff EK1100 Documentation](https://download.beckhoff.com/download/Document/io/ethercat-terminals/ek110x_ek15xx_en.pdf)
[Beckhoff EL3021 Documentation](https://download.beckhoff.com/download/Document/io/ethercat-terminals/el30xx_de.pdf)
@@ -0,0 +1,236 @@
# Minimal Example — Beckhoff EL7031-0030 Stepper Motor Integration
A complete hardware + software walkthrough
---
## Table of Contents
1. [Introduction](#1-introduction)
2. [Requirements](#2-requirements)
3. [Hardware Setup](#3-hardware-setup)
- [3.2 EK1100 Wiring](#32-ek1100-wiring)
- [3.2.1 Safe Wiring Procedure](#321-safe-wiring-procedure-beckhoff-recommended)
- [3.2.2 Wiring (EL7031-0030)](#322-wiring-el7031-0030)
- [3.3 Safety Warning](#33--safety-warning)
4. [Software Setup](#4-software-setup)
- [4.1 Installing on Linux](#41-installing-on-linux-this-depends-on-your-distro)
- [4.2 Running the Backend](#42-running-the-backend)
- [4.3 Running the Frontend](#43-running-the-frontend)
5. [Demo](#5-demo)
- [5.1 Assigning Devices in the Dashboard](#51-assigning-devices-in-the-dashboard)
6. [Documentation](#6-documentation)
7. [Software Architecture](#7-software-architecture)
---
## 1. Introduction
This project documents the successful integration of a **Beckhoff EL7031-0030 Stepper Motor Terminal** into the QiTech control software stack.
The goal was to control a 24V stepper motor via EtherCAT using a custom Rust backend and a React/Electron frontend.
---
## 2. Requirements
### Software
- Rust toolchain
- Node.js + npm
- Git
- QiTech Control repository
- EtherCAT HAL (included inside repo)
### Hardware
- **EtherCAT Master:** Linux PC
- **Bus Coupler:** EtherCAT Beckhoff EK1100
- **Stepper Driver:** Beckhoff EL7031-0030 (THIS IS DIFFERENT FROM EL7031)
- **Motor:** Standard 4-wire Stepper Motor
- **Power Supply:** 24V DC
- **Ethernet Cable:** Standard Ethernet cable
- **Wiring Tools:** Screwdriver, wires
---
## 3. Hardware Setup
### 3.2 EK1100 Wiring
This wiring configuration powers the EL7031-0030.
It is not the only possible wiring but is the **simplest functional setup**.
#### ⚠️ Safety Warning
Always disconnect power before wiring.
Working on live EtherCAT terminals can cause serious damage or electrical shock.
---
#### 3.2.1 Safe Wiring Procedure (Beckhoff Recommended)
1. Insert a screwdriver **straight** into the square release hole.
2. Insert the stripped wire into the round opening.
3. Remove the screwdriver — the spring clamp locks the wire.
![](../assets/wiring.png)
---
We supply power using a **DC hollow-plug adapter**, like this one:
https://www.amazon.de/dp/B093FTFZ8Q
Perform the following wiring on the EK1100:
1. Red wire **(+24 V)** → Terminal **2**
2. Black wire **(0 V)** → Terminal **3**
3. Jumper wire from **Terminal 1 → Terminal 6**
4. Jumper wire from **Terminal 5 → Terminal 7**
After wiring, your module should look like **Figure 1**.
---
#### **Figure 1 — EK1100 Minimal Wiring**
<img src="../assets/ek1100.jpeg" width="400">
---
#### **Figure 2 — EL7031-0030 Terminal**
<img src="../assets/EL7031_0030.jpg" width="300">
Slide the EL7031-0030 onto the right side of the EK1100 until it locks.
The EtherCAT E-Bus and power contacts connect automatically — **no wiring required**.
---
#### **Figure 3 — Motor**
<img src="../assets/motor.jpg" width="300">
---
#### **Figure 4 — Motor Wiring**
<img src="../assets/motor_wiring.jpg" width="300">
Now the motor is wired via the pins 4, 5, 12, 13 on the EL7031-0030.
---
#### **Figure 5 — EL7031 Integration Connected**
<img src="../assets/EL7031_0030_connected.jpg" width="400">
That's what the pin should look like.
---
### 3.2.2 Wiring (EL7031-0030)
**Crucial:** The EL7031 requires two power sources: E-Bus (side contacts) for logic, and Front Terminal (6 & 14) for motor power.
| Terminal Point | Function | Cable Color (Example) |
| :------------- | :--------------- | :-------------------- |
| **4** | Motor Coil A1 | Red |
| **12** | Motor Coil A2 | Blue |
| **5** | Motor Coil B1 | Green |
| **13** | Motor Coil B2 | Black |
| **6** | **Power +24V** | PSU Red (+) |
| **14** | **Power 0V/GND** | PSU Black (-) |
---
### 3.3 ⚠️ Safety Warning
- **Mandatory Power Supply:** Without 24V connected to **Pin 6 (+24V)** and **Pin 14 (GND)**, the terminal will remain in `PREOP` state or show a "Warning" LED (No Power). The motor will **not** move without this external supply.
#### Risk of Destruction (Short Circuit):
- **Never** connect the 24V Power Supply to the Motor Output pins (**4, 5, 12, 13**). This will instantly destroy the terminal (causing pins 12 & 13 to glow red/burn).
- Ensure strict separation: **Pins 4/5/12/13 are for the MOTOR ONLY**.
- **Pins 6/14 are for POWER ONLY**.
Also, see the documentation of the EtherCAT terminal ([EL7031-0030](https://download.beckhoff.com/download/document/io/ethercat-terminals/el7031-0030de.pdf)) for the different power modes (page 46 ff.)
---
## 4. Software Setup
### 4.1 Installing on Linux (this depends on your distro)
Paste this into your terminal:
```bash
# Press Enter when prompted
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
sudo apt update
sudo apt install -y npm nodejs git
git clone git@github.com:qitechgmbh/control.git
cd control/electron
npm install
```
### 4.2 Running the Backend
```bash
./cargo_run_linux.sh
```
This script:
- Builds the backend
- Grants required system capabilities (raw sockets)
- Starts EtherCAT communication
### 4.3 Running the Frontend
```bash
# move to the control directory
cd electron
npm run start
```
This launches the QiTech Control dashboard.
---
## 5. Demo
### 5.1 Assigning Devices in the Dashboard
Once the backend + frontend are running, you should see:
![](../assets/UI_start_motor.png)
Make sure that under "Assign" → "Machine Assignment"
the correct serial number is selected (each device should have the same one, here it is 1).
Under "Machine" select your new Machine however you named it (I named it "TestMotor V1"), else it will detect that there is something connected to it but the connection won't work.
![](../assets/machine_assigment_motor.png)
**NOW THE MOTOR SHOULD TURN!**
In the interface "TestMotor" on the left side, you can now control the motor (its state and its speed).
![](../assets/UI_motor.png)
---
## 6. Documentation
Use the official documentation of the EL7031-0030 for more information:
[Beckhoff EL7031-0030 Documentation](https://download.beckhoff.com/download/document/io/ethercat-terminals/el7031-0030de.pdf)
---
## 7. Software Architecture
### Backend (Rust)
Located in [machines/src/motor_test_machine/](/machines/src/motor_test_machine/).
1. **`mod.rs`**: Defines the `MotorTestMachine` struct and holds the state (driver wrapper, enabled state, target velocity).
2. **`api.rs`**: Handles incoming JSON commands from the frontend (Enable/Disable, Set Velocity) via WebSockets/SocketIO.
3. **`act.rs`**: The real-time control loop. It updates the `StepperVelocityEL70x1` driver wrapper in every cycle based on the current state.
4. **`new.rs`**: Initializes the hardware.
### Frontend (TypeScript/React)
Located in [electron/src/machines/motor_test_machine/](/electron/src/machines/motor_test_machine/) and [electron/src/routes/routes.tsx](/electron/src/routes/routes.tsx).
1. **`useTestMotor.ts`**: Custom hook managing the optimistic state and communication with the backend.
2. **`TestMotorControlPage.tsx`**: The UI using QiTech UI components (`ControlCard`, `EditValue`, `SelectionGroupBoolean`) to match the look and feel of the Winder2.
3. **Routing**: Integrated into `routes.tsx` using TanStack Router, ensuring the machine appears in the sidebar and navigation works correctly.
Loaded 100 of 439 files, more files were not shown because too many files have changed in this diff. Show more