Compare commits

...
Author SHA1 Message Date
93094049+kraemr@users.noreply.github.com 3f4571e798 finally killed borrow_checker issues in setup.rs 2025-08-25 12:28:54 +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
189 changed files with 7122 additions and 2059 deletions

No files matched your search

+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"
+4 -1
View File
@@ -36,6 +36,9 @@ jobs:
- name: Install
run: npm i
- name: Build
run: tsc
- name: Format
run: npm run format
@@ -45,5 +48,5 @@ jobs:
- name: Test
run: npm run test
- name: Build
- name: Package
run: npm run package
+7
View File
@@ -21,6 +21,8 @@ jobs:
- uses: actions/checkout@v4
- name: Set Rust version
run: rustup override set ${{ env.RUST_VERSION }}
- name: Install Formatter
run: rustup component add rustfmt
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
@@ -35,7 +37,12 @@ jobs:
${{ runner.os }}-${{ env.RUST_VERSION }}-cargo-
- name: Build
env:
RUSTFLAGS: "-D warnings"
run: cargo build --verbose
- name: Format
run: cargo fmt --check --verbose
- name: Run tests
run: cargo test --verbose
+33
View File
@@ -1,3 +1,36 @@
# `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) switched to a Realtime Linuxkernel on nixos
- [#574](https://github.com/qitechgmbh/control/pull/587) isolated cores 2 and 3 on nixos to guarentee strict timings for ethercat
- [#588](https://github.com/qitechgmbh/control/pull/589) limited frontend to 30 fps to reduce cpu usage
- [#572](https://github.com/qitechgmbh/control/pull/572) switched to io_uring to reduce latency
# `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_
Generated
+98 -46
View File
@@ -256,7 +256,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -273,7 +273,7 @@ checksum = "99e1aca718ea7b89985790c94aad72d77533063fe00bc497bb79a7c2dae6a661"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -345,7 +345,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -539,7 +539,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -728,7 +728,7 @@ dependencies = [
"deluxe-macros",
"once_cell",
"proc-macro2",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -741,7 +741,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -756,7 +756,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -767,7 +767,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -804,7 +804,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -1019,7 +1019,7 @@ dependencies = [
"deluxe",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -1071,7 +1071,7 @@ dependencies = [
"criterion",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -1200,7 +1200,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -1736,9 +1736,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.172"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libloading"
@@ -2175,7 +2175,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -2206,7 +2206,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -2342,7 +2342,7 @@ dependencies = [
"itertools",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -2374,9 +2374,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
@@ -2582,7 +2582,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -2602,7 +2602,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -2683,9 +2683,9 @@ dependencies = [
[[package]]
name = "serialport"
version = "4.7.1"
version = "4.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2daa7abb9b965493e3c8f4184c6f46435484ff2538a332b886788cf6768b927b"
checksum = "cdb0bc984f6af6ef8bab54e6cf2071579ee75b9286aa9f2319a0d220c28b0a2b"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
@@ -2856,9 +2856,9 @@ dependencies = [
[[package]]
name = "socketioxide"
version = "0.17.1"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85fe8a90fd0cdac0bcb7b3c40c1084f5658f8737c62dfb277f5620cf78f3849a"
checksum = "476190583b592f1e3d55584269600f2d3c4f18af36adad03c41c27e82dcb6bd5"
dependencies = [
"bytes",
"engineioxide",
@@ -2963,9 +2963,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.101"
version = "2.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
dependencies = [
"proc-macro2",
"quote",
@@ -2989,7 +2989,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -3034,7 +3034,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -3045,16 +3045,16 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
[[package]]
name = "thread-priority"
version = "1.2.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe075d7053dae61ac5413a34ea7d4913b6e6207844fd726bdd858b37ff72bf5"
checksum = "cd4ef372b29fbcc6cb0cef97bcbf3ffa9da90e02eb23dbf14db48ce30d11d2d5"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
"libc",
"log",
"rustversion",
"winapi",
"windows",
]
[[package]]
@@ -3129,9 +3129,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.45.0"
version = "1.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
dependencies = [
"backtrace",
"bytes",
@@ -3151,7 +3151,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -3313,7 +3313,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -3570,7 +3570,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
"wasm-bindgen-shared",
]
@@ -3605,7 +3605,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3670,6 +3670,28 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections",
"windows-core",
"windows-future",
"windows-link",
"windows-numerics",
]
[[package]]
name = "windows-collections"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core",
]
[[package]]
name = "windows-core"
version = "0.61.2"
@@ -3683,6 +3705,17 @@ dependencies = [
"windows-strings 0.4.2",
]
[[package]]
name = "windows-future"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core",
"windows-link",
"windows-threading",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
@@ -3691,7 +3724,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -3702,14 +3735,24 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
name = "windows-link"
version = "0.1.1"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core",
"windows-link",
]
[[package]]
name = "windows-registry"
@@ -3812,6 +3855,15 @@ dependencies = [
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows-threading"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@@ -3991,7 +4043,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
"synstructure",
]
@@ -4012,7 +4064,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
[[package]]
@@ -4032,7 +4084,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
"synstructure",
]
@@ -4066,5 +4118,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.105",
]
+8 -3
View File
@@ -4,6 +4,9 @@
QiTech Control is an industrial control panel software for the next generation of QiTech recycling machines built on top of Beckhoff Automation hardware.
[![](https://img.youtube.com/vi/KI3YeBwfV-s/maxresdefault.jpg)](https://www.youtube.com/watch?v=KI3YeBwfV-s)
*Click here to watch a video demo of our software.*
# Repo Structure
Frontend
@@ -41,6 +44,8 @@ We could combine the code of the frontend and backend using [Doxius](https://dio
# Dev Setup
[Developer Documentation](./docs/developer-docs/)
## Backend
- Rust stable 1.86^ toolchain (install via [rustup](https://rustup.rs/))
@@ -66,7 +71,6 @@ We could combine the code of the frontend and backend using [Doxius](https://dio
| Laser | V1 | ??? | Diameter Measuring Laser | - | 1 (Qitech Industries GmbH) | 6 | Not Yet | |
| Mock | - | ??? | Mock Machine for Testing | - | 1 (Qitech Industries GmbH) | 7 | Not Yet | - |
# More Docs
- [x] [Architecture & Data Flow](./docs/architecture-overview.md)
@@ -90,8 +94,9 @@ We could combine the code of the frontend and backend using [Doxius](https://dio
- Write Device Identification
- Server
- [X] [Threading](./docs/control-loop.md)
- [X] [Logging](./docs/logging.md)
- [x] [Threading](./docs/control-loop.md)
- [x] [Logging](./docs/logging.md)
- [ ] Control Loop Setup
- Control Loop Thread
- [ ] realtime
+17 -5
View File
@@ -1,13 +1,25 @@
#!/bin/bash
if [ $1 == "release" ]; then
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
fi
# Only setcap if not building with mock-machine
if [ "$1" != "mock-machine" ]; then
echo "Setting capabilities for server executable"
sudo setcap 'cap_net_raw,cap_sys_nice=eip' ./target/debug/server
fi
# set cap for server executable
sudo setcap cap_net_raw=eip ./target/debug/server
# run
./target/debug/server
+3 -3
View File
@@ -11,9 +11,9 @@ ethercrab = "0.6"
interfaces = "0.0.9"
serde = "1.0.219"
serde_json = "1.0.140"
serialport = "4.7.1"
serialport = "4.7.2"
smol = "2.0.2"
socketioxide = "0.17.1"
socketioxide = "0.17.2"
uom = "0.36.0"
bitvec = { version = "1.0.1", features = ["alloc"] }
crc = "3.3.0"
@@ -23,7 +23,7 @@ erased-serde = "0.4.6"
core_affinity = "0.8.3"
[target.'cfg(unix)'.dependencies]
libc = "0.2.172"
libc = "0.2.175"
[dev-dependencies]
approx = "0.5.1"
@@ -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,
)
}
@@ -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,
)
}
@@ -38,10 +38,10 @@ 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,
)
}
@@ -4,6 +4,7 @@ use ethercat_hal::devices::ek1100::EK1100_IDENTITY_A;
use ethercat_hal::devices::el1002::EL1002_IDENTITY_A;
use ethercat_hal::devices::el1008::EL1008_IDENTITY_A;
use ethercat_hal::devices::el2002::EL2002_IDENTITY_A;
use ethercat_hal::devices::el2002::EL2002_IDENTITY_B;
use ethercat_hal::devices::el2004::EL2004_IDENTITY_A;
use ethercat_hal::devices::el2008::EL2008_IDENTITY_A;
use ethercat_hal::devices::el2521::{
@@ -13,6 +14,7 @@ use ethercat_hal::devices::el2522::EL2522_IDENTITY_A;
use ethercat_hal::devices::el3001::EL3001_IDENTITY_A;
use ethercat_hal::devices::el3021::EL3021_IDENTITY_A;
use ethercat_hal::devices::el3024::EL3024_IDENTITY_A;
use ethercat_hal::devices::el3062_0030::EL3062_0030_IDENTITY_A;
use ethercat_hal::devices::el3204::EL3204_IDENTITY_A;
use ethercat_hal::devices::el3204::EL3204_IDENTITY_B;
use ethercat_hal::devices::el6021::{
@@ -26,6 +28,7 @@ use ethercat_hal::helpers::ethercrab_types::{
EthercrabSubDeviceOperational, EthercrabSubDevicePreoperational,
};
use ethercrab::MainDevice;
use ethercrab::SubDeviceGroup;
use ethercrab::SubDeviceIdentity;
use crate::machines::identification::DeviceMachineIdentification;
@@ -66,11 +69,11 @@ impl Default for MachineIdentificationAddresses {
///
/// Returns a vector of MachineDeviceIdentification for all subdevices
pub async fn read_device_identifications<'maindevice>(
subdevices: &Vec<EthercrabSubDevicePreoperational<'maindevice>>,
group: &SubDeviceGroup<16, 256>,
maindevice: &MainDevice<'maindevice>,
) -> Vec<Result<DeviceMachineIdentification, Error>> {
let mut result = Vec::new();
for subdevice in subdevices.iter() {
for subdevice in group.iter(maindevice) {
let identification = machine_device_identification(&subdevice, maindevice).await;
result.push(identification);
}
@@ -228,7 +231,7 @@ pub fn get_identification_addresses<'maindevice>(
EK1100_IDENTITY_A => MachineIdentificationAddresses::default(),
EL1002_IDENTITY_A => MachineIdentificationAddresses::default(),
EL1008_IDENTITY_A => MachineIdentificationAddresses::default(),
EL2002_IDENTITY_A => MachineIdentificationAddresses::default(),
EL2002_IDENTITY_A | EL2002_IDENTITY_B => MachineIdentificationAddresses::default(),
EL2004_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3204_IDENTITY_A | EL3204_IDENTITY_B => MachineIdentificationAddresses::default(),
EL2008_IDENTITY_A => MachineIdentificationAddresses::default(),
@@ -237,8 +240,9 @@ pub fn get_identification_addresses<'maindevice>(
MachineIdentificationAddresses::default()
}
EL2522_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3024_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3021_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3024_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3062_0030_IDENTITY_A => MachineIdentificationAddresses::default(),
EL7031_IDENTITY_A | EL7031_IDENTITY_B => MachineIdentificationAddresses::default(),
EL7031_0030_IDENTITY_A => MachineIdentificationAddresses::default(),
EL7041_0052_IDENTITY_A => MachineIdentificationAddresses::default(),
@@ -56,7 +56,7 @@ impl DivideByCount for u64 {
/// 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);
/// ```
+53 -4
View File
@@ -12,7 +12,10 @@ use crate::{
serial::SerialDevice,
socketio::event::GenericEvent,
};
use std::{collections::HashMap, sync::Arc};
use std::{
collections::HashMap,
sync::{Arc, Weak},
};
use super::{
Machine,
@@ -23,9 +26,9 @@ use super::{
#[derive(Debug)]
pub struct MachineManager {
pub ethercat_machines:
HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
HashMap<MachineIdentificationUnique, Result<Arc<Mutex<dyn Machine>>, anyhow::Error>>,
pub serial_machines:
HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
HashMap<MachineIdentificationUnique, Result<Arc<Mutex<dyn Machine>>, anyhow::Error>>,
}
impl MachineManager {
@@ -42,6 +45,7 @@ impl MachineManager {
machine_registry: &MachineRegistry,
hardware: &MachineNewHardwareEthercat,
socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
machine_manager: Weak<RwLock<MachineManager>>,
) {
// empty ethercat machines
self.ethercat_machines.clear();
@@ -69,6 +73,7 @@ impl MachineManager {
device_group,
hardware: &machine_new_hardware,
socket_queue_tx: socket_queue_tx.clone(),
machine_manager: machine_manager.clone(),
});
// insert the machine into the ethercat machines map
@@ -82,7 +87,7 @@ impl MachineManager {
pub fn get(
&self,
machine_identification: &MachineIdentificationUnique,
) -> Option<&Result<Box<Mutex<dyn Machine>>, anyhow::Error>> {
) -> Option<&Result<Arc<Mutex<dyn Machine>>, anyhow::Error>> {
self.ethercat_machines
.get(machine_identification)
.or_else(|| self.serial_machines.get(machine_identification))
@@ -94,6 +99,7 @@ impl MachineManager {
device: Arc<RwLock<dyn SerialDevice>>,
machine_registry: &MachineRegistry,
socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
machine_manager: Weak<RwLock<MachineManager>>,
) {
let hardware = MachineNewHardwareSerial { device };
@@ -107,6 +113,7 @@ impl MachineManager {
device_group: &vec![device_identification_identified.clone()],
hardware: &MachineNewHardware::Serial(&hardware),
socket_queue_tx,
machine_manager: machine_manager.clone(),
});
tracing::info!("Adding serial machine {:?}", new_machine);
@@ -137,6 +144,48 @@ impl MachineManager {
.machine_identification_unique,
);
}
pub fn get_machine_weak(
&self,
machine_identification: &MachineIdentificationUnique,
) -> Option<Weak<Mutex<dyn Machine>>> {
let machine = self.ethercat_machines.get(machine_identification);
let machine = match machine {
Some(machine) => machine,
None => {
return None;
}
};
let machine = match machine {
Ok(machine) => machine,
Err(_) => {
return None;
}
};
let machine_weak = Arc::downgrade(machine);
return Some(machine_weak);
}
pub fn get_serial_weak(
&self,
machine_identification: &MachineIdentificationUnique,
) -> Option<Weak<Mutex<dyn Machine>>> {
let machine = self.serial_machines.get(machine_identification);
let machine = match machine {
Some(machine) => machine,
None => {
return None;
}
};
let machine = match machine {
Ok(machine) => machine,
Err(_) => {
return None;
}
};
let machine_weak = Arc::downgrade(machine);
return Some(machine_weak);
}
}
/// Groups devices by machine identification
+5 -5
View File
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::{collections::HashMap, sync::Arc};
use smol::lock::Mutex;
@@ -9,14 +9,14 @@ pub enum MachineManagerIter<'a> {
iter: std::collections::hash_map::Iter<
'a,
MachineIdentificationUnique,
Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
Result<Arc<Mutex<dyn Machine>>, anyhow::Error>,
>,
},
SerialMachines {
iter: std::collections::hash_map::Iter<
'a,
MachineIdentificationUnique,
Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
Result<Arc<Mutex<dyn Machine>>, anyhow::Error>,
>,
},
Done,
@@ -25,13 +25,13 @@ pub enum MachineManagerIter<'a> {
pub struct MachineManagerIterator<'a> {
iter: MachineManagerIter<'a>,
serial_machines:
&'a HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
&'a HashMap<MachineIdentificationUnique, Result<Arc<Mutex<dyn Machine>>, anyhow::Error>>,
}
impl<'a> Iterator for MachineManagerIterator<'a> {
type Item = (
&'a MachineIdentificationUnique,
&'a Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
&'a Result<Arc<Mutex<dyn Machine>>, anyhow::Error>,
);
fn next(&mut self) -> Option<Self::Item> {
+69 -2
View File
@@ -1,8 +1,14 @@
use crate::machines::new::MachineAct;
use anyhow::anyhow;
use api::MachineApi;
use new::MachineNewTrait;
use serde::Serialize;
use smol::lock::Mutex;
use std::any::Any;
use std::fmt::Debug;
use std::sync::{Arc, Weak};
use crate::machines::identification::MachineIdentificationUnique;
use crate::machines::new::MachineAct;
pub mod api;
pub mod identification;
pub mod manager;
@@ -10,4 +16,65 @@ pub mod manager_iter;
pub mod new;
pub mod registry;
pub trait Machine: MachineNewTrait + MachineApi + MachineAct + Any + Send + Sync + Debug {}
pub trait Machine: MachineAct + MachineNewTrait + MachineApi + Any + Debug + Send {
fn as_any(&self) -> &dyn Any;
}
/// Casts a `dyn Machine` to a specific machine type
pub async fn downcast_machine<T: Machine>(
machine: Arc<Mutex<dyn Machine>>,
) -> Result<Arc<Mutex<T>>, anyhow::Error> {
// Acquire a read lock on the RwLock
let read_lock = machine.lock().await;
// Check if the inner type can be downcasted to T
if read_lock.as_any().is::<T>() {
// Clone the Arc and return it as the desired type
let cloned_machine = Arc::clone(&machine);
// Transmute the Arc to the desired type
unsafe {
Ok(Arc::from_raw(
Arc::into_raw(cloned_machine) as *const Mutex<T>
))
}
} else {
Err(anyhow!(
"[{}::downcast_machine] Downcast failed",
module_path!()
))
}
}
/// Trait to get strong count to see if Weak has reference
pub trait GetStrongCount {
fn get_strong_count(&self) -> usize;
}
impl<T> GetStrongCount for Weak<Mutex<T>> {
fn get_strong_count(&self) -> usize {
self.strong_count()
}
}
#[derive(Debug)]
pub struct ConnectedMachine<T: GetStrongCount> {
pub machine_identification_unique: MachineIdentificationUnique,
pub machine: T,
}
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct ConnectedMachineData {
pub machine_identification_unique: MachineIdentificationUnique,
pub is_available: bool,
}
impl<T> From<&ConnectedMachine<T>> for ConnectedMachineData
where
T: GetStrongCount,
{
fn from(value: &ConnectedMachine<T>) -> Self {
Self {
machine_identification_unique: value.machine_identification_unique.clone(),
is_available: value.machine.get_strong_count() != 0,
}
}
}
+7 -2
View File
@@ -1,4 +1,4 @@
use crate::serial::SerialDevice;
use crate::{machines::manager::MachineManager, serial::SerialDevice};
use super::identification::DeviceIdentificationIdentified;
use anyhow::Error;
@@ -8,7 +8,11 @@ use ethercat_hal::{
use ethercrab::{SubDevice, SubDeviceRef};
use smol::{channel::Sender, lock::RwLock};
use socketioxide::extract::SocketRef;
use std::{pin::Pin, sync::Arc, time::Instant};
use std::{
pin::Pin,
sync::{Arc, Weak},
time::Instant,
};
use crate::socketio::event::GenericEvent;
@@ -47,6 +51,7 @@ pub struct MachineNewParams<
'machine_new_hardware_serial,
>,
pub socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
pub machine_manager: Weak<RwLock<MachineManager>>,
}
pub enum MachineNewHardware<
+4 -4
View File
@@ -1,10 +1,10 @@
use super::{Machine, identification::MachineIdentification, new::MachineNewParams};
use anyhow::Error;
use smol::lock::Mutex;
use std::{any::TypeId, collections::HashMap};
use std::{any::TypeId, collections::HashMap, sync::Arc};
pub type MachineNewClosure =
Box<dyn Fn(&MachineNewParams) -> Result<Box<Mutex<dyn Machine>>, Error> + Send + Sync>;
Box<dyn Fn(&MachineNewParams) -> Result<Arc<Mutex<dyn Machine>>, Error> + Send + Sync>;
pub struct MachineRegistry {
type_map: HashMap<TypeId, (MachineIdentification, MachineNewClosure)>,
@@ -27,7 +27,7 @@ impl MachineRegistry {
machine_identficiation,
// create a machine construction closure
Box::new(|machine_new_params| {
Ok(Box::new(Mutex::new(T::new(machine_new_params)?)))
Ok(Arc::new(Mutex::new(T::new(machine_new_params)?)))
}),
),
);
@@ -36,7 +36,7 @@ impl MachineRegistry {
pub fn new_machine(
&self,
machine_new_params: &MachineNewParams,
) -> Result<Box<Mutex<dyn Machine>>, anyhow::Error> {
) -> Result<Arc<Mutex<dyn Machine>>, anyhow::Error> {
// get machiine identification
let device_identification =
&machine_new_params
+1 -1
View File
@@ -160,7 +160,7 @@ pub fn lock_memory() -> Result<(), Box<dyn std::error::Error>> {
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 {
+84
View File
@@ -0,0 +1,84 @@
# Environment Setup
Working on this repository requires you to use cargo and npm. In the following document the installation process will be shown
## Ubuntu/Debian Installation
```bash
# Just press enter when prompted
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
apt install npm
apt install nodejs
# assuming you dont have git already, Git setup not included in here
apt install git
git clone git@github.com:qitechgmbh/control.git
cd control
cd electron
npm install
```
# Running Backend and Frontend Linux
## Backend
To Compile backend code and run it on Linux:
```bash
./cargo_run_linux.sh
```
The script sets capabilities on the compiled binary like raw socket access.
Here it is required that you are connected to atleast one machine that is communicated with over ethercat or usb/serial.
The Most minimal working setup would be to connect over ethernet to an ek1100 beckhoff terminal.
## Mock-Machine
The Mock-Machine can be used to test code that does not require an actual machine connection.
Like Frontend Code for example.
Mock machines can be used if the compile feature mock-machine is enabled.
```bash
# in root git folder
./cargo_run_linux.sh mock-machine
```
## Frontend
To run the Frontend Code:
```bash
# need to be in the electron folder and an additional terminal
cd electron
npm run start
```
# Contributing
Generally you contribute to the codebase by:
1. Opening or Choosing an existing issue (Bug,Feature,Task etc)
2. Work on it locally
3. Commit your changes locally
4. Before pushing changes, if there were changes on the master branch, rebase your branch like so:
```bash
git fetch
git rebase origin/master
```
5. Push changes
```bash
# IF you needed to rebase
git push --force-with-lease
# ELSE you just push
git push
```
6. Open a Pull Request on Github and link the issue by writing fix #issue_number, after the pull request is merged into master the branch is closed automatically
7. Request a Review and hope for the best :)
# Recommended Editor Setup
We recommend you to use an Editor with rust-analyzer support like VSCode to speed up development and detect errors before compiling.
+166
View File
@@ -0,0 +1,166 @@
# Presets
To allow the user to save the current configuration of a machine, they can create a preset in the presets page. Presets are presited in electron's local storage.
Presets are all handled by the `PresetsPage`. To use the `PresetsPage` for a single machine, one must implement the following.
Create a zod schema for the data, the presets contain.
```tsx
const mock1PresetDataSchema = z
.object({
frequency1: z.number(),
frequency2: z.number(),
frequency3: z.number(),
})
.partial();
type Mock1 = typeof mock1PresetDataSchema;
```
Here, intensionally, `Mock1` is the schema type itself. It can be used as a type argument everything that uses presets.
Next, create a map containing all schema versions currently supported (the new schema, to be precise).
```tsx
const schemas = new Map([[1, mock1PresetDataSchema]]);
```
New, the schema will be used to parse version `1`. To create new versions, see migrations below.
One also needs to define a table, how the preset data should be shown to the user.
```tsx
const previewEntries: PresetPreviewEntries<Mock1> = [
{
name: "Frequency 1",
unit: "mHz",
renderValue: (data: PresetData<Mock1>) => data?.frequency1?.toFixed(3),
},
{
name: "Frequency 2",
unit: "mHz",
renderValue: (data: PresetData<Mock1>) => data?.frequency2?.toFixed(3),
},
{
name: "Frequency 3",
unit: "mHz",
renderValue: (data: PresetData<Mock1>) => data?.frequency3?.toFixed(3),
},
];
```
`renderValue` gets the data itself and renders the corresponding part of it. Returning undefined will automatically be rendered as the value is not set, showing "N/A".
Create a function for the new react component and use the appropreate machine.
```tsx
export function Mock1PresetsPage() {
const { setFrequency1, setFrequency2, setFrequency3, defaultState, state } =
useMock1();
// ...
}
```
Here, two more functions need to be created. const toPresetData = (s: typeof state): PresetData<Mock1> => ({
frequency1: s?.frequency1 ?? defaultState?.frequency1,
frequency2: s?.frequency2 ?? defaultState?.frequency2,
frequency3: s?.frequency3 ?? defaultState?.frequency3,The first takes in a preset and applies it on the machine. If data is missing, use the default defined by the backend.
```tsx
export function Mock1PresetsPage() {
const { setFrequency1, setFrequency2, setFrequency3, defaultState, state } =
useMock1();
const applyPreset = (preset: Preset<Mock1>) => {
const frequency1 = preset.data?.frequency1 ?? 100;
const frequency2 = preset.data?.frequency2 ?? 200;
const frequency3 = preset.data?.frequency3 ?? 500;
setFrequency1(frequency1);
setFrequency2(frequency2);
setFrequency3(frequency3);
};
// ...
}
```
The later does the opposite, it turns a machine state, comming from the backend, into the preset data
```tsx
export function Mock1PresetsPage() {
const { setFrequency1, setFrequency2, setFrequency3, defaultState, state } =
useMock1();
// ...
const toPresetData = (s: typeof state): PresetData<Mock1> => ({
frequency1: s?.frequency1 ?? defaultState?.frequency1,
frequency2: s?.frequency2 ?? defaultState?.frequency2,
frequency3: s?.frequency3 ?? defaultState?.frequency3,
});
// ...
}
```
Having all this, we can simple pass it to the `PresetsPage`
```tsx
export function Mock1PresetsPage() {
// ...
return (
<PresetsPage
machine_identification={mock1.machine_identification}
currentState={toPresetData(state)}
schemas={schemas}
schemaVersion={1}
applyPreset={applyPreset}
previewEntries={previewEntries}
defaultState={toPresetData(defaultState)}
/>
);
}
```
Also mind the machine identification, that has to match the machine we are using and the schema versionen. Here we pass `1`, as we also used `1` in the schemas map.
## Migrations
Migrations are handled by the `PresetsPage`, however, one still has to define what to migrate.
To do so, first create the new schema.
```tsx
const fancyNewMock1PresetDataSchema = z
.object({
frequency1_now_with_ai: z.number(),
frequency2: z.number(),
frequency3: z.number(),
frequency4: z.number(),
})
.partial();
type Mock1 = typeof fancyNewMock1PresetDataSchema;
```
and then, also create a migration that uses the new schema, but can handle the old format.
```tsx
const mock1PresetDataSchemaMigration: PresetSchema = z.preprocess(
(obj: any): PresetData<Mock1> => ({
frequency1_now_with_ai: obj?.frequency1, // Here, a field have been renamed
frequency2: obj?.frequency2,
frequency3: obj?.frequency3,
frequency4: 666.0, // A new field is added. Initialize it with default the value
}),
fancyNewMock1PresetDataSchema,
);
```
Usings zods preprocessing step,
we can first transform the data as needed and then validate using the new schema accordingly.
We have to register both schemas in our schema map.
```tsx
const schemas = new Map<number, Mock1>([
[1, mock1PresetDataSchemaMigration as Mock1],
[2, fancyNewMock1PresetDataSchema],
]);
```
Mind the cast, which is ok, because `mock1PresetDataSchemaMigration` parses `fancyNewMock1PresetDataSchema` by construction.
Lastly, simply change the schema version being passed to the `PresetsPage`.
```diff
@@ -62,7 +77,7 @@ export function Mock1PresetsPage() {
machine_identification={mock1.machine_identification}
currentState={toPresetData(state)}
schemas={schemas}
- schemaVersion={1}
+ schemaVersion={2}
applyPreset={applyPreset}
previewEntries={previewEntries}
defaultState={toPresetData(defaultState)}
```
Make sure, that all (exsiting and new migrations) return a schema version 2 conform format.
Of corse, one can chain migrations of needed - just the end result must be complient.
+13
View File
@@ -0,0 +1,13 @@
# Electrical Diagrams
This directory contains electrical wiring diagrams for various machines and systems supported by the control system.
## Available Diagrams
### Extruder
- **Location**: [`extruder/`](extruder/)
- **File**: [QiTech Pro Extruder Electrical Diagram 2025.pdf](extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025.pdf)
### Winder
- **Location**: [`winder/`](winder/)
- **File**: [QiTech Pro Winder Electrical Diagram.pdf](winder/QiTech_Pro_Winder_Electrical_Diagram.pdf)
Binary file not shown.
View File
Whitespace-only changes.
+1 -1
View File
@@ -3,6 +3,6 @@
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"endOfLine": "auto",
"endOfLine": "lf",
"plugins": ["prettier-plugin-tailwindcss"]
}
+1064 -1494
View File
File diff suppressed because it is too large. Load diff
+10 -8
View File
@@ -11,6 +11,7 @@
"make": "electron-forge make",
"publish": "electron-forge publish",
"lint": "eslint .",
"lint:write": "eslint . --fix",
"format": "prettier --check .",
"format:write": "prettier --write .",
"test": "vitest run",
@@ -23,14 +24,14 @@
"devDependencies": {
"@electron-forge/cli": "^7.6.1",
"@electron-forge/maker-deb": "^7.8.0",
"@electron-forge/maker-rpm": "^7.6.1",
"@electron-forge/maker-rpm": "^7.8.3",
"@electron-forge/maker-squirrel": "^7.6.1",
"@electron-forge/maker-zip": "^7.6.1",
"@electron-forge/plugin-auto-unpack-natives": "^7.6.1",
"@electron-forge/plugin-fuses": "^7.8.0",
"@electron-forge/plugin-vite": "^7.6.1",
"@electron-forge/shared-types": "^7.4.0",
"@electron/fuses": "^1.8.0",
"@electron/fuses": "^2.0.0",
"@eslint/compat": "^1.2.6",
"@eslint/js": "^9.18.0",
"@playwright/test": "^1.50.1",
@@ -40,21 +41,21 @@
"@testing-library/user-event": "^14.6.1",
"@types/electron-squirrel-startup": "^1.0.2",
"@types/eslint-config-prettier": "^6.11.3",
"@types/node": "^22.13.4",
"@types/node": "^24.2.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"electron": "36.4.0",
"electron": "^37.2.6",
"electron-playwright-helpers": "^1.7.1",
"eslint": "^9.20.1",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-compiler": "^19.0.0-beta-714736e-20250131",
"globals": "^16.2.0",
"jsdom": "^26.0.0",
"postcss": "^8.5.2",
"prettier": "^3.5.1",
"prettier-plugin-tailwindcss": "^0.6.11",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4.1.4",
"ts-node": "^10.9.2",
"typescript": "^5.7.3",
@@ -78,7 +79,7 @@
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-toggle": "^1.1.1",
"@radix-ui/react-toggle-group": "^1.1.2",
"@tanstack/react-query": "^5.66.7",
"@tanstack/react-query": "^5.84.2",
"@tanstack/react-router": "^1.105.0",
"@tanstack/react-table": "^8.21.2",
"@tanstack/router-devtools": "^1.105.0",
@@ -98,6 +99,7 @@
"react-hook-form": "^7.57.0",
"react-i18next": "^15.4.1",
"react-markdown": "^10.1.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.1",
"socket.io-client": "^4.8.1",
"socket.io-msgpack-parser": "^3.0.2",
File renamed without changes.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+1 -2
View File
@@ -4,7 +4,7 @@
This is the operating manual for the Mock Machine control system. The Mock Machine is a simulated industrial device used for testing and development purposes.
![Mock Machine Duck](duck.jpg)
![Mock Machine Duck](images/manuals/mock/duck.jpg)
_Figure 1: Mock Machine Mascot_
## Safety Information
@@ -20,7 +20,6 @@ _Figure 1: Mock Machine Mascot_
### Starting the Machine
1. **Power On Sequence**
- Check all safety switches
- Verify emergency stops are released
- Power on the main controller
+689 -1
View File
@@ -1 +1,689 @@
# Winder Manual
![Titelseite,screenshot from Word doccument](images/manuals/winder/Titelseite.jpeg)
<br>
# EC Declaration of Conformity
**according to 2006/42/EC, Annex II, No. 1 A**
The content of the EC Declaration of Conformity corresponds to Annex II, No. 1 A, of Directive 2006/42/EC.
<br><br><br>
**Manufacturer of the machine:**
QiTech Industries GmbH
Landwehrsrtaße 55
64293, Darmstadt
<br><br><br>
**Representative of the manufacturer:**
Milan von dem Bussche
Landwehrsrtaße 55
64293, Darmstadt
<br><br><br>
**under consideration:**
| General designation | Filament winding machine |
| ------------------- | ---------------------------------- |
| function | Winding and rewinding of filaments |
| Model | QiTech Winder |
| type | Filament winding machine |
| Trade name | QiTech Winder |
<br><br>
We hereby declare that the above-mentioned machine is in conformity with all relevant provisions of the EC Machinery Directive 2006/42/EC. The machine is also in compliance with all relevant provisions of the following EC Directives:
- 2014/30/EU Electromagnetic Compatibility / European EMC Directive
- 2014/35/EU Low Voltage Directive (protection objectives only)
- 2011/65/EU ROHS Directives (ROHS 2)
The following harmonized standards (or parts of these standards) have been applied:
- DIN EN ISO 12100
Note: References of the EC directives, harmonized standards and standards and specifications can be found in the standards search
<br><br><br>
Darmstadt, 10/06/2025
64392 Darmstadt,
<img src="images/manuals/winder/signature.jpeg" width="300" height="60"/>
Milan von dem Bussche
<br><br><br>
# Operating instructions based on DIN EN ISO 20607
| Designation | QiTech Winder |
| ------------- | --------------------------------- |
| Manufacturer | QiTech Industries GmbH |
| Administrator | Milan von dem Bussche |
| Address | Landwehrstraße 55 64293 Darmstadt |
<br><br>
**Read before commissioning!**
These operating instructions are intended to enable all persons working on the machine to carry out their work safely and correctly.
Every person who operates, maintains or repairs the machine must have read the operating instructions, in particular the safety regulations, before commissioning.
These operating instructions must be available on the machine at all times. The operator must ensure that all information necessary for the safe operation of the machine is made available to the operating personnel.
If the operating instructions are available in electronic form, the operator must ensure that the operating personnel is able to view the operating instructions.
The original version of these operating instructions is written in English.
We therefore ask you to read through the relevant instructions carefully and to observe them thoroughly.
After unpacking the machine, check for any damage that may have occurred during transport.
<br>
ATTENTION: Before using the machine for the first time, please inform yourself about the national regulations (occupational safety, environment).
<br>
Reproduction and reprinting in any form, even in extracts, requires the written authorization of QiTech Industries.
Operating instructions
<br><br><br>
# 1 Machine description
![maschinenbeschreibung](images/manuals/winder/description.png)
| Item number | Item number Description |
| ----------- | ----------------------------------------- |
| 1 | Spool axis |
| 2 | Coil quick-release fastener |
| 3 | Grooved plate |
| 4 | Traverse |
| 5 | Installer |
| 6 | Laser diode |
| 7 | Load arm |
| 8 | Laser measuring system (optional) |
| 9 | Puller rollers |
| 10 | Filament guide |
| 11 | Emergency stop switch |
| 12 | LAN-cable |
| 13 | Connecter cable (if purchased with laser) |
| 14 | Power supply cable |
| 15 | Underframe |
<br><br><br>
# 1.1 Noise emissions
**Sound power level (LWAC)**
| Specification | Value | Unit |
| ------------- | ----- | ---- |
| Idle mode | <70 | dB |
<br><br>
**Workplace-related emission value (LPAC)**
| Specification | Value | Unit |
| ------------- | ----- | ---- |
| Idle mode | <70 | dB |
Noise emission specification according to DIN 45649 Part 1
<br><br>
Noise measurement method and operating condition acc: DIN 45635 Part 16 and Part 1605
Accuracy class: 2
<br>
# 1.2 General, important Safety instructions
1. Only professionally trained persons may operate the machine. The guarantee and warranty will be invalidated if damage is caused by improper operation.
2. We would like to point out that no liability is accepted for damage caused by failure to observe these operating instructions.
3. The operator of the machine must ensure that at least one copy of the operating instructions is kept in the immediate vicinity of the machine and is accessible to persons working with the machine.
4. The operator must ensure that the safety and danger notices on the machine are observed and that the signs are clearly legible.
5. Never work without the prescribed personal protective equipment (e.g. safety footwear).
6. Wear close-fitting clothing and a hairnet if you have long hair. Do not wear loose or baggy clothing (ties, shirt sleeves, jewelry, etc.).
7. Activities with a risk of entrapment may only be carried out without gloves. If gloves are required for individual activities, the machine must first be brought into a safe condition.
8. If the emission sound pressure level at the workplace exceeds 80 dB (A), hearing protection must be worn.
9. Never leave the machine in operation without supervision.
10. Secure your machine so that it cannot be accessed by children. Persons who have not been instructed must not operate the machine.
11. Before using the machine, make sure that it is in perfect condition (carry out a visual inspection before starting work). Pay particular attention to any damage to the earthing contact plug or the electrical connection. Never use the machine with defective, crushed or bare cables.
12. Insert the earthing contact plug into an earthing contact socket suitable for the machine. The supply cable for the machine may only be connected to a socket outlet with earthing contact or a connection box. Have the socket outlet or connection box checked by a qualified electrician beforehand.
13. During maintenance and cleaning work, the machine must be switched off and the earthing contact plug disconnected. Work on the electrical equipment of the machine may only be carried out by qualified electricians. The safety regulations for electrical engineering must be observed:
- Switch off the machine
- Disconnect the LAN-connection
- Secure the machine against being switched on again
- Have the machine de-energized by a qualified electrician
- Earth and short-circuit the machine
- Cover or isolate neighboring live parts
14. Do not reach into moving parts of the machine.
15. Keep a safe distance from rotating parts of the machine. Only change the bobbin when the bobbin axis is stationary.
16. Keep the work area clean. Do not place any objects on the machine.
17. Always switch off the machine when you are not using it.
18. Stay with the machine until it has come to a standstill.
19. Repairs may only be carried out by qualified specialist personnel! Repair work may only be carried out by persons who are qualified for the repair work in question and who are familiar with the relevant occupational safety regulations.
20. Protect the machine from moisture.
21. Check the machine regularly for damage. Only replace damaged parts with original parts and have them replaced by qualified specialists. The guarantee and warranty will be invalidated if accessories and spare parts are used that are not authorized for the machine.
22. To avoid inadequate lighting, we recommend installing light sources in accordance with national health and safety regulations (e.g. workplace directive).
23. All persons working on the machine must be trained based on these operating instructions before starting work. In particular, persons working on the machine must be familiarized with the safety markings described in chapter 1.3.
<br>
| Picture | Danger! |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ![Warning](images/manuals/winder/w001.jpeg) | **Danger to life due to technically faulty machine.** <br> The machine may only be used when it is in perfect technical condition and in accordance with its intended use, in a safety-conscious and hazard-conscious manner and in compliance with these operating instructions. All faults, especially those that could impair safety, must be rectified immediately before starting the winder. Everyone who works on or with the Qitech Winder is responsible for observing these safety aspects! |
<br><br><br>
## 1.3 Description of the pictograms, symbols and warning notices used
| Pictogram | Description of pictogram | Pictogram | Pictogram Description |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ![w001](images/manuals/winder/w001.jpeg) | W001 General warning sign The combination of the symbol W001 and the signal word **"Danger"** indicates an imminently hazardous situation which, if not avoided death or serious injury if it is not avoided. The combination of the symbol W001 and the signal word **"Warning"** indicates an imminently hazardous situation which, if not avoided, will result in death or serious injury. death or serious injury if it is not avoided. The combination of the symbol W001 and the signal word **"Caution"** indicates an imminently hazardous situation which, if not avoided death or serious injury if not avoided. | ![m001](images/manuals/winder/m001.jpeg) | M001 General warning sign Mandatory sign This combination of symbol and signal word indicates a potentially hazardous situation which, if not avoided, may result in damage to property or the environment. not avoided. |
| ![w008](images/manuals/winder/w008.jpeg) | W008 Warning of risk of falling | ![m003](images/manuals/winder/m003.jpeg) | M003 Use hearing protection |
| ![w011](images/manuals/winder/w011.jpeg) | W011 Warning of danger of slipping | ![m004](images/manuals/winder/m004.jpeg) | M004 Use eye protection |
| ![w004](images/manuals/winder/w004.jpeg) | W004 Warning of laser beam | ![m008](images/manuals/winder/m008.jpeg) | M008 Use foot protection |
| ![w012](images/manuals/winder/w012.jpeg) | W012 Warning of electrical voltage | ![m009](images/manuals/winder/m009.jpeg) | M009 Use hand protection |
| ![w017](images/manuals/winder/w017.jpeg) | W017 Warning of hot surface | ![m010](images/manuals/winder/m010.jpeg) | M010 Use protective clothing |
| ![w018](images/manuals/winder/w018.jpeg) | W018 Warning against automatic start-up | ![m012](images/manuals/winder/m012.jpeg) | M012 Use handrail |
| ![w019](images/manuals/winder/w019.jpeg) | W019 Warning of crushing hazard | ![m021](images/manuals/winder/m021.jpeg) | M021 Switch off before maintenance or repair |
| ![w024](images/manuals/winder/w024.jpeg) | W024 Warning against hand injuries | ![m014](images/manuals/winder/m014.jpeg) | M014 Use head protection |
| ![w025](images/manuals/winder/w025.jpeg) | W025 Warning against counter-rotating rollers | | |
| ![w016](images/manuals/winder/w016.jpeg) | W016 Warning of toxic substances | | |
<br><br><br>
# Prohibition signs
| Prohibition signs Pictogram | Pictogram description | International pictograms / other Pictogram | Pictogram Description |
| ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| <img src="images/manuals/winder/p001.jpeg" width="158" height="158"/> | P001 General prohibition sign | <img src="images/manuals/winder/f001.jpeg" width="158" height="158"> | F001 Fire extinguisher |
| <img src="images/manuals/winder/p002.jpeg" width="158" height="158"/> | P002 No smoking | <img src="images/manuals/winder/ANIS_danger.jpeg" width="232" height="60"> | ANSI Z535.3 - DANGER |
| <img src="images/manuals/winder/p003.jpeg" width="158" height="158"/> | P003 No open flames; fire, open source of ignition and smoking prohibited | <img src="images/manuals/winder/ANIS_warning.jpeg" width="232" height="60"> | ANSI Z535.3 - WARNING |
| <img src="images/manuals/winder/p006.jpeg" width="158" height="158"/> | P006 Prohibited for industrial trucks | <img src="images/manuals/winder/ANIS_caution.jpeg" width="232" height="60"> | ANSI Z535.3 - CAUTION |
| <img src="images/manuals/winder/p009.jpeg" width="158" height="158"/> | P009 Climbing prohibited - In the sense of climbing prohibited for unauthorized persons | <img src="images/manuals/winder/ANIS_notice.jpeg" width="232" height="60"> | ANSI Z535.3 - NOTICE |
| <img src="images/manuals/winder/p022.jpeg" width="158" height="158"/> | P022 Eating and drinking prohibited | <img src="images/manuals/winder/ANIS_safetyinstructions.jpeg" width="232" height="60"> | ANSI Z535.3 - SAFETY INSTRUCTIONS |
| <img src="images/manuals/winder/p024.jpeg" width="158" height="158"/> | P024 Entering the area prohibited | <img src="images/manuals/winder/trashcan.jpeg" width="158" height="158"> | Symbol for separate collection of waste electrical and electronic equipment |
| <img src="images/manuals/winder/p031.jpeg" width="158" height="158"/> | P031 Switching prohibited | | |
| <img src="images/manuals/winder/D-p006.jpeg" width="158" height="158"/> | D-P006 Access prohibited for unauthorized persons | | |
<br><br><br>
# 1.4 Intended use
The QiTech Winder is designed for winding and rewinding filaments between 1 mm and 3 mm.
The winder must be placed on a stable, level surface and secured if necessary. The test and work sequence is described in chapter 3.4 Work sequence.
When using the winder, it is essential that the work steps described in the chapter "Work sequence" or in the operating instructions are followed.
In the event of significant changes or deviations, it is necessary to carry out a new risk assessment in accordance with the Machinery Directive and to document the changes in the corresponding documents.
<br>
The machine is designed exclusively for use inside buildings. The machine must not be operated in potentially explosive atmospheres. Use in the area of food production is prohibited.
Use of the machine for purposes other than those specified is prohibited.
Other intended uses include cleaning and maintenance work as well as troubleshooting, fault rectification and repair.
<br><br>
# 1.5 Reasonably foreseeable misuse
Reasonably foreseeable misuse exists in particular when winding unsuitable filaments:
- Filaments that are difficult to bend
- Easily tearable filaments
- Filaments outside the permitted diameter range of 1mm - 3mm
- Filaments with a surface temperature of > 50°C
- Particularly flat, sharp-edged or pointed filaments
In addition, the following procedures must be regarded as misuse:
- Operating the winder on an uneven surface or in a position other than the intended horizontal position or on an unbraked trolley.
- The use of unauthorized coil sizes.
- Use of spare parts and components not authorized by the manufacturer.
- Operating the winder without supervision or in ghost shifts.
<br><br><br>
![spool sizes](images/manuals/winder/spools.jpeg)
<br><br><br>
Furthermore, there is always the possibility that existing/provided safety guards are dismantled or the existing sensors are manipulated or triggered by means other than the intended components.
The misuse mentioned must be explicitly prohibited with reference to these operating instructions and by written operating instructions, and the use of the winder must also be explicitly restricted in the operating instructions to the intended use mentioned above.
Maintenance and repair of the machine may only be carried out by the manufacturer's employees, trained skilled workers or semi-skilled and trained personnel.
<br>
# 1.6 Significant modification / other use
Any use of the machine other than the intended use is prohibited and is considered improper use. Claims of any kind against the manufacturer or its authorized representatives due to damage resulting from improper use of the machine are excluded. The operator alone is liable for all damage caused by improper use.
<br>
# 1.7 Description of the safety devices
**Emergency stop devices**
The emergency stop switch is used to quickly stop the machine in the event of danger. It can be reached from all positions in front of the machine. When an emergency stop device is actuated, the entire machine is stopped by disconnecting the mains supply.
Hazardous movements are brought to a controlled standstill as quickly as possible. The machine can only be restarted by physically resetting the emergency stop switch and then starting one of the operating modes in the software.
<br>
<img src="images/manuals/winder/emergency_stop.jpeg" width="300" height="300"/>
<br>
**Overheating protection**
If one of the motor drivers becomes too hot, it switches off automatically and sends an error message to the control system. This then stops all other components of the machine.
<br>
**Spool stop in the event of filament breakage**
If the filament tears during winding, the spool stops automatically after a short time.
<br>
**Separating protective devices:**
The housing clad with aluminum-dibond plates prevents contact with hot motors, interference with the drive spindle of the traverse and manipulation of the electronics. It also prevents electrostatic charges from jumping from the filament to the electronics.
If guards are opened or removed for maintenance work, they must be closed or refitted before the machine is switched on again. It is not permitted to operate the machine without all properly installed and operational protective devices.
<br><br>
# 1.8 Instruction and Training
As the contractor/operator, you are obliged to inform and instruct the operating personnel about existing legal and accident prevention regulations as well as about existing safety equipment on the winder. This obligation also extends to safety equipment installed around the winder. The various technical qualifications of the employees must be taken into account.
The operating personnel must have understood the instruction and it must be ensured that the instruction is observed. This is the only way to ensure that your personnel work in a safety and hazard-conscious manner. Compliance with the contents of the instruction should be checked regularly.
As the owner/operator, you must therefore obtain written confirmation from each employee that they have attended an instruction session.
<br><br>
# 1.9 Noise emissions from the machine during activities
The noise emissions of the machine were measured on the same machine types. Under normal operating conditions, the maximum noise emission of the extruder is below the maximum permitted values.
Depending on the activity or the duration of the activity, the operator must determine whether suitable hearing protection is required as part of a risk assessment.
<br><br><br>
# 2 Life phases of the machine
<br>
# 2.1 Transport and Assembly
**Requirements for the installation site**
- Installation only in industrial and commercial indoor areas.
- The surface must be level and solid.
- The location must be as fire-free as possible and equipped with a fire extinguisher.
<br><br>
| Picture | Danger |
|---------|--------|
| <img src="images/manuals/winder/w001.jpeg" width="150" height="150"/> | **Risk of injury due to improper transport** <br> Be careful when loading the machine, do not place any limbs under the parts when setting them down, do not stand under suspended loads or between moving loads and walls or machine parts. Wear personal protective equipment: safety shoes, protective work clothing, protective gloves. |
| <img src="images/manuals/winder/w001.jpeg" width="150" height="150"/> | **Danger to life due to incorrect installation and commissioning!** <br> Errors during installation can lead to life-threatening situations or cause considerable damage to property. Installation and commissioning may only be carried out by the manufacturer's employees. |
<br><br>
# 2.2 Assembly, installation and Commissioning
**Basic installation**
The Winder is supplied with a mobile container, the parking brakes must be applied.
The cables must be routed in such a way that they cannot be tripped over.
<br>
**Updating the QiTech Control Software**
The machines are delivered with the newest version of the software installed. Updates, as well as previous updates and the software version can be viewed under Setup-> Updates. If a new version of the software is available, it will be displayed here. To perform the update an internet connection is required. In the next step, click the red "Select" button next to the chosen update. The display will now shut down, and when it is turned back on, the latest version of the software will be installed.
<br>
![screenshot of update tab](images/manuals/winder/screenshot_updates.png)
<br>
**Safety Measures**
Some important safety measures must be observed to ensure safe operation of the QiTech Winder:
- It is important to only use original fuses with the prescribed current ratings to avoid overloading.
- If inspection, maintenance and repair work is carried out on electrical components, these must be de-energized beforehand. Equipment that has been disconnected must be secured against unintentional or automatic reconnection. First check that there is no voltage and insulate neighboring live components.
- Care must be taken to ensure that design features are not modified in such a way as to reduce safety.
- If it is necessary to work on live components, an additional person should be called in to operate the emergency stop button or the main switch in an emergency.
- Only voltage-insulated tools and appropriate clothing may be used.
- Proper earthing of the electrical system must be ensured by protective conductor systems.
- Cables must be checked regularly for damage and replaced if necessary.
<br>
# 2.3 Operation and control (work sequence)
A visual inspection must be carried out every working day before the machine is started.
Then start the machine as follows:
**1. Switch on the machine by plugging in the 220V power supply**
- Make sure that the emergency Stop button is inactive
- Turn on the display by pressing the button on the right side of the display above the USB connectors
- Winder then appears in the left column
**2. Mount the spool on the spool axle**
- Remove the quick-release fastener
- Move the driver in the groove to the height of the hole in the spool flank
- Slide the spool over the axle and the driver
- Tighten the quick-release fastener again
**3. Calibrating the spool sides**
- Select "Control" in the function bar of the software
- And go to the “Hold” mode
- Move the "Outer Limit" slider to the approximate position of the outside of the bobbin
- Click on "save"
- Using the laser dot, repeat the previous steps and fine-tune until the dot is exactly on the edge of the inner bobbin flank
- Repeat the same process for the other outer spool flank
<br>
![screenshot outer limit](images/manuals/winder/screenshot_traverse.png)
<br>
**4. Start pulling**
- Select “Control” in the function bar
- Press the "Pull" button
- Thread the filament through the puller rollers (be careful, counter-rotating rollers!)
- For extrusion: Use the "Puller, Target Speed" slider to manually adjust the speed until the filament has the desired diameter
<br>
![screernshot target speed](images/manuals/winder/screenshot_targetspeed.png)
<br>
**5. Start winding**
- Rotate the load arm upwards and place it on the puller
- Press the "Wind" button
- Thread the filament coming out of the puller through the feeder into the spool and fix it there
- Place the load arm on the filament strand from above
<br>
**6. Change the spool**
- Press the "Pull" button
- Remove the spool as described above and insert a new spool
- Continue winding by clicking on "Wind" and repeat the winding process as mentioned above
<br>
**7. Stopping the winding process**
- Press the "Hold" button - motors energized but not in motion
- Select "Standby" mode - no current on the motors
- If the filament gets caught, the spool does not spin or there is a danger to persons, press the emergency stop button
<br>
**8. Switch off the machine**
- switch off the machine by disconnecting the power supply cable
<br>
<br>
**Unwinding filament**
- Mount the filament roll to be unwound onto the spool axle
- Select “Config” in the function bar
- Press the “Reverse” button to change the mode
- Go back to “Control” in the function bar and adjust the “Puller Target Speed” as normal
- Feed the filament through the puller rollers in the opposite direction to winding (caution, counter-rotating rollers!)
<br>
**Changing process variables during operation**
All settings such as the position of the spool sides, the winding speed or the filament diameter can also be adjusted during the winding process without interrupting it. To do this, change the value in the relevant field and click the "Wind" button again to apply it.
<br>
**Rewinding filament**
- Mount the full spool in front of the Puller wheels, so that it can spin freely
- Mount the empty bobbin on the bobbin axle
- Calibrate the Traverse the same way as mentioned above
- Guide the filament from the full spool over the feeder and fix it to the spool
- Click on “Control" in the function bar
- Click on “Wind”
- Adjust the pulling speed using the slider.
<br>
| Picture | Danger! |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <img src="images/manuals/winder/w001.jpeg" width="150" height="150"/> | **Danger to life due to operation of the machine in faulty condition** <br> If safety-relevant changes or defects occur on the machine, shut down immediately, secure against restarting and report the process to the responsible office/person! |
<br><br>
## 2.4 Maintenance, inspection, Testing
Maintenance and servicing work on the machine may only be carried out by reliable, trained personnel. The minimum age required by law in the country of use must be observed!
Personnel to be trained, instructed or undergoing general training may only work on the Winder under the constant supervision of an experienced person!
<br>
**Updating the Winder software**
There are regular updates for the "QiTech Control" software, which may contain bug fixes as well as new functions or security updates. It is therefore recommended that you regularly check if new Updates are available and install them as described in section 2.2.
<br>
**Lubricating the traverse spindle**
The threaded spindle must be re-oiled after approx. 20 operating hours, but at the latest when the traverser no longer moves to the desired position because the motor is spinning.
To do this, loosen the plate on the top of the housing with an M5 Allen key and drizzle commercially available lubricating oil or grease, e.g. WD40, over the entire length of the threaded spindle.
<br>
**Retightening screw connections**
After approx. 100 operating hours, the rear panel of the winder should be removed using an M5 Allen key so that the screw connections inside the housing can be retightened. The following should be checked:
- Grub screws of the motor couplings (puller, traverse, coil)
- Fastening screws of the motors (puller, traverse, coil)
- Fastening screws of the angle sensor of the load arm
All should be tightened at least hand-tight.
<br>
**Replacing the puller rollers**
The polyurethane coating of the puller rollers will inevitably wear out over time. As soon as you notice the filament slipping through the rollers, they should be replaced. Suitable spare parts are available from the dealer. To replace them, unfasten the screws marked red in the picture.
Now remove the black plastic caps and simply pull out the puller rollers. To install the new rollers back on, slide them on the axes, put the plastic cap back on and screw the screws.
<img src="images/manuals/winder/swapping_out_rollers.jpeg" width="300" height="400"/>
<br>
<br>
| Picture | Caution! |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| <img src="images/manuals/winder/w001.jpeg" width="150" height="150"/> | Observe the instructions for maintenance work when servicing the machine! |
<br>
<br>
## 2.5 Troubleshooting and fault rectification
<br>
**Machine is not recognized by QiTech Control**
- Is the LAN-cable connected?
- Is the latest version of QiTech Control installed?
<br>
**Filament tears off frequently**
- Was the filament sufficiently cooled before winding?
- Is the filament within the permitted diameter range of 1mm - 3mm?
<br>
**Puller rollers are scored**
It is normal for the puller rollers to wear out over time. However, some options can be checked:
- Is the surface temperature &#x3C; 50°C when the filament runs through the rollers?
- Is the filament slipping through the rollers instead of being conveyed by them, causing more abrasion?
- Is the tension of the filament too strong?
<br>
**Filament is not wound perfectly parallel**
- How large is the inner diameter of the spool on which the filament is wound? The closer the winding is to the traverse, the better the winding pattern will be.
- Have the spool sides been calibrated as described?
- Has the correct filament diameter been set?
- Have you tried to fine-tune the two settings independently of the procedure described here?
- Does the problem already occur in the first layer, or only afterwards? If the first layer is not perfectly wound (e.g. due to skewed threading), the filament in the subsequent layers always slips into the gaps in the winding below and "perfect winding" can no longer take place.
<br>
**Winder suddenly switches off**
One of the motor drivers has probably overheated. To prevent uncontrolled winding behavior, the controller therefore switches off the entire winder. It is advisable to wait approx. 5 minutes and try winding again. If the error occurs frequently, a lower ambient temperature (optionally housing temperature) must be ensured.
<br>
**Load adjustment does not work correctly at high or low speeds**
The sensor inside the housing has probably twisted and the load adjustment range has therefore shifted upwards or downwards. To rectify this, turn the angle sensor back and tighten its screws as described in "Tightening screw connections".
<br><br>
# 2.6 Disassembly, decommissioning and Disposal
When the machine has reached the end of its planned service life, it is recommended that the manufacturer be contacted for the purpose of taking it back free of charge. If this is not possible, the machine must be taken out of service permanently and then disposed of or scrapped properly.
To do this, first disconnect the entire power supply. All components must then be separated by material class to enable subsequent recycling or differentiated scrapping.
The machine consists largely of aluminum and plastic and can be easily separated by loosening the screw connections. Electrical scrap, plastic, rubber seals, lubricants and other auxiliary materials must be disposed of separately by an authorized specialist company.
| Picture | Disposal Instructions |
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <img src="images/manuals/winder/trashcan.jpeg" width="150" height="150"/> | Appliances with this logo on the packaging or on the appliance must be disposed of separately. These devices must not be disposed of with normal household waste. You are responsible for ensuring that any electrical or electronic waste is disposed of at the appropriate locations, e.g., recycling centers. |
<br><br><br>
# 3 Hazard warning and information on Residual risks
| Picture | Danger! |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <img src="images/manuals/winder/w001.jpeg" width="150" height="150"/> | **Danger of getting caught, pulled in or pulled along by moving parts of the machine** <br> Do not wear loose long hair, loose clothing or jewelry! There is always a risk of getting caught, pulled in or pulled along by moving parts! |
| <img src="images/manuals/winder/w001.jpeg" width="150" height="150"/> | **Operation only permitted with protective devices fitted!** <br> The winder may only be operated with properly fitted and functioning safety guards. The operator is responsible for ensuring that all protective devices are properly fitted. |
| <img src="images/manuals/winder/w001.jpeg" width="150" height="150"/> | **Danger of cutting tensioned filament** <br> The stretched filament must not be touched between the puller and the spool. Danger of cutting! Do not wind any pointed, sharp-edged or particularly thin materials! |
| <img src="images/manuals/winder/w012.jpeg" width="150" height="150"/> | **Electric shock** <br> Before carrying out any repair, set-up or maintenance work, the winder must be de-energised by disconnecting the mains plug! Pull out the mains plug! |
| <img src="images/manuals/winder/w012.jpeg" width="150" height="150"/> | **Electrostatic charges on the puller and spool** <br> Friction of the filament on the puller rollers and on the spool can lead to electrostatic charges. There is a risk of minor electric shocks! Do not touch the affected areas, but keep a sufficient distance! |
| <img src="images/manuals/winder/w018.jpeg" width="150" height="150"/> | **Automatic speed adjustment of puller, reel and traverse** <br> The bobbin automatically adjusts its speed to keep the winding pressure constant. The traverse automatically adjusts its speed to the reel in order to maintain the same travel distance per revolution. |
| <img src="images/manuals/winder/w025.jpeg" width="150" height="150"/> | **Feeding of body parts** <br> Extreme caution is required in the area of the puller and spool axle! There is an acute danger of getting caught or pulled in! Make sure you wear close-fitting clothing! Always keep the emergency stop switch within easy reach so that it can be activated immediately in an emergency. |
| <img src="images/manuals/winder/w024.jpeg" width="150" height="150"/> | **Risk of crushing when threading the filament** <br> Exercise extreme caution when threading the filament through the puller rollers! There is a risk of crushing between the upper and lower pair of rollers. |
| <img src="images/manuals/winder/w024.jpeg" width="150" height="150"/> | **Risk of crushing between the traversing unit and machine housing** <br> Exercise extreme caution when moving the traverser towards the housing! Do not place any limbs in the gap! |
# 4 Glossary and Definitions
**Specialized personnel**
Due to their specialist training, knowledge and experience as well as knowledge of the relevant regulations, skilled personnel are able to carry out the work assigned to them and to recognize and avoid potential hazards independently. It is assumed that specialized personnel have also been instructed (see definition of instructed person).
<br>
**Instructed person**
The instructed person has been instructed by the operator on the tasks assigned to them and the potential hazards associated with improper behavior. The correct execution of the activities is monitored by specialized personnel.
<br>
**Qualified electrician**
A qualified electrician is someone who is able to assess the work assigned to them and recognize potential hazards based on their technical training, knowledge and experience as well as knowledge of the relevant regulations.
<br>
**Operator**
The operator is the person who uses, adjusts and monitors the machine in day-to-day operation. He is characterized by experience in handling the machine as well as in-depth knowledge of its functions and how to proceed in the event of a fault.
<br>
**Employment ban**
The following persons may not work on the machine:
- Persons with skin diseases
- Persons with chronic respiratory problems or a tendency to allergic reactions
- Pregnant women and nursing mothers
- Children under 18 years
<br>
| Picture | Danger! |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <img src="images/manuals/winder/w001.jpeg" width="150" height="150"/> | **Risk of injury if insufficiently qualified!** <br> Improper handling can lead to considerable personal injury and damage to property. Special activities may only be carried out by the persons specified in the relevant sections of these instructions. |
<br>
<br>
<br>
## Life phases of the machine / system
| Life phase of the machine/system | User/qualification |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| A Construction | Trained and instructed specialized personnel of the manufacturer |
| B Transport | Trained and instructed specialist personnel of the manufacturer or forwarding agent/transporter |
| C Installation | Trained and instructed specialist personnel of the manufacturer or service technician of the manufacturer |
| D Automatic operation | Instructed personnel of the operator |
| E Semi-automatic operation | |
| F Setting/adjusting/restarting the system | |
| G Testing | |
| H Eliminating faults in the work process | Trained and instructed specialized personnel of the operator or service technicians of the manufacturer |
| I Observation of production processes | Instructed personnel of the operator |
| J Troubleshooting and fault rectification | |
| K Cleaning, maintenance | |
| L Maintenance | Trained and instructed specialized personnel of the operator or service technicians of the manufacturer |
| M Decommissioning | Trained and instructed specialized personnel of the operator |
+4 -3
View File
@@ -7,16 +7,17 @@ import { useSyncExternalStore } from "react";
import { z } from "zod";
import { toastError, toastZodError } from "@/components/Toast";
import { MachineIdentificationUnique } from "@/machines/types";
import { FPS_30 } from "@/lib/constants";
/**
* Simple buffer-based store updater to limit React re-renders to ~60 FPS
* Writes events to a plain JS object and syncs to store every 16ms
* Simple buffer-based store updater to limit React re-renders to ~30 FPS
* Writes events to a plain JS object and syncs to store every 33ms
*/
export class ThrottledStoreUpdater<S> {
private store: StoreApi<S>;
private buffer: S;
private syncTimer: NodeJS.Timeout | null = null;
private readonly syncDelay = 1000 / 60; // ~60 FPS (16.67ms)
private readonly syncDelay = FPS_30; // ~30 FPS (33.33ms)
constructor(store: StoreApi<S>) {
this.store = store;
+84
View File
@@ -0,0 +1,84 @@
import React, { useId } from "react";
import { Icon, IconName } from "./Icon";
export type FileInputProps = {
children: React.ReactNode;
accept?: string;
icon?: IconName;
onFile?: (files: File) => void;
onFiles?: (files: FileList) => void;
};
export const FileInput = ({
children,
icon,
accept,
onFiles,
onFile,
}: FileInputProps) => {
const id = useId();
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (!files || files.length == 0) {
return;
}
if (onFile && files.length == 1) {
onFile(files[0]);
}
if (onFiles) {
onFiles(files);
}
};
return (
<label
htmlFor={id}
className="bg-primary text-primary-foreground item-center flex w-full cursor-pointer flex-row justify-center gap-2 rounded-md px-6 py-6 select-none"
>
<Icon name={icon} className="size-6" />
{children}
<input
id={id}
type="file"
accept={accept}
onChange={handleFileChange}
multiple={!!onFiles}
className="hidden"
/>
</label>
);
};
export type JsonFileInputProps = {
children: React.ReactNode;
icon?: IconName;
onJson: (json: any) => void;
};
export function JsonFileInput({ children, icon, onJson }: JsonFileInputProps) {
const handleFile = (file: File) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const text = e.target?.result as string;
const json = JSON.parse(text);
onJson(json);
} catch (err) {
console.error("Invalid JSON file:", err);
}
};
reader.readAsText(file);
};
return (
<FileInput accept=".json,application/json" onFile={handleFile} icon={icon}>
{children}
</FileInput>
);
}
@@ -0,0 +1,115 @@
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import React from "react";
import { Icon } from "@/components/Icon";
import { ControlCard } from "@/control/ControlCard";
interface MachineIdentificationUnique {
machine_identification: {
vendor: number;
machine: number;
};
serial: number;
}
interface Machine {
name: string;
machine_identification_unique: MachineIdentificationUnique;
}
interface MachineSelectorProps {
machines: Machine[];
selectedMachine?: Machine | null;
connectedMachineState?: {
machine_identification_unique?: {
machine_identification: {
vendor: number;
machine: number;
};
serial: number;
} | null;
is_available: boolean;
};
setConnectedMachine: (machine: MachineIdentificationUnique) => void;
clearConnectedMachine?: () => void;
title?: string;
}
export const MachineSelector: React.FC<MachineSelectorProps> = ({
machines,
selectedMachine,
connectedMachineState,
setConnectedMachine,
clearConnectedMachine,
title = "Machine Connection",
}) => {
return (
<ControlCard title={title}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button className="flex items-center gap-2 rounded border px-4 py-2 text-left">
<Icon name="lu:Settings" className="text-xl" />
<span>
{selectedMachine?.name ?? "Select a Machine"}{" "}
{selectedMachine?.machine_identification_unique.serial ?? ""}
</span>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuLabel>Available Machines</DropdownMenuLabel>
<DropdownMenuSeparator />
{machines.map((machine) => {
const isSelected =
connectedMachineState?.machine_identification_unique
?.machine_identification.machine ===
machine.machine_identification_unique.machine_identification
.machine;
return (
<DropdownMenuItem
key={machine.machine_identification_unique.serial}
onClick={() =>
setConnectedMachine(machine.machine_identification_unique)
}
className={`flex min-h-[48px] cursor-pointer items-center gap-2 py-2${
isSelected ? "bg-blue-50" : ""
}`}
>
{connectedMachineState?.is_available ? (
<Icon name="lu:Power" className="text-lg text-green-600" />
) : (
<Icon name="lu:Power" className="text-lg text-gray-400" />
)}
<span>
{machine.name} – Serial:{" "}
{machine.machine_identification_unique.serial}
</span>
</DropdownMenuItem>
);
})}
{clearConnectedMachine && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={clearConnectedMachine}
className="flex cursor-pointer items-center gap-2 text-red-600 hover:text-red-600"
>
<Icon name="lu:Settings" className="text-lg" />
<span>Clear Selection</span>
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
</ControlCard>
);
};
+54 -31
View File
@@ -1,6 +1,7 @@
import React from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import { generateHeadingId } from "@/lib/markdown/heading";
type MarkdownProps = {
@@ -71,6 +72,7 @@ export function Markdown({ text }: MarkdownProps) {
<div>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
components={{
// eslint-disable-next-line @typescript-eslint/no-unused-vars
h1: ({ node, children, ...props }) => {
@@ -78,13 +80,17 @@ export function Markdown({ text }: MarkdownProps) {
const id = generateHeadingId(text);
const isFirst = isFirstHeading(text, 1);
return (
<h1
id={id}
className={`${isFirst ? "" : "pt-4"} pb-2 text-2xl`}
{...props}
>
{children}
</h1>
<>
<h1
id={id}
className={`${isFirst ? "" : "pt-4"} pb-2 text-2xl font-bold`}
{...props}
>
{children}
</h1>
<hr className="my-2" />
<br />
</>
);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -93,13 +99,16 @@ export function Markdown({ text }: MarkdownProps) {
const id = generateHeadingId(text);
const isFirst = isFirstHeading(text, 2);
return (
<h2
id={id}
className={`${isFirst ? "" : "pt-4"} pb-2 text-xl`}
{...props}
>
{children}
</h2>
<>
<h2
id={id}
className={`${isFirst ? "" : "pt-4"} pb-2 text-xl font-bold`}
{...props}
>
{children}
</h2>
<hr className="my-2" />
</>
);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -108,13 +117,16 @@ export function Markdown({ text }: MarkdownProps) {
const id = generateHeadingId(text);
const isFirst = isFirstHeading(text, 3);
return (
<h3
id={id}
className={`${isFirst ? "" : "pt-4"} pb-2 text-lg`}
{...props}
>
{children}
</h3>
<>
<h3
id={id}
className={`${isFirst ? "" : "pt-4"} pb-2 text-lg font-bold`}
{...props}
>
{children}
</h3>
<hr className="my-2" />
</>
);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -123,26 +135,35 @@ export function Markdown({ text }: MarkdownProps) {
const id = generateHeadingId(text);
const isFirst = isFirstHeading(text, 4);
return (
<h4
id={id}
className={`${isFirst ? "" : "pt-4"} pb-2 text-lg`}
{...props}
>
{children}
</h4>
<>
<h4
id={id}
className={`${isFirst ? "" : "pt-4"} pb-2 text-lg font-bold`}
{...props}
>
{children}
</h4>
<hr className="my-2" />
</>
);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
ul: ({ node, ...props }) => (
<ul className="markdown-list list-inside list-disc" {...props} />
<ul
className="markdown-list mb-6 ml-6 list-outside list-disc"
{...props}
/>
),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
li: ({ node, ...props }) => (
<li className="markdown-list list-inside list-disc" {...props} />
<li className="markdown-list" {...props} />
),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
ol: ({ node, ...props }) => (
<ol className="markdown-list list-inside list-decimal" {...props} />
<ol
className="markdown-list mb-6 ml-6 list-outside list-decimal"
{...props}
/>
),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
p: ({ node, ...props }) => <p className="text-base" {...props} />,
@@ -219,6 +240,8 @@ export function Markdown({ text }: MarkdownProps) {
),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
hr: ({ node, ...props }) => <hr className="my-4" {...props} />,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
br: ({ node, ...props }) => <br {...props} />,
}}
>
{text}
+3 -2
View File
@@ -1,6 +1,7 @@
import { RefObject } from "react";
import uPlot from "uplot";
import { GraphConfig, HandlerRefs } from "./types";
import { FPS_30 } from "@/lib/constants";
export interface HandlerCallbacks {
updateYAxisScale: (xMin?: number, xMax?: number) => void;
@@ -40,13 +41,13 @@ export function createEventHandlers(
if (callbackTimeouts.zoom) clearTimeout(callbackTimeouts.zoom);
callbackTimeouts.zoom = setTimeout(() => {
callbacks.onZoomChange?.(graphId, range);
}, 16); // ~60fps
}, FPS_30);
},
onViewModeChange: (mode: "default" | "all" | "manual", isLive: boolean) => {
if (callbackTimeouts.viewMode) clearTimeout(callbackTimeouts.viewMode);
callbackTimeouts.viewMode = setTimeout(() => {
callbacks.onViewModeChange?.(graphId, mode, isLive);
}, 16);
}, FPS_30);
},
};
@@ -0,0 +1,90 @@
import { PresetData, PresetSchema } from "@/lib/preset/preset";
import {
Dialog,
DialogContent,
DialogDescription,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import React, { useState } from "react";
import { TouchButton } from "../touch/TouchButton";
import { DialogHeader } from "../ui/dialog";
import { Icon } from "../Icon";
import { Separator } from "../ui/separator";
import { PresetPreviewEntries, PresetPreviewTable } from "./PresetPreviewTable";
import { Input } from "../ui/input";
export type NewPresetDialogProps<T extends PresetSchema> = {
currentState?: PresetData<T>;
previewEntries: PresetPreviewEntries<T>;
onSave: (name: string) => void;
};
export function NewPresetDialog<T extends PresetSchema>({
currentState,
onSave,
previewEntries,
}: NewPresetDialogProps<T>) {
const [open, setOpen] = useState(false);
const [name, setName] = useState("");
const handleSave = () => {
setOpen(false);
setName("");
onSave(name);
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<TouchButton disabled={!currentState} icon="lu:SquarePlus">
Create New Preset
</TouchButton>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle className="flex flex-row items-center gap-2">
<Icon name="lu:SquarePlus" />
Create a New Preset
</DialogTitle>
<DialogDescription>
Save the configuration of this machine to use it later.
</DialogDescription>
</DialogHeader>
<Separator />
<Input
placeholder="New Preset Name"
onChange={(e) => setName(e.target.value)}
className="w-full"
/>
<div className="flex flex-col gap-6 text-sm">
<span>Current Settings:</span>
<PresetPreviewTable entries={previewEntries} data={currentState} />
</div>
<Separator />
<div className="flex flex-row gap-4">
<TouchButton
variant="outline"
icon="lu:X"
className="h-21 flex-1"
onClick={() => setOpen(false)}
>
Abort
</TouchButton>
<TouchButton
className="h-21 flex-1 flex-shrink-0"
onClick={handleSave}
icon="lu:Save"
disabled={!name}
>
Save
</TouchButton>
</div>
</DialogContent>
</Dialog>
);
}
@@ -0,0 +1,125 @@
import React, { useState } from "react";
import { Preset, PresetSchema } from "@/lib/preset/preset";
import { TouchButton } from "@/components/touch/TouchButton";
import { PresetShowDialog } from "./PresetShowDialog";
import { Icon } from "../Icon";
import { PresetPreviewEntries } from "./PresetPreviewTable";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
type PresetCardMenu = {
onOverwrite?: () => void;
onDelete?: () => void;
onExport?: () => void;
};
export function PresetCardMenu({
onOverwrite,
onDelete,
onExport,
}: PresetCardMenu) {
const [open, setOpen] = useState(false);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<TouchButton variant="outline" icon="lu:Menu" />
</PopoverTrigger>
<PopoverContent className="flex flex-col gap-4">
{onOverwrite && (
<TouchButton
className="flex-shrink-0"
variant="outline"
onClick={onOverwrite}
>
<Icon name="lu:HardDriveUpload" />
Overwrite
</TouchButton>
)}
{onExport && (
<TouchButton
className="flex-shrink-0"
variant="outline"
onClick={onExport}
>
<Icon name="lu:Download" />
Export
</TouchButton>
)}
{onDelete && (
<TouchButton
className="flex-shrink-0"
variant="destructive"
onClick={onDelete}
>
Delete
</TouchButton>
)}
</PopoverContent>
</Popover>
);
}
export type PresetCardProps<T extends PresetSchema> = {
preset: Preset<T>;
onApply: (preset: Preset<T>) => void;
onOverwrite: (preset: Preset<T>) => void;
onDelete: (preset: Preset<T>) => void;
onExport?: (preset: Preset<T>) => void;
previewEntries: PresetPreviewEntries<T>;
isReadOnly?: boolean;
hideDate?: boolean;
isActive?: boolean;
};
export function PresetCard<T extends PresetSchema>({
preset,
onApply,
onOverwrite,
onDelete,
onExport,
previewEntries,
isReadOnly,
hideDate,
isActive,
}: PresetCardProps<T>) {
return (
<div className="flex flex-row items-center gap-4 rounded-3xl border border-gray-200 bg-white p-4 shadow">
<div className="min-w-0 flex-1">
<div>
<div className="flex flex-row gap-2 text-lg font-semibold">
{isActive && <Icon name="lu:Check" className="text-green-500" />}
<span title={preset.name} className="w-60 truncate">
{preset.name}
</span>
</div>
{!hideDate && (
<div className="text-sm text-gray-500">
{preset.lastModified?.toLocaleString() || "Unknown date"}
</div>
)}
{isActive && (
<span className="text-green-500">This preset is active</span>
)}
</div>
</div>
<div className="flex gap-2">
<PresetShowDialog
preset={preset}
onApply={onApply}
previewEntries={previewEntries}
hideDate={hideDate}
/>
{isReadOnly ? (
<TouchButton variant="outline" icon="lu:Menu" disabled />
) : (
<PresetCardMenu
onOverwrite={() => onOverwrite(preset)}
onDelete={() => onDelete(preset)}
onExport={() => onExport && onExport(preset)}
/>
)}
</div>
</div>
);
}
@@ -0,0 +1,58 @@
import React, { Fragment } from "react";
import { PresetData, PresetSchema } from "@/lib/preset/preset";
import { renderUnitSymbol, renderUnitSyntax, Unit } from "@/control/units";
export const previewSeparator = undefined;
type PreviewSeparator = typeof previewSeparator;
type PresetPreviewEntry<T extends PresetSchema> = {
name: string;
unit?: Unit;
renderValue: (data: PresetData<T>) => string | undefined;
};
export type PresetPreviewEntries<T extends PresetSchema> = (
| PresetPreviewEntry<T>
| PreviewSeparator
)[];
export type PresetPreviewTableProps<T extends PresetSchema> = {
data: PresetData<T>;
entries: PresetPreviewEntries<T>;
};
export function PresetPreviewTable<T extends PresetSchema>({
data,
entries,
}: PresetPreviewTableProps<T>) {
return (
<div className="grid grid-cols-[60%_10%_30%]">
{entries.map((entry, i) => {
if (entry === previewSeparator) {
return (
<Fragment key={i}>
<div className="h-[1em]" />
<div />
<div />
</Fragment>
);
}
const value = entry.renderValue(data);
return (
<Fragment key={entry.name}>
<div>{entry.name}</div>
<div>=</div>
<div>
{value === undefined
? "N/A"
: renderUnitSyntax(value, entry.unit)}
{entry.unit && " " + renderUnitSymbol(entry.unit)}
</div>
</Fragment>
);
})}
</div>
);
}
@@ -0,0 +1,88 @@
import { Preset, PresetSchema } from "@/lib/preset/preset";
import {
Dialog,
DialogContent,
DialogDescription,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import React, { useState } from "react";
import { TouchButton } from "../touch/TouchButton";
import { DialogHeader } from "../ui/dialog";
import { Icon } from "../Icon";
import { Separator } from "../ui/separator";
import { PresetPreviewEntries, PresetPreviewTable } from "./PresetPreviewTable";
export type PresetShowDialogProps<T extends PresetSchema> = {
preset: Preset<T>;
previewEntries: PresetPreviewEntries<T>;
onApply: (preset: Preset<T>) => void;
hideDate?: boolean;
};
export function PresetShowDialog<T extends PresetSchema>({
preset,
onApply,
previewEntries,
hideDate,
}: PresetShowDialogProps<T>) {
const [open, setOpen] = useState(false);
const handleApply = (preset: Preset<T>) => {
setOpen(false);
onApply(preset);
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<TouchButton variant="outline" icon="lu:Eye" className="w-max">
Show
</TouchButton>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle className="flex flex-row items-center gap-2">
<Icon name="lu:Save" />
<span className="w-100 text-center">{preset.name}</span>
</DialogTitle>
<DialogDescription>
Applying presets carelessly might damage machines.
</DialogDescription>
</DialogHeader>
<Separator />
<div className="flex flex-col gap-6 text-sm">
{!hideDate && (
<div>
Latest modification at{" "}
{preset.lastModified.toLocaleString() || "N/A"}
</div>
)}
<PresetPreviewTable entries={previewEntries} data={preset.data} />
</div>
<Separator />
<div className="flex flex-row gap-4">
<TouchButton
variant="outline"
icon="lu:X"
className="h-21 flex-1"
onClick={() => setOpen(false)}
>
Abort
</TouchButton>
<TouchButton
className="h-21 flex-1 flex-shrink-0"
onClick={() => handleApply(preset)}
icon="lu:HardDriveDownload"
>
Apply to Machine
</TouchButton>
</div>
</DialogContent>
</Dialog>
);
}
@@ -0,0 +1,102 @@
import React from "react";
import { ControlGrid } from "@/control/ControlGrid";
import { Page } from "@/components/Page";
import { usePresets, UsePresetsParams } from "@/lib/preset/usePresets";
import { Preset, PresetSchema } from "@/lib/preset/preset";
import { PresetCard } from "./PresetCard";
import { PresetPreviewEntries } from "./PresetPreviewTable";
import { NewPresetDialog } from "./NewPresetDialog";
import { downloadJson } from "@/lib/download";
import { JsonFileInput } from "../FileInput";
type PresetsPageProps<T extends PresetSchema> = UsePresetsParams<T> & {
applyPreset: (preset: Preset<T>) => void;
previewEntries: PresetPreviewEntries<T>;
};
export function PresetsPage<T extends PresetSchema>({
applyPreset,
machine_identification,
currentState,
schemas,
schemaVersion,
previewEntries,
defaultState,
}: PresetsPageProps<T>) {
const presets = usePresets<T>({
machine_identification,
currentState,
schemas,
schemaVersion,
defaultState,
});
const handleOverwritePreset = (preset: Preset<T>) => {
const msg = `Are you sure you want to overwrite the preset "${preset.name}" with the current settings? This cannot be undone.`;
if (!confirm(msg)) {
return;
}
presets.updateFromCurrentState(preset);
};
const handleDeletePreset = (preset: Preset<T>) => {
const msg = `Are you sure you want to delete the preset "${preset.name}"? This cannot be undone.`;
if (!confirm(msg)) {
return;
}
presets.remove(preset);
};
const handleExport = (preset: Preset<T>) => {
const data = { ...preset, id: undefined };
const filename = `${preset.name}.preset.json`;
downloadJson(data, filename);
};
return (
<Page>
<ControlGrid columns={2}>
<NewPresetDialog
previewEntries={previewEntries}
onSave={presets.createFromCurrentState}
currentState={currentState}
/>
<JsonFileInput onJson={presets.import} icon="lu:Upload">
Import Preset from File
</JsonFileInput>
{presets.get().map((preset) => {
const isLatest = presets.isLatest(preset);
return (
<PresetCard
key={preset.id}
preset={preset}
onOverwrite={handleOverwritePreset}
onApply={applyPreset}
onDelete={handleDeletePreset}
onExport={isLatest ? undefined : handleExport}
previewEntries={previewEntries}
isReadOnly={isLatest}
isActive={presets.isActive(preset)}
/>
);
})}
{presets.defaultPreset !== undefined && (
<PresetCard
preset={presets.defaultPreset}
onOverwrite={handleOverwritePreset}
onApply={applyPreset}
onDelete={handleDeletePreset}
previewEntries={previewEntries}
isReadOnly
hideDate
isActive={presets.isActive(presets.defaultPreset)}
/>
)}
</ControlGrid>
</Page>
);
}
@@ -1,3 +1,4 @@
import React from "react";
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
function Collapsible({
+1
View File
@@ -1,3 +1,4 @@
import React from "react";
import { cn } from "@/lib/utils";
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
+1
View File
@@ -1,3 +1,4 @@
import React from "react";
import { useTheme } from "next-themes";
import { Toaster as Sonner, ToasterProps } from "sonner";
+16 -4
View File
@@ -38,6 +38,7 @@ type Props = {
step?: number;
valueSchema?: z.ZodType<number>;
inverted?: boolean;
confirmation?: string;
renderValue: (value: number) => string;
onChange?: (value: number) => void;
};
@@ -96,6 +97,7 @@ export function EditValue({
maxLabel,
minSlider,
maxSlider,
confirmation,
onChange,
}: Props) {
const defaultOrZero = defaultValue ?? 0;
@@ -252,10 +254,20 @@ export function EditValue({
const handleSubmit = () => {
form.handleSubmit((data) => {
onChange?.(data.value);
setOpen(false);
setNumpadExtended(false);
resetInput();
if (confirmation) {
if (window.confirm(confirmation)) {
onChange?.(data.value);
setOpen(false);
setNumpadExtended(false);
resetInput();
}
// else do nothing if user cancels
} else {
onChange?.(data.value);
setOpen(false);
setNumpadExtended(false);
resetInput();
}
})();
};
+17 -1
View File
@@ -1,4 +1,4 @@
import { ReactNode } from "@tanstack/react-router";
import { ReactNode } from "react";
import { IconName } from "@/components/Icon";
import React from "react";
import { LoadingSpinner } from "@/components/LoadingSpinner";
@@ -27,6 +27,10 @@ export function getUnitIcon(unit: Unit): IconName {
return "lu:AudioWaveform";
case "W":
return "lu:Zap";
case "V":
return "lu:Zap";
case "A":
return "lu:Zap";
default:
return "lu:ChartNoAxesColumn";
}
@@ -56,6 +60,10 @@ export function renderUnitSymbol(unit: Unit | undefined): string {
return "mHz";
case "W":
return "W";
case "V":
return "V";
case "A":
return "A";
default:
return "";
}
@@ -105,6 +113,12 @@ export function renderUnitSymbolLong(unit: Unit): string {
return "meters/minute";
case "mHz":
return "millihertz";
case "W":
return "watts";
case "V":
return "volts";
case "A":
return "amperes";
default:
return "";
}
@@ -122,6 +136,8 @@ export const units = [
"m/min",
"mHz",
"W",
"V",
"A",
] as const;
export type Unit = (typeof units)[number];
+1 -1
View File
@@ -2,7 +2,7 @@ import { useMemo } from "react";
import {
processMarkdownImages,
createImageImports,
} from "@/utils/markdownImages";
} from "@/lib/markdown/images";
/**
* Hook for processing markdown content with image imports
+3
View File
@@ -0,0 +1,3 @@
// 30 FPS is about 33.3333 ms between refreshes
const FPS_30: number = 1000 / 30;
export { FPS_30 };
+12
View File
@@ -0,0 +1,12 @@
export function downloadJson(data: any, filename: string) {
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
+48
View File
@@ -0,0 +1,48 @@
export function deepEquals<T>(a: T, b: T): boolean {
if (a === b) {
return true;
}
if (!a || typeof a !== "object" || !b || typeof b !== "object") {
return a === b;
}
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (!deepEquals(a[i], b[i])) {
return false;
}
}
return true;
}
if (Array.isArray(a) || Array.isArray(b)) {
return false;
}
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
const aRec = a as Record<string, any>;
const bRec = b as Record<string, any>;
if (aKeys.length !== bKeys.length) {
return false;
}
for (const key of aKeys) {
if (!(key in bRec)) {
return false;
}
if (!deepEquals(aRec[key], bRec[key])) {
return false;
}
}
return true;
}
+20
View File
@@ -0,0 +1,20 @@
import { machineIdentification } from "@/machines/types";
import { z } from "zod";
export type PresetSchema = z.ZodTypeAny;
export const presetSchema = <S extends PresetSchema>(dataSchema: S) =>
z.object({
id: z.number().int().nonnegative().optional(),
name: z.string().nonempty(),
lastModified: z.coerce.date(),
machineIdentification,
schemaVersion: z.number().int().positive(),
data: dataSchema,
});
export type Preset<S extends PresetSchema> = z.infer<
ReturnType<typeof presetSchema<S>>
>;
export type PresetData<S extends PresetSchema> = z.infer<S>;
+186
View File
@@ -0,0 +1,186 @@
import { create } from "zustand";
import { persist, PersistStorage, StorageValue } from "zustand/middleware";
import { Preset, PresetSchema, presetSchema } from "./preset";
import { MachineIdentification } from "@/machines/types";
import { z } from "zod";
import { toastError } from "@/components/Toast";
const persistedPresetSchema = presetSchema(z.any()).extend({
id: z.number().int().nonnegative(),
});
export type PersistedPreset = z.infer<typeof persistedPresetSchema>;
const persistedStateSchema = z.object({
presets: z.array(persistedPresetSchema),
latestPresetIds: z.map(z.string(), z.number()),
});
type PersistedState = z.infer<typeof persistedStateSchema>;
const localStoreItemSchema = z.object({
state: z.object({
presets: z.array(z.any()).default([]),
latestPresetIds: z.array(z.tuple([z.string(), z.number()])).default([]),
}),
});
type LocalStoreItem = z.infer<typeof localStoreItemSchema>;
type PresetStoreData = {
presets: PersistedPreset[];
latestPresetIds: Map<string, number>;
};
export type PresetStore = PresetStoreData & {
insert: <T extends PresetSchema>(preset: Preset<T>) => PersistedPreset;
update: <T extends PresetSchema>(preset: Preset<T>) => void;
remove: <T extends PresetSchema>(preset: Preset<T>) => void;
getById: (id: number) => PersistedPreset | undefined;
setLatestPresetId: (
machineIdentification: MachineIdentification,
id: number,
) => void;
getLatestPresetId: (
machineIdentification: MachineIdentification,
) => number | undefined;
};
const storage: PersistStorage<PersistedState> = {
getItem: (name: string): StorageValue<PersistedState> | null => {
const str = localStorage.getItem(name);
if (!str) {
return null;
}
try {
const json = JSON.parse(str);
const { state: item } = localStoreItemSchema.parse(json);
const latestPresetIds = new Map(item.latestPresetIds);
const state: PersistedState = {
presets: item.presets,
latestPresetIds,
};
return { state };
} catch (e) {
console.error(e);
}
return null;
},
setItem: (name: string, newValue: StorageValue<PersistedState>) => {
const latestPresetIds = Array.from(
newValue.state?.latestPresetIds?.entries(),
);
const item: LocalStoreItem = {
state: {
...newValue.state,
latestPresetIds,
},
};
const json = JSON.stringify(item);
localStorage.setItem(name, json);
},
removeItem: (name: string) => localStorage.removeItem(name),
};
export const usePresetStore = create<PresetStore>()(
persist(
(set, get) => ({
presets: [],
latestPresetIds: new Map(),
insert: <T extends PresetSchema>(preset: Preset<T>) => {
const state = get();
const { presets } = state;
const ids = presets.map((p: PersistedPreset) => p.id);
const id = Math.max(0, ...ids) + 1;
const persistedPreset: PersistedPreset = { ...preset, id };
presets.push(persistedPreset);
set({ ...state, presets });
return persistedPreset;
},
update: <T extends PresetSchema>(preset: Preset<T>) => {
const state = get();
const presets: PersistedPreset[] = state.presets.map(
(p: PersistedPreset) =>
p.id === preset.id ? (preset as PersistedPreset) : p,
);
set({ ...state, presets });
},
remove: <T extends PresetSchema>(preset: Preset<T>) => {
const state = get();
const presets = state.presets.filter(
(p: Preset<any>) => p.id !== preset.id,
);
set({ ...state, presets });
},
getById: (id: number) => {
return get().presets.find((preset: Preset<any>) => preset.id === id);
},
setLatestPresetId: (
machineIdentification: MachineIdentification,
id: number,
) => {
const state = get();
const { latestPresetIds } = state;
const key = `${machineIdentification.vendor}:${machineIdentification.machine}`;
latestPresetIds.set(key, id);
set({ ...state, latestPresetIds });
},
getLatestPresetId: (machineIdentification: MachineIdentification) => {
const state = get();
const { latestPresetIds } = state;
const key = `${machineIdentification.vendor}:${machineIdentification.machine}`;
return latestPresetIds.get(key);
},
}),
{
name: "preset-storage",
merge: (persisted: any, store: PresetStore): PresetStore => {
if (!persisted) {
return store;
}
try {
const persistedState = persistedStateSchema.parse(persisted);
return {
...store,
...persistedState,
};
} catch (e) {
console.error(e);
toastError(
`Loading presets failed`,
`Could not load presets from storage.`,
);
}
return store;
},
storage,
},
),
);
Loaded 100 of 189 files, more files were not shown because too many files have changed in this diff. Show more