Compare commits

...
Author SHA1 Message Date
just-some-entity 8acbb8b082 reset package-lock.json 2026-01-27 15:31:29 +00:00
just-some-entity 4a7682f3a6 hopefully fix nix npm dep issues 2026-01-27 14:40:04 +00:00
just-some-entity 491fe78572 update stuff 2026-01-27 14:33:47 +00:00
just_some_entity b07b4a03e1 update pelletizer and fix bugs 2026-01-27 14:14:53 +01:00
just-some-entity 3f1af9bf9d apply fixes 2026-01-24 17:10:40 +00:00
just-some-entity 4b08be6bed get into working state 2026-01-24 16:12:10 +00:00
just_some_entity 0572c1030f port from fork 2026-01-24 11:51:43 +01:00
37 changed files with 2986 additions and 224 deletions

No files matched your search

Generated
+215 -157
View File
@@ -127,16 +127,16 @@ dependencies = [
"futures-lite",
"parking",
"polling",
"rustix 1.1.2",
"rustix 1.1.3",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-lock"
version = "3.4.1"
version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
dependencies = [
"event-listener",
"event-listener-strategy",
@@ -169,7 +169,7 @@ dependencies = [
"cfg-if",
"event-listener",
"futures-lite",
"rustix 1.1.2",
"rustix 1.1.3",
]
[[package]]
@@ -184,7 +184,7 @@ dependencies = [
"cfg-if",
"futures-core",
"futures-io",
"rustix 1.1.2",
"rustix 1.1.3",
"signal-hook-registry",
"slab",
"windows-sys 0.61.2",
@@ -204,7 +204,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -221,7 +221,7 @@ checksum = "99e1aca718ea7b89985790c94aad72d77533063fe00bc497bb79a7c2dae6a661"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -232,9 +232,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "axum"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [
"axum-core",
"axum-macros",
@@ -266,9 +266,9 @@ dependencies = [
[[package]]
name = "axum-core"
version = "0.5.5"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22"
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
dependencies = [
"bytes",
"futures-core",
@@ -291,7 +291,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -372,9 +372,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.19.0"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
[[package]]
name = "bytemuck"
@@ -405,9 +405,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.2.46"
version = "1.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -427,9 +427,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chrono"
version = "0.4.42"
version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
dependencies = [
"iana-time-zone",
"num-traits",
@@ -465,18 +465,18 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.51"
version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.51"
version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
dependencies = [
"anstyle",
"clap_lex",
@@ -484,9 +484,9 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.7.6"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
[[package]]
name = "colored"
@@ -539,7 +539,7 @@ version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -580,9 +580,9 @@ dependencies = [
[[package]]
name = "crc"
version = "3.3.0"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
dependencies = [
"crc-catalog",
]
@@ -668,9 +668,9 @@ dependencies = [
[[package]]
name = "data-encoding"
version = "2.9.0"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "deluxe"
@@ -682,7 +682,7 @@ dependencies = [
"deluxe-macros",
"once_cell",
"proc-macro2",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -695,7 +695,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -705,12 +705,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87546d9c837f0b7557e47b8bd6eae52c3c223141b76aa233c345c9ab41d9117"
dependencies = [
"deluxe-core",
"heck",
"heck 0.4.1",
"if_chain",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -721,7 +721,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -962,7 +962,7 @@ dependencies = [
"deluxe",
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -980,7 +980,7 @@ dependencies = [
"embedded-io-async",
"ethercrab-wire",
"futures-lite",
"heapless",
"heapless 0.8.0",
"io-uring",
"libc",
"log",
@@ -1002,7 +1002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d15828cb020fb44c92ca67e1ee3c122c9621c2e00d383d066e488f0619023ba2"
dependencies = [
"ethercrab-wire-derive",
"heapless",
"heapless 0.8.0",
]
[[package]]
@@ -1014,14 +1014,14 @@ dependencies = [
"criterion",
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
name = "euclid"
version = "0.22.11"
version = "0.22.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48"
checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63"
dependencies = [
"num-traits",
]
@@ -1055,9 +1055,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
version = "0.1.5"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
[[package]]
name = "fnv"
@@ -1170,9 +1170,9 @@ checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
[[package]]
name = "h2"
version = "0.4.12"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
dependencies = [
"atomic-waker",
"bytes",
@@ -1223,9 +1223,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.16.0"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "heapless"
@@ -1237,12 +1237,28 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "heapless"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.5.2"
@@ -1251,12 +1267,11 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "http"
version = "1.3.1"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"fnv",
"itoa",
]
@@ -1339,9 +1354,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.18"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
dependencies = [
"bytes",
"futures-channel",
@@ -1352,7 +1367,7 @@ dependencies = [
"hyper",
"libc",
"pin-project-lite",
"socket2 0.6.1",
"socket2 0.6.2",
"tokio",
"tower-service",
"tracing",
@@ -1390,9 +1405,9 @@ checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb"
[[package]]
name = "indexmap"
version = "2.12.0"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown",
@@ -1466,15 +1481,15 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.15"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "js-sys"
version = "0.3.82"
version = "0.3.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -1488,9 +1503,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.177"
version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "libloading"
@@ -1551,9 +1566,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.28"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "mach2"
@@ -1577,11 +1592,14 @@ dependencies = [
"ethercrab",
"euclid",
"lazy_static",
"modbus",
"serde",
"serde_json",
"serialport",
"smol",
"socketioxide",
"strum",
"strum_macros 0.26.4",
"textplots",
"tonic",
"tracing",
@@ -1660,15 +1678,26 @@ checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536"
[[package]]
name = "mio"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [
"libc",
"wasi",
"windows-sys 0.61.2",
]
[[package]]
name = "modbus"
version = "0.1.0"
source = "git+https://github.com/just-some-entity/modbus-rs.git#e71a27aaf4356402d38dca6224e8c2b66183dc1e"
dependencies = [
"crc",
"heapless 0.9.2",
"libc",
"strum_macros 0.27.2",
]
[[package]]
name = "nb"
version = "0.1.3"
@@ -1821,17 +1850,11 @@ dependencies = [
"windows-link",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pcap"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83cdabc34a80d9ec3563694cc31423fba6bb9bab4f31a9a5d5b85f29bd6d660a"
checksum = "bbd2eecc2ddc671ec563b5b39f846556aade68a65d1afb14d8fe6b30b0457d75"
dependencies = [
"bitflags 1.3.2",
"errno 0.2.8",
@@ -1850,9 +1873,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pest"
version = "2.8.3"
version = "2.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4"
checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7"
dependencies = [
"memchr",
"ucd-trie",
@@ -1860,9 +1883,9 @@ dependencies = [
[[package]]
name = "pest_derive"
version = "2.8.3"
version = "2.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de"
checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed"
dependencies = [
"pest",
"pest_generator",
@@ -1870,22 +1893,22 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.8.3"
version = "2.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843"
checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
name = "pest_meta"
version = "2.8.3"
version = "2.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a"
checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365"
dependencies = [
"pest",
"sha2",
@@ -1908,7 +1931,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -1982,7 +2005,7 @@ dependencies = [
"concurrent-queue",
"hermit-abi",
"pin-project-lite",
"rustix 1.1.2",
"rustix 1.1.3",
"windows-sys 0.61.2",
]
@@ -2007,9 +2030,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.103"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
@@ -2072,9 +2095,9 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.9.3"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom",
]
@@ -2128,31 +2151,28 @@ dependencies = [
[[package]]
name = "rmp"
version = "0.8.14"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
dependencies = [
"byteorder",
"num-traits",
"paste",
]
[[package]]
name = "rmp-serde"
version = "1.3.0"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
dependencies = [
"byteorder",
"rmp",
"serde",
]
[[package]]
name = "rustc-demangle"
version = "0.1.26"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]]
name = "rustc-hash"
@@ -2175,9 +2195,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
dependencies = [
"bitflags 2.10.0",
"errno 0.3.14",
@@ -2194,9 +2214,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
[[package]]
name = "same-file"
@@ -2221,7 +2241,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -2251,20 +2271,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
name = "serde_json"
version = "1.0.145"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
"zmij",
]
[[package]]
@@ -2381,10 +2401,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.6"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno 0.3.14",
"libc",
]
@@ -2453,9 +2474,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.6.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
dependencies = [
"libc",
"windows-sys 0.60.2",
@@ -2560,6 +2581,37 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
[[package]]
name = "strum_macros"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.114",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "syn"
version = "1.0.109"
@@ -2573,9 +2625,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.111"
version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
@@ -2606,22 +2658,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -2660,15 +2712,15 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.48.0"
version = "1.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
dependencies = [
"bytes",
"libc",
"mio",
"pin-project-lite",
"socket2 0.6.1",
"socket2 0.6.2",
"tokio-macros",
"windows-sys 0.61.2",
]
@@ -2681,14 +2733,14 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
name = "tokio-stream"
version = "0.1.17"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
dependencies = [
"futures-core",
"pin-project-lite",
@@ -2709,9 +2761,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.17"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
dependencies = [
"bytes",
"futures-core",
@@ -2768,9 +2820,9 @@ dependencies = [
[[package]]
name = "tower"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
dependencies = [
"futures-core",
"futures-util",
@@ -2787,9 +2839,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.6.6"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [
"bitflags 2.10.0",
"bytes",
@@ -2843,7 +2895,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -2858,9 +2910,9 @@ dependencies = [
[[package]]
name = "tracing-journald"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0b4143302cf1022dac868d521e36e8b27691f72c84b3311750d5188ebba657"
checksum = "2d3a81ed245bfb62592b1e2bc153e77656d94ee6a0497683a65a12ccaf2438d0"
dependencies = [
"libc",
"tracing-core",
@@ -2880,9 +2932,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.20"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
dependencies = [
"chrono",
"matchers",
@@ -2940,18 +2992,18 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]]
name = "unescaper"
version = "0.1.6"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26"
checksum = "4064ed685c487dbc25bd3f0e9548f2e34bab9d18cefc700f9ec2dba74ba1138e"
dependencies = [
"thiserror",
]
[[package]]
name = "unicase"
version = "2.8.1"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]]
name = "unicode-ident"
@@ -3035,18 +3087,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.105"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
dependencies = [
"cfg-if",
"once_cell",
@@ -3057,9 +3109,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.105"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3067,22 +3119,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.105"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.105"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
dependencies = [
"unicode-ident",
]
@@ -3139,7 +3191,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -3150,7 +3202,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
@@ -3396,9 +3448,9 @@ dependencies = [
[[package]]
name = "wit-bindgen"
version = "0.46.0"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "wyz"
@@ -3411,20 +3463,26 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.27"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
checksum = "71ddd76bcebeed25db614f82bf31a9f4222d3fbba300e6fb6c00afa26cbd4d9d"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.27"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
checksum = "d8187381b52e32220d50b255276aa16a084ec0a9017a0ca2152a1f55c539758d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
"syn 2.0.114",
]
[[package]]
name = "zmij"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
@@ -18,11 +18,11 @@ pub fn set_interface_managed(interface: &str, managed: bool) -> bool {
true => "yes",
false => "no",
};
tracing::info!(
"set_interface_managed for {} managed was set to: {}",
interface,
managed_str
);
// tracing::info!(
// "set_interface_managed for {} managed was set to: {}",
// interface,
// managed_str
// );
let status = Command::new("nmcli")
.args(["dev", "set", interface, "managed", managed_str])
.status();
@@ -52,7 +52,7 @@ fn is_ethernet(interface: &str) -> std::io::Result<bool> {
}
pub async fn discover_ethercat_interface() -> Result<String, anyhow::Error> {
tracing::info!("Discovering EtherCAT interface...");
//tracing::info!("Discovering EtherCAT interface...");
// Set up a custom panic hook that suppresses panic output
let default_hook = std::panic::take_hook();
@@ -119,7 +119,7 @@ pub async fn discover_ethercat_interface() -> Result<String, anyhow::Error> {
}
fn test_interface(interface: &str) -> Result<(), anyhow::Error> {
tracing::info!("Testing interface: {}", interface);
//tracing::info!("Testing interface: {}", interface);
let pdu_storage = Box::leak(Box::new(PduStorage::<
IFACE_DISCOVERY_MAX_FRAMES,
+5 -5
View File
@@ -52,11 +52,11 @@ pub fn set_interface_managed(interface: &str, managed: bool) -> bool {
true => "yes",
false => "no",
};
tracing::info!(
"set_interface_managed for {} managed was set to: {}",
interface,
managed_str
);
// tracing::info!(
// "set_interface_managed for {} managed was set to: {}",
// interface,
// managed_str
// );
let status = Command::new("nmcli")
.args(["dev", "set", interface, "managed", managed_str])
.status();
+344
View File
@@ -0,0 +1,344 @@
# Documentation for the US-3202510 220 V Single‑Phase to Three‑Phase 0.75 kW VFD Inverter
## 1. Specification, Features and Technical Data
### 1.1 Specification
- **Control Mode:** V/F Control
- **Nominal Voltage:** 220 V
- **Rated Power:** 750 W, 400 W, 200 W, 300 W
- **Input Voltage:** Single Phase 220 V
- **Output Voltage:** Three Phase 220 V
- **Dimensions:** 52.00 / 80.00 / 90.00 / 147.00 mm (reference)
---
### 1.2 Features
- Output frequency range: **1.0–99.0 Hz**, suitable for general speed control
- Built-in intelligent logic controller for simple logic control
- Compact size, easy installation, simple commissioning
- Clear and easy-to-understand parameters for general applications
- Equipped with a built-in speed potentiometer; external potentiometer supported
- Specifically designed for **220 V, ≤0.75 kW three-phase AC motors**
- Integrated solution for equipment manufacturers, reducing system cost and improving reliability
- Electronic thermal overload protection, replacing traditional motor protection devices
- External LED indicator output supported for on-site indication
- User-friendly interface with simple parameter setting
- Adjustable V/F curve for special applications
- Shift key allows viewing real-time parameters
- Uses a new-generation **IPM module** with comprehensive protection functions
---
### 1.3 Models and Technical Data
**Single-phase power supply: 200–240 VAC, 50/60 Hz**
| Inverter Model | Power Capacity (kVA) | Input Current (A) | Output Current (A) | Compatible Motor (kW) |
| ----------------| ----------------------| -------------------| --------------------| -----------------------|
| US00R2G1 | 3.0 | 3.2 | 1.6 | 0.2 |
| US00R4G1 | 3.8 | 4.3 | 2.1 | 0.4 |
| US00R7G1 | 5.6 | 6.0 | 3.1 | 0.75 |
---
### 1.4 Front Panel and Terminal Diagram
<img src="../assets/US-3202510_spec.png" alt="VFD Front Panel and Terminal Layout" style="max-width:600px;">
---
### 1.5 Dimensions
<img src="../assets/US-3202510_dimensions.png" alt="VFD Front Panel and Terminal Layout" style="max-width:600px;">
---
## 2. Terminals and Wiring
### 2.1 Main Terminal Wiring
| Terminal Mark | Name | Description |
| ---------------| --------------------------| --------------------------------------------------|
| AC, AC | Single-phase power input | Connection point for single-phase 220 V AC power |
| U, V, W | Inverter output | Connect to three-phase AC motor |
| FG | Ground terminal | Protective earth connection |
---
### 2.2 Signal Terminal Wiring
| Category | Terminal Symbol | Terminal Name | Function Description |
| ----------------| -----------------| ------------------------| ----------------------------------------------|
| Power | 5V, COM | External 5V power | Provides 5V power for external potentiometer |
| Analog input | VR, COM | Analog input | Input voltage range: DC 0–5 V |
| Digital input | M1, COM | Forward run | Forward rotation control |
| Digital input | M2 / D3, COM | Reverse / multi-speed | Reverse rotation or multi-speed stage 3 |
| Digital input | D2, COM | Multi-speed terminal 2 | Multi-speed function 2 |
| Digital input | D1, COM | Multi-speed terminal 1 | Multi-speed function 1 |
| Digital output | M0, 5V | Digital output | For external 5V relay or indicator |
| Communication | RS−, RS+ | RS485 interface | MODBUS control |
---
### 2.2.1 Signal Terminal Layout
| Index | Symbol |
| -------| --------|
| 1 | +5V |
| 2 | VR |
| 3 | COM |
| 4 | M0 |
| 5 | M1 |
| 6 | M2/D3 |
| 7 | RS485- |
| 8 | RS485+ |
| 9 | D2 |
| 10 | D1 |
---
## 3. Control Panel
### 3.1 Indicator Lights and Display
| Indicator | Function | Description |
| -----------| -------------------| -------------------------------------------------------------|
| POWER | Power indicator | Steady ON = power on; Red flashing = keypad locked |
| FWD | Forward indicator | Steady ON = motor running forward; Flashing = motor stopped |
| REV | Reverse indicator | Steady ON = motor running reverse; Flashing = motor stopped |
| Display | Display | Shows current parameters and fault information |
---
### 3.2 Physical Controls
| Key / Control | Type | Function (Run Mode) | Function (Parameter Setting Mode) |
| ---------------| ---------------| -----------------------------------------------------------| ---------------------------------------------------------------------------|
| P.K / SHIFT | Button | View IPM temperature, DC bus voltage/current, motor speed | Shift digit |
| MENU / ESC | Button | Enter parameter setting mode | Exit |
| SAVE / LOCK | Button | — | Long press to lock/unlock keypad; auto-lock after 3 minutes of inactivity |
| FWD / REV | Button | Toggle forward/reverse rotation | — |
| ↑ | Button | — | Increase value |
| ON / OFF | Button | Start/stop motor | Confirm value |
| ↓ | Button | — | Decrease value |
| SPEED | Potentiometer | Adjust motor speed | — |
---
## 4. Parameters
### 4.1 Editing Parameters
1. **Enter Edit Mode**
- Press the **MENU/ESC** key.
- The digital display will flash `-0.1-`.
2. **Select Function Code**
- Use the **↑/↓ keys** to adjust the value and choose the desired **function code**.
- Refer to **Table 4.1** below for available function codes.
3. **Set Sub-Code**
- During the setting process, use the **P.K/SHIFT** and **↑/↓ keys** to select the sub-code.
- Press **ON/OFF** to enter the sub-code selection.
- After selecting the sub-code, press **ON/OFF** again to return to the function code screen (display flashes `-X.X-`).
- Repeat for the next function code as needed.
4. **Save Settings**
- After all settings are selected, press the **SAVE/LOCK** key (display flashes `SAVE`).
- Press **SAVE/LOCK** again to confirm saving.
- When the display stops flashing, the data is saved.
5. **Operation After Saving**
- The VFD will run according to the saved parameters **without needing a power cycle**.
6. **Exit Without Saving**
- Press **MENU/ESC** to exit without saving — previous parameters remain unchanged.
- If no key is pressed for **20 seconds**, the interface will automatically return to the run screen.
---
### 4.2 Parameter Table
| Function Code | Definition | Factory Value | Setting Range | Description |
| ---------------| -------------------------------------------------| ---------------| ---------------| ---------------------------------------------------------------------------------------------------|
| 0.1 | Acceleration Time | 7 | 1–15 | 1–15 corresponds to 5–0.1 s; smaller value = faster (each step is about twice as fast) |
| 0.2 | Deceleration Time | 7 | 1–15 | 1–15 corresponds to 5–0.1 s; smaller value = faster (each step is about twice as fast) |
| 0.4 | V/F Frequency Compensation | 20 | 5–30 Hz | V/F frequency compensation |
| 0.5 | V/F Voltage Compensation | 41 | 25–85 | V/F voltage compensation |
| 0.7 | MODBUS Communication Baud Rate | 4 | 024–384 | 096 = 9600 bps |
| 0.9 | MODBUS Address | 1 | 1–249 | MODBUS address |
| 1.0 | Frequency Source Selection | 2 | 0–4 | 0: Panel keys, 1: Panel potentiometer, 2: External analog VR, 3: MODBUS 485, 4: Multi-speed input |
| 1.1 | Command Source Selection | 0 | 0–4 | 2: Power-on forward, 3: Power-on reverse, 4: External port, 0: free stop |
| 1.2 | Stop Mode | 1 | 0–2 | 1: Deceleration stop, 2: Immediate stop; 0: MI1 forward/stop, MI2 reverse/stop |
| 1.3 | MI Function Selection | 0 | 0–2 | 1: MI1 run/stop, MI2 reverse/forward; 2: MI1 run/stop, MI2 multi-speed |
| 1.4 | MO Function Selection | 0 | 0–2 | 1: Setting reached indicator, 2: Fault indicator |
| 1.6 | Overheat Protection Value | 90 | 40–100 °C | Overheat protection threshold |
| 1.7 | Maximum Operating Frequency | 50.0 | 1.0–99.0 Hz | Maximum frequency setting |
| 1.8 | Minimum Operating Frequency | 0 | 0.0–30.0 Hz | Minimum frequency setting |
| 1.9 | Rated Operating Frequency | 50.0 | 1.0–99.0 Hz | Default power-on frequency |
| 2.0 | Output Voltage Corresponding Frequency | 50.0 | 35–99.0 Hz | Output voltage corresponding frequency |
| 2.1 | Multi-speed 1 Set Frequency | 5.0 | 1.0–99.0 Hz | Multi-speed 1 frequency |
| 2.2 | Multi-speed 2 Set Frequency | 10.0 | 1.0–99.0 Hz | Multi-speed 2 frequency |
| 2.3 | Multi-speed 3 Set Frequency | 20.0 | 1.0–99.0 Hz | Multi-speed 3 frequency |
| 2.4 | Multi-speed 4 Set Frequency | 25.0 | 1.0–99.0 Hz | Multi-speed 4 frequency |
| 2.5 | Multi-speed 5 Set Frequency | 35.0 | 1.0–99.0 Hz | Multi-speed 5 frequency |
| 2.6 | Multi-speed 6 Set Frequency | 40.0 | 1.0–99.0 Hz | Multi-speed 6 frequency |
| 2.7 | Multi-speed 7 Set Frequency | 45.0 | 1.0–99.0 Hz | Multi-speed 7 frequency |
| 2.8 | Frequency Reached During Run | 45.0 | 1.0–99.0 Hz | Frequency reached during operation |
| 3.5 | Motor Pole Pairs | 2 | 1–6 | 2 corresponds to 1500 rpm |
| 3.7 | Motor Speed | 1500 | 1–9999 | Motor operating speed |
| 3.8 | Multi-speed 0 Set Frequency | 1.0 | 1.0–99.0 Hz | Multi-speed 0 frequency |
| 4.0 | Overcurrent Protection Value | 8.00 A | 2.00–9.00 A | Motor protection current setting |
| 4.1 | Overvoltage Protection Value | 520.0 V | 100–900 V | Motor protection voltage setting |
| 4.2 | Overload Protection Value | 2400 W | 400–2700 W | Motor protection power setting |
| 4.3 | Carrier Frequency Setting | 0 | 0–1 | 0: 10 kHz, 1: 20 kHz |
| 4.4 | Power-loss Detection Enable | 1 | 0–1 | 0: Off, 1: On; 0 = fan and motor start/stop together |
| 4.5 | Fan Operation Mode | 0 | 0–2 | 1: Fan stops 5 s after motor stops; 2: Fan runs continuously until power off |
| 4.6 | Motor Speed Synchronization Mode | 0–2 | 0–2 | 1: Motor speed sync within 300 ms after speed change; 2: Motor sync every 1 s |
| 5.0 | Bluetooth / RS485 / External Terminal Selection | 0–2 | 0–2 | 1: Serial = Bluetooth; 2: Serial = external expansion |
| 6.0 | Expansion Board – Year/Month | — | — | 0x23 = 2023, 0x10 = October |
| 6.1 | Expansion Board – Day/Version | — | — | 0x25 = 25th, 0x10 = version 1.0 |
| 6.2 | Expansion Board – Response Interval | 1000 | 100–5000 ms | Time interval for expansion board reporting |
| 6.3 | Expansion Board – Response Enable | 0–1 | 0–1 | 0: Do not report; 1: Enable reporting |
| 6.8 | Expansion Board – Voltage | — | — | Voltage reading, e.g., 503 = 5.03 V |
| 9.1 | Restore Factory Settings | — | — | Display flashes `CL`; press confirm to restore |
| 9.2 | Firmware Version | 107 | — | Display version number, e.g., V107 |
| 9.3 | Output Waveform Selection | 0–1 | 0–1 | 0: Standard sine; 1: Enhanced sine control waveform |
| 9.4 | MCU Reset | — | — | Display flashes 8.88; press confirm to reset MCU |
| 9.6 | Continuous Motor Speed Display | 0 | 0–1 | 0: Auto return to frequency display after inactivity; 1: auto return to speed display |
| 9.7 | Panel 3-Minute Lock Switch | 0 | 0–1 | 0: Auto lock after 3 minutes no operation; 1: No lock |
| 9.8 | Motor Speed Display Multiplier | 0 | 0–100 | 0: display off; other: divides speed, e.g., 4 → 1500 rpm becomes 375 rpm (requires 9.6 = 1) |
---
## 5. Fault Alarms and Countermeasures
The VFD has multiple warning indicators and protection functions.
When an abnormal fault occurs, the protection function activates and the VFD stops output. The display panel shows a fault code.
### Table 5.1 – Fault Codes
| Fault Code | Fault Name | Fault Description | Countermeasure |
| ------------| ------------------------------------| ----------------------------------------------------------------------------------------------------| ---------------------------------------------|
| E-0.1 | VFD Overheat | VFD temperature exceeds 1.6× set protection value; display flashes; max frequency limited to 30 Hz | After clearing the fault, restart the motor |
| E-0.2 | Pulse Overcurrent | VFD current exceeds 4.0× set protection value; motor stops | After clearing the fault, restart the motor |
| E-0.4 | VFD Overload | VFD voltage × current exceeds 4.2× set power; display flashes; operation continues | After clearing the fault, restart the motor |
| E-0.5 | Approaching IGBT Temperature Limit | VFD temperature >120°C, near IGBT limit; motor stops | After clearing the fault, restart the motor |
| E-0.6 | Temperature Sensor Open | VFD temperature sensor open; display flashes; operation continues | After clearing the fault, restart the motor |
| E-0.7 | Temperature Sensor Short | VFD temperature sensor shorted; display flashes; operation continues | After clearing the fault, restart the motor |
| E-0.8 | 100% Overload | VFD overload warning lasts 6 s; motor stops | After clearing the fault, restart the motor |
| E-0.9 | VFD Thermal Protection | Overheat protection warning >1 minute; motor stops | After clearing the fault, restart the motor |
| E-1.0 | Bus Overvoltage Protection | VFD voltage exceeds 4.1× set protection value; motor stops | After clearing the fault, restart the motor |
| E-1.1 | IGBT VF0 Protection | IGBT module VF0 fault; module overload; motor stops | After clearing the fault, restart the motor |
## 6. Multi-speed Settings
| D1 | D2 | D3 | Set Frequency | Corresponding Parameter |
| -----| -----| -----| ---------------| -------------------------|
| OFF | OFF | OFF | Multi-speed 0 | -3.8- |
| ON | OFF | OFF | Multi-speed 1 | -2.1- |
| OFF | ON | OFF | Multi-speed 2 | -2.2- |
| ON | ON | OFF | Multi-speed 3 | -2.3- |
| OFF | OFF | ON | Multi-speed 4 | -2.4- |
| ON | OFF | ON | Multi-speed 5 | -2.5- |
| OFF | ON | ON | Multi-speed 6 | -2.6- |
| ON | ON | ON | Multi-speed 7 | -2.7- |
---
## 7. Low-Frequency V/F Compensation
The VFD allows **low-frequency V/F compensation** to improve motor torque under load.
- Based on the connected load, you can set values in parameters `-0.3-`, `-0.4-`, and `-0.5-` according to the **linear V/F curve** (see Table 7.1).
- Low-frequency compensation **increases motor torque**.
- You must choose the **upper frequency limit for torque boosting**:
- Set the **maximum voltage ratio** in `-0.3-` and `-0.4-` for the desired frequency.
- Reference the corresponding or nearest frequency in Table 2.
**Behavior:**
- When operating **above this frequency**, the V/F slope increases → torque boost.
- When operating **below this frequency**, the V/F slope decreases → torque decreases.
**Example:**
- Parameter `-0.3-` = 20.0 Hz
- Parameter `-0.4-` = 60, 50, 30
- Parameter `-0.5-` default = 8
- The three V/F curves correspond to different voltage ratios at 20 Hz:
- Curve 1: 60% voltage ratio
- Curve 2: 55% voltage ratio
- Curve 3: 30% voltage ratio
This allows fine-tuning of torque at low speeds while keeping proper V/F control at higher frequencies.
---
### Table 7.1 – Linear V/F Curve Values for Low-Frequency Compensation
<img src="../assets/US-3202510_freq.png" alt="VFD Frequency Compensation Curve" style="max-width:600px;">
---
## 8. Modbus Interface
### 8.1 Internal Register List
- Communication format: MODBUS-RTU
- Data bits: 8
- Stop bits: 1
- Parity: None
- Default baud rate: 9600 bps
---
### 8.2 Holding Register Group (Read/Write)
| Hex Addr | Dec Addr | Data Type | Description | Notes |
| ---------:| ---------:| -----------------| ----------------------| ---------------------------------------|
| 0x0002 | 2 | Unsigned 16-bit | MODBUS set frequency | 500 / 10 = 50.0 Hz |
| 0x0003 | 3 | Unsigned 16-bit | MODBUS run command | 1: Forward, 2: Stop, 3: Reverse |
| 0x0004 | 4 | Unsigned 16-bit | Acceleration time | 1–15 → 5–0.1s (1 = 5s, 2 = 2.5s etc.) |
| 0x0005 | 5 | Unsigned 16-bit | Deceleration time | 1–15 → 5–0.1s (1 = 5s, 2 = 2.5s etc.) |
---
### 8.3 Input Registers (Read-only)
| Hex Addr | Dec Addr | Data Type | Description | Notes |
| ---------:| ---------:| -----------------| ---------------------------| -------------------------------|
| 0x0008 | 8 | Unsigned 16-bit | DC bus voltage | 3100 / 10 = 310.0 V |
| 0x0009 | 9 | Unsigned 16-bit | DC bus current | 132 / 100 = 1.32 A |
| 0x000A | 10 | Unsigned 16-bit | Drive temperature | 43 = 43 °C |
| 0x000B | 11 | Unsigned 16-bit | System status | 0: Idle, 1: Running, 2: Fault |
| 0x000C | 12 | Unsigned 16-bit | Error code | See error table below |
| 0x000D | 13 | Unsigned 16-bit | Current running frequency | 500 / 10 = 50.0 Hz |
---
### 8.4 Error Codes
| Code | Description |
| -----:| -------------------------------|
| 0 | No fault |
| 1 | Pulse overcurrent |
| 2 | IGBT overcurrent protection |
| 3 | DC bus overvoltage protection |
| 4 | Temperature near IGBT limit |
| 5 | Inverter thermal protection |
| 6 | Inverter overload 100% |
| 7 | Inverter power cut |
+13 -34
View File
@@ -10326,9 +10326,9 @@
}
},
"node_modules/seroval": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.4.0.tgz",
"integrity": "sha512-BdrNXdzlofomLTiRnwJTSEAaGKyHHZkbMXIywOh7zlzp4uZnXErEwl9XZ+N1hJSNpeTtNxWvVwN0wUzAIQ4Hpg==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.0.tgz",
"integrity": "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==",
"license": "MIT",
"peer": true,
"engines": {
@@ -10336,9 +10336,9 @@
}
},
"node_modules/seroval-plugins": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.4.0.tgz",
"integrity": "sha512-zir1aWzoiax6pbBVjoYVd0O1QQXgIL3eVGBMsBsNmM8Ukq90yGaWlfx0AB9dTS8GPqrOrbXn79vmItCUP9U3BQ==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.0.tgz",
"integrity": "sha512-EAHqADIQondwRZIdeW2I636zgsODzoBDwb3PT/+7TLDWyw1Dy/Xv7iGUIEXXav7usHDE9HVhOU61irI3EnyyHA==",
"license": "MIT",
"engines": {
"node": ">=10"
@@ -10581,36 +10581,15 @@
}
},
"node_modules/solid-js": {
"version": "1.9.10",
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.10.tgz",
"integrity": "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew==",
"license": "MIT",
"dependencies": {
"csstype": "^3.1.0",
"seroval": "~1.3.0",
"seroval-plugins": "~1.3.0"
}
},
"node_modules/solid-js/node_modules/seroval": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz",
"integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==",
"version": "1.9.11",
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.11.tgz",
"integrity": "sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10"
}
},
"node_modules/solid-js/node_modules/seroval-plugins": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.3.tgz",
"integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"peerDependencies": {
"seroval": "^1.0"
"dependencies": {
"csstype": "^3.1.0",
"seroval": "~1.5.0",
"seroval-plugins": "~1.5.0"
}
},
"node_modules/sonner": {
+3
View File
@@ -30,6 +30,9 @@ export function useMachines(): UseMachine[] {
const machinePreset = getMachineProperties(
machine.machine_identification_unique.machine_identification,
);
console.log("Machine!: {}", machine);
const vendorPreset = getVendorProperties(
machinePreset!.machine_identification.vendor,
);
@@ -0,0 +1,117 @@
import React from "react";
import { ControlCard } from "@/control/ControlCard";
import { Page } from "@/components/Page";
import { ControlGrid } from "@/control/ControlGrid";
import { TimeSeriesValueNumeric } from "@/control/TimeSeriesValue";
import { MinMaxValue, TIMEFRAME_OPTIONS } from "@/control/MinMaxValue";
import { EditValue } from "@/control/EditValue";
import { Label } from "@/control/Label";
import { usePellet1 as usePellet1 } from "./usePelletizer";
import { SelectionGroupBoolean } from "@/control/SelectionGroup";
import { createTimeSeries, TimeSeries } from "@/lib/timeseries";
import { roundToDecimals } from "@/lib/decimal";
import { Badge } from "@/components/ui/badge";
export function Pellet1ConfigPage() {
const {
//state
state,
defaultState,
//live values
frequency,
temperature,
voltage,
current,
// mutation functions
SetRunning,
SetDirection,
SetFrequencyTarget,
SetAccelerationLevel,
SetDecelerationLevel,
} = usePellet1();
// Extract values from consolidated state
const running = state?.inverter_state?.running ?? false;
const direction = state?.inverter_state?.direction ?? true;
const frequency_target = state?.inverter_state?.frequency_target ?? 0;
const acceleration_level = state?.inverter_state?.acceleration_level ?? 0;
const deceleration_level = state?.inverter_state?.deceleration_level ?? 0;
// Shared timeframe state (default 5 minutes)
const [timeframe, setTimeframe] = React.useState<number>(5 * 60 * 1000);
return (
<Page>
<ControlGrid columns={2}>
<ControlCard title="Advanced Inverter Configuration">
<SelectionGroupBoolean
value={running}
optionTrue={{ children: "On" }}
optionFalse={{ children: "Off" }}
onChange={(v) => {
SetRunning(v)
}}
/>
<Label label="Rotation Direction">
<SelectionGroupBoolean
value={direction}
optionTrue={{ children: "Forward" }}
optionFalse={{ children: "Backward" }}
onChange={(v) => {
SetDirection(v)
}}
/>
</Label>
<Label label="Set Target Frequency">
<EditValue
title="Set Target Frequency"
value={frequency_target}
unit="Hz"
step={0.1}
min={0}
max={50}
renderValue={(value) => value.toFixed(1)}
onChange={(val) => {
SetFrequencyTarget(val);
}}
defaultValue={5}
/>
</Label>
<Label label="Set Acceleration Level">
<EditValue
title="Set Acceleration Level"
value={acceleration_level}
unit={undefined}
step={1}
min={1}
max={15}
renderValue={(value) => value.toFixed(0)}
onChange={(val) => SetAccelerationLevel(val)}
defaultValue={7}
/>
</Label>
<Label label="Set Deceleration Level">
<EditValue
title="Set Deceleration Level"
value={deceleration_level}
unit={undefined}
step={1}
min={1}
max={15}
renderValue={(value) => value.toFixed(0)}
onChange={(val) => SetDecelerationLevel(val)}
defaultValue={7}
/>
</Label>
</ControlCard>
</ControlGrid>
</Page>
);
}
@@ -0,0 +1,123 @@
import React from "react";
import { ControlCard } from "@/control/ControlCard";
import { Page } from "@/components/Page";
import { ControlGrid } from "@/control/ControlGrid";
import { TimeSeriesValueNumeric } from "@/control/TimeSeriesValue";
import { MinMaxValue, TIMEFRAME_OPTIONS } from "@/control/MinMaxValue";
import { EditValue } from "@/control/EditValue";
import { Label } from "@/control/Label";
import { usePellet1 as usePellet1 } from "./usePelletizer";
import { SelectionGroupBoolean } from "@/control/SelectionGroup";
import { createTimeSeries, TimeSeries } from "@/lib/timeseries";
import { roundToDecimals } from "@/lib/decimal";
import { Badge } from "@/components/ui/badge";
export function Pellet1ControlPage() {
const {
//state
state,
defaultState,
//live values
frequency,
temperature,
voltage,
current,
// mutation functions
SetRunning,
SetDirection,
SetFrequencyTarget,
SetAccelerationLevel,
SetDecelerationLevel,
} = usePellet1();
// Extract values from consolidated state
const running = state?.inverter_state?.running ?? false;
const direction = state?.inverter_state?.direction ?? true;
const frequency_target = state?.inverter_state?.frequency_target ?? 0;
const acceleration_level = state?.inverter_state?.acceleration_level ?? 0;
const deceleration_level = state?.inverter_state?.deceleration_level ?? 0;
// Shared timeframe state (default 5 minutes)
const [timeframe, setTimeframe] = React.useState<number>(5 * 60 * 1000);
return (
<Page>
<ControlGrid columns={2}>
<ControlCard title="Motor Configuration">
<SelectionGroupBoolean
value={running}
optionTrue={{ children: "On" }}
optionFalse={{ children: "Off" }}
onChange={(v) => {
SetRunning(v)
}}
/>
<Label label="Speed">
<EditValue
title="Speed"
value={frequency_target * 2}
unit="%"
step={0.5}
min={0}
max={100}
renderValue={(value) => value.toFixed(1)}
onChange={(val) => {
SetFrequencyTarget(val / 2);
}}
defaultValue={5}
/>
</Label>
</ControlCard>
<ControlCard title="Inverter Status">
{
/*
<Badge
className={`text-md ${true ? "bg-white-600 border-green-600 text-green-600" : "bg-red-600 font-bold text-white"} mx-auto h-12 w-[100%] border-3 text-lg`}
>
{ true ? "Running" : "PULSE OVERCURRENT"}
</Badge>
*/
}
<TimeSeriesValueNumeric
label="Frequency"
unit="Hz"
renderValue={(value) => roundToDecimals(value, 1)}
timeseries={ frequency }
/>
<TimeSeriesValueNumeric
label="Temperature"
unit="C"
renderValue={(value) => roundToDecimals(value, 1)}
timeseries={ temperature }
/>
<TimeSeriesValueNumeric
label="Voltage"
unit="V"
renderValue={(value) => roundToDecimals(value, 1)}
timeseries={ voltage }
/>
<TimeSeriesValueNumeric
label="Current"
unit="A"
renderValue={(value) => roundToDecimals(value, 1)}
timeseries={ current }
/>
</ControlCard>
</ControlGrid>
</Page>
);
}
@@ -0,0 +1,168 @@
import { Page } from "@/components/Page";
import {
AutoSyncedBigGraph,
SyncedFloatingControlPanel,
useGraphSync,
type GraphConfig,
} from "@/components/graph";
import React from "react";
import { usePellet1 } from "./usePelletizer";
export function Pellet1GraphsPage() {
const { diameter, x_diameter, y_diameter, roundness, state } = usePellet1();
const syncHook = useGraphSync("diameter-roundness-group");
const targetDiameter = state?.laser_state?.target_diameter ?? 0;
const lowerTolerance = state?.laser_state?.lower_tolerance ?? 0;
const higherTolerance = state?.laser_state?.higher_tolerance ?? 0;
const isTwoAxis = !!x_diameter?.current || !!y_diameter?.current;
// Convert roundness from ratio (0-1) to percentage (0-100)
const roundnessPercent = React.useMemo(() => {
if (!roundness) return null;
const transformValue = (
val: { value: number; timestamp: number } | null,
) => {
if (!val) return null;
return { value: val.value * 100, timestamp: val.timestamp };
};
const transformSeries = (series: any) => {
if (!series || !series.values) return series;
return {
...series,
values: series.values.map(transformValue),
};
};
return {
current: roundness.current
? transformValue(roundness.current)
: null,
long: transformSeries(roundness.long),
short: transformSeries(roundness.short),
};
}, [roundness]);
const diameterColor = "#3b82f6"; // blue
const xDiameterColor = "#ef4444"; // red
const yDiameterColor = "#22c55e"; // green
const roundnessColor = "#eab308"; // yellow
// config for all graphs
const baseGraphConfig: GraphConfig = {
title: "",
defaultTimeWindow: 30 * 60 * 1000, // 30 minutes Standard
colors: {
grid: "#e2e8f0",
axis: "#64748b",
background: "#ffffff",
},
};
// Diameter-Graph (Diameter, X-Diameter, Y-Diameter)
const diameterGraphData = [
{
newData: diameter,
color: diameterColor,
title: "Diameter",
lines: [
// Tolerance for diameter
{
type: "threshold" as const,
value: targetDiameter + higherTolerance,
label: "Upper Tolerance",
color: diameterColor,
dash: [5, 5],
},
{
type: "threshold" as const,
value: targetDiameter - lowerTolerance,
label: "Lower Tolerance",
color: diameterColor,
dash: [5, 5],
},
{
type: "target" as const,
value: targetDiameter,
label: "Target",
color: diameterColor,
},
],
},
...(isTwoAxis && x_diameter
? [
{
newData: x_diameter,
color: xDiameterColor,
title: "X-Diameter",
},
]
: []),
...(isTwoAxis && y_diameter
? [
{
newData: y_diameter,
color: yDiameterColor,
title: "Y-Diameter",
},
]
: []),
];
// Roundness-Graph
const roundnessGraphData = {
newData: roundnessPercent,
color: roundnessColor,
title: "Roundness (%)",
};
return (
<Page className="pb-27">
<div className="flex flex-col gap-4">
{/* Graph 1: Diameter */}
<AutoSyncedBigGraph
syncHook={syncHook}
newData={diameterGraphData}
unit="mm"
renderValue={(value) => value.toFixed(3)}
config={{
...baseGraphConfig,
title: "Diameter (mm)",
exportFilename: "diameter_data",
colors: {
...baseGraphConfig.colors,
primary: diameterColor,
},
}}
graphId="diameter-graph"
/>
{/* Graph 2: Roundness */}
{isTwoAxis && roundnessPercent && (
<AutoSyncedBigGraph
syncHook={syncHook}
newData={roundnessGraphData}
unit="%"
renderValue={(value) => value.toFixed(2)}
config={{
...baseGraphConfig,
title: "Roundness (%)",
exportFilename: "roundness_data",
colors: {
...baseGraphConfig.colors,
primary: roundnessColor,
},
}}
graphId="roundness-graph"
/>
)}
</div>
<SyncedFloatingControlPanel controlProps={syncHook.controlProps} />
</Page>
);
}
@@ -0,0 +1,12 @@
import { Page } from "@/components/Page";
import { MarkdownWithToc } from "@/components/MarkdownWithToc";
import React from "react";
import laserManualContent from "@/assets/markdown/laser/manual.md?raw";
export function Pellet1ManualPage() {
return (
<Page>
<MarkdownWithToc markdownContent={laserManualContent} />
</Page>
);
}
@@ -0,0 +1,50 @@
import { Topbar } from "@/components/Topbar";
import { pelletizer1SerialRoute } from "@/routes/routes";
import React from "react";
export function Pellet1Page() {
const { serial } = pelletizer1SerialRoute.useParams();
return (
<Topbar
pathname={`/_sidebar/machines/pelletizer1/${serial}`}
items={[
{
link: "control",
activeLink: "control",
title: "Control",
icon: "lu:CirclePlay",
},
{
link: "config",
activeLink: "config",
title: "Config",
icon: "lu:ChartSpline",
},
// {
// link: "graphs",
// activeLink: "graphs",
// title: "Graphs",
// icon: "lu:ChartSpline",
// },
// {
// link: "settings",
// activeLink: "settings",
// title: "Config",
// icon: "lu:ChartSpline",
// },
// {
// link: "manual",
// activeLink: "manual",
// title: "Manual",
// icon: "lu:BookOpen",
// },
// {
// link: "presets",
// activeLink: "presets",
// title: "Presets",
// icon: "lu:Save",
// },
]}
/>
);
}
@@ -0,0 +1,77 @@
import React from "react";
import { z } from "zod";
import { Preset } from "@/lib/preset/preset";
import { laser1 } from "@/machines/properties";
import { PresetPreviewEntries } from "@/components/preset/PresetPreviewTable";
import { PresetsPage } from "@/components/preset/PresetsPage";
import { usePellet1 } from "./usePelletizer";
const laser1PresetDataSchema = z
.object({
targetDiameter: z.number(),
lowerTolerance: z.number(),
higherTolerance: z.number(),
})
.partial();
type Laser1 = z.infer<typeof laser1PresetDataSchema>;
const schemas = new Map([[1, laser1PresetDataSchema]]);
const previewEntries: PresetPreviewEntries<Laser1> = [
{
name: "Target Diameter",
unit: "mm",
renderValue: (data: Laser1) => data?.targetDiameter?.toFixed(3),
},
{
name: "Lower Tolerance",
unit: "mm",
renderValue: (data: Laser1) => data?.lowerTolerance?.toFixed(3),
},
{
name: "Higher Tolerance",
unit: "mm",
renderValue: (data: Laser1) => data?.higherTolerance?.toFixed(3),
},
];
export function Pellet1PresetsPage() {
const {
setTargetDiameter,
setLowerTolerance,
setHigherTolerance,
defaultState,
state,
} = usePellet1();
const applyPreset = (preset: Preset<Laser1>) => {
const targetDiameter = preset?.data?.targetDiameter ?? 1.75;
const lowerTolerance = preset?.data?.lowerTolerance ?? 0.05;
const higherTolerance = preset?.data?.higherTolerance ?? 0.05;
setTargetDiameter(targetDiameter);
setLowerTolerance(lowerTolerance);
setHigherTolerance(higherTolerance);
};
const toPresetData = (s: typeof state): Laser1 => ({
targetDiameter: s?.laser_state.target_diameter,
lowerTolerance: s?.laser_state.lower_tolerance,
higherTolerance: s?.laser_state.higher_tolerance,
});
return (
<PresetsPage
machine_identification={laser1.machine_identification}
currentState={toPresetData(state)}
schemas={schemas}
schemaVersion={1}
applyPreset={applyPreset}
previewEntries={previewEntries}
defaultState={toPresetData(defaultState)}
/>
);
}
@@ -0,0 +1,219 @@
/**
* @file pellet1Namespace.ts
* @description TypeScript implementation of Pellet1 namespace with Zod schema validation.
*/
import { StoreApi } from "zustand";
import { create } from "zustand";
import { z } from "zod";
import {
EventHandler,
eventSchema,
Event,
handleUnhandledEventError,
NamespaceId,
createNamespaceHookImplementation,
ThrottledStoreUpdater,
} from "../../../client/socketioStore";
import { MachineIdentificationUnique } from "@/machines/types";
import {
createTimeSeries,
TimeSeries,
TimeSeriesValue,
} from "@/lib/timeseries";
// ========== Event Schema Definitions ==========
/**
* Live values from Pellet (30 FPS)
*/
export const liveValuesEventDataSchema = z.object({
inverter_values: z.object({
frequency: z.number(),
temperature: z.number(),
voltage: z.number(),
current: z.number(),
})
});
/**
* State event from Pellet (on state changes only)
*/
export const stateEventDataSchema = z.object({
is_default_state: z.boolean(),
inverter_state: z.object({
running: z.boolean(),
direction: z.boolean(),
frequency_target: z.number(),
acceleration_level: z.number(),
deceleration_level: z.number(),
}),
});
// ========== Event Schemas with Wrappers ==========
export const liveValuesEventSchema = eventSchema(liveValuesEventDataSchema);
export const stateEventSchema = eventSchema(stateEventDataSchema);
// ========== Type Inferences ==========
export type LiveValuesEvent = z.infer<typeof liveValuesEventSchema>;
export type StateEvent = z.infer<typeof stateEventSchema>;
export type Pellet1NamespaceStore = {
state: StateEvent | null;
defaultState: StateEvent | null;
// Time series data for live values
frequency: TimeSeries;
temperature: TimeSeries;
voltage: TimeSeries;
current: TimeSeries;
};
// Constants for time durations
const HALF_SECOND = 500;
const { initialTimeSeries: frequency, insert: addFrequency } =
createTimeSeries({ sampleIntervalLong: HALF_SECOND });
const { initialTimeSeries: temperature, insert: addTemperature } =
createTimeSeries({ sampleIntervalLong: HALF_SECOND });
const { initialTimeSeries: voltage, insert: addVoltage } =
createTimeSeries({ sampleIntervalLong: HALF_SECOND });
const { initialTimeSeries: current, insert: addCurrent } =
createTimeSeries({ sampleIntervalLong: HALF_SECOND });
/**
* Factory function to create a new Pellet1 namespace store
* @returns A new Zustand store instance for Pellet1 namespace
*/
export const createPellet1NamespaceStore =
(): StoreApi<Pellet1NamespaceStore> =>
create<Pellet1NamespaceStore>(() => {
return {
state: null,
defaultState: null,
frequency,
temperature,
voltage,
current,
};
});
/**
* Creates a message handler for Pellet1 namespace events with validation and appropriate caching strategies
* @param store The store to update when messages are received
* @param throttledUpdater Throttled updater for batching updates at 30 FPS
* @returns A message handler function
*/
export function pellet1MessageHandler(
store: StoreApi<Pellet1NamespaceStore>,
throttledUpdater: ThrottledStoreUpdater<Pellet1NamespaceStore>,
): EventHandler {
return (event: Event<any>) =>
{
const eventName = event.name;
// Helper function to update store through buffer
const updateStore = (updater: (state: Pellet1NamespaceStore) => Pellet1NamespaceStore) =>
{
throttledUpdater.updateWith(updater);
};
try
{
// Apply appropriate caching strategy based on event type
if (eventName === "StateEvent")
{
const stateEvent = stateEventSchema.parse(event);
console.log(stateEvent);
updateStore((state) => ({
...state,
state: stateEvent,
// only set default state if is_default_state is true
defaultState: stateEvent.data.is_default_state
? stateEvent
: state.defaultState,
}));
}
// Live values events (keep for 1 hour)
else if (eventName === "LiveValuesEvent")
{
const liveValuesEvent = liveValuesEventSchema.parse(event);
const frequencyValue: TimeSeriesValue = {
value: liveValuesEvent.data.inverter_values.frequency,
timestamp: event.ts,
};
updateStore((state) => ({ ...state, frequency: addFrequency(state.frequency, frequencyValue) }));
const temperatureValue: TimeSeriesValue = {
value: liveValuesEvent.data.inverter_values.temperature,
timestamp: event.ts,
};
updateStore((state) => ({
...state,
temperature: addTemperature(state.temperature, temperatureValue),
}));
const voltageValue: TimeSeriesValue = {
value: liveValuesEvent.data.inverter_values.voltage,
timestamp: event.ts,
};
updateStore((state) => ({
...state,
voltage: addVoltage(state.voltage, voltageValue),
}));
const currentValue: TimeSeriesValue = {
value: liveValuesEvent.data.inverter_values.current,
timestamp: event.ts,
};
updateStore((state) => ({
...state,
current: addCurrent(state.current, currentValue),
}));
} else {
handleUnhandledEventError(eventName);
}
} catch (error) {
console.error(
`Unexpected error processing ${eventName} event:`,
error,
);
throw error;
}
};
}
/**
* Create the Pellet1 namespace implementation
*/
const usePellet1NamespaceImplementation =
createNamespaceHookImplementation<Pellet1NamespaceStore>({
createStore: createPellet1NamespaceStore,
createEventHandler: pellet1MessageHandler,
});
export function usePellet1Namespace(
machine_identification_unique: MachineIdentificationUnique,
): Pellet1NamespaceStore {
// Generate namespace ID from validated machine ID
const namespaceId: NamespaceId = {
type: "machine",
machine_identification_unique,
};
// Use the implementation with validated namespace ID
return usePellet1NamespaceImplementation(namespaceId);
}
@@ -0,0 +1,8 @@
import { z } from "zod";
export const liveValuesEventDataSchema = z.object({
frequency: z.number(),
temperature: z.number(),
voltage: z.number(),
current: z.number(),
});
@@ -0,0 +1,15 @@
import { z } from "zod";
export const stateEventDataSchema = z.object({
is_default_state: z.boolean(),
inverter_state: z.object({
running_state: z.number(),
frequency_target: z.number(),
acceleration_level: z.number(),
deceleration_level: z.number(),
error_code: z.number().nullable(),
system_status: z.number(),
}),
});
@@ -0,0 +1,185 @@
import { toastError } from "@/components/Toast";
import { useMachineMutate as useMachineMutation } from "@/client/useClient";
import { MachineIdentificationUnique } from "@/machines/types";
import { pelletizer } from "@/machines/properties";
import { pelletizer1SerialRoute } from "@/routes/routes";
import { z } from "zod";
import { usePellet1Namespace, StateEvent } from "./namespace";
import { useEffect, useMemo } from "react";
import { useStateOptimistic } from "@/lib/useStateOptimistic";
import { produce } from "immer";
function usePellet(machine_identification_unique: MachineIdentificationUnique) {
// Get consolidated state and live values from namespace
const { state, defaultState, frequency, temperature, voltage, current } =
usePellet1Namespace(machine_identification_unique);
// Single optimistic state for all state management
const stateOptimistic = useStateOptimistic<StateEvent>();
useEffect(() => { if (state) { stateOptimistic.setReal(state); } }, [state]);
// Helper function for optimistic updates using produce
const updateStateOptimistically = (
producer: (current: StateEvent) => void,
serverRequest: () => void,
) => {
const currentState = stateOptimistic.value;
if (currentState && !stateOptimistic.isOptimistic) {
stateOptimistic.setOptimistic(produce(currentState, producer));
}
serverRequest();
};
// Mutation schemas
const schemaRunning = z.object({ SetRunning: z.boolean() });
const { request: requestRunning } = useMachineMutation(schemaRunning);
const schemaDirection = z.object({ SetDirection: z.boolean() });
const { request: requestDirection } = useMachineMutation(schemaDirection);
const schemaFrequencyTarget = z.object({ SetFrequencyTarget: z.number() });
const { request: requestFrequencyTarget } = useMachineMutation(schemaFrequencyTarget);
const schemaAccelerationLevel = z.object({ SetAccelerationLevel: z.number() });
const { request: requestAccelerationLevel } = useMachineMutation(schemaAccelerationLevel);
const schemaDecelerationLevel = z.object({ SetDecelerationLevel: z.number() });
const { request: requestDecelerationLevel } = useMachineMutation(schemaDecelerationLevel);
const SetRunning = (running: boolean) => {
updateStateOptimistically(
(current) => {
current.data.inverter_state.running = running;
},
() =>
requestRunning({
machine_identification_unique,
data: {
SetRunning: running,
},
}),
);
};
const SetDirection = (forward: boolean) => {
updateStateOptimistically(
(current) => {
current.data.inverter_state.direction = forward;
},
() =>
requestDirection({
machine_identification_unique,
data: {
SetDirection: forward,
},
}),
);
};
const SetFrequencyTarget = (frequency_target: number) =>
{
updateStateOptimistically(
(current) => {
current.data.inverter_state.frequency_target = frequency_target;
},
() =>
requestFrequencyTarget({
machine_identification_unique,
data: {
SetFrequencyTarget: frequency_target,
},
}),
);
};
const SetAccelerationLevel = (acceleration_level: number) => {
updateStateOptimistically(
(current) => {
current.data.inverter_state.acceleration_level = acceleration_level;
},
() =>
requestAccelerationLevel({
machine_identification_unique,
data: {
SetAccelerationLevel: acceleration_level,
},
}),
);
};
const SetDecelerationLevel = (deceleration_level: number) => {
updateStateOptimistically(
(current) => {
current.data.inverter_state.deceleration_level = deceleration_level;
},
() =>
requestDecelerationLevel({
machine_identification_unique,
data: {
SetDecelerationLevel: deceleration_level,
},
}),
);
};
return {
// Consolidated state
state: stateOptimistic.value?.data,
// Default state for initial values
defaultState: defaultState?.data,
// Live values (TimeSeries)
frequency,
temperature,
voltage,
current,
// Loading states
isLoading: stateOptimistic.isOptimistic,
isDisabled: !stateOptimistic.isInitialized,
// Action functions
SetRunning,
SetDirection,
SetFrequencyTarget,
SetAccelerationLevel,
SetDecelerationLevel,
};
}
export function usePellet1()
{
const { serial: serialString } = pelletizer1SerialRoute.useParams();
// Memoize the machine identification to keep it stable between renders
const machineIdentification: MachineIdentificationUnique = useMemo(() =>
{
const serial = parseInt(serialString); // Use 0 as fallback if NaN
if (isNaN(serial)) {
toastError(
"Invalid Serial Number",
`"${serialString}" is not a valid serial number.`,
);
return {
machine_identification: {
vendor: 0,
machine: 0,
},
serial: 0,
};
}
return {
machine_identification: pelletizer.machine_identification,
serial,
};
}, [serialString]); // Only recreate when serialString changes
const pellet = usePellet(machineIdentification);
return { ...pellet };
}
+13
View File
@@ -302,6 +302,18 @@ export const laser1: MachineProperties = {
device_roles: [],
};
export const pelletizer: MachineProperties = {
name: "Pelletizer",
version: "V1",
slug: "pelletizer1",
icon: "lu:CircleDotDashed",
machine_identification: {
vendor: VENDOR_QITECH,
machine: 0x000b,
},
device_roles: [],
};
export const mock1: MachineProperties = {
name: "Mock",
version: "V1",
@@ -573,6 +585,7 @@ export const machineProperties: MachineProperties[] = [
extruder3,
extruder2,
laser1,
pelletizer,
mock1,
buffer1,
wagoPower1,
+46
View File
@@ -43,6 +43,12 @@ import { Laser1GraphsPage } from "@/machines/laser/laser1/Laser1Graph";
import { Laser1Page } from "@/machines/laser/laser1/Laser1Page";
import { Laser1PresetsPage } from "@/machines/laser/laser1/Laser1PresetsPage";
import { Pellet1Page } from "@/machines/pelletizer/pelletizer1/Page";
import { Pellet1ControlPage } from "@/machines/pelletizer/pelletizer1/ControlPage";
import { Pellet1GraphsPage } from "@/machines/pelletizer/pelletizer1/Graph";
import { Pellet1ConfigPage } from "@/machines/pelletizer/pelletizer1/ConfigPage";
import { Pellet1PresetsPage } from "@/machines/pelletizer/pelletizer1/PresetsPage";
import { Mock1ControlPage } from "@/machines/mock/mock1/Mock1ControlPage";
import { Mock1GraphPage } from "@/machines/mock/mock1/Mock1Graph";
import { Mock1ManualPage } from "@/machines/mock/mock1/Mock1Manual";
@@ -285,6 +291,38 @@ export const laser1PresetsRoute = createRoute({
component: () => <Laser1PresetsPage />,
});
// --- pelletizer
export const pelletizer1SerialRoute = createRoute({
getParentRoute: () => machinesRoute,
path: "pelletizer1/$serial",
component: () => <Pellet1Page />,
});
export const pelletizer1ControlRoute = createRoute({
getParentRoute: () => pelletizer1SerialRoute,
path: "control",
component: () => <Pellet1ControlPage />,
});
export const pelletizer1ConfigRoute = createRoute({
getParentRoute: () => pelletizer1SerialRoute,
path: "config",
component: () => <Pellet1ConfigPage />,
});
export const pelletizer1GraphsRoute = createRoute({
getParentRoute: () => pelletizer1SerialRoute,
path: "graphs",
component: () => <Pellet1GraphsPage />,
});
export const pelletizer1PresetsRoute = createRoute({
getParentRoute: () => pelletizer1SerialRoute,
path: "presets",
component: () => <Pellet1PresetsPage />,
});
// --- mock
export const mock1SerialRoute = createRoute({
getParentRoute: () => machinesRoute,
path: "mock1/$serial",
@@ -470,6 +508,14 @@ export const rootTree = RootRoute.addChildren([
laser1GraphsRoute,
laser1PresetsRoute,
]),
pelletizer1SerialRoute.addChildren([
pelletizer1ControlRoute,
pelletizer1GraphsRoute,
pelletizer1ConfigRoute,
pelletizer1PresetsRoute,
]),
testMachineSerialRoute.addChildren([testMachineControlRoute]),
analogInputTestMachineSerialRoute.addChildren([
+7
View File
@@ -31,6 +31,13 @@ serialport = "4.7.3"
tracing = { version = "0.1.44", features = ["attributes"] }
tonic = { version = "0.13.1", optional = true }
# modbus interfaces
modbus = { git = "https://github.com/just-some-entity/modbus-rs.git", features = ["linux-rtu-transport"] }
# enum stuff
strum = "0.26"
strum_macros = "0.26"
[dev-dependencies]
approx = "0.5.1"
textplots = "0.8.7"
+2
View File
@@ -26,6 +26,7 @@ pub mod extruder1;
pub mod extruder2;
pub mod ip20_test_machine;
pub mod laser;
pub mod pelletizer;
pub mod machine_identification;
pub mod mock;
pub mod registry;
@@ -39,6 +40,7 @@ pub const VENDOR_QITECH: u16 = 0x0001;
pub const MACHINE_WINDER_V1: u16 = 0x0002;
pub const MACHINE_EXTRUDER_V1: u16 = 0x0004;
pub const MACHINE_LASER_V1: u16 = 0x0006;
pub const MACHINE_PELLETIZER: u16 = 0x000B;
pub const MACHINE_MOCK: u16 = 0x0007;
pub const MACHINE_BUFFER_V1: u16 = 0x0008;
pub const MACHINE_AQUAPATH_V1: u16 = 0x0009;
+2
View File
@@ -54,6 +54,7 @@ impl MachineIdentification {
x if x == MACHINE_WINDER_V1 => "winder_v1".to_string(),
x if x == MACHINE_EXTRUDER_V1 => "extruder_v1".to_string(),
x if x == MACHINE_LASER_V1 => "laser_v1".to_string(),
x if x == MACHINE_PELLETIZER => "pelletizer_v1".to_string(),
x if x == MACHINE_MOCK => "mock".to_string(),
x if x == MACHINE_AQUAPATH_V1 => "aquapath_v1".to_string(),
x if x == MACHINE_BUFFER_V1 => "buffer_v1".to_string(),
@@ -182,6 +183,7 @@ use crate::MACHINE_EXTRUDER_V1;
use crate::MACHINE_EXTRUDER_V2;
use crate::MACHINE_LASER_V1;
use crate::MACHINE_MOCK;
use crate::MACHINE_PELLETIZER;
use crate::MACHINE_WAGO_POWER_V1;
use crate::MACHINE_WINDER_V1;
use crate::TEST_MACHINE;
+140
View File
@@ -0,0 +1,140 @@
use std::time::{Duration, Instant};
use serde::de::VariantAccess;
use units::frequency::hertz;
use crate::MachineAct;
use crate::MachineMessage;
use crate::MachineValues;
use super::Pelletizer;
impl MachineAct for Pelletizer
{
fn act(&mut self, now: Instant)
{
if let Ok(msg) = self.api_receiver.try_recv()
{
self.act_machine_message(msg);
};
let should_emit =
now.duration_since(self.last_measurement_emit)
> Duration::from_secs_f64(1.0 / 12.0);
let mut mutated: bool = false;
let mut inverter_snapshot_id: u64 = 0;
{
let mut inverter = smol::block_on(async {
self.inverter.write().await
});
if let Some(value) = self.mutation_request.running.take()
{
tracing::warn!("Setting running to: {}", value);
inverter.set_running(value);
mutated = true;
}
if let Some(value) = self.mutation_request.direction.take()
{
tracing::warn!("Setting direction to: {}", value);
inverter.set_direction(value);
mutated = true;
}
if let Some(value) = self.mutation_request.frequency.take()
{
tracing::warn!("Setting to: {}", value);
inverter.set_frequency_target((value * 10.0) as u16);
mutated = true;
}
if let Some(value) = self.mutation_request.accleration_level.take()
{
tracing::warn!("Setting to: {}", value);
inverter.set_acceleration_level(value);
mutated = true;
}
if let Some(value) = self.mutation_request.decleration_level.take()
{
tracing::warn!("Setting to: {}", value);
inverter.set_deceleration_level(value);
mutated = true;
}
if should_emit {
inverter.refresh_status();
}
if mutated { }
inverter.update();
inverter_snapshot_id = inverter.config.snapshot_id;
} // drop lock
if should_emit
{
if self.inverter_snapshot_id != inverter_snapshot_id
{
self.emit_state();
self.inverter_snapshot_id = inverter_snapshot_id;
}
self.emit_live_values();
self.last_measurement_emit = now;
}
}
fn act_machine_message(&mut self, msg: MachineMessage)
{
match msg
{
MachineMessage::SubscribeNamespace(namespace) =>
{
self.namespace.namespace = Some(namespace);
self.emit_state();
}
MachineMessage::UnsubscribeNamespace => match &mut self.namespace.namespace
{
Some(namespace) =>
{
tracing::info!("UnsubscribeNamespace");
namespace.socket_queue_tx.close();
namespace.sockets.clear();
namespace.events.clear();
}
None => todo!(),
},
MachineMessage::HttpApiJsonRequest(value) =>
{
use crate::MachineApi;
let _res = self.api_mutate(value);
}
MachineMessage::ConnectToMachine(_machine_connection) => {}
MachineMessage::DisconnectMachine(_machine_connection) => {}
MachineMessage::RequestValues(sender) =>
{
tracing::error!("REQUESTED VALUES");
let state = serde_json::to_value(self.create_state_event()).expect("Failed to serialize state");
let live_values = serde_json::to_value(self.create_live_values_event()).expect("Failed to serialize live values");
sender.send_blocking(MachineValues{ state, live_values }).expect("Failed to send values");
sender.close();
}
}
}
}
+158
View File
@@ -0,0 +1,158 @@
use crate::{MachineApi, MachineMessage};
use super::Pelletizer;
use control_core::socketio::{
event::{Event, GenericEvent},
namespace::{
CacheFn, CacheableEvents, Namespace, NamespaceCacheingLogic, cache_first_and_last_event,
},
};
use control_core_derive::BuildEvent;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use smol::channel::Sender;
use std::sync::Arc;
use tracing::instrument;
#[derive(Serialize, Debug, Clone)]
pub struct LiveValuesEvent
{
pub inverter_values: InverterLiveValues
}
#[derive(Serialize, Debug, Clone)]
pub struct InverterLiveValues
{
pub frequency: f64,
pub temperature: f64,
pub voltage: f64,
pub current: f64,
}
impl LiveValuesEvent {
pub fn build(&self) -> Event<Self> {
Event::new("LiveValuesEvent", self.clone())
}
}
#[derive(Serialize, Debug, Clone, PartialEq, BuildEvent)]
pub struct StateEvent {
pub is_default_state: bool,
pub inverter_state: InverterState,
}
impl StateEvent {
pub fn build(&self) -> Event<Self> {
Event::new("StateEvent", self.clone())
}
}
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct InverterState
{
pub running: bool,
pub direction: bool,
pub frequency_target: f64,
pub acceleration_level: u16,
pub deceleration_level: u16,
}
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct RotationState
{
pub forward: bool,
}
pub enum PelletMachineEvents {
LiveValues(Event<LiveValuesEvent>),
State(Event<StateEvent>),
}
#[derive(Debug)]
pub struct PelletMachineNamespace {
pub namespace: Option<Namespace>,
}
#[derive(Debug, Deserialize, Serialize)]
/// Mutation for controlling the Pellet machine
enum Mutation
{
SetRunning(bool),
SetDirection(bool),
SetFrequencyTarget(f64),
SetAccelerationLevel(u16),
SetDecelerationLevel(u16),
}
impl MachineApi for Pelletizer {
fn api_get_sender(&self) -> Sender<MachineMessage> {
self.api_sender.clone()
}
fn api_mutate(&mut self, request_body: Value) -> Result<(), anyhow::Error>
{
tracing::error!("Received: {:?}", &request_body);
let mutation: Mutation = serde_json::from_value(request_body)?;
match mutation
{
Mutation::SetRunning(running) =>
{
self.mutation_request.running = Some(running);
}
Mutation::SetDirection(forward) =>
{
self.mutation_request.direction = Some(forward);
}
Mutation::SetFrequencyTarget(frequency) =>
{
self.mutation_request.frequency = Some(frequency);
}
Mutation::SetAccelerationLevel(level) =>
{
self.mutation_request.accleration_level = Some(level);
}
Mutation::SetDecelerationLevel(level) =>
{
self.mutation_request.decleration_level = Some(level);
}
}
Ok(())
}
fn api_event_namespace(&mut self) -> Option<Namespace> {
self.namespace.namespace.clone()
}
}
//TODO; rename NamespaceCacheingLogic to NamespaceCachingLogic
impl NamespaceCacheingLogic<PelletMachineEvents> for PelletMachineNamespace {
#[instrument(skip_all)]
fn emit(&mut self, events: PelletMachineEvents) {
let event = Arc::new(events.event_value());
let buffer_fn = events.event_cache_fn();
if let Some(ns) = &mut self.namespace { ns.emit(event, &buffer_fn) }
}
}
impl CacheableEvents<Self> for PelletMachineEvents {
fn event_value(&self) -> GenericEvent {
match self {
Self::LiveValues(event) => event.into(),
Self::State(event) => event.into(),
}
}
fn event_cache_fn(&self) -> CacheFn {
let cache_first_and_last = cache_first_and_last_event();
match self {
Self::LiveValues(_) => cache_first_and_last,
Self::State(_) => cache_first_and_last,
}
}
}
+73
View File
@@ -0,0 +1,73 @@
use crate::pelletizer::Pelletizer;
use crate::pelletizer::api::{InverterLiveValues, InverterState, LiveValuesEvent, PelletMachineEvents, StateEvent};
use control_core::socketio::event::BuildEvent;
use control_core::socketio::namespace::NamespaceCacheingLogic;
impl Pelletizer
{
pub fn emit_state(&mut self)
{
let event = self.create_state_event().build();
self.namespace.emit(PelletMachineEvents::State(event));
self.emitted_default_state = true;
}
pub fn emit_live_values(&mut self)
{
let event = self.create_live_values_event().build();
self.namespace.emit(PelletMachineEvents::LiveValues(event));
}
pub fn create_state_event(&self) -> StateEvent
{
let inverter = smol::block_on(async {
self.inverter.read().await
});
tracing::error!("EMIT STATE: {:?}", inverter.config);
StateEvent
{
is_default_state: !self.emitted_default_state,
inverter_state: InverterState {
running: inverter.config.running,
direction: inverter.config.direction,
frequency_target: (inverter.config.frequency_target as f64) / 10.0,
acceleration_level: inverter.config.acceleration_level,
deceleration_level: inverter.config.deceleration_level,
},
}
}
pub fn create_live_values_event(&self) -> LiveValuesEvent
{
let inverter = smol::block_on(async {
self.inverter.read().await
});
if let Some(status) = inverter.status
{
return LiveValuesEvent {
inverter_values: InverterLiveValues {
voltage: status.voltage.value,
current: status.current.value,
temperature: status.temperature.value,
frequency: status.frequency.value,
}
};
}
LiveValuesEvent {
inverter_values: InverterLiveValues {
voltage: 0.0,
current: 0.0,
temperature: 0.0,
frequency: 0.0,
}
}
}
}
+116
View File
@@ -0,0 +1,116 @@
//use serde::{Deserialize, Serialize};
use crate::{
MACHINE_PELLETIZER, MachineMessage, VENDOR_QITECH,
machine_identification::{MachineIdentification, MachineIdentificationUnique},
};
use smol::{
channel::{Receiver, Sender},
lock::RwLock,
};
use socketioxide::extract::SocketRef;
use crate::{AsyncThreadMessage, serial::devices::us_3202510::US3202510};
use std::{sync::Arc, time::Instant};
pub mod act;
pub mod api;
pub mod new;
pub mod emit;
use crate::Machine;
use api::{PelletMachineNamespace};
#[derive(Debug)]
pub struct Pelletizer
{
// stuff for every machine
api_receiver: Receiver<MachineMessage>,
api_sender: Sender<MachineMessage>,
main_sender: Option<Sender<AsyncThreadMessage>>,
machine_identification_unique: MachineIdentificationUnique,
emitted_default_state: bool,
// socketio
namespace: PelletMachineNamespace,
last_measurement_emit: Instant,
// machine specific
inverter: Arc<RwLock<US3202510>>,
inverter_snapshot_id: u64,
mutation_request: MutationRequests
}
#[derive(Debug)]
pub struct MutationRequests
{
running: Option<bool>,
direction: Option<bool>,
frequency: Option<f64>,
accleration_level: Option<u16>,
decleration_level: Option<u16>,
}
impl Machine for Pelletizer
{
fn get_machine_identification_unique(&self) -> MachineIdentificationUnique
{
self.machine_identification_unique.clone()
}
fn get_main_sender(&self) -> Option<Sender<AsyncThreadMessage>>
{
self.main_sender.clone()
}
}
impl PelletMachineNamespace
{
pub async fn disconnect_all(&self)
{
for socket in self.connected_sockets().await
{
let _ = socket.disconnect();
}
}
async fn connected_sockets(&self) -> Vec<SocketRef>
{
if self.namespace.is_none()
{ vec![] }
else
{ self.namespace.clone().unwrap().sockets.clone() }
}
}
impl Drop for Pelletizer
{
fn drop(&mut self)
{
tracing::info!(
"[Pelletizer::{:?}] Dropping machine and disconnecting clients...",
self.machine_identification_unique
);
smol::block_on(self.namespace.disconnect_all());
}
}
impl Pelletizer
{
pub const MACHINE_IDENTIFICATION: MachineIdentification = MachineIdentification {
vendor: VENDOR_QITECH,
machine: MACHINE_PELLETIZER,
};
pub fn update(&mut self)
{
//let laser_data = smol::block_on(async { self.inverter.read().await.get_data().await });
}
}
+61
View File
@@ -0,0 +1,61 @@
use std::time::Instant;
use crate::pelletizer::MutationRequests;
use crate::serial::devices::us_3202510::US3202510;
use crate::serial::{registry::SERIAL_DEVICE_REGISTRY};
use crate::{MachineNewHardware, MachineNewTrait};
use super::{Pelletizer, api::PelletMachineNamespace};
use anyhow::Error;
impl MachineNewTrait for Pelletizer
{
fn new(params: &crate::MachineNewParams<'_, '_, '_, '_, '_, '_, '_>) -> Result<Self, Error>
where
Self: Sized,
{
let hardware_serial = match params.hardware
{
MachineNewHardware::Serial(serial) => *serial,
_ => return Err(Error::msg("Invalid hardware type for Pelletizer")),
};
let inverter = match smol::block_on(
SERIAL_DEVICE_REGISTRY.downcast_arc_rwlock::<US3202510>(hardware_serial.device.clone()),
) {
Ok(inverter) => inverter,
Err(_) => return Err(Error::msg("Failed to downcast to US3202510")),
};
let (sender, receiver) = smol::channel::unbounded();
let pelletizer = Self {
main_sender: params.main_thread_channel.clone(),
api_receiver: receiver,
api_sender: sender,
machine_identification_unique: params.get_machine_identification_unique(),
namespace: PelletMachineNamespace {
namespace: params.namespace.clone(),
},
emitted_default_state: false,
last_measurement_emit: Instant::now(),
inverter,
inverter_snapshot_id: 0,
mutation_request: MutationRequests {
running: None,
direction: None,
frequency: None,
accleration_level: None,
decleration_level: None,
}
};
Ok(pelletizer)
}
}
+4 -1
View File
@@ -16,7 +16,7 @@ use crate::{
use crate::extruder1::ExtruderV2;
#[cfg(not(feature = "mock-machine"))]
use crate::{
aquapath1::AquaPathV1, buffer1::BufferV1, extruder2::ExtruderV3, laser::LaserMachine,
aquapath1::AquaPathV1, buffer1::BufferV1, extruder2::ExtruderV3, laser::LaserMachine, pelletizer::Pelletizer,
winder2::Winder2,
};
@@ -118,6 +118,9 @@ lazy_static! {
#[cfg(not(feature = "mock-machine"))]
mc.register::<LaserMachine>(LaserMachine::MACHINE_IDENTIFICATION);
#[cfg(not(feature = "mock-machine"))]
mc.register::<Pelletizer>(Pelletizer::MACHINE_IDENTIFICATION);
#[cfg(not(feature = "mock-machine"))]
mc.register::<BufferV1>(BufferV1::MACHINE_IDENTIFICATION);
+1
View File
@@ -1,6 +1,7 @@
#[cfg(feature = "mock-machine")]
pub mod extruder_mock;
pub mod laser;
pub mod us_3202510;
#[cfg(feature = "mock-machine")]
pub mod mock;
#[cfg(feature = "mock-machine")]
@@ -0,0 +1,289 @@
use modbus::{RequestResult, rtu::{DispatchError, backends::LinuxTransport}};
// external deps
use serde::{Deserialize, Serialize};
// use modbus::ExceptionCode
// internal deps
pub use request::Request;
use units::{Frequency, electric_current::ampere, electric_potential::volt, frequency::hertz, thermodynamic_temperature::{degree_celsius, kelvin}};
// modules
mod register;
mod request;
mod serial_device;
mod transport;
use crate::serial::devices::us_3202510::{register::{HoldingRegister, InputRegister}, transport::{CustomTransport, EntryContext}};
type ModbusInterface = modbus::rtu::Interface<CustomTransport, EntryContext, 25>;
#[derive(Debug)]
pub struct US3202510
{
pub path: String,
pub config: Config,
pub status: Option<Status>,
failed_attempts: u8,
interface: ModbusInterface,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Config
{
pub snapshot_id: u64,
pub running: bool,
pub direction: bool,
pub frequency_target: u16, // 0 - 99hz
pub frequency_min: u16,
pub frequency_max: u16,
pub acceleration_level: u16, // 1 - 15
pub deceleration_level: u16, // 1 - 15
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Status
{
pub frequency: units::Frequency, // 1 - 99hz
pub voltage: units::ElectricPotential,
pub current: units::ElectricCurrent,
pub temperature: units::ThermodynamicTemperature,
pub operation_status: OperationStatus,
}
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Direction
{
#[default]
Stopped,
Forward,
Reverse,
}
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OperationStatus
{
#[default]
Idle,
Running,
Fault,
}
impl US3202510
{
pub fn update(&mut self)
{
if self.config.snapshot_id == 0
{
self.refresh_config();
self.config.snapshot_id += 1;
}
match self.interface.await_result()
{
Ok(result) =>
{
self.handle_result(result);
},
Err(e) =>
{
match e
{
modbus::rtu::ReceiveError::NoPendingRequest => {},
e =>
{
tracing::error!("Error reciving result: {:?}", e);
}
}
},
}
if !self.interface.has_pending_request()
{
match self.interface.dispatch_next_request()
{
Ok(_) =>
{
tracing::error!("Success sending request: {:?}", self.interface);
},
Err(e) =>
{
match e
{
DispatchError::RequestPending => {},
DispatchError::QueueEmpty => {},
DispatchError::Transport(err) =>
{
tracing::error!("Error sending request: {}", err);
},
DispatchError::BridgeDropped =>
{
tracing::error!("Bridge dropped");
},
}
},
}
}
}
fn queue_request(&mut self, request: Request)
{
let data = request.to_interface_request();
match self.interface.queue_request(data.type_id, data.payload, data.priority)
{
Ok(_) => {},
Err(x) =>
{
match x
{
modbus::QueueItemError::QueueFull => { tracing::error!("QueueFull!"); },
modbus::QueueItemError::DuplicateItem => { tracing::error!("DuplicateItem!"); },
}
},
}
}
pub fn refresh_status(&mut self)
{
self.queue_request(Request::RefreshStatus);
}
pub fn refresh_config(&mut self)
{
self.queue_request(Request::RefreshState);
}
pub fn set_running(&mut self, running: bool)
{
if self.config.running == running { return; }
tracing::error!("Setting running to: {}", running);
match running
{
true =>
{
let value: u16 = match self.config.direction
{
true => 1,
false => 3,
};
self.queue_request(Request::SetRotationState(value));
},
false => { self.queue_request(Request::SetRotationState(2)); },
}
self.config.running = running;
}
pub fn set_direction(&mut self, direction: bool)
{
if self.config.direction != direction && self.config.running
{
let value: u16 = if direction { 1 } else { 3 };
tracing::error!("Setting direction to: {}", value);
self.queue_request(Request::SetRotationState(value));
}
self.config.direction = direction;
}
pub fn set_frequency_target(&mut self, frequency: u16)
{
self.config.frequency_target = frequency.min(990);
self.queue_request(Request::SetFrequencyTarget(self.config.frequency_target));
}
pub fn set_acceleration_level(&mut self, acceleration_level: u16)
{
self.config.acceleration_level = acceleration_level.clamp(1, 15);
self.queue_request(Request::SetAccelerationLevel(self.config.acceleration_level));
}
pub fn set_deceleration_level(&mut self, deceleration_level: u16)
{
self.config.deceleration_level = deceleration_level.clamp(1, 15);
self.queue_request(Request::SetDecelerationLevel(self.config.deceleration_level));
}
fn handle_result(&mut self, response: RequestResult)
{
match response
{
RequestResult::ReadHoldingRegisters(data) =>
{
tracing::error!("BRANCH: HIT: {:?}", data);
let frequency_target: u16 =
*data.result.get(0)
.unwrap_or(&0);
let _run_command: u16 =
*data.result.get(1)
.unwrap_or(&0);
let acceleration_level: u16 =
*data.result.get(2)
.unwrap_or(&0);
let deceleration_level: u16 =
*data.result.get(3)
.unwrap_or(&0);
self.config.running = _run_command != 2;
self.config.frequency_target = frequency_target;
self.config.acceleration_level = acceleration_level;
self.config.deceleration_level = deceleration_level;
self.config.snapshot_id += 1;
},
RequestResult::ReadInputRegisters(data) =>
{
let frequency: u16 = *data.result.get(InputRegister::CurrentFrequency as usize).unwrap_or(&0);
let voltage: u16 = *data.result.get(InputRegister::BusVoltage as usize).unwrap_or(&0);
let current: u16 = *data.result.get(InputRegister::LineCurrent as usize).unwrap_or(&0);
let temperature: u16 = *data.result.get(InputRegister::DriveTemperature as usize).unwrap_or(&0);
let operation_status: u16 = *data.result.get(InputRegister::SystemStatus as usize).unwrap_or(&0);
self.status = Some(Status {
frequency: units::Frequency::new::<hertz>((frequency as f64) / 10.0),
voltage: units::ElectricPotential::new::<volt>((voltage as f64) / 10.0),
current: units::ElectricCurrent::new::<ampere>((current as f64) / 10.0),
temperature: units::ThermodynamicTemperature::new::<kelvin>((temperature as f64)),
operation_status: if operation_status == 0 { OperationStatus::Idle } else if operation_status == 1 {OperationStatus::Running } else { OperationStatus::Fault },
});
},
RequestResult::PresetHoldingRegister(request_result_data) =>
{
tracing::error!("PresetHoldingRegister: WORKED");
self.config.snapshot_id += 1;
},
RequestResult::Exception(request_result_data) =>
{
tracing::error!("Received exception: {:?}", request_result_data.result);
},
}
}
}
#[cfg(test)]
mod tests
{
#[test]
fn test_requests() {}
}
@@ -0,0 +1,83 @@
use strum_macros::{EnumCount};
#[derive(Debug, Clone, Copy, EnumCount)]
pub enum HoldingRegister
{
/// Register 0x0002
SetFrequency = 0x2,
/// Register 0x0003
#[allow(dead_code)]
RunCommand = 0x3,
/// Register 0x0004
#[allow(dead_code)]
AccelerationTime = 0x4,
/// Register 0x0005
#[allow(dead_code)]
DecelerationTime = 0x5,
}
#[derive(Debug, Clone, Copy, EnumCount)]
pub enum InputRegister
{
/// Register 0x0008
#[allow(dead_code)]
BusVoltage,
/// Register 0x0009
#[allow(dead_code)]
LineCurrent,
/// Register 0x000A
#[allow(dead_code)]
DriveTemperature,
/// Register 0x000B
#[allow(dead_code)]
SystemStatus,
/// Register 0x000C
#[allow(dead_code)]
ErrorCode,
/// Register 0x000D
#[allow(dead_code)]
CurrentFrequency,
}
impl HoldingRegister
{
pub const OFFSET: u16 = 0x2;
pub const fn address(self) -> u16
{
match self
{
Self::SetFrequency => Self::OFFSET, // 0x0002
Self::RunCommand => Self::OFFSET + 1, // 0x0003
Self::AccelerationTime => Self::OFFSET + 2, // 0x0004
Self::DecelerationTime => Self::OFFSET + 3, // 0x0005
}
}
}
impl InputRegister
{
pub const OFFSET: u16 = 0x8;
#[allow(dead_code)]
pub const fn address(self) -> u16
{
match self
{
Self::BusVoltage => Self::OFFSET, // 0x0008
Self::LineCurrent => Self::OFFSET + 1, // 0x0009
Self::DriveTemperature => Self::OFFSET + 2, // 0x000A
Self::SystemStatus => Self::OFFSET + 3, // 0x000B
Self::ErrorCode => Self::OFFSET + 4, // 0x000C
Self::CurrentFrequency => Self::OFFSET + 5, // 0x000D
}
}
}
@@ -0,0 +1,117 @@
use modbus::request::WriteRegister;
use strum::EnumCount;
use strum_macros::{EnumCount};
use crate::serial::devices::us_3202510::register::HoldingRegister;
use crate::serial::devices::us_3202510::register::InputRegister;
use modbus::Request as InterfaceRequest;
use modbus::request::ReadRegisters;
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
pub enum Request
{
RefreshStatus,
RefreshState,
SetRotationState(u16),
SetFrequencyTarget(u16),
SetAccelerationLevel(u16),
SetDecelerationLevel(u16),
}
#[derive(Debug)]
pub struct RequestData
{
pub type_id: u32,
pub priority: u32,
pub payload: InterfaceRequest,
}
impl Request
{
pub fn to_interface_request(&self) -> RequestData
{
match self
{
Request::RefreshStatus =>
{
let payload =
InterfaceRequest::ReadInputRegisters(
ReadRegisters
{
start_address: InputRegister::OFFSET,
quantity: InputRegister::COUNT as u16,
}
);
RequestData { type_id: 0, priority: 10, payload }
}
Request::RefreshState =>
{
let payload =
InterfaceRequest::ReadHoldingRegisters(
ReadRegisters
{
start_address: HoldingRegister::OFFSET,
quantity: HoldingRegister::COUNT as u16,
}
);
RequestData { type_id: 1, priority: 20, payload }
}
Request::SetRotationState(value) =>
{
let payload =
InterfaceRequest::PresetHoldingRegister(
WriteRegister
{
address: HoldingRegister::RunCommand as u16,
value: *value,
}
);
RequestData { type_id: 2, priority: 100, payload }
},
Request::SetFrequencyTarget(value) =>
{
let payload =
InterfaceRequest::PresetHoldingRegister(
WriteRegister
{
address: HoldingRegister::SetFrequency as u16,
value: *value,
}
);
RequestData { type_id: 3, priority: 50, payload }
}
Request::SetAccelerationLevel(value) =>
{
let payload =
InterfaceRequest::PresetHoldingRegister(
WriteRegister
{
address: HoldingRegister::AccelerationTime as u16,
value: *value,
}
);
RequestData { type_id: 4, priority: 30, payload }
},
Request::SetDecelerationLevel(value) =>
{
let payload =
InterfaceRequest::PresetHoldingRegister(
WriteRegister
{
address: HoldingRegister::DecelerationTime as u16,
value: *value,
}
);
RequestData { type_id: 5, priority: 30, payload }
},
}
}
}
@@ -0,0 +1,114 @@
use anyhow::anyhow;
use modbus::{Request, request::ReadRegisters, rtu::backends::LinuxTransport};
use smol::lock::RwLock;
use control_core::{
helpers::hashing::{byte_folding_u16, hash_djb2},
};
use units::{ConstZero, Frequency};
use super::US3202510;
use crate::{
MACHINE_PELLETIZER,
SerialDevice,
SerialDeviceNew,
SerialDeviceNewParams,
VENDOR_QITECH,
machine_identification::{
DeviceHardwareIdentification, DeviceHardwareIdentificationSerial, DeviceIdentification, DeviceMachineIdentification, MachineIdentification, MachineIdentificationUnique
}, serial::devices::us_3202510::{Config, ModbusInterface, request, transport::CustomTransport}
};
use serialport::{DataBits, FlowControl, Parity, StopBits};
use std::{sync::Arc, time::Duration};
impl SerialDevice for US3202510 {}
impl SerialDeviceNew for US3202510
{
fn new_serial(
params: &SerialDeviceNewParams,
) -> Result<(DeviceIdentification, Arc<RwLock<Self>>), anyhow::Error>
{
let hash = hash_djb2(params.path.as_bytes());
let serial = byte_folding_u16(&hash.to_le_bytes());
let device_identification = DeviceIdentification {
device_machine_identification: Some(DeviceMachineIdentification {
machine_identification_unique: MachineIdentificationUnique {
machine_identification: MachineIdentification {
vendor: VENDOR_QITECH,
machine: MACHINE_PELLETIZER,
},
serial,
},
role: 0,
}),
device_hardware_identification: DeviceHardwareIdentification::Serial(
DeviceHardwareIdentificationSerial {
path: params.path.clone(),
},
),
};
// let interface_config = modbus_rtu::Config {
// slave_id: 1,
// path: params.path.clone(),
// data_bits: DataBits::Eight,
// parity: Parity::None,
// stop_bits: StopBits::One,
// flow_control: FlowControl::None,
// timeout: Duration::from_millis(1000),
// baudrate: 9600,
// machine_operation_delay: Duration::from_millis(100),
// };
let port =
serialport::new(params.path.clone(), 9600)
.data_bits(DataBits::Eight)
.parity(Parity::None)
.flow_control(FlowControl::None)
.baud_rate(9600)
.stop_bits(StopBits::One)
.timeout(Duration::from_millis(500))
.open()
.expect("");
let transport = CustomTransport::new(port, 1);
let interface = ModbusInterface::new(transport);
// let request: [u8; 8] = [
// 0x01, // slave id
// 0x04, // Read Input Registers
// 0x00, 0x08, // start address
// 0x00, 0x06, // quantity
// 0xB1, 0xCA, // CRC (lo, hi)
// ];
let _self = Arc::new(RwLock::new(Self {
path: params.path.clone(),
config: Config {
running: false,
direction: true,
frequency_target: 0,
frequency_max: 50,
frequency_min: 0,
acceleration_level: 7,
deceleration_level: 7,
snapshot_id: 0,
},
status: None,
failed_attempts: 0,
interface,
}));
Ok((device_identification, _self))
}
}
@@ -0,0 +1,178 @@
use std::io;
use std::time::{Duration, Instant};
use modbus::WithContext;
use modbus::rtu::{FrameParseError, Transport as TransportTrait};
use modbus::rtu::{DispatchError, ReceiveError};
use modbus::rtu::Frame;
#[derive(Debug)]
pub struct CustomTransport
{
port: std::sync::Mutex<Box<dyn serialport::SerialPort>>,
slave_id: u8,
}
// #[derive(Debug, Clone, PartialEq)]
// pub struct EntryContext
// {
// type_id: u32
// }
pub type EntryContext = u32;
impl CustomTransport
{
pub fn new(port: Box<dyn serialport::SerialPort>, slave_id: u8) -> Self
{
let p = std::sync::Mutex::new(port);
Self { port: p ,slave_id }
}
}
// implement InterfaceTransport for your RTU frames
impl TransportTrait<EntryContext> for CustomTransport
{
type Error = io::Error;
fn slave_id(&self) -> u8 { self.slave_id }
fn try_send(
&mut self,
item: WithContext<Frame, EntryContext>
) -> Result<(), DispatchError<Self::Error>>
{
let mut port = self.port.lock().unwrap();
match port.write_all(item.payload.bytes())
{
Ok(_) => {},
Err(e) =>
{
tracing::error!("Error: try_send: {:?}", e);
return Err(DispatchError::BridgeDropped);
},
}
match port.flush()
{
Ok(_) => {},
Err(e) =>
{
tracing::error!("Error: try_send: {:?}", e);
return Err(DispatchError::BridgeDropped);
},
}
Ok(())
}
fn try_recv(
&mut self
) -> Result<Option<Frame>, ReceiveError<Self::Error>>
{
let mut port = self.port.lock().unwrap();
let mut buf = [0u8; 256];
match port.read(&mut buf)
{
Ok(0) => Ok(None),
Ok(n) =>
{
match Frame::from_bytes(&buf[..n])
{
Ok(frame) => Ok(Some(frame)),
Err(e) =>
{
Err(ReceiveError::ParseError(e))
},
}
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(None),
Err(e) => Err(ReceiveError::Transport(e)),
}
}
fn send(
&mut self,
item: WithContext<Frame, EntryContext>
) -> Result<(), DispatchError<Self::Error>>
{
let mut port = self.port.lock().unwrap();
match port.write_all(item.payload.bytes())
{
Ok(_) => {},
Err(e) =>
{
tracing::error!("Error: try_send: {:?}", e);
},
}
match port.flush()
{
Ok(_) => {},
Err(e) =>
{
tracing::error!("Error: try_send: {:?}", e);
},
}
Ok(())
}
fn recv(&mut self) -> Result<Frame, ReceiveError<Self::Error>>
{
let mut port = self.port.lock().unwrap();
let mut buf = [0u8; 256];
let mut pos = 0;
let mut now: Instant = Instant::now();
let timeout = Duration::from_secs(1);
loop
{
match port.read(&mut buf[pos..])
{
Ok(0) =>
{
return Err(ReceiveError::ParseError(FrameParseError::TooSmall(0)));
}
Ok(n) =>
{
pos += n;
if let Ok(frame) = Frame::from_bytes(&buf[..pos])
{
return Ok(frame);
}
now = Instant::now(); // reset timeout timer
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock =>
{
if now.elapsed() >= timeout
{
if pos > 0
{
match Frame::from_bytes(&buf[..pos])
{
Ok(frame) => { return Ok(frame); },
Err(e) => { return Err(ReceiveError::ParseError(e)); },
}
}
return Err(ReceiveError::Timeout);
}
std::thread::sleep(Duration::from_millis(150));
}
Err(e) => return Err(ReceiveError::Transport(e)),
}
}
}
}
+6
View File
@@ -3,6 +3,7 @@ use lazy_static::lazy_static;
use crate::{
SerialDevice, SerialDeviceIdentification, machine_identification::DeviceIdentification,
serial::devices::laser::Laser,
serial::devices::us_3202510::US3202510,
};
#[cfg(feature = "mock-machine")]
@@ -109,6 +110,11 @@ lazy_static! {
product_id: 0x6001,
});
sdr.register::<US3202510>(SerialDeviceIdentification {
vendor_id: 0x0403,
product_id: 0x6001,
});
// Register MockSerialDevice when mock-machine feature is enabled
#[cfg(feature = "mock-machine")]
sdr.register::<MockSerialDevice>(SerialDeviceIdentification {
+1 -1
View File
@@ -10,7 +10,7 @@ buildNpmPackage rec {
ELECTRON_SKIP_BINARY_DOWNLOAD = 1;
makeCacheWritable = true;
npmDepsHash = "sha256-UHPIoyMTzr7jkvbpZsePaNJ6s76h3R/0pcnbCkOcdYo=";
npmDepsHash = "sha256-wzVjsgcZsViPOyQKlKTb5gJIeY416KyCSiheSXSRZoc=";
npmFlags = [ "--no-audit" "--no-fund" ];
installPhase = ''
+1 -1
View File
@@ -17,7 +17,7 @@ pub async fn find_ethercat_interface() -> String {
return interface;
}
Err(e) => {
tracing::warn!("No working interface found: {}. Retrying...", e);
// tracing::warn!("No working interface found: {}. Retrying...", e);
Timer::after(Duration::from_secs(1)).await;
}
}
+13 -18
View File
@@ -3,16 +3,9 @@ use crate::{
socketio::main_namespace::machines_event::MachineObj,
};
use machines::{
AsyncThreadMessage, MachineConnection, MachineNewHardware, MachineNewHardwareSerial,
MachineNewParams, SerialDevice, SerialDeviceIdentification, SerialDeviceNew,
SerialDeviceNewParams,
laser::LaserMachine,
machine_identification::{
AsyncThreadMessage, MachineConnection, MachineNewHardware, MachineNewHardwareSerial, MachineNewParams, SerialDevice, SerialDeviceIdentification, SerialDeviceNew, SerialDeviceNewParams, laser::LaserMachine, machine_identification::{
DeviceIdentification, DeviceIdentificationIdentified, MachineIdentificationUnique,
},
registry::{MACHINE_REGISTRY, MachineRegistry},
serial::{devices::laser::Laser, init::SerialDetection},
winder2::api::GenericEvent,
}, pelletizer::Pelletizer, registry::{MACHINE_REGISTRY, MachineRegistry}, serial::{devices::{laser::Laser, us_3202510::US3202510}, init::SerialDetection}, winder2::api::GenericEvent
};
#[cfg(feature = "development-build")]
use std::sync::atomic::{AtomicBool, Ordering};
@@ -164,19 +157,20 @@ pub async fn handle_serial_device_hotplug(
app_state: Arc<SharedState>,
map: HashMap<String, UsbPortInfo>,
) {
let laser_ident = SerialDeviceIdentification {
// IDENTIFIER OF THE USB DEVICE, NOT THE ACTUAL DEVICE
let usb_interface_id = SerialDeviceIdentification {
vendor_id: 0x0403,
product_id: 0x6001,
};
let laser = SerialDetection::get_path_by_id(laser_ident, map);
let path = SerialDetection::get_path_by_id(usb_interface_id, map);
let mut unique_ident: Option<MachineIdentificationUnique> = None;
{
let machines = app_state.current_machines_meta.lock().await;
for machine in machines.iter() {
if machine.machine_identification_unique.machine_identification
== LaserMachine::MACHINE_IDENTIFICATION
for machine in machines.iter()
{
if machine.machine_identification_unique.machine_identification == Pelletizer::MACHINE_IDENTIFICATION
{
unique_ident = Some(machine.machine_identification_unique.clone());
break;
@@ -185,11 +179,12 @@ pub async fn handle_serial_device_hotplug(
}
// Machine isnt connected, so add it back
if laser.is_some() && unique_ident.is_none() {
if path.is_some() && unique_ident.is_none() {
let serial_params = SerialDeviceNewParams {
path: laser.unwrap(),
path: path.unwrap(),
};
match Laser::new_serial(&serial_params) {
match US3202510::new_serial(&serial_params) {
Ok((device_identification, serial_device)) => {
add_serial_device(
app_state.clone(),
@@ -202,7 +197,7 @@ pub async fn handle_serial_device_hotplug(
}
_ => (),
};
} else if laser.is_none() && unique_ident.is_some() {
} else if path.is_none() && unique_ident.is_some() {
let unique_ident = unique_ident.unwrap();
app_state
.clone()