Compare commits

..
Author SHA1 Message Date
Icedsoda 49b231ac1f add ethercrab as a submodule 2025-08-08 13:23:24 +02:00
216 changed files with 870 additions and 69159 deletions

No files matched your search

-3
View File
@@ -1,3 +0,0 @@
.png filter=lfs diff=lfs merge=lfs -text
.jpg filter=lfs diff=lfs merge=lfs -text
.jpeg filter=lfs diff=lfs merge=lfs -text
-29
View File
@@ -1,29 +0,0 @@
This is a machine control software project.
## Repository structure
### Frontend
`/electron`: Frontend code for the control software built with React and Electron.
### Backend
`/server`: Implements machine setup & loop. Exposes a REST & Socket.io API for the electron frontend.
`/ethercat-hal`: Hardware abstraction layer for Beckhoff (and possibly other EtherCat) devices and protocols.
`/ethercat-hal-derive`: Macros for ethercat-hal
`/control-core`: Generic logic for the server.
### HMI Panel Operating System
`/nixos`: Custom Linux with realtime kernel & preconfigured for touchscreens.
### Other
/docs: Documentation for the project.
## Key Guidelines
1. Follow Rust & Typescript best practices and idiomatic patterns
2. Maintain existing code structure and organization
3. Write unit tests for new functionality where applicable.
4. Document public APIs and complex logic in code. Suggest changes to the `docs/` folder when appropriate
+11 -14
View File
@@ -1,19 +1,16 @@
# 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"
directory: "/electron"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/electron" # Location of package manifests
schedule:
interval: "monthly"
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-minor"
interval: "weekly"
- package-ecosystem: "cargo"
directory: "/"
- package-ecosystem: "cargo" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "monthly"
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-minor"
interval: "weekly"
+1 -4
View File
@@ -36,9 +36,6 @@ jobs:
- name: Install
run: npm i
- name: Build
run: tsc
- name: Format
run: npm run format
@@ -48,5 +45,5 @@ jobs:
- name: Test
run: npm run test
- name: Package
- name: Build
run: npm run package
+3
View File
@@ -0,0 +1,3 @@
[submodule "ethercrab"]
path = ethercrab
url = https://github.com/qitechgmbh/ethercrab.git
-49
View File
@@ -1,49 +0,0 @@
# Canonical identities on the left, aliases on the right.
# Format options:
# Canon Name <canon@example.com> <alias@example.com>
# Canon Name <canon@example.com> Alias Name <alias@example.com>
# Icedsoda -> Philipp
Philipp <29167146+1cedsoda@users.noreply.github.com> <29167146+1cedsoda@users.noreply.github.com>
# kraemr / Robin Krämer -> Robin
Robin <93094049+kraemr@users.noreply.github.com> <93094049+kraemr@users.noreply.github.com>
Robin <93094049+kraemr@users.noreply.github.com> Robin Krämer <93094049+kraemr@users.noreply.github.com>
# Oshgnacknak -> Josh
Josh <osh@oshgnacknak.de> <osh@oshgnacknak.de>
# Milan
Milan <61654753+MilanVDB@users.noreply.github.com>
# dependabot stays as bot
Dependabot <49699333+dependabot[bot]@users.noreply.github.com> <49699333+dependabot[bot]@users.noreply.github.com>
# Medformatik / Florian -> Florian
Florian <dev@fwcr.de> <dev@fwcr.de>
Florian <dev@fwcr.de> Medformatik <dev@fwcr.de>
# Anatomik5 -> Alisher (merge noreply + gmail under Alisher)
Alisher <115714693+Anatomik5@users.noreply.github.com> <115714693+Anatomik5@users.noreply.github.com>
Alisher <115714693+Anatomik5@users.noreply.github.com> Anatomik5 <darmenovalisher@gmail.com>
Alisher <115714693+Anatomik5@users.noreply.github.com> Darmenov Alisher <115714693+Anatomik5@users.noreply.github.com>
# snacj -> Melvin
Melvin <0xsnacj@proton.me> <0xsnacj@proton.me>
Melvin <0xsnacj@proton.me> snacj <bbmelvin@gmx.de>
Melvin <0xsnacj@proton.me> Melvin Spieß <0xsnacj@proton.me>
# Janhegerq -> Jan
Jan <219994445+Janhegerq@users.noreply.github.com> <219994445+Janhegerq@users.noreply.github.com>
Jan <219994445+Janhegerq@users.noreply.github.com> <19994445+Janhegerq@users.noreply.github.com>
# Christian / TheBest6337 -> christian
Christian <christian.zschoegner@outlook.de> <christian.zschoegner@outlook.de>
Christian <christian.zschoegner@outlook.de> TheBest6337 <christian.zschoegner@outlook.de>
Christian <christian.zschoegner@outlook.de> Christian <80114847+TheBest6337@users.noreply.github.com>
# nixos-install.sh
nixos-install.sh <nixos-install.sh@localhost> <nixos-install.sh@localhost>
# copilot-swe-agent bot
Copilot <198982749+Copilot@users.noreply.github.com> <198982749+Copilot@users.noreply.github.com>
+2 -40
View File
@@ -1,43 +1,4 @@
# `2.6.0`
_03.09.2025_
## Extruder V2
- [#658](https://github.com/qitechgmbh/control/pull/658) Added manual
- [#639](https://github.com/qitechgmbh/control/pull/639) Swapped order of screw speed regulation buttons
## Laser V1
- [#642](https://github.com/qitechgmbh/control/pull/642) Improved stability of laser connection
## General
- [#657](https://github.com/qitechgmbh/control/pull/657) Time frame for graphs is now remembered
- [#641](https://github.com/qitechgmbh/control/pull/641) Removed duplicate scrollbar from manuals
# `2.5.0`
_27.08.2025_
## Extruder V2
- [#595](https://github.com/qitechgmbh/control/pull/600) Simplified power graphs
- [#593](https://github.com/qitechgmbh/control/pull/599) Added identifiers for temperature sensors in temperature graph
## Winder V2
- [#598](https://github.com/qitechgmbh/control/pull/601) Images now load properly in the manual
## General
- [#540](https://github.com/qitechgmbh/control/pull/592) Improved readability of the presets
- [#591](https://github.com/qitechgmbh/control/pull/626) Significantly improved user interface performance and stability
# `2.4.0`
_14.08.2025_
## Winder V2
- [#550](https://github.com/qitechgmbh/control/pull/551) Can now reset wound distance while winding
## General
- [#568](https://github.com/qitechgmbh/control/pull/569),[#572](https://github.com/qitechgmbh/control/pull/572), [#574](https://github.com/qitechgmbh/control/pull/587) Improved EtherCAT performance
- [#588](https://github.com/qitechgmbh/control/pull/589) Improved frontend performance
# `2.3.0`
# `Unreleased`
_06.08.2025_
## Extruder V2
@@ -55,6 +16,7 @@ _06.08.2025_
- [#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
+89 -135
View File
@@ -99,9 +99,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.99"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "approx"
@@ -256,7 +256,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -273,7 +273,7 @@ checksum = "99e1aca718ea7b89985790c94aad72d77533063fe00bc497bb79a7c2dae6a661"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -345,7 +345,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -539,7 +539,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -581,7 +581,6 @@ dependencies = [
"approx",
"axum",
"bitvec",
"control_core_derive",
"core_affinity",
"crc",
"erased-serde",
@@ -600,15 +599,6 @@ dependencies = [
"uom",
]
[[package]]
name = "control_core_derive"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
]
[[package]]
name = "core-foundation"
version = "0.10.0"
@@ -738,7 +728,7 @@ dependencies = [
"deluxe-macros",
"once_cell",
"proc-macro2",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -751,7 +741,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -766,7 +756,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -777,7 +767,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -814,7 +804,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -1029,14 +1019,12 @@ dependencies = [
"deluxe",
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
name = "ethercrab"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbb13becfa15713adb09b27fdb74468b827cdc78aa99aea638a89c1b0db3ce94"
dependencies = [
"async-io",
"atomic-waker",
@@ -1065,8 +1053,6 @@ dependencies = [
[[package]]
name = "ethercrab-wire"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d15828cb020fb44c92ca67e1ee3c122c9621c2e00d383d066e488f0619023ba2"
dependencies = [
"ethercrab-wire-derive",
"heapless",
@@ -1075,13 +1061,11 @@ dependencies = [
[[package]]
name = "ethercrab-wire-derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e790b8011211ed70babfcacbd4277b91ee21402ad5ba6a2c293db02e2f12919"
dependencies = [
"criterion",
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -1210,7 +1194,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -1644,9 +1628,9 @@ dependencies = [
[[package]]
name = "io-uring"
version = "0.7.6"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c2f96dfbc20c12b9b4f12eef60472d8c29b9c3f29463570dcb47e4a48551168"
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
@@ -1746,9 +1730,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.175"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libloading"
@@ -1825,11 +1809,11 @@ dependencies = [
[[package]]
name = "matchers"
version = "0.2.0"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata",
"regex-automata 0.1.10",
]
[[package]]
@@ -1953,11 +1937,12 @@ checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
[[package]]
name = "nu-ansi-term"
version = "0.50.1"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"windows-sys 0.52.0",
"overload",
"winapi",
]
[[package]]
@@ -2085,6 +2070,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking"
version = "2.2.1"
@@ -2178,7 +2169,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -2209,7 +2200,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -2345,7 +2336,7 @@ dependencies = [
"itertools",
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -2377,9 +2368,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.9.2"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha",
"rand_core",
@@ -2415,14 +2406,23 @@ dependencies = [
[[package]]
name = "regex"
version = "1.11.2"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
"regex-automata 0.4.9",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
@@ -2433,9 +2433,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.5"
@@ -2570,7 +2576,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -2590,14 +2596,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
name = "serde_json"
version = "1.0.143"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
@@ -2671,9 +2677,9 @@ dependencies = [
[[package]]
name = "serialport"
version = "4.7.3"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b"
checksum = "2daa7abb9b965493e3c8f4184c6f46435484ff2538a332b886788cf6768b927b"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
@@ -2697,7 +2703,6 @@ dependencies = [
"axum",
"bitvec",
"control_core",
"control_core_derive",
"crc",
"dhat",
"ethercat_hal",
@@ -2712,6 +2717,7 @@ dependencies = [
"regex",
"serde",
"serde_json",
"serial",
"serialport",
"smol",
"socketioxide",
@@ -2844,9 +2850,9 @@ dependencies = [
[[package]]
name = "socketioxide"
version = "0.17.2"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "476190583b592f1e3d55584269600f2d3c4f18af36adad03c41c27e82dcb6bd5"
checksum = "85fe8a90fd0cdac0bcb7b3c40c1084f5658f8737c62dfb277f5620cf78f3849a"
dependencies = [
"bytes",
"engineioxide",
@@ -2951,9 +2957,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.105"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
@@ -2977,7 +2983,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -3022,7 +3028,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -3033,16 +3039,16 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
[[package]]
name = "thread-priority"
version = "2.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4ef372b29fbcc6cb0cef97bcbf3ffa9da90e02eb23dbf14db48ce30d11d2d5"
checksum = "cfe075d7053dae61ac5413a34ea7d4913b6e6207844fd726bdd858b37ff72bf5"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
"libc",
"log",
"rustversion",
"windows",
"winapi",
]
[[package]]
@@ -3117,9 +3123,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.45.1"
version = "1.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
dependencies = [
"backtrace",
"bytes",
@@ -3139,7 +3145,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -3301,7 +3307,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -3366,15 +3372,15 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.20"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
dependencies = [
"chrono",
"matchers",
"nu-ansi-term",
"once_cell",
"regex-automata",
"regex",
"sharded-slab",
"smallvec",
"thread_local",
@@ -3558,7 +3564,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
"wasm-bindgen-shared",
]
@@ -3593,7 +3599,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3658,28 +3664,6 @@ 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"
@@ -3693,17 +3677,6 @@ 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"
@@ -3712,7 +3685,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -3723,24 +3696,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
name = "windows-link"
version = "0.1.3"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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",
]
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-registry"
@@ -3843,15 +3806,6 @@ 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"
@@ -4031,7 +3985,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
"synstructure",
]
@@ -4052,7 +4006,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
[[package]]
@@ -4072,7 +4026,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
"synstructure",
]
@@ -4106,5 +4060,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.105",
"syn 2.0.101",
]
+2 -2
View File
@@ -4,7 +4,7 @@ members = [
"ethercat-hal",
"ethercat-hal-derive",
"control-core",
"ethercat-eeprom-dump", "control-core-derive",
"ethercat-eeprom-dump",
]
exclude = []
exclude = ["ethercrab"]
resolver = "2"
-3
View File
@@ -4,9 +4,6 @@
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
+1 -7
View File
@@ -1,7 +1,5 @@
#!/bin/bash
set -e
FEATURE=""
# Check if "mock-machine" was passed as an argument
@@ -9,7 +7,6 @@ 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
@@ -22,10 +19,7 @@ fi
if [ "$1" != "mock-machine" ]; then
echo "Setting capabilities for server executable"
sudo setcap 'cap_net_raw,cap_sys_nice=eip' ./target/debug/server
echo "Setting permissions for serial ports"
sudo chown "root:$USER" /dev/ttyUSB*
fi
# run
./target/debug/server
-12
View File
@@ -1,12 +0,0 @@
[package]
name = "control_core_derive"
version = "0.1.0"
edition = "2024"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }
File diff suppressed because it is too large. Load diff
-31
View File
@@ -1,31 +0,0 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{DeriveInput, Error};
#[proc_macro_derive(BuildEvent)]
pub fn build_event_derive(item: TokenStream) -> TokenStream {
build_event_derive2(item.into()).unwrap().into()
}
fn build_event_derive2(item: TokenStream2) -> Result<TokenStream2, Error> {
// Parse the input tokens into a syntax tree
let ast: DeriveInput = syn::parse2(item)?;
let ident = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
// Generate the BuildEvent impl
let expanded = quote! {
impl #impl_generics control_core::socketio::event::BuildEvent for #ident #ty_generics #where_clause {
#[doc = "Implemented by the BuildEvent derive macro"]
fn build(&self) -> control_core::socketio::event::Event<Self> {
control_core::socketio::event::Event::new(stringify!(#ident), self.clone())
}
}
};
Ok(expanded)
}
+6 -8
View File
@@ -4,16 +4,16 @@ version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.99"
anyhow = "1.0.98"
axum = "0.8.4"
ethercat_hal = { path = "../ethercat-hal" }
ethercrab = "0.6"
ethercrab = { path = "../ethercrab" }
interfaces = "0.0.9"
serde = "1.0.219"
serde_json = "1.0.143"
serialport = "4.7.3"
serde_json = "1.0.140"
serialport = "4.7.1"
smol = "2.0.2"
socketioxide = "0.17.2"
socketioxide = "0.17.1"
uom = "0.36.0"
bitvec = { version = "1.0.1", features = ["alloc"] }
crc = "3.3.0"
@@ -21,11 +21,9 @@ serial = "0.4.0"
tracing = "0.1.41"
erased-serde = "0.4.6"
core_affinity = "0.8.3"
control_core_derive = { version = "0.1.0", path = "../control-core-derive" }
[target.'cfg(unix)'.dependencies]
libc = "0.2.175"
libc = "0.2.172"
[dev-dependencies]
approx = "0.5.1"
@@ -1,279 +0,0 @@
//! # Circular Converter
//!
//! This module provides the `CircularConverter` which handles transformations between
//! linear and angular measurements for circular motion systems.
//!
//! ## Usage
//!
//! The `CircularConverter` is designed to work alongside other converters:
//! - Use `AngularStepConverter` for angular-to-step conversions
//! - Use `LinearStepConverter` for complete linear-to-step conversions (uses `CircularConverter` internally)
//! - Use `CircularConverter` directly when you need linear-to-angular conversions without steps
//!
//! ## Examples
//!
//! ```rust
//! use control_core::converters::circular_converter::CircularConverter;
//! use uom::si::{f64::{Length, Velocity}, length::meter, velocity::meter_per_second};
//!
//! let converter = CircularConverter::from_radius(Length::new::<meter>(0.1));
//! let velocity = Velocity::new::<meter_per_second>(1.0);
//! let angular_velocity = converter.linear_to_angular_velocity(velocity);
//! ```
use uom::si::{
acceleration::meter_per_second_squared,
angle::radian,
angular_acceleration::radian_per_second_squared,
angular_velocity::radian_per_second,
f64::{Acceleration, Angle, AngularAcceleration, AngularVelocity, Length, Velocity},
length::meter,
velocity::meter_per_second,
};
/// A converter that handles transformations between linear and angular measurements
/// for circular motion. This includes conversions for position, velocity, acceleration,
/// and higher-order derivatives based on a circle's geometric properties.
#[derive(Debug, Clone)]
pub struct CircularConverter {
radius: Length,
circumference: Length,
diameter: Length,
}
impl CircularConverter {
/// Create a new converter from radius
pub fn from_radius(radius: Length) -> Self {
let diameter = radius * 2.0;
let circumference =
Length::new::<meter>(2.0 * std::f64::consts::PI * radius.get::<meter>());
Self {
radius,
diameter,
circumference,
}
}
/// Create a new converter from diameter
pub fn from_diameter(diameter: Length) -> Self {
let radius = diameter / 2.0;
Self::from_radius(radius)
}
/// Create a new converter from circumference
pub fn from_circumference(circumference: Length) -> Self {
let radius =
Length::new::<meter>(circumference.get::<meter>() / (2.0 * std::f64::consts::PI));
Self::from_radius(radius)
}
/// Get the radius
pub fn radius(&self) -> Length {
self.radius
}
/// Get the diameter
pub fn diameter(&self) -> Length {
self.diameter
}
/// Get the circumference
pub fn circumference(&self) -> Length {
self.circumference
}
// Linear to Angular Position Conversions
/// Convert linear distance to angular position
/// Formula: angle = distance / radius
pub fn linear_to_angular_position(&self, distance: Length) -> Angle {
let angle_rad = distance.get::<meter>() / self.radius.get::<meter>();
Angle::new::<radian>(angle_rad)
}
/// Convert angular position to linear distance
/// Formula: distance = angle * radius
pub fn angular_to_linear_position(&self, angle: Angle) -> Length {
let distance = angle.get::<radian>() * self.radius.get::<meter>();
Length::new::<meter>(distance)
}
// Linear to Angular Velocity Conversions
/// Convert linear velocity to angular velocity
/// Formula: angular_velocity = linear_velocity / radius
pub fn linear_to_angular_velocity(&self, velocity: Velocity) -> AngularVelocity {
let angular_velocity_rad_per_s =
velocity.get::<meter_per_second>() / self.radius.get::<meter>();
AngularVelocity::new::<radian_per_second>(angular_velocity_rad_per_s)
}
/// Convert angular velocity to linear velocity
/// Formula: linear_velocity = angular_velocity * radius
pub fn angular_to_linear_velocity(&self, angular_velocity: AngularVelocity) -> Velocity {
let velocity = angular_velocity.get::<radian_per_second>() * self.radius.get::<meter>();
Velocity::new::<meter_per_second>(velocity)
}
// Linear to Angular Acceleration Conversions
/// Convert linear acceleration to angular acceleration
/// Formula: angular_acceleration = linear_acceleration / radius
pub fn linear_to_angular_acceleration(
&self,
acceleration: Acceleration,
) -> AngularAcceleration {
let angular_acceleration_rad_per_s2 =
acceleration.get::<meter_per_second_squared>() / self.radius.get::<meter>();
AngularAcceleration::new::<radian_per_second_squared>(angular_acceleration_rad_per_s2)
}
/// Convert angular acceleration to linear acceleration
/// Formula: linear_acceleration = angular_acceleration * radius
pub fn angular_to_linear_acceleration(
&self,
angular_acceleration: AngularAcceleration,
) -> Acceleration {
let acceleration =
angular_acceleration.get::<radian_per_second_squared>() * self.radius.get::<meter>();
Acceleration::new::<meter_per_second_squared>(acceleration)
}
// Jerk Conversions (using f64 for now since UOM may not have jerk units)
/// Convert linear jerk to angular jerk
/// Formula: angular_jerk = linear_jerk / radius
/// Units: rad/s³ = (m/s³) / m
pub fn linear_to_angular_jerk(&self, linear_jerk: f64) -> f64 {
linear_jerk / self.radius.get::<meter>()
}
/// Convert angular jerk to linear jerk
/// Formula: linear_jerk = angular_jerk * radius
/// Units: m/s³ = (rad/s³) * m
pub fn angular_to_linear_jerk(&self, angular_jerk: f64) -> f64 {
angular_jerk * self.radius.get::<meter>()
}
// Arc Length Calculations
/// Calculate arc length from angle
/// Formula: arc_length = angle * radius
pub fn angle_to_arc_length(&self, angle: Angle) -> Length {
self.angular_to_linear_position(angle)
}
/// Calculate angle from arc length
/// Formula: angle = arc_length / radius
pub fn arc_length_to_angle(&self, arc_length: Length) -> Angle {
self.linear_to_angular_position(arc_length)
}
// Circumference-based calculations
/// Convert linear distance to number of revolutions
/// Formula: revolutions = distance / circumference
pub fn linear_distance_to_revolutions(&self, distance: Length) -> f64 {
distance.get::<meter>() / self.circumference.get::<meter>()
}
/// Convert number of revolutions to linear distance
/// Formula: distance = revolutions * circumference
pub fn revolutions_to_linear_distance(&self, revolutions: f64) -> Length {
Length::new::<meter>(revolutions * self.circumference.get::<meter>())
}
/// Convert linear velocity to revolutions per second
/// Formula: rps = velocity / circumference
pub fn linear_velocity_to_rps(&self, velocity: Velocity) -> f64 {
velocity.get::<meter_per_second>() / self.circumference.get::<meter>()
}
/// Convert revolutions per second to linear velocity
/// Formula: velocity = rps * circumference
pub fn rps_to_linear_velocity(&self, rps: f64) -> Velocity {
Velocity::new::<meter_per_second>(rps * self.circumference.get::<meter>())
}
}
#[cfg(test)]
mod tests {
use super::*;
use uom::si::length::centimeter;
#[test]
fn test_constructors() {
let radius = Length::new::<centimeter>(5.0);
let diameter = Length::new::<centimeter>(10.0);
let circumference = Length::new::<centimeter>(2.0 * std::f64::consts::PI * 5.0);
let converter1 = CircularConverter::from_radius(radius);
let converter2 = CircularConverter::from_diameter(diameter);
let converter3 = CircularConverter::from_circumference(circumference);
// All should have approximately the same values
assert!((converter1.radius().get::<centimeter>() - 5.0).abs() < 1e-10);
assert!((converter2.radius().get::<centimeter>() - 5.0).abs() < 1e-10);
assert!((converter3.radius().get::<centimeter>() - 5.0).abs() < 1e-10);
assert!((converter1.diameter().get::<centimeter>() - 10.0).abs() < 1e-10);
assert!((converter2.diameter().get::<centimeter>() - 10.0).abs() < 1e-10);
assert!((converter3.diameter().get::<centimeter>() - 10.0).abs() < 1e-10);
}
#[test]
fn test_position_conversions() {
let converter = CircularConverter::from_radius(Length::new::<meter>(1.0));
// Test linear to angular
let distance = Length::new::<meter>(std::f64::consts::PI);
let angle = converter.linear_to_angular_position(distance);
assert!((angle.get::<radian>() - std::f64::consts::PI).abs() < 1e-10);
// Test angular to linear
let angle = Angle::new::<radian>(std::f64::consts::PI / 2.0);
let distance = converter.angular_to_linear_position(angle);
assert!((distance.get::<meter>() - std::f64::consts::PI / 2.0).abs() < 1e-10);
}
#[test]
fn test_velocity_conversions() {
let converter = CircularConverter::from_radius(Length::new::<meter>(0.5));
// Test linear to angular
let velocity = Velocity::new::<meter_per_second>(1.0);
let angular_velocity = converter.linear_to_angular_velocity(velocity);
assert!((angular_velocity.get::<radian_per_second>() - 2.0).abs() < 1e-10);
// Test angular to linear
let angular_velocity = AngularVelocity::new::<radian_per_second>(4.0);
let velocity = converter.angular_to_linear_velocity(angular_velocity);
assert!((velocity.get::<meter_per_second>() - 2.0).abs() < 1e-10);
}
#[test]
fn test_revolution_conversions() {
let converter = CircularConverter::from_radius(Length::new::<meter>(1.0));
// One full revolution should equal circumference
let revolutions = 1.0;
let distance = converter.revolutions_to_linear_distance(revolutions);
assert!((distance.get::<meter>() - converter.circumference().get::<meter>()).abs() < 1e-10);
// Converting back should give 1 revolution
let calculated_revolutions = converter.linear_distance_to_revolutions(distance);
assert!((calculated_revolutions - 1.0).abs() < 1e-10);
}
#[test]
fn test_jerk_conversions() {
let converter = CircularConverter::from_radius(Length::new::<meter>(2.0));
let linear_jerk = 4.0; // m/s³
let angular_jerk = converter.linear_to_angular_jerk(linear_jerk);
assert!((angular_jerk - 2.0).abs() < 1e-10); // 4.0 / 2.0 = 2.0 rad/s³
let calculated_linear_jerk = converter.angular_to_linear_jerk(angular_jerk);
assert!((calculated_linear_jerk - linear_jerk).abs() < 1e-10);
}
}
@@ -1,11 +1,14 @@
use uom::si::{
acceleration::meter_per_second_squared,
angle::revolution,
angular_acceleration::radian_per_second_squared,
angular_velocity::revolution_per_second,
f64::{Acceleration, Angle, AngularAcceleration, AngularVelocity, Length, Velocity},
length::meter,
velocity::meter_per_second,
};
use super::angular_step_converter::AngularStepConverter;
use super::circular_converter::CircularConverter;
/// Converts between linear measurements and motor steps
///
@@ -15,48 +18,53 @@ use super::circular_converter::CircularConverter;
#[derive(Debug, Clone)]
pub struct LinearStepConverter {
angular_step_converter: AngularStepConverter,
circular_converter: CircularConverter,
radius: Length,
circumference: Length,
}
// Constructor and basic getters
impl LinearStepConverter {
/// Create a new converter from radius and steps per revolution
pub fn from_radius(steps_per_revolution: i16, radius: Length) -> Self {
let circumference =
Length::new::<meter>(2.0 * std::f64::consts::PI * radius.get::<meter>());
Self {
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
circular_converter: CircularConverter::from_radius(radius),
radius,
circumference,
}
}
/// Create a new converter from diameter and steps per revolution
pub fn from_diameter(steps_per_revolution: i16, diameter: Length) -> Self {
Self {
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
circular_converter: CircularConverter::from_diameter(diameter),
}
let radius = diameter / 2.0;
Self::from_radius(steps_per_revolution, radius)
}
/// Create a new converter from circumference and steps per revolution
pub fn from_circumference(steps_per_revolution: i16, circumference: Length) -> Self {
let radius =
Length::new::<meter>(circumference.get::<meter>() / (2.0 * std::f64::consts::PI));
Self {
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
circular_converter: CircularConverter::from_circumference(circumference),
radius,
circumference,
}
}
/// Get the radius used by the converter
pub fn radius(&self) -> Length {
self.circular_converter.radius()
self.radius
}
/// Get the diameter of the system
pub fn diameter(&self) -> Length {
self.circular_converter.diameter()
self.radius * 2.0
}
/// Get the circumference of the system
pub fn circumference(&self) -> Length {
self.circular_converter.circumference()
self.circumference
}
/// Get the steps per revolution
@@ -71,10 +79,8 @@ impl LinearStepConverter {
///
/// Formula: steps = (distance / circumference) * steps_per_revolution
pub fn distance_to_steps(&self, distance: Length) -> f64 {
// Convert distance to revolutions using CircularConverter
let revolutions = self
.circular_converter
.linear_distance_to_revolutions(distance);
// Convert distance to angle: angle = distance/circumference (in revolutions)
let revolutions = distance.get::<meter>() / self.circumference.get::<meter>();
let angle = Angle::new::<revolution>(revolutions);
// Convert angle to steps
@@ -88,19 +94,21 @@ impl LinearStepConverter {
// Convert steps to angle
let angle = self.angular_step_converter.steps_to_angle(steps);
// Convert angle to distance using CircularConverter
// Convert angle to distance: distance = angle * circumference
let revolutions = angle.get::<revolution>();
self.circular_converter
.revolutions_to_linear_distance(revolutions)
let distance = revolutions * self.circumference.get::<meter>();
Length::new::<meter>(distance)
}
/// Convert linear velocity to steps/second
///
/// Formula: steps/second = (velocity / circumference) * steps_per_revolution
pub fn velocity_to_steps(&self, velocity: Velocity) -> f64 {
// Convert linear velocity to revolutions per second using CircularConverter
let rps = self.circular_converter.linear_velocity_to_rps(velocity);
let angular_velocity = AngularVelocity::new::<revolution_per_second>(rps);
// Convert linear velocity to angular velocity: ω = v/circumference (in rev/s)
let linear_velocity = velocity.get::<meter_per_second>();
let angular_velocity_rps = linear_velocity / self.circumference.get::<meter>();
let angular_velocity = AngularVelocity::new::<revolution_per_second>(angular_velocity_rps);
// Convert angular velocity to steps/second
self.angular_step_converter
@@ -116,19 +124,23 @@ impl LinearStepConverter {
.angular_step_converter
.steps_to_angular_velocity(steps_per_second);
// Convert angular velocity to linear velocity using CircularConverter
let rps = angular_velocity.get::<revolution_per_second>();
self.circular_converter.rps_to_linear_velocity(rps)
// Convert angular velocity to linear velocity: v = ω * circumference
let angular_velocity_rps = angular_velocity.get::<revolution_per_second>();
let linear_velocity = angular_velocity_rps * self.circumference.get::<meter>();
Velocity::new::<meter_per_second>(linear_velocity)
}
/// Convert linear acceleration to steps/second²
///
/// Formula: steps/second² = (acceleration / radius) * (steps_per_revolution / (2π))
pub fn acceleration_to_steps(&self, acceleration: Acceleration) -> f64 {
// Convert linear acceleration to angular acceleration using CircularConverter
let angular_acceleration = self
.circular_converter
.linear_to_angular_acceleration(acceleration);
// Convert linear acceleration to angular acceleration: α = a/r
let linear_acceleration = acceleration.get::<meter_per_second_squared>();
// Calculate angular acceleration in radians per second squared
let angular_acceleration_rad_per_s2 = linear_acceleration / self.radius.get::<meter>();
let angular_acceleration =
AngularAcceleration::new::<radian_per_second_squared>(angular_acceleration_rad_per_s2);
// Convert angular acceleration to steps/second²
self.angular_step_converter
@@ -144,9 +156,12 @@ impl LinearStepConverter {
.angular_step_converter
.steps_to_angular_acceleration(steps_per_second_squared);
// Convert angular acceleration to linear acceleration using CircularConverter
self.circular_converter
.angular_to_linear_acceleration(angular_acceleration)
// Convert angular acceleration to linear acceleration: a = α * r
let angular_acceleration_rad_per_s2 =
angular_acceleration.get::<radian_per_second_squared>();
let linear_acceleration = angular_acceleration_rad_per_s2 * self.radius.get::<meter>();
Acceleration::new::<meter_per_second_squared>(linear_acceleration)
}
}
@@ -156,9 +171,7 @@ impl LinearStepConverter {
///
/// Formula: angle (in revolutions) = distance / circumference
pub fn distance_to_angle(&self, distance: Length) -> Angle {
let revolutions = self
.circular_converter
.linear_distance_to_revolutions(distance);
let revolutions = distance.get::<meter>() / self.circumference.get::<meter>();
Angle::new::<revolution>(revolutions)
}
@@ -167,24 +180,26 @@ impl LinearStepConverter {
/// Formula: distance = angle (in revolutions) * circumference
pub fn angle_to_distance(&self, angle: Angle) -> Length {
let revolutions = angle.get::<revolution>();
self.circular_converter
.revolutions_to_linear_distance(revolutions)
let distance = revolutions * self.circumference.get::<meter>();
Length::new::<meter>(distance)
}
/// Convert linear velocity to angular velocity
///
/// Formula: angular velocity (in rev/s) = velocity / circumference
pub fn velocity_to_angular_velocity(&self, velocity: Velocity) -> AngularVelocity {
let rps = self.circular_converter.linear_velocity_to_rps(velocity);
AngularVelocity::new::<revolution_per_second>(rps)
let linear_velocity = velocity.get::<meter_per_second>();
let angular_velocity_rps = linear_velocity / self.circumference.get::<meter>();
AngularVelocity::new::<revolution_per_second>(angular_velocity_rps)
}
/// Convert angular velocity to linear velocity
///
/// Formula: velocity = angular velocity (in rev/s) * circumference
pub fn angular_velocity_to_velocity(&self, angular_velocity: AngularVelocity) -> Velocity {
let rps = angular_velocity.get::<revolution_per_second>();
self.circular_converter.rps_to_linear_velocity(rps)
let angular_velocity_rps = angular_velocity.get::<revolution_per_second>();
let linear_velocity = angular_velocity_rps * self.circumference.get::<meter>();
Velocity::new::<meter_per_second>(linear_velocity)
}
/// Convert linear acceleration to angular acceleration
@@ -194,8 +209,9 @@ impl LinearStepConverter {
&self,
acceleration: Acceleration,
) -> AngularAcceleration {
self.circular_converter
.linear_to_angular_acceleration(acceleration)
let linear_acceleration = acceleration.get::<meter_per_second_squared>();
let angular_acceleration_rad_per_s2 = linear_acceleration / self.radius.get::<meter>();
AngularAcceleration::new::<radian_per_second_squared>(angular_acceleration_rad_per_s2)
}
/// Convert angular acceleration to linear acceleration
@@ -205,8 +221,10 @@ impl LinearStepConverter {
&self,
angular_acceleration: AngularAcceleration,
) -> Acceleration {
self.circular_converter
.angular_to_linear_acceleration(angular_acceleration)
let angular_acceleration_rad_per_s2 =
angular_acceleration.get::<radian_per_second_squared>();
let linear_acceleration = angular_acceleration_rad_per_s2 * self.radius.get::<meter>();
Acceleration::new::<meter_per_second_squared>(linear_acceleration)
}
}
@@ -251,10 +269,6 @@ mod tests {
use super::*;
use approx::assert_relative_eq;
use std::f64::EPSILON;
use uom::si::{
acceleration::meter_per_second_squared, angular_acceleration::radian_per_second_squared,
length::meter, velocity::meter_per_second,
};
#[test]
fn test_new() {
+1 -1
View File
@@ -1,5 +1,5 @@
pub mod angle_converter;
pub mod angular_step_converter;
pub mod circular_converter;
pub mod linear_step_converter;
pub mod motor_converter;
pub mod transmission_converter;
@@ -0,0 +1,22 @@
use uom::si::f64::AngularVelocity;
#[derive(Debug, Clone)]
pub struct TransmissionConverter {
pub transmission_ratio: f64,
}
impl TransmissionConverter {
pub fn new() -> Self {
Self {
transmission_ratio: 34.0,
}
}
pub fn calculate_screw_output_rpm(&self, rpm: AngularVelocity) -> AngularVelocity {
rpm / self.transmission_ratio
}
pub fn calculate_screw_input_rpm(&self, screw_rpm: AngularVelocity) -> AngularVelocity {
screw_rpm * self.transmission_ratio
}
}
@@ -4,10 +4,8 @@ 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::el2008::EL2008_IDENTITY_B;
use ethercat_hal::devices::el2521::{
EL2521_IDENTITY_0000_A, EL2521_IDENTITY_0000_B, EL2521_IDENTITY_0024_A,
};
@@ -15,10 +13,8 @@ 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::el4002::EL4002_IDENTITY_A;
use ethercat_hal::devices::el6021::{
EL6021_IDENTITY_A, EL6021_IDENTITY_B, EL6021_IDENTITY_C, EL6021_IDENTITY_D,
};
@@ -232,19 +228,17 @@ pub fn get_identification_addresses<'maindevice>(
EK1100_IDENTITY_A => MachineIdentificationAddresses::default(),
EL1002_IDENTITY_A => MachineIdentificationAddresses::default(),
EL1008_IDENTITY_A => MachineIdentificationAddresses::default(),
EL2002_IDENTITY_A | EL2002_IDENTITY_B => MachineIdentificationAddresses::default(),
EL2002_IDENTITY_A => MachineIdentificationAddresses::default(),
EL2004_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3204_IDENTITY_A | EL3204_IDENTITY_B => MachineIdentificationAddresses::default(),
EL2008_IDENTITY_A | EL2008_IDENTITY_B => MachineIdentificationAddresses::default(),
EL2008_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3001_IDENTITY_A => MachineIdentificationAddresses::default(),
EL2521_IDENTITY_0000_A | EL2521_IDENTITY_0000_B | EL2521_IDENTITY_0024_A => {
MachineIdentificationAddresses::default()
}
EL2522_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3021_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3024_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3062_0030_IDENTITY_A => MachineIdentificationAddresses::default(),
EL4002_IDENTITY_A => MachineIdentificationAddresses::default(),
EL3021_IDENTITY_A => MachineIdentificationAddresses::default(),
EL7031_IDENTITY_A | EL7031_IDENTITY_B => MachineIdentificationAddresses::default(),
EL7031_0030_IDENTITY_A => MachineIdentificationAddresses::default(),
EL7041_0052_IDENTITY_A => MachineIdentificationAddresses::default(),
@@ -1,360 +0,0 @@
use serde::Serialize;
use serde::ser::{
SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
SerializeTupleStruct, SerializeTupleVariant, Serializer,
};
use std::error::Error;
use std::fmt::Display;
use std::hash::{DefaultHasher, Hasher};
#[derive(Debug)]
pub struct HashSerializerError {}
impl std::fmt::Display for HashSerializerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("{:?}", self))
// f.debug_struct("HashSerialiyerError")
// .finish()
}
}
impl Error for HashSerializerError {}
impl serde::ser::Error for HashSerializerError {
fn custom<T>(_msg: T) -> Self
where
T: Display,
{
HashSerializerError {}
}
}
pub struct HashSerializer<'a, H: Hasher>(&'a mut H);
impl<'a, H: Hasher> SerializeSeq for HashSerializer<'a, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, H: Hasher> SerializeTuple for HashSerializer<'a, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, H: Hasher> SerializeTupleStruct for HashSerializer<'a, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, H: Hasher> SerializeTupleVariant for HashSerializer<'a, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, H: Hasher> SerializeMap for HashSerializer<'a, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
key.serialize(HashSerializer(self.0))
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, H: Hasher> SerializeStruct for HashSerializer<'a, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
// Optional: hash the field name to avoid collisions
self.0.write(key.as_bytes());
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, H: Hasher> SerializeStructVariant for HashSerializer<'a, H> {
type Ok = ();
type Error = HashSerializerError;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
self.0.write(key.as_bytes());
value.serialize(HashSerializer(self.0))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}
impl<'a, H: Hasher> Serializer for HashSerializer<'a, H> {
type Ok = ();
type Error = HashSerializerError;
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Self;
fn serialize_bool(self, v: bool) -> Result<(), Self::Error> {
self.0.write_u8(v as u8);
Ok(())
}
fn serialize_i8(self, v: i8) -> Result<(), Self::Error> {
self.0.write_i8(v);
Ok(())
}
fn serialize_i16(self, v: i16) -> Result<(), Self::Error> {
self.0.write_i16(v);
Ok(())
}
fn serialize_i32(self, v: i32) -> Result<(), Self::Error> {
self.0.write_i32(v);
Ok(())
}
fn serialize_i64(self, v: i64) -> Result<(), Self::Error> {
self.0.write_i64(v);
Ok(())
}
fn serialize_u8(self, v: u8) -> Result<(), Self::Error> {
self.0.write_u8(v);
Ok(())
}
fn serialize_u16(self, v: u16) -> Result<(), Self::Error> {
self.0.write_u16(v);
Ok(())
}
fn serialize_u32(self, v: u32) -> Result<(), Self::Error> {
self.0.write_u32(v);
Ok(())
}
fn serialize_u64(self, v: u64) -> Result<(), Self::Error> {
self.0.write_u64(v);
Ok(())
}
fn serialize_f32(self, v: f32) -> Result<(), Self::Error> {
self.0.write_u32(v.to_bits());
Ok(())
}
fn serialize_f64(self, v: f64) -> Result<(), Self::Error> {
self.0.write_u64(v.to_bits());
Ok(())
}
fn serialize_char(self, v: char) -> Result<(), Self::Error> {
let mut buf = [0; 4];
self.0.write(v.encode_utf8(&mut buf).as_bytes());
Ok(())
}
fn serialize_str(self, v: &str) -> Result<(), Self::Error> {
self.0.write(v.as_bytes());
Ok(())
}
fn serialize_bytes(self, v: &[u8]) -> Result<(), Self::Error> {
self.0.write(v);
Ok(())
}
fn serialize_none(self) -> Result<(), Self::Error> {
self.0.write_u8(0); // type tag for None
Ok(())
}
fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> Result<(), Self::Error> {
self.0.write_u8(1); // type tag for Some
value.serialize(self)
}
fn serialize_unit(self) -> Result<(), Self::Error> {
self.0.write_u8(0);
Ok(())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Self::Error> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
) -> Result<(), Self::Error> {
self.0.write_u32(variant_index);
Ok(())
}
fn serialize_newtype_struct<T: ?Sized + Serialize>(
self,
_name: &'static str,
value: &T,
) -> Result<(), Self::Error> {
value.serialize(self)
}
fn serialize_newtype_variant<T: ?Sized + Serialize>(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
value: &T,
) -> Result<(), Self::Error> {
self.0.write_u32(variant_index);
value.serialize(self)
}
// For sequences, tuples, maps, structs, just return self for now
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(self)
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Ok(self)
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Ok(self)
}
fn serialize_tuple_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
self.0.write_u32(variant_index);
Ok(self)
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(self)
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(self)
}
fn serialize_struct_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
self.0.write_u32(variant_index);
Ok(self)
}
}
// A helper that uses Serialize to feed into a Hasher without allocating a buffer:
pub fn hash_with_serde_model<T: Serialize, H: Hasher>(value: T, hasher: H) -> u64 {
let mut h = hasher;
let ser = HashSerializer(&mut h);
value.serialize(ser).expect("serialize to hasher failed");
h.finish()
}
/// if hash of a and b are different this function returns true
/// a and b need to impl Serialize
pub fn check_hash_different<T: Serialize>(a: T, b: T) -> bool {
// Yes DefaultHasher has to be defined multiple times
// If you use one for both the results will never match even if they are the same value
let hasher = DefaultHasher::new();
let hash = hash_with_serde_model(a, hasher);
let hasher = DefaultHasher::new();
let hash_old = hash_with_serde_model(b, hasher);
return hash != hash_old;
}
#[test]
fn test_same_values() {
let a = 10;
let b = 10;
assert_eq!(
check_hash_different(a, b),
false,
"Hashes should be equal for identical values"
);
}
-1
View File
@@ -1,5 +1,4 @@
pub mod compare_lists;
pub mod hasher_serializer;
pub mod hashing;
pub mod interpolation;
pub mod loop_trottle;
-1
View File
@@ -8,7 +8,6 @@ pub mod realtime;
pub mod rest;
pub mod serial;
pub mod socketio;
pub mod transmission;
pub mod uom_extensions;
#[macro_use]
+5 -3
View File
@@ -2,10 +2,10 @@ pub mod modbus_serial_interface;
use anyhow::Error;
use crc::{CRC_16_MODBUS, Crc};
use serialport::SerialPort;
use serial;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParityType {
Even,
Odd,
@@ -143,7 +143,9 @@ impl From<ModbusRequest> for Vec<u8> {
/// # Returns
/// A `Result` containing an `Option` with the raw Modbus response as a `Vec<u8>` if successful,
/// or `None` if the response is invalid or an error occurs.
pub fn receive_data_modbus(port: &mut dyn SerialPort) -> Result<Option<Vec<u8>>, anyhow::Error> {
pub fn receive_data_modbus(
port: &mut dyn serial::SerialPort,
) -> Result<Option<Vec<u8>>, anyhow::Error> {
let mut buf: [u8; 256] = [0; 256];
let data_length = port.read(&mut buf)?;
if data_length == 0 {
+1 -1
View File
@@ -160,7 +160,7 @@ pub fn lock_memory() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
pub fn set_core_affinity(core_id: usize) -> Result<(), anyhow::Error> {
pub fn set_core_affinity_first_core(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 {
+3 -5
View File
@@ -1,11 +1,9 @@
use std::{any::Any, sync::Arc};
use smol::{channel::Sender, lock::RwLock};
use smol::lock::RwLock;
use std::fmt::Debug;
use crate::{
machines::identification::DeviceIdentification, serial::serial_detection::SerialDeviceRemoval,
};
use crate::machines::identification::DeviceIdentification;
pub mod panic;
pub mod registry;
@@ -27,7 +25,7 @@ pub trait SerialDeviceThread {
pub struct SerialDeviceNewParams {
pub path: String,
pub device_thread_panic_tx: Sender<SerialDeviceRemoval<String>>,
pub device_thread_panic_tx: smol::channel::Sender<(String, anyhow::Error)>,
}
#[derive(PartialEq, Clone, Debug)]
+4 -5
View File
@@ -1,13 +1,11 @@
use smol::channel::Sender;
use crate::serial::serial_detection::SerialDeviceRemoval;
/// Device-level panic handler for individual device crashes
/// Sends device identifier and error information for cleanup
/// Used by serial devices and other individual components that need to be removed on panic
pub fn send_serial_device_panic<T>(
device_identifier: T,
thread_panic_tx: Sender<SerialDeviceRemoval<T>>,
thread_panic_tx: Sender<(T, anyhow::Error)>,
) where
T: Send + Sync + Clone + 'static,
{
@@ -18,6 +16,7 @@ pub fn send_serial_device_panic<T>(
}
}
let device_id_clone = device_identifier.clone();
std::panic::set_hook(Box::new(move |panic_info| {
let thread_name = std::thread::current()
.name()
@@ -40,8 +39,8 @@ pub fn send_serial_device_panic<T>(
// Send device panic info through channel
smol::block_on(async {
let _ = thread_panic_tx
.send(SerialDeviceRemoval::Error(
device_identifier.clone(),
.send((
device_id_clone.clone(),
anyhow::anyhow!("{}", panic_message),
))
.await;
+13 -35
View File
@@ -21,11 +21,6 @@ use super::{
SerialDevice, SerialDeviceIdentification, SerialDeviceNewParams, registry::SerialDeviceRegistry,
};
pub enum SerialDeviceRemoval<T> {
Disconnect(T),
Error(T, anyhow::Error),
}
pub struct SerialDetection<'serialdeviceregistry> {
pub serial_device_registry: &'serialdeviceregistry SerialDeviceRegistry,
pub ports: HashMap<
@@ -36,13 +31,14 @@ pub struct SerialDetection<'serialdeviceregistry> {
Arc<RwLock<dyn SerialDevice>>,
),
>,
pub device_removal_signal_rx: Receiver<SerialDeviceRemoval<String>>,
pub device_removal_signal_tx: Sender<SerialDeviceRemoval<String>>,
pub device_removal_signal_rx: Receiver<(String, anyhow::Error)>,
pub device_removal_signal_tx: Sender<(String, anyhow::Error)>,
}
impl<'serialdeviceregistry> SerialDetection<'serialdeviceregistry> {
pub fn new(serial_device_registry: &'serialdeviceregistry SerialDeviceRegistry) -> Self {
let (device_removal_signal_tx, device_removal_signal_rx) = unbounded();
let (device_removal_signal_tx, device_removal_signal_rx) =
unbounded::<(String, anyhow::Error)>();
SerialDetection {
serial_device_registry,
ports: HashMap::new(),
@@ -162,36 +158,18 @@ impl<'serialdeviceregistry> SerialDetection<'serialdeviceregistry> {
pub async fn check_remove_signals(&mut self) -> Vec<DeviceIdentification> {
let mut removed_signals: Vec<DeviceIdentification> = Vec::new();
match self.device_removal_signal_rx.try_recv() {
Ok((path, error)) => {
// remove the device when the tuple positon 1 equals signal
if let Some(info) = self.ports.get(&path.clone()) {
removed_signals.push(info.1.clone());
};
self.ports.remove(&path);
let mut add_removal_signal = |path: &String| {
if let Some(info) = self.ports.get(path) {
removed_signals.push(info.1.clone());
};
self.ports.remove(path);
};
if self.device_removal_signal_rx.is_empty() {
return removed_signals;
}
let removal = self
.device_removal_signal_rx
.recv()
.await
.expect("Failed to receive removal signals");
match removal {
SerialDeviceRemoval::Disconnect(path) => {
add_removal_signal(&path);
tracing::trace!("Removed serial device after disconnect: {:?}", path);
}
SerialDeviceRemoval::Error(path, error) => {
add_removal_signal(&path);
tracing::error!("Removed erroring serial device: {:?}: {}", path, error);
tracing::trace!("Removed device: {:?}: {}", path, error);
}
Err(_) => {}
}
removed_signals
}
}
-4
View File
@@ -21,10 +21,6 @@ impl std::fmt::Debug for GenericEvent {
}
}
pub trait BuildEvent: Serialize + Send + Sync + Clone {
fn build(&self) -> Event<Self>;
}
#[derive(Debug, Clone, Serialize)]
pub struct Event<T>
where
-27
View File
@@ -1,27 +0,0 @@
use crate::transmission::Transmission;
#[derive(Debug, Clone)]
pub struct FixedTransmission {
ratio: f64,
}
impl Transmission for FixedTransmission {
fn get_ratio(&self) -> f64 {
self.ratio
}
}
impl FixedTransmission {
/// Creates a new FixedTransmission with the specified ratio.
///
/// # Arguments
///
/// * `ratio` - The transmission ratio (output over input).
///
/// # Returns
///
/// A new instance of FixedTransmission.
pub fn new(ratio: f64) -> Self {
Self { ratio }
}
}
-95
View File
@@ -1,95 +0,0 @@
use uom::si::{
angle::radian,
angular_velocity::radian_per_second,
f64::{Angle, AngularVelocity, Length, Velocity},
length::meter,
velocity::meter_per_second,
};
pub mod fixed;
/// A trait representing a mechanical transmission system that converts input to output
/// with a specific gear ratio.
///
/// This trait provides methods for working with transmission ratios and converting
/// between input and output values based on the transmission's gear ratio.
pub trait Transmission {
/// Returns the transmission ratio of this transmission system.
///
/// The ratio represents the relationship between input and output speeds/torques.
/// A ratio greater than 1.0 indicates speed reduction (torque multiplication),
/// while a ratio less than 1.0 indicates speed increase (torque reduction).
///
/// # Returns
///
/// The transmission ratio as a floating-point number.
fn get_ratio(&self) -> f64;
/// Converts an input value to the corresponding output value using the transmission ratio.
///
/// This method multiplies the input by the transmission ratio to calculate the output.
/// Typically used for converting input speed to output speed in a transmission system.
///
/// # Arguments
///
/// * `input` - The input value to be converted
///
/// # Returns
///
/// The output value after applying the transmission ratio.
fn calculate_output(&self, input: f64) -> f64 {
input * self.get_ratio()
}
/// Converts an output value to the corresponding input value using the transmission ratio.
///
/// This method divides the output by the transmission ratio to calculate the required input.
/// Typically used for determining what input is needed to achieve a desired output.
///
/// # Arguments
///
/// * `output` - The output value to be converted back to input
///
/// # Returns
///
/// The input value required to produce the given output.
fn calculate_input(&self, output: f64) -> f64 {
output / self.get_ratio()
}
fn calculate_angular_output(&self, input: Angle) -> Angle {
Angle::new::<radian>(self.calculate_output(input.get::<radian>()))
}
fn calculate_angular_input(&self, output: Angle) -> Angle {
Angle::new::<radian>(self.calculate_input(output.get::<radian>()))
}
fn calculate_angular_velocity_output(&self, input: AngularVelocity) -> AngularVelocity {
AngularVelocity::new::<radian_per_second>(
self.calculate_output(input.get::<radian_per_second>()),
)
}
fn calculate_angular_velocity_input(&self, output: AngularVelocity) -> AngularVelocity {
AngularVelocity::new::<radian_per_second>(
self.calculate_input(output.get::<radian_per_second>()),
)
}
fn calculate_linear_output(&self, input: Length) -> Length {
Length::new::<meter>(self.calculate_output(input.get::<meter>()))
}
fn calculate_linear_input(&self, output: Length) -> Length {
Length::new::<meter>(self.calculate_input(output.get::<meter>()))
}
fn calculate_linear_velocity_output(&self, input: Velocity) -> Velocity {
Velocity::new::<meter_per_second>(self.calculate_output(input.get::<meter_per_second>()))
}
fn calculate_linear_velocity_input(&self, output: Velocity) -> Velocity {
Velocity::new::<meter_per_second>(self.calculate_input(output.get::<meter_per_second>()))
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

-94
View File
@@ -1,94 +0,0 @@
# Code Style
# General Code styling
Try to adhere to the rust style guide:
https://doc.rust-lang.org/nightly/style-guide/
# Beckhoff terminals and Machines
For Beckhoff terminals you should first implement the basic functionality, then an Interpreter of the device/data is built on top.
A good example of how to do this is by looking at the following code:
el3021.rs , AnalogInputDevice
El3021 implements a so called AnalogInputDevice, which acts like an interface.
This allows us to use AnalogInputDevice instead of having to always specify which device should be used.
In Essence any Device that implements AnalogInputDevice can be used.
AnalogInputDevice is then used for creating an AnalogInput, which is plug and play for machines.
Then a further layer of Abstraction is used on top,
where the data is interpreted and incorporated into a sort of control loop for certain functions, for example:
```rust
// ScrewSpeedController controlls the speed (frequency) and direction of the motor
// AnalogInput is used to collect measurements from the pressure sensor.
// screw_speed_controller.rs
#[derive(Debug)]
pub struct ScrewSpeedController {
pub pid: ClampingTimeagnosticPidController,
pub target_pressure: Pressure,
pub target_rpm: AngularVelocity,
pub inverter: MitsubishiCS80,
pressure_sensor: AnalogInput,
last_update: Instant,
uses_rpm: bool,
forward_rotation: bool,
transmission_converter: TransmissionConverter,
frequency: Frequency,
maximum_frequency: Frequency,
minimum_frequency: Frequency,
motor_on: bool,
nozzle_pressure_limit: Pressure,
nozzle_pressure_limit_enabled: bool,
}
// Under the hood for ExtruderV2 a El3021 is used:
// (from new.rs of extruder)
let pressure_sensor = AnalogInput::new(el3021, EL3021Port::AI1);
// But the extruder does not directly use the AnalogInput, instead the logic is decoupled in the screw_speed_controller:
let mut extruder: ExtruderV2 = Self {
namespace: ExtruderV2Namespace::new(params.socket_queue_tx.clone()),
last_measurement_emit: Instant::now(),
mode: ExtruderV2Mode::Standby,
temperature_controller_front: temperature_controller_front,
temperature_controller_middle: temperature_controller_middle,
temperature_controller_back: temperature_controller_back,
temperature_controller_nozzle: temperature_controller_nozzle,
screw_speed_controller: screw_speed_controller,
emitted_default_state: false,
};
// Then in act.rs
// The extruder simply calls the update function of the screw_speed_controller,
// which contains "all" of the logic needed to properly drive the motor
self.screw_speed_controller.update(now_ts, true);
```
This is desired behaviour for all implemented devices, the machines should stay as modular as possible.
# Representing physical Units (Volt,Ampere,Hz,RPM etc)
To represent physical units like voltage ampere etc... we use uom (units of measurement), which acts as a wrapper for units and supplies all conversions between certain units.
For example:
```rust
// Lets say a device measures voltage, you just got the raw value
let volts : ElectricPotential = ElectricPotential::new::<volt>(raw_value_here);
// Then you can do this to get milli_volts
// In api responses we always use the RAW float value (64bit):
let milli_volts : f64 = volts.get::<millivolt>();
```
# General Advice
- Try to avoid duplicate code (DRY principle)
- Try to avoid lifetimes if at all possible
- Avoid excessive abstractions
- Avoid async code unless it is required
- Avoid taking Ownership of values, instead borrow the value
- When implementing a Trait for a struct, do it in the same file as the struct definition
- Split up large impl blocks into smaller impl blocks
-27
View File
@@ -1,27 +0,0 @@
# Testing Ram and Cpu (Hardware)
There were instability problems on one of the hmi panel pc's related to ram.
To test Ram and Cpu for stability problems we use memtest86.
You can download it here: https://www.memtest86.com/download.htm
Then in the unzipped folder you have to flash the .img file to a USB drive with atleast 2GB of space.
We already have USB-Sticks flashed with memtest though.
1. When starting up the Pc mash Del (Entf in german) on an external keyboard and enter the bios
2. Select the Memtest86 usb stick in the Bios
3. Save changes and exit
4. Now memtest boots up, just let it do its thing from here
It works if the screen looks something like this:
![](../assets/Memtest86_v83p.png)
Simply let it do one Pass ~20 minutes and shutdown and unplug usb stick afterwards
If it freezes completely -> unstable
If it shows errors the ram should be swapped out and the test repeated
-13
View File
@@ -1,13 +0,0 @@
# 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)
+60 -253
View File
@@ -17,13 +17,13 @@
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-navigation-menu": "^1.2.5",
"@radix-ui/react-popover": "^1.1.7",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.3",
"@radix-ui/react-slider": "^1.2.3",
"@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.84.2",
"@tanstack/react-query": "^5.66.7",
"@tanstack/react-router": "^1.105.0",
"@tanstack/react-table": "^8.21.2",
"@tanstack/router-devtools": "^1.105.0",
@@ -59,14 +59,14 @@
"devDependencies": {
"@electron-forge/cli": "^7.6.1",
"@electron-forge/maker-deb": "^7.8.0",
"@electron-forge/maker-rpm": "^7.8.3",
"@electron-forge/maker-rpm": "^7.6.1",
"@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": "^2.0.0",
"@electron/fuses": "^1.8.0",
"@eslint/compat": "^1.2.6",
"@eslint/js": "^9.18.0",
"@playwright/test": "^1.50.1",
@@ -76,14 +76,14 @@
"@testing-library/user-event": "^14.6.1",
"@types/electron-squirrel-startup": "^1.0.2",
"@types/eslint-config-prettier": "^6.11.3",
"@types/node": "^24.2.1",
"@types/node": "^22.13.4",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"electron": "^37.2.6",
"electron": "^36.4.0",
"electron-playwright-helpers": "^1.7.1",
"eslint": "^9.20.1",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-compiler": "^19.0.0-beta-714736e-20250131",
"globals": "^16.2.0",
@@ -805,14 +805,13 @@
}
},
"node_modules/@electron-forge/maker-rpm": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.8.3.tgz",
"integrity": "sha512-TqHHdF5D3Dy4NgG+pi8eRc18P1D0FF1LE+CqQF4MRof+XrpHhRtog5Ac4gWjvg1Wma6WjPyTdryHAkURsoBbuQ==",
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.8.2.tgz",
"integrity": "sha512-1jfvUAtfhOXG4+K6gMBdZZDWibD5odtHiB6olb39Lb6+YQAzDIarNV2HT27K7jwcH8AngzfC13C0FY0X8MrQjg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@electron-forge/maker-base": "7.8.3",
"@electron-forge/shared-types": "7.8.3"
"@electron-forge/maker-base": "7.8.2",
"@electron-forge/shared-types": "7.8.2"
},
"engines": {
"node": ">= 16.4.0"
@@ -821,50 +820,6 @@
"electron-installer-redhat": "^3.2.0"
}
},
"node_modules/@electron-forge/maker-rpm/node_modules/@electron-forge/maker-base": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-7.8.3.tgz",
"integrity": "sha512-WmF66cHdziaK8Asi7IRTLxZjCZ8IqXXHr6IPl4d5oatN6s5RG+HHzG1hiJ7LzlOEntqdSpE8Wh2nB2TmyR4huQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@electron-forge/shared-types": "7.8.3",
"fs-extra": "^10.0.0",
"which": "^2.0.2"
},
"engines": {
"node": ">= 16.4.0"
}
},
"node_modules/@electron-forge/maker-rpm/node_modules/@electron-forge/shared-types": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-7.8.3.tgz",
"integrity": "sha512-gkZtD7ALXHPDOthJo1rQYLDNfG09fdDRMWvjEgaXdF3Z69xXFfnOWPNuOkRUODNalMnuuGs6l7jDl+QFQgHlDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@electron-forge/tracer": "7.8.3",
"@electron/packager": "^18.3.5",
"@electron/rebuild": "^3.7.0",
"listr2": "^7.0.2"
},
"engines": {
"node": ">= 16.4.0"
}
},
"node_modules/@electron-forge/maker-rpm/node_modules/@electron-forge/tracer": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@electron-forge/tracer/-/tracer-7.8.3.tgz",
"integrity": "sha512-YVVDaPEUOvR1z+DDdj8wR/vO9OSlC91wz4/2Iqe9rqQz8sztnnQBClMAZTqu9bkDTFyHrns8j8v7tPVuVS6ULQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"chrome-trace-event": "^1.0.3"
},
"engines": {
"node": ">= 14.17.5"
}
},
"node_modules/@electron-forge/maker-squirrel": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-7.8.2.tgz",
@@ -1095,16 +1050,32 @@
}
},
"node_modules/@electron/fuses": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-2.0.0.tgz",
"integrity": "sha512-lyb1zK3YHeWUjaz7yiK0GnxSPduwASKMyiDbCtbn3spP6EEt+UWtktggWehG0icFrXAk3GwvcJ4nCrJO0N9IhQ==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz",
"integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.1",
"fs-extra": "^9.0.1",
"minimist": "^1.2.5"
},
"bin": {
"electron-fuses": "dist/bin.js"
}
},
"node_modules/@electron/fuses/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=22.12.0"
"node": ">=10"
}
},
"node_modules/@electron/get": {
@@ -2824,22 +2795,21 @@
}
},
"node_modules/@radix-ui/react-select": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
"integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==",
"license": "MIT",
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz",
"integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==",
"dependencies": {
"@radix-ui/number": "1.1.1",
"@radix-ui/primitive": "1.1.3",
"@radix-ui/primitive": "1.1.2",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-focus-guards": "1.1.3",
"@radix-ui/react-dismissable-layer": "1.1.10",
"@radix-ui/react-focus-guards": "1.1.2",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-popper": "1.2.7",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-slot": "1.2.3",
@@ -2866,86 +2836,6 @@
}
}
},
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
"license": "MIT"
},
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-escape-keydown": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-focus-guards": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
"integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-rect": "1.1.1",
"@radix-ui/react-use-size": "1.1.1",
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-separator": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
@@ -3719,66 +3609,6 @@
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.4.3",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.0.2",
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.4.3",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.0.2",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.11",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.4.3",
"@emnapi/runtime": "^1.4.3",
"@tybys/wasm-util": "^0.9.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
"version": "0.9.0",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
"version": "2.8.0",
"dev": true,
"inBundle": true,
"license": "0BSD",
"optional": true
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz",
@@ -3917,10 +3747,9 @@
}
},
"node_modules/@tanstack/react-query": {
"version": "5.84.2",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.2.tgz",
"integrity": "sha512-cZadySzROlD2+o8zIfbD978p0IphuQzRWiiH3I2ugnTmz4jbjc0+TdibpwqxlzynEen8OulgAg+rzdNF37s7XQ==",
"license": "MIT",
"version": "5.84.1",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.1.tgz",
"integrity": "sha512-zo7EUygcWJMQfFNWDSG7CBhy8irje/XY0RDVKKV4IQJAysb+ZJkkJPcnQi+KboyGUgT+SQebRFoTqLuTtfoDLw==",
"dependencies": {
"@tanstack/query-core": "5.83.1"
},
@@ -4402,13 +4231,12 @@
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="
},
"node_modules/@types/node": {
"version": "24.2.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
"version": "22.17.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz",
"integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.10.0"
"undici-types": "~6.21.0"
}
},
"node_modules/@types/react": {
@@ -6285,12 +6113,11 @@
"dev": true
},
"node_modules/electron": {
"version": "37.2.6",
"resolved": "https://registry.npmjs.org/electron/-/electron-37.2.6.tgz",
"integrity": "sha512-Ns6xyxE+hIK5UlujtRlw7w4e2Ju/ImCWXf1Q/PoOhc0N3/6SN6YW7+ujCarsHbxWnolbW+1RlkHtdklUJpjbPA==",
"version": "36.7.3",
"resolved": "https://registry.npmjs.org/electron/-/electron-36.7.3.tgz",
"integrity": "sha512-vO2xQsPr+YAIJZNv5ufuTqjiDVJgd67q80028fLPkC+iZWgXXM9OEC0KPhoebhqQTRmuzgB866NG01GfjRmQTw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^22.7.7",
@@ -6828,16 +6655,6 @@
"global-agent": "^3.0.0"
}
},
"node_modules/electron/node_modules/@types/node": {
"version": "22.17.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz",
"integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/electron/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@@ -6870,13 +6687,6 @@
"semver": "bin/semver.js"
}
},
"node_modules/electron/node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/electron/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@@ -7333,11 +7143,10 @@
}
},
"node_modules/eslint-plugin-prettier": {
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz",
"integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==",
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz",
"integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==",
"dev": true,
"license": "MIT",
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.11.7"
@@ -13906,11 +13715,10 @@
"dev": true
},
"node_modules/tmp": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
"integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=14.14"
@@ -14214,11 +14022,10 @@
}
},
"node_modules/undici-types": {
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
"devOptional": true,
"license": "MIT"
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"devOptional": true
},
"node_modules/unified": {
"version": "11.0.5",
+7 -8
View File
@@ -11,7 +11,6 @@
"make": "electron-forge make",
"publish": "electron-forge publish",
"lint": "eslint .",
"lint:write": "eslint . --fix",
"format": "prettier --check .",
"format:write": "prettier --write .",
"test": "vitest run",
@@ -24,14 +23,14 @@
"devDependencies": {
"@electron-forge/cli": "^7.6.1",
"@electron-forge/maker-deb": "^7.8.0",
"@electron-forge/maker-rpm": "^7.8.3",
"@electron-forge/maker-rpm": "^7.6.1",
"@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": "^2.0.0",
"@electron/fuses": "^1.8.0",
"@eslint/compat": "^1.2.6",
"@eslint/js": "^9.18.0",
"@playwright/test": "^1.50.1",
@@ -41,14 +40,14 @@
"@testing-library/user-event": "^14.6.1",
"@types/electron-squirrel-startup": "^1.0.2",
"@types/eslint-config-prettier": "^6.11.3",
"@types/node": "^24.2.1",
"@types/node": "^22.13.4",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"electron": "^37.2.6",
"electron": "^36.4.0",
"electron-playwright-helpers": "^1.7.1",
"eslint": "^9.20.1",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-compiler": "^19.0.0-beta-714736e-20250131",
"globals": "^16.2.0",
@@ -73,13 +72,13 @@
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-navigation-menu": "^1.2.5",
"@radix-ui/react-popover": "^1.1.7",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.3",
"@radix-ui/react-slider": "^1.2.3",
"@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.84.2",
"@tanstack/react-query": "^5.66.7",
"@tanstack/react-router": "^1.105.0",
"@tanstack/react-table": "^8.21.2",
"@tanstack/router-devtools": "^1.105.0",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

+1 -699
View File
@@ -1,699 +1 @@
![Titelseite,screenshot from Word doccument](images/manuals/extruder/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 | Extruder |
| ------------------- | ---------------------------------- |
| function | Extruding plastics into filaments
| Model | QiTech Extruder |
| type | Plastics extruding machine |
| Trade name | QiTech Esxruder |
<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/extruder/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/extruder/description.jpeg)
<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/extruder/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/extruder/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/extruder/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/extruder/w008.jpeg) | W008 Warning of risk of falling | ![m003](images/manuals/extruder/m003.jpeg) | M003 Use hearing protection |
| ![w011](images/manuals/extruder/w011.jpeg) | W011 Warning of danger of slipping | ![m004](images/manuals/extruder/m004.jpeg) | M004 Use eye protection |
| ![w004](images/manuals/extruder/w004.jpeg) | W004 Warning of laser beam | ![m008](images/manuals/extruder/m008.jpeg) | M008 Use foot protection |
| ![w012](images/manuals/extruder/w012.jpeg) | W012 Warning of electrical voltage | ![m009](images/manuals/extruder/m009.jpeg) | M009 Use hand protection |
| ![w017](images/manuals/extruder/w017.jpeg) | W017 Warning of hot surface | ![m010](images/manuals/extruder/m010.jpeg) | M010 Use protective clothing |
| ![w018](images/manuals/extruder/w018.jpeg) | W018 Warning against automatic start-up | ![m012](images/manuals/extruder/m012.jpeg) | M012 Use handrail |
| ![w019](images/manuals/extruder/w019.jpeg) | W019 Warning of crushing hazard | ![m021](images/manuals/extruder/m021.jpeg) | M021 Switch off before maintenance or repair |
| ![w024](images/manuals/extruder/w024.jpeg) | W024 Warning against hand injuries | ![m014](images/manuals/extruder/m014.jpeg) | M014 Use head protection |
| ![w025](images/manuals/extruder/w025.jpeg) | W025 Warning against counter-rotating rollers | | |
| ![w016](images/manuals/extruder/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/extruder/p001.jpeg" width="158" height="158"/> | P001 General prohibition sign | <img src="images/manuals/extruder/f001.jpeg" width="158" height="158"> | F001 Fire extinguisher |
| <img src="images/manuals/extruder/p002.jpeg" width="158" height="158"/> | P002 No smoking | <img src="images/manuals/extruder/ANIS_danger.jpeg" width="232" height="60"> | ANSI Z535.3 - DANGER |
| <img src="images/manuals/extruder/p003.jpeg" width="158" height="158"/> | P003 No open flames; fire, open source of ignition and smoking prohibited | <img src="images/manuals/extruder/ANIS_warning.jpeg" width="232" height="60"> | ANSI Z535.3 - WARNING |
| <img src="images/manuals/extruder/p006.jpeg" width="158" height="158"/> | P006 Prohibited for industrial trucks | <img src="images/manuals/extruder/ANIS_caution.jpeg" width="232" height="60"> | ANSI Z535.3 - CAUTION |
| <img src="images/manuals/extruder/p009.jpeg" width="158" height="158"/> | P009 Climbing prohibited - In the sense of climbing prohibited for unauthorized persons | <img src="images/manuals/extruder/ANIS_notice.jpeg" width="232" height="60"> | ANSI Z535.3 - NOTICE |
| <img src="images/manuals/extruder/p022.jpeg" width="158" height="158"/> | P022 Eating and drinking prohibited | <img src="images/manuals/extruder/ANIS_safetyinstructions.jpeg" width="232" height="60"> | ANSI Z535.3 - SAFETY INSTRUCTIONS |
| <img src="images/manuals/extruder/p024.jpeg" width="158" height="158"/> | P024 Entering the area prohibited | <img src="images/manuals/extruder/trashcan.jpeg" width="158" height="158"> | Symbol for separate collection of waste electrical and electronic equipment |
| <img src="images/manuals/extruder/p031.jpeg" width="158" height="158"/> | P031 Switching prohibited | | |
| <img src="images/manuals/extruder/D-p006.jpeg" width="158" height="158"/> | D-P006 Access prohibited for unauthorized persons | | |
<br><br><br>
## 1.4 Intended use
The QiTech extruder is intended for melting plastic granulate into plastic filament.
The extruder must be placed on a stable, level surface or a braked trolley and secured if necessary. The test and work sequence is described in chapter 2.3 Work sequence.
When using the extruder, 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.
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 setting up the machine and carrying out cleaning and maintenance work, troubleshooting, fault rectification and repairs.
<br>
<br>
## 1.5 Reasonably foreseeable misuse
Reasonably foreseeable misuse exists in particular due to the filling of unsuitable melting material.
The following procedures should also be regarded as misuse:
- Operating the extruder on an uneven surface or in a position other than the intended horizontal position or on an unbraked trolley.
- The introduction of unauthorized molten material (e.g. highly flammable or explosive substances, non-meltable substances).
- Tampering with hot machine parts or the hopper.
- Use of spare parts and components not authorized by the manufacturer.
- Operating the extruder without supervision by tired/stressed supervisors or in ghost shifts.
- Starting extrusion before the plastic has reached the required operating temperature.
- Dismantling machine parts that are surrounded by hardened plastic, in particular the melting head, screw and pressure sensor.
- Operating the heating elements without thermal mass (screw barrel) leads to them burning out.
Furthermore, there is always the possibility that existing/provided safety guards are dismantled or the existing sensors are manipulated or software restrictions are circumvented/deactivated.
The aforementioned misuse must be explicitly prohibited with reference to these operating instructions and by written operating instructions; the use of the extruder must also be explicitly restricted in the operating instructions to the aforementioned intended use.
This documentation refers exclusively to the operation of the extruder as documented in chapters 2.1 and 2.2. If the system is relocated or changes are made to the assemblies/configuration, the risk assessment must be repeated for the affected system parts or, in the case of significant changes within the meaning of the Product Safety Act, for the respective machine.
The 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.
In principle, it is possible to replace the die and melting head of the extruder with identical parts. This does not constitute a major modification / significant change within the meaning of the EC Machinery Directive or the Product Safety Act.
<br>
<br>
## 1.6 Substantial 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>
<br>
## 1.7 Description of the safety devices
ON/OFF switch or main switch
The machine has a main switch that disconnects/connects the mains supply. The ON/OFF switch can also be secured with a lock in the OFF position to prevent unauthorized persons from switching it on.
<br>
**Emergency stop devices**
The emergency stop switch is used to quickly stop the machine in the event of danger. This is designed with redundant emergency stop contactors. An emergency stop switching device also prevents the machine from being switched on again after a power failure. By activating the emergency stop device, the entire machine is shut down and disconnected from the mains supply.
![emergency stop button / switch](images/manuals/extruder/emergency_stop.jpeg)
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.
For more information, see chapter "Operation and control (work sequence)".
<br>
**Overload protection**
The inverter of the main motor is equipped with overload protection. This overload protection automatically switches off the main drive motor in the event of excessive power consumption or excessive temperature. This is made possible by the use of PTC thermistors/PTC resistors in the motor coils. The motor can only be switched on again after the machine has been restarted.
<br>
**Overheating protection**
The machine is equipped with overheating protection. This switches off the heating elements immediately in the event of impermissibly high operating temperatures. A warning is issued in the event of sensor errors. In addition, the selection by the operator is limited to the permissible temperature range by the software.
<br>
**Overpressure protection**
The machine is equipped with overpressure protection. This switches off the main drive motor immediately in the event of unexpectedly high pressure.
Under temperature protection
The software only allows the main motor to be switched on at a temperature of 100°C or higher. This minimizes the risk of screw breakage or motor failure due to start-up against excessive resistance.
<br>
**Isolating protective devices:**
- A lockable switch cabinet prevents interference with or tampering with the electrical system.
- The feed hopper is designed to prevent limbs from being drawn in by the rotating auger.
- The center section of the optional table structure prevents people from standing directly in front of the melting head.
If guards are opened or removed for the purpose of 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 guards.
DANGER!
Danger to life!
Before switching the machine back on, make sure that there are no persons in the danger zone!
<br>
<br>
## 1.8 Instruction and Training
As the owner/operator, you are obliged to inform and instruct the operating personnel about existing legal and accident prevention regulations and about existing safety equipment on the extruder. This obligation also extends to safety equipment installed around the extruder. 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 staff work in a safety and hazard-conscious manner. Compliance with the training content should be checked regularly.
As the owner/operator, you must therefore obtain written confirmation from each employee that they have attended a training session.
## 1.9 Noise emissions from the machine during activities
The noise emissions of the machine were measured on the same machine types. The measured values are listed in the appendix to these operating instructions. 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.
# 2 Life phases of the machine
<br>
## 2.1 Transport and installation
<br>
**Requirements for the installation site**
- Installation only in industrial and commercial indoor areas.
- Sufficient ventilation of the installation site must be ensured.
- It is recommended to install an extraction system at the nozzle.
- The location must be as fire-free as possible and equipped with a fire extinguisher.
- The foundation must be level and secure.
<br><br>
| Picture | Danger |
|---------|--------|
| ![Warning](images/manuals/extruder/w001.jpeg)|**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.
| Picture | Caution! |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ![Warning](images/manuals/extruder/w001.jpeg) | **Danger to life due to incorrect installation and commissioning!** <brErrors 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
<br>
**Basic installation**
The extruder is supplied with a roller container, the parking brakes must be applied. The cables must be routed in such a way that they cannot be tripped over.
**Calibration of the temperature sensors and the pressure sensor**
The sensors are pre-calibrated ex works. Manual recalibration is only possible for the pressure sensor. Please refer to the KMK manufacturer's instructions for this.
<br>
**Connection to the power supply**
The CEE plug of the extruder must be plugged into a suitable three-phase socket with a right-hand field. If only a left-hand rotating field is available, the direction of rotation of the motor can alternatively be adjusted in the "Setup" tab of the software. The correct direction of rotation is marked with an arrow on the extruder.
<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.
![screenshot updates](images/manuals/extruder/screenshot_updates.png)
<br>
**Safety Measures**
A number of important safety measures must be observed to ensure safe operation of the extruder:
- It is important to use only original fuses with the specified current ratings to prevent 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 neighbouring 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.
- When working on high-voltage assemblies, the supply cable must be connected to earth after disconnecting the voltage and the components, e.g. capacitors, must be short-circuited with an earthing rod.
These safety measures are necessary to ensure safe and successful operation of the extruder.
<br>
<br>
## 2.3 Operation and control (work sequence)
<br>
A visual inspection must be carried out every working day before commissioning the machine. In particular, check that the die and melting head are fully screwed in.
Then start the machine as follows:
**1. Switch on the machine by plugging in the 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. Preheat the extruder**
- Go to "Control” mode in the function bar
- Press the "Heat" button in the “mode” box
- Set the “target temperature” of the individual heating zones according to the melting material in the 3 input fields
- Set the “target temperature” of the nozzle
- After reaching the target temperature, wait 15 minutes until the machine has heated through and the plastic in the screw barrel has completely melted.
![Screenshot: set traget temperature](images/manuals/extruder/screenshot_adjust_heat.png)
<br>
**3. Filling the melt into the hopper**
**4. Starting the extrusion**
- Set the screw speed via the "Screw Speed" slider
- Press the "Extrude" button in the “mode” box
- Wait until old melt has been "flushed" out of the extruder, i.e. no more discolouration of the filament is no longer discoloured and a constant material flow has been achieved
![screenshot: adjust screw speed](images/manuals/extruder/screenshot_extruder_adjustscrew_speed.png)
<br>
**5. Stopping the extrusion**
- Press the "Standby" button in the software
- Switch off via emergency stop in an emergency
**6. Switch off the machine using the main switch**
- Turn the main switch to the "O" position
- Lock any existing fuse on the switch with a key
<br>
**Changing the process variables during operation**
- Adjust the desired parameters using the input fields
- Press the "Heat" or "Extrude" function button to accept parameters
<br>
**Set the srew direction**
- Go to "Config" in the funktion bar
- To set the screw direction click on "Forward" or "Backward" in the "Invert settings" box
![Changing screrw direction](images/manuals/extruder/screenshot_config.png)
<br>
**Changing the melting material during operation**
- Skim, suck out or empty the current material from the hopper.
- Empty the extruder screw until no more plastic emerges from the nozzle.
- Preheat to the temperature of the new material.
- Pour new material into the hopper.
- Restart the extrusion and wait until any contamination from the previous material has completely disappeared.
<br>
**Resetting the emergency stop**
- Pull the red switch button back up.
- Click the function button for the desired mode again in the software.
- If the switch has not been reset on the hardware side, the "Emergency" status is displayed in green (emergency stop pressed) in the software and it is not possible to start individual modes.
<br>
| Picture | DANGER! |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ![Warning](images/manuals/extruder/w001.jpeg) | **Danger to life due to operation of the machine in a faulty condition.** <br> If safety-relevant changes or defects occur on the machine, shut it down immediately, secure it against being switched on again and report the incident to the responsible office/person! |
<br>
<br>
## 2.4 Maintenance, inspection, Testing
Maintenance and repair 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 extruder under the constant supervision of an experienced person! Heat-resistant gloves must be worn when working with hot components!
<br>
**Update extruder software**
There are regular updates for the "QiTech Control" software, which, in addition to troubleshooting, also include new features, functions or security updates. It is therefore recommended to check for new updates regularly for new versions of the control software. Updates for the machine software itself and installation instructions may also be provided there.
<br>
**Replacing the nozzle**
The nozzle can be replaced in the event of excessive wear, the need for a changed throughput or for greater resistance with composites. To do this, the system must be preheated to operating temperature. The nozzle can then be removed using a 19 mm spanner in an anticlockwise direction and a new nozzle inserted. It must be ensured that the system is depressurized for this.
<br>
**Replacing the melting head**
First empty the screw and remove the pressure sensor as described. With the system preheated, use a suitable hook spanner to loosen the melting head clockwise (left-hand thread!). Quickly screw in the new melting head anticlockwise before the thread becomes clogged with molten material. After screwing in, wait 15 minutes until the plastic in the melting head has liquefied before continuing extrusion.
<br>
**Dismantling the pressure sensor**
To avoid damaging the sensor, it must not be removed when the melt has cooled down. It is therefore advisable to always remove the sensor from a machine that is still warm (at production temperature). The screw must first be emptied and the sensor cable disconnected using the quick connector. When installing and removing the sensor, ensure that the diaphragm is not damaged by bumping or knocking.
If further work is necessary, such as replacing the extruder screw, please contact the manufacturer.
<br>
| Picture | Caution! |
| --------------------------------------------- | ----------------------------------------------------------------------------- |
| ![Warning](images/manuals/extruder/w001.jpeg) | **Observe the instructions for maintenance work when servicing the machine!** |
| Picture | Caution! |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| ![Warning](images/manuals/extruder/w001.jpeg) | **Risk of screw breakage if disassembled while hot!** <br> Screw connections may only be loosened when the system has cooled down. |
<br>
<br>
## 2.5 Troubleshooting and fault rectification
<br>
**Machine is not recognised by QiTech**
- Is the LAN-cable connected?
- Is the latest version of QiTech Control installed?
<br>
**Temperature sensors issue warnings**
- Check connections of temperature measuring unit and control unit for loose contact or cable breakage
- Check the routing of the motor cable and sensor cables. These should be as far apart as possible and should never be routed in parallel.
<br>
**Melt does not have the desired consistency or colour**
- Too liquid: Are the temperatures set too high? Has the plastic chemically decomposed or burnt?
- Too viscous: Are the temperatures set too low? Was the preheating sufficient?
- Has the screw been completely "flushed" with the new material so that there is no mixture?
- Has the granulate been completely dried before use?
- Is the starting material contaminated?
<br>
**Material throughput is not constant**
- Is the material in the hopper tilted or melted together?
- Is the size of the granulate grains small enough to be drawn in by the screw?
- Is the melting head or nozzle blocked?
- Was an attempt made to reduce the extrusion speed?
- Was there always enough material in the hopper?
<br>
**Are deposits forming on the melting head?**
- Is the temperature at the melting head high enough?
- Has an attempt been made to increase the temperature of the front heating zone?
- Has an attempt been made to additionally insulate the melting head (e.g. with glass wool)?
<br>
**The heating zones do not heat up at the same rate**
- Due to different thermal masses, it is to be expected that the heating zone in the centre heats up the
The fastest, the one at the melting head the second fastest and the one at the hopper the slowest.
Heating up usually takes about 30-45 minutes.
Is there another problem, or could the current problem not be solved with the guiding questions? Answers to problems with the filament can be found here: https://support.3devo.com/find-a-quick-answer#filament-quality-problems (as of 15/03/2023). For problems with the machine itself, contact the manufacturer directly.
<br>
<br>
## 2.6 Dismantling, decommissioning and disposal
When the machine has reached the end of its intended 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, the entire energy supply must first be separated. All components must then be separated according to material class to enable subsequent recycling or differentiated scrapping.
The machine consists largely of aluminium and steel 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 authorised specialist company.
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 centres.
<br>
<br>
# 3 Hazard warning and information on Residual risks
<br>
| Picture | Danger! |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <img src="images/manuals/extruder/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/extruder/w001.jpeg" width="150" height="150"/> | **Operation only permitted with protective devices fitted!** <br> The extruder 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/extruder/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/extruder/w012.jpeg" width="150" height="150"/> | **Electric shock** <br> Before carrying out any repair, set-up or maintenance work, the extruder must be de-energised by disconnecting the mains plug! Pull out the mains plug! |
| <img src="images/manuals/extruder/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/extruder/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/extruder/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/extruder/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/extruder/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! |
<br>
<br>
# 4 Glossary and Definitions
<br>
**Specialized personnel**
Due to their professional training, knowledge and experience as well as knowledge of the relevant regulations, qualified 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 behaviour. 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 specialist 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 must not work on the machine:
- Persons with skin diseases,
- People with chronic respiratory problems or a tendency to allergic reactions
- Pregnant women and nursing mothers
- children under 18 years
<br>
| Picture | DANGER! |
| ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| ![Warning](images/manuals/extruder/w001.jpeg) | **Risk of injury due to insufficient qualification!** 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 respective chapters of these instructions. |
<br>
<br>
**Life phases of the machine / system**
| Life phase | User/qualification |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Construction | Trained and instructed specialised personnel of the manufacturer |
| Transport | Trained and instructed specialist personnel of the manufacturer or forwarding agent/transporter |
| Installation | Trained and instructed specialist personnel of the manufacturer or service technician of the manufacturer |
| Commissioning | |
| Semi-automatic operation | Instructed personnel of the operator |
| Adjustment/setup/restart of the system | |
| Testing | |
| Eliminating faults in the work process | Trained and instructed specialised personnel of the operator or service technicians of the manufacturer |
| Observation of production processes | Instructed personnel of the operator |
| Troubleshooting and fault rectification | |
| Cleaning, maintenance | |
| Maintenance | Trained and instructed specialised personnel of the operator or service technicians of the manufacturer |
| Decommissioning | Trained and instructed specialised personnel of the operator |
# Extruder Manual
File renamed without changes.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

+1 -1
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](images/manuals/mock/duck.jpg)
![Mock Machine Duck](src/assets/markdown/mock/duck.jpg)
_Figure 1: Mock Machine Mascot_
## Safety Information
File renamed without changes.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 188 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 375 KiB

After

Width:  |  Height:  |  Size: 375 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 146 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 471 KiB

After

Width:  |  Height:  |  Size: 471 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 203 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

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