Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10972aabde | ||
|
|
a6b7ec914d | ||
|
|
56c095a10c | ||
|
|
f486aaa31b | ||
|
|
c4f2f96c2e | ||
|
|
99988c0445 | ||
|
|
9c5191a6ae | ||
|
|
2f66715738 | ||
|
|
bcfd579a4b | ||
|
|
bef13832d6 | ||
|
|
130ebf1681 | ||
|
|
587c8c8fdb | ||
|
|
1ad9896be4 | ||
|
|
a6cdcf893d | ||
|
|
37ca881ed7 | ||
|
|
c38d7c0056 | ||
|
|
99b9a8710c | ||
|
|
a859426b06 | ||
|
|
479908bf59 | ||
|
|
3bc6522095 | ||
|
|
6fcce62b04 | ||
|
|
060a7b6dbc | ||
|
|
73521ad80a | ||
|
|
5c3897db9b | ||
|
|
e659ebbfd5 | ||
|
|
3e98d08a0f | ||
|
|
12069c0cd2 | ||
|
|
a62d492a0a | ||
|
|
2b65d2ddd5 | ||
|
|
9bb16d5cc5 | ||
|
|
9b8076b357 | ||
|
|
c86d34d881 | ||
|
|
6dd031df24 | ||
|
|
40b33bf28d | ||
|
|
9a93c7bbd9 | ||
|
|
24ef2b1c6c | ||
|
|
37359b4058 | ||
|
|
ce19f515d7 | ||
|
|
1359e0a33d | ||
|
|
3b234df7e7 | ||
|
|
b820a433df | ||
|
|
9bf061bf6d | ||
|
|
84f1fa123f | ||
|
|
91660fd1ec | ||
|
|
28c4a801a6 | ||
|
|
22af6e61af | ||
|
|
794a309d71 | ||
|
|
273d695d0d | ||
|
|
76d5ae613a | ||
|
|
14cca9e8cd | ||
|
|
8f5ac3706f | ||
|
|
d928f02f2b | ||
|
|
77c099fa8a | ||
|
|
bf9e5e2a3c | ||
|
|
b1ccbf3585 | ||
|
|
1f1050f115 | ||
|
|
007def4a01 | ||
|
|
3737cc4292 | ||
|
|
8723eb1e55 | ||
|
|
1192fc28a3 | ||
|
|
23af0a82ec | ||
|
|
a2ee1d91d9 | ||
|
|
eb9830f8a7 | ||
|
|
3f92b11864 | ||
|
|
96785e8876 | ||
|
|
d36925f616 | ||
|
|
bdc8d7bbd0 | ||
|
|
268685858d | ||
|
|
eeafd93e3e | ||
|
|
8fa6213f9c | ||
|
|
f8b186ec5d | ||
|
|
4f6a1a0493 | ||
|
|
8ee9b233f6 | ||
|
|
4f6f148c70 | ||
|
|
3ea8f38139 | ||
|
|
3657bbf53e | ||
|
|
92b098c240 | ||
|
|
7350eeb354 | ||
|
|
67e2b33db7 | ||
|
|
d9eb49c37c | ||
|
|
dba7ce7494 | ||
|
|
50d16feb3d | ||
|
|
437f94659c | ||
|
|
53efecc636 | ||
|
|
c2371d095e | ||
|
|
cacde7868c | ||
|
|
7cf06e6a9d | ||
|
|
83688daaf4 | ||
|
|
f55a6c6569 | ||
|
|
e42cc64f5d | ||
|
|
5c0a9f3bb6 | ||
|
|
b719731926 | ||
|
|
5b4d81fec2 | ||
|
|
4f50395eb9 | ||
|
|
30a7134db9 | ||
|
|
474d64c2a2 | ||
|
|
0293654803 | ||
|
|
80ca509998 | ||
|
|
a84157bd94 | ||
|
|
0007f14296 | ||
|
|
307ad265a9 | ||
|
|
41f36fdf2e | ||
|
|
86c28fef8a | ||
|
|
c01c09548c | ||
|
|
8f76bb88e7 | ||
|
|
ea24c2f85c | ||
|
|
806603c583 | ||
|
|
4b9a788be4 | ||
|
|
2d91d34e9a | ||
|
|
7e50cce009 | ||
|
|
2f642a11d0 | ||
|
|
6cb027b12e | ||
|
|
58903a885d | ||
|
|
eda4b7a9b5 | ||
|
|
2f48b4093f | ||
|
|
676f19ea0d | ||
|
|
0252cd5ef3 | ||
|
|
ac248e31a1 | ||
|
|
58e43f4fc8 | ||
|
|
3845926b97 | ||
|
|
78c99580bf | ||
|
|
3526711f4d | ||
|
|
5ab97839c6 | ||
|
|
c92455226f | ||
|
|
3b91162fd3 | ||
|
|
c3da8bb357 | ||
|
|
70dd9d2072 | ||
|
|
2a87e17909 | ||
|
|
ac95e23e18 | ||
|
|
5e606e485f | ||
|
|
a35f65b8d0 | ||
|
|
587f7f6c88 | ||
|
|
eedf71b933 | ||
|
|
671c36e9c1 | ||
|
|
f59b1f4096 | ||
|
|
138c5614d1 | ||
|
|
b0e1a09d61 | ||
|
|
29a8a5281b | ||
|
|
92cb25749a | ||
|
|
0b26dfe9de | ||
|
|
a1117ad09d | ||
|
|
d39bd0b7fa | ||
|
|
51db45c341 | ||
|
|
c74fabb1f0 | ||
|
|
afc9b2a135 | ||
|
|
e3cfb6d0a0 | ||
|
|
30a521f27d | ||
|
|
bba2759c1a | ||
|
|
4c1f870eaa | ||
|
|
5d317a9841 | ||
|
|
4c750ec3be | ||
|
|
da531208e5 | ||
|
|
554560681b | ||
|
|
05198ee7e9 | ||
|
|
a3b9f1b505 | ||
|
|
08578bf856 | ||
|
|
ea65f3cd2c | ||
|
|
4df8815bfa | ||
|
|
5f645c3d3a | ||
|
|
c774ace112 | ||
|
|
64a6ab0410 | ||
|
|
def9c22b64 | ||
|
|
6d6f90bf9f | ||
|
|
12dc482e91 | ||
|
|
b52c047cdf | ||
|
|
fa75e91ce6 | ||
|
|
c52d4d464d | ||
|
|
9719b5251c | ||
|
|
ea796bbea6 | ||
|
|
594a8730a4 | ||
|
|
03ae4aeefd | ||
|
|
57f6e50e0e | ||
|
|
fcebb2d0f2 | ||
|
|
56ebc80aab | ||
|
|
6d8aba1577 | ||
|
|
ebaf033b36 | ||
|
|
71f314cf75 | ||
|
|
f520c759ce | ||
|
|
b8d0b1a630 | ||
|
|
11deedb75d | ||
|
|
50a61f9e21 | ||
|
|
2df5a06930 | ||
|
|
0ae30ba8fb | ||
|
|
2b80d6c806 | ||
|
|
fd2ff77b62 | ||
|
|
46e699b745 | ||
|
|
5e0dbf9d7e | ||
|
|
eefc9b3834 | ||
|
|
bbc9783961 | ||
|
|
3c998d2f5c | ||
|
|
52cd1ce050 | ||
|
|
5140410006 | ||
|
|
852beb5a06 | ||
|
|
2cf150a541 | ||
|
|
4f2f1b227c | ||
|
|
7c1f77ff7b | ||
|
|
8b2b769435 | ||
|
|
5350ca8f53 | ||
|
|
14f4ac1248 | ||
|
|
548ecf5723 | ||
|
|
99e74759b5 | ||
|
|
ec177906c8 | ||
|
|
afdf38eaf2 | ||
|
|
d8d509812b | ||
|
|
3eeeadab5c | ||
|
|
657c3cff85 | ||
|
|
7ea7721ea3 | ||
|
|
b5ccf10128 | ||
|
|
a5a0b1a5e5 | ||
|
|
1e8ef812e9 | ||
|
|
4965e73b0c | ||
|
|
6db2b91052 | ||
|
|
a930d058c2 | ||
|
|
5b7acbc7f3 | ||
|
|
344d319d12 | ||
|
|
d5534e0967 | ||
|
|
680cb71dde | ||
|
|
a26ccb0a1f | ||
|
|
9fa037a9f9 | ||
|
|
f07dcbf8f8 | ||
|
|
783ee38a3a | ||
|
|
4a7be41dfe | ||
|
|
8b6960ae64 | ||
|
|
cf0b71bb69 | ||
|
|
e5608d061f | ||
|
|
7eba9523d4 | ||
|
|
5ad85b1a8c | ||
|
|
064a904975 | ||
|
|
ccbab4afba | ||
|
|
79870d995a | ||
|
|
0d95f1c44f | ||
|
|
62aabaa36a | ||
|
|
b57b746379 | ||
|
|
d1dbe7550d | ||
|
|
38d2364ac4 | ||
|
|
581b4f7773 | ||
|
|
0550ecec7b | ||
|
|
3071482a87 | ||
|
|
0c926a5acd | ||
|
|
59387eb214 | ||
|
|
9693af313f | ||
|
|
df1beabfa8 | ||
|
|
9630142921 | ||
|
|
fcef922ab1 | ||
|
|
edd8c83402 | ||
|
|
09d9801482 | ||
|
|
f50997ec23 | ||
|
|
5524e9f10a | ||
|
|
7e73e2f879 | ||
|
|
96719dae15 | ||
|
|
e78e9ba9e3 | ||
|
|
a730ed3228 | ||
|
|
c3b0a8643d | ||
|
|
422d98bc7c | ||
|
|
f1b61dad9b | ||
|
|
c97768975b | ||
|
|
0b882e8bde | ||
|
|
6752b503bc | ||
|
|
847963fb1c | ||
|
|
4441b60d4a | ||
|
|
cb74f4fb6a | ||
|
|
fae9c7151c | ||
|
|
7aa36c0361 | ||
|
|
eb4b4e948d | ||
|
|
13af890e29 | ||
|
|
d366f52127 | ||
|
|
8f8535b6a3 | ||
|
|
83fa27c7be | ||
|
|
8295496671 | ||
|
|
9277521dbd | ||
|
|
3797fc6469 | ||
|
|
9700e47358 | ||
|
|
8ee1c63e8d | ||
|
|
dae3def188 | ||
|
|
fa3dc0aa8c | ||
|
|
69fcdb969d | ||
|
|
9b03b4d909 | ||
|
|
5e9fb93a21 | ||
|
|
b6842a71fb | ||
|
|
f89645a516 | ||
|
|
d928a4ba81 | ||
|
|
c457b21e64 | ||
|
|
c63be3688b | ||
|
|
21c40ac7ad | ||
|
|
0938f68d8d | ||
|
|
87e903781b | ||
|
|
6753f92128 | ||
|
|
59353ff06f | ||
|
|
938bbc97a6 | ||
|
|
bb02eeb2b0 | ||
|
|
7bc91d73a6 | ||
|
|
59783bc2c3 | ||
|
|
d93ff842ab | ||
|
|
78417edcb8 | ||
|
|
f0f1c2c69e | ||
|
|
b4043d1a75 | ||
|
|
3614f812b9 | ||
|
|
26d3af414f | ||
|
|
ec8eb6a835 | ||
|
|
69f0f73087 | ||
|
|
0b3e4c8c96 | ||
|
|
b8777ee4da | ||
|
|
0bc415271e | ||
|
|
efa32e91ee | ||
|
|
f0927513cd | ||
|
|
3599c26c54 | ||
|
|
aa97091b98 | ||
|
|
ea62de3fdc | ||
|
|
b42089e992 | ||
|
|
ebffbb69ec | ||
|
|
c48b5b2292 | ||
|
|
8abd6558d1 | ||
|
|
dc0b4fada3 | ||
|
|
92cacbc6c8 | ||
|
|
4bad318ce7 | ||
|
|
0eeb7f372e | ||
|
|
1a72f3f8bb | ||
|
|
522ace8a4f | ||
|
|
7e776dc360 | ||
|
|
6487ddc6d2 | ||
|
|
4ebd09d2eb | ||
|
|
330b000b6e | ||
|
|
7a817945ee | ||
|
|
47b1b7fc82 | ||
|
|
7f2ea72ad3 | ||
|
|
62553d1a2e | ||
|
|
cff28876ee | ||
|
|
56032ed59c | ||
|
|
73245a31c6 | ||
|
|
f423ac2a14 | ||
|
|
82377c2c25 | ||
|
|
08bd2c8cee | ||
|
|
7205fdea22 | ||
|
|
c1c420ce8f | ||
|
|
576e80cc39 | ||
|
|
a51a871b70 | ||
|
|
f686420e1e | ||
|
|
2237a84d88 | ||
|
|
5c00d28396 | ||
|
|
2b0cff358e | ||
|
|
c3b7f9d050 | ||
|
|
a7fce331a0 | ||
|
|
6dd8573584 | ||
|
|
124418dc7a | ||
|
|
bf3ee15562 | ||
|
|
48fac7f86c |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
.png filter=lfs diff=lfs merge=lfs -text
|
||||
.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -0,0 +1,29 @@
|
||||
This is a machine control software project.
|
||||
|
||||
## Repository structure
|
||||
|
||||
### Frontend
|
||||
|
||||
`/electron`: Frontend code for the control software built with React and Electron.
|
||||
|
||||
### Backend
|
||||
|
||||
`/server`: Implements machine setup & loop. Exposes a REST & Socket.io API for the electron frontend.
|
||||
`/ethercat-hal`: Hardware abstraction layer for Beckhoff (and possibly other EtherCat) devices and protocols.
|
||||
`/ethercat-hal-derive`: Macros for ethercat-hal
|
||||
`/control-core`: Generic logic for the server.
|
||||
|
||||
### HMI Panel Operating System
|
||||
|
||||
`/nixos`: Custom Linux with realtime kernel & preconfigured for touchscreens.
|
||||
|
||||
### Other
|
||||
|
||||
/docs: Documentation for the project.
|
||||
|
||||
## Key Guidelines
|
||||
|
||||
1. Follow Rust & Typescript best practices and idiomatic patterns
|
||||
2. Maintain existing code structure and organization
|
||||
3. Write unit tests for new functionality where applicable.
|
||||
4. Document public APIs and complex logic in code. Suggest changes to the `docs/` folder when appropriate
|
||||
@@ -1,16 +1,19 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/electron" # Location of package manifests
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/electron"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-minor"
|
||||
|
||||
- package-ecosystem: "cargo" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-minor"
|
||||
@@ -36,6 +36,9 @@ jobs:
|
||||
- name: Install
|
||||
run: npm i
|
||||
|
||||
- name: Build
|
||||
run: tsc
|
||||
|
||||
- name: Format
|
||||
run: npm run format
|
||||
|
||||
@@ -45,5 +48,5 @@ jobs:
|
||||
- name: Test
|
||||
run: npm run test
|
||||
|
||||
- name: Build
|
||||
- name: Package
|
||||
run: npm run package
|
||||
@@ -21,6 +21,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set Rust version
|
||||
run: rustup override set ${{ env.RUST_VERSION }}
|
||||
- name: Install Formatter
|
||||
run: rustup component add rustfmt
|
||||
- name: Cache Rust dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@@ -35,7 +37,12 @@ jobs:
|
||||
${{ runner.os }}-${{ env.RUST_VERSION }}-cargo-
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
RUSTFLAGS: "-D warnings"
|
||||
run: cargo build --verbose
|
||||
|
||||
- name: Format
|
||||
run: cargo fmt --check --verbose
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
@@ -0,0 +1,49 @@
|
||||
# Canonical identities on the left, aliases on the right.
|
||||
# Format options:
|
||||
# Canon Name <canon@example.com> <alias@example.com>
|
||||
# Canon Name <canon@example.com> Alias Name <alias@example.com>
|
||||
|
||||
# Icedsoda -> Philipp
|
||||
Philipp <29167146+1cedsoda@users.noreply.github.com> <29167146+1cedsoda@users.noreply.github.com>
|
||||
|
||||
# kraemr / Robin Krämer -> Robin
|
||||
Robin <93094049+kraemr@users.noreply.github.com> <93094049+kraemr@users.noreply.github.com>
|
||||
Robin <93094049+kraemr@users.noreply.github.com> Robin Krämer <93094049+kraemr@users.noreply.github.com>
|
||||
|
||||
# Oshgnacknak -> Josh
|
||||
Josh <osh@oshgnacknak.de> <osh@oshgnacknak.de>
|
||||
|
||||
# Milan
|
||||
Milan <61654753+MilanVDB@users.noreply.github.com>
|
||||
|
||||
# dependabot stays as bot
|
||||
Dependabot <49699333+dependabot[bot]@users.noreply.github.com> <49699333+dependabot[bot]@users.noreply.github.com>
|
||||
|
||||
# Medformatik / Florian -> Florian
|
||||
Florian <dev@fwcr.de> <dev@fwcr.de>
|
||||
Florian <dev@fwcr.de> Medformatik <dev@fwcr.de>
|
||||
|
||||
# Anatomik5 -> Alisher (merge noreply + gmail under Alisher)
|
||||
Alisher <115714693+Anatomik5@users.noreply.github.com> <115714693+Anatomik5@users.noreply.github.com>
|
||||
Alisher <115714693+Anatomik5@users.noreply.github.com> Anatomik5 <darmenovalisher@gmail.com>
|
||||
Alisher <115714693+Anatomik5@users.noreply.github.com> Darmenov Alisher <115714693+Anatomik5@users.noreply.github.com>
|
||||
|
||||
# snacj -> Melvin
|
||||
Melvin <0xsnacj@proton.me> <0xsnacj@proton.me>
|
||||
Melvin <0xsnacj@proton.me> snacj <bbmelvin@gmx.de>
|
||||
Melvin <0xsnacj@proton.me> Melvin Spieß <0xsnacj@proton.me>
|
||||
|
||||
# Janhegerq -> Jan
|
||||
Jan <219994445+Janhegerq@users.noreply.github.com> <219994445+Janhegerq@users.noreply.github.com>
|
||||
Jan <219994445+Janhegerq@users.noreply.github.com> <19994445+Janhegerq@users.noreply.github.com>
|
||||
|
||||
# Christian / TheBest6337 -> christian
|
||||
Christian <christian.zschoegner@outlook.de> <christian.zschoegner@outlook.de>
|
||||
Christian <christian.zschoegner@outlook.de> TheBest6337 <christian.zschoegner@outlook.de>
|
||||
Christian <christian.zschoegner@outlook.de> Christian <80114847+TheBest6337@users.noreply.github.com>
|
||||
|
||||
# nixos-install.sh
|
||||
nixos-install.sh <nixos-install.sh@localhost> <nixos-install.sh@localhost>
|
||||
|
||||
# copilot-swe-agent bot
|
||||
Copilot <198982749+Copilot@users.noreply.github.com> <198982749+Copilot@users.noreply.github.com>
|
||||
@@ -1,3 +1,60 @@
|
||||
# `2.6.0`
|
||||
_03.09.2025_
|
||||
|
||||
## Extruder V2
|
||||
- [#658](https://github.com/qitechgmbh/control/pull/658) Added manual
|
||||
- [#639](https://github.com/qitechgmbh/control/pull/639) Swapped order of screw speed regulation buttons
|
||||
|
||||
|
||||
## Laser V1
|
||||
- [#642](https://github.com/qitechgmbh/control/pull/642) Improved stability of laser connection
|
||||
|
||||
## General
|
||||
- [#657](https://github.com/qitechgmbh/control/pull/657) Time frame for graphs is now remembered
|
||||
- [#641](https://github.com/qitechgmbh/control/pull/641) Removed duplicate scrollbar from manuals
|
||||
|
||||
# `2.5.0`
|
||||
_27.08.2025_
|
||||
|
||||
## Extruder V2
|
||||
- [#595](https://github.com/qitechgmbh/control/pull/600) Simplified power graphs
|
||||
- [#593](https://github.com/qitechgmbh/control/pull/599) Added identifiers for temperature sensors in temperature graph
|
||||
|
||||
## Winder V2
|
||||
- [#598](https://github.com/qitechgmbh/control/pull/601) Images now load properly in the manual
|
||||
|
||||
## General
|
||||
- [#540](https://github.com/qitechgmbh/control/pull/592) Improved readability of the presets
|
||||
- [#591](https://github.com/qitechgmbh/control/pull/626) Significantly improved user interface performance and stability
|
||||
|
||||
# `2.4.0`
|
||||
_14.08.2025_
|
||||
|
||||
## Winder V2
|
||||
- [#550](https://github.com/qitechgmbh/control/pull/551) Can now reset wound distance while winding
|
||||
|
||||
## General
|
||||
- [#568](https://github.com/qitechgmbh/control/pull/569),[#572](https://github.com/qitechgmbh/control/pull/572), [#574](https://github.com/qitechgmbh/control/pull/587) Improved EtherCAT performance
|
||||
- [#588](https://github.com/qitechgmbh/control/pull/589) Improved frontend performance
|
||||
|
||||
# `2.3.0`
|
||||
_06.08.2025_
|
||||
|
||||
## Extruder V2
|
||||
- [#520](https://github.com/qitechgmbh/control/pull/523) added warning message incase inverter is overloaded
|
||||
- [#501](https://github.com/qitechgmbh/control/pull/508) raised pressure limit of pressured PID mode for extruder
|
||||
- [#519](https://github.com/qitechgmbh/control/pull/549) added total power consumption monitor and graphs for voltage,current and wattage of the motor
|
||||
|
||||
## Winder V2
|
||||
- [#64](https://github.com/qitechgmbh/control/pull/504) added winder handbook
|
||||
|
||||
## Laser V1
|
||||
- [#497](https://github.com/qitechgmbh/control/pull/498) improved diameter visualisation
|
||||
|
||||
## General
|
||||
- [#62](https://github.com/qitechgmbh/control/pull/514),[#524](https://github.com/qitechgmbh/control/pull/528) added machine presets to load configurations
|
||||
- [#526](https://github.com/qitechgmbh/control/pull/541) added import and export of presets
|
||||
|
||||
# `2.2.0`
|
||||
|
||||
_15.07.2025_
|
||||
|
||||
@@ -99,9 +99,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
@@ -256,7 +256,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -273,7 +273,7 @@ checksum = "99e1aca718ea7b89985790c94aad72d77533063fe00bc497bb79a7c2dae6a661"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -345,7 +345,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -539,7 +539,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -581,6 +581,7 @@ dependencies = [
|
||||
"approx",
|
||||
"axum",
|
||||
"bitvec",
|
||||
"control_core_derive",
|
||||
"core_affinity",
|
||||
"crc",
|
||||
"erased-serde",
|
||||
@@ -599,6 +600,15 @@ 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"
|
||||
@@ -728,7 +738,7 @@ dependencies = [
|
||||
"deluxe-macros",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -741,7 +751,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -756,7 +766,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -767,7 +777,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -804,7 +814,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1019,7 +1029,7 @@ dependencies = [
|
||||
"deluxe",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1071,7 +1081,7 @@ dependencies = [
|
||||
"criterion",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1200,7 +1210,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1736,9 +1746,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
version = "0.2.175"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -1815,11 +1825,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1943,12 +1953,11 @@ checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
version = "0.50.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2076,12 +2085,6 @@ 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"
|
||||
@@ -2175,7 +2178,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2206,7 +2209,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2342,7 +2345,7 @@ dependencies = [
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2374,9 +2377,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
@@ -2412,23 +2415,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"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",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2439,15 +2433,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -2582,7 +2570,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2602,14 +2590,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
version = "1.0.143"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -2683,9 +2671,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serialport"
|
||||
version = "4.7.1"
|
||||
version = "4.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2daa7abb9b965493e3c8f4184c6f46435484ff2538a332b886788cf6768b927b"
|
||||
checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"cfg-if",
|
||||
@@ -2709,6 +2697,7 @@ dependencies = [
|
||||
"axum",
|
||||
"bitvec",
|
||||
"control_core",
|
||||
"control_core_derive",
|
||||
"crc",
|
||||
"dhat",
|
||||
"ethercat_hal",
|
||||
@@ -2723,7 +2712,6 @@ dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serial",
|
||||
"serialport",
|
||||
"smol",
|
||||
"socketioxide",
|
||||
@@ -2856,9 +2844,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "socketioxide"
|
||||
version = "0.17.1"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85fe8a90fd0cdac0bcb7b3c40c1084f5658f8737c62dfb277f5620cf78f3849a"
|
||||
checksum = "476190583b592f1e3d55584269600f2d3c4f18af36adad03c41c27e82dcb6bd5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"engineioxide",
|
||||
@@ -2963,9 +2951,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
version = "2.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2989,7 +2977,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3034,7 +3022,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3045,16 +3033,16 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
|
||||
|
||||
[[package]]
|
||||
name = "thread-priority"
|
||||
version = "1.2.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe075d7053dae61ac5413a34ea7d4913b6e6207844fd726bdd858b37ff72bf5"
|
||||
checksum = "cd4ef372b29fbcc6cb0cef97bcbf3ffa9da90e02eb23dbf14db48ce30d11d2d5"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"winapi",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3129,9 +3117,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.0"
|
||||
version = "1.45.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -3151,7 +3139,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3313,7 +3301,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3378,15 +3366,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
@@ -3570,7 +3558,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3605,7 +3593,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -3670,6 +3658,28 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-link",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
@@ -3683,6 +3693,17 @@ dependencies = [
|
||||
"windows-strings 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
@@ -3691,7 +3712,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3702,14 +3723,24 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
@@ -3812,6 +3843,15 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
@@ -3991,7 +4031,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4012,7 +4052,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4032,7 +4072,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4066,5 +4106,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"syn 2.0.105",
|
||||
]
|
||||
@@ -4,7 +4,7 @@ members = [
|
||||
"ethercat-hal",
|
||||
"ethercat-hal-derive",
|
||||
"control-core",
|
||||
"ethercat-eeprom-dump",
|
||||
"ethercat-eeprom-dump", "control-core-derive",
|
||||
]
|
||||
exclude = []
|
||||
resolver = "2"
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
QiTech Control is an industrial control panel software for the next generation of QiTech recycling machines built on top of Beckhoff Automation hardware.
|
||||
|
||||
[](https://www.youtube.com/watch?v=KI3YeBwfV-s)
|
||||
*Click here to watch a video demo of our software.*
|
||||
|
||||
# Repo Structure
|
||||
|
||||
Frontend
|
||||
@@ -41,6 +44,8 @@ We could combine the code of the frontend and backend using [Doxius](https://dio
|
||||
|
||||
# Dev Setup
|
||||
|
||||
[Developer Documentation](./docs/developer-docs/)
|
||||
|
||||
## Backend
|
||||
|
||||
- Rust stable 1.86^ toolchain (install via [rustup](https://rustup.rs/))
|
||||
@@ -66,7 +71,6 @@ We could combine the code of the frontend and backend using [Doxius](https://dio
|
||||
| Laser | V1 | ??? | Diameter Measuring Laser | - | 1 (Qitech Industries GmbH) | 6 | Not Yet | |
|
||||
| Mock | - | ??? | Mock Machine for Testing | - | 1 (Qitech Industries GmbH) | 7 | Not Yet | - |
|
||||
|
||||
|
||||
# More Docs
|
||||
|
||||
- [x] [Architecture & Data Flow](./docs/architecture-overview.md)
|
||||
@@ -90,8 +94,9 @@ We could combine the code of the frontend and backend using [Doxius](https://dio
|
||||
- Write Device Identification
|
||||
|
||||
- Server
|
||||
- [X] [Threading](./docs/control-loop.md)
|
||||
- [X] [Logging](./docs/logging.md)
|
||||
|
||||
- [x] [Threading](./docs/control-loop.md)
|
||||
- [x] [Logging](./docs/logging.md)
|
||||
- [ ] Control Loop Setup
|
||||
- Control Loop Thread
|
||||
- [ ] realtime
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ $1 == "release" ]; then
|
||||
set -e
|
||||
|
||||
FEATURE=""
|
||||
|
||||
# Check if "mock-machine" was passed as an argument
|
||||
if [[ "$1" == "mock-machine" ]]; then
|
||||
echo "Building Debug Code with mock-machine feature"
|
||||
FEATURE="--features mock-machine"
|
||||
fi
|
||||
|
||||
if [[ "$1" == "release" ]]; then
|
||||
echo "building Release Code"
|
||||
cargo build --release
|
||||
else
|
||||
echo "building Debug Code"
|
||||
cargo build
|
||||
echo "Building Debug Code"
|
||||
cargo build $FEATURE
|
||||
fi
|
||||
# set cap for server executable
|
||||
sudo setcap cap_net_raw=eip ./target/debug/server
|
||||
|
||||
# Only setcap if not building with mock-machine
|
||||
if [ "$1" != "mock-machine" ]; then
|
||||
echo "Setting capabilities for server executable"
|
||||
sudo setcap 'cap_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
|
||||
@@ -0,0 +1,12 @@
|
||||
[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"] }
|
||||
@@ -0,0 +1,31 @@
|
||||
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)
|
||||
}
|
||||
@@ -4,16 +4,16 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.98"
|
||||
anyhow = "1.0.99"
|
||||
axum = "0.8.4"
|
||||
ethercat_hal = { path = "../ethercat-hal" }
|
||||
ethercrab = "0.6"
|
||||
interfaces = "0.0.9"
|
||||
serde = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
serialport = "4.7.1"
|
||||
serde_json = "1.0.143"
|
||||
serialport = "4.7.3"
|
||||
smol = "2.0.2"
|
||||
socketioxide = "0.17.1"
|
||||
socketioxide = "0.17.2"
|
||||
uom = "0.36.0"
|
||||
bitvec = { version = "1.0.1", features = ["alloc"] }
|
||||
crc = "3.3.0"
|
||||
@@ -21,9 +21,11 @@ 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.172"
|
||||
libc = "0.2.175"
|
||||
|
||||
[dev-dependencies]
|
||||
approx = "0.5.1"
|
||||
|
||||
@@ -44,10 +44,10 @@ impl AccelerationSpeedController {
|
||||
/// No speed limits are applied.
|
||||
pub fn new_simple(max_acceleration: f64, initial_speed: f64) -> Self {
|
||||
Self::new(
|
||||
None, // min_speed
|
||||
None, // max_speed
|
||||
-max_acceleration, // min_acceleration (deceleration)
|
||||
max_acceleration, // max_acceleration
|
||||
None, // min_speed
|
||||
None, // max_speed
|
||||
-max_acceleration, // min_acceleration (deceleration)
|
||||
max_acceleration, // max_acceleration
|
||||
initial_speed,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -37,12 +37,15 @@ impl AngularAccelerationSpeedController {
|
||||
/// Creates a new angular acceleration speed controller with simplified parameters.
|
||||
/// Sets min_acceleration to -max_acceleration for symmetric behavior.
|
||||
/// No speed limits are applied.
|
||||
pub fn new_simple(max_acceleration: AngularAcceleration, initial_speed: AngularVelocity) -> Self {
|
||||
pub fn new_simple(
|
||||
max_acceleration: AngularAcceleration,
|
||||
initial_speed: AngularVelocity,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
None, // min_speed
|
||||
None, // max_speed
|
||||
-max_acceleration, // min_acceleration (deceleration)
|
||||
max_acceleration, // max_acceleration
|
||||
None, // min_speed
|
||||
None, // max_speed
|
||||
-max_acceleration, // min_acceleration (deceleration)
|
||||
max_acceleration, // max_acceleration
|
||||
initial_speed,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ impl LinearAccelerationLimitingController {
|
||||
/// No speed limits are applied.
|
||||
pub fn new_simple(acceleration: Acceleration, initial_speed: Velocity) -> Self {
|
||||
Self::new(
|
||||
None, // min_speed
|
||||
None, // max_speed
|
||||
acceleration, // acceleration
|
||||
-acceleration, // deceleration (negative for deceleration)
|
||||
None, // min_speed
|
||||
None, // max_speed
|
||||
acceleration, // acceleration
|
||||
-acceleration, // deceleration (negative for deceleration)
|
||||
initial_speed,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
//! # Circular Converter
|
||||
//!
|
||||
//! This module provides the `CircularConverter` which handles transformations between
|
||||
//! linear and angular measurements for circular motion systems.
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! The `CircularConverter` is designed to work alongside other converters:
|
||||
//! - Use `AngularStepConverter` for angular-to-step conversions
|
||||
//! - Use `LinearStepConverter` for complete linear-to-step conversions (uses `CircularConverter` internally)
|
||||
//! - Use `CircularConverter` directly when you need linear-to-angular conversions without steps
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! use control_core::converters::circular_converter::CircularConverter;
|
||||
//! use 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,14 +1,11 @@
|
||||
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
|
||||
///
|
||||
@@ -18,53 +15,48 @@ use super::angular_step_converter::AngularStepConverter;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LinearStepConverter {
|
||||
angular_step_converter: AngularStepConverter,
|
||||
radius: Length,
|
||||
circumference: Length,
|
||||
circular_converter: CircularConverter,
|
||||
}
|
||||
|
||||
// Constructor and basic getters
|
||||
impl LinearStepConverter {
|
||||
/// Create a new converter from radius and steps per revolution
|
||||
pub fn from_radius(steps_per_revolution: i16, radius: Length) -> Self {
|
||||
let circumference =
|
||||
Length::new::<meter>(2.0 * std::f64::consts::PI * radius.get::<meter>());
|
||||
Self {
|
||||
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
|
||||
radius,
|
||||
circumference,
|
||||
circular_converter: CircularConverter::from_radius(radius),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new converter from diameter and steps per revolution
|
||||
pub fn from_diameter(steps_per_revolution: i16, diameter: Length) -> Self {
|
||||
let radius = diameter / 2.0;
|
||||
Self::from_radius(steps_per_revolution, radius)
|
||||
Self {
|
||||
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
|
||||
circular_converter: CircularConverter::from_diameter(diameter),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new converter from circumference and steps per revolution
|
||||
pub fn from_circumference(steps_per_revolution: i16, circumference: Length) -> Self {
|
||||
let radius =
|
||||
Length::new::<meter>(circumference.get::<meter>() / (2.0 * std::f64::consts::PI));
|
||||
Self {
|
||||
angular_step_converter: AngularStepConverter::new(steps_per_revolution),
|
||||
radius,
|
||||
circumference,
|
||||
circular_converter: CircularConverter::from_circumference(circumference),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the radius used by the converter
|
||||
pub fn radius(&self) -> Length {
|
||||
self.radius
|
||||
self.circular_converter.radius()
|
||||
}
|
||||
|
||||
/// Get the diameter of the system
|
||||
pub fn diameter(&self) -> Length {
|
||||
self.radius * 2.0
|
||||
self.circular_converter.diameter()
|
||||
}
|
||||
|
||||
/// Get the circumference of the system
|
||||
pub fn circumference(&self) -> Length {
|
||||
self.circumference
|
||||
self.circular_converter.circumference()
|
||||
}
|
||||
|
||||
/// Get the steps per revolution
|
||||
@@ -79,8 +71,10 @@ impl LinearStepConverter {
|
||||
///
|
||||
/// Formula: steps = (distance / circumference) * steps_per_revolution
|
||||
pub fn distance_to_steps(&self, distance: Length) -> f64 {
|
||||
// Convert distance to angle: angle = distance/circumference (in revolutions)
|
||||
let revolutions = distance.get::<meter>() / self.circumference.get::<meter>();
|
||||
// Convert distance to revolutions using CircularConverter
|
||||
let revolutions = self
|
||||
.circular_converter
|
||||
.linear_distance_to_revolutions(distance);
|
||||
let angle = Angle::new::<revolution>(revolutions);
|
||||
|
||||
// Convert angle to steps
|
||||
@@ -94,21 +88,19 @@ impl LinearStepConverter {
|
||||
// Convert steps to angle
|
||||
let angle = self.angular_step_converter.steps_to_angle(steps);
|
||||
|
||||
// Convert angle to distance: distance = angle * circumference
|
||||
// Convert angle to distance using CircularConverter
|
||||
let revolutions = angle.get::<revolution>();
|
||||
let distance = revolutions * self.circumference.get::<meter>();
|
||||
|
||||
Length::new::<meter>(distance)
|
||||
self.circular_converter
|
||||
.revolutions_to_linear_distance(revolutions)
|
||||
}
|
||||
|
||||
/// Convert linear velocity to steps/second
|
||||
///
|
||||
/// Formula: steps/second = (velocity / circumference) * steps_per_revolution
|
||||
pub fn velocity_to_steps(&self, velocity: Velocity) -> f64 {
|
||||
// Convert linear velocity to angular velocity: ω = v/circumference (in rev/s)
|
||||
let linear_velocity = velocity.get::<meter_per_second>();
|
||||
let angular_velocity_rps = linear_velocity / self.circumference.get::<meter>();
|
||||
let angular_velocity = AngularVelocity::new::<revolution_per_second>(angular_velocity_rps);
|
||||
// Convert linear velocity to revolutions per second using CircularConverter
|
||||
let rps = self.circular_converter.linear_velocity_to_rps(velocity);
|
||||
let angular_velocity = AngularVelocity::new::<revolution_per_second>(rps);
|
||||
|
||||
// Convert angular velocity to steps/second
|
||||
self.angular_step_converter
|
||||
@@ -124,23 +116,19 @@ impl LinearStepConverter {
|
||||
.angular_step_converter
|
||||
.steps_to_angular_velocity(steps_per_second);
|
||||
|
||||
// Convert angular velocity to linear velocity: v = ω * circumference
|
||||
let angular_velocity_rps = angular_velocity.get::<revolution_per_second>();
|
||||
let linear_velocity = angular_velocity_rps * self.circumference.get::<meter>();
|
||||
|
||||
Velocity::new::<meter_per_second>(linear_velocity)
|
||||
// Convert angular velocity to linear velocity using CircularConverter
|
||||
let rps = angular_velocity.get::<revolution_per_second>();
|
||||
self.circular_converter.rps_to_linear_velocity(rps)
|
||||
}
|
||||
|
||||
/// Convert linear acceleration to steps/second²
|
||||
///
|
||||
/// Formula: steps/second² = (acceleration / radius) * (steps_per_revolution / (2π))
|
||||
pub fn acceleration_to_steps(&self, acceleration: Acceleration) -> f64 {
|
||||
// Convert linear acceleration to angular acceleration: α = a/r
|
||||
let linear_acceleration = acceleration.get::<meter_per_second_squared>();
|
||||
// Calculate angular acceleration in radians per second squared
|
||||
let angular_acceleration_rad_per_s2 = linear_acceleration / self.radius.get::<meter>();
|
||||
let angular_acceleration =
|
||||
AngularAcceleration::new::<radian_per_second_squared>(angular_acceleration_rad_per_s2);
|
||||
// Convert linear acceleration to angular acceleration using CircularConverter
|
||||
let angular_acceleration = self
|
||||
.circular_converter
|
||||
.linear_to_angular_acceleration(acceleration);
|
||||
|
||||
// Convert angular acceleration to steps/second²
|
||||
self.angular_step_converter
|
||||
@@ -156,12 +144,9 @@ impl LinearStepConverter {
|
||||
.angular_step_converter
|
||||
.steps_to_angular_acceleration(steps_per_second_squared);
|
||||
|
||||
// Convert angular acceleration to linear acceleration: a = α * r
|
||||
let angular_acceleration_rad_per_s2 =
|
||||
angular_acceleration.get::<radian_per_second_squared>();
|
||||
let linear_acceleration = angular_acceleration_rad_per_s2 * self.radius.get::<meter>();
|
||||
|
||||
Acceleration::new::<meter_per_second_squared>(linear_acceleration)
|
||||
// Convert angular acceleration to linear acceleration using CircularConverter
|
||||
self.circular_converter
|
||||
.angular_to_linear_acceleration(angular_acceleration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +156,9 @@ impl LinearStepConverter {
|
||||
///
|
||||
/// Formula: angle (in revolutions) = distance / circumference
|
||||
pub fn distance_to_angle(&self, distance: Length) -> Angle {
|
||||
let revolutions = distance.get::<meter>() / self.circumference.get::<meter>();
|
||||
let revolutions = self
|
||||
.circular_converter
|
||||
.linear_distance_to_revolutions(distance);
|
||||
Angle::new::<revolution>(revolutions)
|
||||
}
|
||||
|
||||
@@ -180,26 +167,24 @@ impl LinearStepConverter {
|
||||
/// Formula: distance = angle (in revolutions) * circumference
|
||||
pub fn angle_to_distance(&self, angle: Angle) -> Length {
|
||||
let revolutions = angle.get::<revolution>();
|
||||
let distance = revolutions * self.circumference.get::<meter>();
|
||||
Length::new::<meter>(distance)
|
||||
self.circular_converter
|
||||
.revolutions_to_linear_distance(revolutions)
|
||||
}
|
||||
|
||||
/// Convert linear velocity to angular velocity
|
||||
///
|
||||
/// Formula: angular velocity (in rev/s) = velocity / circumference
|
||||
pub fn velocity_to_angular_velocity(&self, velocity: Velocity) -> AngularVelocity {
|
||||
let linear_velocity = velocity.get::<meter_per_second>();
|
||||
let angular_velocity_rps = linear_velocity / self.circumference.get::<meter>();
|
||||
AngularVelocity::new::<revolution_per_second>(angular_velocity_rps)
|
||||
let rps = self.circular_converter.linear_velocity_to_rps(velocity);
|
||||
AngularVelocity::new::<revolution_per_second>(rps)
|
||||
}
|
||||
|
||||
/// Convert angular velocity to linear velocity
|
||||
///
|
||||
/// Formula: velocity = angular velocity (in rev/s) * circumference
|
||||
pub fn angular_velocity_to_velocity(&self, angular_velocity: AngularVelocity) -> Velocity {
|
||||
let angular_velocity_rps = angular_velocity.get::<revolution_per_second>();
|
||||
let linear_velocity = angular_velocity_rps * self.circumference.get::<meter>();
|
||||
Velocity::new::<meter_per_second>(linear_velocity)
|
||||
let rps = angular_velocity.get::<revolution_per_second>();
|
||||
self.circular_converter.rps_to_linear_velocity(rps)
|
||||
}
|
||||
|
||||
/// Convert linear acceleration to angular acceleration
|
||||
@@ -209,9 +194,8 @@ impl LinearStepConverter {
|
||||
&self,
|
||||
acceleration: Acceleration,
|
||||
) -> AngularAcceleration {
|
||||
let linear_acceleration = acceleration.get::<meter_per_second_squared>();
|
||||
let angular_acceleration_rad_per_s2 = linear_acceleration / self.radius.get::<meter>();
|
||||
AngularAcceleration::new::<radian_per_second_squared>(angular_acceleration_rad_per_s2)
|
||||
self.circular_converter
|
||||
.linear_to_angular_acceleration(acceleration)
|
||||
}
|
||||
|
||||
/// Convert angular acceleration to linear acceleration
|
||||
@@ -221,10 +205,8 @@ impl LinearStepConverter {
|
||||
&self,
|
||||
angular_acceleration: AngularAcceleration,
|
||||
) -> Acceleration {
|
||||
let angular_acceleration_rad_per_s2 =
|
||||
angular_acceleration.get::<radian_per_second_squared>();
|
||||
let linear_acceleration = angular_acceleration_rad_per_s2 * self.radius.get::<meter>();
|
||||
Acceleration::new::<meter_per_second_squared>(linear_acceleration)
|
||||
self.circular_converter
|
||||
.angular_to_linear_acceleration(angular_acceleration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,6 +251,10 @@ mod tests {
|
||||
use super::*;
|
||||
use approx::assert_relative_eq;
|
||||
use std::f64::EPSILON;
|
||||
use uom::si::{
|
||||
acceleration::meter_per_second_squared, angular_acceleration::radian_per_second_squared,
|
||||
length::meter, velocity::meter_per_second,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod angle_converter;
|
||||
pub mod angular_step_converter;
|
||||
pub mod circular_converter;
|
||||
pub mod linear_step_converter;
|
||||
pub mod motor_converter;
|
||||
pub mod transmission_converter;
|
||||
@@ -1,22 +0,0 @@
|
||||
use uom::si::f64::AngularVelocity;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TransmissionConverter {
|
||||
pub transmission_ratio: f64,
|
||||
}
|
||||
|
||||
impl TransmissionConverter {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
transmission_ratio: 34.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate_screw_output_rpm(&self, rpm: AngularVelocity) -> AngularVelocity {
|
||||
rpm / self.transmission_ratio
|
||||
}
|
||||
|
||||
pub fn calculate_screw_input_rpm(&self, screw_rpm: AngularVelocity) -> AngularVelocity {
|
||||
screw_rpm * self.transmission_ratio
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@ 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,
|
||||
};
|
||||
@@ -13,8 +15,10 @@ 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,
|
||||
};
|
||||
@@ -228,17 +232,19 @@ pub fn get_identification_addresses<'maindevice>(
|
||||
EK1100_IDENTITY_A => MachineIdentificationAddresses::default(),
|
||||
EL1002_IDENTITY_A => MachineIdentificationAddresses::default(),
|
||||
EL1008_IDENTITY_A => MachineIdentificationAddresses::default(),
|
||||
EL2002_IDENTITY_A => MachineIdentificationAddresses::default(),
|
||||
EL2002_IDENTITY_A | EL2002_IDENTITY_B => MachineIdentificationAddresses::default(),
|
||||
EL2004_IDENTITY_A => MachineIdentificationAddresses::default(),
|
||||
EL3204_IDENTITY_A | EL3204_IDENTITY_B => MachineIdentificationAddresses::default(),
|
||||
EL2008_IDENTITY_A => MachineIdentificationAddresses::default(),
|
||||
EL2008_IDENTITY_A | EL2008_IDENTITY_B => 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(),
|
||||
EL3024_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(),
|
||||
EL7031_IDENTITY_A | EL7031_IDENTITY_B => MachineIdentificationAddresses::default(),
|
||||
EL7031_0030_IDENTITY_A => MachineIdentificationAddresses::default(),
|
||||
EL7041_0052_IDENTITY_A => MachineIdentificationAddresses::default(),
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
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,4 +1,5 @@
|
||||
pub mod compare_lists;
|
||||
pub mod hasher_serializer;
|
||||
pub mod hashing;
|
||||
pub mod interpolation;
|
||||
pub mod loop_trottle;
|
||||
|
||||
@@ -56,7 +56,7 @@ impl DivideByCount for u64 {
|
||||
/// let mut window = MovingTimeWindow::new(Duration::from_secs(10), 100);
|
||||
/// window.update(1.5, Instant::now());
|
||||
/// window.update(2.5, Instant::now());
|
||||
///
|
||||
///
|
||||
/// let average = window.average();
|
||||
/// println!("Average: {}", average);
|
||||
/// ```
|
||||
|
||||
@@ -8,6 +8,7 @@ pub mod realtime;
|
||||
pub mod rest;
|
||||
pub mod serial;
|
||||
pub mod socketio;
|
||||
pub mod transmission;
|
||||
pub mod uom_extensions;
|
||||
|
||||
#[macro_use]
|
||||
|
||||
@@ -12,7 +12,10 @@ use crate::{
|
||||
serial::SerialDevice,
|
||||
socketio::event::GenericEvent,
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
use super::{
|
||||
Machine,
|
||||
@@ -23,9 +26,9 @@ use super::{
|
||||
#[derive(Debug)]
|
||||
pub struct MachineManager {
|
||||
pub ethercat_machines:
|
||||
HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
|
||||
HashMap<MachineIdentificationUnique, Result<Arc<Mutex<dyn Machine>>, anyhow::Error>>,
|
||||
pub serial_machines:
|
||||
HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
|
||||
HashMap<MachineIdentificationUnique, Result<Arc<Mutex<dyn Machine>>, anyhow::Error>>,
|
||||
}
|
||||
|
||||
impl MachineManager {
|
||||
@@ -42,6 +45,7 @@ impl MachineManager {
|
||||
machine_registry: &MachineRegistry,
|
||||
hardware: &MachineNewHardwareEthercat,
|
||||
socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
|
||||
machine_manager: Weak<RwLock<MachineManager>>,
|
||||
) {
|
||||
// empty ethercat machines
|
||||
self.ethercat_machines.clear();
|
||||
@@ -69,6 +73,7 @@ impl MachineManager {
|
||||
device_group,
|
||||
hardware: &machine_new_hardware,
|
||||
socket_queue_tx: socket_queue_tx.clone(),
|
||||
machine_manager: machine_manager.clone(),
|
||||
});
|
||||
|
||||
// insert the machine into the ethercat machines map
|
||||
@@ -82,7 +87,7 @@ impl MachineManager {
|
||||
pub fn get(
|
||||
&self,
|
||||
machine_identification: &MachineIdentificationUnique,
|
||||
) -> Option<&Result<Box<Mutex<dyn Machine>>, anyhow::Error>> {
|
||||
) -> Option<&Result<Arc<Mutex<dyn Machine>>, anyhow::Error>> {
|
||||
self.ethercat_machines
|
||||
.get(machine_identification)
|
||||
.or_else(|| self.serial_machines.get(machine_identification))
|
||||
@@ -94,6 +99,7 @@ impl MachineManager {
|
||||
device: Arc<RwLock<dyn SerialDevice>>,
|
||||
machine_registry: &MachineRegistry,
|
||||
socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
|
||||
machine_manager: Weak<RwLock<MachineManager>>,
|
||||
) {
|
||||
let hardware = MachineNewHardwareSerial { device };
|
||||
|
||||
@@ -107,6 +113,7 @@ impl MachineManager {
|
||||
device_group: &vec![device_identification_identified.clone()],
|
||||
hardware: &MachineNewHardware::Serial(&hardware),
|
||||
socket_queue_tx,
|
||||
machine_manager: machine_manager.clone(),
|
||||
});
|
||||
|
||||
tracing::info!("Adding serial machine {:?}", new_machine);
|
||||
@@ -137,6 +144,48 @@ impl MachineManager {
|
||||
.machine_identification_unique,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_machine_weak(
|
||||
&self,
|
||||
machine_identification: &MachineIdentificationUnique,
|
||||
) -> Option<Weak<Mutex<dyn Machine>>> {
|
||||
let machine = self.ethercat_machines.get(machine_identification);
|
||||
let machine = match machine {
|
||||
Some(machine) => machine,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let machine = match machine {
|
||||
Ok(machine) => machine,
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let machine_weak = Arc::downgrade(machine);
|
||||
return Some(machine_weak);
|
||||
}
|
||||
|
||||
pub fn get_serial_weak(
|
||||
&self,
|
||||
machine_identification: &MachineIdentificationUnique,
|
||||
) -> Option<Weak<Mutex<dyn Machine>>> {
|
||||
let machine = self.serial_machines.get(machine_identification);
|
||||
let machine = match machine {
|
||||
Some(machine) => machine,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let machine = match machine {
|
||||
Ok(machine) => machine,
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let machine_weak = Arc::downgrade(machine);
|
||||
return Some(machine_weak);
|
||||
}
|
||||
}
|
||||
|
||||
/// Groups devices by machine identification
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use smol::lock::Mutex;
|
||||
|
||||
@@ -9,14 +9,14 @@ pub enum MachineManagerIter<'a> {
|
||||
iter: std::collections::hash_map::Iter<
|
||||
'a,
|
||||
MachineIdentificationUnique,
|
||||
Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
|
||||
Result<Arc<Mutex<dyn Machine>>, anyhow::Error>,
|
||||
>,
|
||||
},
|
||||
SerialMachines {
|
||||
iter: std::collections::hash_map::Iter<
|
||||
'a,
|
||||
MachineIdentificationUnique,
|
||||
Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
|
||||
Result<Arc<Mutex<dyn Machine>>, anyhow::Error>,
|
||||
>,
|
||||
},
|
||||
Done,
|
||||
@@ -25,13 +25,13 @@ pub enum MachineManagerIter<'a> {
|
||||
pub struct MachineManagerIterator<'a> {
|
||||
iter: MachineManagerIter<'a>,
|
||||
serial_machines:
|
||||
&'a HashMap<MachineIdentificationUnique, Result<Box<Mutex<dyn Machine>>, anyhow::Error>>,
|
||||
&'a HashMap<MachineIdentificationUnique, Result<Arc<Mutex<dyn Machine>>, anyhow::Error>>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for MachineManagerIterator<'a> {
|
||||
type Item = (
|
||||
&'a MachineIdentificationUnique,
|
||||
&'a Result<Box<Mutex<dyn Machine>>, anyhow::Error>,
|
||||
&'a Result<Arc<Mutex<dyn Machine>>, anyhow::Error>,
|
||||
);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
use crate::machines::new::MachineAct;
|
||||
use anyhow::anyhow;
|
||||
use api::MachineApi;
|
||||
use new::MachineNewTrait;
|
||||
use serde::Serialize;
|
||||
use smol::lock::Mutex;
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use crate::machines::identification::MachineIdentificationUnique;
|
||||
use crate::machines::new::MachineAct;
|
||||
pub mod api;
|
||||
pub mod identification;
|
||||
pub mod manager;
|
||||
@@ -10,4 +16,65 @@ pub mod manager_iter;
|
||||
pub mod new;
|
||||
pub mod registry;
|
||||
|
||||
pub trait Machine: MachineNewTrait + MachineApi + MachineAct + Any + Send + Sync + Debug {}
|
||||
pub trait Machine: MachineAct + MachineNewTrait + MachineApi + Any + Debug + Send {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
/// Casts a `dyn Machine` to a specific machine type
|
||||
pub async fn downcast_machine<T: Machine>(
|
||||
machine: Arc<Mutex<dyn Machine>>,
|
||||
) -> Result<Arc<Mutex<T>>, anyhow::Error> {
|
||||
// Acquire a read lock on the RwLock
|
||||
let read_lock = machine.lock().await;
|
||||
|
||||
// Check if the inner type can be downcasted to T
|
||||
if read_lock.as_any().is::<T>() {
|
||||
// Clone the Arc and return it as the desired type
|
||||
let cloned_machine = Arc::clone(&machine);
|
||||
// Transmute the Arc to the desired type
|
||||
unsafe {
|
||||
Ok(Arc::from_raw(
|
||||
Arc::into_raw(cloned_machine) as *const Mutex<T>
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"[{}::downcast_machine] Downcast failed",
|
||||
module_path!()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to get strong count to see if Weak has reference
|
||||
pub trait GetStrongCount {
|
||||
fn get_strong_count(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<T> GetStrongCount for Weak<Mutex<T>> {
|
||||
fn get_strong_count(&self) -> usize {
|
||||
self.strong_count()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectedMachine<T: GetStrongCount> {
|
||||
pub machine_identification_unique: MachineIdentificationUnique,
|
||||
pub machine: T,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||
pub struct ConnectedMachineData {
|
||||
pub machine_identification_unique: MachineIdentificationUnique,
|
||||
pub is_available: bool,
|
||||
}
|
||||
|
||||
impl<T> From<&ConnectedMachine<T>> for ConnectedMachineData
|
||||
where
|
||||
T: GetStrongCount,
|
||||
{
|
||||
fn from(value: &ConnectedMachine<T>) -> Self {
|
||||
Self {
|
||||
machine_identification_unique: value.machine_identification_unique.clone(),
|
||||
is_available: value.machine.get_strong_count() != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::serial::SerialDevice;
|
||||
use crate::{machines::manager::MachineManager, serial::SerialDevice};
|
||||
|
||||
use super::identification::DeviceIdentificationIdentified;
|
||||
use anyhow::Error;
|
||||
@@ -8,7 +8,11 @@ use ethercat_hal::{
|
||||
use ethercrab::{SubDevice, SubDeviceRef};
|
||||
use smol::{channel::Sender, lock::RwLock};
|
||||
use socketioxide::extract::SocketRef;
|
||||
use std::{pin::Pin, sync::Arc, time::Instant};
|
||||
use std::{
|
||||
pin::Pin,
|
||||
sync::{Arc, Weak},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use crate::socketio::event::GenericEvent;
|
||||
|
||||
@@ -47,6 +51,7 @@ pub struct MachineNewParams<
|
||||
'machine_new_hardware_serial,
|
||||
>,
|
||||
pub socket_queue_tx: Sender<(SocketRef, Arc<GenericEvent>)>,
|
||||
pub machine_manager: Weak<RwLock<MachineManager>>,
|
||||
}
|
||||
|
||||
pub enum MachineNewHardware<
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use super::{Machine, identification::MachineIdentification, new::MachineNewParams};
|
||||
use anyhow::Error;
|
||||
use smol::lock::Mutex;
|
||||
use std::{any::TypeId, collections::HashMap};
|
||||
use std::{any::TypeId, collections::HashMap, sync::Arc};
|
||||
|
||||
pub type MachineNewClosure =
|
||||
Box<dyn Fn(&MachineNewParams) -> Result<Box<Mutex<dyn Machine>>, Error> + Send + Sync>;
|
||||
Box<dyn Fn(&MachineNewParams) -> Result<Arc<Mutex<dyn Machine>>, Error> + Send + Sync>;
|
||||
|
||||
pub struct MachineRegistry {
|
||||
type_map: HashMap<TypeId, (MachineIdentification, MachineNewClosure)>,
|
||||
@@ -27,7 +27,7 @@ impl MachineRegistry {
|
||||
machine_identficiation,
|
||||
// create a machine construction closure
|
||||
Box::new(|machine_new_params| {
|
||||
Ok(Box::new(Mutex::new(T::new(machine_new_params)?)))
|
||||
Ok(Arc::new(Mutex::new(T::new(machine_new_params)?)))
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -36,7 +36,7 @@ impl MachineRegistry {
|
||||
pub fn new_machine(
|
||||
&self,
|
||||
machine_new_params: &MachineNewParams,
|
||||
) -> Result<Box<Mutex<dyn Machine>>, anyhow::Error> {
|
||||
) -> Result<Arc<Mutex<dyn Machine>>, anyhow::Error> {
|
||||
// get machiine identification
|
||||
let device_identification =
|
||||
&machine_new_params
|
||||
|
||||
@@ -2,10 +2,10 @@ pub mod modbus_serial_interface;
|
||||
|
||||
use anyhow::Error;
|
||||
use crc::{CRC_16_MODBUS, Crc};
|
||||
use serial;
|
||||
use serialport::SerialPort;
|
||||
use std::time::Duration;
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ParityType {
|
||||
Even,
|
||||
Odd,
|
||||
@@ -143,9 +143,7 @@ impl From<ModbusRequest> for Vec<u8> {
|
||||
/// # Returns
|
||||
/// A `Result` containing an `Option` with the raw Modbus response as a `Vec<u8>` if successful,
|
||||
/// or `None` if the response is invalid or an error occurs.
|
||||
pub fn receive_data_modbus(
|
||||
port: &mut dyn serial::SerialPort,
|
||||
) -> Result<Option<Vec<u8>>, anyhow::Error> {
|
||||
pub fn receive_data_modbus(port: &mut dyn SerialPort) -> Result<Option<Vec<u8>>, anyhow::Error> {
|
||||
let mut buf: [u8; 256] = [0; 256];
|
||||
let data_length = port.read(&mut buf)?;
|
||||
if data_length == 0 {
|
||||
|
||||
@@ -160,7 +160,7 @@ pub fn lock_memory() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_core_affinity_first_core(core_id: usize) -> Result<(), anyhow::Error> {
|
||||
pub fn set_core_affinity(core_id: usize) -> Result<(), anyhow::Error> {
|
||||
let cores = core_affinity::get_core_ids().unwrap_or_default();
|
||||
let core = cores.get(core_id);
|
||||
if let Some(core) = core {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use smol::lock::RwLock;
|
||||
use smol::{channel::Sender, lock::RwLock};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::machines::identification::DeviceIdentification;
|
||||
use crate::{
|
||||
machines::identification::DeviceIdentification, serial::serial_detection::SerialDeviceRemoval,
|
||||
};
|
||||
|
||||
pub mod panic;
|
||||
pub mod registry;
|
||||
@@ -25,7 +27,7 @@ pub trait SerialDeviceThread {
|
||||
|
||||
pub struct SerialDeviceNewParams {
|
||||
pub path: String,
|
||||
pub device_thread_panic_tx: smol::channel::Sender<(String, anyhow::Error)>,
|
||||
pub device_thread_panic_tx: Sender<SerialDeviceRemoval<String>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
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<(T, anyhow::Error)>,
|
||||
thread_panic_tx: Sender<SerialDeviceRemoval<T>>,
|
||||
) where
|
||||
T: Send + Sync + Clone + 'static,
|
||||
{
|
||||
@@ -16,7 +18,6 @@ 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()
|
||||
@@ -39,8 +40,8 @@ pub fn send_serial_device_panic<T>(
|
||||
// Send device panic info through channel
|
||||
smol::block_on(async {
|
||||
let _ = thread_panic_tx
|
||||
.send((
|
||||
device_id_clone.clone(),
|
||||
.send(SerialDeviceRemoval::Error(
|
||||
device_identifier.clone(),
|
||||
anyhow::anyhow!("{}", panic_message),
|
||||
))
|
||||
.await;
|
||||
|
||||
@@ -21,6 +21,11 @@ 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<
|
||||
@@ -31,14 +36,13 @@ pub struct SerialDetection<'serialdeviceregistry> {
|
||||
Arc<RwLock<dyn SerialDevice>>,
|
||||
),
|
||||
>,
|
||||
pub device_removal_signal_rx: Receiver<(String, anyhow::Error)>,
|
||||
pub device_removal_signal_tx: Sender<(String, anyhow::Error)>,
|
||||
pub device_removal_signal_rx: Receiver<SerialDeviceRemoval<String>>,
|
||||
pub device_removal_signal_tx: Sender<SerialDeviceRemoval<String>>,
|
||||
}
|
||||
|
||||
impl<'serialdeviceregistry> SerialDetection<'serialdeviceregistry> {
|
||||
pub fn new(serial_device_registry: &'serialdeviceregistry SerialDeviceRegistry) -> Self {
|
||||
let (device_removal_signal_tx, device_removal_signal_rx) =
|
||||
unbounded::<(String, anyhow::Error)>();
|
||||
let (device_removal_signal_tx, device_removal_signal_rx) = unbounded();
|
||||
SerialDetection {
|
||||
serial_device_registry,
|
||||
ports: HashMap::new(),
|
||||
@@ -158,18 +162,36 @@ 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);
|
||||
|
||||
tracing::trace!("Removed device: {:?}: {}", path, error);
|
||||
}
|
||||
Err(_) => {}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
removed_signals
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ impl std::fmt::Debug for GenericEvent {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BuildEvent: Serialize + Send + Sync + Clone {
|
||||
fn build(&self) -> Event<Self>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Event<T>
|
||||
where
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use crate::transmission::Transmission;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FixedTransmission {
|
||||
ratio: f64,
|
||||
}
|
||||
|
||||
impl Transmission for FixedTransmission {
|
||||
fn get_ratio(&self) -> f64 {
|
||||
self.ratio
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedTransmission {
|
||||
/// Creates a new FixedTransmission with the specified ratio.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `ratio` - The transmission ratio (output over input).
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new instance of FixedTransmission.
|
||||
pub fn new(ratio: f64) -> Self {
|
||||
Self { ratio }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
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>()))
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
@@ -0,0 +1,94 @@
|
||||
# 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
|
||||
@@ -0,0 +1,84 @@
|
||||
# Environment Setup
|
||||
|
||||
Working on this repository requires you to use cargo and npm. In the following document the installation process will be shown
|
||||
|
||||
## Ubuntu/Debian Installation
|
||||
|
||||
```bash
|
||||
# Just press enter when prompted
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
apt install npm
|
||||
apt install nodejs
|
||||
# assuming you dont have git already, Git setup not included in here
|
||||
apt install git
|
||||
git clone git@github.com:qitechgmbh/control.git
|
||||
cd control
|
||||
cd electron
|
||||
npm install
|
||||
```
|
||||
|
||||
# Running Backend and Frontend Linux
|
||||
|
||||
## Backend
|
||||
|
||||
To Compile backend code and run it on Linux:
|
||||
|
||||
```bash
|
||||
./cargo_run_linux.sh
|
||||
```
|
||||
|
||||
The script sets capabilities on the compiled binary like raw socket access.
|
||||
Here it is required that you are connected to atleast one machine that is communicated with over ethercat or usb/serial.
|
||||
The Most minimal working setup would be to connect over ethernet to an ek1100 beckhoff terminal.
|
||||
|
||||
## Mock-Machine
|
||||
|
||||
The Mock-Machine can be used to test code that does not require an actual machine connection.
|
||||
Like Frontend Code for example.
|
||||
|
||||
Mock machines can be used if the compile feature mock-machine is enabled.
|
||||
|
||||
```bash
|
||||
# in root git folder
|
||||
./cargo_run_linux.sh mock-machine
|
||||
```
|
||||
|
||||
## Frontend
|
||||
|
||||
To run the Frontend Code:
|
||||
|
||||
```bash
|
||||
# need to be in the electron folder and an additional terminal
|
||||
cd electron
|
||||
npm run start
|
||||
```
|
||||
|
||||
# Contributing
|
||||
|
||||
Generally you contribute to the codebase by:
|
||||
|
||||
1. Opening or Choosing an existing issue (Bug,Feature,Task etc)
|
||||
2. Work on it locally
|
||||
3. Commit your changes locally
|
||||
4. Before pushing changes, if there were changes on the master branch, rebase your branch like so:
|
||||
|
||||
```bash
|
||||
git fetch
|
||||
git rebase origin/master
|
||||
```
|
||||
|
||||
5. Push changes
|
||||
|
||||
```bash
|
||||
# IF you needed to rebase
|
||||
git push --force-with-lease
|
||||
# ELSE you just push
|
||||
git push
|
||||
```
|
||||
|
||||
6. Open a Pull Request on Github and link the issue by writing fix #issue_number, after the pull request is merged into master the branch is closed automatically
|
||||
7. Request a Review and hope for the best :)
|
||||
|
||||
# Recommended Editor Setup
|
||||
|
||||
We recommend you to use an Editor with rust-analyzer support like VSCode to speed up development and detect errors before compiling.
|
||||
@@ -0,0 +1,166 @@
|
||||
# Presets
|
||||
|
||||
To allow the user to save the current configuration of a machine, they can create a preset in the presets page. Presets are presited in electron's local storage.
|
||||
|
||||
Presets are all handled by the `PresetsPage`. To use the `PresetsPage` for a single machine, one must implement the following.
|
||||
|
||||
Create a zod schema for the data, the presets contain.
|
||||
```tsx
|
||||
const mock1PresetDataSchema = z
|
||||
.object({
|
||||
frequency1: z.number(),
|
||||
frequency2: z.number(),
|
||||
frequency3: z.number(),
|
||||
})
|
||||
.partial();
|
||||
|
||||
type Mock1 = typeof mock1PresetDataSchema;
|
||||
```
|
||||
Here, intensionally, `Mock1` is the schema type itself. It can be used as a type argument everything that uses presets.
|
||||
|
||||
Next, create a map containing all schema versions currently supported (the new schema, to be precise).
|
||||
```tsx
|
||||
const schemas = new Map([[1, mock1PresetDataSchema]]);
|
||||
```
|
||||
New, the schema will be used to parse version `1`. To create new versions, see migrations below.
|
||||
|
||||
One also needs to define a table, how the preset data should be shown to the user.
|
||||
```tsx
|
||||
const previewEntries: PresetPreviewEntries<Mock1> = [
|
||||
{
|
||||
name: "Frequency 1",
|
||||
unit: "mHz",
|
||||
renderValue: (data: PresetData<Mock1>) => data?.frequency1?.toFixed(3),
|
||||
},
|
||||
{
|
||||
name: "Frequency 2",
|
||||
unit: "mHz",
|
||||
renderValue: (data: PresetData<Mock1>) => data?.frequency2?.toFixed(3),
|
||||
},
|
||||
{
|
||||
name: "Frequency 3",
|
||||
unit: "mHz",
|
||||
renderValue: (data: PresetData<Mock1>) => data?.frequency3?.toFixed(3),
|
||||
},
|
||||
];
|
||||
```
|
||||
`renderValue` gets the data itself and renders the corresponding part of it. Returning undefined will automatically be rendered as the value is not set, showing "N/A".
|
||||
|
||||
Create a function for the new react component and use the appropreate machine.
|
||||
```tsx
|
||||
export function Mock1PresetsPage() {
|
||||
const { setFrequency1, setFrequency2, setFrequency3, defaultState, state } =
|
||||
useMock1();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Here, two more functions need to be created. const toPresetData = (s: typeof state): PresetData<Mock1> => ({
|
||||
frequency1: s?.frequency1 ?? defaultState?.frequency1,
|
||||
frequency2: s?.frequency2 ?? defaultState?.frequency2,
|
||||
frequency3: s?.frequency3 ?? defaultState?.frequency3,The first takes in a preset and applies it on the machine. If data is missing, use the default defined by the backend.
|
||||
```tsx
|
||||
export function Mock1PresetsPage() {
|
||||
const { setFrequency1, setFrequency2, setFrequency3, defaultState, state } =
|
||||
useMock1();
|
||||
|
||||
const applyPreset = (preset: Preset<Mock1>) => {
|
||||
const frequency1 = preset.data?.frequency1 ?? 100;
|
||||
const frequency2 = preset.data?.frequency2 ?? 200;
|
||||
const frequency3 = preset.data?.frequency3 ?? 500;
|
||||
|
||||
setFrequency1(frequency1);
|
||||
setFrequency2(frequency2);
|
||||
setFrequency3(frequency3);
|
||||
};
|
||||
// ...
|
||||
}
|
||||
```
|
||||
The later does the opposite, it turns a machine state, comming from the backend, into the preset data
|
||||
```tsx
|
||||
export function Mock1PresetsPage() {
|
||||
const { setFrequency1, setFrequency2, setFrequency3, defaultState, state } =
|
||||
useMock1();
|
||||
// ...
|
||||
const toPresetData = (s: typeof state): PresetData<Mock1> => ({
|
||||
frequency1: s?.frequency1 ?? defaultState?.frequency1,
|
||||
frequency2: s?.frequency2 ?? defaultState?.frequency2,
|
||||
frequency3: s?.frequency3 ?? defaultState?.frequency3,
|
||||
});
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Having all this, we can simple pass it to the `PresetsPage`
|
||||
```tsx
|
||||
export function Mock1PresetsPage() {
|
||||
// ...
|
||||
return (
|
||||
<PresetsPage
|
||||
machine_identification={mock1.machine_identification}
|
||||
currentState={toPresetData(state)}
|
||||
schemas={schemas}
|
||||
schemaVersion={1}
|
||||
applyPreset={applyPreset}
|
||||
previewEntries={previewEntries}
|
||||
defaultState={toPresetData(defaultState)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
Also mind the machine identification, that has to match the machine we are using and the schema versionen. Here we pass `1`, as we also used `1` in the schemas map.
|
||||
|
||||
## Migrations
|
||||
|
||||
Migrations are handled by the `PresetsPage`, however, one still has to define what to migrate.
|
||||
To do so, first create the new schema.
|
||||
```tsx
|
||||
const fancyNewMock1PresetDataSchema = z
|
||||
.object({
|
||||
frequency1_now_with_ai: z.number(),
|
||||
frequency2: z.number(),
|
||||
frequency3: z.number(),
|
||||
frequency4: z.number(),
|
||||
})
|
||||
.partial();
|
||||
|
||||
type Mock1 = typeof fancyNewMock1PresetDataSchema;
|
||||
```
|
||||
and then, also create a migration that uses the new schema, but can handle the old format.
|
||||
```tsx
|
||||
const mock1PresetDataSchemaMigration: PresetSchema = z.preprocess(
|
||||
(obj: any): PresetData<Mock1> => ({
|
||||
frequency1_now_with_ai: obj?.frequency1, // Here, a field have been renamed
|
||||
frequency2: obj?.frequency2,
|
||||
frequency3: obj?.frequency3,
|
||||
frequency4: 666.0, // A new field is added. Initialize it with default the value
|
||||
}),
|
||||
fancyNewMock1PresetDataSchema,
|
||||
);
|
||||
```
|
||||
Usings zods preprocessing step,
|
||||
we can first transform the data as needed and then validate using the new schema accordingly.
|
||||
|
||||
We have to register both schemas in our schema map.
|
||||
```tsx
|
||||
const schemas = new Map<number, Mock1>([
|
||||
[1, mock1PresetDataSchemaMigration as Mock1],
|
||||
[2, fancyNewMock1PresetDataSchema],
|
||||
]);
|
||||
```
|
||||
Mind the cast, which is ok, because `mock1PresetDataSchemaMigration` parses `fancyNewMock1PresetDataSchema` by construction.
|
||||
|
||||
Lastly, simply change the schema version being passed to the `PresetsPage`.
|
||||
```diff
|
||||
@@ -62,7 +77,7 @@ export function Mock1PresetsPage() {
|
||||
machine_identification={mock1.machine_identification}
|
||||
currentState={toPresetData(state)}
|
||||
schemas={schemas}
|
||||
- schemaVersion={1}
|
||||
+ schemaVersion={2}
|
||||
applyPreset={applyPreset}
|
||||
previewEntries={previewEntries}
|
||||
defaultState={toPresetData(defaultState)}
|
||||
```
|
||||
Make sure, that all (exsiting and new migrations) return a schema version 2 conform format.
|
||||
Of corse, one can chain migrations of needed - just the end result must be complient.
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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:
|
||||
|
||||

|
||||
|
||||
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
|
||||
@@ -0,0 +1,13 @@
|
||||
# Electrical Diagrams
|
||||
|
||||
This directory contains electrical wiring diagrams for various machines and systems supported by the control system.
|
||||
|
||||
## Available Diagrams
|
||||
|
||||
### Extruder
|
||||
- **Location**: [`extruder/`](extruder/)
|
||||
- **File**: [QiTech Pro Extruder Electrical Diagram 2025.pdf](extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025.pdf)
|
||||
|
||||
### Winder
|
||||
- **Location**: [`winder/`](winder/)
|
||||
- **File**: [QiTech Pro Winder Electrical Diagram.pdf](winder/QiTech_Pro_Winder_Electrical_Diagram.pdf)
|
||||
@@ -3,6 +3,6 @@
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"endOfLine": "auto",
|
||||
"endOfLine": "lf",
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "eslint .",
|
||||
"lint:write": "eslint . --fix",
|
||||
"format": "prettier --check .",
|
||||
"format:write": "prettier --write .",
|
||||
"test": "vitest run",
|
||||
@@ -23,14 +24,14 @@
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^7.6.1",
|
||||
"@electron-forge/maker-deb": "^7.8.0",
|
||||
"@electron-forge/maker-rpm": "^7.6.1",
|
||||
"@electron-forge/maker-rpm": "^7.8.3",
|
||||
"@electron-forge/maker-squirrel": "^7.6.1",
|
||||
"@electron-forge/maker-zip": "^7.6.1",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^7.6.1",
|
||||
"@electron-forge/plugin-fuses": "^7.8.0",
|
||||
"@electron-forge/plugin-vite": "^7.6.1",
|
||||
"@electron-forge/shared-types": "^7.4.0",
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@electron/fuses": "^2.0.0",
|
||||
"@eslint/compat": "^1.2.6",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@playwright/test": "^1.50.1",
|
||||
@@ -40,21 +41,21 @@
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/electron-squirrel-startup": "^1.0.2",
|
||||
"@types/eslint-config-prettier": "^6.11.3",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/node": "^24.2.1",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"electron": "36.4.0",
|
||||
"electron": "^37.2.6",
|
||||
"electron-playwright-helpers": "^1.7.1",
|
||||
"eslint": "^9.20.1",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-prettier": "^5.2.3",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-compiler": "^19.0.0-beta-714736e-20250131",
|
||||
"globals": "^16.2.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"postcss": "^8.5.2",
|
||||
"prettier": "^3.5.1",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.7.3",
|
||||
@@ -72,13 +73,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.1.6",
|
||||
"@radix-ui/react-select": "^2.2.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.66.7",
|
||||
"@tanstack/react-query": "^5.84.2",
|
||||
"@tanstack/react-router": "^1.105.0",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"@tanstack/router-devtools": "^1.105.0",
|
||||
@@ -98,6 +99,7 @@
|
||||
"react-hook-form": "^7.57.0",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"socket.io-msgpack-parser": "^3.0.2",
|
||||
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 375 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 367 KiB |
|
After Width: | Height: | Size: 362 KiB |
|
After Width: | Height: | Size: 140 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 7.6 KiB |