项目结构大调整
This commit is contained in:
parent
4d8c0e0bad
commit
2e1eecfdad
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
.env
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug",
|
||||||
|
"program": "${workspaceFolder}/<executable file>",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
"Neng",
|
"Neng",
|
||||||
"nextval",
|
"nextval",
|
||||||
"oneshot",
|
"oneshot",
|
||||||
|
"otype",
|
||||||
"plpgsql",
|
"plpgsql",
|
||||||
"prost",
|
"prost",
|
||||||
"proto",
|
"proto",
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
"protos",
|
"protos",
|
||||||
"repr",
|
"repr",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"rtsa",
|
||||||
"rtss",
|
"rtss",
|
||||||
"rumqtt",
|
"rumqtt",
|
||||||
"rumqttc",
|
"rumqttc",
|
||||||
|
|
|
@ -121,9 +121,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.90"
|
version = "1.0.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95"
|
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arraydeque"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ascii_utils"
|
name = "ascii_utils"
|
||||||
|
@ -133,9 +139,9 @@ checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-executor"
|
name = "async-executor"
|
||||||
version = "1.13.0"
|
version = "1.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
|
checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-task",
|
"async-task",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
|
@ -564,7 +570,7 @@ dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bevy_utils_proc_macros",
|
"bevy_utils_proc_macros",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
|
@ -619,9 +625,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.2"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -713,14 +719,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "config"
|
name = "config"
|
||||||
version = "0.14.0"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
|
checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"json5",
|
"json5",
|
||||||
"lazy_static",
|
|
||||||
"nom",
|
"nom",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"ron",
|
"ron",
|
||||||
|
@ -728,7 +733,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"toml",
|
"toml",
|
||||||
"yaml-rust",
|
"yaml-rust2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1076,9 +1081,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.0"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
|
@ -1246,12 +1251,6 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
|
@ -1263,13 +1262,22 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashlink"
|
name = "hashlink"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1491,7 +1499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1589,12 +1597,6 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linked-hash-map"
|
|
||||||
version = "0.5.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
|
@ -1623,7 +1625,22 @@ version = "0.12.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
|
checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "manager"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"config",
|
||||||
|
"enum_dispatch",
|
||||||
|
"rtss_api",
|
||||||
|
"rtss_db",
|
||||||
|
"rtss_log",
|
||||||
|
"serde",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1834,12 +1851,12 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-multimap"
|
name = "ordered-multimap"
|
||||||
version = "0.6.0"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
|
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dlv-list",
|
"dlv-list",
|
||||||
"hashbrown 0.13.2",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2392,15 +2409,12 @@ dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bevy_ecs",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rtss_db",
|
"rtss_db",
|
||||||
"rtss_dto",
|
"rtss_dto",
|
||||||
"rtss_log",
|
"rtss_log",
|
||||||
"rtss_sim_manage",
|
|
||||||
"rtss_trackside",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
|
@ -2408,22 +2422,12 @@ dependencies = [
|
||||||
"tower-http",
|
"tower-http",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_ci"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_common"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bevy_ecs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtss_db"
|
name = "rtss_db"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"lazy_static",
|
||||||
"rtss_dto",
|
"rtss_dto",
|
||||||
"rtss_log",
|
"rtss_log",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2439,14 +2443,11 @@ dependencies = [
|
||||||
"async-graphql",
|
"async-graphql",
|
||||||
"prost",
|
"prost",
|
||||||
"prost-build",
|
"prost-build",
|
||||||
|
"prost-types",
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_iscs"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtss_log"
|
name = "rtss_log"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -2463,55 +2464,12 @@ dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rtss_db",
|
|
||||||
"rtss_log",
|
"rtss_log",
|
||||||
"rumqttc",
|
"rumqttc",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_sim_manage"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bevy_app",
|
|
||||||
"bevy_core",
|
|
||||||
"bevy_ecs",
|
|
||||||
"bevy_time",
|
|
||||||
"rayon",
|
|
||||||
"rtss_common",
|
|
||||||
"rtss_log",
|
|
||||||
"rtss_trackside",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_simulation"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"clap",
|
|
||||||
"config",
|
|
||||||
"enum_dispatch",
|
|
||||||
"rtss_api",
|
|
||||||
"rtss_db",
|
|
||||||
"rtss_log",
|
|
||||||
"serde",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_trackside"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bevy_app",
|
|
||||||
"bevy_core",
|
|
||||||
"bevy_ecs",
|
|
||||||
"bevy_time",
|
|
||||||
"rtss_common",
|
|
||||||
"rtss_log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rumqttc"
|
name = "rumqttc"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
|
@ -2533,9 +2491,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-ini"
|
name = "rust-ini"
|
||||||
version = "0.19.0"
|
version = "0.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
|
checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"ordered-multimap",
|
"ordered-multimap",
|
||||||
|
@ -2686,18 +2644,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.210"
|
version = "1.0.213"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.210"
|
version = "1.0.213"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2706,9 +2664,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.131"
|
version = "1.0.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67d42a0bd4ac281beff598909bb56a86acaf979b84483e1c79c10dcaf98f8cf3"
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -2806,6 +2764,29 @@ dependencies = [
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simulation"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bevy_app",
|
||||||
|
"bevy_core",
|
||||||
|
"bevy_ecs",
|
||||||
|
"bevy_time",
|
||||||
|
"clap",
|
||||||
|
"config",
|
||||||
|
"enum_dispatch",
|
||||||
|
"lazy_static",
|
||||||
|
"rayon",
|
||||||
|
"rtss_db",
|
||||||
|
"rtss_dto",
|
||||||
|
"rtss_log",
|
||||||
|
"rtss_mqtt",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -2904,8 +2885,8 @@ dependencies = [
|
||||||
"futures-intrusive",
|
"futures-intrusive",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
"hashlink",
|
"hashlink 0.9.1",
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
|
@ -3127,9 +3108,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.76"
|
version = "2.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
|
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3180,18 +3161,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.64"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.64"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3265,9 +3246,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.40.0"
|
version = "1.41.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -3536,9 +3517,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typeid"
|
name = "typeid"
|
||||||
version = "1.0.0"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf"
|
checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
|
@ -4054,12 +4035,14 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust2"
|
||||||
version = "0.4.5"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map",
|
"arraydeque",
|
||||||
|
"encoding_rs",
|
||||||
|
"hashlink 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
32
Cargo.toml
32
Cargo.toml
|
@ -1,12 +1,7 @@
|
||||||
[package]
|
|
||||||
name = "rtss_simulation"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/*"]
|
members = ["crates/*", "manager/crates/*", "manager", "simulation"]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bevy_app = "0.14"
|
bevy_app = "0.14"
|
||||||
|
@ -14,29 +9,20 @@ bevy_core = "0.14"
|
||||||
bevy_ecs = "0.14"
|
bevy_ecs = "0.14"
|
||||||
bevy_time = "0.14"
|
bevy_time = "0.14"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.41.0", features = ["macros", "rt-multi-thread"] }
|
||||||
thiserror = "1.0.64"
|
thiserror = "1.0.65"
|
||||||
sqlx = { version = "0.8", features = [
|
sqlx = { version = "0.8", features = [
|
||||||
"runtime-tokio",
|
"runtime-tokio",
|
||||||
"postgres",
|
"postgres",
|
||||||
"json",
|
"json",
|
||||||
"chrono",
|
"chrono",
|
||||||
] }
|
] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0.213", features = ["derive"] }
|
||||||
serde_json = "1.0.131"
|
serde_json = "1.0.132"
|
||||||
anyhow = "1.0.90"
|
anyhow = "1.0.91"
|
||||||
async-trait = "0.1.83"
|
async-trait = "0.1.83"
|
||||||
bytes = "1.7.2"
|
bytes = "1.8.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
|
config = "0.14.1"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
tokio = { version = "1.39.3", features = ["macros", "rt-multi-thread"] }
|
|
||||||
rtss_log = { path = "crates/rtss_log" }
|
|
||||||
rtss_api = { path = "crates/rtss_api" }
|
|
||||||
rtss_db = { path = "crates/rtss_db" }
|
|
||||||
serde = { workspace = true }
|
|
||||||
config = "0.14.0"
|
|
||||||
clap = { version = "4.5.20", features = ["derive"] }
|
clap = { version = "4.5.20", features = ["derive"] }
|
||||||
enum_dispatch = "0.3"
|
enum_dispatch = "0.3"
|
||||||
anyhow = { workspace = true }
|
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
use async_graphql::{Context, InputObject, Object};
|
|
||||||
use rtss_sim_manage::{AvailablePlugins, SimulationBuilder};
|
|
||||||
|
|
||||||
use super::{MutexSimulationManager, SimulationOperation};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SimulationQuery;
|
|
||||||
|
|
||||||
#[Object]
|
|
||||||
impl SimulationQuery {
|
|
||||||
async fn simulations<'ctx>(&self, ctx: &Context<'ctx>) -> usize {
|
|
||||||
let sim = ctx.data::<MutexSimulationManager>().unwrap();
|
|
||||||
sim.lock().await.count()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SimulationMutation;
|
|
||||||
|
|
||||||
#[Object]
|
|
||||||
impl SimulationMutation {
|
|
||||||
async fn start_simulation<'ctx>(
|
|
||||||
&self,
|
|
||||||
ctx: &Context<'ctx>,
|
|
||||||
req: StartSimulationRequest,
|
|
||||||
) -> async_graphql::Result<String> {
|
|
||||||
// let claims = ctx.data::<Option<Claims>>().unwrap();
|
|
||||||
// match claims {
|
|
||||||
// Some(claims) => {
|
|
||||||
// info!("User {claims:?} started simulation");
|
|
||||||
// }
|
|
||||||
// _ => return Err("Unauthorized".into()),
|
|
||||||
// }
|
|
||||||
let sim = ctx.data::<MutexSimulationManager>().unwrap();
|
|
||||||
let id = sim.lock().await.start_simulation(
|
|
||||||
SimulationBuilder::default()
|
|
||||||
.id(req.user_id)
|
|
||||||
.plugins(vec![AvailablePlugins::TrackSideEquipmentPlugin]),
|
|
||||||
)?;
|
|
||||||
Ok(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn exit_simulation<'ctx>(
|
|
||||||
&self,
|
|
||||||
ctx: &Context<'ctx>,
|
|
||||||
id: String,
|
|
||||||
) -> async_graphql::Result<bool> {
|
|
||||||
let sim = ctx.data::<MutexSimulationManager>().unwrap();
|
|
||||||
sim.lock().await.exit_simulation(id)?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn pause_simulation<'ctx>(
|
|
||||||
&self,
|
|
||||||
ctx: &Context<'ctx>,
|
|
||||||
id: String,
|
|
||||||
) -> async_graphql::Result<bool> {
|
|
||||||
let sim = ctx.data::<MutexSimulationManager>().unwrap();
|
|
||||||
sim.lock().await.pause_simulation(id)?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn resume_simulation<'ctx>(
|
|
||||||
&self,
|
|
||||||
ctx: &Context<'ctx>,
|
|
||||||
id: String,
|
|
||||||
) -> async_graphql::Result<bool> {
|
|
||||||
let sim = ctx.data::<MutexSimulationManager>().unwrap();
|
|
||||||
sim.lock().await.resume_simulation(id)?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_simulation_speed<'ctx>(
|
|
||||||
&self,
|
|
||||||
ctx: &Context<'ctx>,
|
|
||||||
id: String,
|
|
||||||
speed: f32,
|
|
||||||
) -> async_graphql::Result<bool> {
|
|
||||||
let sim = ctx.data::<MutexSimulationManager>().unwrap();
|
|
||||||
sim.lock().await.update_simulation_speed(id, speed)?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn trigger_simulation_operation<'ctx>(
|
|
||||||
&self,
|
|
||||||
ctx: &Context<'ctx>,
|
|
||||||
id: String,
|
|
||||||
entity_uid: String,
|
|
||||||
operation: SimulationOperation,
|
|
||||||
) -> async_graphql::Result<bool> {
|
|
||||||
let sim = ctx.data::<MutexSimulationManager>().unwrap();
|
|
||||||
sim.lock().await.trigger_entity_operation(
|
|
||||||
id,
|
|
||||||
entity_uid,
|
|
||||||
operation.to_operation_event(),
|
|
||||||
)?;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(InputObject)]
|
|
||||||
struct StartSimulationRequest {
|
|
||||||
user_id: String,
|
|
||||||
sim_def_id: String,
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use async_graphql::Enum;
|
|
||||||
use bevy_ecs::event::Event;
|
|
||||||
use rtss_sim_manage::SimulationManager;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
pub struct MutexSimulationManager(Mutex<SimulationManager>);
|
|
||||||
impl Default for MutexSimulationManager {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(Mutex::new(SimulationManager::default()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Deref for MutexSimulationManager {
|
|
||||||
type Target = Mutex<SimulationManager>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Enum, Copy, Clone, Eq, PartialEq, Debug)]
|
|
||||||
pub enum SimulationOperation {
|
|
||||||
TurnoutControlDC,
|
|
||||||
TurnoutControlFC,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SimulationOperation {
|
|
||||||
pub fn to_operation_event(self) -> impl Event + Copy {
|
|
||||||
match self {
|
|
||||||
SimulationOperation::TurnoutControlDC => rtss_trackside::TurnoutControlEvent::DC,
|
|
||||||
SimulationOperation::TurnoutControlFC => rtss_trackside::TurnoutControlEvent::FC,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,6 +17,7 @@ sqlx = { workspace = true, features = [
|
||||||
"uuid",
|
"uuid",
|
||||||
] }
|
] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
lazy_static = { workspace = true }
|
||||||
|
|
||||||
rtss_dto = { path = "../rtss_dto" }
|
rtss_dto = { path = "../rtss_dto" }
|
||||||
rtss_log = { path = "../rtss_log" }
|
rtss_log = { path = "../rtss_log" }
|
||||||
|
|
|
@ -458,7 +458,7 @@ mod tests {
|
||||||
id: i + 1,
|
id: i + 1,
|
||||||
name: format!("user{}", i + 1),
|
name: format!("user{}", i + 1),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
|
|
|
@ -236,7 +236,7 @@ mod tests {
|
||||||
id: i + 1,
|
id: i + 1,
|
||||||
name: format!("user{}", i + 1),
|
name: format!("user{}", i + 1),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
|
@ -260,7 +260,7 @@ mod tests {
|
||||||
println!("create feature: {:?}", feature);
|
println!("create feature: {:?}", feature);
|
||||||
assert_eq!(feature.creator_id, creator_id);
|
assert_eq!(feature.creator_id, creator_id);
|
||||||
assert_eq!(feature.updater_id, creator_id);
|
assert_eq!(feature.updater_id, creator_id);
|
||||||
assert_eq!(feature.is_published, true);
|
assert!(feature.is_published);
|
||||||
assert_eq!(feature.config, create_config);
|
assert_eq!(feature.config, create_config);
|
||||||
let feature_id = feature.id;
|
let feature_id = feature.id;
|
||||||
// 获取功能特性测试
|
// 获取功能特性测试
|
||||||
|
@ -269,7 +269,7 @@ mod tests {
|
||||||
assert_eq!(feature.id, feature_id);
|
assert_eq!(feature.id, feature_id);
|
||||||
assert_eq!(feature.creator_id, creator_id);
|
assert_eq!(feature.creator_id, creator_id);
|
||||||
assert_eq!(feature.updater_id, creator_id);
|
assert_eq!(feature.updater_id, creator_id);
|
||||||
assert_eq!(feature.is_published, true);
|
assert!(feature.is_published);
|
||||||
assert_eq!(feature.config, create_config);
|
assert_eq!(feature.config, create_config);
|
||||||
// 更新测试
|
// 更新测试
|
||||||
let updater_id = 2;
|
let updater_id = 2;
|
||||||
|
@ -279,7 +279,7 @@ mod tests {
|
||||||
name: "test update".to_string(),
|
name: "test update".to_string(),
|
||||||
description: "test update".to_string(),
|
description: "test update".to_string(),
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
updater_id: updater_id,
|
updater_id,
|
||||||
};
|
};
|
||||||
let feature = accessor.update_feature(&feature).await?;
|
let feature = accessor.update_feature(&feature).await?;
|
||||||
println!("update feature: {:?}", feature);
|
println!("update feature: {:?}", feature);
|
||||||
|
@ -289,7 +289,7 @@ mod tests {
|
||||||
// 上下架测试
|
// 上下架测试
|
||||||
let feature = accessor.set_feature_published(feature_id, false).await?;
|
let feature = accessor.set_feature_published(feature_id, false).await?;
|
||||||
println!("set feature published: {:?}", feature);
|
println!("set feature published: {:?}", feature);
|
||||||
assert_eq!(feature.is_published, false);
|
assert!(!feature.is_published);
|
||||||
// 分页查询测试
|
// 分页查询测试
|
||||||
// 创建10个功能特性,5个发布的,5个未发布的
|
// 创建10个功能特性,5个发布的,5个未发布的
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
|
|
|
@ -1,14 +1,42 @@
|
||||||
mod draft_data;
|
mod draft_data;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
pub use draft_data::*;
|
pub use draft_data::*;
|
||||||
mod release_data;
|
mod release_data;
|
||||||
pub use release_data::*;
|
pub use release_data::*;
|
||||||
mod user;
|
mod user;
|
||||||
|
use rtss_log::tracing::error;
|
||||||
pub use user::*;
|
pub use user::*;
|
||||||
mod feature;
|
mod feature;
|
||||||
pub use feature::*;
|
pub use feature::*;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::{model::MqttClientIdSeq, DbAccessError};
|
use crate::{model::MqttClientIdSeq, DbAccessError};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref RDA: Mutex<Option<RtssDbAccessor>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 初始化全局默认数据库访问器
|
||||||
|
pub async fn init_default_db_accessor(url: &str) {
|
||||||
|
if RDA.lock().unwrap().is_some() {
|
||||||
|
error!("数据库访问器已初始化,请勿重复初始化");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let accessor = get_db_accessor(url).await;
|
||||||
|
let mut rda = RDA.lock().unwrap();
|
||||||
|
*rda = Some(accessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取默认数据库访问器
|
||||||
|
pub fn get_default_db_accessor() -> RtssDbAccessor {
|
||||||
|
let rda = RDA.lock().unwrap();
|
||||||
|
if rda.is_none() {
|
||||||
|
panic!("数据库访问器未初始化");
|
||||||
|
}
|
||||||
|
rda.as_ref().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RtssDbAccessor {
|
pub struct RtssDbAccessor {
|
||||||
pool: sqlx::PgPool,
|
pool: sqlx::PgPool,
|
||||||
|
|
|
@ -704,7 +704,7 @@ mod tests {
|
||||||
id: i + 1,
|
id: i + 1,
|
||||||
name: format!("user{}", i + 1),
|
name: format!("user{}", i + 1),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
|
@ -745,7 +745,7 @@ mod tests {
|
||||||
|
|
||||||
// 发布新版本
|
// 发布新版本
|
||||||
let (release_data, version1) = accessor
|
let (release_data, version1) = accessor
|
||||||
.release_new_from_draft(draft.id, name, &description, None)
|
.release_new_from_draft(draft.id, name, description, None)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(release_data.name, name);
|
assert_eq!(release_data.name, name);
|
||||||
// 检查使用版本是刚刚发布的版本
|
// 检查使用版本是刚刚发布的版本
|
||||||
|
@ -759,7 +759,7 @@ mod tests {
|
||||||
// 检查版本描述
|
// 检查版本描述
|
||||||
assert_eq!(version1.description, description);
|
assert_eq!(version1.description, description);
|
||||||
// 检查上架状态
|
// 检查上架状态
|
||||||
assert_eq!(release_data.is_published, true);
|
assert!(release_data.is_published);
|
||||||
// 检查草稿数据的默认发布数据id
|
// 检查草稿数据的默认发布数据id
|
||||||
let draft = accessor.query_draft_data_by_id(draft.id).await?;
|
let draft = accessor.query_draft_data_by_id(draft.id).await?;
|
||||||
assert_eq!(draft.default_release_data_id, Some(release_data.id));
|
assert_eq!(draft.default_release_data_id, Some(release_data.id));
|
||||||
|
@ -769,7 +769,7 @@ mod tests {
|
||||||
let exist = accessor
|
let exist = accessor
|
||||||
.is_release_data_name_exist(rtss_dto::common::DataType::Iscs, name)
|
.is_release_data_name_exist(rtss_dto::common::DataType::Iscs, name)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(exist, true);
|
assert!(exist);
|
||||||
|
|
||||||
// 修改草稿数据
|
// 修改草稿数据
|
||||||
let data = "test2".as_bytes();
|
let data = "test2".as_bytes();
|
||||||
|
@ -798,7 +798,7 @@ mod tests {
|
||||||
let release_data = accessor
|
let release_data = accessor
|
||||||
.set_release_data_published(release_data.id, false)
|
.set_release_data_published(release_data.id, false)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(release_data.is_published, false);
|
assert!(!release_data.is_published);
|
||||||
assert!(release_data.updated_at > release_data.created_at);
|
assert!(release_data.updated_at > release_data.created_at);
|
||||||
println!("更新发布数据上架状态测试成功");
|
println!("更新发布数据上架状态测试成功");
|
||||||
|
|
||||||
|
|
|
@ -300,7 +300,7 @@ mod tests {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "test1".to_string(),
|
name: "test1".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
|
@ -310,7 +310,7 @@ mod tests {
|
||||||
id: 2,
|
id: 2,
|
||||||
name: "test2".to_string(),
|
name: "test2".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::Admin]).unwrap(),
|
roles: serde_json::to_value(vec![Role::Admin]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
|
@ -342,7 +342,7 @@ mod tests {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "test1".to_string(),
|
name: "test1".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: Some("walker@163.com".to_string()),
|
email: Some("walker@163.com".to_string()),
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now() - Duration::from_secs(60),
|
created_at: Local::now() - Duration::from_secs(60),
|
||||||
|
@ -352,7 +352,7 @@ mod tests {
|
||||||
id: 2,
|
id: 2,
|
||||||
name: "test2".to_string(),
|
name: "test2".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::Admin, Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::Admin, Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: Some("123456789".to_string()),
|
mobile: Some("123456789".to_string()),
|
||||||
created_at: Local::now() - Duration::from_secs(60),
|
created_at: Local::now() - Duration::from_secs(60),
|
||||||
|
@ -362,7 +362,7 @@ mod tests {
|
||||||
id: 3,
|
id: 3,
|
||||||
name: "test3".to_string(),
|
name: "test3".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
prost = "0.13"
|
prost = "0.13"
|
||||||
|
prost-types = "0.13"
|
||||||
async-graphql = { version = "7.0.7", features = ["chrono"] }
|
async-graphql = { version = "7.0.7", features = ["chrono"] }
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|
|
@ -42,6 +42,7 @@ fn main() {
|
||||||
"../../rtss-proto-msg/src/em_data.proto",
|
"../../rtss-proto-msg/src/em_data.proto",
|
||||||
"../../rtss-proto-msg/src/common.proto",
|
"../../rtss-proto-msg/src/common.proto",
|
||||||
"../../rtss-proto-msg/src/iscs_graphic_data.proto",
|
"../../rtss-proto-msg/src/iscs_graphic_data.proto",
|
||||||
|
"../../rtss-proto-msg/src/simulation.proto",
|
||||||
],
|
],
|
||||||
&["../../rtss-proto-msg/src/"],
|
&["../../rtss-proto-msg/src/"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,27 @@
|
||||||
mod pb;
|
mod pb;
|
||||||
|
|
||||||
pub use pb::*;
|
pub use pb::*;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::simulation::{self, operation, SetSpeedParam};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prost_any() {
|
||||||
|
let op = simulation::Operation {
|
||||||
|
otype: simulation::OperationType::SetSpeed as i32,
|
||||||
|
param: Some(operation::Param::SetSpeedParam(SetSpeedParam {
|
||||||
|
speed: 1.0,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
println!("{:?}", op);
|
||||||
|
if let Some(param) = op.param {
|
||||||
|
match param {
|
||||||
|
operation::Param::SetSpeedParam(ssp) => {
|
||||||
|
assert_eq!(ssp.speed, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,11 +3,15 @@
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct IscsGraphicStorage {
|
pub struct IscsGraphicStorage {
|
||||||
#[prost(message, repeated, tag = "1")]
|
#[prost(message, repeated, tag = "1")]
|
||||||
pub cctv_of_station_control_storages: ::prost::alloc::vec::Vec<
|
pub cctv_of_equipment_layout_storages: ::prost::alloc::vec::Vec<
|
||||||
CctvOfStationControlStorage,
|
CctvOfEquipmentLayoutStorage,
|
||||||
>,
|
>,
|
||||||
#[prost(message, repeated, tag = "2")]
|
#[prost(message, repeated, tag = "2")]
|
||||||
pub fas_platform_alarm_storages: ::prost::alloc::vec::Vec<FasPlatformAlarmStorage>,
|
pub fas_of_platform_alarm_storages: ::prost::alloc::vec::Vec<
|
||||||
|
FasOfPlatformAlarmStorage,
|
||||||
|
>,
|
||||||
|
#[prost(message, repeated, tag = "3")]
|
||||||
|
pub bas_of_escalator_storages: ::prost::alloc::vec::Vec<BasOfEscalatorStorage>,
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
@ -24,6 +28,22 @@ pub struct UniqueIdOfStationLayout {
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct CommonGraphicStorage {
|
||||||
|
#[prost(message, repeated, tag = "1")]
|
||||||
|
pub arrows: ::prost::alloc::vec::Vec<Arrow>,
|
||||||
|
#[prost(message, repeated, tag = "2")]
|
||||||
|
pub texts: ::prost::alloc::vec::Vec<Text>,
|
||||||
|
#[prost(message, repeated, tag = "3")]
|
||||||
|
pub rects: ::prost::alloc::vec::Vec<Rect>,
|
||||||
|
#[prost(message, repeated, tag = "4")]
|
||||||
|
pub lines: ::prost::alloc::vec::Vec<Line>,
|
||||||
|
#[prost(message, repeated, tag = "5")]
|
||||||
|
pub circles: ::prost::alloc::vec::Vec<Circle>,
|
||||||
|
#[prost(message, repeated, tag = "6")]
|
||||||
|
pub buttons: ::prost::alloc::vec::Vec<Button>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct Arrow {
|
pub struct Arrow {
|
||||||
#[prost(message, optional, tag = "1")]
|
#[prost(message, optional, tag = "1")]
|
||||||
pub common: ::core::option::Option<super::common::CommonInfo>,
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
@ -32,10 +52,9 @@ pub struct Arrow {
|
||||||
#[prost(message, repeated, tag = "3")]
|
#[prost(message, repeated, tag = "3")]
|
||||||
pub points: ::prost::alloc::vec::Vec<super::common::Point>,
|
pub points: ::prost::alloc::vec::Vec<super::common::Point>,
|
||||||
}
|
}
|
||||||
/// Iscs文字
|
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct IscsText {
|
pub struct Text {
|
||||||
#[prost(message, optional, tag = "1")]
|
#[prost(message, optional, tag = "1")]
|
||||||
pub common: ::core::option::Option<super::common::CommonInfo>,
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
#[prost(string, tag = "2")]
|
#[prost(string, tag = "2")]
|
||||||
|
@ -72,20 +91,92 @@ pub struct Rect {
|
||||||
/// 画第一个点的坐标
|
/// 画第一个点的坐标
|
||||||
#[prost(message, optional, tag = "8")]
|
#[prost(message, optional, tag = "8")]
|
||||||
pub point: ::core::option::Option<super::common::Point>,
|
pub point: ::core::option::Option<super::common::Point>,
|
||||||
|
/// 填充色
|
||||||
|
#[prost(string, tag = "9")]
|
||||||
|
pub fill_color: ::prost::alloc::string::String,
|
||||||
|
/// 透明度
|
||||||
|
#[prost(float, tag = "10")]
|
||||||
|
pub alpha: f32,
|
||||||
}
|
}
|
||||||
/// CCTV按钮
|
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct CctvButton {
|
pub struct Line {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
/// 编号
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
/// 点列表
|
||||||
|
#[prost(message, repeated, tag = "3")]
|
||||||
|
pub points: ::prost::alloc::vec::Vec<super::common::Point>,
|
||||||
|
/// 是否曲线
|
||||||
|
#[prost(bool, tag = "4")]
|
||||||
|
pub is_curve: bool,
|
||||||
|
/// 曲线分段数
|
||||||
|
#[prost(int32, tag = "5")]
|
||||||
|
pub segments_count: i32,
|
||||||
|
/// 线宽
|
||||||
|
#[prost(int32, tag = "6")]
|
||||||
|
pub line_width: i32,
|
||||||
|
/// 线色
|
||||||
|
#[prost(string, tag = "7")]
|
||||||
|
pub line_color: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct Circle {
|
||||||
#[prost(message, optional, tag = "1")]
|
#[prost(message, optional, tag = "1")]
|
||||||
pub common: ::core::option::Option<super::common::CommonInfo>,
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
#[prost(string, tag = "2")]
|
#[prost(string, tag = "2")]
|
||||||
pub code: ::prost::alloc::string::String,
|
pub code: ::prost::alloc::string::String,
|
||||||
#[prost(enumeration = "cctv_button::ButtonType", tag = "3")]
|
#[prost(message, optional, tag = "3")]
|
||||||
pub button_type: i32,
|
pub position: ::core::option::Option<super::common::Point>,
|
||||||
|
#[prost(float, tag = "4")]
|
||||||
|
pub radius: f32,
|
||||||
|
#[prost(int32, tag = "5")]
|
||||||
|
pub line_width: i32,
|
||||||
|
#[prost(string, tag = "6")]
|
||||||
|
pub line_color: ::prost::alloc::string::String,
|
||||||
|
#[prost(string, tag = "7")]
|
||||||
|
pub fill_color: ::prost::alloc::string::String,
|
||||||
|
#[prost(float, tag = "8")]
|
||||||
|
pub alpha: f32,
|
||||||
}
|
}
|
||||||
/// Nested message and enum types in `CCTVButton`.
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
pub mod cctv_button {
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct Button {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
#[prost(string, tag = "3")]
|
||||||
|
pub code_color: ::prost::alloc::string::String,
|
||||||
|
#[prost(int32, tag = "4")]
|
||||||
|
pub code_font_size: i32,
|
||||||
|
/// 所属ISCS子菜单
|
||||||
|
#[prost(string, tag = "5")]
|
||||||
|
pub belong_sub_menu: ::prost::alloc::string::String,
|
||||||
|
/// 类似icon
|
||||||
|
#[prost(enumeration = "button::ButtonType", tag = "6")]
|
||||||
|
pub button_type: i32,
|
||||||
|
/// 宽度
|
||||||
|
#[prost(float, tag = "7")]
|
||||||
|
pub width: f32,
|
||||||
|
/// 高度
|
||||||
|
#[prost(float, tag = "8")]
|
||||||
|
pub height: f32,
|
||||||
|
/// 圆角半径_可变圆
|
||||||
|
#[prost(int32, tag = "9")]
|
||||||
|
pub radius: i32,
|
||||||
|
/// 填充色
|
||||||
|
#[prost(string, tag = "10")]
|
||||||
|
pub fill_color: ::prost::alloc::string::String,
|
||||||
|
/// 透明度
|
||||||
|
#[prost(float, tag = "11")]
|
||||||
|
pub alpha: f32,
|
||||||
|
}
|
||||||
|
/// Nested message and enum types in `Button`.
|
||||||
|
pub mod button {
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
Copy,
|
Copy,
|
||||||
|
@ -99,11 +190,14 @@ pub mod cctv_button {
|
||||||
)]
|
)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum ButtonType {
|
pub enum ButtonType {
|
||||||
Rect = 0,
|
/// 没有Icon
|
||||||
|
NoIcon = 0,
|
||||||
|
/// 监控方形
|
||||||
|
CctvRect = 1,
|
||||||
/// 监控样子的按钮
|
/// 监控样子的按钮
|
||||||
Monitor = 1,
|
CctvMonitor = 2,
|
||||||
/// 半圆样子的按钮
|
/// 半圆样子的按钮
|
||||||
Semicircle = 2,
|
CctvSemicircle = 3,
|
||||||
}
|
}
|
||||||
impl ButtonType {
|
impl ButtonType {
|
||||||
/// String value of the enum field names used in the ProtoBuf definition.
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
@ -112,17 +206,19 @@ pub mod cctv_button {
|
||||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||||
pub fn as_str_name(&self) -> &'static str {
|
pub fn as_str_name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ButtonType::Rect => "rect",
|
ButtonType::NoIcon => "noIcon",
|
||||||
ButtonType::Monitor => "monitor",
|
ButtonType::CctvRect => "cctvRect",
|
||||||
ButtonType::Semicircle => "semicircle",
|
ButtonType::CctvMonitor => "cctvMonitor",
|
||||||
|
ButtonType::CctvSemicircle => "cctvSemicircle",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||||
match value {
|
match value {
|
||||||
"rect" => Some(Self::Rect),
|
"noIcon" => Some(Self::NoIcon),
|
||||||
"monitor" => Some(Self::Monitor),
|
"cctvRect" => Some(Self::CctvRect),
|
||||||
"semicircle" => Some(Self::Semicircle),
|
"cctvMonitor" => Some(Self::CctvMonitor),
|
||||||
|
"cctvSemicircle" => Some(Self::CctvSemicircle),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,31 +271,392 @@ pub struct TemperatureDetector {
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct CctvOfStationControlStorage {
|
pub struct BasOfEscalatorStorage {
|
||||||
#[prost(string, tag = "1")]
|
#[prost(string, tag = "1")]
|
||||||
pub station_name: ::prost::alloc::string::String,
|
pub station_name: ::prost::alloc::string::String,
|
||||||
#[prost(message, optional, tag = "2")]
|
#[prost(message, optional, tag = "2")]
|
||||||
pub canvas: ::core::option::Option<super::common::Canvas>,
|
pub canvas: ::core::option::Option<super::common::Canvas>,
|
||||||
#[prost(message, repeated, tag = "3")]
|
#[prost(message, optional, tag = "3")]
|
||||||
pub arrows: ::prost::alloc::vec::Vec<Arrow>,
|
pub common_graphic_storage: ::core::option::Option<CommonGraphicStorage>,
|
||||||
#[prost(message, repeated, tag = "4")]
|
#[prost(message, repeated, tag = "4")]
|
||||||
pub iscs_texts: ::prost::alloc::vec::Vec<IscsText>,
|
pub escalators: ::prost::alloc::vec::Vec<Escalator>,
|
||||||
#[prost(message, repeated, tag = "5")]
|
#[prost(message, repeated, tag = "5")]
|
||||||
pub rects: ::prost::alloc::vec::Vec<Rect>,
|
pub vertical_elevators: ::prost::alloc::vec::Vec<VerticalElevator>,
|
||||||
#[prost(message, repeated, tag = "6")]
|
}
|
||||||
pub cctv_buttons: ::prost::alloc::vec::Vec<CctvButton>,
|
/// 自动扶梯
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct Escalator {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 垂直电梯
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct VerticalElevator {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct FasPlatformAlarmStorage {
|
pub struct CctvOfEquipmentLayoutStorage {
|
||||||
#[prost(string, tag = "1")]
|
#[prost(string, tag = "1")]
|
||||||
pub station_name: ::prost::alloc::string::String,
|
pub station_name: ::prost::alloc::string::String,
|
||||||
#[prost(message, optional, tag = "2")]
|
#[prost(message, optional, tag = "2")]
|
||||||
pub canvas: ::core::option::Option<super::common::Canvas>,
|
pub canvas: ::core::option::Option<super::common::Canvas>,
|
||||||
#[prost(message, repeated, tag = "3")]
|
#[prost(message, optional, tag = "3")]
|
||||||
pub arrows: ::prost::alloc::vec::Vec<Arrow>,
|
pub common_graphic_storage: ::core::option::Option<CommonGraphicStorage>,
|
||||||
#[prost(message, repeated, tag = "4")]
|
/// 分层
|
||||||
pub iscs_texts: ::prost::alloc::vec::Vec<IscsText>,
|
#[prost(enumeration = "cctv_of_equipment_layout_storage::LayerType", tag = "4")]
|
||||||
#[prost(message, repeated, tag = "5")]
|
pub layer: i32,
|
||||||
pub rects: ::prost::alloc::vec::Vec<Rect>,
|
}
|
||||||
|
/// Nested message and enum types in `CCTVOfEquipmentLayoutStorage`.
|
||||||
|
pub mod cctv_of_equipment_layout_storage {
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
::prost::Enumeration
|
||||||
|
)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum LayerType {
|
||||||
|
/// 站厅
|
||||||
|
StationHall = 0,
|
||||||
|
/// 站台
|
||||||
|
Platform = 1,
|
||||||
|
/// 云台
|
||||||
|
Ptz = 2,
|
||||||
|
}
|
||||||
|
impl LayerType {
|
||||||
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
///
|
||||||
|
/// The values are not transformed in any way and thus are considered stable
|
||||||
|
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||||
|
pub fn as_str_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
LayerType::StationHall => "StationHall",
|
||||||
|
LayerType::Platform => "platform",
|
||||||
|
LayerType::Ptz => "PTZ",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||||
|
match value {
|
||||||
|
"StationHall" => Some(Self::StationHall),
|
||||||
|
"platform" => Some(Self::Platform),
|
||||||
|
"PTZ" => Some(Self::Ptz),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FasOfPlatformAlarmStorage {
|
||||||
|
#[prost(string, tag = "1")]
|
||||||
|
pub station_name: ::prost::alloc::string::String,
|
||||||
|
#[prost(message, optional, tag = "2")]
|
||||||
|
pub canvas: ::core::option::Option<super::common::Canvas>,
|
||||||
|
#[prost(message, optional, tag = "3")]
|
||||||
|
pub common_graphic_storage: ::core::option::Option<CommonGraphicStorage>,
|
||||||
|
/// 分区
|
||||||
|
#[prost(string, tag = "4")]
|
||||||
|
pub partition: ::prost::alloc::string::String,
|
||||||
|
#[prost(message, repeated, tag = "5")]
|
||||||
|
pub fas_failure_control_hosts: ::prost::alloc::vec::Vec<FasFailureControlHost>,
|
||||||
|
#[prost(message, repeated, tag = "6")]
|
||||||
|
pub fas_alarms: ::prost::alloc::vec::Vec<FasAlarm>,
|
||||||
|
#[prost(message, repeated, tag = "7")]
|
||||||
|
pub manual_alarm_buttons: ::prost::alloc::vec::Vec<ManualAlarmButton>,
|
||||||
|
#[prost(message, repeated, tag = "8")]
|
||||||
|
pub hydrant_alarm_buttons: ::prost::alloc::vec::Vec<HydrantAlarmButton>,
|
||||||
|
#[prost(message, repeated, tag = "9")]
|
||||||
|
pub gas_extinguishings: ::prost::alloc::vec::Vec<GasExtinguishing>,
|
||||||
|
#[prost(message, repeated, tag = "10")]
|
||||||
|
pub smoke_detectors: ::prost::alloc::vec::Vec<SmokeDetector>,
|
||||||
|
#[prost(message, repeated, tag = "11")]
|
||||||
|
pub temperature_detectors: ::prost::alloc::vec::Vec<TemperatureDetector>,
|
||||||
|
#[prost(message, repeated, tag = "12")]
|
||||||
|
pub fire_shutters: ::prost::alloc::vec::Vec<FireShutter>,
|
||||||
|
#[prost(message, repeated, tag = "13")]
|
||||||
|
pub fire_pumps: ::prost::alloc::vec::Vec<FirePump>,
|
||||||
|
#[prost(message, repeated, tag = "14")]
|
||||||
|
pub spray_pumps: ::prost::alloc::vec::Vec<SprayPump>,
|
||||||
|
#[prost(message, repeated, tag = "15")]
|
||||||
|
pub stabilized_pressure_pumps: ::prost::alloc::vec::Vec<StabilizedPressurePump>,
|
||||||
|
#[prost(message, repeated, tag = "16")]
|
||||||
|
pub acs: ::prost::alloc::vec::Vec<Acs>,
|
||||||
|
#[prost(message, repeated, tag = "17")]
|
||||||
|
pub afc: ::prost::alloc::vec::Vec<Afc>,
|
||||||
|
#[prost(message, repeated, tag = "18")]
|
||||||
|
pub non_fire_power_supplies: ::prost::alloc::vec::Vec<NonFirePowerSupply>,
|
||||||
|
#[prost(message, repeated, tag = "19")]
|
||||||
|
pub water_flow_indicators: ::prost::alloc::vec::Vec<WaterFlowIndicator>,
|
||||||
|
#[prost(message, repeated, tag = "20")]
|
||||||
|
pub signal_butterfly_valves: ::prost::alloc::vec::Vec<SignalButterflyValve>,
|
||||||
|
#[prost(message, repeated, tag = "21")]
|
||||||
|
pub pressure_switches: ::prost::alloc::vec::Vec<PressureSwitch>,
|
||||||
|
#[prost(message, repeated, tag = "22")]
|
||||||
|
pub fault_valves: ::prost::alloc::vec::Vec<FaultValve>,
|
||||||
|
#[prost(message, repeated, tag = "23")]
|
||||||
|
pub start_pump_buttons: ::prost::alloc::vec::Vec<StartPumpButton>,
|
||||||
|
#[prost(message, repeated, tag = "24")]
|
||||||
|
pub temperature_cables: ::prost::alloc::vec::Vec<TemperatureCable>,
|
||||||
|
#[prost(message, repeated, tag = "25")]
|
||||||
|
pub emergency_lightings: ::prost::alloc::vec::Vec<EmergencyLighting>,
|
||||||
|
#[prost(message, repeated, tag = "26")]
|
||||||
|
pub elevator_lift_to_tops: ::prost::alloc::vec::Vec<ElevatorLiftToTop>,
|
||||||
|
#[prost(message, repeated, tag = "27")]
|
||||||
|
pub electric_butterfly_valves: ::prost::alloc::vec::Vec<ElectricButterflyValve>,
|
||||||
|
#[prost(message, repeated, tag = "28")]
|
||||||
|
pub fire_valves: ::prost::alloc::vec::Vec<FireValve>,
|
||||||
|
#[prost(message, repeated, tag = "29")]
|
||||||
|
pub electric_fire_extinguishing_valves: ::prost::alloc::vec::Vec<
|
||||||
|
ElectricFireExtinguishingValve,
|
||||||
|
>,
|
||||||
|
#[prost(message, repeated, tag = "30")]
|
||||||
|
pub fire_intercommunication_signals: ::prost::alloc::vec::Vec<
|
||||||
|
FireIntercommunicationSignal,
|
||||||
|
>,
|
||||||
|
}
|
||||||
|
/// 火灾故障控制主机
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FasFailureControlHost {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 警铃
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FasAlarm {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 防火卷帘
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FireShutter {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
#[prost(enumeration = "fire_shutter::ShutterType", tag = "3")]
|
||||||
|
pub r#type: i32,
|
||||||
|
}
|
||||||
|
/// Nested message and enum types in `FireShutter`.
|
||||||
|
pub mod fire_shutter {
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
::prost::Enumeration
|
||||||
|
)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum ShutterType {
|
||||||
|
/// 隔断型
|
||||||
|
Partition = 0,
|
||||||
|
/// 疏散型
|
||||||
|
Dispersal = 1,
|
||||||
|
}
|
||||||
|
impl ShutterType {
|
||||||
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
///
|
||||||
|
/// The values are not transformed in any way and thus are considered stable
|
||||||
|
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||||
|
pub fn as_str_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
ShutterType::Partition => "partition",
|
||||||
|
ShutterType::Dispersal => "dispersal",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||||
|
match value {
|
||||||
|
"partition" => Some(Self::Partition),
|
||||||
|
"dispersal" => Some(Self::Dispersal),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 消防泵
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FirePump {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 喷淋泵
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SprayPump {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 稳压泵
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct StabilizedPressurePump {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// ACS
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct Acs {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// AFC
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct Afc {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 非消防电源
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct NonFirePowerSupply {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 水流指示器
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct WaterFlowIndicator {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 信号蝶阀
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SignalButterflyValve {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 压力开关
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct PressureSwitch {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 故障阀
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FaultValve {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 启泵按钮
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct StartPumpButton {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 感温电缆
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct TemperatureCable {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 应急照明
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct EmergencyLighting {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 电梯归首
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct ElevatorLiftToTop {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 电动蝶阀
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct ElectricButterflyValve {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 防火阀
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FireValve {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 电动防烟防火阀
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct ElectricFireExtinguishingValve {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
/// 火灾互联互通信号
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct FireIntercommunicationSignal {
|
||||||
|
#[prost(message, optional, tag = "1")]
|
||||||
|
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub code: ::prost::alloc::string::String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod em_data;
|
pub mod em_data;
|
||||||
|
pub mod simulation;
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
// This file is @generated by prost-build.
|
||||||
|
/// 仿真操作
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||||
|
pub struct Operation {
|
||||||
|
/// 操作类型
|
||||||
|
#[prost(enumeration = "OperationType", tag = "1")]
|
||||||
|
pub otype: i32,
|
||||||
|
/// 操作参数
|
||||||
|
#[prost(oneof = "operation::Param", tags = "2")]
|
||||||
|
pub param: ::core::option::Option<operation::Param>,
|
||||||
|
}
|
||||||
|
/// Nested message and enum types in `Operation`.
|
||||||
|
pub mod operation {
|
||||||
|
/// 操作参数
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, ::prost::Oneof)]
|
||||||
|
pub enum Param {
|
||||||
|
/// 设置仿真运行速度参数
|
||||||
|
#[prost(message, tag = "2")]
|
||||||
|
SetSpeedParam(super::SetSpeedParam),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 设置仿真运行速度参数
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||||
|
pub struct SetSpeedParam {
|
||||||
|
/// 运行速度
|
||||||
|
#[prost(float, tag = "1")]
|
||||||
|
pub speed: f32,
|
||||||
|
}
|
||||||
|
/// 仿真操作类型
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum OperationType {
|
||||||
|
/// 未知
|
||||||
|
Unknown = 0,
|
||||||
|
/// -------仿真控制操作--------
|
||||||
|
/// 暂停
|
||||||
|
Pause = 1,
|
||||||
|
/// 恢复运行
|
||||||
|
Unpause = 2,
|
||||||
|
/// 重置
|
||||||
|
Reset = 3,
|
||||||
|
/// 设置运行速度
|
||||||
|
SetSpeed = 4,
|
||||||
|
/// 销毁
|
||||||
|
Destroy = 5,
|
||||||
|
}
|
||||||
|
impl OperationType {
|
||||||
|
/// String value of the enum field names used in the ProtoBuf definition.
|
||||||
|
///
|
||||||
|
/// The values are not transformed in any way and thus are considered stable
|
||||||
|
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||||
|
pub fn as_str_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
OperationType::Unknown => "Unknown",
|
||||||
|
OperationType::Pause => "Pause",
|
||||||
|
OperationType::Unpause => "Unpause",
|
||||||
|
OperationType::Reset => "Reset",
|
||||||
|
OperationType::SetSpeed => "SetSpeed",
|
||||||
|
OperationType::Destroy => "Destroy",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||||
|
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||||
|
match value {
|
||||||
|
"Unknown" => Some(Self::Unknown),
|
||||||
|
"Pause" => Some(Self::Pause),
|
||||||
|
"Unpause" => Some(Self::Unpause),
|
||||||
|
"Reset" => Some(Self::Reset),
|
||||||
|
"SetSpeed" => Some(Self::SetSpeed),
|
||||||
|
"Destroy" => Some(Self::Destroy),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,11 +5,10 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rumqttc = { version = "0.24.0", features = ["url"] }
|
rumqttc = { version = "0.24.0", features = ["url"] }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true, features = ["sync"] }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
bytes = { workspace = true }
|
bytes = { workspace = true }
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|
||||||
rtss_db = { path = "../rtss_db" }
|
|
||||||
rtss_log = { path = "../rtss_log" }
|
rtss_log = { path = "../rtss_log" }
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use core::panic;
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -11,7 +12,7 @@ use std::{
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rtss_log::tracing::{debug, error, info};
|
use rtss_log::tracing::{debug, error, info, trace};
|
||||||
use rumqttc::{
|
use rumqttc::{
|
||||||
v5::{
|
v5::{
|
||||||
mqttbytes::{
|
mqttbytes::{
|
||||||
|
@ -29,9 +30,19 @@ use error::MqttClientError;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// 全局静态MqttClient实例
|
/// 全局静态MqttClient实例
|
||||||
|
/// 使用注意事项:
|
||||||
|
/// 每次订阅/发布/请求时都通过get_global_mqtt_client获取新的实例,否则可能会出现死锁
|
||||||
static ref MQTT_CLIENT: tokio::sync::Mutex<Option<MqttClient>> = tokio::sync::Mutex::new(None);
|
static ref MQTT_CLIENT: tokio::sync::Mutex<Option<MqttClient>> = tokio::sync::Mutex::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 初始化全局MqttClient实例
|
||||||
|
pub async fn init_global_mqtt_client(
|
||||||
|
mut options: MqttClientOptions,
|
||||||
|
) -> Result<(), MqttClientError> {
|
||||||
|
let client = options.build();
|
||||||
|
set_global_mqtt_client(client).await
|
||||||
|
}
|
||||||
|
|
||||||
/// 设置全局MqttClient实例
|
/// 设置全局MqttClient实例
|
||||||
pub async fn set_global_mqtt_client(client: MqttClient) -> Result<(), MqttClientError> {
|
pub async fn set_global_mqtt_client(client: MqttClient) -> Result<(), MqttClientError> {
|
||||||
let mut mqtt_client = MQTT_CLIENT.lock().await;
|
let mut mqtt_client = MQTT_CLIENT.lock().await;
|
||||||
|
@ -42,14 +53,20 @@ pub async fn set_global_mqtt_client(client: MqttClient) -> Result<(), MqttClient
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_global_mqtt_client() -> Option<MqttClient> {
|
/// 获取全局MqttClient实例
|
||||||
|
pub async fn get_global_mqtt_client() -> MqttClient {
|
||||||
let mqtt_client = MQTT_CLIENT.lock().await;
|
let mqtt_client = MQTT_CLIENT.lock().await;
|
||||||
mqtt_client.clone()
|
if let Some(client) = mqtt_client.as_ref() {
|
||||||
|
return client.clone();
|
||||||
|
}
|
||||||
|
panic!("MqttClient未初始化: 使用init_global_mqtt_client初始化,或者在main函数中调用set_global_mqtt_client设置");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MqttClientOptions {
|
pub struct MqttClientOptions {
|
||||||
id: String,
|
id: String,
|
||||||
options: MqttOptions,
|
options: MqttOptions,
|
||||||
|
/// mqtt客户端请求队列的最大容量
|
||||||
|
max_cap: usize,
|
||||||
request_timeout: Duration,
|
request_timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,37 +76,40 @@ impl MqttClientOptions {
|
||||||
id: id.to_string(),
|
id: id.to_string(),
|
||||||
options: MqttOptions::parse_url(format!("{}?client_id={}", url, id))
|
options: MqttOptions::parse_url(format!("{}?client_id={}", url, id))
|
||||||
.expect("解析mqtt url失败"),
|
.expect("解析mqtt url失败"),
|
||||||
|
max_cap: 30,
|
||||||
request_timeout: Duration::from_secs(5),
|
request_timeout: Duration::from_secs(5),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_request_timeout(&mut self, timeout: Duration) -> &mut Self {
|
pub fn set_max_cap(mut self, max_cap: usize) -> Self {
|
||||||
|
self.max_cap = max_cap;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_request_timeout(mut self, timeout: Duration) -> Self {
|
||||||
self.request_timeout = timeout;
|
self.request_timeout = timeout;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_credentials(&mut self, username: &str, password: &str) -> &mut Self {
|
pub fn set_credentials(mut self, username: &str, password: &str) -> Self {
|
||||||
self.options.set_credentials(username, password);
|
self.options.set_credentials(username, password);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(&mut self) -> MqttClient {
|
pub fn build(&mut self) -> MqttClient {
|
||||||
self.options.set_keep_alive(Duration::from_secs(10));
|
self.options.set_keep_alive(Duration::from_secs(10));
|
||||||
let (client, eventloop) = AsyncClient::new(self.options.clone(), 10);
|
let (client, eventloop) = AsyncClient::new(self.options.clone(), self.max_cap);
|
||||||
|
|
||||||
let subscriptions = SubscribeHandlerMap::new();
|
let subscriptions = SubscribeHandlerMap::new();
|
||||||
let loop_sub = subscriptions.clone();
|
let cli = MqttClient {
|
||||||
tokio::spawn(async move {
|
|
||||||
MqttClient::handle_connection_loop(eventloop, loop_sub).await;
|
|
||||||
});
|
|
||||||
|
|
||||||
MqttClient {
|
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
request_timeout: self.request_timeout,
|
request_timeout: self.request_timeout,
|
||||||
client,
|
client,
|
||||||
request_id: Arc::new(AtomicU64::new(0)),
|
request_id: Arc::new(AtomicU64::new(0)),
|
||||||
subscriptions,
|
subscriptions,
|
||||||
}
|
};
|
||||||
|
cli.run_async(eventloop);
|
||||||
|
cli
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +124,7 @@ impl MqttClientOptions {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MqttClient {
|
pub struct MqttClient {
|
||||||
id: String,
|
id: String,
|
||||||
|
/// 全局的请求超时时间
|
||||||
request_timeout: Duration,
|
request_timeout: Duration,
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
request_id: Arc<AtomicU64>,
|
request_id: Arc<AtomicU64>,
|
||||||
|
@ -154,11 +175,11 @@ impl SubscribeHandlerMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_handlers(&self, topic: &str) -> Option<Vec<Arc<dyn MessageHandler>>> {
|
fn get_handlers(&self, topic: &str) -> Option<Vec<Arc<dyn MessageHandler>>> {
|
||||||
if let Some(topic_handlers) = self.sub_handlers.lock().unwrap().get(topic) {
|
self.sub_handlers
|
||||||
Some(topic_handlers.values())
|
.lock()
|
||||||
} else {
|
.unwrap()
|
||||||
None
|
.get(topic)
|
||||||
}
|
.map(|handlers| handlers.values())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -261,7 +282,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MqttClient {
|
impl MqttClient {
|
||||||
pub async fn close(&self) -> Result<(), MqttClientError> {
|
pub async fn clear(&self) -> Result<(), MqttClientError> {
|
||||||
self.client.disconnect().await?;
|
self.client.disconnect().await?;
|
||||||
// 清空订阅处理器
|
// 清空订阅处理器
|
||||||
self.subscriptions.clear();
|
self.subscriptions.clear();
|
||||||
|
@ -319,6 +340,7 @@ impl MqttClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 发送请求并等待响应
|
/// 发送请求并等待响应
|
||||||
|
/// TODO: 需要测试中请求时的并发情况(多个请求同时等待,以及在请求时订阅和发布是否受影响)
|
||||||
pub async fn request(
|
pub async fn request(
|
||||||
&self,
|
&self,
|
||||||
topic: &str,
|
topic: &str,
|
||||||
|
@ -335,7 +357,7 @@ impl MqttClient {
|
||||||
self.register_topic_handler(&response_topic, response_future.clone());
|
self.register_topic_handler(&response_topic, response_future.clone());
|
||||||
// 发布请求
|
// 发布请求
|
||||||
let property = PublishProperties {
|
let property = PublishProperties {
|
||||||
response_topic: Some(response_topic.clone().into()),
|
response_topic: Some(response_topic.clone()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
self.client
|
self.client
|
||||||
|
@ -349,14 +371,48 @@ impl MqttClient {
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_connection_loop(mut eventloop: EventLoop, subscriptions: SubscribeHandlerMap) {
|
/// 发送请求并等待响应,指定响应超时时间
|
||||||
|
/// 响应超时时间为0时表示永不超时
|
||||||
|
pub async fn request_with_timeout(
|
||||||
|
&self,
|
||||||
|
topic: &str,
|
||||||
|
qos: QoS,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
timeout: Duration,
|
||||||
|
) -> Result<MqttResponse, MqttClientError> {
|
||||||
|
let response_topic = format!("{}/{}/resp/{}", self.id, topic, self.next_request_id());
|
||||||
|
self.subscribe(&response_topic, QoS::ExactlyOnce).await?;
|
||||||
|
let response_future = MqttResponseFuture::new(&response_topic, timeout);
|
||||||
|
let response_handler =
|
||||||
|
self.register_topic_handler(&response_topic, response_future.clone());
|
||||||
|
let property = PublishProperties {
|
||||||
|
response_topic: Some(response_topic.clone()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
self.client
|
||||||
|
.publish_with_properties(topic, qos, false, payload, property)
|
||||||
|
.await?;
|
||||||
|
let resp = response_future.await;
|
||||||
|
response_handler.unregister();
|
||||||
|
self.unsubscribe(&response_topic).await?;
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_async(&self, eventloop: EventLoop) {
|
||||||
|
let cli = self.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
cli.run(eventloop).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&self, mut eventloop: EventLoop) {
|
||||||
while let Ok(notification) = eventloop.poll().await {
|
while let Ok(notification) = eventloop.poll().await {
|
||||||
match notification {
|
match notification {
|
||||||
Event::Incoming(Packet::Publish(publish)) => {
|
Event::Incoming(Packet::Publish(publish)) => {
|
||||||
debug!("Received message: {:?}", publish);
|
trace!("Received message: {:?}", publish);
|
||||||
let topic: String = String::from_utf8_lossy(&publish.topic).to_string();
|
let topic: String = String::from_utf8_lossy(&publish.topic).to_string();
|
||||||
|
|
||||||
if let Some(topic_handlers) = subscriptions.get_handlers(&topic) {
|
if let Some(topic_handlers) = self.subscriptions.get_handlers(&topic) {
|
||||||
for handler in topic_handlers {
|
for handler in topic_handlers {
|
||||||
let handler = handler.clone();
|
let handler = handler.clone();
|
||||||
let p = publish.clone();
|
let p = publish.clone();
|
||||||
|
@ -375,7 +431,7 @@ impl MqttClient {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Unhandled event: {:?}", notification);
|
trace!("Unhandled event: {:?}", notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,6 +452,12 @@ pub struct MqttResponse {
|
||||||
response: Arc<Mutex<Bytes>>,
|
response: Arc<Mutex<Bytes>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for MqttResponse {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MqttResponse {
|
impl MqttResponse {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -460,12 +522,15 @@ impl MqttResponseFuture {
|
||||||
|
|
||||||
/// 启动超时监控任务逻辑
|
/// 启动超时监控任务逻辑
|
||||||
fn start_timeout_monitor(&self, rx: oneshot::Receiver<()>) {
|
fn start_timeout_monitor(&self, rx: oneshot::Receiver<()>) {
|
||||||
|
if self.timeout.as_millis() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let response = self.response.clone();
|
let response = self.response.clone();
|
||||||
let response_topic = self.response_topic.clone();
|
let response_topic = self.response_topic.clone();
|
||||||
let duration = self.timeout.clone();
|
let duration = self.timeout;
|
||||||
let waker = self.waker.clone();
|
let waker = self.waker.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(_) = timeout(duration, rx).await {
|
if (timeout(duration, rx).await).is_err() {
|
||||||
error!("Mqtt response timeout: {:?}", response_topic);
|
error!("Mqtt response timeout: {:?}", response_topic);
|
||||||
response.set_timeout();
|
response.set_timeout();
|
||||||
if let Some(waker) = waker.lock().unwrap().take() {
|
if let Some(waker) = waker.lock().unwrap().take() {
|
||||||
|
@ -500,18 +565,24 @@ impl std::future::Future for MqttResponseFuture {
|
||||||
cx: &mut std::task::Context<'_>,
|
cx: &mut std::task::Context<'_>,
|
||||||
) -> std::task::Poll<Self::Output> {
|
) -> std::task::Poll<Self::Output> {
|
||||||
if self.response.is_waiting() {
|
if self.response.is_waiting() {
|
||||||
debug!("Response future poll waiting...");
|
debug!(
|
||||||
|
"topic={} Response future poll waiting...",
|
||||||
|
self.response_topic
|
||||||
|
);
|
||||||
self.waker.lock().unwrap().replace(cx.waker().clone());
|
self.waker.lock().unwrap().replace(cx.waker().clone());
|
||||||
std::task::Poll::Pending
|
std::task::Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
debug!("Response future poll ready: {:?}", self.response.get());
|
debug!(
|
||||||
|
"topic={} Response future poll ready: {:?}",
|
||||||
|
self.response_topic, self.response
|
||||||
|
);
|
||||||
std::task::Poll::Ready(self.response.clone())
|
std::task::Poll::Ready(self.response.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_publish_response_topic(publish: Option<PublishProperties>) -> Option<String> {
|
pub fn get_publish_response_topic(publish: Option<PublishProperties>) -> Option<String> {
|
||||||
publish.map(|p| p.response_topic.clone()).flatten()
|
publish.and_then(|p| p.response_topic.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -519,14 +590,20 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use rtss_log::tracing::{info, Level};
|
use rtss_log::tracing::{info, Level};
|
||||||
|
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::{
|
||||||
|
sync::broadcast,
|
||||||
|
time::{sleep, Duration},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn create_mqtt_options() -> MqttClientOptions {
|
||||||
|
MqttClientOptions::new("rtss_test1", "tcp://localhost:1883")
|
||||||
|
.set_credentials("rtsa", "Joylink@0503")
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_subscribe_and_publish() {
|
async fn test_subscribe_and_publish() {
|
||||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
rtss_log::Logging::default().with_level(Level::TRACE).init();
|
||||||
let client = MqttClientOptions::new("rtss_test1", "tcp://localhost:1883")
|
let client = create_mqtt_options().build();
|
||||||
.set_credentials("rtss_simulation", "Joylink@0503")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
client
|
client
|
||||||
.subscribe("test/topic", QoS::AtMostOnce)
|
.subscribe("test/topic", QoS::AtMostOnce)
|
||||||
|
@ -539,7 +616,7 @@ mod tests {
|
||||||
String::from_utf8_lossy(&publish.payload)
|
String::from_utf8_lossy(&publish.payload)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
let h2 = client.register_topic_handler("test/topic", |publish: Publish| {
|
let _ = client.register_topic_handler("test/topic", |publish: Publish| {
|
||||||
info!(
|
info!(
|
||||||
"Handler 2 received: topic={}, payload={:?}",
|
"Handler 2 received: topic={}, payload={:?}",
|
||||||
String::from_utf8_lossy(&publish.topic),
|
String::from_utf8_lossy(&publish.topic),
|
||||||
|
@ -561,52 +638,65 @@ mod tests {
|
||||||
handler1.unregister();
|
handler1.unregister();
|
||||||
assert_eq!(client.topic_handler_count("test/topic"), 1);
|
assert_eq!(client.topic_handler_count("test/topic"), 1);
|
||||||
|
|
||||||
h2.unregister();
|
sleep(Duration::from_millis(200)).await;
|
||||||
assert_eq!(client.topic_handler_count("test/topic"), 0);
|
// Test clear
|
||||||
|
client.clear().await.unwrap();
|
||||||
|
|
||||||
// Test unsubscribe
|
assert_eq!(client.topic_handler_count("test/topic"), 0);
|
||||||
client.close().await.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_request() {
|
async fn test_request() {
|
||||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
||||||
let client = MqttClientOptions::new("rtss_test1", "tcp://localhost:1883")
|
init_global_mqtt_client(create_mqtt_options())
|
||||||
.set_credentials("rtss_simulation", "Joylink@0503")
|
.await
|
||||||
.build();
|
.unwrap();
|
||||||
set_global_mqtt_client(client.clone()).await.unwrap();
|
|
||||||
|
|
||||||
if let Some(c) = get_global_mqtt_client().await {
|
let c = get_global_mqtt_client().await;
|
||||||
c.subscribe("test/request", QoS::AtMostOnce).await.unwrap();
|
c.subscribe("test/request", QoS::AtMostOnce).await.unwrap();
|
||||||
let handler = |p: Publish| {
|
let handler = |p: Publish| {
|
||||||
info!(
|
info!(
|
||||||
"Request handler received: topic={}, payload={:?}",
|
"Request handler received: topic={}, payload={:?}",
|
||||||
String::from_utf8_lossy(&p.topic),
|
String::from_utf8_lossy(&p.topic),
|
||||||
String::from_utf8_lossy(&p.payload)
|
String::from_utf8_lossy(&p.payload)
|
||||||
);
|
);
|
||||||
let response = Bytes::from("Hello, response!");
|
let response = Bytes::from("Hello, response!");
|
||||||
let resp_topic = get_publish_response_topic(p.properties.clone());
|
let resp_topic = get_publish_response_topic(p.properties.clone());
|
||||||
if let Some(r_topic) = resp_topic {
|
if let Some(r_topic) = resp_topic {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Some(c) = get_global_mqtt_client().await {
|
// 此处需要使用全局MqttClient实例,否则会出现死锁
|
||||||
c.publish(&r_topic, QoS::AtMostOnce, response.to_vec())
|
let c = get_global_mqtt_client().await;
|
||||||
.await
|
c.publish(&r_topic, QoS::AtMostOnce, response.to_vec())
|
||||||
.unwrap();
|
.await
|
||||||
}
|
.unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let _ = c.register_topic_handler("test/request", handler);
|
let _ = c.register_topic_handler("test/request", handler);
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(c) = get_global_mqtt_client().await {
|
let response = c
|
||||||
let response = c
|
.request("test/request", QoS::AtMostOnce, b"Hello, request!".to_vec())
|
||||||
.request("test/request", QoS::AtMostOnce, b"Hello, request!".to_vec())
|
.await
|
||||||
.await
|
.unwrap();
|
||||||
.unwrap();
|
info!("Request response: {:?}", response);
|
||||||
info!("Request response: {:?}", response);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
client.close().await.unwrap();
|
#[tokio::test]
|
||||||
|
async fn test_async_broadcast() {
|
||||||
|
let (tx, mut rx1) = broadcast::channel(16);
|
||||||
|
let mut rx2 = tx.subscribe();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
assert_eq!(rx1.recv().await.unwrap(), 10);
|
||||||
|
assert_eq!(rx1.recv().await.unwrap(), 20);
|
||||||
|
});
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
assert_eq!(rx2.recv().await.unwrap(), 10);
|
||||||
|
assert_eq!(rx2.recv().await.unwrap(), 20);
|
||||||
|
});
|
||||||
|
|
||||||
|
tx.send(10).unwrap();
|
||||||
|
tx.send(20).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
[package]
|
||||||
|
name = "manager"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
|
||||||
|
rtss_log = { path = "../crates/rtss_log" }
|
||||||
|
rtss_api = { path = "crates/rtss_api" }
|
||||||
|
rtss_db = { path = "../crates/rtss_db" }
|
||||||
|
serde = { workspace = true }
|
||||||
|
config = { workspace = true }
|
||||||
|
clap = { workspace = true, features = ["derive"] }
|
||||||
|
enum_dispatch = { workspace = true }
|
||||||
|
anyhow = { workspace = true }
|
|
@ -19,9 +19,6 @@ base64 = "0.22.1"
|
||||||
sysinfo = "0.31.3"
|
sysinfo = "0.31.3"
|
||||||
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls", "json"] }
|
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls", "json"] }
|
||||||
|
|
||||||
bevy_ecs = { workspace = true }
|
rtss_log = { path = "../../../crates/rtss_log" }
|
||||||
rtss_log = { path = "../rtss_log" }
|
rtss_db = { path = "../../../crates/rtss_db" }
|
||||||
rtss_sim_manage = { path = "../rtss_sim_manage" }
|
rtss_dto = { path = "../../../crates/rtss_dto" }
|
||||||
rtss_trackside = { path = "../rtss_trackside" }
|
|
||||||
rtss_db = { path = "../rtss_db" }
|
|
||||||
rtss_dto = { path = "../rtss_dto" }
|
|
|
@ -3,9 +3,7 @@ use draft_data::{DraftDataMutation, DraftDataQuery};
|
||||||
use feature::{FeatureMutation, FeatureQuery};
|
use feature::{FeatureMutation, FeatureQuery};
|
||||||
use release_data::{ReleaseDataMutation, ReleaseDataQuery};
|
use release_data::{ReleaseDataMutation, ReleaseDataQuery};
|
||||||
|
|
||||||
mod simulation_definition;
|
|
||||||
mod sys_info;
|
mod sys_info;
|
||||||
use simulation_definition::*;
|
|
||||||
use user::{UserMutation, UserQuery};
|
use user::{UserMutation, UserQuery};
|
||||||
|
|
||||||
mod data_options_def;
|
mod data_options_def;
|
||||||
|
@ -13,7 +11,6 @@ mod draft_data;
|
||||||
mod feature;
|
mod feature;
|
||||||
mod feature_config_def;
|
mod feature_config_def;
|
||||||
mod release_data;
|
mod release_data;
|
||||||
mod simulation;
|
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
#[derive(Default, MergedObject)]
|
#[derive(Default, MergedObject)]
|
|
@ -1,7 +1,9 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
use crate::{app_config, db::DbSubCommand, CmdExecutor};
|
use crate::{app_config, CmdExecutor};
|
||||||
|
|
||||||
|
use super::DbSubCommand;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "rtss-sim", version, author, about, long_about = None)]
|
#[command(name = "rtss-sim", version, author, about, long_about = None)]
|
|
@ -1,4 +1,3 @@
|
||||||
mod app_config;
|
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod db;
|
mod db;
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
mod app_config;
|
||||||
|
mod commands;
|
||||||
|
|
||||||
|
pub use commands::*;
|
|
@ -1,5 +1,5 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rtss_simulation::{Cmd, CmdExecutor};
|
use manager::{Cmd, CmdExecutor};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
|
@ -1 +1 @@
|
||||||
Subproject commit 236252fc0fa258e6beaae5b5ac0a7e28ebbcb04b
|
Subproject commit 1f53057b3f87790ef27c91399a5bb7e890f05549
|
|
@ -0,0 +1,24 @@
|
||||||
|
[package]
|
||||||
|
name = "simulation"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy_core = { workspace = true }
|
||||||
|
bevy_ecs = { workspace = true }
|
||||||
|
bevy_app = { workspace = true }
|
||||||
|
bevy_time = { workspace = true }
|
||||||
|
rayon = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
lazy_static = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
config = { workspace = true }
|
||||||
|
clap = { workspace = true, features = ["derive"] }
|
||||||
|
enum_dispatch = { workspace = true }
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
|
||||||
|
rtss_log = { path = "../crates/rtss_log" }
|
||||||
|
rtss_dto = { path = "../crates/rtss_dto" }
|
||||||
|
rtss_db = { path = "../crates/rtss_db" }
|
||||||
|
rtss_mqtt = { path = "../crates/rtss_mqtt" }
|
|
@ -0,0 +1,9 @@
|
||||||
|
[database]
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "debug"
|
||||||
|
|
||||||
|
[mqtt]
|
||||||
|
url = "http://localhost:8080"
|
||||||
|
username = "admin"
|
||||||
|
password = "admin"
|
|
@ -0,0 +1,7 @@
|
||||||
|
[database]
|
||||||
|
url = "postgresql://joylink:Joylink@0503@localhost:5432/joylink"
|
||||||
|
|
||||||
|
[mqtt]
|
||||||
|
url = "tcp://localhost:1883"
|
||||||
|
username = "rtsa"
|
||||||
|
password = "Joylink@0503"
|
|
@ -0,0 +1,10 @@
|
||||||
|
[database]
|
||||||
|
url = "postgresql://joylink:Joylink@0503@10.11.11.2:5432/joylink"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "debug"
|
||||||
|
|
||||||
|
[mqtt]
|
||||||
|
url = "tcp://192.168.3.233:1883"
|
||||||
|
username = "rtsa"
|
||||||
|
password = "Joylink@0503"
|
|
@ -11,6 +11,6 @@ bevy_time = {workspace = true}
|
||||||
rayon = {workspace = true}
|
rayon = {workspace = true}
|
||||||
thiserror = {workspace = true}
|
thiserror = {workspace = true}
|
||||||
|
|
||||||
rtss_log = { path = "../rtss_log" }
|
rtss_log = { path = "../../crates/rtss_log" }
|
||||||
rtss_common = { path = "../rtss_common" }
|
rtss_common = { path = "../rtss_common" }
|
||||||
rtss_trackside = { path = "../rtss_trackside" }
|
rtss_trackside = { path = "../rtss_trackside" }
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
|
@ -9,5 +9,5 @@ bevy_ecs = {workspace = true}
|
||||||
bevy_app = {workspace = true}
|
bevy_app = {workspace = true}
|
||||||
bevy_time = {workspace = true}
|
bevy_time = {workspace = true}
|
||||||
|
|
||||||
rtss_log = { path = "../rtss_log" }
|
rtss_log = { path = "../../crates/rtss_log" }
|
||||||
rtss_common = { path = "../rtss_common" }
|
rtss_common = { path = "../rtss_common" }
|
|
@ -0,0 +1,87 @@
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use config::{Config, ConfigError, Environment, File};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct Database {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct Log {
|
||||||
|
level: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Log> for rtss_log::Logging {
|
||||||
|
fn from(log: Log) -> Self {
|
||||||
|
rtss_log::Logging {
|
||||||
|
level: log.level.parse().unwrap(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct Mqtt {
|
||||||
|
pub url: String,
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub struct AppConfig {
|
||||||
|
pub log: Log,
|
||||||
|
pub database: Database,
|
||||||
|
pub mqtt: Mqtt,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppConfig {
|
||||||
|
pub fn new(dir: &str) -> Result<Self, ConfigError> {
|
||||||
|
let run_mode = env::var("RUN_MODE")
|
||||||
|
.unwrap_or_else(|_| "dev".into())
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
println!("RUN_MODE: {}", run_mode);
|
||||||
|
|
||||||
|
// log 当前目录
|
||||||
|
// println!("Current dir: {:?}", std::env::current_dir().unwrap());
|
||||||
|
|
||||||
|
let s = Config::builder()
|
||||||
|
// Start off by merging in the "default" configuration file
|
||||||
|
.add_source(File::with_name(&format!("{dir}/default")))
|
||||||
|
// Add in a local configuration file
|
||||||
|
// This file shouldn't be checked in to git
|
||||||
|
.add_source(File::with_name(&format!("{dir}/local")).required(false))
|
||||||
|
// Add in the current environment file
|
||||||
|
// Default to 'dev' env
|
||||||
|
// Note that this file is _optional_
|
||||||
|
.add_source(File::with_name(&format!("{dir}/{run_mode}")).required(true))
|
||||||
|
// Add in settings from the environment (with a prefix of RTSS_SIM)
|
||||||
|
// Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key
|
||||||
|
.add_source(Environment::with_prefix("RTSS_SIM").separator("_"))
|
||||||
|
// You may also programmatically change settings
|
||||||
|
// .set_override("database.url", "postgres://")?
|
||||||
|
// build the configuration
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
// You can deserialize (and thus freeze) the entire configuration as
|
||||||
|
s.try_deserialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_app_config() {
|
||||||
|
std::env::set_var("RUN_MODE", "local_test");
|
||||||
|
let config = AppConfig::new("conf").unwrap();
|
||||||
|
println!("{:?}", config);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
use clap::Parser;
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
|
use crate::app_config;
|
||||||
|
|
||||||
|
use super::{CmdExecutor, DbSubCommand};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(name = "rtss-sim", version, author, about, long_about = None)]
|
||||||
|
pub struct Cmd {
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub cmd: SubCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[enum_dispatch(CmdExecutor)]
|
||||||
|
pub enum SubCommand {
|
||||||
|
#[command(name = "serve")]
|
||||||
|
Serve(ServerOpts),
|
||||||
|
#[command(name = "db", subcommand, about = "Database operations")]
|
||||||
|
Db(DbSubCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub struct ServerOpts {
|
||||||
|
#[clap(short, long, required = false, default_value = "conf")]
|
||||||
|
config_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdExecutor for ServerOpts {
|
||||||
|
async fn execute(&self) -> anyhow::Result<()> {
|
||||||
|
let app_config =
|
||||||
|
app_config::AppConfig::new(&self.config_path).expect("Failed to load app config");
|
||||||
|
let log: rtss_log::Logging = app_config.log.into();
|
||||||
|
log.init();
|
||||||
|
// 数据库访问器初始化
|
||||||
|
rtss_db::init_default_db_accessor(&app_config.database.url).await;
|
||||||
|
// mqtt客户端初始化
|
||||||
|
let cli_id = rtss_db::get_default_db_accessor()
|
||||||
|
.get_next_mqtt_client_id()
|
||||||
|
.await?;
|
||||||
|
let mqtt_cli_options =
|
||||||
|
rtss_mqtt::MqttClientOptions::new(&format!("rtsa{}", cli_id), &app_config.mqtt.url)
|
||||||
|
.set_credentials(&app_config.mqtt.username, &app_config.mqtt.password);
|
||||||
|
rtss_mqtt::init_global_mqtt_client(mqtt_cli_options).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
use clap::Parser;
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
|
use crate::app_config;
|
||||||
|
|
||||||
|
use super::CmdExecutor;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[enum_dispatch(CmdExecutor)]
|
||||||
|
pub enum DbSubCommand {
|
||||||
|
#[command(name = "migrate", about = "Migrate database")]
|
||||||
|
Migrate(MigrateOpts),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub struct MigrateOpts {
|
||||||
|
#[clap(long, required = false, default_value = "conf")]
|
||||||
|
config_path: String,
|
||||||
|
#[clap(long, required = false, default_value = "migrations")]
|
||||||
|
file_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdExecutor for MigrateOpts {
|
||||||
|
async fn execute(&self) -> anyhow::Result<()> {
|
||||||
|
let app_config =
|
||||||
|
app_config::AppConfig::new(&self.config_path).expect("Failed to load app config");
|
||||||
|
rtss_db::run_migrations(&app_config.database.url).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
mod cmd;
|
||||||
|
mod db;
|
||||||
|
|
||||||
|
pub use cmd::*;
|
||||||
|
use db::*;
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
|
#[allow(async_fn_in_trait)]
|
||||||
|
#[enum_dispatch]
|
||||||
|
pub trait CmdExecutor {
|
||||||
|
async fn execute(&self) -> anyhow::Result<()>;
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
//! 顶级通用组件
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use bevy_ecs::{component::Component, entity::Entity, system::Resource};
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
#[derive(Resource, Clone, Debug)]
|
||||||
|
pub struct SimulationInfo {
|
||||||
|
pub id: String,
|
||||||
|
pub feature_id: String,
|
||||||
|
pub is_paused: bool,
|
||||||
|
pub speed: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimulationInfo {
|
||||||
|
pub fn new(id: &str, feature_id: &str) -> Self {
|
||||||
|
SimulationInfo {
|
||||||
|
id: id.to_string(),
|
||||||
|
feature_id: feature_id.to_string(),
|
||||||
|
is_paused: false,
|
||||||
|
speed: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备编号组件
|
||||||
|
#[derive(Component, Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Uid(pub String);
|
||||||
|
impl Default for Uid {
|
||||||
|
fn default() -> Self {
|
||||||
|
Uid("".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 仿真uid与实体映射资源
|
||||||
|
#[derive(Resource, Clone, Debug, Default)]
|
||||||
|
pub struct SimulationUidEntityMapResource(pub Arc<Mutex<HashMap<String, Entity>>>);
|
||||||
|
|
||||||
|
impl SimulationUidEntityMapResource {
|
||||||
|
pub fn get_entity(&self, uid: &str) -> Option<Entity> {
|
||||||
|
self.0.lock().unwrap().get(uid).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_entity(&self, uid: String, entity: Entity) {
|
||||||
|
self.0.lock().unwrap().insert(uid, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Debug)]
|
||||||
|
pub struct TxResource {
|
||||||
|
tx: broadcast::Sender<rtss_dto::simulation::Operation>,
|
||||||
|
rx: Option<broadcast::Receiver<rtss_dto::simulation::Operation>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TxResource {
|
||||||
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
let (tx, rx) = broadcast::channel(capacity);
|
||||||
|
TxResource { tx, rx: Some(rx) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tx(&self) -> broadcast::Sender<rtss_dto::simulation::Operation> {
|
||||||
|
self.tx.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe(&mut self) -> broadcast::Receiver<rtss_dto::simulation::Operation> {
|
||||||
|
let rx = self.tx.subscribe();
|
||||||
|
if self.rx.is_some() {
|
||||||
|
std::mem::take(&mut self.rx);
|
||||||
|
}
|
||||||
|
rx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use bevy_ecs::world;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let simulation_resource = SimulationUidEntityMapResource::default();
|
||||||
|
let mut world = world::World::default();
|
||||||
|
let uid = Uid("1".to_string());
|
||||||
|
let entity = world.spawn(uid.clone()).id();
|
||||||
|
simulation_resource.insert_entity(uid.clone().0, entity);
|
||||||
|
assert_eq!(simulation_resource.get_entity(&uid.0), Some(entity));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum SimulationError {
|
||||||
|
#[error("未知的仿真错误")]
|
||||||
|
Unknown,
|
||||||
|
#[error("创建错误: {0}")]
|
||||||
|
CreateError(String),
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
pub mod app_config;
|
||||||
|
pub mod commands;
|
||||||
|
pub mod components;
|
||||||
|
pub mod error;
|
||||||
|
pub mod manage;
|
||||||
|
pub mod modules;
|
|
@ -0,0 +1,9 @@
|
||||||
|
use clap::Parser;
|
||||||
|
use simulation::commands::{Cmd, CmdExecutor};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let cmd = Cmd::parse();
|
||||||
|
cmd.cmd.execute().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
use bevy_app::{Plugin, Startup};
|
||||||
|
use bevy_ecs::system::Res;
|
||||||
|
use rtss_log::tracing::debug;
|
||||||
|
|
||||||
|
use crate::components::SimulationInfo;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct DataLoadingPlugin;
|
||||||
|
|
||||||
|
impl Plugin for DataLoadingPlugin {
|
||||||
|
fn build(&self, app: &mut bevy_app::App) {
|
||||||
|
app.add_systems(Startup, loading);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loading(info: Res<SimulationInfo>) {
|
||||||
|
debug!("Loading data: {:?}", info);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
mod data_loading_plugin;
|
||||||
|
mod simulation;
|
||||||
|
mod simulation_control_plugin;
|
||||||
|
|
||||||
|
pub use simulation::{Simulation, SimulationOptions};
|
|
@ -0,0 +1,157 @@
|
||||||
|
use std::{
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bevy_app::{prelude::*, MainSchedulePlugin};
|
||||||
|
use bevy_ecs::{
|
||||||
|
event::{event_update_system, EventRegistry},
|
||||||
|
prelude::*,
|
||||||
|
reflect::AppTypeRegistry,
|
||||||
|
schedule::ScheduleLabel,
|
||||||
|
system::ResMut,
|
||||||
|
};
|
||||||
|
use bevy_time::{Fixed, Time, TimePlugin, Virtual};
|
||||||
|
use rtss_log::tracing::{debug, error, info};
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
components::{SimulationInfo, SimulationUidEntityMapResource, TxResource},
|
||||||
|
error::SimulationError,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
data_loading_plugin::DataLoadingPlugin, simulation_control_plugin::SimulationControlPlugin,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 仿真构造配置项
|
||||||
|
pub struct SimulationOptions {
|
||||||
|
/// 仿真ID
|
||||||
|
pub(crate) id: String,
|
||||||
|
/// 仿真功能特性类型
|
||||||
|
pub(crate) feature_id: String,
|
||||||
|
/// 仿真主逻辑循环间隔,详细请查看 [`Time<Fixed>`](bevy_time::fixed::Fixed)
|
||||||
|
pub(crate) loop_duration: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimulationOptions {
|
||||||
|
pub fn new(id: &str, feature_id: &str) -> Self {
|
||||||
|
SimulationOptions {
|
||||||
|
id: id.to_string(),
|
||||||
|
feature_id: feature_id.to_string(),
|
||||||
|
loop_duration: Duration::from_millis(20),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loop_duration(mut self, loop_duration: Duration) -> Self {
|
||||||
|
self.loop_duration = loop_duration;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Simulation {
|
||||||
|
app: Arc<Mutex<SubApp>>,
|
||||||
|
tx: broadcast::Sender<rtss_dto::simulation::Operation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Simulation {
|
||||||
|
pub fn new(options: SimulationOptions) -> Result<Self, SimulationError> {
|
||||||
|
let mut sub = SubApp::new();
|
||||||
|
sub.init_resource::<AppTypeRegistry>()
|
||||||
|
.init_resource::<EventRegistry>();
|
||||||
|
sub.update_schedule = Some(Main.intern());
|
||||||
|
sub.add_plugins(MainSchedulePlugin).add_plugins(TimePlugin);
|
||||||
|
sub.add_systems(
|
||||||
|
First,
|
||||||
|
event_update_system
|
||||||
|
.in_set(bevy_ecs::event::EventUpdates)
|
||||||
|
.run_if(bevy_ecs::event::event_update_condition),
|
||||||
|
);
|
||||||
|
sub.add_systems(
|
||||||
|
PreStartup,
|
||||||
|
move |mut tv: ResMut<Time<Virtual>>, mut tf: ResMut<Time<Fixed>>| {
|
||||||
|
debug!("PreStartup set time");
|
||||||
|
tv.set_max_delta(options.loop_duration);
|
||||||
|
tf.set_timestep(options.loop_duration);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// 添加全局共享资源
|
||||||
|
let tx_resource = TxResource::new(100);
|
||||||
|
let tx = tx_resource.get_tx();
|
||||||
|
sub.insert_resource(SimulationInfo::new(&options.id, &options.feature_id))
|
||||||
|
.insert_resource(SimulationUidEntityMapResource::default())
|
||||||
|
.insert_resource(tx_resource);
|
||||||
|
|
||||||
|
// 添加通用插件
|
||||||
|
sub.add_plugins(SimulationControlPlugin)
|
||||||
|
.add_plugins(DataLoadingPlugin);
|
||||||
|
|
||||||
|
sub.add_systems(FixedUpdate, hello_world_system);
|
||||||
|
|
||||||
|
Ok(Simulation {
|
||||||
|
app: Arc::new(Mutex::new(sub)),
|
||||||
|
tx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_operation(&self, op: rtss_dto::simulation::Operation) {
|
||||||
|
if let Err(e) = self.tx.send(op) {
|
||||||
|
error!("send operation error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&self) {
|
||||||
|
let mut app = self.app.lock().unwrap();
|
||||||
|
app.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hello_world_system(tv: Res<Time<Virtual>>) {
|
||||||
|
info!("hello world: {}", tv.relative_speed());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::thread::{self, sleep};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use rtss_dto::simulation::OperationType;
|
||||||
|
use rtss_log::tracing::Level;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_simulation() {
|
||||||
|
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
||||||
|
let simulation1 = Simulation::new(SimulationOptions::new("1", "1")).unwrap();
|
||||||
|
assert!(simulation1.app.lock().unwrap().update_schedule.is_some());
|
||||||
|
simulation1.send_operation(rtss_dto::simulation::Operation {
|
||||||
|
otype: rtss_dto::simulation::OperationType::SetSpeed as i32,
|
||||||
|
param: Some(rtss_dto::simulation::operation::Param::SetSpeedParam(
|
||||||
|
rtss_dto::simulation::SetSpeedParam { speed: 2.0 },
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
let clone2 = simulation1.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
sleep(Duration::from_millis(100));
|
||||||
|
info!("send pause");
|
||||||
|
clone2.send_operation(rtss_dto::simulation::Operation {
|
||||||
|
otype: OperationType::Pause as i32,
|
||||||
|
param: None,
|
||||||
|
});
|
||||||
|
sleep(Duration::from_millis(200));
|
||||||
|
info!("send unpause");
|
||||||
|
clone2.send_operation(rtss_dto::simulation::Operation {
|
||||||
|
otype: OperationType::Unpause as i32,
|
||||||
|
param: None,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// let simulation2 = Simulation::new(SimulationOptions::new("2", "1")).unwrap();
|
||||||
|
|
||||||
|
for _ in 0..30 {
|
||||||
|
sleep(Duration::from_millis(20));
|
||||||
|
simulation1.update();
|
||||||
|
// simulation2.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
use bevy_app::prelude::*;
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
use bevy_time::prelude::*;
|
||||||
|
use rtss_dto::simulation::{operation, OperationType, SetSpeedParam};
|
||||||
|
use rtss_log::tracing::debug;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
use crate::components::{SimulationInfo, TxResource};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SimulationControlPlugin;
|
||||||
|
|
||||||
|
impl Plugin for SimulationControlPlugin {
|
||||||
|
fn build(&self, app: &mut bevy_app::App) {
|
||||||
|
app.add_systems(Startup, init_rx_resource)
|
||||||
|
.add_systems(Update, handle_control_operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_rx_resource(mut commands: Commands, mut tx: ResMut<TxResource>) {
|
||||||
|
debug!("init_rx_resource");
|
||||||
|
commands.insert_resource(RxResource::new(tx.subscribe()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Debug)]
|
||||||
|
struct RxResource(pub broadcast::Receiver<rtss_dto::simulation::Operation>);
|
||||||
|
|
||||||
|
impl RxResource {
|
||||||
|
fn new(rx: broadcast::Receiver<rtss_dto::simulation::Operation>) -> Self {
|
||||||
|
RxResource(rx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_control_operation(
|
||||||
|
mut rx: ResMut<RxResource>,
|
||||||
|
mut time: ResMut<Time<Virtual>>,
|
||||||
|
info: Res<SimulationInfo>,
|
||||||
|
) {
|
||||||
|
if let Ok(op) = rx.0.try_recv() {
|
||||||
|
match OperationType::try_from(op.otype) {
|
||||||
|
Ok(OperationType::Pause) => {
|
||||||
|
debug!("Pausing simulation, id={}", info.id);
|
||||||
|
time.pause();
|
||||||
|
}
|
||||||
|
Ok(OperationType::Unpause) => {
|
||||||
|
debug!("Unpausing simulation, id={}", info.id);
|
||||||
|
time.unpause();
|
||||||
|
}
|
||||||
|
Ok(OperationType::SetSpeed) => {
|
||||||
|
if let Some(operation::Param::SetSpeedParam(SetSpeedParam { speed })) = op.param {
|
||||||
|
debug!("Update simulation id={} speed to {}", info.id, speed);
|
||||||
|
time.set_relative_speed(speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(OperationType::Destroy) => {
|
||||||
|
debug!("Exiting simulation, id={}", info.id);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
mod plugin;
|
||||||
|
|
||||||
|
pub use plugin::CiPlugin;
|
|
@ -0,0 +1,10 @@
|
||||||
|
use bevy_app::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CiPlugin;
|
||||||
|
|
||||||
|
impl Plugin for CiPlugin {
|
||||||
|
fn build(&self, _app: &mut bevy_app::App) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod ci;
|
||||||
|
pub mod trackside;
|
|
@ -0,0 +1,21 @@
|
||||||
|
use bevy_ecs::bundle::Bundle;
|
||||||
|
|
||||||
|
use crate::components::Uid;
|
||||||
|
|
||||||
|
use super::{PsdState, TurnoutState, TwoNormalPositionsTransform};
|
||||||
|
|
||||||
|
// 道岔设备组件包
|
||||||
|
#[derive(Bundle, Default)]
|
||||||
|
pub struct TurnoutBundle {
|
||||||
|
pub uid: Uid,
|
||||||
|
pub turnout_state: TurnoutState,
|
||||||
|
pub two_normal_positions_conversion: TwoNormalPositionsTransform,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 屏蔽门设备组件包
|
||||||
|
#[derive(Bundle, Default)]
|
||||||
|
pub struct PsdBundle {
|
||||||
|
pub uid: Uid,
|
||||||
|
pub psd_state: PsdState,
|
||||||
|
pub two_normal_positions_conversion: TwoNormalPositionsTransform,
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
use bevy_ecs::component::Component;
|
||||||
|
|
||||||
|
/// 两常态位置转换组件,用于像道岔位置,屏蔽门位置等
|
||||||
|
#[derive(Component, Debug, Clone, PartialEq, Default)]
|
||||||
|
pub struct TwoNormalPositionsTransform {
|
||||||
|
// 当前实际位置,百分比值,0-100
|
||||||
|
pub position: f32,
|
||||||
|
// 当前转换速度
|
||||||
|
pub velocity: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 道岔设备状态组件
|
||||||
|
#[derive(Component, Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
pub struct TurnoutState {
|
||||||
|
// 定位表示继电器状态
|
||||||
|
pub dbj: bool,
|
||||||
|
// 反位表示继电器状态
|
||||||
|
pub fbj: bool,
|
||||||
|
// 是否定位
|
||||||
|
pub dw: bool,
|
||||||
|
// 是否反位
|
||||||
|
pub fw: bool,
|
||||||
|
// 定操继电器状态
|
||||||
|
pub dcj: bool,
|
||||||
|
// 反操继电器状态
|
||||||
|
pub fcj: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 屏蔽门设备状态组件
|
||||||
|
#[derive(Component, Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
pub struct PsdState {
|
||||||
|
// 门关继电器状态
|
||||||
|
pub mgj: bool,
|
||||||
|
// 关门继电器状态
|
||||||
|
pub gmj: bool,
|
||||||
|
// 开门继电器状态
|
||||||
|
pub kmj: bool,
|
||||||
|
// 门旁路继电器状态(互锁解除)
|
||||||
|
pub mplj: bool,
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
mod bundle;
|
||||||
|
mod equipment;
|
||||||
|
pub use bundle::*;
|
||||||
|
pub use equipment::*;
|
|
@ -0,0 +1,10 @@
|
||||||
|
use bevy_ecs::event::Event;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Event, Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum TurnoutControlEvent {
|
||||||
|
// 道岔定操
|
||||||
|
DC,
|
||||||
|
// 道岔反操
|
||||||
|
FC,
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
mod equipment;
|
||||||
|
pub use equipment::*;
|
|
@ -0,0 +1,7 @@
|
||||||
|
mod components;
|
||||||
|
mod events;
|
||||||
|
mod plugin;
|
||||||
|
mod resources;
|
||||||
|
mod systems;
|
||||||
|
|
||||||
|
pub use plugin::TrackSideEquipmentPlugin;
|
|
@ -0,0 +1,26 @@
|
||||||
|
use bevy_app::{FixedUpdate, Plugin, Startup};
|
||||||
|
use bevy_ecs::schedule::IntoSystemConfigs;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
events::TurnoutControlEvent,
|
||||||
|
resources::SimulationConfig,
|
||||||
|
systems::{
|
||||||
|
handle_turnout_control, loading, turnout_state_update, two_normal_position_transform,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TrackSideEquipmentPlugin;
|
||||||
|
|
||||||
|
impl Plugin for TrackSideEquipmentPlugin {
|
||||||
|
fn build(&self, app: &mut bevy_app::App) {
|
||||||
|
app.insert_resource(SimulationConfig::default())
|
||||||
|
.add_event::<TurnoutControlEvent>()
|
||||||
|
.add_systems(Startup, loading)
|
||||||
|
.add_systems(
|
||||||
|
FixedUpdate,
|
||||||
|
(two_normal_position_transform, turnout_state_update).chain(),
|
||||||
|
)
|
||||||
|
.observe(handle_turnout_control);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
use bevy_ecs::system::Resource;
|
||||||
|
|
||||||
|
#[derive(Resource, Debug)]
|
||||||
|
pub struct SimulationConfig {
|
||||||
|
// 道岔转换总时间,单位ms
|
||||||
|
pub turnout_transform_time: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SimulationConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
SimulationConfig {
|
||||||
|
turnout_transform_time: 3500,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
use bevy_ecs::{entity::Entity, system::Query};
|
||||||
|
use rtss_log::tracing::debug;
|
||||||
|
|
||||||
|
use crate::{components::Uid, modules::trackside::components::TwoNormalPositionsTransform};
|
||||||
|
|
||||||
|
pub const TWO_NORMAL_POSITION_MIN: i32 = 0;
|
||||||
|
pub const TWO_NORMAL_POSITION_MAX: i32 = 100;
|
||||||
|
// 两常态位置转换系统
|
||||||
|
pub fn two_normal_position_transform(
|
||||||
|
mut query: Query<(Entity, &Uid, &mut TwoNormalPositionsTransform)>,
|
||||||
|
) {
|
||||||
|
for (entity, uid, mut transform) in &mut query {
|
||||||
|
debug!(
|
||||||
|
"Entity: {:?}, Uid: {:?}, Conversion: {:?}",
|
||||||
|
entity, uid, transform
|
||||||
|
);
|
||||||
|
if transform.velocity == 0f32 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let p = transform.position + transform.velocity;
|
||||||
|
if p >= TWO_NORMAL_POSITION_MAX as f32 {
|
||||||
|
transform.position = TWO_NORMAL_POSITION_MAX as f32;
|
||||||
|
transform.velocity = TWO_NORMAL_POSITION_MIN as f32;
|
||||||
|
} else if p <= TWO_NORMAL_POSITION_MIN as f32 {
|
||||||
|
transform.position = TWO_NORMAL_POSITION_MIN as f32;
|
||||||
|
transform.velocity = 0 as f32;
|
||||||
|
} else {
|
||||||
|
transform.position = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
use bevy_ecs::{prelude::Commands, system::ResMut};
|
||||||
|
use rtss_log::tracing::debug;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
components::{SimulationUidEntityMapResource, Uid},
|
||||||
|
modules::trackside::components,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn loading(mut commands: Commands, res_uid_mapping: ResMut<SimulationUidEntityMapResource>) {
|
||||||
|
debug!("loading trackside equipment");
|
||||||
|
let uid = Uid("1".to_string());
|
||||||
|
let et = commands.spawn(components::TurnoutBundle {
|
||||||
|
uid: uid.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
res_uid_mapping.insert_entity(uid.0, et.id());
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
mod common;
|
||||||
|
mod loading;
|
||||||
|
mod turnout;
|
||||||
|
pub use common::*;
|
||||||
|
pub use loading::*;
|
||||||
|
pub use turnout::*;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue