Compare commits

...
Author SHA1 Message Date
Robin Krämer 97811a12d1 Merge pull request #842 from qitechgmbh/changelog-2.11.0
updated Changelog
2025-10-21 13:11:26 +02:00
kraemr 1f7e01820a updated Changelog 2025-10-21 13:10:37 +02:00
Robin Krämer 2f73771bbd Merge pull request #827 from qitechgmbh/825-add-button-to-change-the-spool-motor-direction-of-the-winder
825 add button to change the spool motor direction of the winder
2025-10-21 13:01:25 +02:00
Christian 503ac18e7b Ensure spool RPM is always displayed as a positive value 2025-10-21 12:37:34 +02:00
Christian 6a99b21c15 Fix spool speed rendering to display absolute values 2025-10-21 12:37:34 +02:00
Christian 170667f2b4 Add spool direction control to Winder2
- Introduced a new setting for spool direction (Forward/Reverse) in Winder2PresetsPage and Winder2Settings.
- Updated the state management to include forward direction in useWinder.
- Enhanced the API to handle SetSpoolForward mutation.
- Adjusted the spool speed controller to apply direction based on the forward setting.
2025-10-21 12:37:34 +02:00
Robin Krämer 314b7f2484 Merge pull request #840 from qitechgmbh/828-add-xl-winder-button-to-the-winder-config
828 add xl winder button to the winder config
2025-10-21 12:28:22 +02:00
Christian 9353b12707 Fix traverse limit reset logic in handleXlModeChange function 2025-10-21 11:51:02 +02:00
Christian 063810f906 Add gotoTraverseHome call when switching from XL to normal mode 2025-10-21 11:51:02 +02:00
Christian ac7ce126e6 Refactor handleXlModeChange to reset traverse limits when switching from XL mode 2025-10-21 11:51:02 +02:00
Christian a920830dcf Update WINDER2_TRAVERSE_MAX_XL to 385 mm for improved configuration 2025-10-21 11:51:02 +02:00
Christian 15d74fcee0 Add configuration for Winder2 settings and implement XL mode functionality 2025-10-21 11:51:02 +02:00
Robin Krämer c598e3fc9f Merge pull request #826 from qitechgmbh/818-smooth-fluctuating-temperature-extruder
818 smooth fluctuating temperature extruder
2025-10-20 18:16:08 +02:00
snacj 0dba0f2774 Removed package-lock from root folder 2025-10-20 18:12:06 +02:00
snacj e2cdb6a94c Removed package-lock from root folder 2025-10-20 18:12:06 +02:00
snacj 5dd2011e78 removed file 2025-10-20 18:12:06 +02:00
snacj 7bc142dc83 Fixed error in Extruder mock and removed zone from UpdateStateOptimistically 2025-10-20 18:12:06 +02:00
snacj 1931f00806 Changed rounding and step size to 0.001 for temperature PID 2025-10-20 18:12:06 +02:00
snacj 253336b02c Refactored frontend code of new PID settings 2025-10-20 18:12:06 +02:00
snacj b0c3a97f7f Fixed 3 New Pid Controllers for Extruder front, middle, back, heating. 2025-10-20 18:12:06 +02:00
snacj 22887d4f7a Added 3 New Pid Controllers for Extruder front, middle, back, heating. 2025-10-20 18:12:06 +02:00
Robin Krämer 00dbc90c85 Merge pull request #834 from qitechgmbh/lag-spike-fix
removed async sleep from control loop
2025-10-20 18:07:21 +02:00
kraemr 9ade1d8993 removed async sleep from control loop 2025-10-20 18:04:52 +02:00
Robin Krämer b2fb803033 Merge pull request #829 from qitechgmbh/808-lock-kernel-in-nix-to-last-working-rt-kernel
Newest kernel with realtime preemption
2025-10-20 13:42:38 +02:00
nixos-install.sh 988ed5b959 Newest kernel with realtime preemption 2025-10-17 17:36:14 +02:00
22 changed files with 534 additions and 53 deletions

No files matched your search

+2
View File
@@ -3,3 +3,5 @@ dist/
.DS_Store
Cargo.lock
dhat-heap.json
build.sh
+14
View File
@@ -1,3 +1,17 @@
# `2.11.0`
_21.10.2025_
## General
- [#829](https://github.com/qitechgmbh/control/pull/829), [#834](https://github.com/qitechgmbh/control/pull/834), Improved overall system stability
## Winder V2
- [#840](https://github.com/qitechgmbh/control/pull/840) Added XL Winder option
- [#827](https://github.com/qitechgmbh/control/pull/827) Spool can now be run in reverse
## Extruder V2
- [#826](https://github.com/qitechgmbh/control/pull/826) Fine grained Temperature-PID configuration now possible
# `2.10.0`
_16.10.2025_
+36 -22
View File
@@ -175,6 +175,7 @@
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz",
"integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.27.1",
@@ -638,6 +639,7 @@
"url": "https://opencollective.com/csstools"
}
],
"peer": true,
"engines": {
"node": ">=18"
},
@@ -660,6 +662,7 @@
"url": "https://opencollective.com/csstools"
}
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -1102,6 +1105,7 @@
"integrity": "sha512-lyb1zK3YHeWUjaz7yiK0GnxSPduwASKMyiDbCtbn3spP6EEt+UWtktggWehG0icFrXAk3GwvcJ4nCrJO0N9IhQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"electron-fuses": "dist/bin.js"
},
@@ -4028,6 +4032,7 @@
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.130.17.tgz",
"integrity": "sha512-F1tyMEbX7PJclnA25hAFXhLKq4jqeODvvaogsd0/NX02Kmz3QoT9GV+rgpv2TxamU6RwjRgihSH06Dn5hM6SOw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@tanstack/history": "1.130.12",
"@tanstack/react-store": "^0.7.0",
@@ -4109,6 +4114,7 @@
"resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.130.17.tgz",
"integrity": "sha512-0AH4s/52X2shYLifr9hR+6R6NpGsBp1M3LyjBTZm6Jlkud+nOhkkRxKxYS7l+JErRlffdxVHxMdoTnKcQ+duiQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@tanstack/history": "1.130.12",
"@tanstack/store": "^0.7.0",
@@ -4342,8 +4348,7 @@
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -4500,6 +4505,7 @@
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.10.0"
}
@@ -4516,6 +4522,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.16.tgz",
"integrity": "sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog==",
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -4525,6 +4532,7 @@
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz",
"integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
"devOptional": true,
"peer": true,
"peerDependencies": {
"@types/react": "^19.0.0"
}
@@ -4607,6 +4615,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz",
"integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
"dev": true,
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.38.0",
"@typescript-eslint/types": "8.38.0",
@@ -4961,6 +4970,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5408,6 +5418,7 @@
"url": "https://github.com/sponsors/ai"
}
],
"peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001726",
"electron-to-chromium": "^1.5.173",
@@ -6100,7 +6111,8 @@
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"peer": true
},
"node_modules/data-urls": {
"version": "5.0.0",
@@ -6373,8 +6385,7 @@
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/dom-helpers": {
"version": "5.2.1",
@@ -7014,16 +7025,6 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
},
"node_modules/encoding": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"dev": true,
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.2"
}
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
@@ -7384,6 +7385,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz",
"integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==",
"dev": true,
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -7444,6 +7446,7 @@
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true,
"peer": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
@@ -8750,6 +8753,7 @@
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
}
],
"peer": true,
"dependencies": {
"@babel/runtime": "^7.27.6"
},
@@ -8812,6 +8816,7 @@
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -9486,6 +9491,7 @@
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
"dev": true,
"peer": true,
"dependencies": {
"cssstyle": "^4.2.1",
"data-urls": "^5.0.0",
@@ -10051,7 +10057,6 @@
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -11994,6 +11999,7 @@
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -12107,7 +12113,6 @@
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -12122,7 +12127,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=10"
},
@@ -12251,6 +12255,7 @@
"version": "19.1.1",
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -12259,6 +12264,7 @@
"version": "19.1.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
"peer": true,
"dependencies": {
"scheduler": "^0.26.0"
},
@@ -12270,6 +12276,7 @@
"version": "7.62.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz",
"integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==",
"peer": true,
"engines": {
"node": ">=18.0.0"
},
@@ -12310,8 +12317,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
@@ -13062,6 +13068,7 @@
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz",
"integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==",
"peer": true,
"engines": {
"node": ">=10"
}
@@ -13851,7 +13858,8 @@
"node_modules/tailwindcss": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
"integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="
"integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==",
"peer": true
},
"node_modules/tailwindcss-animate": {
"version": "1.0.7",
@@ -13953,7 +13961,8 @@
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"peer": true
},
"node_modules/tiny-warning": {
"version": "1.0.3",
@@ -14004,6 +14013,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -14315,6 +14325,7 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"devOptional": true,
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -14676,6 +14687,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz",
"integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@@ -14797,6 +14809,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -15340,6 +15353,7 @@
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
@@ -6,6 +6,7 @@ import { SelectionGroupBoolean } from "@/control/SelectionGroup";
import { EditValue } from "@/control/EditValue";
import { roundToDecimals } from "@/lib/decimal";
import { useExtruder2 } from "./useExtruder";
import { ControlGrid } from "@/control/ControlGrid";
export function Extruder2SettingsPage() {
const {
@@ -18,6 +19,7 @@ export function Extruder2SettingsPage() {
setPressurePidKp,
setPressurePidKi,
setPressurePidKd,
setTemperaturePidValue,
} = useExtruder2();
const [showAdvanced, setShowAdvanced] = useState(false);
@@ -116,6 +118,128 @@ export function Extruder2SettingsPage() {
/>
</Label>
</ControlCard>
<ControlGrid>
<ControlCard title="Temperature PID Settings (Front) ">
<Label label="Kp">
<EditValue
value={state?.pid_settings.temperature.front.kp}
defaultValue={defaultState?.pid_settings.temperature.front.kp}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("front", "kp", v)}
title="Temperature PID KP"
/>
</Label>
<Label label="Ki">
<EditValue
value={state?.pid_settings.temperature.front.ki}
defaultValue={defaultState?.pid_settings.temperature.front.ki}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("front", "ki", v)}
title="Temperature PID KI"
/>
</Label>
<Label label="Kd">
<EditValue
value={state?.pid_settings.temperature.front.kd}
defaultValue={defaultState?.pid_settings.temperature.front.kd}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("front", "kd", v)}
title="Temperature PID KD"
/>
</Label>
</ControlCard>
<ControlCard title="Temperature PID Settings (Middle) ">
<Label label="Kp">
<EditValue
value={state?.pid_settings.temperature.middle.kp}
defaultValue={
defaultState?.pid_settings.temperature.middle.kp
}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("middle", "kp", v)}
title="Temperature PID KP"
/>
</Label>
<Label label="Ki">
<EditValue
value={state?.pid_settings.temperature.middle.ki}
defaultValue={
defaultState?.pid_settings.temperature.middle.ki
}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("middle", "ki", v)}
title="Temperature PID KI"
/>
</Label>
<Label label="Kd">
<EditValue
value={state?.pid_settings.temperature.middle.kd}
defaultValue={
defaultState?.pid_settings.temperature.middle.kd
}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("middle", "kd", v)}
title="Temperature PID KD"
/>
</Label>
</ControlCard>
<ControlCard title="Temperature PID Settings (Back) ">
<Label label="Kp">
<EditValue
value={state?.pid_settings.temperature.back.kp}
defaultValue={defaultState?.pid_settings.temperature.back.kp}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("back", "kp", v)}
title="Temperature PID KP"
/>
</Label>
<Label label="Ki">
<EditValue
value={state?.pid_settings.temperature.back.ki}
defaultValue={defaultState?.pid_settings.temperature.back.ki}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("back", "ki", v)}
title="Temperature PID KI"
/>
</Label>
<Label label="Kd">
<EditValue
value={state?.pid_settings.temperature.back.kd}
defaultValue={defaultState?.pid_settings.temperature.back.kd}
min={0}
max={100}
step={0.001}
renderValue={(v) => roundToDecimals(v, 3)}
onChange={(v) => setTemperaturePidValue("back", "kd", v)}
title="Temperature PID KD"
/>
</Label>
</ControlCard>
</ControlGrid>
</>
)}
</Page>
@@ -131,9 +131,24 @@ export const inverterStatusStateSchema = z.object({
*/
export const pidSettingsSchema = z.object({
temperature: z.object({
ki: z.number(),
kp: z.number(),
kd: z.number(),
front: z.object({
ki: z.number(),
kp: z.number(),
kd: z.number(),
zone: z.string(),
}),
middle: z.object({
ki: z.number(),
kp: z.number(),
kd: z.number(),
zone: z.string(),
}),
back: z.object({
ki: z.number(),
kp: z.number(),
kd: z.number(),
zone: z.string(),
}),
}),
pressure: z.object({
ki: z.number(),
@@ -293,6 +293,33 @@ export function useExtruder2() {
);
};
const setTemperaturePidValue = (
zone: "front" | "middle" | "back",
key: "kp" | "ki" | "kd",
value: number,
) => {
updateStateOptimistically(
(current) => {
current.data.pid_settings.temperature[zone][key] = value;
},
() => {
const currentState = stateOptimistic.value;
if (currentState) {
const settings = produce(
currentState.data.pid_settings.temperature[zone],
(draft) => {
draft[key] = value;
},
);
requestTemperaturePidSettings({
machine_identification_unique: machineIdentification,
data: { SetTemperaturePidSettings: settings },
});
}
},
);
};
const resetInverter = () => {
// No optimistic update needed for reset
requestResetInverter({
@@ -356,6 +383,17 @@ export function useExtruder2() {
}),
);
const { request: requestTemperaturePidSettings } = useMachineMutation(
z.object({
SetTemperaturePidSettings: z.object({
ki: z.number(),
kp: z.number(),
kd: z.number(),
zone: z.string(),
}),
}),
);
const { request: requestResetInverter } = useMachineMutation(
z.object({ ResetInverter: z.boolean() }),
);
@@ -404,6 +442,7 @@ export function useExtruder2() {
setPressurePidKp,
setPressurePidKi,
setPressurePidKd,
setTemperaturePidValue,
resetInverter,
};
}
@@ -28,9 +28,11 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { getWinder2TraverseMax } from "./winder2Config";
export function Winder2ControlPage() {
const [showResetConfirmDialog, setShowResetConfirmDialog] = useState(false);
const traverseMax = getWinder2TraverseMax();
// use optimistic state
const {
@@ -102,7 +104,7 @@ export function Winder2ControlPage() {
{state?.traverse_state && (
<TraverseBar
inside={0}
outside={180}
outside={traverseMax}
min={state?.traverse_state.limit_inner}
max={state?.traverse_state.limit_outer}
current={traversePosition.current?.value ?? 0}
@@ -121,7 +123,7 @@ export function Winder2ControlPage() {
min={Math.max(0, (state?.traverse_state?.limit_inner ?? 0) + 1)}
minLabel="IN"
maxLabel="OUT"
max={180}
max={traverseMax}
renderValue={(value) => roundToDecimals(value, 0)}
onChange={setTraverseLimitOuter}
/>
@@ -145,8 +147,8 @@ export function Winder2ControlPage() {
// We use 1mm buffer to ensure the backend validation (which requires outer > inner + 0.9mm) will pass
// Formula: max_inner = outer_limit - 1mm
max={Math.min(
180,
(state?.traverse_state?.limit_outer ?? 180) - 1,
traverseMax,
(state?.traverse_state?.limit_outer ?? traverseMax) - 1,
)}
defaultValue={defaultState?.traverse_state?.limit_inner}
minLabel="IN"
@@ -101,6 +101,11 @@ const previewEntries: PresetPreviewEntries<Winder2> = [
renderValue: (data: PresetData<Winder2>) =>
data.spool_speed_controller_state?.regulation_mode,
},
{
name: "Spool Direction",
renderValue: (data: PresetData<Winder2>) =>
data.spool_speed_controller_state?.forward ? "Forward" : "Reverse",
},
{
name: "Spool Min Speed",
unit: "rpm",
@@ -166,6 +171,7 @@ export function Winder2PresetsPage() {
setPullerGearRatio,
setSpoolRegulationMode,
setSpoolForward,
setSpoolMinMaxMinSpeed,
setSpoolMinMaxMaxSpeed,
@@ -194,6 +200,7 @@ export function Winder2PresetsPage() {
setSpoolRegulationMode(
preset.data?.spool_speed_controller_state?.regulation_mode ?? "MinMax",
);
setSpoolForward(preset.data?.spool_speed_controller_state?.forward ?? true);
setSpoolMinMaxMinSpeed(
preset.data?.spool_speed_controller_state?.minmax_min_speed ?? 0,
);
@@ -2,25 +2,37 @@ import { Page } from "@/components/Page";
import { ControlCard } from "@/control/ControlCard";
import { ControlGrid } from "@/control/ControlGrid";
import { EditValue } from "@/control/EditValue";
import React from "react";
import React, { useState } from "react";
import { useWinder2 } from "./useWinder";
import { roundToDecimals } from "@/lib/decimal";
import { Label } from "@/control/Label";
import { SelectionGroupBoolean } from "@/control/SelectionGroup";
import { SelectionGroup } from "@/control/SelectionGroup";
import { MachineSelector } from "@/components/MachineConnectionDropdown";
import {
getWinder2XLMode,
setWinder2XLMode,
WINDER2_TRAVERSE_MAX_STANDARD,
WINDER2_TRAVERSE_MAX_XL,
} from "./winder2Config";
export function Winder2SettingPage() {
const [xlMode, setXlMode] = useState(getWinder2XLMode());
const {
state,
defaultState,
setTraverseStepSize,
setTraversePadding,
setTraverseLimitInner,
setTraverseLimitOuter,
gotoTraverseHome,
setPullerForward,
setPullerGearRatio,
setSpoolRegulationMode,
setSpoolMinMaxMinSpeed,
setSpoolMinMaxMaxSpeed,
setSpoolForward,
setSpoolAdaptiveTensionTarget,
setSpoolAdaptiveRadiusLearningRate,
setSpoolAdaptiveMaxSpeedMultiplier,
@@ -34,10 +46,58 @@ export function Winder2SettingPage() {
disconnectMachine,
} = useWinder2();
const handleXlModeChange = (enabled: boolean) => {
setWinder2XLMode(enabled);
setXlMode(enabled);
// When switching from XL to normal mode, reset traverse limits to default values
if (!enabled && defaultState) {
// Only reset if current values exceed the standard max
const currentOuter = state?.traverse_state?.limit_outer ?? 0;
const currentInner = state?.traverse_state?.limit_inner ?? 0;
const defaultOuter = defaultState.traverse_state?.limit_outer;
const defaultInner = defaultState.traverse_state?.limit_inner;
if (
currentOuter > WINDER2_TRAVERSE_MAX_STANDARD &&
defaultOuter !== undefined
) {
setTraverseLimitOuter(defaultOuter);
setTraverseLimitInner(defaultInner);
}
if (
currentInner > WINDER2_TRAVERSE_MAX_STANDARD &&
defaultInner !== undefined
) {
setTraverseLimitOuter(defaultOuter);
setTraverseLimitInner(defaultInner);
}
// Home the traverse when switching from XL to normal mode
gotoTraverseHome();
}
};
return (
<Page>
<ControlGrid>
<ControlCard title="Traverse">
<Label label="Traverse Size">
<SelectionGroupBoolean
value={xlMode}
disabled={isDisabled}
loading={isLoading}
optionFalse={{
children: `Standard (${WINDER2_TRAVERSE_MAX_STANDARD}mm)`,
icon: "lu:Settings",
}}
optionTrue={{
children: `XL (${WINDER2_TRAVERSE_MAX_XL}mm)`,
icon: "lu:Maximize2",
}}
onChange={handleXlModeChange}
/>
</Label>
<Label label="Step Size">
<EditValue
value={state?.traverse_state?.step_size}
@@ -88,6 +148,23 @@ export function Winder2SettingPage() {
/>
</Label>
<Label label="Rotation Direction">
<SelectionGroupBoolean
value={state?.spool_speed_controller_state?.forward}
disabled={isDisabled}
loading={isLoading}
optionFalse={{
children: "Reverse",
icon: "lu:RotateCcw",
}}
optionTrue={{
children: "Forward",
icon: "lu:RotateCw",
}}
onChange={(value) => setSpoolForward(value)}
/>
</Label>
{state?.spool_speed_controller_state?.regulation_mode ===
"MinMax" && (
<>
@@ -134,6 +134,9 @@ export function useWinder2() {
const { request: requestSpoolSetMinMaxMaxSpeed } = useMachineMutation(
z.object({ SetSpoolMinMaxMaxSpeed: z.number() }),
);
const { request: requestSpoolSetForward } = useMachineMutation(
z.object({ SetSpoolForward: z.boolean() }),
);
const { request: requestSpoolSetAdaptiveTensionTarget } = useMachineMutation(
z.object({ SetSpoolAdaptiveTensionTarget: z.number() }),
);
@@ -467,6 +470,19 @@ export function useWinder2() {
);
};
const setSpoolForward = (forward: boolean) => {
updateStateOptimistically(
(current) => {
current.data.spool_speed_controller_state.forward = forward;
},
() =>
requestSpoolSetForward({
machine_identification_unique: machineIdentification,
data: { SetSpoolForward: forward },
}),
);
};
const setSpoolAdaptiveTensionTarget = (value: number) => {
updateStateOptimistically(
(current) => {
@@ -652,6 +668,7 @@ export function useWinder2() {
setSpoolRegulationMode,
setSpoolMinMaxMinSpeed,
setSpoolMinMaxMaxSpeed,
setSpoolForward,
setSpoolAdaptiveTensionTarget,
setSpoolAdaptiveRadiusLearningRate,
setSpoolAdaptiveMaxSpeedMultiplier,
@@ -0,0 +1,35 @@
/**
* Configuration helper for Winder2 machine settings
*/
const WINDER2_XL_MODE_KEY = "winder2_xl_mode";
export const WINDER2_TRAVERSE_MAX_STANDARD = 180; // mm
export const WINDER2_TRAVERSE_MAX_XL = 385; // mm
/**
* Get the XL mode setting from localStorage
* @returns true if XL mode is enabled, false otherwise
*/
export function getWinder2XLMode(): boolean {
const stored = localStorage.getItem(WINDER2_XL_MODE_KEY);
return stored === "true";
}
/**
* Set the XL mode setting in localStorage
* @param enabled - true to enable XL mode, false to disable
*/
export function setWinder2XLMode(enabled: boolean): void {
localStorage.setItem(WINDER2_XL_MODE_KEY, enabled.toString());
}
/**
* Get the maximum traverse limit based on XL mode
* @returns the maximum traverse limit in mm
*/
export function getWinder2TraverseMax(): number {
return getWinder2XLMode()
? WINDER2_TRAVERSE_MAX_XL
: WINDER2_TRAVERSE_MAX_STANDARD;
}
@@ -177,6 +177,7 @@ export const spoolSpeedControllerStateSchema = z.object({
adaptive_max_speed_multiplier: z.number(),
adaptive_acceleration_factor: z.number(),
adaptive_deacceleration_urgency_multiplier: z.number(),
forward: z.boolean(),
});
/**
+4 -1
View File
@@ -15,12 +15,15 @@ in
consoleMode = "max"; # Use the highest available resolution
};
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelPackages = pkgs.linuxPackages-rt_latest;
boot.kernelPackages = pkgs.linuxPackages_6_13;
boot.kernelModules = [ "i915" ];
boot.kernelParams = [
# Realtime Preemption
"preempt=full"
# Graphical
"logo.nologo" # Remove kernel logo during boot
+1 -7
View File
@@ -3,7 +3,7 @@ use bitvec::prelude::*;
use control_core::machines::connection::MachineConnection;
use control_core::realtime::{set_core_affinity, set_realtime_priority};
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::time::Instant;
use tracing::{instrument, trace_span};
pub fn init_loop(app_state: Arc<AppState>) -> Result<(), anyhow::Error> {
@@ -122,12 +122,6 @@ pub async fn loop_once<'maindevice>(app_state: Arc<AppState>) -> Result<(), anyh
)
})?;
}
#[cfg(feature = "development-build")]
smol::Timer::after(Duration::from_micros(2000)).await;
#[cfg(not(feature = "development-build"))]
smol::Timer::after(Duration::from_micros(500)).await;
}
// execute machines
+21 -1
View File
@@ -183,9 +183,24 @@ pub struct PidSettings {
pub kd: f64,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct TemperaturePidStates {
pub front: TemperaturePid,
pub middle: TemperaturePid,
pub back: TemperaturePid,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
pub struct TemperaturePid {
pub ki: f64,
pub kp: f64,
pub kd: f64,
pub zone: String,
}
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct PidSettingsStates {
pub temperature: PidSettings,
pub temperature: TemperaturePidStates,
pub pressure: PidSettings,
}
@@ -217,6 +232,7 @@ pub enum Mutation {
// Pid Configure
SetPressurePidSettings(PidSettings),
SetTemperaturePidSettings(TemperaturePid),
// Reset
ResetInverter(bool),
@@ -292,6 +308,10 @@ impl MachineApi for ExtruderV2 {
Mutation::SetPressurePidSettings(settings) => {
self.configure_pressure_pid(settings);
}
Mutation::SetTemperaturePidSettings(settings) => {
self.configure_temperature_pid(settings);
}
}
Ok(())
}
+50 -5
View File
@@ -5,7 +5,7 @@ use crate::machines::extruder1::{
api::{
ExtruderSettingsState, ExtruderV2Events, HeatingState, HeatingStates, InverterStatusState,
LiveValuesEvent, ModeState, PidSettings, PidSettingsStates, PressureState, RegulationState,
RotationState, ScrewState, StateEvent,
RotationState, ScrewState, StateEvent, TemperaturePid,
},
};
#[cfg(not(feature = "mock-machine"))]
@@ -27,6 +27,8 @@ use uom::si::thermodynamic_temperature::ThermodynamicTemperature;
#[cfg(not(feature = "mock-machine"))]
impl ExtruderV2 {
pub fn build_state_event(&mut self) -> StateEvent {
use crate::machines::extruder1::api::{TemperaturePid, TemperaturePidStates};
StateEvent {
is_default_state: !std::mem::replace(&mut self.emitted_default_state, true),
rotation_state: RotationState {
@@ -106,10 +108,25 @@ impl ExtruderV2 {
fault_occurence: self.screw_speed_controller.inverter.status.fault_occurence,
},
pid_settings: PidSettingsStates {
temperature: PidSettings {
ki: 0.0,
kp: 0.0,
kd: 0.0,
temperature: TemperaturePidStates {
front: TemperaturePid {
ki: self.temperature_controller_front.pid.get_ki(),
kp: self.temperature_controller_front.pid.get_kp(),
kd: self.temperature_controller_front.pid.get_kd(),
zone: String::from("front"),
},
middle: TemperaturePid {
ki: self.temperature_controller_middle.pid.get_ki(),
kp: self.temperature_controller_middle.pid.get_kp(),
kd: self.temperature_controller_middle.pid.get_kd(),
zone: String::from("middle"),
},
back: TemperaturePid {
ki: self.temperature_controller_back.pid.get_ki(),
kp: self.temperature_controller_back.pid.get_kp(),
kd: self.temperature_controller_back.pid.get_kd(),
zone: String::from("back"),
},
},
pressure: PidSettings {
ki: self.screw_speed_controller.pid.get_ki(),
@@ -279,4 +296,32 @@ impl ExtruderV2 {
.configure(settings.ki, settings.kp, settings.kd);
self.emit_state();
}
pub fn configure_temperature_pid(&mut self, settings: TemperaturePid) {
match settings.zone.as_str() {
"front" => {
self.temperature_controller_front.pid.configure(
settings.ki,
settings.kp,
settings.kd,
);
}
"middle" => {
self.temperature_controller_middle.pid.configure(
settings.ki,
settings.kp,
settings.kd,
);
}
"back" => {
self.temperature_controller_back.pid.configure(
settings.ki,
settings.kp,
settings.kd,
);
}
_ => tracing::warn!("Unknown zone: {}", settings.zone),
}
self.emit_state();
}
}
@@ -37,6 +37,10 @@ impl MachineApi for ExtruderV2 {
Mutation::SetPressurePidSettings(settings) => {
self.configure_pressure_pid(settings);
}
Mutation::SetTemperaturePidSettings(settings) => {
self.configure_temperature_pid(settings);
}
}
Ok(())
}
@@ -1,6 +1,6 @@
use crate::machines::extruder1::{
ExtruderV2Mode, HeatingType,
api::{ExtruderV2Events, LiveValuesEvent, ModeState, PidSettings, StateEvent},
api::{ExtruderV2Events, LiveValuesEvent, ModeState, PidSettings, StateEvent, TemperaturePid},
mock::ExtruderV2,
};
use control_core::{
@@ -133,4 +133,26 @@ impl ExtruderV2 {
self.pid_settings.pressure.kd = settings.kd;
self.emit_state();
}
pub fn configure_temperature_pid(&mut self, settings: TemperaturePid) {
match settings.zone.as_str() {
"front" => {
self.pid_settings.temperature.front.ki = settings.ki;
self.pid_settings.temperature.front.kp = settings.kp;
self.pid_settings.temperature.front.kd = settings.kd;
}
"middle" => {
self.pid_settings.temperature.middle.ki = settings.ki;
self.pid_settings.temperature.middle.kp = settings.kp;
self.pid_settings.temperature.middle.kd = settings.kd;
}
"back" => {
self.pid_settings.temperature.back.ki = settings.ki;
self.pid_settings.temperature.back.kp = settings.kp;
self.pid_settings.temperature.back.kd = settings.kd;
}
_ => tracing::warn!("Unknown zone: {}", settings.zone),
}
self.emit_state();
}
}
+21 -5
View File
@@ -3,7 +3,8 @@ use crate::machines::extruder1::{
api::{
ExtruderSettingsState, ExtruderV2Namespace, HeatingState, HeatingStates,
InverterStatusState, ModeState, MotorStatusValues, PidSettings, PidSettingsStates,
PressureState, RegulationState, RotationState, ScrewState,
PressureState, RegulationState, RotationState, ScrewState, TemperaturePid,
TemperaturePidStates,
},
mock::ExtruderV2,
};
@@ -88,10 +89,25 @@ impl control_core::machines::new::MachineNewTrait for ExtruderV2 {
fault_occurence: false,
},
pid_settings: PidSettingsStates {
temperature: PidSettings {
ki: 0.0,
kp: 0.0,
kd: 0.0,
temperature: TemperaturePidStates {
front: TemperaturePid {
ki: 0.0,
kp: 0.0,
kd: 0.0,
zone: String::from("front"),
},
middle: TemperaturePid {
ki: 0.0,
kp: 0.0,
kd: 0.0,
zone: String::from("middle"),
},
back: TemperaturePid {
ki: 0.0,
kp: 0.0,
kd: 0.0,
zone: String::from("back"),
},
},
pressure: PidSettings {
ki: 0.0,
+4
View File
@@ -83,6 +83,7 @@ enum Mutation {
SetSpoolRegulationMode(super::spool_speed_controller::SpoolSpeedControllerType),
SetSpoolMinMaxMinSpeed(f64),
SetSpoolMinMaxMaxSpeed(f64),
SetSpoolForward(bool),
// Adaptive Spool Speed Controller Parameters
SetSpoolAdaptiveTensionTarget(f64),
@@ -241,6 +242,8 @@ pub struct SpoolSpeedControllerState {
pub adaptive_acceleration_factor: f64,
/// deacceleration urgency multiplier for adaptive mode
pub adaptive_deacceleration_urgency_multiplier: f64,
/// forward rotation direction
pub forward: bool,
}
pub enum Winder2Events {
@@ -304,6 +307,7 @@ impl MachineApi for Winder2 {
Mutation::SetSpoolRegulationMode(mode) => self.spool_set_regulation_mode(mode),
Mutation::SetSpoolMinMaxMinSpeed(speed) => self.spool_set_minmax_min_speed(speed),
Mutation::SetSpoolMinMaxMaxSpeed(speed) => self.spool_set_minmax_max_speed(speed),
Mutation::SetSpoolForward(value) => self.spool_set_forward(value),
Mutation::SetSpoolAdaptiveTensionTarget(value) => {
self.spool_set_adaptive_tension_target(value)
}
+19 -3
View File
@@ -30,9 +30,17 @@ impl Winder2 {
&self.tension_arm,
&self.puller_speed_controller,
);
// Apply direction based on forward setting
let directed_angular_velocity = if self.spool_speed_controller.get_forward() {
angular_velocity
} else {
-angular_velocity
};
let steps_per_second = self
.spool_step_converter
.angular_velocity_to_steps(angular_velocity);
.angular_velocity_to_steps(directed_angular_velocity);
let _ = self.spool.set_speed(steps_per_second);
}
@@ -174,11 +182,12 @@ impl Winder2 {
// Divide by gear ratio to get actual puller/material speed
let puller_speed = motor_speed / self.puller_speed_controller.get_gear_ratio().multiplier();
// Calculate spool RPM from current motor steps
// Calculate spool RPM from current motor steps (always positive regardless of direction)
let spool_rpm = self
.spool_step_converter
.steps_to_angular_velocity(self.spool.get_speed() as f64)
.get::<revolution_per_minute>();
.get::<revolution_per_minute>()
.abs();
let live_values = LiveValuesEvent {
traverse_position: self
@@ -270,6 +279,7 @@ impl Winder2 {
adaptive_deacceleration_urgency_multiplier: self
.spool_speed_controller
.get_adaptive_deacceleration_urgency_multiplier(),
forward: self.spool_speed_controller.get_forward(),
},
spool_automatic_action_state: SpoolAutomaticActionState {
spool_required_meters: self.spool_automatic_action.target_length.get::<meter>(),
@@ -472,6 +482,12 @@ impl Winder2 {
self.emit_state();
}
/// Set forward rotation direction
pub fn spool_set_forward(&mut self, forward: bool) {
self.spool_speed_controller.set_forward(forward);
self.emit_state();
}
/// implement machine connection
/// set connected buffer
pub fn set_connected_buffer(
@@ -21,6 +21,7 @@ pub struct SpoolSpeedController {
adaptive_controller: AdaptiveSpoolSpeedController,
minmax_controller: MinMaxSpoolSpeedController,
r#type: SpoolSpeedControllerType,
forward: bool,
}
impl Default for SpoolSpeedController {
@@ -35,6 +36,7 @@ impl SpoolSpeedController {
adaptive_controller: AdaptiveSpoolSpeedController::new(),
minmax_controller: MinMaxSpoolSpeedController::new(),
r#type: SpoolSpeedControllerType::Adaptive,
forward: true,
}
}
@@ -180,4 +182,12 @@ impl SpoolSpeedController {
self.adaptive_controller
.set_deacceleration_urgency_multiplier(deacceleration_urgency_multiplier);
}
pub const fn get_forward(&self) -> bool {
self.forward
}
pub const fn set_forward(&mut self, forward: bool) {
self.forward = forward;
}
}