Compare commits

...
Author SHA1 Message Date
93094049+kraemr@users.noreply.github.com 5e47a30a2c fixed optimistic state handling 2025-09-09 16:38:41 +02:00
93094049+kraemr@users.noreply.github.com c675495c30 added tick 2025-09-09 16:00:02 +02:00
93094049+kraemr@users.noreply.github.com 12749c1a1f fixed optimistic state handling 2025-09-09 15:59:52 +02:00
93094049+kraemr@users.noreply.github.com 57c94f891f implemented tokio tick interval for loop delay 2025-09-09 15:39:03 +02:00
93094049+kraemr@users.noreply.github.com d6911ed6f0 1ms loop delay 2025-09-09 15:39:03 +02:00
Robin Krämer 3cb2d20df9 Merge pull request #690 from qitechgmbh/sync-act
Make `act` method non-async
2025-09-09 14:21:22 +02:00
Icedsoda ee4b5b15d7 Make act method non-async 2025-09-09 14:18:21 +02:00
93094049+kraemr@users.noreply.github.com 299e6314b8 Revert "using virtualized list"
This reverts commit 01170e1a1b.
2025-09-09 13:15:46 +02:00
93094049+kraemr@users.noreply.github.com 01170e1a1b using virtualized list 2025-09-09 13:13:52 +02:00
Robin Krämer 6fe2e498aa Merge pull request #686 from qitechgmbh/668-frontend-buttons-are-flickering
Fixed Buttons Flickering
2025-09-09 11:21:18 +02:00
93094049+kraemr@users.noreply.github.com fc7262f8f0 Fixed Buttons Flickering 2025-09-09 11:16:56 +02:00
Robin Krämer 880d2ddcb0 Merge pull request #684 from qitechgmbh/683-ethercat-loop-broken
683 ethercat loop broken
2025-09-08 14:06:02 +02:00
93094049+kraemr@users.noreply.github.com e9ea378ef0 set loop timer to 500us 2025-09-08 13:44:33 +02:00
93094049+kraemr@users.noreply.github.com 145ad7ed2c added 1ms delay 2025-09-08 13:25:46 +02:00
93094049+kraemr@users.noreply.github.com d5ce65c868 added 1ms delay 2025-09-08 13:23:39 +02:00
93094049+kraemr@users.noreply.github.com 1bc76dfe09 wait_loop_delay set to 0 2025-09-08 12:52:45 +02:00
Robin Krämer 17e82cf5ea Merge pull request #680 from qitechgmbh/402-many-journald-logs-slow-down-the-frontend
402 many journald logs slow down the frontend
2025-09-08 12:04:59 +02:00
93094049+kraemr@users.noreply.github.com 926650802b reduces Logs, by A LOT 2025-09-08 12:01:01 +02:00
Robin Krämer fba7c0375d Merge pull request #677 from qitechgmbh/676-reduce-heap-allocationsdeallocations-in-machineact
676 reduce heap allocationsdeallocations in machineact
2025-09-08 11:51:46 +02:00
93094049+kraemr@users.noreply.github.com 27be5b8ec4 fixed extruder 2025-09-08 11:49:42 +02:00
93094049+kraemr@users.noreply.github.com d036246b2a refactored all machines 2025-09-08 11:49:42 +02:00
93094049+kraemr@users.noreply.github.com 59a9b15632 started refactoring MachineAct 2025-09-08 11:49:42 +02:00
Robin Krämer c09e061390 Merge pull request #679 from qitechgmbh/678-remove-loopthrottle
678 remove loopthrottle
2025-09-08 11:49:22 +02:00
93094049+kraemr@users.noreply.github.com 02f6dffaff fixed compile error and replaced loopthrottle with sleep 2025-09-08 10:00:37 +02:00
93094049+kraemr@users.noreply.github.com 509d4c8734 removed unused import 2025-09-08 09:57:03 +02:00
93094049+kraemr@users.noreply.github.com 084fdef754 wait_loop_delay of 1ms 2025-09-08 09:56:38 +02:00
93094049+kraemr@users.noreply.github.com a2f21da355 remove LoopThrottle 2025-09-08 09:55:47 +02:00
Robin Krämer b722502f5b Merge pull request #674 from qitechgmbh/dependabot/npm_and_yarn/electron/electron-37.3.1
Bump electron from 37.2.6 to 37.3.1 in /electron
2025-09-04 19:04:55 +02:00
dependabot[bot] ab45407763 Bump electron from 37.2.6 to 37.3.1 in /electron
Bumps [electron](https://github.com/electron/electron) from 37.2.6 to 37.3.1.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v37.2.6...v37.3.1)

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

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

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 12:18:57 +02:00
Robin Krämer 44127bec42 Merge pull request #669 from qitechgmbh/603-add-electrical-diagrams-to-the-machines-manuals
Implement electrical diagrams into the manuals
2025-09-04 12:12:00 +02:00
Christian f48b97dd24 Add electrical diagram images to the public folder and implement them in the md files for extruder and winder. 2025-09-03 21:28:23 +02:00
36 changed files with 195 additions and 348 deletions

No files matched your search

-189
View File
@@ -1,189 +0,0 @@
use smol::Timer;
use std::time::{Duration, Instant};
use tracing::instrument;
pub struct LoopThrottle {
target_cycle_time: Duration,
update_interval_cycles: usize,
window_start: Instant,
total_cycles: usize,
delay: Duration,
last_avg_cycle_time: Duration,
on_update: Option<Box<dyn Fn(LoopThrottleUpdate)>>,
}
pub struct LoopThrottleUpdate {
pub delay: Duration,
pub avg_cycle_time: Duration,
pub error: i128, // Error in nanoseconds
}
impl LoopThrottle {
pub fn new(
target_cycle_time: Duration,
update_interval_cycles: usize,
on_update: Option<Box<dyn Fn(LoopThrottleUpdate)>>,
) -> Self {
Self {
target_cycle_time,
update_interval_cycles,
window_start: Instant::now(),
total_cycles: 0,
delay: target_cycle_time,
last_avg_cycle_time: target_cycle_time,
on_update,
}
}
#[instrument(skip_all)]
pub async fn sleep(&mut self) {
self.total_cycles += 1;
// Check if it's time to update (every N cycles)
if self.total_cycles % self.update_interval_cycles == 0 {
let actual_total_time = self.window_start.elapsed();
// Use integer arithmetic where possible
let avg_actual_cycle_time_nanos =
actual_total_time.as_nanos() / self.update_interval_cycles as u128;
let target_cycle_time_nanos = self.target_cycle_time.as_nanos();
// Calculate error in nanoseconds (signed)
let error_nanos = target_cycle_time_nanos as i128 - avg_actual_cycle_time_nanos as i128;
// Update current delay, allowing zero or negative values to become zero
let new_delay_nanos = (self.delay.as_nanos() as i128 + error_nanos).max(0) as u64;
self.delay = Duration::from_nanos(new_delay_nanos);
self.last_avg_cycle_time = Duration::from_nanos(avg_actual_cycle_time_nanos as u64);
// Reset for next measurement window
self.window_start = Instant::now();
self.total_cycles = 0; // Reset to prevent overflow
// if we have an update callback, call it
if let Some(on_update) = &self.on_update {
on_update(LoopThrottleUpdate {
delay: self.delay,
avg_cycle_time: self.last_avg_cycle_time,
error: error_nanos,
});
}
}
Timer::after(self.delay).await;
}
pub fn get_cycle_time(&self) -> Duration {
self.last_avg_cycle_time
}
pub fn get_delay(&self) -> Duration {
self.delay
}
}
#[cfg(test)]
mod tests {
use super::*;
use smol::block_on;
#[test]
fn test_adaptive_throttle_timing() {
block_on(async {
let update_interval_cycles = 100;
let target_cycle_time = Duration::from_micros(5);
let mut throttle = LoopThrottle::new(target_cycle_time, update_interval_cycles, None);
let test_start = Instant::now();
let mut cycle_times = Vec::new();
// Run for several cycles to trigger multiple updates
for _ in 0..10000 {
let cycle_start = Instant::now();
// Simulate some work
Timer::after(Duration::from_micros(5)).await;
throttle.sleep().await;
let cycle_time = cycle_start.elapsed();
cycle_times.push(cycle_time);
}
let total_test_time = test_start.elapsed();
let avg_cycle_time = cycle_times.iter().sum::<Duration>() / cycle_times.len() as u32;
println!("Test completed in: {:?}", total_test_time);
println!("Average cycle time: {:?}", avg_cycle_time);
println!("Target cycle time: {:?}", target_cycle_time);
println!("Last measured avg: {:?}", throttle.get_cycle_time());
// Assert that we're reasonably close to target timing
let tolerance = Duration::from_millis(10);
assert!(
avg_cycle_time >= target_cycle_time.saturating_sub(tolerance)
&& avg_cycle_time <= target_cycle_time + tolerance,
"Average cycle time {:?} not within tolerance of target {:?}",
avg_cycle_time,
target_cycle_time
);
});
}
#[test]
fn test_throttle_adaptation() {
block_on(async {
let target_cycle_time = Duration::from_millis(100);
let update_interval_cycles = 3;
let mut throttle = LoopThrottle::new(target_cycle_time, update_interval_cycles, None);
let initial_delay = throttle.get_delay();
// Run enough cycles to trigger multiple updates
for _ in 0..12 {
// Add variable work to test adaptation
Timer::after(Duration::from_millis(10)).await;
throttle.sleep().await;
}
println!("Initial delay: {:?}", initial_delay);
println!("Final delay: {:?}", throttle.get_delay());
println!("Last avg cycle time: {:?}", throttle.get_cycle_time());
assert!(throttle.get_cycle_time() > Duration::ZERO);
});
}
#[test]
fn test_throttle_initialization() {
let update_interval_cycles = 10;
let target_cycle_time = Duration::from_millis(75);
let throttle = LoopThrottle::new(target_cycle_time, update_interval_cycles, None);
assert_eq!(throttle.get_delay(), target_cycle_time);
assert_eq!(throttle.get_cycle_time(), target_cycle_time);
assert_eq!(throttle.total_cycles, 0);
}
#[test]
fn test_overflow_protection() {
block_on(async {
let target_cycle_time = Duration::from_micros(1);
let update_interval_cycles = 5;
let mut throttle = LoopThrottle::new(target_cycle_time, update_interval_cycles, None);
// Run many cycles to ensure total_cycles resets properly
for i in 0..50 {
throttle.sleep().await;
// Verify that total_cycles never exceeds update_interval_cycles
assert!(
throttle.total_cycles <= update_interval_cycles,
"Cycle {}: total_cycles ({}) exceeded update_interval_cycles ({})",
i,
throttle.total_cycles,
update_interval_cycles
);
}
});
}
}
-1
View File
@@ -2,6 +2,5 @@ pub mod compare_lists;
pub mod hasher_serializer;
pub mod hashing;
pub mod interpolation;
pub mod loop_trottle;
pub mod moving_time_window;
pub mod retry;
+1 -1
View File
@@ -71,7 +71,7 @@ impl<'a> Iterator for MachineManagerIterator<'a> {
impl MachineManager {
// Returns an iterator over all machines (both ethercat and serial)
pub fn iter(&self) -> MachineManagerIterator {
pub fn iter(&'_ self) -> MachineManagerIterator<'_> {
MachineManagerIterator {
iter: MachineManagerIter::EthercatMachines {
iter: self.ethercat_machines.iter(),
+1 -2
View File
@@ -9,7 +9,6 @@ use ethercrab::{SubDevice, SubDeviceRef};
use smol::{channel::Sender, lock::RwLock};
use socketioxide::extract::SocketRef;
use std::{
pin::Pin,
sync::{Arc, Weak},
time::Instant,
};
@@ -25,7 +24,7 @@ pub trait MachineNewTrait {
}
pub trait MachineAct {
fn act(&mut self, now: Instant) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
fn act(&mut self, now: Instant);
}
pub struct MachineNewParams<
+46 -14
View File
@@ -11,7 +11,7 @@
"@hookform/resolvers": "^4.1.2",
"@icons-pack/react-simple-icons": "^11.2.0",
"@jub3i/tree-kill": "^0.0.15",
"@radix-ui/react-collapsible": "^1.1.8",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-label": "^2.1.2",
@@ -54,7 +54,7 @@
"xlsx": "^0.18.5",
"zod": "^3.24.2",
"zod-validation-error": "^3.4.0",
"zustand": "^5.0.3"
"zustand": "^5.0.8"
},
"devDependencies": {
"@electron-forge/cli": "^7.6.1",
@@ -79,7 +79,7 @@
"@types/node": "^24.2.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"electron": "^37.2.6",
"electron": "^37.3.1",
"electron-playwright-helpers": "^1.7.1",
"eslint": "^9.20.1",
"eslint-config-prettier": "^10.1.2",
@@ -2323,15 +2323,16 @@
}
},
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz",
"integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz",
"integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.2",
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-presence": "1.1.4",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
@@ -2351,6 +2352,36 @@
}
}
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
"license": "MIT"
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-presence": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
@@ -6285,9 +6316,9 @@
"dev": true
},
"node_modules/electron": {
"version": "37.2.6",
"resolved": "https://registry.npmjs.org/electron/-/electron-37.2.6.tgz",
"integrity": "sha512-Ns6xyxE+hIK5UlujtRlw7w4e2Ju/ImCWXf1Q/PoOhc0N3/6SN6YW7+ujCarsHbxWnolbW+1RlkHtdklUJpjbPA==",
"version": "37.3.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-37.3.1.tgz",
"integrity": "sha512-7DhktRLqhe6OJh/Bo75bTI0puUYEmIwSzMinocgO63mx3MVjtIn2tYMzLmAleNIlud2htkjpsMG2zT4PiTCloA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -15204,9 +15235,10 @@
}
},
"node_modules/zustand": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.7.tgz",
"integrity": "sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg==",
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
"integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
+3 -3
View File
@@ -44,7 +44,7 @@
"@types/node": "^24.2.1",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"electron": "^37.2.6",
"electron": "^37.3.1",
"electron-playwright-helpers": "^1.7.1",
"eslint": "^9.20.1",
"eslint-config-prettier": "^10.1.2",
@@ -67,7 +67,7 @@
"@hookform/resolvers": "^4.1.2",
"@icons-pack/react-simple-icons": "^11.2.0",
"@jub3i/tree-kill": "^0.0.15",
"@radix-ui/react-collapsible": "^1.1.8",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-label": "^2.1.2",
@@ -110,6 +110,6 @@
"xlsx": "^0.18.5",
"zod": "^3.24.2",
"zod-validation-error": "^3.4.0",
"zustand": "^5.0.3"
"zustand": "^5.0.8"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 850 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

@@ -697,3 +697,26 @@ The following persons must not work on the machine:
| Cleaning, maintenance | |
| Maintenance | Trained and instructed specialised personnel of the operator or service technicians of the manufacturer |
| Decommissioning | Trained and instructed specialised personnel of the operator |
<br>
<br>
## Electrical Diagrams
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 1](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025-001.png)
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 2](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025_2-002.png)
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 3](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025_3-003.png)
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 4](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025_4-004.png)
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 5](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025_5-005.png)
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 6](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025_6-006.png)
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 7](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025_7-007.png)
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 8](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025_8-008.png)
![QiTech Pro Extruder Electrical Diagram 2025 - Sheet 9](images/manuals/extruder/QiTech_Pro_Extruder_Electrical_Diagram_2025_9-009.png)
@@ -687,3 +687,18 @@ The following persons may not work on the machine:
| K Cleaning, maintenance | |
| L Maintenance | Trained and instructed specialized personnel of the operator or service technicians of the manufacturer |
| M Decommissioning | Trained and instructed specialized personnel of the operator |
<br>
<br>
## Electrical Diagrams
![QiTech Pro Winder Electrical Diagram - Sheet 1](images/manuals/winder/QiTech_Pro_Winder_Electrical_Diagram-001.png)
![QiTech Pro Winder Electrical Diagram - Sheet 2](images/manuals/winder/QiTech_Pro_Winder_Electrical_Diagram_2-002.png)
![QiTech Pro Winder Electrical Diagram - Sheet 3](images/manuals/winder/QiTech_Pro_Winder_Electrical_Diagram_3-003.png)
![QiTech Pro Winder Electrical Diagram - Sheet 4](images/manuals/winder/QiTech_Pro_Winder_Electrical_Diagram_4-004.png)
![QiTech Pro Winder Electrical Diagram - Sheet 5](images/manuals/winder/QiTech_Pro_Winder_Electrical_Diagram_5-005.png)
@@ -50,12 +50,11 @@ export function useBuffer1() {
// Single optimistic state for all state management
const stateOptimistic = useStateOptimistic<StateEvent>();
// Update optimistic state when real state changes
useEffect(() => {
if (state) {
stateOptimistic.setReal(state);
}
}, [state, stateOptimistic]);
}, [state]);
// Helper function for optimistic updates using produce
const updateStateOptimistically = (
@@ -63,7 +62,7 @@ export function useBuffer1() {
serverRequest: () => void,
) => {
const currentState = stateOptimistic.value;
if (currentState) {
if (currentState && !stateOptimistic.isOptimistic) {
stateOptimistic.setOptimistic(produce(currentState, producer));
}
serverRequest();
@@ -61,12 +61,11 @@ export function useExtruder2() {
// Single optimistic state for all state management
const stateOptimistic = useStateOptimistic<StateEvent>();
// Update optimistic state when real state changes
useEffect(() => {
if (state) {
stateOptimistic.setReal(state);
}
}, [state, stateOptimistic]);
}, [state]);
// Helper function for optimistic updates using produce
const updateStateOptimistically = (
@@ -74,7 +73,7 @@ export function useExtruder2() {
serverRequest: () => void,
) => {
const currentState = stateOptimistic.value;
if (currentState) {
if (currentState && !stateOptimistic.isOptimistic) {
stateOptimistic.setOptimistic(produce(currentState, producer));
}
serverRequest();
@@ -18,12 +18,11 @@ function useLaser(machine_identification_unique: MachineIdentificationUnique) {
// Single optimistic state for all state management
const stateOptimistic = useStateOptimistic<StateEvent>();
// Update optimistic state when real state changes
useEffect(() => {
if (state) {
stateOptimistic.setReal(state);
}
}, [state, stateOptimistic]);
}, [state]);
// Helper function for optimistic updates using produce
const updateStateOptimistically = (
@@ -31,7 +30,7 @@ function useLaser(machine_identification_unique: MachineIdentificationUnique) {
serverRequest: () => void,
) => {
const currentState = stateOptimistic.value;
if (currentState) {
if (currentState && !stateOptimistic.isOptimistic) {
stateOptimistic.setOptimistic(produce(currentState, producer));
}
serverRequest();
+2 -3
View File
@@ -17,12 +17,11 @@ function useMock(machine_identification_unique: MachineIdentificationUnique) {
// Single optimistic state for all state management
const stateOptimistic = useStateOptimistic<StateEvent>();
// Update optimistic state when real state changes
useEffect(() => {
if (state) {
stateOptimistic.setReal(state);
}
}, [state, stateOptimistic]);
}, [state]);
// Helper function for optimistic updates using produce
const updateStateOptimistically = (
@@ -30,7 +29,7 @@ function useMock(machine_identification_unique: MachineIdentificationUnique) {
serverRequest: () => Promise<any>,
) => {
const currentState = stateOptimistic.value;
if (currentState) {
if (currentState && !stateOptimistic.isOptimistic) {
stateOptimistic.setOptimistic(produce(currentState, producer));
}
serverRequest()
@@ -69,12 +69,12 @@ export function useWinder2() {
// Single optimistic state for all state management
const stateOptimistic = useStateOptimistic<StateEvent>();
// Update optimistic state when real state changes
// Update optimistic state to real when real state changes
useEffect(() => {
if (state) {
stateOptimistic.setReal(state);
}
}, [state, stateOptimistic]);
}, [state]);
// Request functions for all operations
const { request: requestTraverseGotoLimitInner } = useMachineMutation(
@@ -178,7 +178,7 @@ export function useWinder2() {
serverRequest: () => void,
) => {
const currentState = stateOptimistic.value;
if (currentState) {
if (currentState && !stateOptimistic.isOptimistic) {
stateOptimistic.setOptimistic(produce(currentState, producer));
}
serverRequest();
+1 -1
View File
@@ -103,7 +103,7 @@ in {
environment = {
RUST_BACKTRACE = "full";
RUST_LOG = "debug";
RUST_LOG = "info";
};
};
+4 -4
View File
@@ -78,13 +78,13 @@ pub async fn setup_loop(
pdu,
Timeouts {
// Default 5000ms
state_transition: Duration::from_millis(10 * 1000),
state_transition: Duration::from_millis(5000),
// Default 30_000us
pdu: Duration::from_millis(500),
pdu: Duration::from_micros(30_000),
// Default 10ms
eeprom: Duration::from_millis(10),
// Default 0ms
wait_loop_delay: Duration::from_millis(0),
wait_loop_delay: Duration::from_millis(1),
// Default 100ms
mailbox_echo: Duration::from_millis(100),
// Default 1000ms
@@ -92,7 +92,7 @@ pub async fn setup_loop(
},
MainDeviceConfig {
// Default RetryBehaviour::None
retry_behaviour: RetryBehaviour::Count(10), // 100ms * 25 = 2.5s
retry_behaviour: RetryBehaviour::Count(5),
// Default 10_000
dc_static_sync_iterations: 10_000,
},
+2 -6
View File
@@ -1,19 +1,15 @@
use std::time::Duration;
use control_core::helpers::loop_trottle::LoopThrottle;
use std::{thread::sleep, time::Duration};
pub fn init_jemalloc_stats() {
std::thread::Builder::new()
.name("jemalloc-stats".to_string())
.spawn(|| {
let mut throttle = LoopThrottle::new(Duration::from_secs(10), 1, None);
let mut last_allocated = 0usize;
let mut last_time = std::time::Instant::now();
loop {
smol::block_on(async {
throttle.sleep().await;
sleep(Duration::from_secs(10));
// Advance epoch and get allocated bytes
let _ = tikv_jemalloc_ctl::epoch::advance();
+20 -12
View File
@@ -1,11 +1,12 @@
use crate::app_state::AppState;
use crate::panic::{PanicDetails, send_panic};
use bitvec::prelude::*;
use control_core::helpers::loop_trottle::LoopThrottle;
use control_core::realtime::{set_core_affinity, set_realtime_priority};
use smol::Timer;
use smol::channel::Sender;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::time::MissedTickBehavior;
use tracing::{instrument, trace_span};
pub fn init_loop(
@@ -18,7 +19,6 @@ pub fn init_loop(
.spawn(move || {
send_panic(thread_panic_tx.clone());
let rt = smol::LocalExecutor::new();
let mut throttle = LoopThrottle::new(Duration::from_millis(1), 10, None);
// Set core affinity to third core
let _ = set_core_affinity(2);
@@ -37,17 +37,25 @@ pub fn init_loop(
);
}
loop {
let res = smol::block_on(rt.run(async {
throttle.sleep().await;
loop_once(app_state.clone()).await
}));
smol::block_on(rt.run(async move {
let tick_duration = Duration::from_millis(1);
if let Err(err) = res {
tracing::error!("Loop failed\n{:?}", err);
break;
loop {
let loop_start = Instant::now();
if let Err(err) = loop_once(app_state.clone()).await {
tracing::error!("Loop failed: {:?}", err);
break;
}
// Sleep until next tick
let elapsed = loop_start.elapsed();
if elapsed < tick_duration {
smol::Timer::after(tick_duration - elapsed).await;
}
}
}
}));
// Exit the entire program if the Loop fails (gets restarted by systemd if running on NixOS)
std::process::exit(1);
})
@@ -151,7 +159,7 @@ pub async fn loop_once<'maindevice>(app_state: Arc<AppState>) -> Result<(), anyh
let span = trace_span!("loop_once_act_machine",);
let _enter = span.enter();
// execute machine
machine_guard.act(now).await;
machine_guard.act(now);
}
None => {}
}
+7 -10
View File
@@ -3,16 +3,13 @@ use std::time::{Duration, Instant};
use super::BufferV1;
use control_core::machines::new::MachineAct;
impl MachineAct for BufferV1 {
fn act(&mut self, now: Instant) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
// if last measurement is older than 1 second, emit a new measurement
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0)
{
// Emit live values at 30 FPS
self.emit_live_values();
fn act(&mut self, now: Instant) {
// if last measurement is older than 1 second, emit a new measurement
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0) {
// Emit live values at 30 FPS
self.emit_live_values();
self.last_measurement_emit = now;
}
})
self.last_measurement_emit = now;
}
}
}
+28 -34
View File
@@ -1,45 +1,39 @@
use control_core::machines::new::MachineAct;
use super::{ExtruderV2, ExtruderV2Mode};
use std::time::{Duration, Instant};
use crate::machines::extruder1::ExtruderV2;
impl MachineAct for ExtruderV2 {
fn act(
&mut self,
now_ts: Instant,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + '_>> {
Box::pin(async move {
self.temperature_controller_back.update(now_ts);
self.temperature_controller_nozzle.update(now_ts);
self.temperature_controller_front.update(now_ts);
self.temperature_controller_middle.update(now_ts);
fn act(&mut self, now: Instant) {
self.temperature_controller_back.update(now);
self.temperature_controller_nozzle.update(now);
self.temperature_controller_front.update(now);
self.temperature_controller_middle.update(now);
if self.mode == ExtruderV2Mode::Extrude {
self.screw_speed_controller.update(now_ts, true);
} else {
self.screw_speed_controller.update(now_ts, false);
}
if self.mode == super::ExtruderV2Mode::Extrude {
self.screw_speed_controller.update(now, true);
} else {
self.screw_speed_controller.update(now, false);
}
if self.mode == ExtruderV2Mode::Standby {
self.turn_heating_off();
}
if self.mode == super::ExtruderV2Mode::Standby {
self.turn_heating_off();
}
if self.mode == ExtruderV2Mode::Extrude
&& !self.screw_speed_controller.get_motor_enabled()
{
self.switch_to_heat();
}
if self.mode == super::ExtruderV2Mode::Extrude
&& !self.screw_speed_controller.get_motor_enabled()
{
self.switch_to_heat();
}
let now = Instant::now();
let now = Instant::now();
// more than 33ms have passed since last emit (30 "fps" target)
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0)
{
self.maybe_emit_state_event();
// Emit live values at 30 FPS
self.emit_live_values();
self.last_measurement_emit = now;
}
})
// more than 33ms have passed since last emit (30 "fps" target)
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0) {
self.maybe_emit_state_event();
// Emit live values at 30 FPS
self.emit_live_values();
self.last_measurement_emit = now;
}
}
}
+8 -15
View File
@@ -1,10 +1,6 @@
use super::LaserMachine;
use control_core::machines::new::MachineAct;
use std::{
future::Future,
pin::Pin,
time::{Duration, Instant},
};
use std::time::{Duration, Instant};
/// Implements the `MachineAct` trait for the `LaserMachine`.
///
@@ -22,15 +18,12 @@ use std::{
/// The method ensures that the diameter value is updated approximately 60 times per second.
///
impl MachineAct for LaserMachine {
fn act(&mut self, now: Instant) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
// more than 33ms have passed since last emit (30 "fps" target)
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0)
{
self.maybe_emit_state_event();
self.emit_live_values();
self.last_measurement_emit = now;
}
})
fn act(&mut self, now: Instant) {
// more than 33ms have passed since last emit (30 "fps" target)
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0) {
self.maybe_emit_state_event();
self.emit_live_values();
self.last_measurement_emit = now;
}
}
}
+9 -18
View File
@@ -1,11 +1,7 @@
use control_core::machines::new::MachineAct;
use super::MockMachine;
use std::{
future::Future,
pin::Pin,
time::{Duration, Instant},
};
use std::time::{Duration, Instant};
/// Implements the `MachineAct` trait for the `MockMachine`.
///
@@ -24,18 +20,13 @@ use std::{
/// The method ensures that the sine wave value is updated approximately 60 times per second (16ms intervals) when running.
///
impl MachineAct for MockMachine {
fn act(&mut self, _now_ts: Instant) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
let now = Instant::now();
// Only emit live values if machine is in Running mode
// The live values are updated approximately 30 times per second
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0)
{
self.maybe_emit_state_event();
self.emit_live_values();
self.last_measurement_emit = now;
}
})
fn act(&mut self, now: Instant) {
// Only emit live values if machine is in Running mode
// The live values are updated approximately 30 times per second
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0) {
self.maybe_emit_state_event();
self.emit_live_values();
self.last_measurement_emit = now;
}
}
}
+16 -23
View File
@@ -1,33 +1,26 @@
use std::time::{Duration, Instant};
use super::Winder2;
use control_core::machines::new::MachineAct;
use std::time::{Duration, Instant};
impl MachineAct for Winder2 {
fn act(
&mut self,
now: Instant,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + '_>> {
Box::pin(async move {
// sync the spool speed
self.sync_spool_speed(now);
fn act(&mut self, now: Instant) {
// sync the spool speed
self.sync_spool_speed(now);
// sync the puller speed
self.sync_puller_speed(now);
// sync the puller speed
self.sync_puller_speed(now);
// sync the traverse speed
self.sync_traverse_speed();
// sync the traverse speed
self.sync_traverse_speed();
// automatically stops or pulls after N Meters if enabled
self.stop_or_pull_spool(now);
// automatically stops or pulls after N Meters if enabled
self.stop_or_pull_spool(now);
// more than 33ms have passed since last emit (30 "fps" target)
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0)
{
self.maybe_emit_state_event();
self.emit_live_values();
self.last_measurement_emit = now;
}
})
// more than 33ms have passed since last emit (30 "fps" target)
if now.duration_since(self.last_measurement_emit) > Duration::from_secs_f64(1.0 / 30.0) {
self.maybe_emit_state_event();
self.emit_live_values();
self.last_measurement_emit = now;
}
}
}