Compare commits

...
Author SHA1 Message Date
kraemr 16e929fcf9 removed txpdo assignment causing issues 2026-02-09 14:11:53 +01:00
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
Robin Krämer 13117a5d94 Merge pull request #845 from qitechgmbh/844-add-winder-mock-machine
added Winder mock
2025-10-28 17:58:25 +01:00
kraemr 6b74c7ec7a removed default mock-machine 2025-10-28 17:53:04 +01:00
kraemr e8b561f722 added Winder mock 2025-10-28 17:53:04 +01:00
Robin Krämer 75ee5af180 Merge pull request #852 from qitechgmbh/851-improve-caching
851 improve caching
2025-10-28 17:46:21 +01:00
kraemr 29f924f6cc removed socketioManager 2025-10-27 16:42:03 +01:00
kraemr 6e058c8e95 Keep namespaces alive 2025-10-27 15:11:30 +01:00
Robin Krämer 6822da80cf Merge pull request #838 from qitechgmbh/dependabot/npm_and_yarn/electron/multi-a50d7f32cf
Bump playwright and @playwright/test in /electron
2025-10-27 11:24:43 +01:00
dependabot[bot] 37e3302f61 Bump playwright and @playwright/test in /electron
Bumps [playwright](https://github.com/microsoft/playwright) to 1.56.1 and updates ancestor dependency [@playwright/test](https://github.com/microsoft/playwright). These dependencies need to be updated together.


Updates `playwright` from 1.54.2 to 1.56.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.54.2...v1.56.1)

Updates `@playwright/test` from 1.54.2 to 1.56.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.54.2...v1.56.1)

---
updated-dependencies:
- dependency-name: playwright
  dependency-version: 1.56.1
  dependency-type: indirect
- dependency-name: "@playwright/test"
  dependency-version: 1.56.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 11:21:25 +01:00
Robin Krämer 9b369d71ee Merge pull request #839 from qitechgmbh/dependabot/npm_and_yarn/electron/vite-6.4.1
Bump vite from 6.3.6 to 6.4.1 in /electron
2025-10-27 11:21:10 +01:00
dependabot[bot] cf58f0db6f Bump vite from 6.3.6 to 6.4.1 in /electron
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.6 to 6.4.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/create-vite@6.4.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.4.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 11:17:46 +01:00
Robin Krämer 299f23c796 Merge pull request #847 from qitechgmbh/846-refactor-initialization-to-be-based-around-futures
started refactoring initialization to use futures instead
2025-10-27 11:14:18 +01:00
kraemr b58e4333a0 removed traces 2025-10-27 11:08:21 +01:00
kraemr f76cb9c381 Fixed Loop async code 2025-10-27 11:01:04 +01:00
kraemr e93e10d14d fixed all warnings and restored original ethercat discovery behaviour 2025-10-23 20:13:48 +02:00
kraemr 025464ebff all init functions look sane now
fixed compilation errors

fixed compile errors

refactored serial device detection successfully

added serial_detection functions

fixed  Killed error, and improved serial_detection

fixed all remaining issues

fix

fix
2025-10-23 20:13:33 +02:00
kraemr ae8f77f070 started refactoring initialization to use futures instead 2025-10-22 17:56:52 +02:00
Robin Krämer 97811a12d1 Merge pull request #842 from qitechgmbh/changelog-2.11.0
updated Changelog
2025-10-21 13:11:26 +02:00
kraemr 1f7e01820a updated Changelog 2025-10-21 13:10:37 +02:00
Robin Krämer 2f73771bbd Merge pull request #827 from qitechgmbh/825-add-button-to-change-the-spool-motor-direction-of-the-winder
825 add button to change the spool motor direction of the winder
2025-10-21 13:01:25 +02:00
Christian 503ac18e7b Ensure spool RPM is always displayed as a positive value 2025-10-21 12:37:34 +02:00
Christian 6a99b21c15 Fix spool speed rendering to display absolute values 2025-10-21 12:37:34 +02:00
Christian 170667f2b4 Add spool direction control to Winder2
- Introduced a new setting for spool direction (Forward/Reverse) in Winder2PresetsPage and Winder2Settings.
- Updated the state management to include forward direction in useWinder.
- Enhanced the API to handle SetSpoolForward mutation.
- Adjusted the spool speed controller to apply direction based on the forward setting.
2025-10-21 12:37:34 +02:00
Robin Krämer 314b7f2484 Merge pull request #840 from qitechgmbh/828-add-xl-winder-button-to-the-winder-config
828 add xl winder button to the winder config
2025-10-21 12:28:22 +02:00
Christian 9353b12707 Fix traverse limit reset logic in handleXlModeChange function 2025-10-21 11:51:02 +02:00
Christian 063810f906 Add gotoTraverseHome call when switching from XL to normal mode 2025-10-21 11:51:02 +02:00
Christian ac7ce126e6 Refactor handleXlModeChange to reset traverse limits when switching from XL mode 2025-10-21 11:51:02 +02:00
Christian a920830dcf Update WINDER2_TRAVERSE_MAX_XL to 385 mm for improved configuration 2025-10-21 11:51:02 +02:00
Christian 15d74fcee0 Add configuration for Winder2 settings and implement XL mode functionality 2025-10-21 11:51:02 +02:00
Robin Krämer c598e3fc9f Merge pull request #826 from qitechgmbh/818-smooth-fluctuating-temperature-extruder
818 smooth fluctuating temperature extruder
2025-10-20 18:16:08 +02:00
snacj 0dba0f2774 Removed package-lock from root folder 2025-10-20 18:12:06 +02:00
snacj e2cdb6a94c Removed package-lock from root folder 2025-10-20 18:12:06 +02:00
snacj 5dd2011e78 removed file 2025-10-20 18:12:06 +02:00
snacj 7bc142dc83 Fixed error in Extruder mock and removed zone from UpdateStateOptimistically 2025-10-20 18:12:06 +02:00
snacj 1931f00806 Changed rounding and step size to 0.001 for temperature PID 2025-10-20 18:12:06 +02:00
snacj 253336b02c Refactored frontend code of new PID settings 2025-10-20 18:12:06 +02:00
snacj b0c3a97f7f Fixed 3 New Pid Controllers for Extruder front, middle, back, heating. 2025-10-20 18:12:06 +02:00
snacj 22887d4f7a Added 3 New Pid Controllers for Extruder front, middle, back, heating. 2025-10-20 18:12:06 +02:00
Robin Krämer 00dbc90c85 Merge pull request #834 from qitechgmbh/lag-spike-fix
removed async sleep from control loop
2025-10-20 18:07:21 +02:00
kraemr 9ade1d8993 removed async sleep from control loop 2025-10-20 18:04:52 +02:00
Robin Krämer b2fb803033 Merge pull request #829 from qitechgmbh/808-lock-kernel-in-nix-to-last-working-rt-kernel
Newest kernel with realtime preemption
2025-10-20 13:42:38 +02:00
nixos-install.sh 988ed5b959 Newest kernel with realtime preemption 2025-10-17 17:36:14 +02:00
Oshgnacknak 2e62f99724 Merge pull request #806 from qitechgmbh/fix-caps
Fix caps in cargo run Linux
2025-10-17 15:01:53 +02:00
Oshgnacknak 530327b1e8 Fix caps in cargo run Linux 2025-10-17 14:55:44 +02:00
Robin Krämer 2f75b31fa9 Merge pull request #816 from qitechgmbh/812-improve-interface-discovery-to-only-use-ethernet-devices
fixed the atrocious interface_discovery
2025-10-17 12:42:31 +02:00
kraemr ce8f0a8c4b fixed the atrocious interface_discovery 2025-10-17 12:40:18 +02:00
Robin Krämer 321aae5b86 Merge pull request #820 from qitechgmbh/changelog-210
updated Changelog
2025-10-16 15:16:05 +02:00
kraemr 7712ea2d1f updated Changelog 2025-10-16 15:15:36 +02:00
Robin Krämer 175f64f784 Merge pull request #804 from qitechgmbh/803-add-winder-gearbox-conversion
803 add winder gearbox conversion
2025-10-16 12:39:14 +02:00
ChristianandCopilot 9357960463 Update electron/src/machines/winder/winder2/useWinder.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-16 12:22:54 +02:00
Christian 4d043906bc Remove unnecessary whitespace in puller speed calculation 2025-10-16 12:22:54 +02:00
Christian b99b7f796b Reset target speed to 0 when setting gear ratio and adjust puller speed calculation based on gear ratio 2025-10-16 12:22:54 +02:00
Christian a7b0414613 Add gear ratio calculation and display in Winder2 control and presets 2025-10-16 12:22:54 +02:00
Christian 13c608b11a Add gear ratio functionality to puller speed controller
- Introduced gear ratio enum with options for 1:5 and 1:10.
- Updated puller speed controller to include gear ratio state.
- Implemented gear ratio selection in Winder2Settings component.
- Added corresponding mutation and state management in useWinder2 hook.
2025-10-16 12:22:54 +02:00
Oshgnacknak d14fb91736 Simpliy panic handling and fix backtrace (#809)
* Hide memory locking behind feature flag

* Simplify panic handling
2025-10-15 21:09:05 +02:00
Oshgnacknak 6beaa444af Simplify panic handling 2025-10-15 17:08:37 +02:00
Oshgnacknak 3b552558d4 Hide memory locking behind feature flag 2025-10-15 17:08:37 +02:00
Robin Krämer 9cc63bf8fb Merge pull request #805 from qitechgmbh/743-electron-doesnt-use-gpu-for-hw-acceleration
Remove bad hardware acceleration flag
2025-10-15 10:31:04 +02:00
Oshgnacknak 70ce7625b2 Remove bad hardware acceleration flag 2025-10-13 15:00:21 +02:00
Robin Krämer 9936764125 Merge pull request #717 from qitechgmbh/700-add-a-show-from-option-for-graphs
700 add a show from option for graphs
2025-10-13 12:17:26 +02:00
Christian a0f09e1671 Improve formatting and adjust future time validation messages in TimeInput component 2025-10-13 12:14:09 +02:00
Christian 1100b880f9 Add validation to prevent future time selection in TimeInput component 2025-10-13 12:14:09 +02:00
Christian 6224f33ba5 fix eslint 2025-10-13 12:14:09 +02:00
Christian b0a9b5a6d2 Fix styling 2025-10-13 12:14:09 +02:00
ChristianandCopilot ac7247c87a Update electron/src/components/time/TimeInput.tsx
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-13 12:14:09 +02:00
Christian 2ea459f3be Refactor button styles and improve formatting in GraphControls and TimeInput components 2025-10-13 12:14:09 +02:00
Christian 25250f640f Add showFromTimestamp functionality to graph components and implement TimeInput for time selection 2025-10-13 12:14:09 +02:00
Robin Krämer 57a0cc8af4 Merge pull request #776 from qitechgmbh/775-add-explanation-of-padding-to-the-winder-manual
Add padding parameter explanation to manual for filament winding
2025-10-13 12:13:53 +02:00
Christian 5ebb978eae Add padding example image to the winder docs 2025-10-13 12:09:20 +02:00
Christian d647ee6ebe fix format 2025-10-13 12:09:20 +02:00
Christian fea203ae7a Add padding parameter explanation to manual for filament winding 2025-10-13 12:09:20 +02:00
Robin Krämer b5eb413d37 Merge pull request #791 from qitechgmbh/788-fix-remote-setting-reset-and-update-label-in-update-tab
Fix remote setting reset and update label to "Repository Owner" in Update tab
2025-10-13 12:08:43 +02:00
Christian aa1e5c4f39 Fix typos in GitHub repository owner label and description 2025-10-13 12:05:31 +02:00
Christian ee91934bf7 Refactor Github source management: integrate condition store and update UI labels 2025-10-13 12:05:31 +02:00
Milan von dem Bussche 1fa04eae9a Update README.md
Added YouTube Explainer Video about QiTech Control
2025-10-10 18:41:46 +02:00
Robin Krämer 98e1269f6f Merge pull request #800 from qitechgmbh/changelog-2.9.1
updated Changelog
2025-10-10 15:07:51 +02:00
kraemr 8f640fcb21 updated Changelog 2025-10-10 15:06:30 +02:00
Robin Krämer 6c120b7fe9 Merge pull request #796 from qitechgmbh/795-winding-when-puller-reverse-doesnt-work-in-adaptive
adaptive spool speed now takes negative speeds into account,
2025-10-08 18:12:23 +02:00
kraemr 033b93b9da puller speed now is always positive in frontend 2025-10-08 17:49:55 +02:00
kraemr 6092358e87 adaptive spool speed now takes negative speeds into account,
aswell as Auto Stop Spool
2025-10-08 17:40:50 +02:00
Robin Krämer 71de39e871 Merge pull request #793 from qitechgmbh/790-refactor-aquapath-newrs-to-match-others
790 refactor aquapath newrs to match others
2025-10-08 16:29:49 +02:00
joe 54ffa193c3 fixed fmt 2025-10-08 16:23:40 +02:00
joe 03164f1ceb refactor(aquapath1): cleanup and fix device initialization and configuration 2025-10-08 15:45:58 +02:00
Robin Krämer f67a46ab4c Merge pull request #787 from qitechgmbh/786-add-memory-locking
added CAP_IPC_LOCK and added memory locking
2025-10-07 13:54:02 +02:00
kraemr 610790a778 added CAP_IPC_LOCK and memory locking 2025-10-07 13:04:49 +02:00
Robin Krämer 6eaf451ad4 Merge pull request #692 from JanHBade/master
Update README.md
2025-10-07 12:57:27 +02:00
JanHBade 022e0adae3 Update README.md
Minor typo in EtherCAT
2025-10-07 12:57:16 +02:00
Robin Krämer 6e6d4b31e7 Merge pull request #746 from qitechgmbh/640-improve-scroll-bar-styling-for-manuals-to-resemble-macos-style
Update scrollbar styling to be similar to macos
2025-10-07 12:45:12 +02:00
Christian 0dafad3fb5 fix code stye 2025-10-07 12:40:01 +02:00
Christian 534de55239 Refactor scrollbar styles to apply only to html and body elements 2025-10-07 12:40:01 +02:00
Christian d9f92bcdf2 Refactor scrollbar styles for Markdown and Terminal components to be similar to MacOS styling 2025-10-07 12:40:01 +02:00
Robin Krämer 1570299e38 Merge pull request #782 from qitechgmbh/781-refactor-winderv2-emit-functions-to-emitrs
refactored winder2's emit methods to emit.rs
2025-10-06 17:01:38 +02:00
kraemr c95f6f3bd9 refactored winder2's emit methods to emit.rs 2025-10-06 16:36:59 +02:00
Robin Krämer 9fcde2a234 Merge pull request #780 from qitechgmbh/772-remove-dependencies-that-are-not-necessary
decreased compile time by removing jemalloc, and using only f64 uom
2025-10-06 16:05:08 +02:00
kraemr a2ffc41e05 decreased compile time by removing jemalloc, and using only f64 uom 2025-10-06 16:02:29 +02:00
Robin Krämer ff13baf257 Merge pull request #779 from qitechgmbh/changelog-2.9.0
updated Changelog
2025-10-06 14:25:39 +02:00
kraemr 8f5df07c19 updated Changelog 2025-10-06 14:24:43 +02:00
Robin Krämer 299b38515a Merge pull request #778 from qitechgmbh/777-increase-max-step-size
Increased Winder Step Size
2025-10-06 14:12:24 +02:00
kraemr 2b073aeb76 Increased Winder Step Size 2025-10-06 13:56:20 +02:00
Robin Krämer 7a6f92f8a4 Merge pull request #771 from qitechgmbh/770-update-machine-implementation-status-in-the-readme
Update machine documentation to reflect current implementation status
2025-10-02 17:07:13 +02:00
Christian 2e283ea95f Update machine documentation to reflect current implementation status 2025-10-02 17:03:55 +02:00
Robin Krämer 829bb5ea1c Merge pull request #625 from qitechgmbh/improved-disconnect-handling
Introduce machine connection
2025-10-02 14:26:07 +02:00
Oshgnacknak 0be18aaed0 Derive machines from macro again
This time we also add get_machine_identification_unique.

Also fix import warnings
2025-10-02 13:58:54 +02:00
Oshgnacknak ffe0f09061 Clean up machine event creation 2025-10-01 16:50:04 +02:00
nixos-install.sh 730d61a18d Don't clear ethercat slots, we want to keep the namespaces 2025-10-01 15:57:03 +02:00
Oshgnacknak fc07525976 Don't remove, only disconnect serial machines
This allows use to keep the namespaces
2025-10-01 15:57:03 +02:00
Oshgnacknak c48953aa6b Introduce machine connection
This is to hold on to the namespace of
a machine even if the devices of that
machine has disconnected and to allow
reattaching to the namespace once the
devices reconnects.

Additionally to the new struct,
a wast refactor took place cleaning up
connections to machines, cross connections
between machines the way we downcast
machines downcasts.
2025-10-01 15:57:03 +02:00
Melvin Spieß 0c948b436d Merge pull request #764 from qitechgmbh/dependabot/cargo/axum-0.8.6
Bump axum from 0.8.4 to 0.8.6
2025-10-01 12:05:48 +02:00
Robin Krämer 1b3f92dd1c Merge pull request #725 from qitechgmbh/425-fix-terminal-component-issues-v2
425 fix terminal component issues v2
2025-10-01 11:58:55 +02:00
dependabot[bot] c29c549a1c Bump axum from 0.8.4 to 0.8.6
Bumps [axum](https://github.com/tokio-rs/axum) from 0.8.4 to 0.8.6.
- [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.4...axum-v0.8.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 09:57:52 +00:00
kraemr 17622cacde set log level to "info" for rust 2025-10-01 11:56:29 +02:00
kraemr 0b423454ec fixed line duplication in Terminal Component on Update Page 2025-10-01 11:56:29 +02:00
kraemr 81ec98f670 dont clear log, causes react errors 2025-10-01 11:56:29 +02:00
93094049+kraemr@users.noreply.github.com 1aed425544 eslint bypass for control character 2025-10-01 11:56:29 +02:00
93094049+kraemr@users.noreply.github.com da080ace68 try using clear functions before interactions 2025-10-01 11:56:29 +02:00
93094049+kraemr@users.noreply.github.com ded8dd2d2d added react-virtualized 2025-10-01 11:56:29 +02:00
93094049+kraemr@users.noreply.github.com edd83ba559 added virtualized list 2025-10-01 11:56:29 +02:00
Melvin Spieß 7d1d8559a5 Merge pull request #672 from qitechgmbh/671-refactor-machine-creation
reduced code duplication in new.rs of machines
2025-10-01 11:56:17 +02:00
kraemr f0bb9bfed1 fixed lifetimes of get_ethercat_device and refactored buffer 2025-10-01 11:52:13 +02:00
93094049+kraemr@users.noreply.github.com c6ea057736 renamed helper function 2025-10-01 11:52:13 +02:00
93094049+kraemr@users.noreply.github.com 90a00198a7 reduced code duplication in new.rs of machines 2025-10-01 11:52:12 +02:00
Melvin Spieß f8241547fe Merge pull request #762 from qitechgmbh/dependabot/cargo/erased-serde-0.4.8
Bump erased-serde from 0.4.6 to 0.4.8
2025-10-01 11:50:54 +02:00
Melvin Spieß 1cb5d51c13 Merge pull request #760 from qitechgmbh/dependabot/npm_and_yarn/electron/tanstack/react-router-1.130.17
Bump @tanstack/react-router from 1.130.12 to 1.130.17 in /electron
2025-10-01 11:38:53 +02:00
Melvin Spieß c42cdf8db1 Merge pull request #759 from qitechgmbh/dependabot/npm_and_yarn/electron/radix-ui/react-navigation-menu-1.2.14
Bump @radix-ui/react-navigation-menu from 1.2.13 to 1.2.14 in /electron
2025-10-01 11:38:22 +02:00
Melvin Spieß 69014ef8c6 Merge pull request #758 from qitechgmbh/dependabot/npm_and_yarn/electron/types/react-19.1.16
Bump @types/react from 19.1.9 to 19.1.16 in /electron
2025-10-01 11:35:53 +02:00
Melvin Spieß 5faab29a2b Merge pull request #763 from qitechgmbh/dependabot/cargo/libc-0.2.176
Bump libc from 0.2.175 to 0.2.176
2025-10-01 11:35:31 +02:00
dependabot[bot] 9a8b16ca69 Bump @radix-ui/react-navigation-menu from 1.2.13 to 1.2.14 in /electron
Bumps [@radix-ui/react-navigation-menu](https://github.com/radix-ui/primitives) from 1.2.13 to 1.2.14.
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-navigation-menu"
  dependency-version: 1.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 09:32:49 +00:00
Melvin Spieß 53f3c66214 Merge pull request #765 from qitechgmbh/dependabot/cargo/regex-1.11.3
Bump regex from 1.11.2 to 1.11.3
2025-10-01 11:30:59 +02:00
dependabot[bot] 432bf0d6ba Bump libc from 0.2.175 to 0.2.176
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.175 to 0.2.176.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.176/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.175...0.2.176)

---
updated-dependencies:
- dependency-name: libc
  dependency-version: 0.2.176
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 09:30:49 +00:00
dependabot[bot] f3990f35f1 Bump erased-serde from 0.4.6 to 0.4.8
Bumps [erased-serde](https://github.com/dtolnay/erased-serde) from 0.4.6 to 0.4.8.
- [Release notes](https://github.com/dtolnay/erased-serde/releases)
- [Commits](https://github.com/dtolnay/erased-serde/compare/0.4.6...0.4.8)

---
updated-dependencies:
- dependency-name: erased-serde
  dependency-version: 0.4.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 09:30:40 +00:00
dependabot[bot] 6f17412363 Bump regex from 1.11.2 to 1.11.3
Bumps [regex](https://github.com/rust-lang/regex) from 1.11.2 to 1.11.3.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.11.2...1.11.3)

---
updated-dependencies:
- dependency-name: regex
  dependency-version: 1.11.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 09:28:49 +00:00
dependabot[bot] 68907b9750 Bump @types/react from 19.1.9 to 19.1.16 in /electron
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 19.1.9 to 19.1.16.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.1.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 09:26:34 +00:00
dependabot[bot] e7007e1980 Bump @tanstack/react-router from 1.130.12 to 1.130.17 in /electron
Bumps [@tanstack/react-router](https://github.com/TanStack/router/tree/HEAD/packages/react-router) from 1.130.12 to 1.130.17.
- [Release notes](https://github.com/TanStack/router/releases)
- [Commits](https://github.com/TanStack/router/commits/v1.130.17/packages/react-router)

---
updated-dependencies:
- dependency-name: "@tanstack/react-router"
  dependency-version: 1.130.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 09:26:27 +00:00
Melvin Spieß 3bcd96d73c Merge pull request #766 from qitechgmbh/dependabot/cargo/anyhow-1.0.100
Bump anyhow from 1.0.99 to 1.0.100
2025-10-01 11:09:51 +02:00
dependabot[bot] 8a77c62a5e Bump anyhow from 1.0.99 to 1.0.100
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.99 to 1.0.100.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.99...1.0.100)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-version: 1.0.100
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-01 02:24:32 +00:00
kraemr 93add82ab5 updated Changelog 2025-09-30 15:29:27 +02:00
Robin Krämer 690b1f7ab3 Merge pull request #757 from qitechgmbh/revert-ethercat-setup
fixed ethercat setup timeouts and retry
2025-09-29 17:19:33 +02:00
kraemr 3c07b94e3a fixed ethercat setup timeouts and retry 2025-09-29 17:18:49 +02:00
Robin Krämer 3b63b55859 Merge pull request #756 from qitechgmbh/fix-winder-regression
fixed winder regression due to refactor
2025-09-29 17:10:33 +02:00
kraemr f87982c465 fixed winder regression due to refactor 2025-09-29 17:01:04 +02:00
Robin Krämer b03cedb55c Merge pull request #447 from qitechgmbh/fix/438
Implementation AquaPath
2025-09-29 15:42:32 +02:00
kraemr f5934efa6c restored mock-machine functionality 2025-09-29 15:39:25 +02:00
kraemr 186619cd48 fixed compilation error 2025-09-29 15:33:25 +02:00
Darmenov Alisher 583d4c1a4d resolved comment 2025-09-29 15:33:25 +02:00
Anatomik5 63bee54948 AquapathV1 2025-09-29 15:33:25 +02:00
Robin Krämer 1523d64ed7 Merge pull request #749 from qitechgmbh/738-add-mock-machines
738 add mock machines
2025-09-29 11:32:39 +02:00
kraemr 48fdde61dc further improved readability by moving feature flags to mod.rs 2025-09-29 11:30:54 +02:00
kraemr 7bc700ac5f when selecting mock-machine feature extruder now appears as well 2025-09-29 11:30:54 +02:00
93094049+kraemr@users.noreply.github.com bdd6147f92 rewrote extruder and winder, also added mock-machine for extruder 2025-09-29 11:30:54 +02:00
93094049+kraemr@users.noreply.github.com f7f65bdaaa refactored all emit functions out of mod.rs for clarity for extruder 2025-09-29 11:30:54 +02:00
Robin Krämer 232b0782ca Merge pull request #754 from qitechgmbh/748-add-feature-flag-or-env-var-for-setting-loop-timeout
748 add feature flag or env var for setting loop timeout
2025-09-29 11:30:19 +02:00
kraemr c3c36a26b6 also added development-build flag to irq affinity code 2025-09-25 17:11:18 +02:00
kraemr 7f22bdfbf2 added feature development-build to set timeout 2025-09-25 17:06:42 +02:00
Robin Krämer ff4c579f7b Merge pull request #673 from qitechgmbh/632-add-documentation-for-creating-a-new-machine
added documentation for adding a machine
2025-09-25 15:33:41 +02:00
93094049+kraemr@users.noreply.github.com 9683882775 added documentation for adding a machine 2025-09-25 15:32:33 +02:00
Robin Krämer 893e01532d Merge pull request #753 from qitechgmbh/633-update-documentation-for-adding-new-devices
updated devices documentation by adding an example and explaining the architecture
2025-09-25 14:53:17 +02:00
kraemr 713f78fa4e updated devices documentation by adding an example
and explaining how devices fit into the architecture
2025-09-25 14:50:32 +02:00
Robin Krämer f444347fb9 Merge pull request #752 from qitechgmbh/751-document-performancestability-tweaks-made
added performance and stabillity documentation
2025-09-25 11:37:04 +02:00
kraemr ea03c0f755 added performance and stabillity documentation 2025-09-25 11:31:53 +02:00
Robin Krämer e283a0f164 Merge pull request #745 from qitechgmbh/744-windermode-switch-from-modewind-to-modepull-or-modehold-disables-the-wind-button
fixed emit state after state change
2025-09-23 15:41:28 +02:00
snacj 6820206d78 fixed emit state after state change 2025-09-23 15:38:47 +02:00
Robin Krämer 5069ee425c Merge pull request #742 from qitechgmbh/681-remove-unused-or-redundant-crates
removed unused crates
2025-09-23 13:37:48 +02:00
kraemr f93f342508 added ignores for cargo machete 2025-09-23 13:17:29 +02:00
kraemr c6d3563101 removed unused crates 2025-09-23 12:56:24 +02:00
Robin Krämer f2f893c917 Merge pull request #741 from qitechgmbh/739-add-roundness-value-to-laser-graph-and-control-page
Added Roundness Value to 2 Axis Laser
2025-09-22 14:27:30 +02:00
Robin KrämerandCopilot bbbca4085d Update server/src/machines/laser/mod.rs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-22 14:24:42 +02:00
Robin KrämerandCopilot 4fdf461520 Update server/src/machines/laser/mod.rs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-22 14:23:37 +02:00
snacj dcfc6728ba Made Roundness unitless 2025-09-22 14:10:25 +02:00
snacj 2673a92f50 Added Roundness Value to 2 Axis Laser 2025-09-22 13:39:59 +02:00
Robin Krämer 4d72185ba9 Merge pull request #675 from qitechgmbh/512-add-troubleshooting-documentation
512 add troubleshooting documentation
2025-09-18 19:44:43 +02:00
93094049+kraemr@users.noreply.github.com ade4f5cd2b updated troubleshooting.md 2025-09-18 19:41:15 +02:00
93094049+kraemr@users.noreply.github.com cf1ebf2b3d added troubleshooting.md 2025-09-18 19:41:15 +02:00
Robin Krämer 6b5cb7d87e Merge pull request #737 from qitechgmbh/fix_state_handling
Fix state handling
2025-09-17 18:26:01 +02:00
93094049+kraemr@users.noreply.github.com 4894bbc231 fixed all state issues 2025-09-17 18:20:31 +02:00
Robin Krämer 721c5a99ee Merge pull request #736 from qitechgmbh/less_amps_spool_test
Less amps spool test
2025-09-17 17:28:23 +02:00
93094049+kraemr@users.noreply.github.com 36fccc80cb set puller amps to 2.7A 2025-09-17 17:21:15 +02:00
93094049+kraemr@users.noreply.github.com e14fe86306 testing with less current 2025-09-17 17:21:15 +02:00
Robin Krämer 95c9f99961 Merge pull request #735 from qitechgmbh/fix_compiler_flags
Fix compiler flags
2025-09-17 12:53:50 +02:00
93094049+kraemr@users.noreply.github.com 546189b9c8 removed log::error 2025-09-17 12:47:46 +02:00
93094049+kraemr@users.noreply.github.com dfbc2a65c0 hopefully all compiler flags now work as expected 2025-09-17 12:47:46 +02:00
Robin Krämer 2038d79e18 Merge pull request #734 from qitechgmbh/changelog-v2.7.0
updated Changelog for 2.7.0
2025-09-17 11:47:54 +02:00
93094049+kraemr@users.noreply.github.com 188caf397b updated Changelog for 2.7.0 2025-09-17 11:44:22 +02:00
Robin Krämer 13138b1d7d Merge pull request #726 from qitechgmbh/video-streaming
Add video streaming api
2025-09-16 18:25:34 +02:00
Icedsoda 5466efd8c3 add video streaming api 2025-09-16 18:23:36 +02:00
Robin Krämer 61f0dc92d1 Merge pull request #733 from qitechgmbh/685-use-nixos-cache-server-for-private-packages
685 use nixos cache server for private packages
2025-09-16 17:49:32 +02:00
93094049+kraemr@users.noreply.github.com 073c174203 removed unnecessary comment 2025-09-16 17:44:03 +02:00
93094049+kraemr@users.noreply.github.com 1d0f7231e5 added substituter
fixed nix config

actually fixed config now

test

removed git hash as version

nix sucks

removed gitcommitHash from overlays

added fake version

fixed compile script

test

fixed script

updated ip

test

test 2

uppdated script and fixed config

to check for caching

test

fixed config

test

test

extra-subs changed to trusted-public-keys

test

test

removed substituter

removed print statements
2025-09-16 17:44:03 +02:00
93094049+kraemr@users.noreply.github.com 9401c81581 added scripts to compile our nix packages 2025-09-16 17:44:03 +02:00
Robin Krämer 936e54f3e2 Merge pull request #731 from qitechgmbh/666-implement-laserv2
666 implement laserv2
2025-09-16 16:54:29 +02:00
snacj 0c02fc1ce0 Added Comment do explain 45deg rotation 2025-09-16 16:52:12 +02:00
snacj c723f1df00 Fixed Graphs to work with 2axis and 1 axis 2025-09-16 16:52:12 +02:00
snacj c65a3f7fb1 Renamed Values and changed to one Graph 2025-09-16 16:52:12 +02:00
snacj a0c59f9bb6 Added Const for graph color. 2025-09-16 16:52:12 +02:00
snacj 069dcd65bb Removed tracing::info! 2025-09-16 16:52:12 +02:00
snacj 5331d39c1d Changed Visual of Diameter to use x and y values 2025-09-16 16:52:12 +02:00
snacj 2c928e6158 Implemented 2-Axis Laser 2025-09-16 16:52:12 +02:00
snacj 3ae17479cb Adjusted Frontend to work with 2-Axis Laser. 2025-09-16 16:52:12 +02:00
93094049+kraemr@users.noreply.github.com b3589755f5 fixed winder events 2025-09-16 16:41:38 +02:00
Robin Krämer e7f97ff4d4 Merge pull request #729 from qitechgmbh/728-set_irq_handler_affinity-doesnt-work-on-macos
fixed target_os flags for set_irq_affinity
2025-09-16 15:37:40 +02:00
93094049+kraemr@users.noreply.github.com 7b4a3b52b3 fixed target_os flags for set_irq_affinity 2025-09-16 15:30:16 +02:00
Robin Krämer 310fd99730 Merge pull request #711 from qitechgmbh/694-over-time-nixos-generations-takes-up-all-diskspace-even-when-deleted
added collect-garbage on each delete
2025-09-15 19:38:31 +02:00
93094049+kraemr@users.noreply.github.com 26473f5596 collect-garbage does not exist, replaced with nix store gc 2025-09-15 19:36:46 +02:00
93094049+kraemr@users.noreply.github.com ead8a02b6a added collect-garbage on each delete 2025-09-15 19:36:46 +02:00
Robin Krämer b2ca9cf792 Merge pull request #712 from qitechgmbh/708-refactor-nix-config-so-that-no-dynamic-installinfonix-is-created
Read git information during build instead of addign new commit via installInfo.nix
2025-09-15 18:29:19 +02:00
nixos-install.sh 301c254446 Pass git info into gitInfo.nix via env vars 2025-09-15 17:57:43 +02:00
Robin Krämer 769091d271 Merge pull request #720 from qitechgmbh/714-pressing-the-same-button-twice-triggers-isloading
714 pressing the same button twice triggers isloading
2025-09-15 16:55:05 +02:00
93094049+kraemr@users.noreply.github.com 1bc7907ba0 all machines now use old emit_state again, but extruder uses custom maybe_emit_state_hash
fixed compile error after merge conflict
2025-09-15 16:49:47 +02:00
93094049+kraemr@users.noreply.github.com 8260c606dc removed maybe_emit_state from every machine except extruder 2025-09-15 16:48:09 +02:00
Christian c0b7e563c5 Merge pull request #713 from qitechgmbh/596-extend-total-power-output-tile-2
596 extend total power output tile
2025-09-15 16:10:45 +02:00
Melvin Spieß 9062e15712 Merge pull request #727 from qitechgmbh/722-remove-estimated-diameter-value
722 remove estimated diameter value
2025-09-15 15:34:57 +02:00
snacj 134da6fad3 fixed formatting 2025-09-15 15:25:31 +02:00
snacj 4cec6ef711 Changed Radius to Factor 2025-09-15 15:13:09 +02:00
snacj 36d64c85fb Removed Estimated Diameter Value 2025-09-15 15:06:30 +02:00
Robin Krämer 8e00019023 Merge pull request #721 from qitechgmbh/as-any-mut
Add `Machin::as_any_mut`
2025-09-15 13:44:51 +02:00
Icedsoda 575d4d2d26 Add Machin::as_any_mut 2025-09-15 13:26:47 +02:00
Robin Krämer 19e9ebc20f Merge pull request #719 from qitechgmbh/718-ethercat-timeout-on-setup
set wait_loop_delay to zero
2025-09-15 09:34:00 +02:00
93094049+kraemr@users.noreply.github.com dd4476b786 set wait_loop_delay to zero 2025-09-15 09:32:20 +02:00
Christian fc283a64a7 Merge branch 'master' into 596-extend-total-power-output-tile-2 2025-09-13 17:05:56 +02:00
Robin Krämer b6dc1d35c6 Merge pull request #710 from qitechgmbh/707-make-adaptive-mode-for-winder-default
changed MinMax to Adaptive by default
2025-09-11 15:27:20 +02:00
93094049+kraemr@users.noreply.github.com 4f542510d6 changed MinMax to Adaptive by default 2025-09-11 15:23:08 +02:00
Oshgnacknak 0ca334decc Disable hardening in nix develop shell (#705)
Hardening causes jmalloc to fail at compilation
2025-09-11 12:57:34 +02:00
Robin Krämer 239a926301 Merge pull request #706 from qitechgmbh/703-fix-permissions-for-set_irq_affinity
703 fix permissions for set irq affinity
2025-09-11 11:01:27 +02:00
93094049+kraemr@users.noreply.github.com 9f4c7fbedd fixed permissions 2025-09-11 10:59:21 +02:00
93094049+kraemr@users.noreply.github.com 6a9de48bb7 added extra logging 2025-09-11 10:59:21 +02:00
93094049+kraemr@users.noreply.github.com d9fc22399a added DAC_OVERRIDE CAP 2025-09-11 10:59:21 +02:00
Robin Krämer f41b0dc0c2 Merge pull request #697 from qitechgmbh/dependabot/npm_and_yarn/electron/vite-6.3.6
Bump vite from 6.3.5 to 6.3.6 in /electron
2025-09-10 15:58:37 +02:00
dependabot[bot] 14233c0aa1 Bump vite from 6.3.5 to 6.3.6 in /electron
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.5 to 6.3.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.3.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-10 13:48:41 +00:00
Robin Krämer 58e56c4226 Merge pull request #702 from qitechgmbh/701-fix-optimistic-state-handling
701 fix optimistic state handling
2025-09-10 15:01:24 +02:00
93094049+kraemr@users.noreply.github.com 8ee184475e only return state not optimisticstate 2025-09-10 14:56:05 +02:00
93094049+kraemr@users.noreply.github.com be9c5b1be5 fixed optimistic state handling 2025-09-10 14:55:08 +02:00
Robin Krämer 8411f8f2b9 Merge pull request #698 from qitechgmbh/696-pin-ethernet-interrupt-handler-to-the-same-core-as-ethercrab-task
696 pin ethernet interrupt handler to the same core as ethercrab task
2025-09-10 14:19:59 +02:00
93094049+kraemr@users.noreply.github.com 5441a7a126 changed lint, renamed irq_handling funcs, refactor 2025-09-10 14:08:32 +02:00
93094049+kraemr@users.noreply.github.com b2425de273 fixed non unix compilation 2025-09-10 13:55:59 +02:00
93094049+kraemr@users.noreply.github.com 82d5e73129 removed unused import 2025-09-10 13:45:58 +02:00
93094049+kraemr@users.noreply.github.com 31189ebdef refactored irq_handling into own file 2025-09-10 13:45:58 +02:00
93094049+kraemr@users.noreply.github.com ee1720fa95 added code to set irq handler affinity 2025-09-10 13:45:58 +02:00
Robin Krämer 72dc0634f3 Merge pull request #693 from qitechgmbh/clippy-fix
Use Cargo Clippy
2025-09-10 13:43:51 +02:00
Icedsoda 13f5125daa cargo clippy --fix 2025-09-10 12:14:19 +02:00
Icedsoda 2b6904b145 configure clippy 2025-09-10 12:14:19 +02:00
Robin Krämer f776a4ba19 Merge pull request #695 from qitechgmbh/688-pressure-and-rpm-are-switched
reverse prop doesnt make sense on Extruder2ControlPage Regulation
2025-09-09 16:54:29 +02:00
93094049+kraemr@users.noreply.github.com a7c8cb218f reverse prop doesnt make sense on Extruder2ControlPage Regulation 2025-09-09 16:49:31 +02:00
Robin Krämer 3cb2d20df9 Merge pull request #690 from qitechgmbh/sync-act
Make `act` method non-async
2025-09-09 14:21:22 +02:00
Icedsoda ee4b5b15d7 Make act method non-async 2025-09-09 14:18:21 +02:00
93094049+kraemr@users.noreply.github.com 299e6314b8 Revert "using virtualized list"
This reverts commit 01170e1a1b.
2025-09-09 13:15:46 +02:00
93094049+kraemr@users.noreply.github.com 01170e1a1b using virtualized list 2025-09-09 13:13:52 +02:00
Robin Krämer 6fe2e498aa Merge pull request #686 from qitechgmbh/668-frontend-buttons-are-flickering
Fixed Buttons Flickering
2025-09-09 11:21:18 +02:00
93094049+kraemr@users.noreply.github.com fc7262f8f0 Fixed Buttons Flickering 2025-09-09 11:16:56 +02:00
Robin Krämer 880d2ddcb0 Merge pull request #684 from qitechgmbh/683-ethercat-loop-broken
683 ethercat loop broken
2025-09-08 14:06:02 +02:00
93094049+kraemr@users.noreply.github.com e9ea378ef0 set loop timer to 500us 2025-09-08 13:44:33 +02:00
93094049+kraemr@users.noreply.github.com 145ad7ed2c added 1ms delay 2025-09-08 13:25:46 +02:00
93094049+kraemr@users.noreply.github.com d5ce65c868 added 1ms delay 2025-09-08 13:23:39 +02:00
93094049+kraemr@users.noreply.github.com 1bc76dfe09 wait_loop_delay set to 0 2025-09-08 12:52:45 +02:00
Robin Krämer 17e82cf5ea Merge pull request #680 from qitechgmbh/402-many-journald-logs-slow-down-the-frontend
402 many journald logs slow down the frontend
2025-09-08 12:04:59 +02:00
93094049+kraemr@users.noreply.github.com 926650802b reduces Logs, by A LOT 2025-09-08 12:01:01 +02:00
Robin Krämer fba7c0375d Merge pull request #677 from qitechgmbh/676-reduce-heap-allocationsdeallocations-in-machineact
676 reduce heap allocationsdeallocations in machineact
2025-09-08 11:51:46 +02:00
93094049+kraemr@users.noreply.github.com 27be5b8ec4 fixed extruder 2025-09-08 11:49:42 +02:00
93094049+kraemr@users.noreply.github.com d036246b2a refactored all machines 2025-09-08 11:49:42 +02:00
93094049+kraemr@users.noreply.github.com 59a9b15632 started refactoring MachineAct 2025-09-08 11:49:42 +02:00
Robin Krämer c09e061390 Merge pull request #679 from qitechgmbh/678-remove-loopthrottle
678 remove loopthrottle
2025-09-08 11:49:22 +02:00
93094049+kraemr@users.noreply.github.com 02f6dffaff fixed compile error and replaced loopthrottle with sleep 2025-09-08 10:00:37 +02:00
93094049+kraemr@users.noreply.github.com 509d4c8734 removed unused import 2025-09-08 09:57:03 +02:00
93094049+kraemr@users.noreply.github.com 084fdef754 wait_loop_delay of 1ms 2025-09-08 09:56:38 +02:00
93094049+kraemr@users.noreply.github.com a2f21da355 remove LoopThrottle 2025-09-08 09:55:47 +02:00
Robin Krämer b722502f5b Merge pull request #674 from qitechgmbh/dependabot/npm_and_yarn/electron/electron-37.3.1
Bump electron from 37.2.6 to 37.3.1 in /electron
2025-09-04 19:04:55 +02:00
dependabot[bot] ab45407763 Bump electron from 37.2.6 to 37.3.1 in /electron
Bumps [electron](https://github.com/electron/electron) from 37.2.6 to 37.3.1.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v37.2.6...v37.3.1)

---
updated-dependencies:
- dependency-name: electron
  dependency-version: 37.3.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 18:57:30 +02:00
Robin Krämer dd4fbbdafd Merge pull request #647 from qitechgmbh/dependabot/npm_and_yarn/electron/radix-ui/react-collapsible-1.1.12
Bump @radix-ui/react-collapsible from 1.1.11 to 1.1.12 in /electron
2025-09-04 12:25:18 +02:00
dependabot[bot] c8c609d88b Bump @radix-ui/react-collapsible from 1.1.11 to 1.1.12 in /electron
Bumps [@radix-ui/react-collapsible](https://github.com/radix-ui/primitives) from 1.1.11 to 1.1.12.
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-collapsible"
  dependency-version: 1.1.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 12:22:05 +02:00
Robin Krämer 92a4905b44 Merge pull request #649 from qitechgmbh/dependabot/npm_and_yarn/electron/zustand-5.0.8
Bump zustand from 5.0.7 to 5.0.8 in /electron
2025-09-04 12:21:47 +02:00
dependabot[bot] 1c2aea7cb1 Bump zustand from 5.0.7 to 5.0.8 in /electron
Bumps [zustand](https://github.com/pmndrs/zustand) from 5.0.7 to 5.0.8.
- [Release notes](https://github.com/pmndrs/zustand/releases)
- [Commits](https://github.com/pmndrs/zustand/compare/v5.0.7...v5.0.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 12:18:57 +02:00
Robin Krämer 44127bec42 Merge pull request #669 from qitechgmbh/603-add-electrical-diagrams-to-the-machines-manuals
Implement electrical diagrams into the manuals
2025-09-04 12:12:00 +02:00
Christian f48b97dd24 Add electrical diagram images to the public folder and implement them in the md files for extruder and winder. 2025-09-03 21:28:23 +02:00
Icedsoda 74c107c032 Merge pull request #667 from qitechgmbh/fix-stuck-loading
fixed infinite loading error
2025-09-03 19:23:59 +02:00
93094049+kraemr@users.noreply.github.com c4263dbe84 fixed infinite loading error 2025-09-03 19:00:50 +02:00
Icedsoda a6b7ec914d Add rustdoc 2025-09-03 17:25:18 +02:00
Icedsoda 56c095a10c Merge pull request #446 from qitechgmbh/fix/61
Add `EL4002`
2025-09-03 16:44:09 +02:00
Icedsoda f486aaa31b .mailmap 2025-09-03 16:42:57 +02:00
Darmenov Alisher c4f2f96c2e f 2025-09-03 16:31:34 +02:00
Anatomik5 99988c0445 el4002 and el40xx pdo implementation 2025-09-03 16:28:42 +02:00
Robin Krämer 9c5191a6ae Merge pull request #662 from qitechgmbh/changelog-2.6.0
Update CHANGELOG.md
2025-09-03 15:56:22 +02:00
93094049+kraemr@users.noreply.github.com 2f66715738 added missing Cargo.lock changes 2025-09-03 15:46:31 +02:00
Robin Krämer bcfd579a4b Update CHANGELOG.md 2025-09-03 11:54:44 +02:00
Icedsoda bef13832d6 Merge pull request #659 from qitechgmbh/575-automatically-send-stateevent-when-changed
575 automatically send stateevent when changed
2025-09-03 11:30:40 +02:00
93094049+kraemr@users.noreply.github.com 130ebf1681 changed mock and winder to 30 fps and added missing maybe_emit 2025-09-03 11:29:22 +02:00
93094049+kraemr@users.noreply.github.com 587c8c8fdb removed ethercrab (again) 2025-09-03 11:29:22 +02:00
93094049+kraemr@users.noreply.github.com 1ad9896be4 fixed linter errors 2025-09-03 11:29:22 +02:00
93094049+kraemr@users.noreply.github.com a6cdcf893d removed unused imports 2025-09-03 11:29:22 +02:00
93094049+kraemr@users.noreply.github.com 37ca881ed7 fixed stateschema
fixed stateschema for real this time
2025-09-03 11:29:17 +02:00
93094049+kraemr@users.noreply.github.com c38d7c0056 added maybe emit to mock 2025-09-03 11:27:09 +02:00
93094049+kraemr@users.noreply.github.com 99b9a8710c added maybe_emit to winder2 2025-09-03 11:27:09 +02:00
93094049+kraemr@users.noreply.github.com a859426b06 added maybe_emit to laser 2025-09-03 11:27:09 +02:00
93094049+kraemr@users.noreply.github.com 479908bf59 made changes for extruder and added derive 2025-09-03 11:27:09 +02:00
93094049+kraemr@users.noreply.github.com 3bc6522095 fixed hasher compare func 2025-09-03 11:27:09 +02:00
93094049+kraemr@users.noreply.github.com 6fcce62b04 added hasher_serializer and compare func 2025-09-03 11:27:09 +02:00
93094049+kraemr@users.noreply.github.com 060a7b6dbc refactoring States 2025-09-03 11:27:09 +02:00
93094049+kraemr@users.noreply.github.com 73521ad80a state gets sent when changed 2025-09-03 11:27:09 +02:00
Icedsoda 5c3897db9b Merge pull request #658 from qitechgmbh/65-handbook-for-extruder-v2
added extruder manual
2025-09-03 11:23:27 +02:00
Icedsoda e659ebbfd5 added extruder handbook 2025-09-03 10:14:41 +02:00
Icedsoda 3e98d08a0f Merge pull request #661 from qitechgmbh/lfs
track images in git lfs
2025-09-03 10:09:47 +02:00
Icedsoda 12069c0cd2 track images in git lfs 2025-09-03 10:09:17 +02:00
Robin Krämer a62d492a0a Merge pull request #645 from qitechgmbh/dependabot/cargo/tracing-subscriber-0.3.20
Bump tracing-subscriber from 0.3.19 to 0.3.20
2025-09-02 15:33:58 +02:00
dependabot[bot] 2b65d2ddd5 Bump tracing-subscriber from 0.3.19 to 0.3.20
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.19 to 0.3.20.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20)

---
updated-dependencies:
- dependency-name: tracing-subscriber
  dependency-version: 0.3.20
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 15:28:44 +02:00
Robin Krämer 9bb16d5cc5 Merge pull request #651 from qitechgmbh/dependabot/npm_and_yarn/electron/radix-ui/react-select-2.2.6
Bump @radix-ui/react-select from 2.2.5 to 2.2.6 in /electron
2025-09-02 15:27:52 +02:00
dependabot[bot] 9b8076b357 Bump @radix-ui/react-select from 2.2.5 to 2.2.6 in /electron
Bumps [@radix-ui/react-select](https://github.com/radix-ui/primitives) from 2.2.5 to 2.2.6.
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-select"
  dependency-version: 2.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 15:25:02 +02:00
Robin Krämer c86d34d881 Merge pull request #653 from qitechgmbh/dependabot/cargo/serde_json-1.0.143
Bump serde_json from 1.0.140 to 1.0.143
2025-09-02 15:22:52 +02:00
Robin Krämer 6dd031df24 Merge branch 'master' into dependabot/cargo/serde_json-1.0.143 2025-09-02 14:57:56 +02:00
Robin Krämer 40b33bf28d Merge pull request #652 from qitechgmbh/dependabot/cargo/serialport-4.7.3
Bump serialport from 4.7.2 to 4.7.3
2025-09-02 14:56:26 +02:00
dependabot[bot] 9a93c7bbd9 Bump serialport from 4.7.2 to 4.7.3
Bumps [serialport](https://github.com/serialport/serialport-rs) from 4.7.2 to 4.7.3.
- [Changelog](https://github.com/serialport/serialport-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/serialport/serialport-rs/compare/v4.7.2...v4.7.3)

---
updated-dependencies:
- dependency-name: serialport
  dependency-version: 4.7.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 14:53:38 +02:00
dependabot[bot] 24ef2b1c6c Bump serde_json from 1.0.140 to 1.0.143
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.140 to 1.0.143.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.140...v1.0.143)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 14:52:53 +02:00
Robin Krämer 37359b4058 Merge pull request #654 from qitechgmbh/dependabot/cargo/regex-1.11.2
Bump regex from 1.11.1 to 1.11.2
2025-09-02 14:51:54 +02:00
dependabot[bot] ce19f515d7 Bump regex from 1.11.1 to 1.11.2
Bumps [regex](https://github.com/rust-lang/regex) from 1.11.1 to 1.11.2.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.11.1...1.11.2)

---
updated-dependencies:
- dependency-name: regex
  dependency-version: 1.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 14:49:40 +02:00
Robin Krämer 1359e0a33d Merge pull request #655 from qitechgmbh/dependabot/cargo/anyhow-1.0.99
Bump anyhow from 1.0.98 to 1.0.99
2025-09-02 14:49:29 +02:00
dependabot[bot] 3b234df7e7 Bump anyhow from 1.0.98 to 1.0.99
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.98 to 1.0.99.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.98...1.0.99)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-version: 1.0.99
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 12:48:03 +00:00
Icedsoda b820a433df Merge pull request #641 from qitechgmbh/604-manuals-have-two-scroll-bars
604 manuals have two scroll bars
2025-09-01 14:09:38 +02:00
Icedsoda 9bf061bf6d Merge pull request #657 from qitechgmbh/634-store-graphs-timeframe-globally
added global timeframe store
2025-09-01 14:05:25 +02:00
93094049+kraemr@users.noreply.github.com 84f1fa123f fixed linter error 2025-09-01 14:00:14 +02:00
93094049+kraemr@users.noreply.github.com 91660fd1ec fixed compile errors 2025-09-01 13:58:07 +02:00
93094049+kraemr@users.noreply.github.com 28c4a801a6 added global timeframe store 2025-09-01 13:53:41 +02:00
Robin Krämer 22af6e61af Merge pull request #639 from qitechgmbh/extruder-regulation-mode-order-reversion
Add reverse prop to `SelectionGroupBoolean`
2025-09-01 11:48:37 +02:00
Icedsoda 794a309d71 feat(control): Add reverse prop to SelectionGroupBoolean
This change introduces a `reverse` property to the `SelectionGroupBoolean` component to allow for more flexible UI representation.

- When `reverse` is set to true, the `optionTrue` and `optionFalse` props are swapped.
- This allows the visual "on" state to correspond to a `false` value and vice-versa.
- The prop is implemented on the Inverter Regulation control on the Extruder2 page.
2025-09-01 11:14:38 +02:00
Christian 273d695d0d fix eslint error 2025-08-28 21:12:10 +02:00
Christian 76d5ae613a fix code formatting 2025-08-28 21:12:10 +02:00
Christian 14cca9e8cd fix different padding leading to an line jump when changing from passive to active 2025-08-28 21:12:10 +02:00
Christian 8f5ac3706f Improve active heading detection and scrolling behavior 2025-08-28 21:12:10 +02:00
Oshgnacknak d928f02f2b Merge pull request #642 from qitechgmbh/356-substitute-unmaintained-serial-crate
Improve serial conneciton handling
2025-08-28 17:42:44 +02:00
Oshgnacknak 77c099fa8a Formatting 2025-08-28 17:39:48 +02:00
Oshgnacknak bf9e5e2a3c Allow serial connection errors to bubble up and actually log them 2025-08-28 17:39:48 +02:00
Oshgnacknak b1ccbf3585 Fix permission issues for USB serial devices 2025-08-28 17:39:48 +02:00
Oshgnacknak 1f1050f115 Remove unmaintained serial crate on favor of serialport 2025-08-28 17:39:48 +02:00
Christian df0f86ce7e feat(extruder): Add total energy consumption tracking and related metrics 2025-08-28 16:55:26 +02:00
Icedsoda 007def4a01 Merge pull request #635 from qitechgmbh/627-add-documentation-for-hardware-tests-memtestcyclictest
627 add documentation for hardware tests memtestcyclictest
2025-08-28 16:39:18 +02:00
Icedsoda 3737cc4292 Merge pull request #636 from qitechgmbh/511-add-a-code-styling-document
511 add a code styling document
2025-08-28 16:38:40 +02:00
Icedsoda 8723eb1e55 Merge pull request #638 from qitechgmbh/circular-converter
Add `CircularConverter`
2025-08-28 14:34:29 +02:00
Icedsoda 1192fc28a3 refactor(converters): Introduce CircularConverter
This patch introduces a new `CircularConverter` and refactors the `LinearStepConverter` to use it. This change separates the concerns of geometric conversion from step-based conversion, leading to a more modular and maintainable design.

- **feat(converters): Add `CircularConverter`**
  - A new `CircularConverter` is added to handle all conversions between linear and angular quantities (position, velocity, acceleration, jerk).
  - It can be initialized from radius, diameter, or circumference.
  - This centralizes the geometric conversion logic into a single, well-tested module.

- **refactor(converters): Update `LinearStepConverter`**
  - The `LinearStepConverter` is refactored to compose and use the new `CircularConverter`.
  - It no longer stores `radius` or `circumference` directly.
  - All linear-to-angular calculations are now delegated to the `CircularConverter`, simplifying the `LinearStepConverter`'s implementation and removing duplicated code.
2025-08-28 14:32:07 +02:00
Oshgnacknak 23af0a82ec Merge pull request #637 from qitechgmbh/transmission
Add `Transmission` trait and `FixedTransmission` struct
2025-08-28 14:02:49 +02:00
Icedsoda a2ee1d91d9 Refactor: Introduce generic Transmission trait
This patch refactors the transmission calculation logic by replacing the specific `TransmissionConverter` with a more generic `Transmission` trait.

- A new `transmission` module is created, containing the `Transmission` trait and a `FixedTransmission` implementation.
- The `Transmission` trait provides a generic interface for converting between input and output values for various `uom` types (e.g., `AngularVelocity`, `Angle`, `Velocity`).
- The hardcoded `TransmissionConverter` is removed.
- The `ScrewSpeedController` is updated to use the new `FixedTransmission`, making the ratio configuration more explicit and improving type safety.
2025-08-28 13:45:22 +02:00
93094049+kraemr@users.noreply.github.com eb9830f8a7 updated docs 2025-08-28 13:31:14 +02:00
93094049+kraemr@users.noreply.github.com 3f92b11864 added code-styling backend doc 2025-08-28 13:31:14 +02:00
93094049+kraemr@users.noreply.github.com 96785e8876 updated docs 2025-08-28 13:01:17 +02:00
93094049+kraemr@users.noreply.github.com d36925f616 added memtest docs 2025-08-28 13:01:17 +02:00
Robin Krämer bdc8d7bbd0 Merge pull request #621 from qitechgmbh/no-driver-message-hex
refactor(devices): Display device IDs in hex format
2025-08-27 14:31:46 +02:00
Icedsoda 268685858d refactor(devices): Display device IDs in hex format
- Change the error message for unknown devices to display IDs in hexadecimal format.
- This improves readability and makes it easier to look up device information.
2025-08-27 14:29:22 +02:00
Icedsoda eeafd93e3e Merge pull request #630 from qitechgmbh/629-ethercrab-submodule-on-master
removed ethercrab submodule
2025-08-27 14:28:48 +02:00
93094049+kraemr@users.noreply.github.com 8fa6213f9c removed submodule 2025-08-27 14:23:55 +02:00
Robin Krämer f8b186ec5d Merge pull request #618 from qitechgmbh/fail-early
Fail early in run script if compile fails
2025-08-27 14:23:48 +02:00
Oshgnacknak 4f6a1a0493 Fail early in run script if compile fails 2025-08-27 13:53:24 +02:00
Icedsoda 8ee9b233f6 Merge pull request #608 from qitechgmbh/copilot-instructions
Add copilot-instructions.md
2025-08-27 13:39:18 +02:00
IcedsodaandCopilot 4f6f148c70 add compilot-instructions.md
Update .github/copilot-instructions.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-27 13:36:46 +02:00
Icedsoda 3ea8f38139 Merge pull request #628 from qitechgmbh/changelog-2.5.0
Update CHANGELOG.md
2025-08-27 13:15:27 +02:00
Robin Krämer 3657bbf53e Update CHANGELOG.md 2025-08-27 13:14:30 +02:00
Robin Krämer 92b098c240 Update CHANGELOG.md 2025-08-27 12:48:14 +02:00
Robin Krämer 7350eeb354 Merge pull request #626 from qitechgmbh/591-improve-elctron-frame-times
591 improve electron frame times
2025-08-27 11:37:23 +02:00
93094049+kraemr@users.noreply.github.com 67e2b33db7 intel driver properly loads now
added comments and increased niceness values

added intel gpu driver as module

added mesa mesa_drivers

mesa_drivers has been removed

enable drm render

force i915 enable

fixed config issue

commented kernelPatches

hopefully fixed  now

added intel as xvideo driver

removed driSupport

fixed config file

removed deprecated powermanagement

does not exist ...

downgrade to lts rt kernel

nixos is not behaving

added neofetch and pciutils
2025-08-27 11:33:54 +02:00
Icedsoda d9eb49c37c Merge pull request #623 from qitechgmbh/el2008-identity-b
Add `EL2008_IDENTITY_B`
2025-08-25 16:52:30 +02:00
Icedsoda dba7ce7494 Add EL2008_IDENTITY_B 2025-08-25 16:26:27 +02:00
Icedsoda 50d16feb3d Merge pull request #620 from qitechgmbh/re-introduce-io-uring
Re-introduce `io_uring`
2025-08-25 15:57:27 +02:00
Icedsoda 437f94659c Re-introduce io_uring 2025-08-25 14:20:59 +02:00
Melvin Spieß 53efecc636 Merge pull request #614 from qitechgmbh/613-io_uring-causes-setup-to-fail
removed io_uring
2025-08-21 12:15:16 +02:00
93094049+kraemr@users.noreply.github.com c2371d095e use tx_rx_task import 2025-08-20 16:33:00 +02:00
93094049+kraemr@users.noreply.github.com cacde7868c removed io_uring 2025-08-20 15:33:34 +02:00
Icedsoda 7cf06e6a9d Merge pull request #607 from qitechgmbh/copilot/fix-3cec0eaa-f61d-4097-a3a3-fb391de9205b
Add EtherCAT IO performance metrics logging (fixes #578)
2025-08-20 12:46:16 +02:00
Icedsoda 83688daaf4 Refactor: Rename cycle metrics to loop and adjust config
This patch refactors the performance metrics collection for better clarity and tunes its parameters.

- Renamed "cycle" metrics to "loop" metrics to more accurately reflect the timing of the main processing loop. This change is applied to variable names and log messages.
- Increased the metrics window size from 10,000 to 30,000 samples.
- Extended the logging interval from 5 to 30 seconds for less verbose output.
- Simplified log messages for brevity.
2025-08-20 12:44:49 +02:00
copilot-swe-agent[bot]and1cedsoda f55a6c6569 Add EtherCAT performance metrics module and integrate into app state
Co-authored-by: 1cedsoda <29167146+1cedsoda@users.noreply.github.com>
2025-08-20 12:37:41 +02:00
copilot-swe-agent[bot] e42cc64f5d Initial plan 2025-08-20 12:37:41 +02:00
Robin Krämer 5c0a9f3bb6 Merge pull request #602 from qitechgmbh/594-remove-motor-volts-graph
Remove motor voltage references from extruder components
2025-08-20 12:30:41 +02:00
Christian b719731926 Remove motor voltage references from extruder components 2025-08-20 12:22:56 +02:00
Icedsoda 5b4d81fec2 Merge pull request #599 from qitechgmbh/593-Fix-Graph-Labels-for-Temperatures
Add title to temp value sensors in temperature graph
2025-08-20 12:17:32 +02:00
Robin Krämer 4f50395eb9 Merge pull request #600 from qitechgmbh/595-Extend-Power-Outputs-Graph
Add combined power and motor power graphs to Extruder2GraphsPage, cor…
2025-08-20 12:09:51 +02:00
Christian 30a7134db9 Add combined power and motor power graphs to Extruder2GraphsPage, correct the order of the values by front to back 2025-08-19 13:57:32 +02:00
Robin Krämer 474d64c2a2 Merge pull request #601 from qitechgmbh/598-Images-in-the-manual-are-not-loading
Fix images in the manual not loading
2025-08-19 13:53:52 +02:00
Christian 0293654803 fix code styling 2025-08-18 15:51:54 +02:00
Christian 80ca509998 Move images to electron public folder 2025-08-18 15:26:38 +02:00
Christian a84157bd94 Add title to temp value sensors in temperature graph 2025-08-15 14:18:12 +02:00
Oshgnacknak 0007f14296 Merge pull request #592 from qitechgmbh/540-long-preset-names-mess-with-ui
Fix 540: long preset names mess with ui
2025-08-14 18:06:17 +02:00
Oshgnacknak 307ad265a9 Move preset menu options into hamburger menu 2025-08-14 18:05:02 +02:00
Oshgnacknak 41f36fdf2e Fix ellipsis when rendering long preset names 2025-08-14 18:05:02 +02:00
Oshgnacknak 86c28fef8a Merge pull request #590 from qitechgmbh/changelog_2.4.0_update
Update CHANGELOG.md
2025-08-14 17:37:21 +02:00
Robin Krämer c01c09548c Update CHANGELOG.md 2025-08-14 17:34:40 +02:00
Oshgnacknak 8f76bb88e7 Merge pull request #589 from qitechgmbh/588-limit-backend-and-frontend-to-30-fps
588 limit backend and frontend to 30 fps
2025-08-14 17:19:04 +02:00
93094049+kraemr@users.noreply.github.com ea24c2f85c replaced 60fps with 30fps 2025-08-14 17:14:35 +02:00
93094049+kraemr@users.noreply.github.com 806603c583 Every machine now sends live events every 33.3 ms 2025-08-14 17:13:18 +02:00
93094049+kraemr@users.noreply.github.com 4b9a788be4 changed comments and timeouts to 30fps 2025-08-14 17:13:18 +02:00
93094049+kraemr@users.noreply.github.com 2d91d34e9a Introduced a 30FPS Constant 2025-08-14 17:13:18 +02:00
Oshgnacknak 7e50cce009 Merge pull request #582 from qitechgmbh/dependabot/cargo/libc-0.2.175
Bump libc from 0.2.172 to 0.2.175
2025-08-14 16:53:39 +02:00
Robin Krämer 2f642a11d0 Merge pull request #587 from qitechgmbh/574-isolate-core-for-realtime-kernel
testing with cores 2,3 isolated
2025-08-14 16:19:39 +02:00
93094049+kraemr@users.noreply.github.com 6cb027b12e added comments 2025-08-14 16:15:35 +02:00
93094049+kraemr@users.noreply.github.com 58903a885d testing with cores 2,3 isolated 2025-08-14 16:15:35 +02:00
Oshgnacknak eda4b7a9b5 Merge pull request #584 from qitechgmbh/dependabot/cargo/syn-2.0.105
Bump syn from 2.0.101 to 2.0.105
2025-08-14 16:14:19 +02:00
Oshgnacknak 2f48b4093f Merge pull request #556 from qitechgmbh/dependabot/npm_and_yarn/electron/tmp-0.2.4
Bump tmp from 0.2.3 to 0.2.4 in /electron
2025-08-14 16:14:03 +02:00
dependabot[bot] 676f19ea0d Bump libc from 0.2.172 to 0.2.175
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.172 to 0.2.175.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.175/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.172...0.2.175)

---
updated-dependencies:
- dependency-name: libc
  dependency-version: 0.2.175
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 14:02:55 +00:00
dependabot[bot] 0252cd5ef3 Bump syn from 2.0.101 to 2.0.105
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.101 to 2.0.105.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.101...2.0.105)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 14:01:45 +00:00
dependabot[bot] ac248e31a1 Bump tmp from 0.2.3 to 0.2.4 in /electron
Bumps [tmp](https://github.com/raszi/node-tmp) from 0.2.3 to 0.2.4.
- [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/raszi/node-tmp/compare/v0.2.3...v0.2.4)

---
updated-dependencies:
- dependency-name: tmp
  dependency-version: 0.2.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 14:01:19 +00:00
Oshgnacknak 58e43f4fc8 Merge pull request #545 from qitechgmbh/dependabot/cargo/tokio-1.47.1
Bump tokio from 1.45.0 to 1.47.1
2025-08-14 15:58:45 +02:00
Oshgnacknak 3845926b97 Merge pull request #581 from qitechgmbh/dependabot/npm_and_yarn/electron/tanstack/react-query-5.84.2
Bump @tanstack/react-query from 5.84.1 to 5.84.2 in /electron
2025-08-14 15:56:30 +02:00
Oshgnacknak 78c99580bf Merge pull request #583 from qitechgmbh/dependabot/cargo/rand-0.9.2
Bump rand from 0.9.1 to 0.9.2
2025-08-14 15:55:40 +02:00
dependabot[bot] 3526711f4d Bump rand from 0.9.1 to 0.9.2
Bumps [rand](https://github.com/rust-random/rand) from 0.9.1 to 0.9.2.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/rand_core-0.9.1...rand_core-0.9.2)

---
updated-dependencies:
- dependency-name: rand
  dependency-version: 0.9.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 13:47:44 +00:00
dependabot[bot] 5ab97839c6 Bump @tanstack/react-query from 5.84.1 to 5.84.2 in /electron
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.84.1 to 5.84.2.
- [Release notes](https://github.com/TanStack/query/releases)
- [Commits](https://github.com/TanStack/query/commits/v5.84.2/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.84.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 13:42:48 +00:00
Oshgnacknak c92455226f Merge pull request #566 from qitechgmbh/dependabot/npm_and_yarn/electron/eslint-plugin-prettier-5.5.4
Bump eslint-plugin-prettier from 5.5.3 to 5.5.4 in /electron
2025-08-14 15:41:27 +02:00
Oshgnacknak 3b91162fd3 Merge pull request #565 from qitechgmbh/dependabot/npm_and_yarn/electron/types/node-24.2.1
Bump @types/node from 22.17.0 to 24.2.1 in /electron
2025-08-14 15:40:57 +02:00
dependabot[bot] c3da8bb357 Bump tokio from 1.45.0 to 1.47.1
Dependabot couldn't find the original pull request head commit, 32fa361c9bbd7a001d6efe407f2697c376ee0d54.
2025-08-14 13:36:08 +00:00
Oshgnacknak 70dd9d2072 Merge pull request #586 from qitechgmbh/youtube-thumbnais
Add YouTube Thumbnail to README.md
2025-08-14 15:36:00 +02:00
Oshgnacknak 2a87e17909 Merge pull request #375 from qitechgmbh/dependabot/cargo/thread-priority-2.1.0
Bump thread-priority from 1.2.0 to 2.1.0
2025-08-14 15:33:57 +02:00
Oshgnacknak ac95e23e18 Merge pull request #585 from qitechgmbh/dependabot/npm_and_yarn/electron/electron/fuses-2.0.0
Bump @electron/fuses from 1.8.0 to 2.0.0 in /electron
2025-08-14 15:33:09 +02:00
dependabot[bot] 5e606e485f Bump @electron/fuses from 1.8.0 to 2.0.0 in /electron
Bumps [@electron/fuses](https://github.com/electron/fuses) from 1.8.0 to 2.0.0.
- [Release notes](https://github.com/electron/fuses/releases)
- [Changelog](https://github.com/electron/fuses/blob/main/.releaserc.json)
- [Commits](https://github.com/electron/fuses/compare/v1.8.0...v2.0.0)

---
updated-dependencies:
- dependency-name: "@electron/fuses"
  dependency-version: 2.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 13:32:50 +00:00
Icedsoda a35f65b8d0 Update README.md 2025-08-14 15:31:49 +02:00
dependabot[bot] 587f7f6c88 Bump thread-priority from 1.2.0 to 2.1.0
Bumps [thread-priority](https://github.com/iddm/thread-priority) from 1.2.0 to 2.1.0.
- [Commits](https://github.com/iddm/thread-priority/commits)

---
updated-dependencies:
- dependency-name: thread-priority
  dependency-version: 2.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 13:31:01 +00:00
dependabot[bot] eedf71b933 Bump eslint-plugin-prettier from 5.5.3 to 5.5.4 in /electron
Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 5.5.3 to 5.5.4.
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.5.3...v5.5.4)

---
updated-dependencies:
- dependency-name: eslint-plugin-prettier
  dependency-version: 5.5.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 13:30:43 +00:00
dependabot[bot] 671c36e9c1 Bump @types/node from 22.17.0 to 24.2.1 in /electron
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.17.0 to 24.2.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.2.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 13:30:31 +00:00
Oshgnacknak f59b1f4096 Merge pull request #564 from qitechgmbh/dependabot/npm_and_yarn/electron/electron-37.2.6
Bump electron from 36.7.3 to 37.2.6 in /electron
2025-08-14 15:29:15 +02:00
dependabot[bot] 138c5614d1 Bump electron from 36.7.3 to 37.2.6 in /electron
Bumps [electron](https://github.com/electron/electron) from 36.7.3 to 37.2.6.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v36.7.3...v37.2.6)

---
updated-dependencies:
- dependency-name: electron
  dependency-version: 37.2.6
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 13:28:18 +00:00
Oshgnacknak b0e1a09d61 Merge pull request #580 from qitechgmbh/depbot
Fix dependabot.yml
2025-08-14 15:26:45 +02:00
Oshgnacknak 29a8a5281b Fix dependabot.yml 2025-08-14 15:19:49 +02:00
Oshgnacknak 92cb25749a Merge pull request #579 from qitechgmbh/1cedsoda-patch-1
Make dependabot less verbose
2025-08-14 15:12:35 +02:00
Icedsoda 0b26dfe9de Update dependabot.yml 2025-08-14 15:10:38 +02:00
Oshgnacknak a1117ad09d Merge pull request #353 from qitechgmbh/dependabot/cargo/serialport-4.7.2
Bump serialport from 4.7.1 to 4.7.2
2025-08-14 15:10:07 +02:00
Oshgnacknak d39bd0b7fa Merge pull request #350 from qitechgmbh/dependabot/cargo/socketioxide-0.17.2
Bump socketioxide from 0.17.1 to 0.17.2
2025-08-14 15:09:29 +02:00
Oshgnacknak 51db45c341 Merge pull request #567 from qitechgmbh/dependabot/npm_and_yarn/electron/electron-forge/maker-rpm-7.8.3
Bump @electron-forge/maker-rpm from 7.8.2 to 7.8.3 in /electron
2025-08-14 15:06:32 +02:00
Oshgnacknak c74fabb1f0 Merge pull request #505 from qitechgmbh/build-frontend-in-ci
Build front-end in CI
2025-08-14 15:03:21 +02:00
Oshgnacknak afc9b2a135 Fix compile errors 2025-08-14 14:55:17 +02:00
Oshgnacknak e3cfb6d0a0 Build front-end in CI
Currently, electron-forge seems to mute a loot of the error messages.
This is why first compiling with tsc might help
2025-08-14 14:55:17 +02:00
Robin Krämer 30a521f27d Merge pull request #572 from qitechgmbh/io-uring
Use `io_uring` on linux
2025-08-14 14:53:11 +02:00
Icedsoda bba2759c1a use io_uring on linux 2025-08-14 14:48:59 +02:00
Oshgnacknak 4c1f870eaa Merge pull request #577 from qitechgmbh/576-missing-el2002-revision
576 missing el2002 revision
2025-08-14 14:45:53 +02:00
93094049+kraemr@users.noreply.github.com 5d317a9841 added missing properties 2025-08-13 15:22:38 +02:00
93094049+kraemr@users.noreply.github.com 4c750ec3be added missing revision to el2002 2025-08-13 15:10:21 +02:00
Robin Krämer da531208e5 Merge branch 'master' into dependabot/npm_and_yarn/electron/electron-forge/maker-rpm-7.8.3 2025-08-13 14:24:19 +02:00
Robin Krämer 554560681b Merge pull request #516 from qitechgmbh/format-write
Allow running linter with fix option
2025-08-13 14:23:43 +02:00
Oshgnacknak 05198ee7e9 Allow running linter with fix option 2025-08-13 14:17:33 +02:00
Robin Krämer a3b9f1b505 Merge pull request #551 from qitechgmbh/550-Allow-wound-distance-reset-during-winding
550 allow wound distance reset during winding
2025-08-13 14:14:39 +02:00
Christian 08578bf856 add warning dialog when wantig to reset while winding 2025-08-13 14:09:30 +02:00
Christian ea65f3cd2c make reset possible while winding 2025-08-13 14:09:30 +02:00
Robin Krämer 4df8815bfa Merge pull request #463 from qitechgmbh/445-implement-el3062-0030
Add `EL3062-0030`
2025-08-13 13:53:28 +02:00
Darmenov Alisher 5f645c3d3a working el3062-0030 2025-08-13 13:50:10 +02:00
Darmenov Alisher c774ace112 feat: Add support for EL3062-0030 device in EtherCAT HAL 2025-08-13 13:50:10 +02:00
Icedsoda 64a6ab0410 Add EL3062-0030 EEPROM dump 2025-08-13 13:50:10 +02:00
Robin Krämer def9c22b64 Merge pull request #571 from qitechgmbh/570-preempt_rt-causes-electron-to-lag
added niceness value to wrapper
2025-08-11 16:10:56 +02:00
93094049+kraemr@users.noreply.github.com 6d6f90bf9f added niceness value to wrapper 2025-08-11 14:06:07 +02:00
Robin Krämer 12dc482e91 Merge pull request #560 from qitechgmbh/fix-typo-in-traverse_controller
Fix typo in traverse controller
2025-08-11 14:04:49 +02:00
snacj b52c047cdf Fix typo in traverse controller 2025-08-11 13:59:10 +02:00
Robin Krämer fa75e91ce6 Merge pull request #569 from qitechgmbh/568-use-realtime-kernel-preempt_rt
fixed config for RT Kernel
2025-08-11 13:29:22 +02:00
93094049+kraemr@users.noreply.github.com c52d4d464d fixed config for RT 2025-08-11 12:17:25 +02:00
Robin Krämer 9719b5251c Merge pull request #563 from qitechgmbh/562-Add-electrical-wiring-diagrams-for-extruder-and-winder
Add wiring diagrams for extruder and winder.
2025-08-11 11:20:53 +02:00
dependabot[bot] ea796bbea6 Bump @electron-forge/maker-rpm from 7.8.2 to 7.8.3 in /electron
Bumps [@electron-forge/maker-rpm](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/maker-rpm"
  dependency-version: 7.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 03:56:03 +00:00
Christian 594a8730a4 Refractor file naming 2025-08-10 14:56:23 +02:00
Christian 03ae4aeefd Add wiring diagrams for extruder and winder. 2025-08-10 14:52:21 +02:00
Oshgnacknak 57f6e50e0e Merge pull request #558 from qitechgmbh/557-fix-rust-formatting-error
removed unused imports
2025-08-07 15:05:49 +02:00
93094049+kraemr@users.noreply.github.com fcebb2d0f2 fixed formatting error 2025-08-07 14:55:07 +02:00
93094049+kraemr@users.noreply.github.com 56ebc80aab removed unused imports 2025-08-07 14:47:24 +02:00
Oshgnacknak 6d8aba1577 Merge pull request #555 from qitechgmbh/rust-formating
Run formatter for rust code in ci
2025-08-06 16:36:21 +02:00
Oshgnacknak ebaf033b36 Run formatter in ci 2025-08-06 16:35:40 +02:00
Oshgnacknak 71f314cf75 Fix warnings 2025-08-06 16:35:40 +02:00
Oshgnacknak f520c759ce Run formatter once 2025-08-06 16:35:40 +02:00
Robin Krämer b8d0b1a630 Update CHANGELOG.md
Update CHANGELOG.md
2025-08-06 16:30:24 +02:00
Icedsoda 11deedb75d Merge pull request #553 from qitechgmbh/552-show-all-branches-on-the-choosversionpage
Show all branches
2025-08-05 16:45:31 +02:00
Robin Krämer 50a61f9e21 Merge pull request #549 from qitechgmbh/519-show-inverter-voltagecurrent
519 show inverter voltagecurrent
2025-08-05 15:48:00 +02:00
Christian 2df5a06930 fix control page graph 2025-08-05 15:44:55 +02:00
TheBest6337 0ae30ba8fb fix styling 2025-08-05 15:44:55 +02:00
TheBest6337 2b80d6c806 Smaller Timewindow and sampleInterval to be more perfomant 2025-08-05 15:44:55 +02:00
Christian fd2ff77b62 fix formatting 2025-08-05 15:44:55 +02:00
Christian 46e699b745 Add combined power consumption calculation and graph it in the ui 2025-08-05 15:44:55 +02:00
Christian 5e0dbf9d7e Add motor power and current tracking to extruder graphs and add power calculation to the extruder api 2025-08-05 15:44:55 +02:00
93094049+kraemr@users.noreply.github.com eefc9b3834 fixed errors and fixed units 2025-08-05 15:44:55 +02:00
93094049+kraemr@users.noreply.github.com bbc9783961 fixed frontend and added timeseries 2025-08-05 15:44:55 +02:00
93094049+kraemr@users.noreply.github.com 3c998d2f5c fixed naming 2025-08-05 15:44:55 +02:00
93094049+kraemr@users.noreply.github.com 52cd1ce050 added backend to read frequency, voltage and current of motor 2025-08-05 15:44:55 +02:00
Icedsoda 5140410006 Show all branches 2025-08-04 15:53:46 +02:00
Christian 852beb5a06 Revert "flip plus and minus buttons"
This reverts commit 2cf150a541.
2025-07-31 13:52:42 +02:00
TheBest6337 2cf150a541 flip plus and minus buttons 2025-07-31 13:48:30 +02:00
Oshgnacknak 4f2f1b227c Merge pull request #541 from qitechgmbh/526-import-and-export-presets
Allow importing and exporting presets
2025-07-30 23:37:51 +02:00
Oshgnacknak 7c1f77ff7b Allow importing presets from file 2025-07-30 23:29:54 +02:00
Oshgnacknak 8b2b769435 Use typed storage instead of any 2025-07-30 23:29:54 +02:00
Oshgnacknak 5350ca8f53 General purpose file input matching our button style 2025-07-30 23:29:40 +02:00
Oshgnacknak 14f4ac1248 Allow exporting individual presets 2025-07-30 19:50:57 +02:00
Oshgnacknak 548ecf5723 Merge pull request #539 from qitechgmbh/537-developer-docs-for-presers
Create presets documentation
2025-07-29 16:01:07 +02:00
Oshgnacknak 99e74759b5 Create presets.md 2025-07-29 15:59:17 +02:00
Oshgnacknak ec177906c8 Merge pull request #538 from qitechgmbh/527-presets-for-the-laser
Implement presets for the laser
2025-07-29 14:44:38 +02:00
Oshgnacknak afdf38eaf2 Implement presets for the laser
Closes #527
2025-07-29 14:42:16 +02:00
Oshgnacknak d8d509812b Merge pull request #531 from qitechgmbh/525-preset-migrations
Validate presets after loading them from the store
2025-07-29 14:23:41 +02:00
Robin Krämer 3eeeadab5c Update CHANGELOG.md 2025-07-25 17:39:43 +02:00
Robin Krämer 657c3cff85 Update CHANGELOG.md 2025-07-25 17:38:46 +02:00
Oshgnacknak 7ea7721ea3 Validate presets after loading them from the store 2025-07-25 17:16:07 +02:00
Robin Krämer b5ccf10128 Merge pull request #528 from qitechgmbh/524-error-thrown-in-console-when-no-presets-exist
Fix loading presets if the store does not exist yet
2025-07-25 16:17:09 +02:00
Oshgnacknak a5a0b1a5e5 Fix loading presets if the store does not exist yet 2025-07-25 16:08:31 +02:00
Robin Krämer 1e8ef812e9 Merge pull request #523 from qitechgmbh/520-add-overload-warning
add overload warning for the inverter
2025-07-25 14:51:17 +02:00
93094049+kraemr@users.noreply.github.com 4965e73b0c fixed styling 2025-07-25 12:40:19 +02:00
Christian 6db2b91052 add overload warning for the inverter 2025-07-24 18:52:31 +02:00
Robin Krämer a930d058c2 Merge pull request #504 from qitechgmbh/winder-1-docs-v2
64 Winder 1 docs v2
2025-07-24 14:38:28 +02:00
93094049+kraemr@users.noreply.github.com 5b7acbc7f3 updated prettier version 2025-07-24 14:27:58 +02:00
93094049+kraemr@users.noreply.github.com 344d319d12 force line endings 2025-07-24 14:22:33 +02:00
93094049+kraemr@users.noreply.github.com d5534e0967 line endings caused issues 2025-07-24 14:18:38 +02:00
93094049+kraemr@users.noreply.github.com 680cb71dde added manual back to winder 2025-07-24 14:02:05 +02:00
93094049+kraemr@users.noreply.github.com a26ccb0a1f fixed regression 2025-07-24 14:02:05 +02:00
93094049+kraemr@users.noreply.github.com 9fa037a9f9 fixed styling again 2025-07-24 14:02:05 +02:00
93094049+kraemr@users.noreply.github.com f07dcbf8f8 fixed styling issues 2025-07-24 14:02:04 +02:00
Christian 783ee38a3a fix: sizing and formatting errors 2025-07-24 14:02:04 +02:00
Christian 4a7be41dfe add line under headings and make them bolt 2025-07-24 14:02:04 +02:00
Christian 8b6960ae64 fix: adjust list styling in Markdown component for better alignment 2025-07-24 14:02:04 +02:00
Christian cf0b71bb69 fix: typo 2025-07-24 14:02:04 +02:00
Christian e5608d061f fix: improve Markdown component styling 2025-07-24 14:02:04 +02:00
Christian 7eba9523d4 fix: update image paths in manuals for correct referencing 2025-07-24 14:02:04 +02:00
Christian 5ad85b1a8c add rehype-raw plugin to Markdown component for HTML support 2025-07-24 14:02:04 +02:00
93094049+kraemr@users.noreply.github.com 064a904975 for easier testing added winder doc to MockManual 2025-07-24 14:02:04 +02:00
93094049+kraemr@users.noreply.github.com ccbab4afba renamed winder-1 to manual 2025-07-24 14:02:04 +02:00
Janhegerq 79870d995a correction of winder usermanual 2025-07-24 14:02:04 +02:00
Janhegerq 0d95f1c44f renamed picture 2025-07-24 14:02:04 +02:00
Janhegerq 62aabaa36a fini9shed windr usermanual 2025-07-24 14:02:04 +02:00
Janhegerq b57b746379 pictures for winder 2025-07-24 14:02:04 +02:00
Janhegerq d1dbe7550d updated with pictures 2025-07-24 14:02:04 +02:00
Janhegerq 38d2364ac4 renamed file (Titelseite) 2025-07-24 14:02:04 +02:00
Janhegerq 581b4f7773 delted file(manual) 2025-07-24 14:02:04 +02:00
Janhegerq 0550ecec7b file moved to electron/src/assets/markdown/winder 2025-07-24 14:02:04 +02:00
Janhegerq 3071482a87 named winder description 2025-07-24 14:02:04 +02:00
Janhegerq 0c926a5acd renamed signs 2025-07-24 14:02:04 +02:00
Janhegerq 59387eb214 named mandatory sign 2025-07-24 14:02:04 +02:00
Janhegerq 9693af313f named mandatory signs 2025-07-24 14:02:04 +02:00
Janhegerq df1beabfa8 named security signs 2025-07-24 14:02:04 +02:00
Janhegerq 9630142921 named screenshots 2025-07-24 14:02:04 +02:00
Janhegerq fcef922ab1 named warning labels 2025-07-24 14:02:04 +02:00
Janhegerq edd8c83402 named warning labels 2025-07-24 14:02:04 +02:00
Janhegerq 09d9801482 Add files via upload 2025-07-24 14:02:04 +02:00
Janhegerq f50997ec23 Update winder-1.md
experiment Bild
2025-07-24 14:02:04 +02:00
Janhegerq 5524e9f10a Add files via upload 2025-07-24 14:02:04 +02:00
Janhegerq 7e73e2f879 Update winder-1.md 2025-07-24 14:02:04 +02:00
Robin Krämer 96719dae15 Merge pull request #517 from qitechgmbh/482-communicate-with-winder
482 communicate with winder
2025-07-23 12:18:26 +02:00
snacj e78e9ba9e3 Fixed Typo 2025-07-23 12:11:38 +02:00
snacj a730ed3228 Fixed Merge Conflicts 2025-07-23 12:04:24 +02:00
snacj c3b0a8643d Fixed unused imports unreachable code and unused configs 2025-07-23 11:55:12 +02:00
snacj 422d98bc7c Fixed machine connection backend errors 2025-07-23 11:55:12 +02:00
snacj f1b61dad9b Moved Machine Connection dropdown to settings page 2025-07-23 11:55:12 +02:00
snacj c97768975b Fixed Connection Errors 2025-07-23 11:54:29 +02:00
snacj 0b882e8bde added machine connection logic and dropdown to buffer and winder 2025-07-23 11:54:29 +02:00
snacj 6752b503bc Added Connected Machine State to Buffer and Winder 2025-07-23 11:54:29 +02:00
snacj 847963fb1c Changed Title of dropdown menu 2025-07-23 11:54:29 +02:00
snacj 4441b60d4a Added Connecting Machine Functionality to Winder and Buffer backend 2025-07-23 11:54:29 +02:00
snacj cb74f4fb6a Fixed Merge issues 2025-07-23 11:54:29 +02:00
snacj fae9c7151c Added frontend Connection Logic to Mock Machine 2025-07-23 11:54:29 +02:00
snacj 7aa36c0361 Added Reverse connection and fixed deadlock 2025-07-23 11:53:54 +02:00
snacj eb4b4e948d Fixed compiling errors due to new MachineAct trait 2025-07-23 11:53:54 +02:00
snacj 13af890e29 Added disconnection logic to backend 2025-07-23 11:53:54 +02:00
snacj d366f52127 Added Availability of machine to frontend 2025-07-23 11:53:54 +02:00
snacj 8f8535b6a3 Fixed Manager to also return weak serial machine 2025-07-23 11:53:54 +02:00
snacj 83fa27c7be Added clear selection and disconnectMachine functionality in frontend 2025-07-23 11:53:54 +02:00
snacj 8295496671 Fixed usage of component machineConnectionDropdown 2025-07-23 11:53:54 +02:00
snacj 9277521dbd Fixed Machine Selection dropdown 2025-07-23 11:53:54 +02:00
snacj 3797fc6469 Refactoring of registry 2025-07-23 11:53:54 +02:00
snacj 9700e47358 Implemented as_any function to machines 2025-07-23 11:53:54 +02:00
snacj 8ee1c63e8d Added machine manager reference to machines 2025-07-23 11:53:54 +02:00
snacj dae3def188 Added and implemented downcast function 2025-07-23 11:53:54 +02:00
snacj fa3dc0aa8c Added Buffer Speed Controller 2025-07-23 11:53:54 +02:00
snacj 69fcdb969d Changed Smartpointers from Box to Arc in machine manager 2025-07-23 11:53:54 +02:00
snacj 9b03b4d909 Fixed Typo 2025-07-23 11:53:54 +02:00
snacj 5e9fb93a21 Added MachineConnectionDropdown component 2025-07-23 11:53:54 +02:00
Robin Krämer b6842a71fb Merge pull request #514 from qitechgmbh/62-persist-config-values
Persist machine configuration in presets
2025-07-23 00:05:00 +02:00
Oshgnacknak f89645a516 Use types of schemas for type checking 2025-07-22 13:19:32 +02:00
Oshgnacknak d928a4ba81 Fix winder preset preview table 2025-07-22 13:17:02 +02:00
Oshgnacknak c457b21e64 Fix units in extruder preset preview 2025-07-22 13:17:02 +02:00
Oshgnacknak c63be3688b Introduce new preset dialog and thus fix styling 2025-07-22 13:17:02 +02:00
Oshgnacknak 21c40ac7ad Implement presets page for the extruder 2025-07-22 13:17:01 +02:00
Oshgnacknak 0938f68d8d Adapt to newer presets for winder 2 2025-07-22 13:17:01 +02:00
Oshgnacknak 87e903781b Have own zod schema for persisted presets 2025-07-22 13:17:01 +02:00
Oshgnacknak 6753f92128 Rename defaultData to defaultState 2025-07-22 13:17:01 +02:00
Oshgnacknak 59353ff06f Use z.coerce to always parse preset.lastModified as a date 2025-07-22 13:17:01 +02:00
Oshgnacknak 938bbc97a6 Parse presets from local storage via zod 2025-07-22 13:17:01 +02:00
Oshgnacknak bb02eeb2b0 Turn type Preset into zod schema 2025-07-22 13:16:57 +02:00
Oshgnacknak 7bc91d73a6 Use default state if state is not fully loaded yet 2025-07-22 13:12:10 +02:00
Oshgnacknak 59783bc2c3 Save latest settings in a preset upon machine change 2025-07-22 13:12:10 +02:00
Oshgnacknak d93ff842ab Don't show date for default preset 2025-07-22 13:12:10 +02:00
Oshgnacknak 78417edcb8 only show default preset of loaded from server 2025-07-22 13:12:10 +02:00
Oshgnacknak f0f1c2c69e Fix setting presets in mock page 2025-07-22 13:12:10 +02:00
Oshgnacknak b4043d1a75 Show a preset with the default settings to the user 2025-07-22 13:12:10 +02:00
Oshgnacknak 3614f812b9 Abstract rendering a preset's preview into its own component 2025-07-22 13:12:10 +02:00
Oshgnacknak 26d3af414f Sort presets by last modified 2025-07-22 13:12:10 +02:00
Oshgnacknak ec8eb6a835 Implement preset preview for winder2 2025-07-22 13:12:10 +02:00
Oshgnacknak 69f0f73087 Render preview dialog before applying presets 2025-07-22 13:12:09 +02:00
Oshgnacknak 0b3e4c8c96 Fix mock graph 2025-07-22 13:12:09 +02:00
Oshgnacknak b8777ee4da Filter presets to only show presets of the current machine 2025-07-22 13:12:09 +02:00
Oshgnacknak 0bc415271e Add presets page for winder2 2025-07-22 13:12:09 +02:00
Oshgnacknak efa32e91ee Implement PresetsPage in a generic manner 2025-07-22 13:12:03 +02:00
Oshgnacknak f0927513cd Allow saving and applying presets presets on the mock machine 2025-07-22 13:05:00 +02:00
Oshgnacknak 3599c26c54 Allow setting 3 frequencies in the UI 2025-07-22 13:05:00 +02:00
Oshgnacknak aa97091b98 Report multiple sine waves in the mock machine 2025-07-22 13:05:00 +02:00
Robin Krämer ea62de3fdc Merge pull request #503 from qitechgmbh/437-add-buffered-winder-base
437 add buffered winder base
2025-07-21 16:02:23 +02:00
Robin Krämer b42089e992 Merge branch 'master' into 437-add-buffered-winder-base 2025-07-21 15:58:31 +02:00
Robin Krämer ebffbb69ec Merge pull request #502 from qitechgmbh/fix-caps
Set caps also for real time
2025-07-21 15:54:48 +02:00
snacj c48b5b2292 Changed frontend Formatting 2025-07-21 15:54:36 +02:00
Oshgnacknak 8abd6558d1 Set caps also for real time clock 2025-07-21 15:51:02 +02:00
snacj dc0b4fada3 Removed unused imports and Debug messages 2025-07-21 15:49:03 +02:00
Robin Krämer 92cacbc6c8 Merge pull request #513 from qitechgmbh/510-add-a-getting-started-dev-documentation
510 add a getting started dev documentation
2025-07-21 14:28:26 +02:00
93094049+kraemr@users.noreply.github.com 4bad318ce7 updated readme and getting-started dev docs 2025-07-21 14:25:41 +02:00
93094049+kraemr@users.noreply.github.com 0eeb7f372e added Contributing 2025-07-21 14:18:30 +02:00
93094049+kraemr@users.noreply.github.com 1a72f3f8bb added inital dev docs and updated build script 2025-07-21 14:11:38 +02:00
Robin Krämer 522ace8a4f Merge pull request #508 from qitechgmbh/501-raise-pressure-limit-of-extruder-and-add-conifrmation
501 raise pressure limit of extruder and add confirmation to EditValue
2025-07-21 12:05:50 +02:00
snacj 7e776dc360 Fixed formatting 2025-07-17 16:15:54 +02:00
snacj 6487ddc6d2 Cleaned up Buffered winder base. Ready for review 2025-07-17 16:11:26 +02:00
93094049+kraemr@users.noreply.github.com 4ebd09d2eb fixed compile errors 2025-07-16 16:29:15 +02:00
snacj 330b000b6e Added start of controller logic 2025-07-16 16:24:02 +02:00
snacj 7a817945ee Added StateEvent to manipulate Live Event Data 2025-07-16 16:24:02 +02:00
snacj 47b1b7fc82 Added live event functionality with sineWave example live value. 2025-07-16 16:24:02 +02:00
snacj 7f2ea72ad3 Implemented Buffer Communication Frontend and Backend with new refactored style events. 2025-07-16 16:24:02 +02:00
snacj 62553d1a2e Buffer frontend accepts backend events. 2025-07-16 16:24:02 +02:00
snacj cff28876ee Added backend Event emits for Buffer1 2025-07-16 16:24:02 +02:00
snacj 56032ed59c Changed formatting 2025-07-16 16:24:01 +02:00
snacj 73245a31c6 added button logic to frontend buffer 2025-07-16 16:24:01 +02:00
snacj f423ac2a14 Added mutation and simulation for moving buffer motor 2025-07-16 16:24:01 +02:00
snacj 82377c2c25 Added constant for Buffer 2025-07-16 16:24:01 +02:00
snacj 08bd2c8cee Created and added Buffer1Page and Buffer1ControlPage to routes 2025-07-16 16:24:01 +02:00
snacj 7205fdea22 Changed Roles of Stepper Puller in backend to fit frontend 2025-07-16 16:24:01 +02:00
snacj c1c420ce8f Updated registry with Buffer1; Renamed BufferedWinder to Buffer1 2025-07-16 16:24:01 +02:00
snacj 576e80cc39 Added Buffer1 properties 2025-07-16 16:24:01 +02:00
snacj a51a871b70 Added different devices to new function of BufferedWinder 2025-07-16 16:24:01 +02:00
snacj f686420e1e added modes to api.rs&new.rs 2025-07-16 16:24:01 +02:00
snacj 2237a84d88 Added folder buffered_winder under server/src/machines/ ; added files act.rs;api.rs;mod.rs;new.rs to buffered_winder and added buffered_winder to mod.rs in server/src/machines 2025-07-16 16:24:01 +02:00
93094049+kraemr@users.noreply.github.com 5c00d28396 raised maximum for Target Pressure 2025-07-16 16:09:19 +02:00
93094049+kraemr@users.noreply.github.com 2b0cff358e added optional confirmation to EditValue 2025-07-16 16:08:49 +02:00
Robin Krämer c3b7f9d050 Merge pull request #498 from qitechgmbh/497-laser-control-page-diameter-circle-extends-over-the-boundaries
clamped diameter visualisation to avoid large circles
2025-07-15 16:08:17 +02:00
93094049+kraemr@users.noreply.github.com a7fce331a0 fixed lint error 2025-07-15 16:06:24 +02:00
93094049+kraemr@users.noreply.github.com 6dd8573584 removed console log and cleaned up code 2025-07-15 16:04:24 +02:00
93094049+kraemr@users.noreply.github.com 124418dc7a clamped diameter visualisation to avoid large circles 2025-07-15 16:00:49 +02:00
dependabot[bot] bf3ee15562 Bump serialport from 4.7.1 to 4.7.2
Bumps [serialport](https://github.com/serialport/serialport-rs) from 4.7.1 to 4.7.2.
- [Changelog](https://github.com/serialport/serialport-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/serialport/serialport-rs/compare/v4.7.1...v4.7.2)

---
updated-dependencies:
- dependency-name: serialport
  dependency-version: 4.7.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-28 13:59:48 +00:00
dependabot[bot] 48fac7f86c Bump socketioxide from 0.17.1 to 0.17.2
Bumps [socketioxide](https://github.com/totodore/socketioxide) from 0.17.1 to 0.17.2.
- [Release notes](https://github.com/totodore/socketioxide/releases)
- [Changelog](https://github.com/Totodore/socketioxide/blob/main/CHANGELOG.md)
- [Commits](https://github.com/totodore/socketioxide/compare/socketioxide-v0.17.1...socketioxide-v0.17.2)

---
updated-dependencies:
- dependency-name: socketioxide
  dependency-version: 0.17.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-28 13:59:45 +00:00
693 changed files with 107541 additions and 16037 deletions

No files matched your search

+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.
+29
View File
@@ -0,0 +1,29 @@
This is a machine control software project.
## Repository structure
### Frontend
`/electron`: Frontend code for the control software built with React and Electron.
### Backend
`/server`: Implements machine setup & loop. Exposes a REST & Socket.io API for the electron frontend.
`/ethercat-hal`: Hardware abstraction layer for Beckhoff (and possibly other EtherCat) devices and protocols.
`/ethercat-hal-derive`: Macros for ethercat-hal
`/control-core`: Generic logic for the server.
### HMI Panel Operating System
`/nixos`: Custom Linux with realtime kernel & preconfigured for touchscreens.
### Other
/docs: Documentation for the project.
## Key Guidelines
1. Follow Rust & Typescript best practices and idiomatic patterns
2. Maintain existing code structure and organization
3. Write unit tests for new functionality where applicable.
4. Document public APIs and complex logic in code. Suggest changes to the `docs/` folder when appropriate
+14 -11
View File
@@ -1,16 +1,19 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/electron" # Location of package manifests
- package-ecosystem: "npm"
directory: "/electron"
schedule:
interval: "weekly"
interval: "monthly"
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-minor"
- package-ecosystem: "cargo" # See documentation for possible values
directory: "/" # Location of package manifests
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
interval: "monthly"
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-minor"
+52 -26
View File
@@ -3,47 +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: Compile
run: tsc
- name: Install
run: npm i
- name: Build
run: npm run build
- name: Format
run: npm run format
test:
runs-on: ubuntu-latest
needs: build
- name: Lint
run: npm run lint
steps:
- *checkout
- *setup-node
- *install
- name: Test
run: npm run test
- name: Build
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
+53 -7
View File
@@ -3,9 +3,10 @@ name: Rust
on:
push:
branches:
- "**"
- master
pull_request:
branches: "*"
branches:
- master
env:
CARGO_TERM_COLOR: always
@@ -16,12 +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: 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: |
@@ -35,7 +48,40 @@ jobs:
${{ runner.os }}-${{ env.RUST_VERSION }}-cargo-
- name: Build
env:
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```'
})
+6
View File
@@ -3,3 +3,9 @@ dist/
.DS_Store
Cargo.lock
dhat-heap.json
build.sh
shell.nix
node_modules/
runtime_metrics.csv
compile_metrics.csv
.idea/
View File
Whitespace-only changes.
+49
View File
@@ -0,0 +1,49 @@
# Canonical identities on the left, aliases on the right.
# Format options:
# Canon Name <canon@example.com> <alias@example.com>
# Canon Name <canon@example.com> Alias Name <alias@example.com>
# Icedsoda -> Philipp
Philipp <29167146+1cedsoda@users.noreply.github.com> <29167146+1cedsoda@users.noreply.github.com>
# kraemr / Robin Krämer -> Robin
Robin <93094049+kraemr@users.noreply.github.com> <93094049+kraemr@users.noreply.github.com>
Robin <93094049+kraemr@users.noreply.github.com> Robin Krämer <93094049+kraemr@users.noreply.github.com>
# Oshgnacknak -> Josh
Josh <osh@oshgnacknak.de> <osh@oshgnacknak.de>
# Milan
Milan <61654753+MilanVDB@users.noreply.github.com>
# dependabot stays as bot
Dependabot <49699333+dependabot[bot]@users.noreply.github.com> <49699333+dependabot[bot]@users.noreply.github.com>
# Medformatik / Florian -> Florian
Florian <dev@fwcr.de> <dev@fwcr.de>
Florian <dev@fwcr.de> Medformatik <dev@fwcr.de>
# Anatomik5 -> Alisher (merge noreply + gmail under Alisher)
Alisher <115714693+Anatomik5@users.noreply.github.com> <115714693+Anatomik5@users.noreply.github.com>
Alisher <115714693+Anatomik5@users.noreply.github.com> Anatomik5 <darmenovalisher@gmail.com>
Alisher <115714693+Anatomik5@users.noreply.github.com> Darmenov Alisher <115714693+Anatomik5@users.noreply.github.com>
# snacj -> Melvin
Melvin <0xsnacj@proton.me> <0xsnacj@proton.me>
Melvin <0xsnacj@proton.me> snacj <bbmelvin@gmx.de>
Melvin <0xsnacj@proton.me> Melvin Spieß <0xsnacj@proton.me>
# Janhegerq -> Jan
Jan <219994445+Janhegerq@users.noreply.github.com> <219994445+Janhegerq@users.noreply.github.com>
Jan <219994445+Janhegerq@users.noreply.github.com> <19994445+Janhegerq@users.noreply.github.com>
# Christian / TheBest6337 -> christian
Christian <christian.zschoegner@outlook.de> <christian.zschoegner@outlook.de>
Christian <christian.zschoegner@outlook.de> TheBest6337 <christian.zschoegner@outlook.de>
Christian <christian.zschoegner@outlook.de> Christian <80114847+TheBest6337@users.noreply.github.com>
# nixos-install.sh
nixos-install.sh <nixos-install.sh@localhost> <nixos-install.sh@localhost>
# copilot-swe-agent bot
Copilot <198982749+Copilot@users.noreply.github.com> <198982749+Copilot@users.noreply.github.com>
+3 -2
View File
@@ -1,5 +1,6 @@
{
"typescript.inlayHints.enumMemberValues.enabled": false,
"git.detectSubmodules": true,
"git.detectSubmodulesLimit": 10,
"git.detectSubmodules": true,
"git.detectSubmodulesLimit": 10,
"rust-analyzer.check.command": "clippy",
}
+208
View File
@@ -1,3 +1,210 @@
# `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_
## General
- [#829](https://github.com/qitechgmbh/control/pull/829), [#834](https://github.com/qitechgmbh/control/pull/834), Improved overall system stability
## Winder V2
- [#840](https://github.com/qitechgmbh/control/pull/840) Added XL Winder option
- [#827](https://github.com/qitechgmbh/control/pull/827) Spool can now be run in reverse
## Extruder V2
- [#826](https://github.com/qitechgmbh/control/pull/826) Fine grained Temperature-PID configuration now possible
# `2.10.0`
_16.10.2025_
## Winder V2
- [#776](https://github.com/qitechgmbh/control/pull/776) Added explanation for padding in winder manual
- [#804](https://github.com/qitechgmbh/control/pull/804) Added gearbox conversion
## General
- [#717](https://github.com/qitechgmbh/control/pull/717) Show from option for graphs added
- [#805](https://github.com/qitechgmbh/control/pull/805) Graphs run better now
# `2.9.1`
_10.10.2025_
## Winder V2
- [#796](https://github.com/qitechgmbh/control/pull/796) Winding in adaptive mode, while puller is in reverse mode is now possible
## General
- [#780](https://github.com/qitechgmbh/control/pull/780) Updates are faster now
- [#746](https://github.com/qitechgmbh/control/pull/746) Improved scrollbar styling for manuals
# `2.9.0`
_06.10.2025_
## Winder V2
- [#778](https://github.com/qitechgmbh/control/pull/778) Increased Winder Traverse Step size limit
## General
- [#725](https://github.com/qitechgmbh/control/pull/725) Improved troubleshooting and update page
- [#625](https://github.com/qitechgmbh/control/pull/625) Improved stability of Machine-Connections
# `2.8.0`
_30.09.2025_
## Laser V1
- [#741](https://github.com/qitechgmbh/control/pull/741) Added roundness measurement to two-axis Laser
## Winder V2
- [#745](https://github.com/qitechgmbh/control/pull/745), [#756](https://github.com/qitechgmbh/control/pull/756) improved Winder Frontend
## General
- [#447](https://github.com/qitechgmbh/control/pull/447) added AquaPath Machine
# `2.7.0`
_17.09.2025_
## Extruder V2
- [#695](https://github.com/qitechgmbh/control/pull/695) Pressure and Rpm Regulation properly switch again
## Winder V2
- [#727](https://github.com/qitechgmbh/control/pull/727) Removed estimated diameter
## Laser V1
- [#731](https://github.com/qitechgmbh/control/pull/731) Added Laser with two-axis measurements
## General
- [#679](https://github.com/qitechgmbh/control/pull/679),[#677](https://github.com/qitechgmbh/control/pull/677),[#698](https://github.com/qitechgmbh/control/pull/698), [#684](https://github.com/qitechgmbh/control/pull/684),[#706](https://github.com/qitechgmbh/control/pull/706) Greatly improved overall system latency and stability
- [#720](https://github.com/qitechgmbh/control/pull/720), [#686](https://github.com/qitechgmbh/control/pull/686) Improved User Interface
# `2.6.0`
_03.09.2025_
## Extruder V2
- [#658](https://github.com/qitechgmbh/control/pull/658) Added manual
- [#639](https://github.com/qitechgmbh/control/pull/639) Swapped order of screw speed regulation buttons
## Laser V1
- [#642](https://github.com/qitechgmbh/control/pull/642) Improved stability of laser connection
## General
- [#657](https://github.com/qitechgmbh/control/pull/657) Time frame for graphs is now remembered
- [#641](https://github.com/qitechgmbh/control/pull/641) Removed duplicate scrollbar from manuals
# `2.5.0`
_27.08.2025_
## Extruder V2
- [#595](https://github.com/qitechgmbh/control/pull/600) Simplified power graphs
- [#593](https://github.com/qitechgmbh/control/pull/599) Added identifiers for temperature sensors in temperature graph
## Winder V2
- [#598](https://github.com/qitechgmbh/control/pull/601) Images now load properly in the manual
## General
- [#540](https://github.com/qitechgmbh/control/pull/592) Improved readability of the presets
- [#591](https://github.com/qitechgmbh/control/pull/626) Significantly improved user interface performance and stability
# `2.4.0`
_14.08.2025_
## Winder V2
- [#550](https://github.com/qitechgmbh/control/pull/551) Can now reset wound distance while winding
## General
- [#568](https://github.com/qitechgmbh/control/pull/569),[#572](https://github.com/qitechgmbh/control/pull/572), [#574](https://github.com/qitechgmbh/control/pull/587) Improved EtherCAT performance
- [#588](https://github.com/qitechgmbh/control/pull/589) Improved frontend performance
# `2.3.0`
_06.08.2025_
## Extruder V2
- [#520](https://github.com/qitechgmbh/control/pull/523) added warning message incase inverter is overloaded
- [#501](https://github.com/qitechgmbh/control/pull/508) raised pressure limit of pressured PID mode for extruder
- [#519](https://github.com/qitechgmbh/control/pull/549) added total power consumption monitor and graphs for voltage,current and wattage of the motor
## Winder V2
- [#64](https://github.com/qitechgmbh/control/pull/504) added winder handbook
## Laser V1
- [#497](https://github.com/qitechgmbh/control/pull/498) improved diameter visualisation
## General
- [#62](https://github.com/qitechgmbh/control/pull/514),[#524](https://github.com/qitechgmbh/control/pull/528) added machine presets to load configurations
- [#526](https://github.com/qitechgmbh/control/pull/541) added import and export of presets
# `2.2.0`
_15.07.2025_
@@ -10,6 +217,7 @@ _15.07.2025_
- [#169](https://github.com/qitechgmbh/control/pull/492), Added a Confirmation Dialog when extruder has not reached 90% of target temperature
## Winder V2
- [#471](https://github.com/qitechgmbh/control/pull/480), Added a live value to track the pulled distance and the option of switching to a different Mode after a given distance.
## General
Generated
+564 -1194
View File
File diff suppressed because it is too large. Load diff
+21 -1
View File
@@ -4,7 +4,27 @@ members = [
"ethercat-hal",
"ethercat-hal-derive",
"control-core",
"ethercat-eeprom-dump",
"machines",
#"ethercat-eeprom-dump",
"control-core-derive",
"units",
"utils",
]
exclude = []
resolver = "2"
[workspace.lints.clippy]
# Individual lints (priority 0)
# don't lint poissbilities for let chains because they were introduces after Rust 1.86
collapsible_if = "allow"
# Lint groups (priority -1)
correctness = { level = "warn", priority = -1 }
suspicious = { level = "warn", priority = -1 }
complexity = { level = "warn", priority = -1 }
perf = { level = "warn", priority = -1 }
style = { level = "warn", priority = -1 }
pendantic = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
+39 -117
View File
@@ -2,145 +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.
# Repo Structure
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.
Frontend
QiTech Control combines the modularity and reliability of standard EtherCAT terminals (e.g., WAGO, Beckhoff) with the power of a modern Rust & React stack.
- `/electron`: Frontend code for the control software built with React and Electron.
## Documentation
Backend
**[View Full Documentation Wiki](https://github.com/qitechgmbh/control/wiki)**
- `/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.
- [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)
Operating System
## Videos
- `/nixos`: Custom Linux with realtime kernel & preconfigured for touchscreens.
| 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* |
Other
## Repository Structure
- `/docs`: Documentation for the project.
- **`/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
# Technology Choices
## Technology Stack
## Backend
**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
To interface with Beckhoff and other EtherCAT devices we need an EtherCAY 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.
[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.
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.
## Frontend
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).
# Dev Setup
## 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)
## Frontend
- nodejs and npm installed
- run `cd electron && npm i && npm run start` to start the frontend
# 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 | In Progress | [](./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 | Not Yet | |
| Waterway | V1 | 2025 | Filament Water Cooling | - | 1 (Qitech Industries GmbH) | 5 | Not Yet | |
| Laser | V1 | ??? | Diameter Measuring Laser | - | 1 (Qitech Industries GmbH) | 6 | Not Yet | |
| Mock | - | ??? | Mock Machine for Testing | - | 1 (Qitech Industries GmbH) | 7 | Not Yet | - |
**Frontend:** [Electron](https://www.electronjs.org/) + [React](https://react.dev/) with [Shadcn](https://ui.shadcn.com/) components and [Tailwind](https://tailwindcss.com/) styling
# More Docs
## Hardware Examples
- [x] [Architecture & Data Flow](./docs/architecture-overview.md)
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.
- [x] Example Winder V2
**[View all hardware examples and tutorials on the wiki](https://github.com/qitechgmbh/control/wiki/Hardware-Examples)**
- [ ] Electron
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)
- Folder Structure
- Routing with TanStack Router
- Design with Tailwind & Shadcn
- ...
## QiTech Machines
- [ ] Interfacing with Electron/Server
QiTech Control powers 10+ production machines for filament extrusion, winding, measurement, and material processing.
- [ ] SocketIO
- Machine Namespace
- Main Namespace
- [ ] REST
- Machine Mutations
- Write Device Identification
**[View complete machine catalog on the wiki](https://github.com/qitechgmbh/control/wiki/QiTech-Machines)**
- 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
## Contributing
- [x] [Actors](./docs/actors.md)
- [ ] SocketIO
- Namespaces & Caching
- Joining leaving namespaces
- NamespaceId
- Caching
- Serverside Caching
- Clientside Caching
- [ ] REST
This is an open-source project. Contributions are welcome! Please see the [wiki](https://github.com/qitechgmbh/control/wiki) for development guidelines.
- [x] Ethercat HAL
## License
- [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.
+25 -5
View File
@@ -1,13 +1,33 @@
#!/bin/bash
if [ $1 == "release" ]; then
set -e
FEATURE=""
# Check if "mock-machine" was passed as an argument
if [[ "$1" == "mock-machine" ]]; then
echo "Building Debug Code with mock-machine feature"
FEATURE="--features mock-machine"
fi
if [[ "$1" == "release" ]]; then
echo "building Release Code"
cargo build --release
else
echo "building Debug Code"
cargo build
echo "Building Debug Code"
cargo build $FEATURE --features development-build
fi
# set cap for server executable
sudo setcap cap_net_raw=eip ./target/debug/server
# Only setcap if not building with mock-machine
if [ "$1" != "mock-machine" ]; then
echo "Setting capabilities for server executable"
sudo setcap 'cap_dac_override,cap_net_raw,cap_sys_nice,cap_ipc_lock=eip' ./target/debug/server
if compgen -G "/dev/ttyUSB*" > /dev/null; then
echo "Setting permissions for serial ports"
sudo chown "root:$(groups | cut -f1 -d' ')" /dev/ttyUSB*
fi
fi
# run
./target/debug/server
+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"
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
# Nix store uses sqlite ... SO HAHAHAHAHH NO CONCURRENT ACCESS AHAHAHHAHAHAHHAHAHAHAH
time nix build .#packages.x86_64-linux.server --impure --extra-experimental-features nix-command --extra-experimental-features flakes --out-link /tmp/server-result
serverstore=$(readlink -f /tmp/server-result)
time nix build .#packages.x86_64-linux.electron --impure --option sandbox false --extra-experimental-features nix-command --extra-experimental-features flakes --out-link /tmp/electron-result
electronstore=$(readlink -f /tmp/electron-result)
nix copy --to "file:///opt/dev-cache" \
$(readlink -f /tmp/server-result) \
$(readlink -f /tmp/electron-result) --extra-experimental-features nix-command
nix store sign \
--key-file /opt/dev-cache/cache-private-key.pem \
--store "file:///opt/dev-cache" \
--extra-experimental-features nix-command \
"$serverstore" "$electronstore"
nix path-info "$serverstore" --extra-experimental-features nix-command --extra-experimental-features flakes
# nix verify --store "file:///opt/dev-cache" \
# --trusted-public-keys "$(cat /opt/dev-cache/cache-public-key.pem)" --extra-experimental-features nix-command --extra-experimental-features flakes \
# "$FIRST_PATH" || echo "Verification failed!"
#time nix build .#packages.x86_64-linux.electron --extra-experimental-features nix-command --extra-experimental-features flakes --out-link /tmp/electron-result
#electronstore=$(readlink -f /tmp/electron-result)
#nix-store --generate-binary-cache-key cache.key cache.pem --extra-experimental-features nix-command
#nix store sign --key-file cache.key "$serverstore" --extra-experimental-features nix-command
#nix path-info --json "$serverstore" --extra-experimental-features nix-command --extra-experimental-features flakes
#export
#nix-store --export /nix/store/<hash>-server > nix-cache/server.nar
#nix-store --export /nix/store/<hash>-electron > nix-cache/electron.nar
# Any http server
# cd ~/nix-cache
# python3 -m http.server 8080
#
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "control_core_derive"
version = "0.1.0"
edition = "2024"
[lints]
workspace = true
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }
File diff suppressed because it is too large. Load diff
+65
View File
@@ -0,0 +1,65 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{DeriveInput, Error};
#[proc_macro_derive(BuildEvent)]
pub fn build_event_derive(item: TokenStream) -> TokenStream {
build_event_derive2(item.into()).unwrap().into()
}
fn build_event_derive2(item: TokenStream2) -> Result<TokenStream2, Error> {
// Parse the input tokens into a syntax tree
let ast: DeriveInput = syn::parse2(item)?;
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
// Generate the BuildEvent impl
let expanded = quote! {
impl #impl_generics control_core::socketio::event::BuildEvent for #ident #ty_generics #where_clause {
#[doc = "Implemented by the BuildEvent derive macro"]
fn build(&self) -> control_core::socketio::event::Event<Self> {
control_core::socketio::event::Event::new(stringify!(#ident), self.clone())
}
}
};
Ok(expanded)
}
#[proc_macro_derive(Machine)]
pub fn machine_derive(item: TokenStream) -> TokenStream {
machine_derive2(item.into()).unwrap().into()
}
fn machine_derive2(item: TokenStream2) -> Result<TokenStream2, Error> {
// Parse the input tokens into a syntax tree
let ast: DeriveInput = syn::parse2(item)?;
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let expanded = quote! {
impl #impl_generics control_core::machines::Machine for #ident #ty_generics #where_clause {
fn get_machine_identification_unique(&self) -> control_core::machines::identification::MachineIdentificationUnique {
self.machine_identification_unique.clone()
}
}
impl #impl_generics control_core::machines::AnyGetters for #ident #ty_generics #where_clause {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
};
Ok(expanded)
}
+26 -11
View File
@@ -3,28 +3,43 @@ name = "control_core"
version = "0.1.0"
edition = "2024"
[lints]
workspace = true
[dependencies]
anyhow = "1.0.98"
axum = "0.8.4"
anyhow = "1.0.100"
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.140"
serialport = "4.7.1"
serialport = "4.7.3"
smol = "2.0.2"
socketioxide = "0.17.1"
uom = "0.36.0"
socketioxide = "0.17.2"
bitvec = { version = "1.0.1", features = ["alloc"] }
crc = "3.3.0"
serial = "0.4.0"
tracing = "0.1.41"
erased-serde = "0.4.6"
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" }
tokio = { version = "1.45.1", features = ["sync"], optional = true }
[target.'cfg(unix)'.dependencies]
libc = "0.2.172"
libc = "0.2.176"
[dev-dependencies]
approx = "0.5.1"
textplots = "0.8"
tokio = { version = "1.45.1", features = ["rt-multi-thread", "macros", "time"] }
[features]
default = []
video-streaming = ["dep:tokio"]
[package.metadata.cargo-machete]
ignored = ["axum","bitvec","control_core_derive"]
@@ -33,7 +33,7 @@ pub struct ClampingTimeagnosticPidController {
}
impl ClampingTimeagnosticPidController {
pub fn new(
pub const fn new(
kp: f64,
ki: f64,
kd: f64,
@@ -65,7 +65,7 @@ impl ClampingTimeagnosticPidController {
}
}
pub fn simple_new(kp: f64, ki: f64, kd: f64) -> Self {
pub const fn simple_new(kp: f64, ki: f64, kd: f64) -> Self {
Self {
kp,
ki,
@@ -85,26 +85,26 @@ impl ClampingTimeagnosticPidController {
}
}
pub fn get_kp(&self) -> f64 {
pub const fn get_kp(&self) -> f64 {
self.kp
}
pub fn get_ki(&self) -> f64 {
pub const fn get_ki(&self) -> f64 {
self.ki
}
pub fn get_kd(&self) -> f64 {
pub const fn get_kd(&self) -> f64 {
self.kd
}
pub fn configure(&mut self, ki: f64, kp: f64, kd: f64) {
pub const fn configure(&mut self, ki: f64, kp: f64, kd: f64) {
self.reset();
self.kp = kp;
self.ki = ki;
self.kd = kd;
}
pub fn optional_clamp(value: f64, min: Option<f64>, max: Option<f64>) -> f64 {
pub const fn optional_clamp(value: f64, min: Option<f64>, max: Option<f64>) -> f64 {
match (min, max) {
(Some(min), Some(max)) => value.clamp(min, max),
(Some(min), None) => value.max(min),
@@ -114,7 +114,7 @@ impl ClampingTimeagnosticPidController {
}
pub fn update(&mut self, error: f64, t: Instant) -> f64 {
let signal = match self.last {
match self.last {
// First update
None => {
// Calculate error
@@ -122,15 +122,10 @@ impl ClampingTimeagnosticPidController {
// Calculate signal
let signal = self.kp * ep;
let clamped_signal = ClampingTimeagnosticPidController::optional_clamp(
signal,
self.min_signal,
self.max_signal,
);
let clamped_signal = Self::optional_clamp(signal, self.min_signal, self.max_signal);
// Set values
self.ep =
ClampingTimeagnosticPidController::optional_clamp(ep, self.min_ep, self.max_ep);
self.ep = Self::optional_clamp(ep, self.min_ep, self.max_ep);
self.ei = 0.0;
self.ed = 0.0;
self.last = Some(t);
@@ -143,23 +138,11 @@ impl ClampingTimeagnosticPidController {
let dt = t.duration_since(last).as_secs_f64();
// Calculate errors
let ep = ClampingTimeagnosticPidController::optional_clamp(
error,
self.min_ep,
self.max_ep,
);
let ep = Self::optional_clamp(error, self.min_ep, self.max_ep);
let ei = ClampingTimeagnosticPidController::optional_clamp(
self.ei + ep * dt,
self.min_ei,
self.max_ei,
);
let ei = Self::optional_clamp(ep.mul_add(dt, self.ei), self.min_ei, self.max_ei);
let ed = ClampingTimeagnosticPidController::optional_clamp(
(ep - self.ep) / dt,
self.min_ed,
self.max_ed,
);
let ed = Self::optional_clamp((ep - self.ep) / dt, self.min_ed, self.max_ed);
// Make factors timeagnostic
let kp = self.kp * dt;
@@ -167,12 +150,8 @@ impl ClampingTimeagnosticPidController {
let kd = self.kd * dt;
// Calculate signal
let signal = kp * ep + ki * ei + kd * ed;
let clamped_signal = ClampingTimeagnosticPidController::optional_clamp(
signal,
self.min_signal,
self.max_signal,
);
let signal = kd.mul_add(ed, kp.mul_add(ep, ki * ei));
let clamped_signal = Self::optional_clamp(signal, self.min_signal, self.max_signal);
// Set values
self.ep = ep;
@@ -182,12 +161,10 @@ impl ClampingTimeagnosticPidController {
clamped_signal
}
};
signal
}
}
pub fn reset(&mut self) {
pub const fn reset(&mut self) {
self.ep = 0.0;
self.ei = 0.0;
self.ed = 0.0;
@@ -22,7 +22,7 @@ pub struct AccelerationSpeedController {
}
impl AccelerationSpeedController {
pub fn new(
pub const fn new(
min_speed: Option<f64>,
max_speed: Option<f64>,
min_acceleration: f64,
@@ -44,10 +44,10 @@ impl AccelerationSpeedController {
/// No speed limits are applied.
pub fn new_simple(max_acceleration: f64, initial_speed: f64) -> Self {
Self::new(
None, // min_speed
None, // max_speed
-max_acceleration, // min_acceleration (deceleration)
max_acceleration, // max_acceleration
None, // min_speed
None, // max_speed
-max_acceleration, // min_acceleration (deceleration)
max_acceleration, // max_acceleration
initial_speed,
)
}
@@ -98,16 +98,16 @@ impl AccelerationSpeedController {
new_speed
}
pub fn reset(&mut self, initial_speed: f64) {
pub const fn reset(&mut self, initial_speed: f64) {
self.last_speed = initial_speed;
self.last_t = None; // Reset the last update time
}
pub fn set_max_acceleration(&mut self, acceleration: f64) {
pub const fn set_max_acceleration(&mut self, acceleration: f64) {
self.max_acceleration = acceleration;
}
fn apply_speed_limits(&self, speed: f64) -> f64 {
const fn apply_speed_limits(&self, speed: f64) -> f64 {
let mut limited_speed = speed;
if let Some(min) = self.min_speed {
@@ -121,23 +121,23 @@ impl AccelerationSpeedController {
limited_speed
}
pub fn get_min_speed(&self) -> Option<f64> {
pub const fn get_min_speed(&self) -> Option<f64> {
self.min_speed
}
pub fn get_max_speed(&self) -> Option<f64> {
pub const fn get_max_speed(&self) -> Option<f64> {
self.max_speed
}
pub fn set_min_speed(&mut self, min_speed: Option<f64>) {
pub const fn set_min_speed(&mut self, min_speed: Option<f64>) {
self.min_speed = min_speed;
}
pub fn set_max_speed(&mut self, max_speed: Option<f64>) {
pub const fn set_max_speed(&mut self, max_speed: Option<f64>) {
self.max_speed = max_speed;
}
pub fn set_min_acceleration(&mut self, deceleration: f64) {
pub const fn set_min_acceleration(&mut self, deceleration: f64) {
self.min_acceleration = deceleration;
}
}
@@ -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},
@@ -37,12 +37,15 @@ impl AngularAccelerationSpeedController {
/// Creates a new angular acceleration speed controller with simplified parameters.
/// Sets min_acceleration to -max_acceleration for symmetric behavior.
/// No speed limits are applied.
pub fn new_simple(max_acceleration: AngularAcceleration, initial_speed: AngularVelocity) -> Self {
pub fn new_simple(
max_acceleration: AngularAcceleration,
initial_speed: AngularVelocity,
) -> Self {
Self::new(
None, // min_speed
None, // max_speed
-max_acceleration, // min_acceleration (deceleration)
max_acceleration, // max_acceleration
None, // min_speed
None, // max_speed
-max_acceleration, // min_acceleration (deceleration)
max_acceleration, // max_acceleration
initial_speed,
)
}
@@ -50,7 +53,7 @@ impl AngularAccelerationSpeedController {
pub fn update(&mut self, target_speed: AngularVelocity, t: Instant) -> AngularVelocity {
let target_speed = target_speed.get::<radian_per_second>();
let new_speed = self.controller.update(target_speed, t);
return AngularVelocity::new::<radian_per_second>(new_speed);
AngularVelocity::new::<radian_per_second>(new_speed)
}
pub fn reset(&mut self, initial_speed: AngularVelocity) {
@@ -70,13 +73,13 @@ impl AngularAccelerationSpeedController {
pub fn get_min_speed(&self) -> Option<AngularVelocity> {
self.controller
.get_min_speed()
.map(|s| AngularVelocity::new::<radian_per_second>(s))
.map(AngularVelocity::new::<radian_per_second>)
}
pub fn get_max_speed(&self) -> Option<AngularVelocity> {
self.controller
.get_max_speed()
.map(|s| AngularVelocity::new::<radian_per_second>(s))
.map(AngularVelocity::new::<radian_per_second>)
}
pub fn set_min_speed(&mut self, min_speed: Option<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,
@@ -38,17 +38,17 @@ impl LinearAccelerationLimitingController {
/// No speed limits are applied.
pub fn new_simple(acceleration: Acceleration, initial_speed: Velocity) -> Self {
Self::new(
None, // min_speed
None, // max_speed
acceleration, // acceleration
-acceleration, // deceleration (negative for deceleration)
None, // min_speed
None, // max_speed
acceleration, // acceleration
-acceleration, // deceleration (negative for deceleration)
initial_speed,
)
}
pub fn update(&mut self, target_speed: Velocity, t: Instant) -> Velocity {
let target_speed = target_speed.get::<meter_per_second>();
let new_speed = self.controller.update(target_speed, t);
return Velocity::new::<meter_per_second>(new_speed);
Velocity::new::<meter_per_second>(new_speed)
}
pub fn reset(&mut self, initial_speed: Velocity) {
let initial_speed = initial_speed.get::<meter_per_second>();
@@ -65,12 +65,12 @@ impl LinearAccelerationLimitingController {
pub fn get_min_speed(&self) -> Option<Velocity> {
self.controller
.get_min_speed()
.map(|s| Velocity::new::<meter_per_second>(s))
.map(Velocity::new::<meter_per_second>)
}
pub fn get_max_speed(&self) -> Option<Velocity> {
self.controller
.get_max_speed()
.map(|s| Velocity::new::<meter_per_second>(s))
.map(Velocity::new::<meter_per_second>)
}
pub fn set_min_speed(&mut self, min_speed: Option<Velocity>) {
self.controller
+10 -12
View File
@@ -21,7 +21,7 @@ pub struct PidController {
}
impl PidController {
pub fn new(kp: f64, ki: f64, kd: f64) -> Self {
pub const fn new(kp: f64, ki: f64, kd: f64) -> Self {
Self {
kp,
ki,
@@ -33,27 +33,27 @@ impl PidController {
}
}
pub fn configure(&mut self, ki: f64, kp: f64, kd: f64) {
pub const fn configure(&mut self, ki: f64, kp: f64, kd: f64) {
self.reset();
self.kp = kp;
self.ki = ki;
self.kd = kd;
}
pub fn get_kp(&self) -> f64 {
pub const fn get_kp(&self) -> f64 {
self.kp
}
pub fn get_ki(&self) -> f64 {
pub const fn get_ki(&self) -> f64 {
self.ki
}
pub fn get_kd(&self) -> f64 {
pub const fn get_kd(&self) -> f64 {
self.kd
}
pub fn update(&mut self, error: f64, t: Instant) -> f64 {
let signal = match self.last {
match self.last {
// First update
None => {
// Calculate error
@@ -78,11 +78,11 @@ impl PidController {
// Calculate errors
let ep = error;
let ei = self.ei + ep * dt;
let ei = ep.mul_add(dt, self.ei);
let ed = (ep - self.ep) / dt;
// Calculate signal
let signal = self.kp * ep + self.ki * ei + self.kd * ed;
let signal = self.kd.mul_add(ed, self.kp.mul_add(ep, self.ki * ei));
// Set values
self.ep = ep;
@@ -92,12 +92,10 @@ impl PidController {
signal
}
};
signal
}
}
pub fn reset(&mut self) {
pub const fn reset(&mut self) {
self.ep = 0.0;
self.ei = 0.0;
self.ed = 0.0;
@@ -3,7 +3,7 @@ use std::fmt;
// Remove the anyhow::Ok import as it conflicts with std::result::Ok
/// Errors that can occur when creating or using the motion controller
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MotionControllerError {
InvalidSpeedLimits,
InvalidAccelerationLimits,
@@ -14,25 +14,25 @@ pub enum MotionControllerError {
impl fmt::Display for MotionControllerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MotionControllerError::InvalidSpeedLimits => {
Self::InvalidSpeedLimits => {
write!(
f,
"Invalid speed limits: min_speed must be ≤ 0 and max_speed must be ≥ 0"
)
}
MotionControllerError::InvalidAccelerationLimits => {
Self::InvalidAccelerationLimits => {
write!(
f,
"Invalid acceleration limits: min_acceleration must be < 0 and max_acceleration must be > 0"
)
}
MotionControllerError::InvalidPositionLimits => {
Self::InvalidPositionLimits => {
write!(
f,
"Invalid position limits: min_position must be ≤ max_position"
)
}
MotionControllerError::ZeroDeceleration => {
Self::ZeroDeceleration => {
write!(
f,
"Zero deceleration rate: cannot plan motion without deceleration capability"
@@ -148,7 +148,7 @@ struct MotionConstants {
}
/// Represents the current phase of motion
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MotionPhase {
Idle,
IncreasingSpeed,
@@ -170,7 +170,7 @@ pub struct ControllerBuilder {
impl ControllerBuilder {
/// Create a new builder
pub fn new() -> Self {
pub const fn new() -> Self {
Self {
min_speed: None,
max_speed: None,
@@ -184,28 +184,36 @@ impl ControllerBuilder {
}
/// Set speed limits (min_speed ≤ 0, max_speed ≥ 0)
pub fn speed_limits(mut self, min_speed: f64, max_speed: f64) -> Self {
pub const fn speed_limits(mut self, min_speed: f64, max_speed: f64) -> Self {
self.min_speed = Some(min_speed);
self.max_speed = Some(max_speed);
self
}
/// Set acceleration limits (min_acceleration < 0, max_acceleration > 0)
pub fn acceleration_limits(mut self, min_acceleration: f64, max_acceleration: f64) -> Self {
pub const fn acceleration_limits(
mut self,
min_acceleration: f64,
max_acceleration: f64,
) -> Self {
self.min_acceleration = Some(min_acceleration);
self.max_acceleration = Some(max_acceleration);
self
}
/// Set position limits (optional)
pub fn position_limits(mut self, min_position: Option<f64>, max_position: Option<f64>) -> Self {
pub const fn position_limits(
mut self,
min_position: Option<f64>,
max_position: Option<f64>,
) -> Self {
self.min_position = min_position;
self.max_position = max_position;
self
}
/// Set tolerances for position and speed comparisons
pub fn tolerances(mut self, position_tolerance: f64, speed_tolerance: f64) -> Self {
pub const fn tolerances(mut self, position_tolerance: f64, speed_tolerance: f64) -> Self {
self.position_tolerance = position_tolerance;
self.speed_tolerance = speed_tolerance;
self
@@ -322,7 +330,7 @@ impl AccelerationPositionController {
let motion_constants = Self::calculate_motion_constants(&config);
Ok(AccelerationPositionController {
Ok(Self {
config,
current_position: 0.0,
current_speed: 0.0,
@@ -366,19 +374,19 @@ impl AccelerationPositionController {
}
Self::new(
-speed, // min_speed
speed, // max_speed
-acceleration, // min_acceleration
acceleration, // max_acceleration
position.map(|p| -p), // min_position
position.map(|p| p), // max_position
1e-6, // position_tolerance (default)
1e-6, // speed_tolerance (default)
-speed, // min_speed
speed, // max_speed
-acceleration, // min_acceleration
acceleration, // max_acceleration
position.map(|p| -p.abs()), // min_position
position.map(|p| p.abs()), // max_position
1e-6, // position_tolerance (default)
1e-6, // speed_tolerance (default)
)
}
/// Create a new builder for the controller
pub fn builder() -> ControllerBuilder {
pub const fn builder() -> ControllerBuilder {
ControllerBuilder::new()
}
@@ -564,8 +572,10 @@ impl AccelerationPositionController {
// Calculate distance needed to reach cruise speed from current speed
let speed_change_distance = if current_speed_in_direction < cruise_speed.abs() {
(cruise_speed.abs().powi(2) - current_speed_in_direction.max(0.0).powi(2))
/ (2.0 * acceleration_rate)
current_speed_in_direction.max(0.0).mul_add(
-current_speed_in_direction.max(0.0),
cruise_speed.abs().powi(2),
) / (2.0 * acceleration_rate)
} else {
0.0
};
@@ -579,8 +589,10 @@ impl AccelerationPositionController {
// Using corrected kinematic equation: v_peak² = v_current² + 2*a*d_total*a_accel*a_decel/(a_accel + a_decel)
let accel_decel_factor =
(acceleration_rate * deceleration_rate) / (acceleration_rate + deceleration_rate);
let discriminant = current_speed_in_direction.max(0.0).powi(2)
+ 2.0 * abs_position_change * accel_decel_factor;
let discriminant = current_speed_in_direction.max(0.0).mul_add(
current_speed_in_direction.max(0.0),
2.0 * abs_position_change * accel_decel_factor,
);
if discriminant >= 0.0 {
self.peak_speed = discriminant.sqrt() * cruise_speed.signum();
@@ -634,7 +646,7 @@ impl AccelerationPositionController {
self.current_acceleration = self.config.max_acceleration * self.direction as f64;
// Update speed
let new_speed = self.current_speed + self.current_acceleration * dt;
let new_speed = self.current_acceleration.mul_add(dt, self.current_speed);
// Check if we've reached peak speed
if (self.direction > 0 && new_speed >= self.peak_speed)
@@ -665,7 +677,7 @@ impl AccelerationPositionController {
self.current_acceleration = self.config.min_acceleration * self.direction as f64;
// Update speed
let new_speed = self.current_speed + self.current_acceleration * dt;
let new_speed = self.current_acceleration.mul_add(dt, self.current_speed);
// Check if we've reached zero speed or target
if (self.current_speed > 0.0 && new_speed <= 0.0)
@@ -689,7 +701,7 @@ impl AccelerationPositionController {
}
// Update position based on current speed
let new_position = self.current_position + self.current_speed * dt;
let new_position = self.current_speed.mul_add(dt, self.current_position);
// Check position limits and handle violations
let mut position_limited = false;
@@ -742,7 +754,7 @@ impl AccelerationPositionController {
///
/// Returns the current position of the controlled object.
/// This value is updated with each call to `update()`.
pub fn get_position(&self) -> f64 {
pub const fn get_position(&self) -> f64 {
self.current_position
}
@@ -751,7 +763,7 @@ impl AccelerationPositionController {
/// Returns the current speed (velocity) of the controlled object.
/// Positive values indicate motion towards increasing position,
/// negative values indicate motion towards decreasing position.
pub fn get_speed(&self) -> f64 {
pub const fn get_speed(&self) -> f64 {
self.current_speed
}
@@ -759,32 +771,32 @@ impl AccelerationPositionController {
///
/// Returns the current acceleration of the controlled object.
/// Positive values indicate increasing speed, negative values indicate decreasing speed.
pub fn get_acceleration(&self) -> f64 {
pub const fn get_acceleration(&self) -> f64 {
self.current_acceleration
}
/// Get the target position
pub fn get_target_position(&self) -> f64 {
pub const fn get_target_position(&self) -> f64 {
self.target_position
}
/// Get the current motion phase
pub fn get_motion_phase(&self) -> MotionPhase {
pub const fn get_motion_phase(&self) -> MotionPhase {
self.motion_phase
}
/// Get the direction of motion
pub fn get_direction(&self) -> i8 {
pub const fn get_direction(&self) -> i8 {
self.direction
}
/// Get the calculated peak speed for current motion
pub fn get_peak_speed(&self) -> f64 {
pub const fn get_peak_speed(&self) -> f64 {
self.peak_speed
}
/// Get the position at which deceleration will begin
pub fn get_deceleration_position(&self) -> f64 {
pub const fn get_deceleration_position(&self) -> f64 {
self.deceleration_position
}
@@ -837,7 +849,7 @@ impl AccelerationPositionController {
}
/// Force the controller to stop at current position
pub fn emergency_stop(&mut self) {
pub const fn emergency_stop(&mut self) {
self.target_position = self.current_position;
self.current_speed = 0.0;
self.current_acceleration = 0.0;
@@ -1006,32 +1018,32 @@ impl AccelerationPositionController {
}
/// Get the minimum speed limit
pub fn get_min_speed(&self) -> f64 {
pub const fn get_min_speed(&self) -> f64 {
self.config.min_speed
}
/// Get the maximum speed limit
pub fn get_max_speed(&self) -> f64 {
pub const fn get_max_speed(&self) -> f64 {
self.config.max_speed
}
/// Get the minimum acceleration limit
pub fn get_min_acceleration(&self) -> f64 {
pub const fn get_min_acceleration(&self) -> f64 {
self.config.min_acceleration
}
/// Get the maximum acceleration limit
pub fn get_max_acceleration(&self) -> f64 {
pub const fn get_max_acceleration(&self) -> f64 {
self.config.max_acceleration
}
/// Get the minimum position limit
pub fn get_min_position(&self) -> Option<f64> {
pub const fn get_min_position(&self) -> Option<f64> {
self.config.min_position
}
/// Get the maximum position limit
pub fn get_max_position(&self) -> Option<f64> {
pub const fn get_max_position(&self) -> Option<f64> {
self.config.max_position
}
@@ -1068,17 +1080,17 @@ impl AccelerationPositionController {
}
/// Get the position tolerance
pub fn get_position_tolerance(&self) -> f64 {
pub const fn get_position_tolerance(&self) -> f64 {
self.config.position_tolerance
}
/// Get the speed tolerance
pub fn get_speed_tolerance(&self) -> f64 {
pub const fn get_speed_tolerance(&self) -> f64 {
self.config.speed_tolerance
}
/// Set tolerances for position and speed comparisons
pub fn set_tolerances(&mut self, position_tolerance: f64, speed_tolerance: f64) {
pub const fn set_tolerances(&mut self, position_tolerance: f64, speed_tolerance: f64) {
self.config.position_tolerance = position_tolerance;
self.config.speed_tolerance = speed_tolerance;
}
@@ -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,
@@ -102,16 +102,12 @@ impl AngularAccelerationPositionController {
/// Get the minimum angle position limit
pub fn get_min_position(&self) -> Option<Angle> {
self.controller
.get_min_position()
.map(|angle| Angle::new::<radian>(angle))
self.controller.get_min_position().map(Angle::new::<radian>)
}
/// Get the maximum angle position limit
pub fn get_max_position(&self) -> Option<Angle> {
self.controller
.get_max_position()
.map(|angle| Angle::new::<radian>(angle))
self.controller.get_max_position().map(Angle::new::<radian>)
}
/// Set the minimum angle position limit
@@ -203,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);
@@ -236,7 +236,7 @@ impl AngularJerkSpeedController {
pub fn get_min_speed(&self) -> Option<AngularVelocity> {
self.controller
.get_min_speed()
.map(|speed| AngularVelocity::new::<radian_per_second>(speed))
.map(AngularVelocity::new::<radian_per_second>)
}
/// Get the maximum angular velocity limit
@@ -248,7 +248,7 @@ impl AngularJerkSpeedController {
pub fn get_max_speed(&self) -> Option<AngularVelocity> {
self.controller
.get_max_speed()
.map(|speed| AngularVelocity::new::<radian_per_second>(speed))
.map(AngularVelocity::new::<radian_per_second>)
}
/// Set the minimum angular velocity limit
@@ -88,7 +88,7 @@ impl JerkSpeedController {
)
.expect("Failed to create AccelerationPositionController");
JerkSpeedController { base_controller }
Self { base_controller }
}
/// Create a new speed controller with simple symmetric limits
@@ -183,7 +183,7 @@ impl JerkSpeedController {
///
/// # Returns
/// Current speed as a floating-point value
pub fn get_speed(&self) -> f64 {
pub const fn get_speed(&self) -> f64 {
self.base_controller.get_position()
}
@@ -193,7 +193,7 @@ impl JerkSpeedController {
///
/// # Returns
/// Current acceleration as a floating-point value
pub fn get_acceleration(&self) -> f64 {
pub const fn get_acceleration(&self) -> f64 {
self.base_controller.get_speed()
}
@@ -203,7 +203,7 @@ impl JerkSpeedController {
///
/// # Returns
/// Current jerk as a floating-point value
pub fn get_jerk(&self) -> f64 {
pub const fn get_jerk(&self) -> f64 {
self.base_controller.get_acceleration()
}
@@ -213,7 +213,7 @@ impl JerkSpeedController {
///
/// # Returns
/// Target speed as a floating-point value
pub fn get_target_speed(&self) -> f64 {
pub const fn get_target_speed(&self) -> f64 {
self.base_controller.get_target_position()
}
@@ -223,7 +223,7 @@ impl JerkSpeedController {
///
/// # Returns
/// Minimum speed limit as an Option<f64>, None if no limit is set
pub fn get_min_speed(&self) -> Option<f64> {
pub const fn get_min_speed(&self) -> Option<f64> {
self.base_controller.get_min_position()
}
@@ -233,7 +233,7 @@ impl JerkSpeedController {
///
/// # Returns
/// Maximum speed limit as an Option<f64>, None if no limit is set
pub fn get_max_speed(&self) -> Option<f64> {
pub const fn get_max_speed(&self) -> Option<f64> {
self.base_controller.get_max_position()
}
@@ -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)),
@@ -205,9 +205,7 @@ impl LinearAccelerationPositionController {
/// # Returns
/// Minimum position limit as an Option<Length>, None if no limit is set
pub fn get_min_position(&self) -> Option<Length> {
self.controller
.get_min_position()
.map(|length| Length::new::<meter>(length))
self.controller.get_min_position().map(Length::new::<meter>)
}
/// Get the maximum position limit
@@ -217,9 +215,7 @@ impl LinearAccelerationPositionController {
/// # Returns
/// Maximum position limit as an Option<Length>, None if no limit is set
pub fn get_max_position(&self) -> Option<Length> {
self.controller
.get_max_position()
.map(|length| Length::new::<meter>(length))
self.controller.get_max_position().map(Length::new::<meter>)
}
/// Set the minimum position limit
@@ -379,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,
@@ -195,14 +195,14 @@ impl LinearJerkSpeedController {
pub fn get_min_speed(&self) -> Option<Velocity> {
self.controller
.get_min_speed()
.map(|speed| Velocity::new::<meter_per_second>(speed))
.map(Velocity::new::<meter_per_second>)
}
/// Get the maximum velocity limit
pub fn get_max_speed(&self) -> Option<Velocity> {
self.controller
.get_max_speed()
.map(|speed| Velocity::new::<meter_per_second>(speed))
.map(Velocity::new::<meter_per_second>)
}
/// Set the minimum velocity limit
+40 -58
View File
@@ -1,8 +1,6 @@
use std::f32::consts::PI;
use units::{angle::radian, f64::Angle};
use uom::si::angle::radian;
#[derive(Debug, Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AngleConverter {
pub flip_x: bool, // true to flip X-axis (mirror horizontally)
pub flip_y: bool, // true to flip Y-axis (mirror vertically)
@@ -11,7 +9,7 @@ pub struct AngleConverter {
impl AngleConverter {
/// Create a custom coordinate system
pub fn new(flip_x: bool, flip_y: bool, is_cw: bool) -> Self {
pub const fn new(flip_x: bool, flip_y: bool, is_cw: bool) -> Self {
Self {
flip_x,
flip_y,
@@ -20,52 +18,52 @@ impl AngleConverter {
}
/// Standard mathematical coordinate system (CCW positive, 0° at positive X)
pub fn mathematical() -> Self {
pub const fn mathematical() -> Self {
Self::new(false, false, false)
}
/// Screen/graphics coordinate system (CW positive, 0° at positive X, Y-flipped)
pub fn screen() -> Self {
pub const fn screen() -> Self {
Self::new(false, true, false)
}
/// System where 0° points up, clockwise positive
pub fn y_up_cw() -> Self {
pub const fn y_up_cw() -> Self {
Self::new(false, false, true)
}
/// System where 0° points down, counter-clockwise positive
pub fn y_down_ccw() -> Self {
pub const fn y_down_ccw() -> Self {
Self::new(false, true, true)
}
/// System where 0° points left, clockwise positive
pub fn x_left_cw() -> Self {
pub const fn x_left_cw() -> Self {
Self::new(true, false, false)
}
/// System where 0° points right, counter-clockwise positive (same as mathematical)
pub fn x_right_ccw() -> Self {
pub const fn x_right_ccw() -> Self {
Self::mathematical()
}
/// System where 0° points up, counter-clockwise positive
pub fn y_up_ccw() -> Self {
pub const fn y_up_ccw() -> Self {
Self::new(false, false, false)
}
/// System where 0° points down, clockwise positive
pub fn y_down_cw() -> Self {
pub const fn y_down_cw() -> Self {
Self::new(false, true, false)
}
/// System where 0° points left, counter-clockwise positive
pub fn x_left_ccw() -> Self {
pub const fn x_left_ccw() -> Self {
Self::new(true, false, true)
}
/// Convert angle from mathematical system to this system (f32)
pub fn degrees_encode(&self, math_angle_degrees: f32) -> f32 {
pub fn degrees_encode(&self, math_angle_degrees: f64) -> f64 {
let normalized_input = self.normalize_angle(math_angle_degrees);
let mut result = normalized_input;
@@ -75,7 +73,7 @@ impl AngleConverter {
(false, false) => {} // No change
(true, false) => result = 180.0 - result, // X-flip: mirror across Y-axis
(false, true) => result = -result, // Y-flip: mirror across X-axis
(true, true) => result = 180.0 + result, // Both flips: 180° rotation
(true, true) => result += 180.0, // Both flips: 180° rotation
}
// Handle rotation direction
@@ -87,7 +85,7 @@ impl AngleConverter {
}
/// Convert angle from this system to mathematical system (f32)
pub fn degrees_decode(&self, system_angle_degrees: f32) -> f32 {
pub fn degrees_decode(&self, system_angle_degrees: f64) -> f64 {
let normalized_input = self.normalize_angle(system_angle_degrees);
let mut result = normalized_input;
@@ -102,24 +100,24 @@ impl AngleConverter {
(false, false) => {} // No change
(true, false) => result = 180.0 - result, // X-flip: mirror across Y-axis
(false, true) => result = -result, // Y-flip: mirror across X-axis
(true, true) => result = result - 180.0, // Both flips: inverse 180° rotation
(true, true) => result -= 180.0, // Both flips: inverse 180° rotation
}
self.normalize_angle(result)
}
/// Convert angle in radians from mathematical system to this system (f32)
pub fn radians_encode(&self, math_angle_radians: f32) -> f32 {
let degrees = math_angle_radians * 180.0 / PI;
pub fn radians_encode(&self, math_angle_radians: f64) -> f64 {
let degrees = math_angle_radians.to_degrees();
let converted_degrees = self.degrees_encode(degrees);
converted_degrees * PI / 180.0
converted_degrees.to_radians()
}
/// Convert angle in radians from this system to mathematical system (f32)
pub fn radians_decode(&self, system_angle_radians: f32) -> f32 {
let degrees = system_angle_radians * 180.0 / PI;
pub fn radians_decode(&self, system_angle_radians: f64) -> f64 {
let degrees = system_angle_radians.to_degrees();
let converted_degrees = self.degrees_decode(degrees);
converted_degrees * PI / 180.0
converted_degrees.to_radians()
}
/// Convert angle from mathematical system to this system (f64)
@@ -133,7 +131,7 @@ impl AngleConverter {
(false, false) => {} // No change
(true, false) => result = 180.0 - result, // X-flip: mirror across Y-axis
(false, true) => result = -result, // Y-flip: mirror across X-axis
(true, true) => result = 180.0 + result, // Both flips: 180° rotation
(true, true) => result += 180.0, // Both flips: 180° rotation
}
// Handle rotation direction
@@ -160,7 +158,7 @@ impl AngleConverter {
(false, false) => {} // No change
(true, false) => result = 180.0 - result, // X-flip: mirror across Y-axis
(false, true) => result = -result, // Y-flip: mirror across X-axis
(true, true) => result = result - 180.0, // Both flips: inverse 180° rotation
(true, true) => result -= 180.0, // Both flips: inverse 180° rotation
}
self.normalize_angle_f64(result)
@@ -168,20 +166,20 @@ impl AngleConverter {
/// Convert angle in radians from mathematical system to this system (f64)
pub fn radians_encode_f64(&self, math_angle_radians: f64) -> f64 {
let degrees = math_angle_radians * 180.0 / std::f64::consts::PI;
let degrees = math_angle_radians.to_degrees();
let converted_degrees = self.degrees_encode_f64(degrees);
converted_degrees * std::f64::consts::PI / 180.0
converted_degrees.to_radians()
}
/// Convert angle in radians from this system to mathematical system (f64)
pub fn radians_decode_f64(&self, system_angle_radians: f64) -> f64 {
let degrees = system_angle_radians * 180.0 / std::f64::consts::PI;
let degrees = system_angle_radians.to_degrees();
let converted_degrees = self.degrees_decode_f64(degrees);
converted_degrees * std::f64::consts::PI / 180.0
converted_degrees.to_radians()
}
/// Normalize angle to [0, 360) range (f32)
fn normalize_angle(&self, angle: f32) -> f32 {
fn normalize_angle(&self, angle: f64) -> f64 {
let mut normalized = angle % 360.0;
if normalized < 0.0 {
normalized += 360.0;
@@ -205,39 +203,23 @@ pub struct AngleConverterUom {
}
impl AngleConverterUom {
pub fn new(angle_converter: AngleConverter) -> Self {
pub const fn new(angle_converter: AngleConverter) -> Self {
Self { angle_converter }
}
pub fn encode(&self, angle: uom::si::f32::Angle) -> uom::si::f32::Angle {
uom::si::f32::Angle::new::<radian>(
self.angle_converter.radians_encode(angle.get::<radian>()),
)
pub fn encode(&self, angle: Angle) -> Angle {
Angle::new::<radian>(self.angle_converter.radians_encode(angle.get::<radian>()))
}
pub fn decode(&self, angle: uom::si::f32::Angle) -> uom::si::f32::Angle {
uom::si::f32::Angle::new::<radian>(
self.angle_converter.radians_decode(angle.get::<radian>()),
)
}
pub fn encode_f64(&self, angle: uom::si::f64::Angle) -> uom::si::f64::Angle {
uom::si::f64::Angle::new::<radian>(
self.angle_converter
.radians_encode_f64(angle.get::<radian>()),
)
}
pub fn decode_f64(&self, angle: uom::si::f64::Angle) -> uom::si::f64::Angle {
uom::si::f64::Angle::new::<radian>(
self.angle_converter
.radians_decode_f64(angle.get::<radian>()),
)
pub fn decode(&self, angle: Angle) -> Angle {
Angle::new::<radian>(self.angle_converter.radians_decode(angle.get::<radian>()))
}
}
#[cfg(test)]
mod tests {
use std::f32::consts::PI;
use super::*;
#[test]
@@ -297,8 +279,8 @@ mod tests {
let converter = AngleConverter::screen();
// Test radians_encode
let result = converter.radians_encode(PI / 2.0); // 90° in radians
let expected = 270.0 * PI / 180.0; // 270° in radians
let result = converter.radians_encode(PI as f64 / 2.0) as f64; // 90° in radians
let expected: f64 = 270.0f64 * PI as f64 / 180.0f64; // 270° in radians
assert!((result - expected).abs() < 1e-6);
}
@@ -306,10 +288,10 @@ mod tests {
fn test_roundtrip_conversion() {
let converter = AngleConverter::new(true, false, true); // X-flip + CW
let original = 75.0_f32;
let original = 75.0_f64;
let converted = converter.degrees_encode(original);
let roundtrip = converter.degrees_decode(converted);
assert!((original - roundtrip).abs() < 1e-6);
assert!((original - roundtrip as f64).abs() < 1e-6);
}
}
@@ -1,4 +1,4 @@
use uom::si::{
use units::{
angle::revolution,
angular_acceleration::radian_per_second_squared,
angular_velocity::revolution_per_second,
@@ -18,7 +18,7 @@ pub struct AngularStepConverter {
impl AngularStepConverter {
/// Create a new converter with the specified steps per revolution
pub fn new(steps_per_revolution: i16) -> Self {
pub const fn new(steps_per_revolution: i16) -> Self {
Self {
steps_per_revolution,
}
@@ -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,
};
@@ -0,0 +1,279 @@
//! # Circular Converter
//!
//! This module provides the `CircularConverter` which handles transformations between
//! linear and angular measurements for circular motion systems.
//!
//! ## Usage
//!
//! The `CircularConverter` is designed to work alongside other converters:
//! - Use `AngularStepConverter` for angular-to-step conversions
//! - Use `LinearStepConverter` for complete linear-to-step conversions (uses `CircularConverter` internally)
//! - Use `CircularConverter` directly when you need linear-to-angular conversions without steps
//!
//! ## Examples
//!
//! ```rust
//! use control_core::converters::circular_converter::CircularConverter;
//! 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 units::{
acceleration::meter_per_second_squared,
angle::radian,
angular_acceleration::radian_per_second_squared,
angular_velocity::radian_per_second,
f64::{Acceleration, Angle, AngularAcceleration, AngularVelocity, Length, Velocity},
length::meter,
velocity::meter_per_second,
};
/// A converter that handles transformations between linear and angular measurements
/// for circular motion. This includes conversions for position, velocity, acceleration,
/// and higher-order derivatives based on a circle's geometric properties.
#[derive(Debug, Clone)]
pub struct CircularConverter {
radius: Length,
circumference: Length,
diameter: Length,
}
impl CircularConverter {
/// Create a new converter from radius
pub fn from_radius(radius: Length) -> Self {
let diameter = radius * 2.0;
let circumference =
Length::new::<meter>(2.0 * std::f64::consts::PI * radius.get::<meter>());
Self {
radius,
diameter,
circumference,
}
}
/// Create a new converter from diameter
pub fn from_diameter(diameter: Length) -> Self {
let radius = diameter / 2.0;
Self::from_radius(radius)
}
/// Create a new converter from circumference
pub fn from_circumference(circumference: Length) -> Self {
let radius =
Length::new::<meter>(circumference.get::<meter>() / (2.0 * std::f64::consts::PI));
Self::from_radius(radius)
}
/// Get the radius
pub fn radius(&self) -> Length {
self.radius
}
/// Get the diameter
pub fn diameter(&self) -> Length {
self.diameter
}
/// Get the circumference
pub fn circumference(&self) -> Length {
self.circumference
}
// Linear to Angular Position Conversions
/// Convert linear distance to angular position
/// Formula: angle = distance / radius
pub fn linear_to_angular_position(&self, distance: Length) -> Angle {
let angle_rad = distance.get::<meter>() / self.radius.get::<meter>();
Angle::new::<radian>(angle_rad)
}
/// Convert angular position to linear distance
/// Formula: distance = angle * radius
pub fn angular_to_linear_position(&self, angle: Angle) -> Length {
let distance = angle.get::<radian>() * self.radius.get::<meter>();
Length::new::<meter>(distance)
}
// Linear to Angular Velocity Conversions
/// Convert linear velocity to angular velocity
/// Formula: angular_velocity = linear_velocity / radius
pub fn linear_to_angular_velocity(&self, velocity: Velocity) -> AngularVelocity {
let angular_velocity_rad_per_s =
velocity.get::<meter_per_second>() / self.radius.get::<meter>();
AngularVelocity::new::<radian_per_second>(angular_velocity_rad_per_s)
}
/// Convert angular velocity to linear velocity
/// Formula: linear_velocity = angular_velocity * radius
pub fn angular_to_linear_velocity(&self, angular_velocity: AngularVelocity) -> Velocity {
let velocity = angular_velocity.get::<radian_per_second>() * self.radius.get::<meter>();
Velocity::new::<meter_per_second>(velocity)
}
// Linear to Angular Acceleration Conversions
/// Convert linear acceleration to angular acceleration
/// Formula: angular_acceleration = linear_acceleration / radius
pub fn linear_to_angular_acceleration(
&self,
acceleration: Acceleration,
) -> AngularAcceleration {
let angular_acceleration_rad_per_s2 =
acceleration.get::<meter_per_second_squared>() / self.radius.get::<meter>();
AngularAcceleration::new::<radian_per_second_squared>(angular_acceleration_rad_per_s2)
}
/// Convert angular acceleration to linear acceleration
/// Formula: linear_acceleration = angular_acceleration * radius
pub fn angular_to_linear_acceleration(
&self,
angular_acceleration: AngularAcceleration,
) -> Acceleration {
let acceleration =
angular_acceleration.get::<radian_per_second_squared>() * self.radius.get::<meter>();
Acceleration::new::<meter_per_second_squared>(acceleration)
}
// Jerk Conversions (using f64 for now since UOM may not have jerk units)
/// Convert linear jerk to angular jerk
/// Formula: angular_jerk = linear_jerk / radius
/// Units: rad/s³ = (m/s³) / m
pub fn linear_to_angular_jerk(&self, linear_jerk: f64) -> f64 {
linear_jerk / self.radius.get::<meter>()
}
/// Convert angular jerk to linear jerk
/// Formula: linear_jerk = angular_jerk * radius
/// Units: m/s³ = (rad/s³) * m
pub fn angular_to_linear_jerk(&self, angular_jerk: f64) -> f64 {
angular_jerk * self.radius.get::<meter>()
}
// Arc Length Calculations
/// Calculate arc length from angle
/// Formula: arc_length = angle * radius
pub fn angle_to_arc_length(&self, angle: Angle) -> Length {
self.angular_to_linear_position(angle)
}
/// Calculate angle from arc length
/// Formula: angle = arc_length / radius
pub fn arc_length_to_angle(&self, arc_length: Length) -> Angle {
self.linear_to_angular_position(arc_length)
}
// Circumference-based calculations
/// Convert linear distance to number of revolutions
/// Formula: revolutions = distance / circumference
pub fn linear_distance_to_revolutions(&self, distance: Length) -> f64 {
distance.get::<meter>() / self.circumference.get::<meter>()
}
/// Convert number of revolutions to linear distance
/// Formula: distance = revolutions * circumference
pub fn revolutions_to_linear_distance(&self, revolutions: f64) -> Length {
Length::new::<meter>(revolutions * self.circumference.get::<meter>())
}
/// Convert linear velocity to revolutions per second
/// Formula: rps = velocity / circumference
pub fn linear_velocity_to_rps(&self, velocity: Velocity) -> f64 {
velocity.get::<meter_per_second>() / self.circumference.get::<meter>()
}
/// Convert revolutions per second to linear velocity
/// Formula: velocity = rps * circumference
pub fn rps_to_linear_velocity(&self, rps: f64) -> Velocity {
Velocity::new::<meter_per_second>(rps * self.circumference.get::<meter>())
}
}
#[cfg(test)]
mod tests {
use super::*;
use units::length::centimeter;
#[test]
fn test_constructors() {
let radius = Length::new::<centimeter>(5.0);
let diameter = Length::new::<centimeter>(10.0);
let circumference = Length::new::<centimeter>(2.0 * std::f64::consts::PI * 5.0);
let converter1 = CircularConverter::from_radius(radius);
let converter2 = CircularConverter::from_diameter(diameter);
let converter3 = CircularConverter::from_circumference(circumference);
// All should have approximately the same values
assert!((converter1.radius().get::<centimeter>() - 5.0).abs() < 1e-10);
assert!((converter2.radius().get::<centimeter>() - 5.0).abs() < 1e-10);
assert!((converter3.radius().get::<centimeter>() - 5.0).abs() < 1e-10);
assert!((converter1.diameter().get::<centimeter>() - 10.0).abs() < 1e-10);
assert!((converter2.diameter().get::<centimeter>() - 10.0).abs() < 1e-10);
assert!((converter3.diameter().get::<centimeter>() - 10.0).abs() < 1e-10);
}
#[test]
fn test_position_conversions() {
let converter = CircularConverter::from_radius(Length::new::<meter>(1.0));
// Test linear to angular
let distance = Length::new::<meter>(std::f64::consts::PI);
let angle = converter.linear_to_angular_position(distance);
assert!((angle.get::<radian>() - std::f64::consts::PI).abs() < 1e-10);
// Test angular to linear
let angle = Angle::new::<radian>(std::f64::consts::PI / 2.0);
let distance = converter.angular_to_linear_position(angle);
assert!((distance.get::<meter>() - std::f64::consts::PI / 2.0).abs() < 1e-10);
}
#[test]
fn test_velocity_conversions() {
let converter = CircularConverter::from_radius(Length::new::<meter>(0.5));
// Test linear to angular
let velocity = Velocity::new::<meter_per_second>(1.0);
let angular_velocity = converter.linear_to_angular_velocity(velocity);
assert!((angular_velocity.get::<radian_per_second>() - 2.0).abs() < 1e-10);
// Test angular to linear
let angular_velocity = AngularVelocity::new::<radian_per_second>(4.0);
let velocity = converter.angular_to_linear_velocity(angular_velocity);
assert!((velocity.get::<meter_per_second>() - 2.0).abs() < 1e-10);
}
#[test]
fn test_revolution_conversions() {
let converter = CircularConverter::from_radius(Length::new::<meter>(1.0));
// One full revolution should equal circumference
let revolutions = 1.0;
let distance = converter.revolutions_to_linear_distance(revolutions);
assert!((distance.get::<meter>() - converter.circumference().get::<meter>()).abs() < 1e-10);
// Converting back should give 1 revolution
let calculated_revolutions = converter.linear_distance_to_revolutions(distance);
assert!((calculated_revolutions - 1.0).abs() < 1e-10);
}
#[test]
fn test_jerk_conversions() {
let converter = CircularConverter::from_radius(Length::new::<meter>(2.0));
let linear_jerk = 4.0; // m/s³
let angular_jerk = converter.linear_to_angular_jerk(linear_jerk);
assert!((angular_jerk - 2.0).abs() < 1e-10); // 4.0 / 2.0 = 2.0 rad/s³
let calculated_linear_jerk = converter.angular_to_linear_jerk(angular_jerk);
assert!((calculated_linear_jerk - linear_jerk).abs() < 1e-10);
}
}
@@ -1,14 +1,11 @@
use uom::si::{
acceleration::meter_per_second_squared,
use units::{
angle::revolution,
angular_acceleration::radian_per_second_squared,
angular_velocity::revolution_per_second,
f64::{Acceleration, Angle, AngularAcceleration, AngularVelocity, Length, Velocity},
length::meter,
velocity::meter_per_second,
};
use super::angular_step_converter::AngularStepConverter;
use super::circular_converter::CircularConverter;
/// Converts between linear measurements and motor steps
///
@@ -18,57 +15,52 @@ use super::angular_step_converter::AngularStepConverter;
#[derive(Debug, Clone)]
pub struct LinearStepConverter {
angular_step_converter: AngularStepConverter,
radius: Length,
circumference: Length,
circular_converter: CircularConverter,
}
// Constructor and basic getters
impl LinearStepConverter {
/// Create a new converter from radius and steps per revolution
pub fn from_radius(steps_per_revolution: i16, radius: Length) -> Self {
let circumference =
Length::new::<meter>(2.0 * std::f64::consts::PI * radius.get::<meter>());
Self {
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
radius,
circumference,
circular_converter: CircularConverter::from_radius(radius),
}
}
/// Create a new converter from diameter and steps per revolution
pub fn from_diameter(steps_per_revolution: i16, diameter: Length) -> Self {
let radius = diameter / 2.0;
Self::from_radius(steps_per_revolution, radius)
Self {
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
circular_converter: CircularConverter::from_diameter(diameter),
}
}
/// Create a new converter from circumference and steps per revolution
pub fn from_circumference(steps_per_revolution: i16, circumference: Length) -> Self {
let radius =
Length::new::<meter>(circumference.get::<meter>() / (2.0 * std::f64::consts::PI));
Self {
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
radius,
circumference,
circular_converter: CircularConverter::from_circumference(circumference),
}
}
/// Get the radius used by the converter
pub fn radius(&self) -> Length {
self.radius
self.circular_converter.radius()
}
/// Get the diameter of the system
pub fn diameter(&self) -> Length {
self.radius * 2.0
self.circular_converter.diameter()
}
/// Get the circumference of the system
pub fn circumference(&self) -> Length {
self.circumference
self.circular_converter.circumference()
}
/// Get the steps per revolution
pub fn steps_per_revolution(&self) -> i16 {
pub const fn steps_per_revolution(&self) -> i16 {
self.angular_step_converter.steps_per_revolution
}
}
@@ -79,8 +71,10 @@ impl LinearStepConverter {
///
/// Formula: steps = (distance / circumference) * steps_per_revolution
pub fn distance_to_steps(&self, distance: Length) -> f64 {
// Convert distance to angle: angle = distance/circumference (in revolutions)
let revolutions = distance.get::<meter>() / self.circumference.get::<meter>();
// Convert distance to revolutions using CircularConverter
let revolutions = self
.circular_converter
.linear_distance_to_revolutions(distance);
let angle = Angle::new::<revolution>(revolutions);
// Convert angle to steps
@@ -94,21 +88,19 @@ impl LinearStepConverter {
// Convert steps to angle
let angle = self.angular_step_converter.steps_to_angle(steps);
// Convert angle to distance: distance = angle * circumference
// Convert angle to distance using CircularConverter
let revolutions = angle.get::<revolution>();
let distance = revolutions * self.circumference.get::<meter>();
Length::new::<meter>(distance)
self.circular_converter
.revolutions_to_linear_distance(revolutions)
}
/// Convert linear velocity to steps/second
///
/// Formula: steps/second = (velocity / circumference) * steps_per_revolution
pub fn velocity_to_steps(&self, velocity: Velocity) -> f64 {
// Convert linear velocity to angular velocity: ω = v/circumference (in rev/s)
let linear_velocity = velocity.get::<meter_per_second>();
let angular_velocity_rps = linear_velocity / self.circumference.get::<meter>();
let angular_velocity = AngularVelocity::new::<revolution_per_second>(angular_velocity_rps);
// Convert linear velocity to revolutions per second using CircularConverter
let rps = self.circular_converter.linear_velocity_to_rps(velocity);
let angular_velocity = AngularVelocity::new::<revolution_per_second>(rps);
// Convert angular velocity to steps/second
self.angular_step_converter
@@ -124,23 +116,19 @@ impl LinearStepConverter {
.angular_step_converter
.steps_to_angular_velocity(steps_per_second);
// Convert angular velocity to linear velocity: v = ω * circumference
let angular_velocity_rps = angular_velocity.get::<revolution_per_second>();
let linear_velocity = angular_velocity_rps * self.circumference.get::<meter>();
Velocity::new::<meter_per_second>(linear_velocity)
// Convert angular velocity to linear velocity using CircularConverter
let rps = angular_velocity.get::<revolution_per_second>();
self.circular_converter.rps_to_linear_velocity(rps)
}
/// Convert linear acceleration to steps/second²
///
/// Formula: steps/second² = (acceleration / radius) * (steps_per_revolution / (2π))
pub fn acceleration_to_steps(&self, acceleration: Acceleration) -> f64 {
// Convert linear acceleration to angular acceleration: α = a/r
let linear_acceleration = acceleration.get::<meter_per_second_squared>();
// Calculate angular acceleration in radians per second squared
let angular_acceleration_rad_per_s2 = linear_acceleration / self.radius.get::<meter>();
let angular_acceleration =
AngularAcceleration::new::<radian_per_second_squared>(angular_acceleration_rad_per_s2);
// Convert linear acceleration to angular acceleration using CircularConverter
let angular_acceleration = self
.circular_converter
.linear_to_angular_acceleration(acceleration);
// Convert angular acceleration to steps/second²
self.angular_step_converter
@@ -156,12 +144,9 @@ impl LinearStepConverter {
.angular_step_converter
.steps_to_angular_acceleration(steps_per_second_squared);
// Convert angular acceleration to linear acceleration: a = α * r
let angular_acceleration_rad_per_s2 =
angular_acceleration.get::<radian_per_second_squared>();
let linear_acceleration = angular_acceleration_rad_per_s2 * self.radius.get::<meter>();
Acceleration::new::<meter_per_second_squared>(linear_acceleration)
// Convert angular acceleration to linear acceleration using CircularConverter
self.circular_converter
.angular_to_linear_acceleration(angular_acceleration)
}
}
@@ -171,7 +156,9 @@ impl LinearStepConverter {
///
/// Formula: angle (in revolutions) = distance / circumference
pub fn distance_to_angle(&self, distance: Length) -> Angle {
let revolutions = distance.get::<meter>() / self.circumference.get::<meter>();
let revolutions = self
.circular_converter
.linear_distance_to_revolutions(distance);
Angle::new::<revolution>(revolutions)
}
@@ -180,26 +167,24 @@ impl LinearStepConverter {
/// Formula: distance = angle (in revolutions) * circumference
pub fn angle_to_distance(&self, angle: Angle) -> Length {
let revolutions = angle.get::<revolution>();
let distance = revolutions * self.circumference.get::<meter>();
Length::new::<meter>(distance)
self.circular_converter
.revolutions_to_linear_distance(revolutions)
}
/// Convert linear velocity to angular velocity
///
/// Formula: angular velocity (in rev/s) = velocity / circumference
pub fn velocity_to_angular_velocity(&self, velocity: Velocity) -> AngularVelocity {
let linear_velocity = velocity.get::<meter_per_second>();
let angular_velocity_rps = linear_velocity / self.circumference.get::<meter>();
AngularVelocity::new::<revolution_per_second>(angular_velocity_rps)
let rps = self.circular_converter.linear_velocity_to_rps(velocity);
AngularVelocity::new::<revolution_per_second>(rps)
}
/// Convert angular velocity to linear velocity
///
/// Formula: velocity = angular velocity (in rev/s) * circumference
pub fn angular_velocity_to_velocity(&self, angular_velocity: AngularVelocity) -> Velocity {
let angular_velocity_rps = angular_velocity.get::<revolution_per_second>();
let linear_velocity = angular_velocity_rps * self.circumference.get::<meter>();
Velocity::new::<meter_per_second>(linear_velocity)
let rps = angular_velocity.get::<revolution_per_second>();
self.circular_converter.rps_to_linear_velocity(rps)
}
/// Convert linear acceleration to angular acceleration
@@ -209,9 +194,8 @@ impl LinearStepConverter {
&self,
acceleration: Acceleration,
) -> AngularAcceleration {
let linear_acceleration = acceleration.get::<meter_per_second_squared>();
let angular_acceleration_rad_per_s2 = linear_acceleration / self.radius.get::<meter>();
AngularAcceleration::new::<radian_per_second_squared>(angular_acceleration_rad_per_s2)
self.circular_converter
.linear_to_angular_acceleration(acceleration)
}
/// Convert angular acceleration to linear acceleration
@@ -221,10 +205,8 @@ impl LinearStepConverter {
&self,
angular_acceleration: AngularAcceleration,
) -> Acceleration {
let angular_acceleration_rad_per_s2 =
angular_acceleration.get::<radian_per_second_squared>();
let linear_acceleration = angular_acceleration_rad_per_s2 * self.radius.get::<meter>();
Acceleration::new::<meter_per_second_squared>(linear_acceleration)
self.circular_converter
.angular_to_linear_acceleration(angular_acceleration)
}
}
@@ -269,6 +251,10 @@ mod tests {
use super::*;
use approx::assert_relative_eq;
use std::f64::EPSILON;
use units::{
acceleration::meter_per_second_squared, angular_acceleration::radian_per_second_squared,
length::meter, velocity::meter_per_second,
};
#[test]
fn test_new() {
+1 -1
View File
@@ -1,5 +1,5 @@
pub mod angle_converter;
pub mod angular_step_converter;
pub mod circular_converter;
pub mod linear_step_converter;
pub mod motor_converter;
pub mod transmission_converter;
@@ -1,22 +0,0 @@
use uom::si::f64::AngularVelocity;
#[derive(Debug, Clone)]
pub struct TransmissionConverter {
pub transmission_ratio: f64,
}
impl TransmissionConverter {
pub fn new() -> Self {
Self {
transmission_ratio: 34.0,
}
}
pub fn calculate_screw_output_rpm(&self, rpm: AngularVelocity) -> AngularVelocity {
rpm / self.transmission_ratio
}
pub fn calculate_screw_input_rpm(&self, screw_rpm: AngularVelocity) -> AngularVelocity {
screw_rpm * self.transmission_ratio
}
}
+36
View File
@@ -0,0 +1,36 @@
use anyhow::anyhow;
use smol::lock::Mutex;
use std::any::Any;
use std::sync::Arc;
pub trait Downcast<T> {
fn downcast(&self) -> Result<T, anyhow::Error>;
}
pub trait DowncastRef<T> {
fn downcast_ref(&self) -> Result<&T, anyhow::Error>;
}
impl<T: 'static, U: Any + 'static> Downcast<Arc<Mutex<T>>> for Arc<Mutex<U>> {
fn downcast(&self) -> Result<Arc<Mutex<T>>, anyhow::Error> {
// Acquire a read lock on the RwLock
let lock = self.lock_blocking();
// Check if the inner type can be downcasted to T
let any: &dyn Any = &*lock;
if any.is::<T>() {
// Clone the Arc and return it as the desired type
let cloned: Arc<Mutex<dyn Any>> = self.clone();
// Transmute the Arc to the desired type
unsafe {
let arc = Arc::from_raw(Arc::into_raw(cloned) as *const Mutex<T>);
Ok(arc)
}
} else {
Err(anyhow!("[{}::downcast] Downcast failed", module_path!()))
}
}
}
@@ -1,10 +1,9 @@
use std::{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
@@ -12,14 +11,46 @@ const IFACE_DISCOVERY_MAX_PDU_DATA: usize = PduStorage::element_size(1100);
const IFACE_DISCOVERY_MAX_FRAMES: usize = 16;
const IFACE_DISCOVERY_MAX_PDI_LEN: usize = 128;
/// Finds an ethernet interface that is suitable for EtherCAT communication.
///
/// ```ignore
/// match discover_ethercat_interface().await {
/// Ok(interface) => println!("Found working interface: {}", interface),
/// Err(_) => println!("No working interface found"),
/// }
/// ```
/// 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())
}
/// 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");
return Ok(reports_as_ethernet && !actually_wifi);
}
#[cfg(not(target_os = "linux"))]
return Ok(true);
}
pub async fn discover_ethercat_interface() -> Result<String, anyhow::Error> {
tracing::info!("Discovering EtherCAT interface...");
@@ -37,17 +68,17 @@ pub async fn discover_ethercat_interface() -> Result<String, anyhow::Error> {
} else {
tracing::debug!("Suppressed panic in interface test: {}", panic_info);
}
// Don't call the default hook, which would print the backtrace
}));
// Get eligible interfaces
let mut interfaces = Interface::get_all()
.or_else(|e| Err(anyhow::anyhow!("Failed to get network interfaces: {}", e)))?
.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
@@ -56,60 +87,39 @@ pub async fn discover_ethercat_interface() -> Result<String, anyhow::Error> {
})
.collect::<Vec<_>>();
// Sort interfaces by name
interfaces.sort_by(|a, b| a.name.cmp(&b.name));
let mut interface: Option<&str> = None;
// Try all interfaces concurrently
let tasks = interfaces
.iter()
.map(|iface| {
let name = iface.name.clone();
std::thread::Builder::new()
.name(format!("ethercat-test-{}", name))
.spawn(move || {
std::panic::catch_unwind(|| {
smol::block_on(async {
match test_interface(&name) {
Ok(_) => {
tracing::info!("Found working EtherCAT interface: {}", name);
Some(name.clone())
}
Err(_) => {
tracing::debug!("Interface {} failed", name);
None
}
}
})
})
.unwrap_or(None)
})
.expect("Failed to spawn thread")
})
.collect::<Vec<_>>();
for i in 0..interfaces.len() {
match test_interface(&interfaces[i].name) {
Ok(_) => {
// if interface found with ethercat Exit early, we expect only one interface with ethercat
interface = Some(&interfaces[i].name);
break;
}
Err(_) => (),
}
}
// Wait for all tasks to complete and collect successful results
let successful_interfaces: Vec<String> = tasks
.into_iter()
.filter_map(|task| match task.join().expect("Should join thread") {
Some(name) => Some(name),
None => None,
})
.collect();
// Return the first successful interface (they're sorted by name)
let result = successful_interfaces.into_iter().next();
for i in 0..interfaces.len() {
if interface.is_some() && interface.unwrap() == &interfaces[i].name {
set_interface_managed(&interfaces[i].name, false);
} else {
set_interface_managed(&interfaces[i].name, true);
}
}
// Restore the default panic hook
std::panic::set_hook(default_hook);
match result {
Some(name) => Ok(name),
None => Err(anyhow::anyhow!("No suitable EtherCAT interface found")),
match interface {
Some(interface) => return Ok(interface.to_string()),
None => return Err(anyhow::anyhow!("No suitable EtherCAT interface found")),
}
}
fn test_interface(interface: &str) -> Result<(), anyhow::Error> {
tracing::trace!("Testing interface: {}", interface);
tracing::info!("Testing interface: {}", interface);
let pdu_storage = Box::leak(Box::new(PduStorage::<
IFACE_DISCOVERY_MAX_FRAMES,
@@ -147,7 +157,7 @@ fn test_interface(interface: &str) -> Result<(), anyhow::Error> {
// Default 10000
dc_static_sync_iterations: 10000,
// Default None
retry_behaviour: ethercrab::RetryBehaviour::None,
retry_behaviour: ethercrab::RetryBehaviour::Count(3),
},
));
-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
}
}
@@ -0,0 +1,337 @@
use serde::Serialize;
use serde::ser::{
SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
SerializeTupleStruct, SerializeTupleVariant, Serializer,
};
use std::error::Error;
use std::fmt::Display;
use std::hash::{DefaultHasher, Hasher};
#[derive(Debug)]
pub struct HashSerializerError {}
impl std::fmt::Display for HashSerializerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("{:?}", self))
// f.debug_struct("HashSerialiyerError")
// .finish()
}
}
impl Error for HashSerializerError {}
impl serde::ser::Error for HashSerializerError {
fn custom<T>(_msg: T) -> Self
where
T: Display,
{
Self {}
}
}
pub struct HashSerializer<'a, H: Hasher>(&'a mut H);
impl<H: Hasher> SerializeSeq for HashSerializer<'_, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<H: Hasher> SerializeTuple for HashSerializer<'_, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<H: Hasher> SerializeTupleStruct for HashSerializer<'_, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<H: Hasher> SerializeTupleVariant for HashSerializer<'_, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<H: Hasher> SerializeMap for HashSerializer<'_, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
key.serialize(HashSerializer(self.0))
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<H: Hasher> SerializeStruct for HashSerializer<'_, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
// Optional: hash the field name to avoid collisions
self.0.write(key.as_bytes());
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<H: Hasher> SerializeStructVariant for HashSerializer<'_, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
self.0.write(key.as_bytes());
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<H: Hasher> Serializer for HashSerializer<'_, H> {
type Ok = ();
type Error = HashSerializerError;
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Self;
fn serialize_bool(self, v: bool) -> Result<(), Self::Error> {
self.0.write_u8(v as u8);
Ok(())
}
fn serialize_i8(self, v: i8) -> Result<(), Self::Error> {
self.0.write_i8(v);
Ok(())
}
fn serialize_i16(self, v: i16) -> Result<(), Self::Error> {
self.0.write_i16(v);
Ok(())
}
fn serialize_i32(self, v: i32) -> Result<(), Self::Error> {
self.0.write_i32(v);
Ok(())
}
fn serialize_i64(self, v: i64) -> Result<(), Self::Error> {
self.0.write_i64(v);
Ok(())
}
fn serialize_u8(self, v: u8) -> Result<(), Self::Error> {
self.0.write_u8(v);
Ok(())
}
fn serialize_u16(self, v: u16) -> Result<(), Self::Error> {
self.0.write_u16(v);
Ok(())
}
fn serialize_u32(self, v: u32) -> Result<(), Self::Error> {
self.0.write_u32(v);
Ok(())
}
fn serialize_u64(self, v: u64) -> Result<(), Self::Error> {
self.0.write_u64(v);
Ok(())
}
fn serialize_f32(self, v: f32) -> Result<(), Self::Error> {
self.0.write_u32(v.to_bits());
Ok(())
}
fn serialize_f64(self, v: f64) -> Result<(), Self::Error> {
self.0.write_u64(v.to_bits());
Ok(())
}
fn serialize_char(self, v: char) -> Result<(), Self::Error> {
let mut buf = [0; 4];
self.0.write(v.encode_utf8(&mut buf).as_bytes());
Ok(())
}
fn serialize_str(self, v: &str) -> Result<(), Self::Error> {
self.0.write(v.as_bytes());
Ok(())
}
fn serialize_bytes(self, v: &[u8]) -> Result<(), Self::Error> {
self.0.write(v);
Ok(())
}
fn serialize_none(self) -> Result<(), Self::Error> {
self.0.write_u8(0); // type tag for None
Ok(())
}
fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<(), Self::Error> {
self.0.write_u8(1); // type tag for Some
value.serialize(self)
}
fn serialize_unit(self) -> Result<(), Self::Error> {
self.0.write_u8(0);
Ok(())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Self::Error> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
) -> Result<(), Self::Error> {
self.0.write_u32(variant_index);
Ok(())
}
fn serialize_newtype_struct<T: ?Sized + Serialize>(
self,
_name: &'static str,
value: &T,
) -> Result<(), Self::Error> {
value.serialize(self)
}
fn serialize_newtype_variant<T: ?Sized + Serialize>(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
value: &T,
) -> Result<(), Self::Error> {
self.0.write_u32(variant_index);
value.serialize(self)
}
// For sequences, tuples, maps, structs, just return self for now
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(self)
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Ok(self)
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Ok(self)
}
fn serialize_tuple_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
self.0.write_u32(variant_index);
Ok(self)
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(self)
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(self)
}
fn serialize_struct_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
self.0.write_u32(variant_index);
Ok(self)
}
}
// A helper that uses Serialize to feed into a Hasher without allocating a buffer:
pub fn hash_with_serde_model<T: Serialize>(value: T) -> u64 {
let mut h = DefaultHasher::new();
let ser = HashSerializer(&mut h);
value.serialize(ser).expect("serialize to hasher failed");
h.finish()
}
+7 -7
View File
@@ -63,11 +63,11 @@ pub fn scale<T>(normalized_value: f64, new_min: T, new_max: T) -> T
where
T: Into<f64> + From<f64>,
{
let normalized_value: f64 = normalized_value.into();
let normalized_value: f64 = normalized_value;
let new_min: f64 = new_min.into();
let new_max: f64 = new_max.into();
let result = normalized_value * (new_max - new_min) + new_min;
let result = normalized_value.mul_add(new_max - new_min, new_min);
T::from(result)
}
@@ -128,11 +128,11 @@ where
// If we are under X we scale linearly from 0 to y
// If we are over X we scale linearly from y to 1
if value_f64 < x_f64 {
return normalize(value_f64, 0.0, x_f64) * y_f64;
normalize(value_f64, 0.0, x_f64) * y_f64
} else if value_f64 > x_f64 {
return normalize(value_f64, x_f64, 1.0) * (1.0 - y_f64) + y_f64;
normalize(value_f64, x_f64, 1.0).mul_add(1.0 - y_f64, y_f64)
} else {
return y_f64;
y_f64
}
}
@@ -273,7 +273,7 @@ pub fn interpolate_exponential(normalized_value: f64, steepness: f64) -> f64 {
return normalized_value;
}
let numerator = steepness.exp().powf(normalized_value) - 1.0;
let denominator = steepness.exp() - 1.0;
let denominator = steepness.exp_m1();
clip(numerator / denominator)
}
@@ -414,7 +414,7 @@ pub fn interpolate_inflected_exponential(x: f64, steepness: f64) -> f64 {
// Use standard exponential interpolation
let result = interpolate_exponential(x_norm, steepness);
0.5 + 0.5 * result
0.5f64.mul_add(result, 0.5)
}
}
-189
View File
@@ -1,189 +0,0 @@
use smol::Timer;
use std::time::{Duration, Instant};
use tracing::instrument;
pub struct LoopThrottle {
target_cycle_time: Duration,
update_interval_cycles: usize,
window_start: Instant,
total_cycles: usize,
delay: Duration,
last_avg_cycle_time: Duration,
on_update: Option<Box<dyn Fn(LoopThrottleUpdate)>>,
}
pub struct LoopThrottleUpdate {
pub delay: Duration,
pub avg_cycle_time: Duration,
pub error: i128, // Error in nanoseconds
}
impl LoopThrottle {
pub fn new(
target_cycle_time: Duration,
update_interval_cycles: usize,
on_update: Option<Box<dyn Fn(LoopThrottleUpdate)>>,
) -> Self {
Self {
target_cycle_time,
update_interval_cycles,
window_start: Instant::now(),
total_cycles: 0,
delay: target_cycle_time,
last_avg_cycle_time: target_cycle_time,
on_update,
}
}
#[instrument(skip_all)]
pub async fn sleep(&mut self) {
self.total_cycles += 1;
// Check if it's time to update (every N cycles)
if self.total_cycles % self.update_interval_cycles == 0 {
let actual_total_time = self.window_start.elapsed();
// Use integer arithmetic where possible
let avg_actual_cycle_time_nanos =
actual_total_time.as_nanos() / self.update_interval_cycles as u128;
let target_cycle_time_nanos = self.target_cycle_time.as_nanos();
// Calculate error in nanoseconds (signed)
let error_nanos = target_cycle_time_nanos as i128 - avg_actual_cycle_time_nanos as i128;
// Update current delay, allowing zero or negative values to become zero
let new_delay_nanos = (self.delay.as_nanos() as i128 + error_nanos).max(0) as u64;
self.delay = Duration::from_nanos(new_delay_nanos);
self.last_avg_cycle_time = Duration::from_nanos(avg_actual_cycle_time_nanos as u64);
// Reset for next measurement window
self.window_start = Instant::now();
self.total_cycles = 0; // Reset to prevent overflow
// if we have an update callback, call it
if let Some(on_update) = &self.on_update {
on_update(LoopThrottleUpdate {
delay: self.delay,
avg_cycle_time: self.last_avg_cycle_time,
error: error_nanos,
});
}
}
Timer::after(self.delay).await;
}
pub fn get_cycle_time(&self) -> Duration {
self.last_avg_cycle_time
}
pub fn get_delay(&self) -> Duration {
self.delay
}
}
#[cfg(test)]
mod tests {
use super::*;
use smol::block_on;
#[test]
fn test_adaptive_throttle_timing() {
block_on(async {
let update_interval_cycles = 100;
let target_cycle_time = Duration::from_micros(5);
let mut throttle = LoopThrottle::new(target_cycle_time, update_interval_cycles, None);
let test_start = Instant::now();
let mut cycle_times = Vec::new();
// Run for several cycles to trigger multiple updates
for _ in 0..10000 {
let cycle_start = Instant::now();
// Simulate some work
Timer::after(Duration::from_micros(5)).await;
throttle.sleep().await;
let cycle_time = cycle_start.elapsed();
cycle_times.push(cycle_time);
}
let total_test_time = test_start.elapsed();
let avg_cycle_time = cycle_times.iter().sum::<Duration>() / cycle_times.len() as u32;
println!("Test completed in: {:?}", total_test_time);
println!("Average cycle time: {:?}", avg_cycle_time);
println!("Target cycle time: {:?}", target_cycle_time);
println!("Last measured avg: {:?}", throttle.get_cycle_time());
// Assert that we're reasonably close to target timing
let tolerance = Duration::from_millis(10);
assert!(
avg_cycle_time >= target_cycle_time.saturating_sub(tolerance)
&& avg_cycle_time <= target_cycle_time + tolerance,
"Average cycle time {:?} not within tolerance of target {:?}",
avg_cycle_time,
target_cycle_time
);
});
}
#[test]
fn test_throttle_adaptation() {
block_on(async {
let target_cycle_time = Duration::from_millis(100);
let update_interval_cycles = 3;
let mut throttle = LoopThrottle::new(target_cycle_time, update_interval_cycles, None);
let initial_delay = throttle.get_delay();
// Run enough cycles to trigger multiple updates
for _ in 0..12 {
// Add variable work to test adaptation
Timer::after(Duration::from_millis(10)).await;
throttle.sleep().await;
}
println!("Initial delay: {:?}", initial_delay);
println!("Final delay: {:?}", throttle.get_delay());
println!("Last avg cycle time: {:?}", throttle.get_cycle_time());
assert!(throttle.get_cycle_time() > Duration::ZERO);
});
}
#[test]
fn test_throttle_initialization() {
let update_interval_cycles = 10;
let target_cycle_time = Duration::from_millis(75);
let throttle = LoopThrottle::new(target_cycle_time, update_interval_cycles, None);
assert_eq!(throttle.get_delay(), target_cycle_time);
assert_eq!(throttle.get_cycle_time(), target_cycle_time);
assert_eq!(throttle.total_cycles, 0);
}
#[test]
fn test_overflow_protection() {
block_on(async {
let target_cycle_time = Duration::from_micros(1);
let update_interval_cycles = 5;
let mut throttle = LoopThrottle::new(target_cycle_time, update_interval_cycles, None);
// Run many cycles to ensure total_cycles resets properly
for i in 0..50 {
throttle.sleep().await;
// Verify that total_cycles never exceeds update_interval_cycles
assert!(
throttle.total_cycles <= update_interval_cycles,
"Cycle {}: total_cycles ({}) exceeded update_interval_cycles ({})",
i,
throttle.total_cycles,
update_interval_cycles
);
}
});
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
pub mod compare_lists;
pub mod hasher_serializer;
pub mod hashing;
pub mod interpolation;
pub mod loop_trottle;
pub mod moving_time_window;
pub mod retry;
+12 -30
View File
@@ -11,56 +11,40 @@ pub trait DivideByCount {
impl DivideByCount for f64 {
fn divide_by_count(self, count: usize) -> Self {
self / (count as f64)
self / (count as Self)
}
}
impl DivideByCount for f32 {
fn divide_by_count(self, count: usize) -> Self {
self / (count as f32)
self / (count as Self)
}
}
impl DivideByCount for i32 {
fn divide_by_count(self, count: usize) -> Self {
self / (count as i32)
self / (count as Self)
}
}
impl DivideByCount for i64 {
fn divide_by_count(self, count: usize) -> Self {
self / (count as i64)
self / (count as Self)
}
}
impl DivideByCount for u32 {
fn divide_by_count(self, count: usize) -> Self {
self / (count as u32)
self / (count as Self)
}
}
impl DivideByCount for u64 {
fn divide_by_count(self, count: usize) -> Self {
self / (count as u64)
self / (count as Self)
}
}
/// A generic moving time window that works with numeric types.
///
/// # Examples
///
/// ```rust
/// use std::time::{Duration, Instant};
/// use control_core::helpers::moving_time_window::MovingTimeWindow;
///
/// let mut window = MovingTimeWindow::new(Duration::from_secs(10), 100);
/// window.update(1.5, Instant::now());
/// window.update(2.5, Instant::now());
///
/// let average = window.average();
/// println!("Average: {}", average);
/// ```
// Type aliases for common use cases
pub type F64Window = MovingTimeWindow<f64>;
pub type F32Window = MovingTimeWindow<f32>;
@@ -194,15 +178,14 @@ where
.samples
.iter()
.map(|(v, _)| *v)
.fold(None, |acc, val| match acc {
None => Some(val),
Some(current_max) => {
.fold(None, |acc, val| {
acc.map_or(Some(val), |current_max| {
if val > current_max {
Some(val)
} else {
Some(current_max)
}
}
})
})
.unwrap_or_default();
@@ -229,15 +212,14 @@ where
.samples
.iter()
.map(|(v, _)| *v)
.fold(None, |acc, val| match acc {
None => Some(val),
Some(current_min) => {
.fold(None, |acc, val| {
acc.map_or(Some(val), |current_min| {
if val < current_min {
Some(val)
} else {
Some(current_min)
}
}
})
})
.unwrap_or_default();
+113
View File
@@ -0,0 +1,113 @@
#[cfg(target_os = "linux")]
use libc;
use std::ffi::CString;
use std::io;
use std::str;
#[cfg(target_os = "linux")]
fn read_proc_interrupts() -> Result<String, io::Error> {
let path = CString::new("/proc/interrupts").unwrap();
// O_RDONLY
let fd = unsafe { libc::open(path.as_ptr(), libc::O_RDONLY) };
if fd < 0 {
return Err(io::Error::from_raw_os_error(-fd));
}
let mut buf: Vec<u8> = Vec::new();
let mut chunk = [0u8; 8192];
loop {
let n = unsafe { libc::read(fd, chunk.as_mut_ptr() as *mut _, chunk.len()) };
if n < 0 {
let e = io::Error::from_raw_os_error(-n as i32);
unsafe { libc::close(fd) };
return Err(e);
} else if n == 0 {
break;
} else {
buf.extend_from_slice(&chunk[..n as usize]);
}
}
unsafe { libc::close(fd) };
Ok(String::from_utf8_lossy(&buf).into_owned())
}
#[cfg(target_os = "linux")]
fn get_interface_irq(proc_content: &str, interface_name: &str) -> Option<u32> {
for line in proc_content.lines() {
if line.contains(interface_name) {
if let Some(colon_pos) = line.find(':') {
let head = &line[..colon_pos].trim();
// try parse head as integer IRQ number
if let Ok(irq) = head.parse::<u32>() {
return Some(irq);
}
}
}
}
None
}
/// Since kernel 3.0 it’s possible to use the /proc/irq/<IRQ-NUMBER>/smp_affinity_list
/// With comma seperated values
/// This function takes the irq identifier and writes the cpu string
/// into /proc/irq/irq_number/smp_affinity_list
#[cfg(target_os = "linux")]
fn set_irq_affinity_raw(irq: u32, cpu: &str) -> Result<(), io::Error> {
let path: String = format!("/proc/irq/{}/smp_affinity_list", irq);
let cpath = CString::new(path.clone()).unwrap();
// We want to completely overwrite the smp affinities, so that we guarentee execution on the specified cores
let file_descriptor = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) };
if file_descriptor < 0 {
tracing::info!("Permission denied to write: {}", path);
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
format!("failed to open {}: errno {}", path, -file_descriptor),
));
}
// convert to "raw bytes"
// cpu needs to be a string because we need the raw ascii code 48 = '0' and so on as bytes
let bytes = cpu.as_bytes();
let mut bytes_written_total = 0usize;
while bytes_written_total < bytes.len() {
let to_write = &bytes[bytes_written_total..];
let bytes_written = unsafe {
libc::write(
file_descriptor,
to_write.as_ptr() as *const _,
to_write.len(),
)
};
if bytes_written < 0 {
let e = io::Error::from_raw_os_error(-bytes_written as i32);
unsafe { libc::close(file_descriptor) };
return Err(e);
}
bytes_written_total += bytes_written as usize;
}
unsafe { libc::close(file_descriptor) };
Ok(())
}
/// This is used to make sure that a given irq handler runs on the expected cpu
/// On Realtime systems this is the make or break fix for certain usecases like ethercrab
/// On our machines the 99.99th percentile of cycle times was very bad ~5-11 ms while ethercat is supposed to be in the microseconds
/// After pinning the ethernet irq to the core that the ethercat code ran on 99.99th percentile went down to ~200us (microseconds)
/// Example input: irq_name: "eno1" , cpu: 2
/// Remember that cpu cores are counted from 0
#[cfg(target_os = "linux")]
pub fn set_irq_affinity(irq_name: &str, cpu: u32) -> Result<(), anyhow::Error> {
let proc_contents = read_proc_interrupts()?;
let irq = get_interface_irq(&proc_contents, irq_name);
if irq.is_none() {
return Err(anyhow::anyhow!("Couldnt find irq number!"));
}
let res = set_irq_affinity_raw(irq.unwrap(), &cpu.to_string());
if res.is_err() {
return Err(anyhow::anyhow!("Couldnt write affinity list!"));
} else {
return Ok(());
}
}
+7 -5
View File
@@ -1,14 +1,16 @@
pub mod controllers;
pub mod converters;
pub mod downcast;
pub mod ethercat;
pub mod ethernet;
pub mod futures;
pub mod helpers;
pub mod machines;
pub mod irq_handling;
pub mod modbus;
pub mod realtime;
pub mod rest;
pub mod serial;
pub mod socketio;
pub mod uom_extensions;
pub mod transmission;
#[macro_use]
extern crate uom;
#[cfg(feature = "video-streaming")]
pub mod video_streaming;
-8
View File
@@ -1,8 +0,0 @@
use serde_json::Value;
use crate::socketio::namespace::Namespace;
pub trait MachineApi {
fn api_mutate(&mut self, value: Value) -> Result<(), anyhow::Error>;
fn api_event_namespace(&mut self) -> &mut Namespace;
}
-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 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 fn is_valid(&self) -> bool {
self.vendor != 0 && self.machine != 0
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DeviceMachineIdentification {
pub machine_identification_unique: MachineIdentificationUnique,
pub role: u16,
}
impl DeviceMachineIdentification {
/// Check if values are non-zero
pub 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)]
pub struct DeviceIdentification {
pub device_machine_identification: Option<DeviceMachineIdentification>,
pub device_hardware_identification: DeviceHardwareIdentification,
}
impl From<DeviceIdentificationIdentified> for DeviceIdentification {
fn from(value: DeviceIdentificationIdentified) -> Self {
DeviceIdentification {
device_machine_identification: Some(value.device_machine_identification),
device_hardware_identification: value.device_hardware_identification,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
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(DeviceIdentificationIdentified {
device_machine_identification,
device_hardware_identification: value.device_hardware_identification,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum DeviceHardwareIdentification {
Ethercat(DeviceHardwareIdentificationEthercat),
Serial(DeviceHardwareIdentificationSerial),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DeviceHardwareIdentificationEthercat {
pub subdevice_index: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DeviceHardwareIdentificationSerial {
pub path: String,
}
-220
View File
@@ -1,220 +0,0 @@
use smol::{
channel::Sender,
lock::{Mutex, RwLock},
};
use socketioxide::extract::SocketRef;
use crate::{
machines::{
identification::MachineIdentificationUnique,
new::{MachineNewHardware, MachineNewHardwareEthercat, MachineNewParams},
},
serial::SerialDevice,
socketio::event::GenericEvent,
};
use std::{collections::HashMap, sync::Arc};
use super::{
Machine,
identification::{DeviceIdentification, DeviceIdentificationIdentified},
new::MachineNewHardwareSerial,
registry::MachineRegistry,
};
#[derive(Debug)]
pub struct MachineManager {
pub ethercat_machines:
HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
pub serial_machines:
HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
}
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>)>,
) {
// empty ethercat machines
self.ethercat_machines.clear();
// 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 = match device_group.first() {
Some(device_identification) => &device_identification.device_machine_identification,
None => continue, // Skip this group if empty
};
// create the machine
let new_machine = machine_registry.new_machine(&MachineNewParams {
device_group,
hardware: &machine_new_hardware,
socket_queue_tx: socket_queue_tx.clone(),
});
// insert the machine into the ethercat machines map
self.ethercat_machines.insert(
machine_identification.machine_identification_unique.clone(),
new_machine,
);
}
}
pub fn get(
&self,
machine_identification: &MachineIdentificationUnique,
) -> Option<&Result<Box<Mutex<dyn Machine>>, anyhow::Error>> {
self.ethercat_machines
.get(machine_identification)
.or_else(|| self.serial_machines.get(machine_identification))
}
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>)>,
) {
let hardware = MachineNewHardwareSerial { device };
let device_identification_identified: DeviceIdentificationIdentified =
device_identification
.clone()
.try_into()
.expect("Serial devices always have machine identification");
let new_machine = machine_registry.new_machine(&MachineNewParams {
device_group: &vec![device_identification_identified.clone()],
hardware: &MachineNewHardware::Serial(&hardware),
socket_queue_tx,
});
tracing::info!("Adding serial machine {:?}", new_machine);
self.serial_machines.insert(
device_identification_identified
.device_machine_identification
.machine_identification_unique,
new_machine,
);
}
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
);
self.serial_machines.remove(
&device_identification_identified
.device_machine_identification
.machine_identification_unique,
);
}
}
/// 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>,
}
-82
View File
@@ -1,82 +0,0 @@
use std::collections::HashMap;
use smol::lock::Mutex;
use super::{Machine, identification::MachineIdentificationUnique, manager::MachineManager};
pub enum MachineManagerIter<'a> {
EthercatMachines {
iter: std::collections::hash_map::Iter<
'a,
MachineIdentificationUnique,
Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
>,
},
SerialMachines {
iter: std::collections::hash_map::Iter<
'a,
MachineIdentificationUnique,
Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
>,
},
Done,
}
pub struct MachineManagerIterator<'a> {
iter: MachineManagerIter<'a>,
serial_machines:
&'a HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
}
impl<'a> Iterator for MachineManagerIterator<'a> {
type Item = (
&'a MachineIdentificationUnique,
&'a Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
);
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,
}
}
}
-13
View File
@@ -1,13 +0,0 @@
use crate::machines::new::MachineAct;
use api::MachineApi;
use new::MachineNewTrait;
use std::any::Any;
use std::fmt::Debug;
pub mod api;
pub mod identification;
pub mod manager;
pub mod manager_iter;
pub mod new;
pub mod registry;
pub trait Machine: MachineNewTrait + MachineApi + MachineAct + Any + Send + Sync + Debug {}
-257
View File
@@ -1,257 +0,0 @@
use crate::serial::SerialDevice;
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::RwLock};
use socketioxide::extract::SocketRef;
use std::{pin::Pin, sync::Arc, time::Instant};
use crate::socketio::event::GenericEvent;
pub trait MachineNewTrait {
fn new<'maindevice, 'subdevices>(
params: &MachineNewParams<'maindevice, 'subdevices, '_, '_, '_, '_, '_>,
) -> Result<Self, Error>
where
Self: Sized;
}
pub trait MachineAct {
fn act(&mut self, now: Instant) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
}
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 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,);
}
}
-68
View File
@@ -1,68 +0,0 @@
use super::{Machine, identification::MachineIdentification, new::MachineNewParams};
use anyhow::Error;
use smol::lock::Mutex;
use std::{any::TypeId, collections::HashMap};
pub type MachineNewClosure =
Box<dyn Fn(&MachineNewParams) -> Result<Box<Mutex<dyn Machine>>, Error> + Send + Sync>;
pub struct MachineRegistry {
type_map: HashMap<TypeId, (MachineIdentification, MachineNewClosure)>,
}
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,
// create a machine construction closure
Box::new(|machine_new_params| {
Ok(Box::new(Mutex::new(T::new(machine_new_params)?)))
}),
),
);
}
pub fn new_machine(
&self,
machine_new_params: &MachineNewParams,
) -> Result<Box<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)
}
}
+32 -39
View File
@@ -1,11 +1,12 @@
pub mod modbus_serial_interface;
pub mod tcp;
use anyhow::Error;
use crc::{CRC_16_MODBUS, Crc};
use serial;
use serialport::SerialPort;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParityType {
Even,
Odd,
@@ -62,17 +63,17 @@ impl From<ModbusExceptionCode> for u8 {
impl From<u8> for ModbusExceptionCode {
fn from(value: u8) -> Self {
match value {
0 => ModbusExceptionCode::None,
1 => ModbusExceptionCode::IllegalFunction,
2 => ModbusExceptionCode::IllegalDataAddress,
3 => ModbusExceptionCode::IllegalDataValue,
4 => ModbusExceptionCode::SlaveDeviceFailure,
5 => ModbusExceptionCode::Acknowledge,
6 => ModbusExceptionCode::SlaveDeviceBusy,
8 => ModbusExceptionCode::MemoryParityError,
10 => ModbusExceptionCode::GatewayPathUnavailable,
11 => ModbusExceptionCode::GatewayTargetDeviceFailedToRespond,
other => ModbusExceptionCode::Unknown(other),
0 => Self::None,
1 => Self::IllegalFunction,
2 => Self::IllegalDataAddress,
3 => Self::IllegalDataValue,
4 => Self::SlaveDeviceFailure,
5 => Self::Acknowledge,
6 => Self::SlaveDeviceBusy,
8 => Self::MemoryParityError,
10 => Self::GatewayPathUnavailable,
11 => Self::GatewayTargetDeviceFailedToRespond,
other => Self::Unknown(other),
}
}
}
@@ -97,10 +98,10 @@ impl TryFrom<u8> for ModbusFunctionCode {
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x03 => Ok(ModbusFunctionCode::ReadHoldingRegister),
0x04 => Ok(ModbusFunctionCode::ReadInputRegister),
0x06 => Ok(ModbusFunctionCode::PresetHoldingRegister),
0x08 => Ok(ModbusFunctionCode::DiagnoseFunction),
0x03 => Ok(Self::ReadHoldingRegister),
0x04 => Ok(Self::ReadInputRegister),
0x06 => Ok(Self::PresetHoldingRegister),
0x08 => Ok(Self::DiagnoseFunction),
_ => Err(anyhow::anyhow!("Error: Modbus Function Code doesnt exist!")),
}
}
@@ -119,7 +120,7 @@ impl From<ModbusFunctionCode> for u8 {
impl From<ModbusRequest> for Vec<u8> {
fn from(request: ModbusRequest) -> Self {
let mut buffer = Vec::new();
let mut buffer = Self::new();
buffer.push(request.slave_id);
buffer.push(request.function_code.into()); // convert functioncode into u8 and push it
@@ -132,7 +133,7 @@ impl From<ModbusRequest> for Vec<u8> {
// convert u16 value to le_bytes and add it to the end of the frame
buffer.extend_from_slice(&result_crc.to_le_bytes());
return buffer;
buffer
}
}
/// Reads data from a Modbus device via a serial port.
@@ -143,9 +144,7 @@ impl From<ModbusRequest> for Vec<u8> {
/// # Returns
/// A `Result` containing an `Option` with the raw Modbus response as a `Vec<u8>` if successful,
/// or `None` if the response is invalid or an error occurs.
pub fn receive_data_modbus(
port: &mut dyn serial::SerialPort,
) -> Result<Option<Vec<u8>>, anyhow::Error> {
pub fn receive_data_modbus(port: &mut dyn SerialPort) -> Result<Option<Vec<u8>>, anyhow::Error> {
let mut buf: [u8; 256] = [0; 256];
let data_length = port.read(&mut buf)?;
if data_length == 0 {
@@ -155,7 +154,7 @@ pub fn receive_data_modbus(
}
/// Computes Modbus CRC-16 using `crc` crate.
pub fn modbus_crc16(data: &[u8]) -> u16 {
pub const fn modbus_crc16(data: &[u8]) -> u16 {
let modbus: Crc<u16> = Crc::<u16>::new(&CRC_16_MODBUS);
modbus.checksum(data)
}
@@ -206,23 +205,17 @@ fn extract_crc(raw_data: &Vec<u8>) -> Result<u16, Error> {
}
impl TryFrom<Vec<u8>> for ModbusResponse {
type Error = anyhow::Error;
fn try_from(value: Vec<u8>) -> Result<ModbusResponse, Error> {
let crc = match extract_crc(&value) {
Ok(crc_value) => crc_value,
Err(err) => return Err(err),
};
fn try_from(value: Vec<u8>) -> Result<Self, Error> {
let crc = extract_crc(&value)?;
let function_code_res = ModbusFunctionCode::try_from(value[1]);
let function_code = match function_code_res {
Ok(code) => code,
Err(err) => return Err(err),
};
let function_code = function_code_res?;
Ok(ModbusResponse {
Ok(Self {
slave_id: value[0],
function_code: function_code,
function_code,
data: value[2..value.len() - 2].to_vec(), // get data without the crc
crc: crc,
crc,
})
}
}
@@ -234,23 +227,23 @@ impl TryFrom<Vec<u8>> for ModbusResponse {
/// machine_operation_delay_nano: Delay for the given operation in nanoseconds as specified by the slaves datasheet (example: mitsubishi csfr84 has 12ms for read write in RAM)
/// baudrate: bits per second
/// message_size: size of original message in bytes
pub fn calculate_modbus_rtu_timeout(
pub const fn calculate_modbus_rtu_timeout(
bits: u8,
machine_operation_delay_nano: Duration,
baudrate: u32,
message_size: usize,
) -> Duration {
let nanoseconds_per_bit: u64 = (1000000 / baudrate) as u64;
let nanoseconds_per_byte: u64 = bits as u64 * nanoseconds_per_bit as u64;
let nanoseconds_per_byte: u64 = bits as u64 * nanoseconds_per_bit;
let transmission_timeout: u64 = nanoseconds_per_byte * message_size as u64;
let silent_time: u64 = (nanoseconds_per_byte * (35)) / 10 as u64; // silent_time is 3.5x of character length,which is 11 bit for 8E1
let silent_time: u64 = (nanoseconds_per_byte * (35)) / 10_u64; // silent_time is 3.5x of character length,which is 11 bit for 8E1
let mut full_timeout: u64 = transmission_timeout;
full_timeout += machine_operation_delay_nano.as_nanos() as u64;
full_timeout += silent_time;
return Duration::from_nanos(full_timeout);
Duration::from_nanos(full_timeout)
}
#[cfg(test)]
@@ -66,7 +66,7 @@ impl ModbusSerialInterface {
}
}
pub fn is_initialized(&self) -> bool {
pub const fn is_initialized(&self) -> bool {
!matches!(self.state, State::Uninitialized)
}
@@ -199,7 +199,7 @@ impl ModbusSerialInterface {
Some(Duration::from_nanos(total_timeout))
}
pub fn get_response(&self) -> Option<&ModbusResponse> {
pub const fn get_response(&self) -> Option<&ModbusResponse> {
self.response.as_ref()
}
+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)
}
}
+8 -8
View File
@@ -1,7 +1,7 @@
use tracing::debug;
use tracing::error;
#[cfg(unix)]
#[cfg(target_os = "linux")]
/// Makes the current thread a real-time thread using the PREEMPT_RT capabilities of Linux.
///
/// This function configures the current thread with real-time scheduling properties by:
@@ -47,7 +47,7 @@ use tracing::error;
///
/// - `Ok(())` if the real-time priority was successfully set
/// - `Err` with the error message if setting the priority failed
#[cfg(unix)]
#[cfg(target_os = "linux")]
pub fn set_realtime_priority() -> Result<(), anyhow::Error> {
use anyhow::anyhow;
use libc::{SCHED_FIFO, pthread_setschedparam, sched_param};
@@ -84,9 +84,9 @@ pub fn set_realtime_priority() -> Result<(), anyhow::Error> {
Ok(())
}
#[cfg(not(unix))]
#[cfg(not(target_os = "linux"))]
pub fn set_realtime_priority() -> Result<(), anyhow::Error> {
log::error!("This platform does not support realtime threads");
tracing::error!("This platform does not support realtime threads");
Ok(())
}
@@ -137,7 +137,7 @@ pub fn set_realtime_priority() -> Result<(), anyhow::Error> {
/// # How it works
///
/// [Rest of documentation as before...]
#[cfg(unix)]
#[cfg(target_os = "linux")]
pub fn lock_memory() -> Result<(), Box<dyn std::error::Error>> {
use libc::{MCL_FUTURE, mlockall};
@@ -154,13 +154,13 @@ pub fn lock_memory() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
#[cfg(not(unix))]
#[cfg(not(target_os = "linux"))]
pub fn lock_memory() -> Result<(), Box<dyn std::error::Error>> {
log::error!("This platform does not support memory locking");
tracing::error!("This platform does not support memory locking");
Ok(())
}
pub fn set_core_affinity_first_core(core_id: usize) -> Result<(), anyhow::Error> {
pub fn set_core_affinity(core_id: usize) -> Result<(), anyhow::Error> {
let cores = core_affinity::get_core_ids().unwrap_or_default();
let core = cores.get(core_id);
if let Some(core) = core {
-1
View File
@@ -1 +0,0 @@
pub mod mutation;
-35
View File
@@ -1,35 +0,0 @@
use std::fmt::Debug;
use serde::Serialize;
use crate::machines::identification::MachineIdentificationUnique;
#[derive(Debug, Serialize, Clone)]
pub struct MutationResponse {
pub success: bool,
pub error: Option<String>,
}
impl MutationResponse {
pub fn success() -> Self {
Self {
success: true,
error: None,
}
}
pub fn error(error: String) -> Self {
Self {
success: false,
error: Some(error),
}
}
}
#[derive(Debug, serde::Deserialize)]
pub struct MachineMutationBody<T>
where
T: Debug,
{
pub machine_identification_unique: MachineIdentificationUnique,
pub data: T,
}
+3 -5
View File
@@ -1,12 +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 panic;
pub mod registry;
pub mod serial_detection;
pub trait SerialDevice: Any + Send + Sync + SerialDeviceNew + Debug {}
@@ -25,11 +23,11 @@ pub trait SerialDeviceThread {
pub struct SerialDeviceNewParams {
pub path: String,
pub device_thread_panic_tx: smol::channel::Sender<(String, anyhow::Error)>,
}
#[derive(PartialEq, Clone, Debug)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct SerialDeviceIdentification {
pub vendor_id: u16,
pub product_id: u16,
}
*/
-52
View File
@@ -1,52 +0,0 @@
use smol::channel::Sender;
/// Device-level panic handler for individual device crashes
/// Sends device identifier and error information for cleanup
/// Used by serial devices and other individual components that need to be removed on panic
pub fn send_serial_device_panic<T>(
device_identifier: T,
thread_panic_tx: Sender<(T, anyhow::Error)>,
) where
T: Send + Sync + Clone + 'static,
{
// Ensure backtrace is enabled for panics
if std::env::var("RUST_BACKTRACE").is_err() {
unsafe {
std::env::set_var("RUST_BACKTRACE", "full");
}
}
let device_id_clone = device_identifier.clone();
std::panic::set_hook(Box::new(move |panic_info| {
let thread_name = std::thread::current()
.name()
.unwrap_or("unnamed")
.to_string();
let location = panic_info.location().map_or("unknown".to_string(), |loc| {
format!("{}:{}:{}", loc.file(), loc.line(), loc.column())
});
let message = panic_info
.payload()
.downcast_ref::<&str>()
.unwrap_or(&"Box<dyn Any>")
.to_string();
let panic_message = format!(
"Device panicked in thread '{}' at {}: {}",
thread_name, location, message
);
// Send device panic info through channel
smol::block_on(async {
let _ = thread_panic_tx
.send((
device_id_clone.clone(),
anyhow::anyhow!("{}", panic_message),
))
.await;
});
// Note: We don't call old_hook to avoid duplicate logging
// All logging is handled in the main thread when receiving panic details
}));
}
+49 -154
View File
@@ -7,175 +7,70 @@
*/
use serialport::{SerialPortInfo, SerialPortType, UsbPortInfo};
use smol::{
channel::{Receiver, Sender, unbounded},
lock::RwLock,
};
use std::{collections::HashMap, sync::Arc};
use std::collections::HashMap;
use crate::{
helpers::compare_lists::compare_lists, machines::identification::DeviceIdentification,
};
use super::SerialDeviceIdentification;
/// Handles scanning and caching of connected USB serial devices.
pub struct SerialDetection {}
use super::{
SerialDevice, SerialDeviceIdentification, SerialDeviceNewParams, registry::SerialDeviceRegistry,
};
pub struct SerialDetection<'serialdeviceregistry> {
pub serial_device_registry: &'serialdeviceregistry SerialDeviceRegistry,
pub ports: HashMap<
String,
(
UsbPortInfo,
DeviceIdentification,
Arc<RwLock<dyn SerialDevice>>,
),
>,
pub device_removal_signal_rx: Receiver<(String, anyhow::Error)>,
pub device_removal_signal_tx: Sender<(String, anyhow::Error)>,
}
impl<'serialdeviceregistry> SerialDetection<'serialdeviceregistry> {
pub fn new(serial_device_registry: &'serialdeviceregistry SerialDeviceRegistry) -> Self {
let (device_removal_signal_tx, device_removal_signal_rx) =
unbounded::<(String, anyhow::Error)>();
SerialDetection {
serial_device_registry,
ports: HashMap::new(),
device_removal_signal_rx,
device_removal_signal_tx,
impl SerialDetection {
/// Returns all serial ports on the system (minus macOS "cu" noise).
fn get_ports() -> Vec<SerialPortInfo> {
match serialport::available_ports() {
Ok(ports) => ports
.into_iter()
.filter(|port| !port.port_name.contains("cu"))
.collect(),
Err(_) => Vec::new(),
}
}
/*
*@return: Option::None if there are no available ports on device
*@return: Option::Some(Vec) with all available ports
*
*@description: detects all available ports and returns them as list
*/
fn get_ports() -> Vec<SerialPortInfo> {
let all = match serialport::available_ports() {
Ok(ports) => ports,
Err(_) => return Vec::new(),
};
// exclude ports that contai "cu" in their name
all.into_iter()
.filter(|port| !port.port_name.contains("cu"))
.collect::<Vec<_>>()
}
/* @param: port_list -> list of available ports
*
* @return: Vec<(String, UsbPortInfo)> -> list of ports with their names and usb information
*
* @description: This function extracts the USB serial devices from the given list of ports.
*/
fn extract_usb_serial_devices(
&self,
port_list: Vec<SerialPortInfo>,
) -> Vec<(String, UsbPortInfo)> {
let mut usb_ports: Vec<(String, UsbPortInfo)> = Vec::new();
/// Extracts only USB serial devices from the port list.
fn extract_usb_serial_devices(port_list: Vec<SerialPortInfo>) -> HashMap<String, UsbPortInfo> {
let mut map = HashMap::new();
for port in port_list {
match &port.port_type {
SerialPortType::UsbPort(usb_info) => {
usb_ports.push((port.port_name.to_string(), usb_info.clone()));
}
_ => {}
};
if let SerialPortType::UsbPort(usb_info) = port.port_type {
map.insert(port.port_name, usb_info);
}
}
return usb_ports;
map
}
pub async fn check_ports(&mut self) -> CheckPortsResult {
// get available ports
/// Scans for connected USB serial devices and updates the internal map.
/// Returns a clone of the map for quick access.
pub fn detect_devices() -> HashMap<String, UsbPortInfo> {
let ports = Self::get_ports();
// extract all ports that are usb ports
let usb_ports = self.extract_usb_serial_devices(ports);
let last_usb_ports = self
.ports
.iter()
.map(|last_usb_port| (last_usb_port.0.clone(), last_usb_port.1.0.clone()))
.collect::<Vec<_>>();
let usb_ports_diff = compare_lists(&last_usb_ports, &usb_ports);
let mut result = CheckPortsResult {
added: Vec::new(),
removed: Vec::new(),
};
// reamove ports
for removed in usb_ports_diff.removed {
if let Some((_, device_identification, _)) = self.ports.remove(&removed.0) {
result.removed.push(device_identification);
}
tracing::trace!("Removed port: {}", removed.0);
}
// add new ports
for added in usb_ports_diff.added {
// add the port to the list
let serial_device_identification = SerialDeviceIdentification {
vendor_id: added.1.vid,
product_id: added.1.pid,
};
let device_result = self.serial_device_registry.new_serial_device(
&SerialDeviceNewParams {
path: added.0.clone(),
device_thread_panic_tx: self.device_removal_signal_tx.clone(),
},
&serial_device_identification,
);
// only if created device driver sucessfully
if let Ok((device_identification, device)) = device_result {
// add the device to the ports list
self.ports.insert(
added.0.clone(),
(
added.1.clone(),
device_identification.clone(),
device.clone(),
),
);
// add to result list
result.added.push((device_identification, device.clone()));
tracing::trace!("Added port: {}", added.0);
}
}
result
let usb_devices = Self::extract_usb_serial_devices(ports);
usb_devices
}
pub async fn check_remove_signals(&mut self) -> Vec<DeviceIdentification> {
let mut removed_signals: Vec<DeviceIdentification> = Vec::new();
match self.device_removal_signal_rx.try_recv() {
Ok((path, error)) => {
// remove the device when the tuple positon 1 equals signal
if let Some(info) = self.ports.get(&path.clone()) {
removed_signals.push(info.1.clone());
};
self.ports.remove(&path);
pub fn device_port_exists(device_path: &str) -> bool {
let ports = Self::get_ports();
return ports.iter().any(|p| p.port_name == device_path);
}
tracing::trace!("Removed device: {:?}: {}", path, error);
pub fn device_id_exists(sdevid: SerialDeviceIdentification) -> bool {
let ports = Self::get_ports();
for port in ports {
if let SerialPortType::UsbPort(usb_info) = port.port_type {
if usb_info.vid == sdevid.vendor_id && usb_info.pid == sdevid.product_id {
return true;
}
}
Err(_) => {}
}
removed_signals
return false;
}
pub fn get_path_by_id(
sdevid: SerialDeviceIdentification,
map: HashMap<String, UsbPortInfo>,
) -> Option<String> {
for (port, usbport) in map.iter() {
if usbport.vid == sdevid.vendor_id && usbport.pid == sdevid.product_id {
return Some(port.clone());
}
}
return None;
}
}
#[derive(Debug)]
pub struct CheckPortsResult {
pub added: Vec<(DeviceIdentification, Arc<RwLock<dyn SerialDevice>>)>,
pub removed: Vec<DeviceIdentification>,
}
+4
View File
@@ -21,6 +21,10 @@ impl std::fmt::Debug for GenericEvent {
}
}
pub trait BuildEvent: Serialize + Send + Sync + Clone {
fn build(&self) -> Event<Self>;
}
#[derive(Debug, Clone, Serialize)]
pub struct Event<T>
where
-1
View File
@@ -1,3 +1,2 @@
pub mod event;
pub mod namespace;
pub mod namespace_id;
+10 -15
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>>>,
@@ -30,7 +30,7 @@ impl Namespace {
#[instrument(skip_all)]
pub fn subscribe(&mut self, socket: SocketRef) {
// add the socket to the list
self.sockets.push(socket.clone());
self.sockets.push(socket);
}
/// Removes a socket from the namespace.
@@ -81,7 +81,7 @@ impl Namespace {
for (_event_name, events) in event_groups {
for event in events {
// Send to global queue instead of per-socket queue
self.send_to_queue(&socket, &event, "reemit");
self.send_to_queue(&socket, event, "reemit");
}
}
}
@@ -97,13 +97,10 @@ impl Namespace {
fn cache(
&mut self,
event: Arc<GenericEvent>,
buffer_fn: &Box<dyn Fn(&mut Vec<Arc<GenericEvent>>, &Arc<GenericEvent>) -> ()>,
buffer_fn: &Box<dyn Fn(&mut Vec<Arc<GenericEvent>>, &Arc<GenericEvent>)>,
) {
let mut cached_events_for_key = self
.events
.entry(event.name.clone())
.or_insert_with(Vec::new);
buffer_fn(&mut cached_events_for_key, &event);
let cached_events_for_key = self.events.entry(event.name.clone()).or_default();
buffer_fn(cached_events_for_key, &event);
}
/// Emits an event to all sockets in the namespace and caches it.
@@ -117,11 +114,10 @@ impl Namespace {
pub fn emit(
&mut self,
event: Arc<GenericEvent>,
buffer_fn: &Box<dyn Fn(&mut Vec<Arc<GenericEvent>>, &Arc<GenericEvent>) -> ()>,
buffer_fn: &Box<dyn Fn(&mut Vec<Arc<GenericEvent>>, &Arc<GenericEvent>)>,
) {
// cache the event
self.cache(event.clone(), &buffer_fn);
self.cache(event.clone(), buffer_fn);
// emit the event - inlined from emit function
// Send to global queue for each socket in the namespace
for socket in self.sockets.clone() {
@@ -171,7 +167,7 @@ impl Namespace {
impl Drop for Namespace {
fn drop(&mut self) {
self.disconnect_all();
//self.disconnect_all();
}
}
@@ -191,7 +187,7 @@ pub trait CacheableEvents<Events> {
fn event_cache_fn(&self) -> CacheFn;
}
pub type CacheFn = Box<dyn Fn(&mut Vec<Arc<GenericEvent>>, &Arc<GenericEvent>) -> ()>;
pub type CacheFn = Box<dyn Fn(&mut Vec<Arc<GenericEvent>>, &Arc<GenericEvent>)>;
/// [`BufferFn`] that stores the last n events
pub fn cache_n_events(n: usize) -> CacheFn {
@@ -223,7 +219,6 @@ pub fn cache_first_and_last_event() -> CacheFn {
if events.len() == 2 {
events.remove(1);
events.push(event.clone());
return;
}
})
}
+27
View File
@@ -0,0 +1,27 @@
use crate::transmission::Transmission;
#[derive(Debug, Clone)]
pub struct FixedTransmission {
ratio: f64,
}
impl Transmission for FixedTransmission {
fn get_ratio(&self) -> f64 {
self.ratio
}
}
impl FixedTransmission {
/// Creates a new FixedTransmission with the specified ratio.
///
/// # Arguments
///
/// * `ratio` - The transmission ratio (output over input).
///
/// # Returns
///
/// A new instance of FixedTransmission.
pub const fn new(ratio: f64) -> Self {
Self { ratio }
}
}
+95
View File
@@ -0,0 +1,95 @@
use units::{
angle::radian,
angular_velocity::radian_per_second,
f64::{Angle, AngularVelocity, Length, Velocity},
length::meter,
velocity::meter_per_second,
};
pub mod fixed;
/// A trait representing a mechanical transmission system that converts input to output
/// with a specific gear ratio.
///
/// This trait provides methods for working with transmission ratios and converting
/// between input and output values based on the transmission's gear ratio.
pub trait Transmission {
/// Returns the transmission ratio of this transmission system.
///
/// The ratio represents the relationship between input and output speeds/torques.
/// A ratio greater than 1.0 indicates speed reduction (torque multiplication),
/// while a ratio less than 1.0 indicates speed increase (torque reduction).
///
/// # Returns
///
/// The transmission ratio as a floating-point number.
fn get_ratio(&self) -> f64;
/// Converts an input value to the corresponding output value using the transmission ratio.
///
/// This method multiplies the input by the transmission ratio to calculate the output.
/// Typically used for converting input speed to output speed in a transmission system.
///
/// # Arguments
///
/// * `input` - The input value to be converted
///
/// # Returns
///
/// The output value after applying the transmission ratio.
fn calculate_output(&self, input: f64) -> f64 {
input * self.get_ratio()
}
/// Converts an output value to the corresponding input value using the transmission ratio.
///
/// This method divides the output by the transmission ratio to calculate the required input.
/// Typically used for determining what input is needed to achieve a desired output.
///
/// # Arguments
///
/// * `output` - The output value to be converted back to input
///
/// # Returns
///
/// The input value required to produce the given output.
fn calculate_input(&self, output: f64) -> f64 {
output / self.get_ratio()
}
fn calculate_angular_output(&self, input: Angle) -> Angle {
Angle::new::<radian>(self.calculate_output(input.get::<radian>()))
}
fn calculate_angular_input(&self, output: Angle) -> Angle {
Angle::new::<radian>(self.calculate_input(output.get::<radian>()))
}
fn calculate_angular_velocity_output(&self, input: AngularVelocity) -> AngularVelocity {
AngularVelocity::new::<radian_per_second>(
self.calculate_output(input.get::<radian_per_second>()),
)
}
fn calculate_angular_velocity_input(&self, output: AngularVelocity) -> AngularVelocity {
AngularVelocity::new::<radian_per_second>(
self.calculate_input(output.get::<radian_per_second>()),
)
}
fn calculate_linear_output(&self, input: Length) -> Length {
Length::new::<meter>(self.calculate_output(input.get::<meter>()))
}
fn calculate_linear_input(&self, output: Length) -> Length {
Length::new::<meter>(self.calculate_input(output.get::<meter>()))
}
fn calculate_linear_velocity_output(&self, input: Velocity) -> Velocity {
Velocity::new::<meter_per_second>(self.calculate_output(input.get::<meter_per_second>()))
}
fn calculate_linear_velocity_input(&self, output: Velocity) -> Velocity {
Velocity::new::<meter_per_second>(self.calculate_input(output.get::<meter_per_second>()))
}
}
@@ -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";
}
+208
View File
@@ -0,0 +1,208 @@
use anyhow::Error;
use std::collections::HashMap;
use tokio::sync::broadcast;
/// Video stream manager that handles multiple video streams per machine with true broadcasting
/// Uses tokio broadcast channels to ensure all subscribers receive all messages without stealing
#[derive(Debug)]
pub struct VideoStreamManager {
/// Each stream has its own broadcast transmitter and template receiver
streams: HashMap<String, (broadcast::Sender<Vec<u8>>, broadcast::Receiver<Vec<u8>>)>,
}
impl VideoStreamManager {
pub fn new() -> Self {
Self {
streams: HashMap::new(),
}
}
/// Send a frame to all subscribers of a stream
/// Returns Ok if the frame was sent, Err if the stream doesn't exist
pub fn send_frame(&self, stream_id: &str, frame: Vec<u8>) -> Result<(), Error> {
if let Some((tx, _rx)) = self.streams.get(stream_id) {
match tx.send(frame) {
Ok(_) => Ok(()),
Err(_) => Ok(()), // No receivers, but that's not an error
}
} else {
Err(anyhow::anyhow!("Stream '{}' not found", stream_id))
}
}
/// Register a new video stream with the given ID
pub fn add_stream(&mut self, stream_id: String) {
let (tx, rx) = broadcast::channel::<Vec<u8>>(256); // 256 frame buffer capacity
self.streams.insert(stream_id, (tx, rx));
}
/// Get list of available stream IDs
pub fn get_streams(&self) -> Vec<String> {
self.streams.keys().cloned().collect()
}
/// Check if a stream exists
pub fn has_stream(&self, stream_id: &str) -> bool {
self.streams.contains_key(stream_id)
}
/// Remove a stream and all its subscribers
pub fn remove_stream(&mut self, stream_id: &str) -> bool {
self.streams.remove(stream_id).is_some()
}
/// Subscribe to a video stream and get a receiver
/// Returns the receiver for the new subscription
pub fn subscribe_to_stream(&self, stream_id: &str) -> Option<broadcast::Receiver<Vec<u8>>> {
self.streams
.get(stream_id)
.map(|(_tx, rx)| rx.resubscribe()) // Resubscribe to create a new independent receiver
}
/// Get subscriber count for a stream
pub fn get_subscriber_count(&self, stream_id: &str) -> usize {
self.streams
.get(stream_id)
.map_or(0, |(tx, _rx)| tx.receiver_count())
}
/// Get a stream transmitter for sending frames (used by machines to send video)
/// Returns None if the stream doesn't exist
pub fn get_sender(&self, stream_id: &str) -> Option<broadcast::Sender<Vec<u8>>> {
self.streams.get(stream_id).map(|(tx, _rx)| tx.clone())
}
}
impl Default for VideoStreamManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_video_stream_manager() {
let mut manager = VideoStreamManager::new();
// Register streams
manager.register_stream("camera1".to_string());
manager.register_stream("camera2".to_string());
assert_eq!(manager.get_stream_ids().len(), 2);
assert!(manager.has_stream("camera1"));
assert!(manager.has_stream("camera2"));
assert!(!manager.has_stream("camera3"));
// Subscribe to streams
let mut rx1 = manager.subscribe_to_stream("camera1").unwrap();
let mut rx2 = manager.subscribe_to_stream("camera1").unwrap();
let mut rx3 = manager.subscribe_to_stream("camera2").unwrap();
assert_eq!(manager.get_subscriber_count("camera1"), 3); // Original + 2 clones
assert_eq!(manager.get_subscriber_count("camera2"), 2); // Original + 1 clone
assert_eq!(manager.get_subscriber_count("nonexistent"), 0);
// Send frames
let frame1 = vec![1, 2, 3, 4];
let frame2 = vec![5, 6, 7, 8];
assert!(manager.send_single_frame("camera1", frame1.clone()));
assert!(manager.send_single_frame("camera2", frame2.clone()));
assert!(!manager.send_single_frame("nonexistent", vec![]));
// Both camera1 subscribers should receive the frame
assert_eq!(rx1.recv().await.unwrap(), frame1);
assert_eq!(rx2.recv().await.unwrap(), frame1);
// Camera2 subscriber should receive its frame
assert_eq!(rx3.recv().await.unwrap(), frame2);
}
#[tokio::test]
async fn test_video_frame_broadcasting() {
let mut manager = VideoStreamManager::new();
manager.register_stream("main_camera".to_string());
// Multiple clients subscribe to the same camera
let mut rx1 = manager.subscribe_to_stream("main_camera").unwrap();
let mut rx2 = manager.subscribe_to_stream("main_camera").unwrap();
let mut rx3 = manager.subscribe_to_stream("main_camera").unwrap();
// Simulate video frames
let frames = vec![
vec![0xFF, 0xD8, 0xFF, 0xE0], // JPEG header
vec![0x00, 0x01, 0x02, 0x03], // Frame 1
vec![0x04, 0x05, 0x06, 0x07], // Frame 2
];
for frame in &frames {
assert!(manager.send_single_frame("main_camera", frame.clone()));
}
// All clients should receive all frames in order
for frame in &frames {
assert_eq!(rx1.recv().await.unwrap(), *frame);
assert_eq!(rx2.recv().await.unwrap(), *frame);
assert_eq!(rx3.recv().await.unwrap(), *frame);
}
}
#[test]
fn test_stream_management() {
let mut manager = VideoStreamManager::new();
// Initially no streams
assert_eq!(manager.get_stream_ids().len(), 0);
// Add streams
manager.register_stream("front_camera".to_string());
manager.register_stream("rear_camera".to_string());
manager.register_stream("side_camera".to_string());
let mut stream_ids = manager.get_stream_ids();
stream_ids.sort(); // Sort for consistent testing
assert_eq!(
stream_ids,
vec!["front_camera", "rear_camera", "side_camera"]
);
// Remove a stream
assert!(manager.remove_stream("rear_camera"));
assert!(!manager.remove_stream("rear_camera")); // Already removed
assert!(!manager.has_stream("rear_camera"));
assert_eq!(manager.get_stream_ids().len(), 2);
}
#[tokio::test]
async fn test_send_frame_results() {
let mut manager = VideoStreamManager::new();
manager.register_stream("test_camera".to_string());
// One initial receiver (the original one created in register_stream)
let result = manager.send_frame("test_camera", vec![1, 2, 3]).unwrap();
assert_eq!(result, 1); // Number of subscribers
// Add subscribers by cloning the receiver
let _rx1 = manager.subscribe_to_stream("test_camera").unwrap();
let _rx2 = manager.subscribe_to_stream("test_camera").unwrap();
// Send frame to multiple subscribers (original + 2 clones = 3 total)
let result = manager.send_frame("test_camera", vec![4, 5, 6]).unwrap();
assert_eq!(result, 3); // Number of subscribers
// Try sending to non-existent stream
let result = manager.send_frame("nonexistent", vec![7, 8, 9]);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Stream 'nonexistent' not found");
}
#[test]
fn test_default_video_stream_manager() {
let manager = VideoStreamManager::default();
assert_eq!(manager.get_stream_ids().len(), 0);
}
}
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: 7.2 KiB

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

Loaded 100 of 693 files, more files were not shown because too many files have changed in this diff. Show more