Răsfoiți Sursa

内容确定,该版本基本保存。单纯采用sqlite作为存储,以定时任务作为基本实现思路。

zii 3 luni în urmă
comite
4cd384c49c
12 a modificat fișierele cu 1802 adăugiri și 0 ștergeri
  1. 7 0
      .cargo/config.toml
  2. 7 0
      .gitignore
  3. 924 0
      Cargo.lock
  4. 13 0
      Cargo.toml
  5. 2 0
      build.bat
  6. 64 0
      src/data/mod.rs
  7. 140 0
      src/main.rs
  8. 54 0
      src/modbus/master.rs
  9. 76 0
      src/modbus/mod.rs
  10. 54 0
      src/modbus/slave.rs
  11. 411 0
      src/uart/mod.rs
  12. 50 0
      src/uart/ttt.txt

+ 7 - 0
.cargo/config.toml

@@ -0,0 +1,7 @@
+[target.armv7-unknown-linux-gnueabihf]
+linker = "arm-linux-gnueabihf-gcc"
+rustflags = ["-C", "target-feature=+crt-static"]
+
+[target.armv7-unknown-linux-musleabihf]
+linker = "/cross/armv7l-linux-musleabihf-native/bin/armv7l-linux-musleabihf-gcc"
+rustflags = ["-C", "target-feature=+crt-static"]

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+/target
+*.img
+*.zip
+*.dts
+*.bin
+/*.store
+*.sql

+ 924 - 0
Cargo.lock

@@ -0,0 +1,924 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "backtrace"
+version = "0.3.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+
+[[package]]
+name = "bumpalo"
+version = "3.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+
+[[package]]
+name = "cc"
+version = "1.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+
+[[package]]
+name = "chrono"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "fs2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
+dependencies = [
+ "hashbrown",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "io-kit-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b"
+dependencies = [
+ "core-foundation-sys",
+ "mach2",
+]
+
+[[package]]
+name = "io-uring"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
+dependencies = [
+ "bitflags 2.9.1",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.174"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libudev"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0"
+dependencies = [
+ "libc",
+ "libudev-sys",
+]
+
+[[package]]
+name = "libudev-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
+name = "mach2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+
+[[package]]
+name = "memmap2"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core 0.8.6",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.11",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall 0.2.16",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.5.17",
+ "smallvec",
+ "windows-targets",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
+dependencies = [
+ "bitflags 2.9.1",
+]
+
+[[package]]
+name = "rusqlite"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f"
+dependencies = [
+ "bitflags 2.9.1",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serialport"
+version = "4.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acaf3f973e8616d7ceac415f53fc60e190b2a686fbcf8d27d0256c741c5007b"
+dependencies = [
+ "bitflags 2.9.1",
+ "cfg-if",
+ "core-foundation",
+ "core-foundation-sys",
+ "io-kit-sys",
+ "libudev",
+ "mach2",
+ "nix",
+ "scopeguard",
+ "unescaper",
+ "winapi",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
+
+[[package]]
+name = "sled"
+version = "0.34.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
+dependencies = [
+ "crc32fast",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+ "fs2",
+ "fxhash",
+ "libc",
+ "log",
+ "parking_lot 0.11.2",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "snmp"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2a575449a5c487091e541c0cb4ccd83620167fd52363f816fe28f6f357fc00"
+
+[[package]]
+name = "socket2"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "t113_uart_data_collector"
+version = "0.1.0"
+dependencies = [
+ "chrono",
+ "memmap2",
+ "rusqlite",
+ "serialport",
+ "sled",
+ "snmp",
+ "tokio",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio"
+version = "1.47.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "io-uring",
+ "libc",
+ "mio",
+ "parking_lot 0.12.4",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "slab",
+ "socket2",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unescaper"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
+
+[[package]]
+name = "windows-result"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

+ 13 - 0
Cargo.toml

@@ -0,0 +1,13 @@
+[package]
+name = "t113_uart_data_collector"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+serialport = "4.7.3"
+rusqlite =  { version = "0.37.0", features = ["bundled"] }
+sled = "0.34.7"
+snmp = "0.2.2"
+tokio = { version = "1.28.0", features = ["full"] }
+memmap2 = "0.9.8"
+chrono = "0.4.42"

+ 2 - 0
build.bat

@@ -0,0 +1,2 @@
+docker exec -w "/home/code/全志service/t113_uart_data_collector" debCode "/root/.cargo/bin/cargo" build --target=armv7-unknown-linux-musleabihf --release
+@REM docker exec -w "/home/code/全志service/t113_uart_data_collector" debCode "/root/.cargo/bin/cargo" clean

+ 64 - 0
src/data/mod.rs

@@ -0,0 +1,64 @@
+// 更新函数签名以接受前缀和端口号列表
+pub fn init(tty_prefix: &str, tty_numbers: Vec<i32>) -> Result<(),rusqlite::Error> {
+    let backends = rusqlite::Connection::open("data/backends.db")?;
+     /*
+     id             点位序号
+     name           点位名
+     offset 
+     uart           串口号 1=ttyS1 2=ttyS2 ... 5=ttyS5
+     client_id      modbus-clientID
+     command_code   modbus-rtu指令码
+     address        modbus-rtu地址
+     words          modbus-rtu数据长度
+     last           最后采集的数据(字节串)
+     interval       采集间隔
+     schadule       采集计划(下一次采集发生的时间)
+     enabled        是否启用
+     */
+    backends.execute(r#"CREATE TABLE IF NOT EXISTS r_only (
+        id INTEGER PRIMARY KEY,
+        name TEXT,
+        baudrate INTEGER,
+        uart_ctl INTEGER,
+        uart INTEGER NOT NULL,
+        client_id INTEGER NOT NULL,
+        command_code INTEGER NOT NULL,
+        address INTEGER NOT NULL,
+        words INTEGER NOT NULL,
+        last blob,
+        interval INTEGER NOT NULL DEFAULT 60,
+        schadule real NOT NULL DEFAULT (julianday('now')),
+        enabled bool NOT NULL DEFAULT true);"#, [])?;
+    if let Some(a) = backends.close().err(){
+        return Err(a.1);
+    }
+    println!("[ ] table r_only created");
+    
+    let sql = r#"CREATE TABLE IF NOT EXISTS data(
+        ts real NOT NULL default (julianday('now')),
+        client_id INTEGER NOT NULL,
+        address INTEGER NOT NULL,
+        raw blob NOT NULL,
+        opln blob not null);"#;
+        
+    // 根据提供的前缀和编号创建数据库文件
+    for i in &tty_numbers {
+        let db_filename = format!("data/{}{}_data.db", tty_prefix, i);
+        let conn = rusqlite::Connection::open(&db_filename)?;
+        conn.execute(sql, [])?;
+        if let Some(a) = conn.close().err(){
+            return Err(a.1);
+        }
+    }
+    
+    let conn = rusqlite::Connection::open("data/gpoi_data.db")?;
+    conn.execute("CREATE TABLE IF NOT EXISTS data(
+        time timestamp NOT NULL default current_timestamp,
+        data blob not null
+)", [])?;
+    if let Some(a) = conn.close().err(){
+        return Err(a.1);
+    }
+    println!("[ ] table data created");
+    Ok(())
+}

+ 140 - 0
src/main.rs

@@ -0,0 +1,140 @@
+mod data;
+mod uart;
+mod modbus;
+
+/// 数据保持时间(julianday) 默认14天
+const HOLD_INTERVAL: f32 = 1.0;
+
+fn now() -> String{
+    chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
+}
+
+
+#[repr(u8)]
+#[derive(PartialEq, PartialOrd,Debug)]
+#[allow(dead_code)]
+pub enum LogLevel{
+    Debug = 0,
+    Warning,
+    Info,
+    Error,
+    OFF = 0xfe,
+    Debuging,
+}
+
+use LogLevel::*;
+const LOG_LEVEL: LogLevel = Warning;
+
+pub fn log(level: LogLevel, msg: String) {
+    if level < LOG_LEVEL {
+        return;
+    }
+    println!("[{:?}] {} {}", level, now(), msg);
+}
+
+fn main(){
+    // 初始化日志系统
+    // env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
+    let tty_prefix = "ttyUSB";
+    let tty_numbers: Vec<i32> = vec![0];
+    data::init(tty_prefix, tty_numbers.clone()).unwrap();
+    uart::collector(tty_prefix, tty_numbers);
+    // uart::collector("/dev/ttyS",vec![0,1,2,3,4,5]);
+}
+
+// #[test]
+// fn main() {
+// // fn test() {
+//     use crate::modbus::crc16;
+//     println!("welcome with service modbus");
+//     println!("now in main progress");
+//     // uart::collector();
+//     for i in 0..6{
+//         let uart = serialport::new(format!("/dev/ttyS{i}").as_str(), 9600);
+//         let mut buf:[u8;256] = [0;256];
+//         match uart.open(){
+//             Ok(mut u) => {
+//                 let backend = (4,0x03,0x01,1);
+//                 let mut payload_byte = crate::modbus::master::raw_payload_for_ronly(backend.0, backend.1, backend.2, backend.3);
+//                 payload_byte.append(&mut crc16(&payload_byte).to_le_bytes().to_vec());
+//                 match u.write(payload_byte.as_slice()).map_err(|e| println!("{}:{}:{}", file!(),line!(),e)){
+//                     Ok(u) => {
+//                         println!("at ttyS{i}send {u} bytes: {:02x?}",payload_byte);
+//                     },
+//                     Err(e) => {
+//                         println!("{:?}",e)
+//                     }
+//                 };
+//                 // let _ =u.bytes_to_read(std::time::Duration::from_millis(3000)).map_err(|e| println!("{}:{}:{}", file!(),line!(),e));
+//                 let _ =u.set_timeout(std::time::Duration::from_millis(2000)).map_err(|e| println!("{}:{}:{}", file!(),line!(),e));
+//                 let mut short_buf = [0u8];
+//                 if let Err(e)= u.read(&mut short_buf){
+//                     println!("\t{:?}",e);
+//                     continue;
+//                 }
+//                 print!("{:02x} ", short_buf[0]);
+//                 buf[0] = short_buf[0];
+//                 if let Err(e) =u.set_timeout(std::time::Duration::from_millis(10)).map_err(|e| println!("{}:{}:{}", file!(),line!(),e)){println!("\tread-timeout set failure {:?}",e)};
+//                 let mut i = 1;
+//                 while match u.read(&mut short_buf){
+//                     Ok(_) => {buf[i] = short_buf[0];print!("{:02x} ", short_buf[0]);i+=1;true},
+//                     Err(_) => false,
+//                 }{}
+//                 println!();
+//                 let layload_len = i;
+
+//                 // let layload_len = match u.read(&mut buf){
+//                 //     Ok(len) => len,
+//                 //     Err(e) => {println!("now on ttyS{i}, got err {}, send {:02x?}", e, payload_byte);continue;},
+//                 // };
+
+//                 if layload_len < 2{
+//                     println!("data too short, got {:02x?}", buf[..layload_len].to_vec());
+//                     continue;
+//                 }
+//                 let buf = &buf[..layload_len];
+//                 if u16::from_be_bytes([buf[layload_len-1],buf[layload_len-2]])!= crc16(&buf[..layload_len-2]){
+//                     println!("CRC16 error, {}, {}, rcv {:02x?}",u16::from_be_bytes([buf[layload_len-1],buf[layload_len-2]]),crc16(&buf[..layload_len-2]), buf.to_vec());
+//                     continue;
+//                 }
+//                 let (client_id, op_back, len_rest, data) = (buf[0],buf[1],buf[2],&buf[3..layload_len-2]);
+//                 if backend.0 != client_id || backend.1 != op_back || len_rest as u16 != backend.3*2{
+//                     println!("Wrong data\n\tclientid {client_id}, op_back {op_back}, len_rest {len_rest}\ngot\tclietnid {}, op_back {}, len_rest {}", backend.0,backend.1,backend.3*2);
+//                 }
+//                 println!("{:02x?}",data)
+//             }
+//             Err(e) => println!("{e}")
+//         }
+//     }
+//     print!("asd")
+// }
+
+/* sql
+import sqlite3; conn= sqlite3.connect('data/backends.db');conn.cursor().execute(
+''' CREATE TABLE IF NOT EXISTS data(
+ts real NOT NULL default (julianday('now')),
+client_id INTEGER NOT NULL,
+address INTEGER NOT NULL,
+raw blob NOT NULL,
+opln blob not null) '''
+).fetchall();
+PRAGMA table_info
+conn.cursor().execute(' PRAGMA table_info(r_only) ').fetchall();
+
+*/
+/* 
+echo "[Unit]
+Description=a pounch of toolkit service
+
+[Service]
+Type=simple
+ExecStart=/root/toolkits/t113_uart_data_collector
+WorkingDirectory=/root/toolkits
+Restart=always
+RestartSec=5
+MemoryMax=50M
+
+[Install]
+WantedBy=multi-user.target" > /etc/systemd/system/toolkit.uart_collector.service
+systemctl daemon-reload
+*/

+ 54 - 0
src/modbus/master.rs

@@ -0,0 +1,54 @@
+// pub enum Message{
+//     // ReadCoil([u8;6]),
+//     // ReadInputStatus([u8;6]),
+//     // ReadRWRegister([u8;6]),
+//     // ReadROnlyRegister([u8;6]),
+//     // WriteCoil([u8;6]),
+//     // WritRegister([u8;6]),
+//     // WritMultiCoils(Vec<u8>),
+//     // WritMultiRegister(Vec<u8>),
+//     Normal([u8;6]),
+//     Multi(Vec<u8>)
+// }
+
+// impl Message{
+//     pub fn to_bytes(&self) -> &[u8]{
+//         match self {
+//             Message::Normal(byte) => byte,
+//             Message::Multi(bytes) => bytes.as_slice(),
+//         }
+//     }
+// }
+
+// pub enum Payload{
+//     WordLength(u16),
+//     Data(Vec<u8>),
+// }
+
+pub fn raw_payload_for_ronly(client_id: u8,operation: u8, address: u16, words: u16)-> Vec<u8>{
+    let addr: [u8; 2] = address.to_be_bytes();
+    let words: [u8; 2] = words.to_be_bytes();
+    return vec![client_id, operation as u8,addr[0], addr[1], words[0],words[1]];
+}
+
+// pub fn payload(client_id: u8,operation: super::CommandCode, address: u16, words: Payload) -> Message {
+//     let addr: [u8; 2] = address.to_be_bytes();
+//     let (words,mut data ): ([u8;2],_) = match words {
+//         Payload::WordLength(len) => (len.to_be_bytes(), vec![]),
+//         Payload::Data(data) => ((data.len()as u16).to_be_bytes(), data),
+//     };
+//     let pays: [u8;6] =[client_id, operation as u8,addr[0], addr[1], words[0],words[1]];
+//     match operation {
+//         super::CommandCode::ReadCoil |
+//         super::CommandCode::ReadInputStatus |
+//         super::CommandCode::ReadRWRegister |
+//         super::CommandCode::ReadROnlyRegister |
+//         super::CommandCode::WriteCoil |
+//         super::CommandCode::WritRegister 
+//              => Message::Normal(pays),
+//         super::CommandCode::WritMultiCoils |
+//         super::CommandCode::WritMultiRegister
+//              =>{let mut pays = pays.to_vec();pays.append(&mut data); Message::Multi(pays)},
+//         _ => panic!("Unknown command code"),
+//     }
+// }

+ 76 - 0
src/modbus/mod.rs

@@ -0,0 +1,76 @@
+pub mod master;
+// pub mod slave;
+
+// enum UartMode {
+//     Master,
+//     Slave,
+// }
+
+#[derive(Debug,Clone, Copy)]
+pub enum CommandCode {
+    Unknown,
+    ReadCoil,
+    ReadInputStatus,
+    ReadRWRegister,
+    ReadROnlyRegister,
+    WriteCoil,
+    WritRegister,
+    WritMultiCoils,
+    WritMultiRegister,
+}
+
+impl From<u8> for CommandCode{
+    fn from(value: u8) -> Self {
+        match value{
+            0x01 => CommandCode::ReadCoil,
+            0x02 => CommandCode::ReadInputStatus,
+            0x03 => CommandCode::ReadRWRegister,
+            0x04 => CommandCode::ReadROnlyRegister,
+            0x05 => CommandCode::WriteCoil,
+            0x06 => CommandCode::WritRegister,
+            0x0f => CommandCode::WritMultiCoils,
+            0x10 => CommandCode::WritMultiRegister,
+            _ => CommandCode::Unknown
+        }
+    }
+}
+
+impl Into<u8> for CommandCode{
+    fn into(self)->u8{
+        match self {
+            CommandCode::ReadCoil => 0x01,
+            CommandCode::ReadInputStatus => 0x02,
+            CommandCode::ReadRWRegister => 0x03,
+            CommandCode::ReadROnlyRegister => 0x04,
+            CommandCode::WriteCoil => 0x05,
+            CommandCode::WritRegister => 0x06,
+            CommandCode::WritMultiCoils => 0x0f,
+            CommandCode::WritMultiRegister => 0x10,
+            CommandCode::Unknown => 0x00,
+        }
+    }
+
+}
+
+pub fn crc16(data: &[u8])->u16{
+    let mut crc = 0xFFFF;
+    // let mut len = 0;
+    for &byte in data {
+        // len += 1;
+        crc ^= byte as u16;
+        for _ in 0..8 {
+            if crc & 0x0001 != 0 {
+                crc >>= 1;
+                crc ^= 0xA001;
+            } else {
+                crc >>= 1;
+            }
+        }
+    }
+    // let mut ans: Vec<u8> = Vec::with_capacity(2);
+    // ans[..len].copy_from_slice(data);
+    // ans[len] = (crc >> 8) as u8;
+    // ans[len+1] = (crc & 0x00FF) as u8;
+    // return ans;
+    return crc;
+}

+ 54 - 0
src/modbus/slave.rs

@@ -0,0 +1,54 @@
+use super::master::Payload;
+
+pub enum Error{
+    PayloadTooShort,
+    DataNotSuitWithWordLength,
+    CRCError,
+    PayloadError
+}
+
+pub struct Operation{
+    pub client_id: u8,
+    pub operation: super::CommandCode,
+    pub address: u16,
+    pub msg: super::master::Payload,
+}
+
+impl TryFrom<&[u8]> for Operation{ 
+    type Error = Error;
+    fn try_from(value: &[u8]) -> Result<Self, Error> {
+        let len = value.len() as u16;
+        if len < 8{
+            Err(Error::PayloadTooShort)
+        } else{
+            let op = super::CommandCode::from(value[1]);
+            let msg = match op{
+                super::CommandCode::Unknown 
+                        => Err(Error::PayloadError),
+                    super::CommandCode::ReadCoil |
+                    super::CommandCode::ReadInputStatus |
+                    super::CommandCode::ReadRWRegister |
+                    super::CommandCode::ReadROnlyRegister |
+                    super::CommandCode::WriteCoil |
+                    super::CommandCode::WritRegister
+                        => Ok(super::master::Payload::WordLength(u16::from_be_bytes([value[4],value[5]]))),
+                    super::CommandCode::WritMultiCoils |
+                    super::CommandCode::WritMultiRegister
+                        =>{
+                            let ln_rest = u16::from_be_bytes([value[4],value[5]]);
+                            if ln_rest*2 +6!=len{
+                                Err(Error::DataNotSuitWithWordLength)
+                            } else {
+                                Ok(super::master::Payload::Data(value[6..ln_rest as usize].to_vec()))
+                            }
+                        }
+            }?;
+            Ok(Operation{
+                client_id: value[0],
+                operation: op,
+                address: u16::from_be_bytes([value[2],value[3]]),
+                msg: msg,
+            })
+        }
+    }
+}

+ 411 - 0
src/uart/mod.rs

@@ -0,0 +1,411 @@
+use crate::modbus::{crc16};
+use rusqlite::{params, Connection, Error};
+use std::{time::{Duration, Instant}};
+use std::path::Path;
+use crate::{log, LogLevel::*};
+
+const BUFFER_SIZE: usize = 256;
+const MAX_RETRY_ATTEMPTS: u32 = 3;
+
+fn now() -> String{
+    chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
+}
+
+// 安全地打开数据库连接,带重试机制
+fn safe_open_db(db_path: &str) -> Result<Connection, rusqlite::Error> {
+    log(Debug, format!("Attempting to open database: {}", db_path));
+    let mut attempts = 0;
+    loop {
+        match Connection::open(db_path) {
+            Ok(conn) => {
+                log(Debug, format!("Successfully opened database: {}", db_path));
+                return Ok(conn);
+            },
+            Err(e) => {
+                attempts += 1;
+                if attempts >= MAX_RETRY_ATTEMPTS {
+                    log(Error, format!("Failed to open database {} after {} attempts: {}", db_path, MAX_RETRY_ATTEMPTS, e));
+                    return Err(e);
+                }
+                log(Warning, format!("Failed to open database {}, attempt {}/{}, retrying... Error: {}", 
+                         db_path, attempts, MAX_RETRY_ATTEMPTS, e));
+                std::thread::sleep(Duration::from_secs(1));
+            }
+        }
+    }
+}
+
+// 安全地执行数据库操作,带重试机制
+fn safe_db_operation<T, F>(mut operation: F) -> Result<T, rusqlite::Error> 
+where
+    F: FnMut() -> Result<T, rusqlite::Error>,
+{
+    let mut attempts = 0;
+    loop {
+        match operation() {
+            Ok(result) => return Ok(result),
+            Err(e) => {
+                // 检查是否是数据库被删除或损坏的错误
+                match &e {
+                    Error::QueryReturnedNoRows => {
+                        return Err(e);
+                    }
+                    Error::SqliteFailure(_, Some(msg)) if msg.contains("no such table") => {}
+                    Error::SqliteFailure(_, Some(msg)) if msg.contains("no such column") => {}
+                    Error::SqliteFailure(_, Some(msg)) if msg.contains("no such database") => {}
+                    Error::SqliteFailure(_, Some(msg)) if msg.contains("no such index") => {}
+
+                    Error::SqliteFailure(_error, Some(msg)) => {
+                        // 如果数据库文件被删除或损坏,可能需要重新打开连接
+                        if msg.contains("database disk image is malformed") || 
+                           msg.contains("file is encrypted or is not a database") {
+                            log(Error, format!("{}: Database corrupted or deleted, error: {}", now(), msg));
+                            return Err(e);
+                        }
+                    }
+                    _ => {}
+                }
+                
+                attempts += 1;
+                if attempts >= MAX_RETRY_ATTEMPTS {
+                    log(Error, format!("{}: Database operation failed after {} attempts, giving up. Error: {:?}", 
+                             now(), MAX_RETRY_ATTEMPTS, e));
+                    return Err(e);
+                }
+                log(Warning, format!("{}: Database operation failed, attempt {}/{}, retrying... Error: {:?}", 
+                         now(), attempts, MAX_RETRY_ATTEMPTS, e));
+                std::thread::sleep(Duration::from_secs(1));
+            }
+        }
+    }
+}
+
+// 初始化数据库连接
+fn init_db_connections(tty: String) -> Result<(Connection, Connection), rusqlite::Error> {
+    log(Info, format!("Initializing database connections for uart {}...", tty));
+    
+    // 点位信息
+    log(Debug, "Opening backends.db...".to_string());
+    let backends_conn = safe_open_db("data/backends.db")?;
+    log(Debug, "Setting WAL mode for backends.db...".to_string());
+    backends_conn.query_one("PRAGMA journal_mode = WAL;", [],|_|{Ok(())})?;
+    
+    let db = format!("data/{}_data.db",tty);
+    // 历史数据
+    log(Debug, format!("Opening {}_data.db...", tty));
+    let data_conn = safe_open_db(&db)?;
+    log(Debug, format!("Setting WAL mode for {}...", db));
+    data_conn.query_one("PRAGMA journal_mode = WAL;", [],|_|{Ok(())})?;
+    
+    log(Info, format!("Database connections initialized successfully for uart {}", tty));
+    Ok((backends_conn, data_conn))
+}
+
+// 定义Backend结构体,使代码更清晰
+#[derive(Debug)]
+struct Backend {
+    client_id: u8,
+    command_code: u8,
+    address: u16,
+    words: u16,
+    schedule_delay: i64, // 距离下次执行的秒数
+    id: i64,
+    baudrate: u32,
+    uart_ctl: u8,
+}
+
+pub fn collector(tty_prefix: &str,tty_no: Vec<i32>){
+    let mut jobs = vec![];
+    for i in tty_no{
+        let tty_prefix = tty_prefix.to_string();
+        let job = std::thread::spawn(move || {
+            let i = i;
+            let tty = format!("/dev/{}{}", tty_prefix.clone(), i);
+            let interval = 10;
+            let mut buf:[u8;BUFFER_SIZE] = [0;BUFFER_SIZE];
+            
+            // 数据库连接
+            log(Info, format!("Starting uart {} collector thread...", i));
+            let tty_data = format!("{}{}", tty_prefix.clone(),i);
+            let (mut backends, mut conn) = match init_db_connections(tty_data) {
+                Ok(connections) => connections,
+                Err(e) => {
+                    log(Error, format!("{}: Failed to initialize database connections: {}", now(), e));
+                    std::thread::sleep(Duration::from_secs(interval));
+                    return;
+                }
+            };
+            // 串口轮询
+            loop {
+                let tty_data = format!("{}{}", tty_prefix.clone(),i);
+                // 检查数据库文件是否存在,如果不存在则重新初始化
+                if !Path::new("data/backends.db").exists() {
+                    log(Warning, format!("{}: backends.db file not found, reinitializing...", now()));
+                    match init_db_connections(tty_data) {
+                        Ok(connections) => {
+                            (backends, conn) = connections;
+                        },
+                        Err(e) => {
+                            log(Error, format!("{}: Failed to reinitialize database connections: {}", now(), e));
+                            std::thread::sleep(Duration::from_secs(interval));
+                            continue;
+                        }
+                    }
+                    continue;
+                }
+                
+                // 查询所有需要执行的backend信息
+                let backends_list = {
+                    let result = safe_db_operation(|| {
+                        let tx = backends.transaction()?;
+                        let mut backends_vec = Vec::new();
+                        
+                        {
+                            let mut stmt = tx.prepare("SELECT client_id, command_code, address, words,
+                                        CAST((schadule - julianday('now')) * 86400 AS INTEGER), id, baudrate, uart_ctl
+                                        FROM r_only WHERE uart=? ORDER BY schadule ASC")?;
+                            
+                            let backends_iter = stmt.query_map(params![i], |row| {
+                                Ok(Backend {
+                                    client_id: row.get(0)?,
+                                    command_code: row.get(1)?,
+                                    address: row.get(2)?,
+                                    words: row.get(3)?,
+                                    schedule_delay: row.get(4)?,
+                                    id: row.get(5)?,
+                                    baudrate: row.get(6)?,
+                                    uart_ctl: row.get(7)?,
+                                })
+                            })?;
+                            
+                            for backend in backends_iter {
+                                backends_vec.push(backend?);
+                            }
+                        } // stmt在这里被销毁,释放对tx的借用
+                        
+                        tx.commit()?; // 提交事务
+                        Ok(backends_vec)
+                    });
+                    
+                    match result {
+                        Ok(backends_vec) => backends_vec,
+                        Err(e) => {
+                            match e {
+                                rusqlite::Error::QueryReturnedNoRows => {
+                                    // 没有找到backend,这是正常情况,等待一段时间再继续
+                                    log(Debug, format!("{}: No backends found for uart {}, waiting...", now(), i));
+                                    Vec::new() // 返回空向量而不是None
+                                },
+                                _ => {
+                                    log(Error, format!("{} {}:{} {}", now(), file!(), line!(), e));
+                                    // 数据库可能已损坏或被删除,尝试重新初始化
+                                    match init_db_connections(tty_data.clone()) {
+                                        Ok(connections) => {
+                                            (backends, conn) = connections;
+                                        },
+                                        Err(e) => {
+                                            log(Error, format!("{}: Failed to reinitialize database connections: {}", now(), e));
+                                        }
+                                    }
+                                    Vec::new() // 返回空向量
+                                }
+                            }
+                        }
+                    }
+                };
+                
+                // 如果没有获取到backend信息,继续下一次循环
+                if backends_list.is_empty() {
+                    // 没有数据,等待一段时间再重新查询
+                    std::thread::sleep(Duration::from_secs(5));
+                    continue;
+                }
+                
+                // 查找需要立即执行的任务(schedule_delay <= 0)
+                let ready_backends: Vec<&Backend> = backends_list.iter()
+                    .filter(|backend| backend.schedule_delay <= 0)
+                    .collect();
+                
+                // 如果有需要立即执行的任务,则执行它们
+                if !ready_backends.is_empty() {
+                    for backend in ready_backends {
+                        // 记录操作开始时间
+                        let operation_start = Instant::now();
+                        
+                        // 继续执行串口通信逻辑...
+                        use serialport::DataBits as d;
+                        use serialport::Parity as p;
+                        use serialport::StopBits as s;
+                        use serialport::FlowControl as f;
+                        match serialport::new(
+                            &tty,
+                            backend.baudrate)
+                            .timeout(Duration::from_millis(5000))
+                            .parity([p::None,p::Odd,p::Even][((backend.uart_ctl & 0b11)%3)as usize] )
+                            .data_bits([d::Five,d::Six,d::Seven,d::Eight][((backend.uart_ctl >>2) & 0b11)as usize])
+                            .stop_bits([s::One,s::Two][((backend.uart_ctl >> 4)&0b1)as usize])
+                            .flow_control([f::None,f::Software,f::Hardware][(((backend.uart_ctl >> 5)&0b11)%3 )as usize])
+                        .open(){
+                            Ok(mut u) => {
+                                // let _ =u.set_timeout(Duration::from_millis(1000)).map_err(|e| log(Debug, format!("{} {}:{} {}",now(), file!(),line!(),e)));
+                                let mut payload_byte = crate::modbus::master::raw_payload_for_ronly(backend.client_id, backend.command_code, backend.address, backend.words);
+                                payload_byte.append(&mut crc16(&payload_byte).to_le_bytes().to_vec());
+                                let _ = u.write(&payload_byte).map_err(|e| log(Error, format!("{} {}:{} {}",now(), file!(),line!(),e)));
+                                
+                                let _ =u.set_timeout(Duration::from_millis(1000)).map_err(|e| log(Error, format!("{} {}:{} {}",now(), file!(),line!(),e)));
+                                if let Err(e)= u.read(&mut buf){
+                                    if e.kind() != std::io::ErrorKind::TimedOut {
+                                        log(Error, format!("{} {}:{} {:?}",now(),file!(),line!(), e));
+                                    }
+                                    continue;
+                                }
+                                let _ =u.set_timeout(Duration::from_millis(10)).map_err(|e| log(Warning, format!("{} {}:{} {}",now(), file!(),line!(),e)));
+                                let mut short_buf = [0u8];
+                                let mut buffer_index = 1;
+                                while match u.read(&mut short_buf){
+                                    Ok(_) => {buf[buffer_index] = short_buf[0];buffer_index+=1; buffer_index<BUFFER_SIZE},
+                                    Err(_) => false,
+                                }{}
+                                if buffer_index == BUFFER_SIZE{
+                                    log(Warning, "cost TOO MUCH BUFFER, now wasting ...".to_string());
+                                    while match u.read(&mut short_buf){
+                                        Ok(_) => {true},
+                                        Err(_) => false,
+                                    }{}
+                                    log(Warning, "wasted all left meassage".to_string());
+                                    continue;
+                                }
+                                if buffer_index < 6{
+                                    log(Warning, "buffer too short".to_string());
+                                    continue;
+                                }
+                                let payload_len = buffer_index;
+                                
+                                let buf_slice = &buf[..payload_len];
+                                if u16::from_le_bytes([buf_slice[payload_len-2],buf_slice[payload_len-1]])!= crc16(&buf_slice[..payload_len-2]){
+                                    log(Warning, format!("CRC16 error {:?}  {:?}",[buf_slice[payload_len-2],buf_slice[payload_len-1]], &buf_slice[payload_len-2..]));
+                                    continue;
+                                }
+                                let (client_id, op_back, len_rest, data) = (buf_slice[0],buf_slice[1],u16::from_le_bytes([buf_slice[2],buf_slice[3]]),&buf_slice[4..]);
+                                if backend.client_id != client_id || backend.command_code != op_back || len_rest != backend.words*2{
+                                    log(Warning, format!( "Wrong data client={} {} | op={} {} | words={} {}", backend.client_id,client_id , backend.command_code,op_back, backend.words, len_rest));
+                                    continue;
+                                }
+                                
+                                // 使用事务来确保更新操作的原子性
+                                {
+                                    let update_result = safe_db_operation(|| {
+                                        let tx = backends.transaction()?;
+                                        let rows_affected = tx.execute("UPDATE r_only SET schadule=datetime('now', '+' || interval || ' seconds'),last=? WHERE id=?", 
+                                                                      params![data, backend.id])?;
+                                        tx.commit()?;
+                                        Ok(rows_affected)
+                                    });
+                                    
+                                    match update_result {
+                                        Ok(rows_affected) => {
+                                            if rows_affected == 0 {
+                                                // 没有行被更新,可能记录已被删除或修改
+                                                log(Warning, format!("{}: No rows updated, backend may have been modified by another process", now()));
+                                            }
+                                        },
+                                        Err(e) => {
+                                            log(Error, format!("err at {}:{} ::{}", file!(), line!(), e));
+                                            // 尝试重新初始化数据库
+                                            match init_db_connections(tty_data.clone()) {
+                                                Ok(connections) => {
+                                                    (backends, conn) = connections;
+                                                },
+                                                Err(e) => {
+                                                    log(Error, format!("{}: Failed to reinitialize database connections: {}", now(), e));
+                                                }
+                                            }
+                                            continue;
+                                        }
+                                    }
+                                }
+                                
+                                // 插入数据到历史记录
+                                {
+                                    let insert_result = safe_db_operation(|| {
+                                        let tx = conn.transaction()?;
+                                        let result = tx.execute("INSERT INTO data (client_id, address,raw, opln)VALUES(?,?,?,?)", 
+                                                               params![client_id, backend.address, data, &buf_slice[1..4]]);
+                                        tx.commit()?;
+                                        result
+                                    });
+                                    
+                                    if let Err(e) = insert_result {
+                                        log(Error, format!("err at {}:{} ::{}", file!(), line!(), e));
+                                        // 尝试重新初始化数据库
+                                        match init_db_connections(tty_data.clone()) {
+                                            Ok(connections) => {
+                                                (backends, conn) = connections;
+                                            },
+                                            Err(e) => {
+                                                log(Error, format!("{}: Failed to reinitialize database connections: {}", now(), e));
+                                            }
+                                        }
+                                        continue;
+                                    }
+                                }
+                                
+                                // 删除过期数据
+                                {
+                                    let delete_result = safe_db_operation(|| {
+                                        let tx = conn.transaction()?;
+                                        let result = tx.execute("DELETE FROM data WHERE ts<(julianday('now')-?)", 
+                                                               params![crate::HOLD_INTERVAL]);
+                                        tx.commit()?;
+                                        result
+                                    });
+                                    
+                                    if let Err(e) = delete_result {
+                                        log(Error, format!("err at {}:{} ::{}", file!(), line!(), e));
+                                        // 尝试重新初始化数据库
+                                        match init_db_connections(tty_data.clone()) {
+                                            Ok(connections) => {
+                                                (backends, conn) = connections;
+                                            },
+                                            Err(e) => {
+                                                log(Error, format!("{}: Failed to reinitialize database connections: {}", now(), e));
+                                            }
+                                        }
+                                        continue;
+                                    }
+                                }
+                            },
+                            Err(e) => {
+                                log(Error, format!("{} {}:{} {}", now(), file!(), line!(), e));
+                                continue;
+                            },
+                        }
+                        
+                        // 计算操作耗时
+                        let operation_duration = operation_start.elapsed();
+                        let operation_duration_secs = operation_duration.as_secs();
+                        
+                        log(Debug, format!("Operation for backend {} took {} seconds", backend.id, operation_duration_secs));
+                    }
+                } else {
+                    // 没有需要立即执行的任务,找到最近需要执行的任务并等待
+                    if let Some(soonest_backend) = backends_list.first() {
+                        if soonest_backend.schedule_delay > 0 {
+                            // 等待到最近任务的执行时间
+                            let wait_time = std::cmp::min(soonest_backend.schedule_delay as u64, interval as u64);
+                            log(Debug, format!("{}: Waiting {} seconds for next scheduled task on uart {}", now(), wait_time, i));
+                            std::thread::sleep(Duration::from_secs(wait_time));
+                        }
+                    } else {
+                        // 没有任务,等待默认间隔
+                        std::thread::sleep(Duration::from_secs(5));
+                    }
+                }
+            }
+        });
+        jobs.push(job);
+    };
+    for job in jobs{
+        let _ = job.join();
+    }
+}

+ 50 - 0
src/uart/ttt.txt

@@ -0,0 +1,50 @@
+aw-ubi-spinand.ubootblks=24
+androidboot.dramsize=128
+*/
+
+ /*
+ device 2500800.uart
+state default
+type MUX_GROUP (2)
+controlling device 2000000.pinctrl
+group PD1
+function uart2
+
+device 2500800.uart
+state default
+type CONFIGS_GROUP (4)
+controlling device 2000000.pinctrl
+group PD1
+config 00000a09
+config 00000105
+
+device 2500800.uart
+state default
+type MUX_GROUP (2)
+controlling device 2000000.pinctrl
+group PD2
+function uart2
+
+device 2500800.uart
+state default
+type CONFIGS_GROUP (4)
+controlling device 2000000.pinctrl
+group PD2
+config 00000a09
+config 00000105
+
+device 2500800.uart
+state sleep
+type MUX_GROUP (2)
+controlling device 2000000.pinctrl
+group PD1
+function gpio_in
+
+device 2500800.uart
+state sleep
+type MUX_GROUP (2)
+controlling device 2000000.pinctrl
+group PD2
+function gpio_in
+
+*/