Compare commits
6 Commits
3798572b0c
...
f90e4048ef
Author | SHA1 | Date |
---|---|---|
soul-walker | f90e4048ef | |
soul-walker | 50ed464ff2 | |
soul-walker | 505230b26c | |
soul-walker | 2e1eecfdad | |
soul-walker | 4d8c0e0bad | |
soul-walker | 7226904e04 |
|
@ -50,7 +50,7 @@ jobs:
|
|||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
gitea.joylink.club/joylink/rtss-simulation:local-test
|
||||
gitea.joylink.club/joylink/rtsam:lt
|
||||
- name: 发布到本地测试环境
|
||||
uses: https://gitea.joylink.club/appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
|
@ -59,6 +59,6 @@ jobs:
|
|||
username: ${{ secrets.LOCAL_233_SSH_USER }}
|
||||
password: ${{ secrets.LOCAL_233_SSH_PASSWORD }}
|
||||
script: |
|
||||
docker rm -f rtss-simulation || echo "rtss-simulation not exist"
|
||||
docker pull gitea.joylink.club/joylink/rtss-simulation:local-test
|
||||
docker run --name rtss-simulation --restart=always -e RUN_MODE=local_test --network jlnet --ip 10.11.11.11 -d -p 8765:8765 -v /usr/local/joylink/logs/simulation:/logs/simulation gitea.joylink.club/joylink/rtss-simulation:local-test
|
||||
docker rm -f rtsa-manage || echo "rtsa-manage not exist"
|
||||
docker pull gitea.joylink.club/joylink/rtsam:lt
|
||||
docker run --name rtsa-manage --restart=always -e RUN_MODE=local_test --network jlnet --ip 10.11.11.11 -d -p 8765:8765 -v /usr/local/joylink/logs/simulation:/logs/simulation gitea.joylink.club/joylink/rtsam:lt
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
/target
|
||||
.env
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[submodule "rtss-proto-msg"]
|
||||
path = rtss-proto-msg
|
||||
url = https://gitea.joylink.club/joylink/rtss-proto-msg.git
|
||||
[submodule "rtsa-proto-msg"]
|
||||
path = rtsa-proto-msg
|
||||
url = https://gitea.joylink.club/joylink/rtsa-proto-msg.git
|
||||
|
|
|
@ -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}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
"cSpell.words": [
|
||||
"chrono",
|
||||
"cpus",
|
||||
"dashmap",
|
||||
"eventloop",
|
||||
"Graphi",
|
||||
"graphiql",
|
||||
"hashbrown",
|
||||
|
@ -11,7 +13,12 @@
|
|||
"Joylink",
|
||||
"jsonwebtoken",
|
||||
"mplj",
|
||||
"Mqtt",
|
||||
"mqttbytes",
|
||||
"Neng",
|
||||
"nextval",
|
||||
"oneshot",
|
||||
"otype",
|
||||
"plpgsql",
|
||||
"prost",
|
||||
"proto",
|
||||
|
@ -19,7 +26,10 @@
|
|||
"protos",
|
||||
"repr",
|
||||
"reqwest",
|
||||
"rtss",
|
||||
"rtsa",
|
||||
"rtsa",
|
||||
"rumqtt",
|
||||
"rumqttc",
|
||||
"sqlx",
|
||||
"sysinfo",
|
||||
"thiserror",
|
||||
|
|
|
@ -121,9 +121,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.88"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356"
|
||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||
|
||||
[[package]]
|
||||
name = "arraydeque"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
|
||||
|
||||
[[package]]
|
||||
name = "ascii_utils"
|
||||
|
@ -133,9 +139,9 @@ checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
|
|||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
|
||||
checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
|
@ -270,9 +276,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.81"
|
||||
version = "0.1.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -325,7 +331,7 @@ dependencies = [
|
|||
"sync_wrapper 1.0.1",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tower",
|
||||
"tower 0.4.13",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
@ -369,7 +375,7 @@ dependencies = [
|
|||
"mime",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"tower",
|
||||
"tower 0.4.13",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
@ -564,7 +570,7 @@ dependencies = [
|
|||
"ahash",
|
||||
"bevy_utils_proc_macros",
|
||||
"getrandom",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"web-time",
|
||||
|
@ -619,9 +625,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -658,9 +664,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.17"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
||||
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -668,9 +674,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.17"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
||||
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -680,9 +686,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.13"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
@ -713,14 +719,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.14.0"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
|
||||
checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"convert_case",
|
||||
"json5",
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"pathdiff",
|
||||
"ron",
|
||||
|
@ -728,7 +733,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
"yaml-rust",
|
||||
"yaml-rust2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -776,6 +781,16 @@ dependencies = [
|
|||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
|
@ -1066,9 +1081,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
|||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
|
@ -1236,12 +1251,6 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
|
@ -1253,13 +1262,22 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1407,10 +1425,10 @@ dependencies = [
|
|||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.26.0",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
@ -1430,7 +1448,7 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower 0.4.13",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
@ -1481,7 +1499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -1579,12 +1597,6 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
|
@ -1613,7 +1625,22 @@ version = "0.12.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"config",
|
||||
"enum_dispatch",
|
||||
"rtsa_api",
|
||||
"rtsa_db",
|
||||
"rtsa_log",
|
||||
"serde",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1817,13 +1844,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.6.0"
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
|
||||
dependencies = [
|
||||
"dlv-list",
|
||||
"hashbrown 0.13.2",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2119,7 +2152,7 @@ dependencies = [
|
|||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"rustls 0.23.13",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
@ -2136,7 +2169,7 @@ dependencies = [
|
|||
"rand",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"rustls 0.23.13",
|
||||
"slab",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
|
@ -2301,7 +2334,7 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
@ -2309,7 +2342,7 @@ dependencies = [
|
|||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.1",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.26.0",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
|
@ -2367,7 +2400,7 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_api"
|
||||
name = "rtsa_api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
|
@ -2376,15 +2409,12 @@ dependencies = [
|
|||
"axum",
|
||||
"axum-extra",
|
||||
"base64 0.22.1",
|
||||
"bevy_ecs",
|
||||
"chrono",
|
||||
"jsonwebtoken",
|
||||
"reqwest",
|
||||
"rtss_db",
|
||||
"rtss_dto",
|
||||
"rtss_log",
|
||||
"rtss_sim_manage",
|
||||
"rtss_trackside",
|
||||
"rtsa_db",
|
||||
"rtsa_dto",
|
||||
"rtsa_log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sysinfo",
|
||||
|
@ -2393,23 +2423,13 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_ci"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rtss_common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy_ecs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_db"
|
||||
name = "rtsa_db"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rtss_dto",
|
||||
"rtss_log",
|
||||
"lazy_static",
|
||||
"rtsa_dto",
|
||||
"rtsa_log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
|
@ -2417,22 +2437,19 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_dto"
|
||||
name = "rtsa_dto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-graphql",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"prost-types",
|
||||
"serde",
|
||||
"sqlx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_iscs"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rtss_log"
|
||||
name = "rtsa_log"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
|
@ -2441,52 +2458,43 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_sim_manage"
|
||||
name = "rtsa_mqtt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_core",
|
||||
"bevy_ecs",
|
||||
"bevy_time",
|
||||
"rayon",
|
||||
"rtss_common",
|
||||
"rtss_log",
|
||||
"rtss_trackside",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"lazy_static",
|
||||
"rtsa_log",
|
||||
"rumqttc",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_simulation"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"config",
|
||||
"enum_dispatch",
|
||||
"rtss_api",
|
||||
"rtss_db",
|
||||
"rtss_log",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tower 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_trackside"
|
||||
version = "0.1.0"
|
||||
name = "rumqttc"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1568e15fab2d546f940ed3a21f48bbbd1c494c90c99c4481339364a497f94a9"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_core",
|
||||
"bevy_ecs",
|
||||
"bevy_time",
|
||||
"rtss_common",
|
||||
"rtss_log",
|
||||
"bytes",
|
||||
"flume",
|
||||
"futures-util",
|
||||
"log",
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile",
|
||||
"rustls-webpki",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-rustls 0.25.0",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.19.0"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
|
||||
checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ordered-multimap",
|
||||
|
@ -2517,6 +2525,20 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.13"
|
||||
|
@ -2531,6 +2553,19 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "2.1.3"
|
||||
|
@ -2570,6 +2605,15 @@ version = "1.0.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
@ -2577,19 +2621,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.214"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
version = "1.0.214"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2598,9 +2665,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.125"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
@ -2698,6 +2765,29 @@ dependencies = [
|
|||
"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",
|
||||
"rtsa_db",
|
||||
"rtsa_dto",
|
||||
"rtsa_log",
|
||||
"rtsa_mqtt",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
@ -2796,8 +2886,8 @@ dependencies = [
|
|||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashbrown 0.14.5",
|
||||
"hashlink",
|
||||
"hashbrown",
|
||||
"hashlink 0.9.1",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"log",
|
||||
|
@ -3019,9 +3109,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.76"
|
||||
version = "2.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3072,18 +3162,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.63"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -3157,9 +3247,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.40.0"
|
||||
version = "1.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
||||
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
@ -3182,13 +3272,24 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
||||
dependencies = [
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
@ -3291,6 +3392,20 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper 0.1.2",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.0"
|
||||
|
@ -3417,9 +3532,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typeid"
|
||||
version = "1.0.0"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf"
|
||||
checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
|
@ -3935,12 +4050,14 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
name = "yaml-rust2"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
"arraydeque",
|
||||
"encoding_rs",
|
||||
"hashlink 0.8.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
35
Cargo.toml
35
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]
|
||||
members = ["crates/*"]
|
||||
members = ["crates/*", "manager/crates/*", "manager", "simulation"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
bevy_app = "0.14"
|
||||
|
@ -14,25 +9,21 @@ bevy_core = "0.14"
|
|||
bevy_ecs = "0.14"
|
||||
bevy_time = "0.14"
|
||||
rayon = "1.10"
|
||||
tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.41.0", features = ["macros", "rt-multi-thread"] }
|
||||
thiserror = "1.0.65"
|
||||
sqlx = { version = "0.8", features = [
|
||||
"runtime-tokio",
|
||||
"postgres",
|
||||
"json",
|
||||
"chrono",
|
||||
] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.125"
|
||||
anyhow = "1.0"
|
||||
|
||||
[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", features = ["derive"] }
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.132"
|
||||
anyhow = "1.0.91"
|
||||
async-trait = "0.1.83"
|
||||
bytes = "1.8.0"
|
||||
lazy_static = "1.5.0"
|
||||
config = "0.14.1"
|
||||
clap = { version = "4.5.20", features = ["derive"] }
|
||||
enum_dispatch = "0.3"
|
||||
anyhow = { workspace = true }
|
||||
tower = { version = "0.5.1", features = ["util"] }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[default.extend-words]
|
||||
|
||||
[files]
|
||||
extend-exclude = ["README.md", "rtss-proto-msg/*"]
|
||||
extend-exclude = ["README.md", "rtsa-proto-msg/*"]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "rtss_db"
|
||||
name = "rtsa_db"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@ -17,6 +17,7 @@ sqlx = { workspace = true, features = [
|
|||
"uuid",
|
||||
] }
|
||||
thiserror = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
|
||||
rtss_dto = { path = "../rtss_dto" }
|
||||
rtss_log = { path = "../rtss_log" }
|
||||
rtsa_dto = { path = "../rtsa_dto" }
|
||||
rtsa_log = { path = "../rtsa_log" }
|
|
@ -1,7 +1,7 @@
|
|||
use std::vec;
|
||||
|
||||
use rtss_dto::common::DataType;
|
||||
use rtss_log::tracing::debug;
|
||||
use rtsa_dto::common::DataType;
|
||||
use rtsa_log::tracing::debug;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
|||
DbAccessError,
|
||||
};
|
||||
|
||||
use super::RtssDbAccessor;
|
||||
use super::RtsaDbAccessor;
|
||||
|
||||
/// 草稿数据管理
|
||||
#[allow(async_fn_in_trait)]
|
||||
|
@ -205,7 +205,7 @@ impl CreateDraftData {
|
|||
}
|
||||
}
|
||||
|
||||
impl DraftDataAccessor for RtssDbAccessor {
|
||||
impl DraftDataAccessor for RtsaDbAccessor {
|
||||
async fn paging_query_draft_data(
|
||||
&self,
|
||||
query: DraftDataQuery,
|
||||
|
@ -436,8 +436,8 @@ mod tests {
|
|||
use crate::{SyncUserInfo, UserAccessor};
|
||||
|
||||
use super::*;
|
||||
use rtss_dto::common::{IscsStyle, Role};
|
||||
use rtss_log::tracing::Level;
|
||||
use rtsa_dto::common::{IscsStyle, Role};
|
||||
use rtsa_log::tracing::Level;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{types::chrono::Local, PgPool};
|
||||
|
||||
|
@ -449,8 +449,8 @@ mod tests {
|
|||
// You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here.
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn basic_use_test(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = crate::db_access::RtssDbAccessor::new(pool);
|
||||
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = crate::db_access::RtsaDbAccessor::new(pool);
|
||||
// 同步10个用户
|
||||
let mut users = vec![];
|
||||
for i in 0..10 {
|
||||
|
@ -458,7 +458,7 @@ mod tests {
|
|||
id: i + 1,
|
||||
name: format!("user{}", i + 1),
|
||||
password: "password".to_string(),
|
||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
||||
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||
email: None,
|
||||
mobile: None,
|
||||
created_at: Local::now(),
|
|
@ -1,4 +1,4 @@
|
|||
use rtss_dto::common::FeatureType;
|
||||
use rtsa_dto::common::FeatureType;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||
DbAccessError,
|
||||
};
|
||||
|
||||
use super::RtssDbAccessor;
|
||||
use super::RtsaDbAccessor;
|
||||
|
||||
/// 功能特性管理
|
||||
#[allow(async_fn_in_trait)]
|
||||
|
@ -34,7 +34,7 @@ pub trait FeatureAccessor {
|
|||
async fn get_features(&self, ids: &[i32]) -> Result<Vec<FeatureModel>, DbAccessError>;
|
||||
}
|
||||
|
||||
impl FeatureAccessor for RtssDbAccessor {
|
||||
impl FeatureAccessor for RtsaDbAccessor {
|
||||
async fn create_feature(&self, feature: &CreateFeature) -> Result<FeatureModel, DbAccessError> {
|
||||
let table = FeatureColumn::Table.name();
|
||||
let feature_type_column = FeatureColumn::FeatureType.name();
|
||||
|
@ -217,8 +217,8 @@ impl FeaturePagingFilter {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rtss_dto::common::Role;
|
||||
use rtss_log::tracing::Level;
|
||||
use rtsa_dto::common::Role;
|
||||
use rtsa_log::tracing::Level;
|
||||
use sqlx::{types::chrono::Local, PgPool};
|
||||
|
||||
use crate::{SyncUserInfo, UserAccessor};
|
||||
|
@ -227,8 +227,8 @@ mod tests {
|
|||
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_basic_use(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = crate::db_access::RtssDbAccessor::new(pool);
|
||||
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = crate::db_access::RtsaDbAccessor::new(pool);
|
||||
// 同步10个用户
|
||||
let mut users = vec![];
|
||||
for i in 0..10 {
|
||||
|
@ -236,7 +236,7 @@ mod tests {
|
|||
id: i + 1,
|
||||
name: format!("user{}", i + 1),
|
||||
password: "password".to_string(),
|
||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
||||
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||
email: None,
|
||||
mobile: None,
|
||||
created_at: Local::now(),
|
||||
|
@ -260,7 +260,7 @@ mod tests {
|
|||
println!("create feature: {:?}", feature);
|
||||
assert_eq!(feature.creator_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);
|
||||
let feature_id = feature.id;
|
||||
// 获取功能特性测试
|
||||
|
@ -269,7 +269,7 @@ mod tests {
|
|||
assert_eq!(feature.id, feature_id);
|
||||
assert_eq!(feature.creator_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);
|
||||
// 更新测试
|
||||
let updater_id = 2;
|
||||
|
@ -279,7 +279,7 @@ mod tests {
|
|||
name: "test update".to_string(),
|
||||
description: "test update".to_string(),
|
||||
config: config.clone(),
|
||||
updater_id: updater_id,
|
||||
updater_id,
|
||||
};
|
||||
let feature = accessor.update_feature(&feature).await?;
|
||||
println!("update feature: {:?}", feature);
|
||||
|
@ -289,7 +289,7 @@ mod tests {
|
|||
// 上下架测试
|
||||
let feature = accessor.set_feature_published(feature_id, false).await?;
|
||||
println!("set feature published: {:?}", feature);
|
||||
assert_eq!(feature.is_published, false);
|
||||
assert!(!feature.is_published);
|
||||
// 分页查询测试
|
||||
// 创建10个功能特性,5个发布的,5个未发布的
|
||||
for i in 0..10 {
|
|
@ -0,0 +1,82 @@
|
|||
mod draft_data;
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub use draft_data::*;
|
||||
mod release_data;
|
||||
pub use release_data::*;
|
||||
mod user;
|
||||
use rtsa_log::tracing::error;
|
||||
pub use user::*;
|
||||
mod feature;
|
||||
pub use feature::*;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::{model::MqttClientIdSeq, DbAccessError};
|
||||
|
||||
lazy_static! {
|
||||
static ref RDA: Mutex<Option<RtsaDbAccessor>> = 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() -> RtsaDbAccessor {
|
||||
let rda = RDA.lock().unwrap();
|
||||
if rda.is_none() {
|
||||
panic!("数据库访问器未初始化");
|
||||
}
|
||||
rda.as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtsaDbAccessor {
|
||||
pool: sqlx::PgPool,
|
||||
}
|
||||
|
||||
impl RtsaDbAccessor {
|
||||
pub fn new(pool: sqlx::PgPool) -> Self {
|
||||
RtsaDbAccessor { pool }
|
||||
}
|
||||
|
||||
pub async fn get_next_mqtt_client_id(&self) -> Result<i64, DbAccessError> {
|
||||
let seq_name = MqttClientIdSeq::Name.name();
|
||||
let next = sqlx::query_scalar(&format!("SELECT nextval('{}')", seq_name))
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
Ok(next)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_db_accessor(url: &str) -> RtsaDbAccessor {
|
||||
let pool = sqlx::PgPool::connect(url).await.expect("连接数据库失败");
|
||||
RtsaDbAccessor::new(pool)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rtsa_log::tracing::{self, Level};
|
||||
use sqlx::PgPool;
|
||||
|
||||
// You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here.
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_get_mqtt_client_id(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = crate::db_access::RtsaDbAccessor::new(pool);
|
||||
for _ in 0..10 {
|
||||
let id = accessor.get_next_mqtt_client_id().await?;
|
||||
tracing::info!("id = {}", id);
|
||||
assert!(id > 0);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use rtss_dto::common::DataType;
|
||||
use rtsa_dto::common::DataType;
|
||||
use serde_json::Value;
|
||||
use sqlx::{types::chrono, Postgres};
|
||||
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
|||
DbAccessError,
|
||||
};
|
||||
|
||||
use super::{CreateDraftData, DraftDataAccessor, RtssDbAccessor};
|
||||
use super::{CreateDraftData, DraftDataAccessor, RtsaDbAccessor};
|
||||
|
||||
#[allow(async_fn_in_trait)]
|
||||
pub trait ReleaseDataAccessor {
|
||||
|
@ -109,7 +109,7 @@ pub struct ReleaseFromDraftResult {
|
|||
pub struct ReleaseDataQuery {
|
||||
pub name: Option<String>,
|
||||
pub user_id: Option<i32>,
|
||||
pub data_type: Option<rtss_dto::common::DataType>,
|
||||
pub data_type: Option<rtsa_dto::common::DataType>,
|
||||
pub options: Option<Value>,
|
||||
pub is_published: Option<bool>,
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ impl ReleaseDataQuery {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_data_type(mut self, data_type: rtss_dto::common::DataType) -> Self {
|
||||
pub fn with_data_type(mut self, data_type: rtsa_dto::common::DataType) -> Self {
|
||||
self.data_type = Some(data_type);
|
||||
self
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ pub struct CreateReleaseVersionData {
|
|||
pub user_id: i32,
|
||||
}
|
||||
|
||||
impl RtssDbAccessor {
|
||||
impl RtsaDbAccessor {
|
||||
async fn insert_release_data_version<'e, 'c: 'e, E>(
|
||||
&self,
|
||||
data: CreateReleaseVersionData,
|
||||
|
@ -237,7 +237,7 @@ impl RtssDbAccessor {
|
|||
}
|
||||
}
|
||||
|
||||
impl ReleaseDataAccessor for RtssDbAccessor {
|
||||
impl ReleaseDataAccessor for RtsaDbAccessor {
|
||||
async fn release_new_from_draft(
|
||||
&self,
|
||||
draft_id: i32,
|
||||
|
@ -645,12 +645,12 @@ impl ReleaseDataAccessor for RtssDbAccessor {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{CreateDraftData, DraftDataAccessor, RtssDbAccessor, SyncUserInfo, UserAccessor};
|
||||
use crate::{CreateDraftData, DraftDataAccessor, RtsaDbAccessor, SyncUserInfo, UserAccessor};
|
||||
|
||||
use super::*;
|
||||
use chrono::Local;
|
||||
use rtss_dto::common::{IscsStyle, Role};
|
||||
use rtss_log::tracing::Level;
|
||||
use rtsa_dto::common::{IscsStyle, Role};
|
||||
use rtsa_log::tracing::Level;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
|
@ -695,8 +695,8 @@ mod tests {
|
|||
// You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here.
|
||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_basic_use(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = RtssDbAccessor::new(pool);
|
||||
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = RtsaDbAccessor::new(pool);
|
||||
// 同步10个用户
|
||||
let mut users = vec![];
|
||||
for i in 0..10 {
|
||||
|
@ -704,7 +704,7 @@ mod tests {
|
|||
id: i + 1,
|
||||
name: format!("user{}", i + 1),
|
||||
password: "password".to_string(),
|
||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
||||
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||
email: None,
|
||||
mobile: None,
|
||||
created_at: Local::now(),
|
||||
|
@ -717,7 +717,7 @@ mod tests {
|
|||
let data = "test".as_bytes();
|
||||
let draft = accessor
|
||||
.create_draft_data(
|
||||
CreateDraftData::new("test", rtss_dto::common::DataType::Iscs, 1)
|
||||
CreateDraftData::new("test", rtsa_dto::common::DataType::Iscs, 1)
|
||||
.with_options(
|
||||
serde_json::to_value(IscsDataOptions {
|
||||
style: IscsStyle::DaShiZhiNeng,
|
||||
|
@ -745,7 +745,7 @@ mod tests {
|
|||
|
||||
// 发布新版本
|
||||
let (release_data, version1) = accessor
|
||||
.release_new_from_draft(draft.id, name, &description, None)
|
||||
.release_new_from_draft(draft.id, name, description, None)
|
||||
.await?;
|
||||
assert_eq!(release_data.name, name);
|
||||
// 检查使用版本是刚刚发布的版本
|
||||
|
@ -759,7 +759,7 @@ mod tests {
|
|||
// 检查版本描述
|
||||
assert_eq!(version1.description, description);
|
||||
// 检查上架状态
|
||||
assert_eq!(release_data.is_published, true);
|
||||
assert!(release_data.is_published);
|
||||
// 检查草稿数据的默认发布数据id
|
||||
let draft = accessor.query_draft_data_by_id(draft.id).await?;
|
||||
assert_eq!(draft.default_release_data_id, Some(release_data.id));
|
||||
|
@ -767,9 +767,9 @@ mod tests {
|
|||
|
||||
// name重复检查
|
||||
let exist = accessor
|
||||
.is_release_data_name_exist(rtss_dto::common::DataType::Iscs, name)
|
||||
.is_release_data_name_exist(rtsa_dto::common::DataType::Iscs, name)
|
||||
.await?;
|
||||
assert_eq!(exist, true);
|
||||
assert!(exist);
|
||||
|
||||
// 修改草稿数据
|
||||
let data = "test2".as_bytes();
|
||||
|
@ -798,7 +798,7 @@ mod tests {
|
|||
let release_data = accessor
|
||||
.set_release_data_published(release_data.id, false)
|
||||
.await?;
|
||||
assert_eq!(release_data.is_published, false);
|
||||
assert!(!release_data.is_published);
|
||||
assert!(release_data.updated_at > release_data.created_at);
|
||||
println!("更新发布数据上架状态测试成功");
|
||||
|
||||
|
@ -849,7 +849,7 @@ mod tests {
|
|||
let data = "test data".as_bytes();
|
||||
let draft = accessor
|
||||
.create_draft_data(
|
||||
CreateDraftData::new(&name, rtss_dto::common::DataType::Em, i).with_data(data),
|
||||
CreateDraftData::new(&name, rtsa_dto::common::DataType::Em, i).with_data(data),
|
||||
)
|
||||
.await?;
|
||||
let (release_data, _) = accessor
|
||||
|
@ -883,7 +883,7 @@ mod tests {
|
|||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||
assert_eq!(page_result.total, 2);
|
||||
// 分页查询发布数据,按数据类型过滤
|
||||
let query = ReleaseDataQuery::new().with_data_type(rtss_dto::common::DataType::Em);
|
||||
let query = ReleaseDataQuery::new().with_data_type(rtsa_dto::common::DataType::Em);
|
||||
let page = PageQuery::new(1, 10);
|
||||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||
assert_eq!(page_result.total, 8);
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
DbAccessError,
|
||||
};
|
||||
|
||||
use super::RtssDbAccessor;
|
||||
use super::RtsaDbAccessor;
|
||||
|
||||
/// 草稿数据管理
|
||||
#[allow(async_fn_in_trait)]
|
||||
|
@ -96,7 +96,7 @@ pub struct SyncUserInfo {
|
|||
pub updated_at: Option<DateTime<Local>>,
|
||||
}
|
||||
|
||||
impl RtssDbAccessor {
|
||||
impl RtsaDbAccessor {
|
||||
/// 首次同步用户数据
|
||||
async fn sync_new_users(&self, users: &[SyncUserInfo]) -> Result<(), DbAccessError> {
|
||||
let table = UserColumn::Table.name();
|
||||
|
@ -223,7 +223,7 @@ impl RtssDbAccessor {
|
|||
}
|
||||
}
|
||||
|
||||
impl UserAccessor for RtssDbAccessor {
|
||||
impl UserAccessor for RtsaDbAccessor {
|
||||
async fn sync_user(&self, users: &[SyncUserInfo]) -> Result<(), DbAccessError> {
|
||||
self.check_and_sync_user(users).await
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ impl UserAccessor for RtssDbAccessor {
|
|||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use rtss_log::tracing::Level;
|
||||
use rtsa_log::tracing::Level;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
|
||||
|
@ -293,14 +293,14 @@ mod tests {
|
|||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||
async fn test_sync_user(pool: PgPool) -> Result<(), DbAccessError> {
|
||||
// 日志初始化
|
||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = RtssDbAccessor::new(pool);
|
||||
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let accessor = RtsaDbAccessor::new(pool);
|
||||
let users = vec![
|
||||
SyncUserInfo {
|
||||
id: 1,
|
||||
name: "test1".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,
|
||||
mobile: None,
|
||||
created_at: Local::now(),
|
||||
|
@ -310,7 +310,7 @@ mod tests {
|
|||
id: 2,
|
||||
name: "test2".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,
|
||||
mobile: None,
|
||||
created_at: Local::now(),
|
||||
|
@ -342,7 +342,7 @@ mod tests {
|
|||
id: 1,
|
||||
name: "test1".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()),
|
||||
mobile: None,
|
||||
created_at: Local::now() - Duration::from_secs(60),
|
||||
|
@ -352,7 +352,7 @@ mod tests {
|
|||
id: 2,
|
||||
name: "test2".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,
|
||||
mobile: Some("123456789".to_string()),
|
||||
created_at: Local::now() - Duration::from_secs(60),
|
||||
|
@ -362,7 +362,7 @@ mod tests {
|
|||
id: 3,
|
||||
name: "test3".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,
|
||||
mobile: None,
|
||||
created_at: Local::now(),
|
|
@ -1,4 +1,4 @@
|
|||
use rtss_dto::common::{DataType, FeatureType, Role};
|
||||
use rtsa_dto::common::{DataType, FeatureType, Role};
|
||||
use serde_json::Value;
|
||||
use sqlx::types::{
|
||||
chrono::{DateTime, Local},
|
||||
|
@ -7,6 +7,18 @@ use sqlx::types::{
|
|||
|
||||
use crate::common::TableColumn;
|
||||
|
||||
pub enum MqttClientIdSeq {
|
||||
Name,
|
||||
}
|
||||
|
||||
impl MqttClientIdSeq {
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
MqttClientIdSeq::Name => "rtsa.mqtt_client_id_seq",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UserColumn {
|
||||
Table,
|
||||
|
@ -34,7 +46,7 @@ pub struct UserModel {
|
|||
pub updated_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.draft_data 列映射
|
||||
/// 数据库表 rtsa.draft_data 列映射
|
||||
#[derive(Debug)]
|
||||
pub enum DraftDataColumn {
|
||||
Table,
|
||||
|
@ -68,7 +80,7 @@ pub struct DraftDataModel {
|
|||
pub updated_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.release_data 列映射
|
||||
/// 数据库表 rtsa.release_data 列映射
|
||||
#[derive(Debug)]
|
||||
pub enum ReleaseDataColumn {
|
||||
Table,
|
||||
|
@ -99,9 +111,9 @@ pub struct ReleaseDataModel {
|
|||
pub updated_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.release_data_version 列映射
|
||||
/// 数据库表 rtsa.release_data_version 列映射
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ReleaseDataVersionColumn {
|
||||
pub enum ReleaseDataVersionColumn {
|
||||
Table,
|
||||
Id,
|
||||
ReleaseDataId,
|
||||
|
@ -125,10 +137,10 @@ pub struct ReleaseDataVersionModel {
|
|||
pub created_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.feature 列映射
|
||||
/// 数据库表 rtsa.feature 列映射
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum FeatureColumn {
|
||||
pub enum FeatureColumn {
|
||||
Table,
|
||||
Id,
|
||||
FeatureType,
|
||||
|
@ -157,10 +169,10 @@ pub struct FeatureModel {
|
|||
pub updated_at: DateTime<Local>,
|
||||
}
|
||||
|
||||
/// 数据库表 rtss.user_config 列映射
|
||||
/// 数据库表 rtsa.user_config 列映射
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum UserConfigColumn {
|
||||
pub enum UserConfigColumn {
|
||||
Table,
|
||||
Id,
|
||||
UserId,
|
||||
|
@ -183,7 +195,7 @@ pub struct UserConfigModel {
|
|||
impl TableColumn for UserColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
UserColumn::Table => "rtss.user",
|
||||
UserColumn::Table => "rtsa.user",
|
||||
UserColumn::Id => "id",
|
||||
UserColumn::Username => "username",
|
||||
UserColumn::Password => "password",
|
||||
|
@ -199,7 +211,7 @@ impl TableColumn for UserColumn {
|
|||
impl TableColumn for DraftDataColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
DraftDataColumn::Table => "rtss.draft_data",
|
||||
DraftDataColumn::Table => "rtsa.draft_data",
|
||||
DraftDataColumn::Id => "id",
|
||||
DraftDataColumn::Name => "name",
|
||||
DraftDataColumn::DataType => "data_type",
|
||||
|
@ -217,7 +229,7 @@ impl TableColumn for DraftDataColumn {
|
|||
impl TableColumn for ReleaseDataColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
ReleaseDataColumn::Table => "rtss.release_data",
|
||||
ReleaseDataColumn::Table => "rtsa.release_data",
|
||||
ReleaseDataColumn::Id => "id",
|
||||
ReleaseDataColumn::Name => "name",
|
||||
ReleaseDataColumn::DataType => "data_type",
|
||||
|
@ -234,7 +246,7 @@ impl TableColumn for ReleaseDataColumn {
|
|||
impl TableColumn for ReleaseDataVersionColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
ReleaseDataVersionColumn::Table => "rtss.release_data_version",
|
||||
ReleaseDataVersionColumn::Table => "rtsa.release_data_version",
|
||||
ReleaseDataVersionColumn::Id => "id",
|
||||
ReleaseDataVersionColumn::ReleaseDataId => "release_data_id",
|
||||
ReleaseDataVersionColumn::Options => "options",
|
||||
|
@ -249,7 +261,7 @@ impl TableColumn for ReleaseDataVersionColumn {
|
|||
impl TableColumn for FeatureColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
FeatureColumn::Table => "rtss.feature",
|
||||
FeatureColumn::Table => "rtsa.feature",
|
||||
FeatureColumn::Id => "id",
|
||||
FeatureColumn::FeatureType => "feature_type",
|
||||
FeatureColumn::Name => "name",
|
||||
|
@ -267,7 +279,7 @@ impl TableColumn for FeatureColumn {
|
|||
impl TableColumn for UserConfigColumn {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
UserConfigColumn::Table => "rtss.user_config",
|
||||
UserConfigColumn::Table => "rtsa.user_config",
|
||||
UserConfigColumn::Id => "id",
|
||||
UserConfigColumn::UserId => "user_id",
|
||||
UserConfigColumn::ConfigType => "config_type",
|
|
@ -1,10 +1,11 @@
|
|||
[package]
|
||||
name = "rtss_dto"
|
||||
name = "rtsa_dto"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
prost = "0.13"
|
||||
prost-types = "0.13"
|
||||
async-graphql = { version = "7.0.7", features = ["chrono"] }
|
||||
sqlx = { workspace = true }
|
||||
serde = { workspace = true }
|
|
@ -16,7 +16,7 @@ fn main() {
|
|||
{
|
||||
std::env::set_var(
|
||||
"PROTOC",
|
||||
"../../rtss-proto-msg/protoc/protoc-27.4-win64/bin/protoc.exe",
|
||||
"../../rtsa-proto-msg/protoc/protoc-27.4-win64/bin/protoc.exe",
|
||||
);
|
||||
}
|
||||
Config::new()
|
||||
|
@ -39,11 +39,12 @@ fn main() {
|
|||
)
|
||||
.compile_protos(
|
||||
&[
|
||||
"../../rtss-proto-msg/src/em_data.proto",
|
||||
"../../rtss-proto-msg/src/common.proto",
|
||||
"../../rtss-proto-msg/src/iscs_graphic_data.proto",
|
||||
"../../rtsa-proto-msg/src/em_data.proto",
|
||||
"../../rtsa-proto-msg/src/common.proto",
|
||||
"../../rtsa-proto-msg/src/iscs_graphic_data.proto",
|
||||
"../../rtsa-proto-msg/src/simulation.proto",
|
||||
],
|
||||
&["../../rtss-proto-msg/src/"],
|
||||
&["../../rtsa-proto-msg/src/"],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -51,5 +52,5 @@ fn main() {
|
|||
Command::new("cargo")
|
||||
.args(["fmt"])
|
||||
.status()
|
||||
.expect("Failed to run cargo fmt on rtss-dto");
|
||||
.expect("Failed to run cargo fmt on rtsa-dto");
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
mod 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,662 @@
|
|||
// This file is @generated by prost-build.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct IscsGraphicStorage {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub cctv_of_equipment_layout_storages: ::prost::alloc::vec::Vec<
|
||||
CctvOfEquipmentLayoutStorage,
|
||||
>,
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
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)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct UniqueIdOfStationLayout {
|
||||
/// 城市
|
||||
#[prost(string, tag = "1")]
|
||||
pub city: ::prost::alloc::string::String,
|
||||
/// 线路号
|
||||
#[prost(string, tag = "2")]
|
||||
pub line_id: ::prost::alloc::string::String,
|
||||
/// 地图的公里标主坐标系
|
||||
#[prost(string, tag = "3")]
|
||||
pub main_coordinate_system: ::prost::alloc::string::String,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[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 {
|
||||
#[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>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Text {
|
||||
#[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 content: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "4")]
|
||||
pub color: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag = "5")]
|
||||
pub font_size: i32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Rect {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||
#[prost(string, tag = "2")]
|
||||
pub code: ::prost::alloc::string::String,
|
||||
/// 线宽
|
||||
#[prost(int32, tag = "3")]
|
||||
pub line_width: i32,
|
||||
/// 线色
|
||||
#[prost(string, tag = "4")]
|
||||
pub line_color: ::prost::alloc::string::String,
|
||||
/// 宽度
|
||||
#[prost(float, tag = "5")]
|
||||
pub width: f32,
|
||||
/// 高度
|
||||
#[prost(float, tag = "6")]
|
||||
pub height: f32,
|
||||
/// 圆角半径
|
||||
#[prost(int32, tag = "7")]
|
||||
pub radius: i32,
|
||||
/// 画第一个点的坐标
|
||||
#[prost(message, optional, tag = "8")]
|
||||
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,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
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")]
|
||||
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||
#[prost(string, tag = "2")]
|
||||
pub code: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "3")]
|
||||
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,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[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(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
::prost::Enumeration
|
||||
)]
|
||||
#[repr(i32)]
|
||||
pub enum ButtonType {
|
||||
/// 没有Icon
|
||||
NoIcon = 0,
|
||||
/// 监控方形
|
||||
CctvRect = 1,
|
||||
/// 监控样子的按钮
|
||||
CctvMonitor = 2,
|
||||
/// 半圆样子的按钮
|
||||
CctvSemicircle = 3,
|
||||
}
|
||||
impl ButtonType {
|
||||
/// 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 {
|
||||
ButtonType::NoIcon => "noIcon",
|
||||
ButtonType::CctvRect => "cctvRect",
|
||||
ButtonType::CctvMonitor => "cctvMonitor",
|
||||
ButtonType::CctvSemicircle => "cctvSemicircle",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"noIcon" => Some(Self::NoIcon),
|
||||
"cctvRect" => Some(Self::CctvRect),
|
||||
"cctvMonitor" => Some(Self::CctvMonitor),
|
||||
"cctvSemicircle" => Some(Self::CctvSemicircle),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 手动报警按钮
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ManualAlarmButton {
|
||||
#[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 HydrantAlarmButton {
|
||||
#[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 GasExtinguishing {
|
||||
#[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 SmokeDetector {
|
||||
#[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 TemperatureDetector {
|
||||
#[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 BasOfEscalatorStorage {
|
||||
#[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(message, repeated, tag = "4")]
|
||||
pub escalators: ::prost::alloc::vec::Vec<Escalator>,
|
||||
#[prost(message, repeated, tag = "5")]
|
||||
pub vertical_elevators: ::prost::alloc::vec::Vec<VerticalElevator>,
|
||||
}
|
||||
/// 自动扶梯
|
||||
#[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)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CctvOfEquipmentLayoutStorage {
|
||||
#[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(enumeration = "cctv_of_equipment_layout_storage::LayerType", tag = "4")]
|
||||
pub layer: i32,
|
||||
}
|
||||
/// 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 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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "rtss_log"
|
||||
name = "rtsa_log"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "rtsa_mqtt"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rumqttc = { version = "0.24.0", features = ["url"] }
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
async-trait = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tower = { workspace = true }
|
||||
|
||||
rtsa_log = { path = "../rtsa_log" }
|
|
@ -0,0 +1,16 @@
|
|||
use rumqttc::v5::ClientError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MqttClientError {
|
||||
#[error("未知的Mqtt客户端错误")]
|
||||
Unknown,
|
||||
#[error("客户端已设置")]
|
||||
AlreadySet,
|
||||
#[error("rumqttc 错误: {0}")]
|
||||
ClientError(#[from] ClientError),
|
||||
#[error("全局客户端未设置")]
|
||||
NoClient,
|
||||
#[error("请求超时")]
|
||||
RequestTimeout,
|
||||
}
|
|
@ -0,0 +1,512 @@
|
|||
use core::panic;
|
||||
use std::{
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
task::Waker,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use bytes::Bytes;
|
||||
use lazy_static::lazy_static;
|
||||
use rtsa_log::tracing::{error, info, trace};
|
||||
use rumqttc::{
|
||||
v5::{
|
||||
mqttbytes::{
|
||||
v5::{Packet, Publish, PublishProperties},
|
||||
QoS,
|
||||
},
|
||||
AsyncClient, Event, EventLoop, MqttOptions,
|
||||
},
|
||||
Outgoing,
|
||||
};
|
||||
use service::{Handler, MqttRequest, MqttRouter};
|
||||
use tokio::{sync::oneshot, time::timeout};
|
||||
|
||||
mod error;
|
||||
use error::MqttClientError;
|
||||
mod service;
|
||||
|
||||
lazy_static! {
|
||||
/// 全局静态MqttClient实例
|
||||
/// 使用注意事项:
|
||||
/// 每次订阅/发布/请求时都通过get_global_mqtt_client获取新的实例,否则可能会出现死锁
|
||||
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实例
|
||||
pub async fn set_global_mqtt_client(client: MqttClient) -> Result<(), MqttClientError> {
|
||||
let mut mqtt_client = MQTT_CLIENT.lock().await;
|
||||
if mqtt_client.is_some() {
|
||||
return Err(MqttClientError::AlreadySet);
|
||||
}
|
||||
*mqtt_client = Some(client);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 获取全局MqttClient实例
|
||||
pub async fn get_global_mqtt_client() -> MqttClient {
|
||||
let mqtt_client = MQTT_CLIENT.lock().await;
|
||||
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 {
|
||||
id: String,
|
||||
options: MqttOptions,
|
||||
/// mqtt客户端请求队列的最大容量
|
||||
max_cap: usize,
|
||||
request_timeout: Duration,
|
||||
}
|
||||
|
||||
impl MqttClientOptions {
|
||||
pub fn new(id: &str, url: &str) -> Self {
|
||||
Self {
|
||||
id: id.to_string(),
|
||||
options: MqttOptions::parse_url(format!("{}?client_id={}", url, id))
|
||||
.expect("解析mqtt url失败"),
|
||||
max_cap: 30,
|
||||
request_timeout: Duration::from_secs(5),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn set_credentials(mut self, username: &str, password: &str) -> Self {
|
||||
self.options.set_credentials(username, password);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> MqttClient {
|
||||
self.options.set_keep_alive(Duration::from_secs(10));
|
||||
let (client, eventloop) = AsyncClient::new(self.options.clone(), self.max_cap);
|
||||
|
||||
let cli = MqttClient {
|
||||
id: self.id.clone(),
|
||||
request_timeout: self.request_timeout,
|
||||
client,
|
||||
request_id: Arc::new(AtomicU64::new(0)),
|
||||
router: MqttRouter::new(),
|
||||
};
|
||||
cli.run_async(eventloop);
|
||||
cli
|
||||
}
|
||||
}
|
||||
|
||||
/// MQTT客户端
|
||||
/// id: 客户端ID,从数据库的id序列中获取
|
||||
/// 客户端具有的功能:
|
||||
/// 1. 启动
|
||||
/// 2. 订阅
|
||||
/// 3. 发布
|
||||
/// 4. 实现类似http的请求相应功能
|
||||
/// 5. 断开连接
|
||||
#[derive(Clone)]
|
||||
pub struct MqttClient {
|
||||
id: String,
|
||||
/// 全局的请求超时时间
|
||||
request_timeout: Duration,
|
||||
client: AsyncClient,
|
||||
request_id: Arc<AtomicU64>,
|
||||
router: MqttRouter,
|
||||
}
|
||||
|
||||
impl MqttClient {
|
||||
pub async fn clear(&self) -> Result<(), MqttClientError> {
|
||||
self.client.disconnect().await?;
|
||||
// 清空订阅处理器
|
||||
self.router.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub async fn publish(
|
||||
&self,
|
||||
topic: &str,
|
||||
qos: QoS,
|
||||
payload: Vec<u8>,
|
||||
) -> Result<(), MqttClientError> {
|
||||
self.client.publish(topic, qos, false, payload).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_route<H>(&self, topic: &str, handler: H, qos: QoS)
|
||||
where
|
||||
H: Handler + Send + Sync + 'static,
|
||||
{
|
||||
self.client.subscribe(topic, qos).await.unwrap();
|
||||
self.router.add_route(topic, handler);
|
||||
}
|
||||
|
||||
pub async fn remove_route(&self, topic: &str) {
|
||||
self.client.unsubscribe(topic).await.unwrap();
|
||||
self.router.remove_route(topic);
|
||||
}
|
||||
|
||||
pub fn next_request_id(&self) -> u64 {
|
||||
self.request_id.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub async fn handle_request(
|
||||
&self,
|
||||
req: Request,
|
||||
timeout: Duration,
|
||||
) -> Result<Response, MqttClientError> {
|
||||
// 订阅响应主题
|
||||
let response_topic = format!("{}/{}/resp/{}", self.id, req.topic, self.next_request_id());
|
||||
// 创建请求future
|
||||
let response_future = MqttResponseFuture::new(&response_topic, timeout);
|
||||
// 添加响应处理器
|
||||
self.add_route(&response_topic, response_future.clone(), QoS::ExactlyOnce)
|
||||
.await;
|
||||
// 发布请求
|
||||
let property = PublishProperties {
|
||||
response_topic: Some(response_topic.clone()),
|
||||
..req.properties.unwrap_or_default()
|
||||
};
|
||||
self.client
|
||||
.publish_with_properties(req.topic, req.qos, false, req.payload, property)
|
||||
.await?;
|
||||
// 等待响应
|
||||
let resp = response_future.await;
|
||||
// 注销响应处理器并取消订阅
|
||||
self.remove_route(&response_topic).await;
|
||||
if resp.is_timeout() {
|
||||
return Err(MqttClientError::RequestTimeout);
|
||||
}
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
/// 发送请求并等待响应
|
||||
pub async fn request(&self, req: Request) -> Result<Response, MqttClientError> {
|
||||
self.handle_request(req, self.request_timeout).await
|
||||
}
|
||||
|
||||
/// 发送请求并等待响应,指定响应超时时间
|
||||
/// 响应超时时间为0时表示永不超时
|
||||
pub async fn request_with_timeout(
|
||||
&self,
|
||||
req: Request,
|
||||
timeout: Duration,
|
||||
) -> Result<Response, MqttClientError> {
|
||||
self.handle_request(req, timeout).await
|
||||
}
|
||||
|
||||
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 {
|
||||
match notification {
|
||||
Event::Incoming(Packet::Publish(publish)) => {
|
||||
trace!("Received message: {:?}", publish);
|
||||
let this = self.clone();
|
||||
let router = self.router.clone();
|
||||
tokio::spawn(async move {
|
||||
let response_topic = publish
|
||||
.properties
|
||||
.clone()
|
||||
.and_then(|p| p.response_topic.clone());
|
||||
if let Some(resp) = router.handle_request(MqttRequest::new(publish)).await {
|
||||
if let Some(r_topic) = response_topic {
|
||||
this.publish(&r_topic, QoS::AtMostOnce, resp.payload.to_vec())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Event::Outgoing(Outgoing::Disconnect) => {
|
||||
info!("Disconnected to the broker");
|
||||
break;
|
||||
}
|
||||
Event::Incoming(Packet::Disconnect(disconnect)) => {
|
||||
info!("Disconnected from the broker: {:?}", disconnect);
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
trace!("Unhandled event: {:?}", notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
topic: String,
|
||||
qos: QoS,
|
||||
payload: Bytes,
|
||||
properties: Option<PublishProperties>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn new(topic: &str, payload: Bytes) -> Self {
|
||||
Self {
|
||||
topic: topic.to_string(),
|
||||
qos: QoS::AtMostOnce,
|
||||
payload,
|
||||
properties: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_qos(mut self, qos: QoS) -> Self {
|
||||
self.qos = qos;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_properties(mut self, properties: PublishProperties) -> Self {
|
||||
self.properties = Some(properties);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum MqttResponseState {
|
||||
Waiting,
|
||||
Received,
|
||||
Timeout,
|
||||
}
|
||||
|
||||
/// MQTT请求响应
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Response {
|
||||
state: Arc<Mutex<MqttResponseState>>,
|
||||
response: Arc<Mutex<Publish>>,
|
||||
}
|
||||
|
||||
impl Default for Response {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: Arc::new(Mutex::new(MqttResponseState::Waiting)),
|
||||
response: Arc::new(Mutex::new(Publish::default())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_waiting(&self) -> bool {
|
||||
*self.state.lock().unwrap() == MqttResponseState::Waiting
|
||||
}
|
||||
|
||||
pub fn is_received(&self) -> bool {
|
||||
*self.state.lock().unwrap() == MqttResponseState::Received
|
||||
}
|
||||
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
*self.state.lock().unwrap() == MqttResponseState::Timeout
|
||||
}
|
||||
|
||||
pub fn set_timeout(&self) {
|
||||
*self.state.lock().unwrap() = MqttResponseState::Timeout;
|
||||
}
|
||||
|
||||
pub fn set(&self, response: Publish) {
|
||||
*self.state.lock().unwrap() = MqttResponseState::Received;
|
||||
*self.response.lock().unwrap() = response;
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Publish {
|
||||
self.response.lock().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// MQTT响应Future
|
||||
#[derive(Clone)]
|
||||
pub struct MqttResponseFuture {
|
||||
pub start_time: std::time::Instant,
|
||||
timeout: Duration,
|
||||
tx: Arc<Mutex<Option<oneshot::Sender<()>>>>,
|
||||
waker: Arc<Mutex<Option<Waker>>>,
|
||||
response_topic: String,
|
||||
response: Response,
|
||||
}
|
||||
|
||||
impl MqttResponseFuture {
|
||||
pub fn new(response_topic: &str, timeout: Duration) -> Self {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let r = Self {
|
||||
start_time: std::time::Instant::now(),
|
||||
timeout,
|
||||
tx: Arc::new(Mutex::new(Some(tx))),
|
||||
waker: Arc::new(Mutex::new(None)),
|
||||
response_topic: response_topic.to_string(),
|
||||
response: Response::new(),
|
||||
};
|
||||
// 启动超时检查
|
||||
r.start_timeout_monitor(rx);
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
/// 启动超时监控任务逻辑
|
||||
fn start_timeout_monitor(&self, rx: oneshot::Receiver<()>) {
|
||||
if self.timeout.as_millis() == 0 {
|
||||
return;
|
||||
}
|
||||
let response = self.response.clone();
|
||||
let response_topic = self.response_topic.clone();
|
||||
let duration = self.timeout;
|
||||
let waker = self.waker.clone();
|
||||
tokio::spawn(async move {
|
||||
if (timeout(duration, rx).await).is_err() {
|
||||
error!("Mqtt response timeout: {:?}", response_topic);
|
||||
response.set_timeout();
|
||||
if let Some(waker) = waker.lock().unwrap().take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for MqttResponseFuture {
|
||||
fn handle(
|
||||
&self,
|
||||
req: MqttRequest,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Option<service::MqttResponse>> + Send>>
|
||||
{
|
||||
let topic = req.topic();
|
||||
trace!("MqttResponseFuture handle: {:?}", topic);
|
||||
if topic == self.response_topic {
|
||||
self.response.set(req.get());
|
||||
if let Some(tx) = self.tx.lock().unwrap().take() {
|
||||
tx.send(())
|
||||
.expect("Send Mqtt response timeout signal failed");
|
||||
}
|
||||
if let Some(waker) = self.waker.lock().unwrap().take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
Box::pin(async { None })
|
||||
}
|
||||
}
|
||||
|
||||
impl std::future::Future for MqttResponseFuture {
|
||||
type Output = Response;
|
||||
|
||||
fn poll(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Self::Output> {
|
||||
if self.response.is_waiting() {
|
||||
trace!(
|
||||
"topic={} Response future poll waiting...",
|
||||
self.response_topic
|
||||
);
|
||||
self.waker.lock().unwrap().replace(cx.waker().clone());
|
||||
std::task::Poll::Pending
|
||||
} else {
|
||||
trace!(
|
||||
"topic={} Response future poll ready: {:?}",
|
||||
self.response_topic,
|
||||
self.response
|
||||
);
|
||||
std::task::Poll::Ready(self.response.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rtsa_log::tracing::Level;
|
||||
|
||||
use tokio::time::Duration;
|
||||
|
||||
fn create_mqtt_options() -> MqttClientOptions {
|
||||
MqttClientOptions::new("rtsa_test1", "tcp://localhost:1883")
|
||||
.set_credentials("rtsa", "Joylink@0503")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mqtt_client_initialization() {
|
||||
let options = create_mqtt_options();
|
||||
let result = init_global_mqtt_client(options).await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mqtt_client_publish() {
|
||||
let options = create_mqtt_options();
|
||||
init_global_mqtt_client(options).await.unwrap();
|
||||
let mqtt_client = get_global_mqtt_client().await;
|
||||
let result = mqtt_client
|
||||
.publish("test/topic", QoS::AtLeastOnce, b"Hello, MQTT!".to_vec())
|
||||
.await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mqtt_client_request_response() {
|
||||
rtsa_log::Logging::default().with_level(Level::TRACE).init();
|
||||
let options = create_mqtt_options();
|
||||
init_global_mqtt_client(options).await.unwrap();
|
||||
let mqtt_client = get_global_mqtt_client().await;
|
||||
|
||||
struct EchoHandler;
|
||||
impl Handler for EchoHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
req: MqttRequest,
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = Option<service::MqttResponse>> + Send>,
|
||||
> {
|
||||
let payload = req.payload();
|
||||
Box::pin(async move { Some(service::MqttResponse::new(payload)) })
|
||||
}
|
||||
}
|
||||
mqtt_client
|
||||
.add_route("test/echo", EchoHandler, QoS::AtLeastOnce)
|
||||
.await;
|
||||
|
||||
let request = Request::new("test/echo", Bytes::from("Echo message"));
|
||||
let response = mqtt_client.request(request).await.unwrap();
|
||||
assert_eq!(response.get().payload, Bytes::from("Echo message"));
|
||||
|
||||
mqtt_client.remove_route("test/echo").await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mqtt_client_timeout() {
|
||||
let options = create_mqtt_options();
|
||||
init_global_mqtt_client(options).await.unwrap();
|
||||
let mqtt_client = get_global_mqtt_client().await;
|
||||
|
||||
let request =
|
||||
Request::new("test/timeout", Bytes::from("Timeout test")).with_qos(QoS::ExactlyOnce);
|
||||
let result = mqtt_client
|
||||
.request_with_timeout(request, Duration::from_secs(1))
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use bytes::Bytes;
|
||||
use rumqttc::v5::mqttbytes::v5::{Publish, PublishProperties};
|
||||
|
||||
pub struct MqttRequest {
|
||||
publish: Publish,
|
||||
}
|
||||
|
||||
impl MqttRequest {
|
||||
pub fn new(publish: Publish) -> Self {
|
||||
MqttRequest { publish }
|
||||
}
|
||||
|
||||
pub fn topic(&self) -> String {
|
||||
String::from_utf8_lossy(&self.publish.topic).to_string()
|
||||
}
|
||||
|
||||
pub fn payload(&self) -> Bytes {
|
||||
self.publish.payload.clone()
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Publish {
|
||||
self.publish.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MqttResponse {
|
||||
pub properties: Option<PublishProperties>,
|
||||
pub payload: Bytes,
|
||||
}
|
||||
|
||||
impl MqttResponse {
|
||||
pub fn new(payload: Bytes) -> Self {
|
||||
MqttResponse {
|
||||
properties: None,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_properties(payload: Bytes, properties: PublishProperties) -> Self {
|
||||
MqttResponse {
|
||||
properties: Some(properties),
|
||||
payload,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Handler: Send + Sync + 'static {
|
||||
fn handle(
|
||||
&self,
|
||||
req: MqttRequest,
|
||||
) -> Pin<Box<dyn Future<Output = Option<MqttResponse>> + Send>>;
|
||||
}
|
||||
|
||||
impl<F, Fut> Handler for F
|
||||
where
|
||||
F: Fn(MqttRequest) -> Fut + Send + Sync + 'static,
|
||||
Fut: Future<Output = Option<MqttResponse>> + Send + 'static,
|
||||
{
|
||||
fn handle(
|
||||
&self,
|
||||
req: MqttRequest,
|
||||
) -> Pin<Box<dyn Future<Output = Option<MqttResponse>> + Send>> {
|
||||
Box::pin((self)(req))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MqttRouter {
|
||||
routes: Arc<Mutex<HashMap<String, Arc<dyn Handler + Send + Sync>>>>,
|
||||
}
|
||||
|
||||
impl MqttRouter {
|
||||
pub fn new() -> Self {
|
||||
MqttRouter {
|
||||
routes: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_route<H>(&self, topic: &str, handler: H)
|
||||
where
|
||||
H: Handler + Send + Sync + 'static,
|
||||
{
|
||||
self.routes
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(topic.to_string(), Arc::new(handler));
|
||||
}
|
||||
|
||||
pub fn remove_route(&self, topic: &str) {
|
||||
self.routes.lock().unwrap().remove(topic);
|
||||
}
|
||||
|
||||
fn get_handler(&self, topic: &str) -> Option<Arc<dyn Handler + Send + Sync>> {
|
||||
let routes = self.routes.lock().unwrap();
|
||||
routes.get(topic).cloned()
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.routes.lock().unwrap().clear();
|
||||
}
|
||||
|
||||
pub async fn handle_request(&self, req: MqttRequest) -> Option<MqttResponse> {
|
||||
if let Some(handler) = self.get_handler(&req.topic()) {
|
||||
handler.handle(req).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use bytes::Bytes;
|
||||
use rumqttc::v5::mqttbytes::v5::Publish;
|
||||
|
||||
// Helper function to create a Publish message
|
||||
fn create_publish(topic: &str, payload: &[u8]) -> Publish {
|
||||
Publish {
|
||||
topic: topic.as_bytes().to_vec().into(),
|
||||
payload: Bytes::from(payload.to_vec()),
|
||||
dup: false,
|
||||
qos: rumqttc::v5::mqttbytes::QoS::AtMostOnce,
|
||||
retain: false,
|
||||
pkid: 0,
|
||||
properties: None,
|
||||
}
|
||||
}
|
||||
|
||||
// Sample handler that echoes the payload back
|
||||
async fn echo_handler(req: MqttRequest) -> Option<MqttResponse> {
|
||||
Some(MqttResponse {
|
||||
properties: None,
|
||||
payload: req.payload(),
|
||||
})
|
||||
}
|
||||
|
||||
// Sample handler that returns None
|
||||
async fn none_handler(_req: MqttRequest) -> Option<MqttResponse> {
|
||||
None
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_add_and_get_handler() {
|
||||
let router = MqttRouter::new();
|
||||
router.add_route("test/topic", echo_handler);
|
||||
|
||||
assert!(router.get_handler("test/topic").is_some());
|
||||
assert!(router.get_handler("invalid/topic").is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_request_with_existing_route() {
|
||||
let router = MqttRouter::new();
|
||||
router.add_route("test/topic", echo_handler);
|
||||
|
||||
let publish = create_publish("test/topic", b"hello");
|
||||
let req = MqttRequest::new(publish);
|
||||
|
||||
let response = router.handle_request(req).await;
|
||||
|
||||
assert!(response.is_some());
|
||||
assert_eq!(response.unwrap().payload, Bytes::from("hello"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_request_with_nonexistent_route() {
|
||||
let router = MqttRouter::new();
|
||||
|
||||
let publish = create_publish("invalid/topic", b"hello");
|
||||
let req = MqttRequest::new(publish);
|
||||
|
||||
let response = router.handle_request(req).await;
|
||||
|
||||
assert!(response.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handler_returning_none() {
|
||||
let router = MqttRouter::new();
|
||||
router.add_route("test/topic", none_handler);
|
||||
|
||||
let publish = create_publish("test/topic", b"hello");
|
||||
let req = MqttRequest::new(publish);
|
||||
|
||||
let response = router.handle_request(req).await;
|
||||
|
||||
assert!(response.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_multiple_routes() {
|
||||
let router = MqttRouter::new();
|
||||
router.add_route("topic/one", echo_handler);
|
||||
router.add_route("topic/two", none_handler);
|
||||
|
||||
let publish_one = create_publish("topic/one", b"payload1");
|
||||
let req_one = MqttRequest::new(publish_one);
|
||||
let response_one = router.handle_request(req_one).await;
|
||||
assert!(response_one.is_some());
|
||||
assert_eq!(response_one.unwrap().payload, Bytes::from("payload1"));
|
||||
|
||||
let publish_two = create_publish("topic/two", b"payload2");
|
||||
let req_two = MqttRequest::new(publish_two);
|
||||
let response_two = router.handle_request(req_two).await;
|
||||
assert!(response_two.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_concurrent_access() {
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Barrier;
|
||||
|
||||
let router = Arc::new(MqttRouter::new());
|
||||
router.add_route("test/topic", echo_handler);
|
||||
|
||||
let barrier = Arc::new(Barrier::new(10));
|
||||
let mut handles = Vec::new();
|
||||
|
||||
for _ in 0..10 {
|
||||
let router_cloned = router.clone();
|
||||
let barrier_cloned = barrier.clone();
|
||||
|
||||
let handle = tokio::spawn(async move {
|
||||
let publish = create_publish("test/topic", b"concurrent");
|
||||
let req = MqttRequest::new(publish);
|
||||
|
||||
barrier_cloned.wait().await;
|
||||
let response = router_cloned.handle_request(req).await;
|
||||
assert!(response.is_some());
|
||||
assert_eq!(response.unwrap().payload, Bytes::from("concurrent"));
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
use async_graphql::{
|
||||
dataloader::DataLoader, ComplexObject, Context, InputObject, Object, SimpleObject,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use rtss_db::{FeatureAccessor, RtssDbAccessor};
|
||||
use rtss_dto::common::FeatureType;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
apis::{PageDto, PageQueryDto},
|
||||
loader::RtssDbLoader,
|
||||
};
|
||||
|
||||
use super::user::UserId;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FeatureQuery;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FeatureMutation;
|
||||
|
||||
#[Object]
|
||||
impl FeatureQuery {
|
||||
/// 分页查询特征(系统管理)
|
||||
async fn feature_paging(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
page: PageQueryDto,
|
||||
query: FeatureQueryDto,
|
||||
) -> async_graphql::Result<PageDto<FeatureDto>> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let paging = dba
|
||||
.paging_query_features(page.into(), &query.into())
|
||||
.await?;
|
||||
Ok(paging.into())
|
||||
}
|
||||
|
||||
/// id获取特征
|
||||
async fn feature(&self, ctx: &Context<'_>, id: i32) -> async_graphql::Result<FeatureDto> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let feature = dba.get_feature(id).await?;
|
||||
Ok(feature.into())
|
||||
}
|
||||
|
||||
/// id列表获取特征
|
||||
async fn features(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
ids: Vec<i32>,
|
||||
) -> async_graphql::Result<Vec<FeatureDto>> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let features = dba.get_features(ids.as_slice()).await?;
|
||||
Ok(features.into_iter().map(|f| f.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[Object]
|
||||
impl FeatureMutation {
|
||||
/// 上下架特征
|
||||
async fn publish_feature(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
id: i32,
|
||||
is_published: bool,
|
||||
) -> async_graphql::Result<FeatureDto> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let feature = dba.set_feature_published(id, is_published).await?;
|
||||
Ok(feature.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, InputObject)]
|
||||
pub struct FeatureQueryDto {
|
||||
pub name: Option<String>,
|
||||
pub feature_type: Option<i32>,
|
||||
pub is_published: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<FeatureQueryDto> for rtss_db::FeaturePagingFilter {
|
||||
fn from(value: FeatureQueryDto) -> Self {
|
||||
Self {
|
||||
name: value.name,
|
||||
feature_type: value.feature_type,
|
||||
is_published: value.is_published,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, SimpleObject)]
|
||||
#[graphql(complex)]
|
||||
pub struct FeatureDto {
|
||||
pub id: i32,
|
||||
pub feature_type: FeatureType,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub config: Value,
|
||||
pub is_published: bool,
|
||||
pub creator_id: i32,
|
||||
pub updater_id: i32,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[ComplexObject]
|
||||
impl FeatureDto {
|
||||
/// 创建用户name
|
||||
async fn creator_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.creator_id)).await?;
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
/// 更新用户name
|
||||
async fn updater_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.updater_id)).await?;
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rtss_db::model::FeatureModel> for FeatureDto {
|
||||
fn from(value: rtss_db::model::FeatureModel) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
feature_type: value.feature_type,
|
||||
name: value.name,
|
||||
description: value.description,
|
||||
config: value.config,
|
||||
is_published: value.is_published,
|
||||
creator_id: value.creator_id,
|
||||
updater_id: value.updater_id,
|
||||
created_at: value.created_at.naive_local(),
|
||||
updated_at: value.updated_at.naive_local(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/// 数据库加载器
|
||||
pub struct RtssDbLoader {
|
||||
pub(crate) db_accessor: rtss_db::RtssDbAccessor,
|
||||
}
|
||||
|
||||
impl RtssDbLoader {
|
||||
pub fn new(db_accessor: rtss_db::RtssDbAccessor) -> Self {
|
||||
Self { db_accessor }
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
[package]
|
||||
name = "rtss_ci"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
|
@ -1,14 +0,0 @@
|
|||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
[package]
|
||||
name = "rtss_common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bevy_ecs = {workspace = true}
|
|
@ -1,76 +0,0 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use bevy_ecs::{component::Component, entity::Entity, system::Resource};
|
||||
|
||||
/// 仿真公共资源
|
||||
pub struct SimulationResource {
|
||||
id: String,
|
||||
uid_entity_mapping: HashMap<String, Entity>,
|
||||
}
|
||||
|
||||
impl SimulationResource {
|
||||
pub fn new(id: String) -> Self {
|
||||
SimulationResource {
|
||||
id,
|
||||
uid_entity_mapping: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn get_entity(&self, uid: &str) -> Option<Entity> {
|
||||
self.uid_entity_mapping.get(uid).cloned()
|
||||
}
|
||||
|
||||
pub fn insert_entity(&mut self, uid: String, entity: Entity) {
|
||||
self.uid_entity_mapping.insert(uid, entity);
|
||||
}
|
||||
}
|
||||
|
||||
// 设备编号组件
|
||||
#[derive(Component, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Uid(pub String);
|
||||
impl Default for Uid {
|
||||
fn default() -> Self {
|
||||
Uid("".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct SharedSimulationResource(pub Arc<Mutex<SimulationResource>>);
|
||||
|
||||
impl SharedSimulationResource {
|
||||
pub fn get_entity(&self, uid: &str) -> Option<Entity> {
|
||||
self.0.lock().unwrap().uid_entity_mapping.get(uid).cloned()
|
||||
}
|
||||
|
||||
pub fn insert_entity(&self, uid: String, entity: Entity) {
|
||||
self.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.uid_entity_mapping
|
||||
.insert(uid, entity);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bevy_ecs::world;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let mut simulation_resource = SimulationResource::new("1".to_string());
|
||||
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));
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
mod draft_data;
|
||||
pub use draft_data::*;
|
||||
mod release_data;
|
||||
pub use release_data::*;
|
||||
mod user;
|
||||
pub use user::*;
|
||||
mod feature;
|
||||
pub use feature::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtssDbAccessor {
|
||||
pool: sqlx::PgPool,
|
||||
}
|
||||
|
||||
impl RtssDbAccessor {
|
||||
pub fn new(pool: sqlx::PgPool) -> Self {
|
||||
RtssDbAccessor { pool }
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_db_accessor(url: &str) -> RtssDbAccessor {
|
||||
let pool = sqlx::PgPool::connect(url).await.expect("连接数据库失败");
|
||||
RtssDbAccessor::new(pool)
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
mod pb;
|
||||
|
||||
pub use pb::*;
|
|
@ -1,205 +0,0 @@
|
|||
// This file is @generated by prost-build.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct IscsGraphicStorage {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub cctv_of_station_control_storages: ::prost::alloc::vec::Vec<
|
||||
CctvOfStationControlStorage,
|
||||
>,
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
pub fas_platform_alarm_storages: ::prost::alloc::vec::Vec<FasPlatformAlarmStorage>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct UniqueIdOfStationLayout {
|
||||
/// 城市
|
||||
#[prost(string, tag = "1")]
|
||||
pub city: ::prost::alloc::string::String,
|
||||
/// 线路号
|
||||
#[prost(string, tag = "2")]
|
||||
pub line_id: ::prost::alloc::string::String,
|
||||
/// 地图的公里标主坐标系
|
||||
#[prost(string, tag = "3")]
|
||||
pub main_coordinate_system: ::prost::alloc::string::String,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Arrow {
|
||||
#[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>,
|
||||
}
|
||||
/// Iscs文字
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct IscsText {
|
||||
#[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 content: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "4")]
|
||||
pub color: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag = "5")]
|
||||
pub font_size: i32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Rect {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub common: ::core::option::Option<super::common::CommonInfo>,
|
||||
#[prost(string, tag = "2")]
|
||||
pub code: ::prost::alloc::string::String,
|
||||
/// 线宽
|
||||
#[prost(int32, tag = "3")]
|
||||
pub line_width: i32,
|
||||
/// 线色
|
||||
#[prost(string, tag = "4")]
|
||||
pub line_color: ::prost::alloc::string::String,
|
||||
/// 宽度
|
||||
#[prost(float, tag = "5")]
|
||||
pub width: f32,
|
||||
/// 高度
|
||||
#[prost(float, tag = "6")]
|
||||
pub height: f32,
|
||||
/// 圆角半径
|
||||
#[prost(int32, tag = "7")]
|
||||
pub radius: i32,
|
||||
/// 画第一个点的坐标
|
||||
#[prost(message, optional, tag = "8")]
|
||||
pub point: ::core::option::Option<super::common::Point>,
|
||||
}
|
||||
/// CCTV按钮
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CctvButton {
|
||||
#[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 = "cctv_button::ButtonType", tag = "3")]
|
||||
pub button_type: i32,
|
||||
}
|
||||
/// Nested message and enum types in `CCTVButton`.
|
||||
pub mod cctv_button {
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
::prost::Enumeration
|
||||
)]
|
||||
#[repr(i32)]
|
||||
pub enum ButtonType {
|
||||
Rect = 0,
|
||||
/// 监控样子的按钮
|
||||
Monitor = 1,
|
||||
/// 半圆样子的按钮
|
||||
Semicircle = 2,
|
||||
}
|
||||
impl ButtonType {
|
||||
/// 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 {
|
||||
ButtonType::Rect => "rect",
|
||||
ButtonType::Monitor => "monitor",
|
||||
ButtonType::Semicircle => "semicircle",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"rect" => Some(Self::Rect),
|
||||
"monitor" => Some(Self::Monitor),
|
||||
"semicircle" => Some(Self::Semicircle),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 手动报警按钮
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ManualAlarmButton {
|
||||
#[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 HydrantAlarmButton {
|
||||
#[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 GasExtinguishing {
|
||||
#[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 SmokeDetector {
|
||||
#[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 TemperatureDetector {
|
||||
#[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 CctvOfStationControlStorage {
|
||||
#[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, repeated, tag = "3")]
|
||||
pub arrows: ::prost::alloc::vec::Vec<Arrow>,
|
||||
#[prost(message, repeated, tag = "4")]
|
||||
pub iscs_texts: ::prost::alloc::vec::Vec<IscsText>,
|
||||
#[prost(message, repeated, tag = "5")]
|
||||
pub rects: ::prost::alloc::vec::Vec<Rect>,
|
||||
#[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 FasPlatformAlarmStorage {
|
||||
#[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, repeated, tag = "3")]
|
||||
pub arrows: ::prost::alloc::vec::Vec<Arrow>,
|
||||
#[prost(message, repeated, tag = "4")]
|
||||
pub iscs_texts: ::prost::alloc::vec::Vec<IscsText>,
|
||||
#[prost(message, repeated, tag = "5")]
|
||||
pub rects: ::prost::alloc::vec::Vec<Rect>,
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
[package]
|
||||
name = "rtss_iscs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
|
@ -1,14 +0,0 @@
|
|||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
[package]
|
||||
name = "rtss_sim_manage"
|
||||
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}
|
||||
|
||||
rtss_log = { path = "../rtss_log" }
|
||||
rtss_common = { path = "../rtss_common" }
|
||||
rtss_trackside = { path = "../rtss_trackside" }
|
|
@ -1,17 +0,0 @@
|
|||
use bevy_app::App;
|
||||
use rtss_trackside::TrackSideEquipmentPlugin;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AvailablePlugins {
|
||||
TrackSideEquipmentPlugin,
|
||||
}
|
||||
|
||||
pub(crate) fn add_needed_plugins(app: &mut App, plugins: Vec<AvailablePlugins>) {
|
||||
for plugin in plugins {
|
||||
match plugin {
|
||||
AvailablePlugins::TrackSideEquipmentPlugin => {
|
||||
app.add_plugins(TrackSideEquipmentPlugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
mod config_plugins;
|
||||
mod simulation;
|
||||
pub use config_plugins::*;
|
||||
pub use simulation::*;
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
|
@ -1,468 +0,0 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
ops::Deref,
|
||||
sync::{mpsc, Arc, Mutex},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use bevy_app::{prelude::*, PluginsState};
|
||||
use bevy_ecs::{
|
||||
event::{Event, EventWriter},
|
||||
observer::Trigger,
|
||||
system::{Query, Res, ResMut, Resource},
|
||||
world::OnAdd,
|
||||
};
|
||||
use bevy_time::{prelude::*, TimePlugin};
|
||||
use rtss_common::{SharedSimulationResource, SimulationResource, Uid};
|
||||
use rtss_log::tracing::{debug, error, warn};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{add_needed_plugins, AvailablePlugins};
|
||||
|
||||
/// 仿真管理器
|
||||
/// 非线程安全,若需要线程安全请使用类似 `Arc<Mutex<SimulationManager>>` 的方式
|
||||
pub struct SimulationManager {
|
||||
txs: RefCell<HashMap<String, Simulation>>,
|
||||
}
|
||||
|
||||
impl Default for SimulationManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SimulationControlError {
|
||||
#[error("Unknown error")]
|
||||
UnknownError,
|
||||
#[error("Simulation not exist")]
|
||||
SimulationNotExist,
|
||||
#[error("Trigger event failed")]
|
||||
TriggerEventFailed,
|
||||
#[error("Simulation entity not exist")]
|
||||
SimulationEntityNotExist,
|
||||
}
|
||||
|
||||
impl SimulationManager {
|
||||
fn new() -> Self {
|
||||
let txs = RefCell::new(HashMap::new());
|
||||
SimulationManager { txs }
|
||||
}
|
||||
|
||||
pub fn count(&self) -> usize {
|
||||
self.txs.borrow().len()
|
||||
}
|
||||
|
||||
pub fn start_simulation(
|
||||
&self,
|
||||
builder: SimulationBuilder,
|
||||
) -> Result<String, SimulationControlError> {
|
||||
let id = builder.id.clone();
|
||||
let sim = Simulation::new(builder);
|
||||
self.txs.borrow_mut().insert(id.clone(), sim);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn exit_simulation(&self, id: String) -> Result<(), SimulationControlError> {
|
||||
match self.txs.borrow_mut().remove(&id) {
|
||||
Some(sim) => sim.exit_simulation(),
|
||||
None => {
|
||||
warn!("Simulation not exist, id={}", id);
|
||||
Err(SimulationControlError::SimulationNotExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pause_simulation(&self, id: String) -> Result<(), SimulationControlError> {
|
||||
match self.txs.borrow().get(&id) {
|
||||
Some(sim) => sim.pause_simulation(),
|
||||
None => {
|
||||
warn!("Simulation not exist, id={}", id);
|
||||
Err(SimulationControlError::SimulationNotExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resume_simulation(&self, id: String) -> Result<(), SimulationControlError> {
|
||||
match self.txs.borrow().get(&id) {
|
||||
Some(sim) => sim.resume_simulation(),
|
||||
None => {
|
||||
warn!("Simulation not exist, id={}", id);
|
||||
Err(SimulationControlError::SimulationNotExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_simulation_speed(
|
||||
&self,
|
||||
id: String,
|
||||
speed: f32,
|
||||
) -> Result<(), SimulationControlError> {
|
||||
match self.txs.borrow().get(&id) {
|
||||
Some(sim) => sim.update_simulation_speed(speed),
|
||||
None => {
|
||||
warn!("Simulation not exist, id={}", id);
|
||||
Err(SimulationControlError::SimulationNotExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trigger_operation<E>(&self, id: String, event: E) -> Result<(), SimulationControlError>
|
||||
where
|
||||
E: Event + Copy,
|
||||
{
|
||||
match self.txs.borrow().get(&id) {
|
||||
Some(sim) => sim.trigger_operation(event),
|
||||
None => {
|
||||
warn!("Simulation not exist, id={}", id);
|
||||
Err(SimulationControlError::SimulationNotExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trigger_entity_operation<E>(
|
||||
&self,
|
||||
id: String,
|
||||
entity_uid: String,
|
||||
event: E,
|
||||
) -> Result<(), SimulationControlError>
|
||||
where
|
||||
E: Event + Copy,
|
||||
{
|
||||
match self.txs.borrow().get(&id) {
|
||||
Some(sim) => sim.trigger_entity_operation(entity_uid, event),
|
||||
None => {
|
||||
warn!("Simulation not exist, id={}", id);
|
||||
Err(SimulationControlError::SimulationNotExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SimulationBuilder {
|
||||
/// 仿真ID
|
||||
pub(crate) id: String,
|
||||
/// 仿真主逻辑循环间隔,详细请查看 [`Time<Fixed>`](bevy_time::fixed::Fixed)
|
||||
pub(crate) loop_duration: Duration,
|
||||
/// 仿真所需插件
|
||||
pub(crate) plugins: Vec<AvailablePlugins>,
|
||||
}
|
||||
|
||||
impl Default for SimulationBuilder {
|
||||
fn default() -> Self {
|
||||
SimulationBuilder {
|
||||
id: "default".to_string(),
|
||||
loop_duration: Duration::from_millis(500),
|
||||
plugins: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SimulationBuilder {
|
||||
pub fn id(mut self, id: String) -> Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn loop_duration(mut self, loop_duration: Duration) -> Self {
|
||||
self.loop_duration = loop_duration;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn plugins(mut self, plugins: Vec<AvailablePlugins>) -> Self {
|
||||
self.plugins = plugins;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Debug)]
|
||||
pub struct SimulationId(String);
|
||||
|
||||
impl SimulationId {
|
||||
pub fn new(id: String) -> Self {
|
||||
SimulationId(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SimulationId {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Debug)]
|
||||
pub struct SimulationStatus {
|
||||
// 仿真倍速
|
||||
pub speed: f32,
|
||||
// 仿真是否暂停状态
|
||||
pub paused: bool,
|
||||
}
|
||||
|
||||
impl Default for SimulationStatus {
|
||||
fn default() -> Self {
|
||||
SimulationStatus {
|
||||
speed: 1.0,
|
||||
paused: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 仿真控制事件
|
||||
#[derive(Event, Debug, Clone, Copy)]
|
||||
pub enum SimulationControlEvent {
|
||||
Pause,
|
||||
Unpause,
|
||||
UpdateSpeed(f32),
|
||||
Exit,
|
||||
}
|
||||
|
||||
pub struct Simulation {
|
||||
tx: mpsc::Sender<Box<SimulationHandle>>,
|
||||
resource: Arc<Mutex<SimulationResource>>,
|
||||
}
|
||||
|
||||
pub type SimulationHandle = dyn FnMut(&mut App) + Send;
|
||||
|
||||
impl Simulation {
|
||||
pub fn new(builder: SimulationBuilder) -> Self {
|
||||
let simulation_resource = Arc::new(Mutex::new(SimulationResource::new(builder.id.clone())));
|
||||
let cloned_resource = Arc::clone(&simulation_resource);
|
||||
|
||||
let (tx, mut rx) = mpsc::channel();
|
||||
|
||||
rayon::spawn(move || {
|
||||
let mut app = App::new();
|
||||
|
||||
let mut virtual_time =
|
||||
Time::<Virtual>::from_max_delta(builder.loop_duration.mul_f32(2f32));
|
||||
virtual_time.pause();
|
||||
// 初始化仿真App
|
||||
app.add_plugins(TimePlugin)
|
||||
.insert_resource(virtual_time)
|
||||
.insert_resource(Time::<Fixed>::from_duration(builder.loop_duration))
|
||||
.insert_resource(SimulationId::new(builder.id))
|
||||
.insert_resource(SimulationStatus::default())
|
||||
.insert_resource(SharedSimulationResource(Arc::clone(&cloned_resource)))
|
||||
.add_event::<SimulationControlEvent>()
|
||||
.observe(simulation_status_control)
|
||||
.observe(entity_observer);
|
||||
// 添加仿真所需插件
|
||||
add_needed_plugins(&mut app, builder.plugins);
|
||||
|
||||
let wait = Some(builder.loop_duration);
|
||||
app.set_runner(move |mut app: App| {
|
||||
let plugins_state = app.plugins_state();
|
||||
if plugins_state != PluginsState::Cleaned {
|
||||
app.finish();
|
||||
app.cleanup();
|
||||
}
|
||||
|
||||
loop {
|
||||
match runner(&mut app, wait, &mut rx) {
|
||||
Ok(Some(delay)) => std::thread::sleep(delay),
|
||||
Ok(None) => continue,
|
||||
Err(exit) => return exit,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.run();
|
||||
});
|
||||
Simulation {
|
||||
tx,
|
||||
resource: simulation_resource,
|
||||
}
|
||||
}
|
||||
|
||||
fn trigger_event(&self, event: SimulationControlEvent) -> Result<(), SimulationControlError> {
|
||||
let id = self.resource.lock().unwrap().id().to_string();
|
||||
let result = self.tx.send(Box::new(move |app: &mut App| {
|
||||
app.world_mut().trigger(event);
|
||||
}));
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to send event to simulation, id={}, error={:?}",
|
||||
id, e
|
||||
);
|
||||
Err(SimulationControlError::TriggerEventFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trigger_operation<E>(&self, event: E) -> Result<(), SimulationControlError>
|
||||
where
|
||||
E: Event + Copy,
|
||||
{
|
||||
let id = self.resource.lock().unwrap().id().to_string();
|
||||
let result = self.tx.send(Box::new(move |app: &mut App| {
|
||||
app.world_mut().trigger(event);
|
||||
}));
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to send event to simulation, id={}, error={:?}",
|
||||
id, e
|
||||
);
|
||||
Err(SimulationControlError::TriggerEventFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trigger_entity_operation<E>(
|
||||
&self,
|
||||
entity_uid: String,
|
||||
event: E,
|
||||
) -> Result<(), SimulationControlError>
|
||||
where
|
||||
E: Event + Copy,
|
||||
{
|
||||
let id = self.resource.lock().unwrap().id().to_string();
|
||||
match self.resource.lock().unwrap().get_entity(&entity_uid) {
|
||||
Some(entity) => {
|
||||
let result = self.tx.send(Box::new(move |app: &mut App| {
|
||||
app.world_mut().trigger_targets(event, entity);
|
||||
}));
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to send event to simulation, id={}, error={:?}",
|
||||
id, e
|
||||
);
|
||||
Err(SimulationControlError::TriggerEventFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
error!("Entity not exist, id={}", entity_uid);
|
||||
Err(SimulationControlError::SimulationEntityNotExist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit_simulation(&self) -> Result<(), SimulationControlError> {
|
||||
self.trigger_event(SimulationControlEvent::Exit)
|
||||
}
|
||||
|
||||
pub fn pause_simulation(&self) -> Result<(), SimulationControlError> {
|
||||
self.trigger_event(SimulationControlEvent::Pause)
|
||||
}
|
||||
|
||||
pub fn resume_simulation(&self) -> Result<(), SimulationControlError> {
|
||||
self.trigger_event(SimulationControlEvent::Unpause)
|
||||
}
|
||||
|
||||
pub fn update_simulation_speed(&self, speed: f32) -> Result<(), SimulationControlError> {
|
||||
self.trigger_event(SimulationControlEvent::UpdateSpeed(speed))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entity_observer(
|
||||
trigger: Trigger<OnAdd>,
|
||||
query: Query<&Uid>,
|
||||
shared: ResMut<SharedSimulationResource>,
|
||||
) {
|
||||
let entity = trigger.entity();
|
||||
match query.get(entity) {
|
||||
Ok(uid) => {
|
||||
shared.insert_entity(uid.0.clone(), entity);
|
||||
debug!("添加uid实体映射, Uid: {:?}, Entity: {:?}", uid, entity);
|
||||
}
|
||||
Err(_) => {
|
||||
warn!("Failed to get Uid from entity: {:?}", entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulation_status_control(
|
||||
trigger: Trigger<SimulationControlEvent>,
|
||||
mut time: ResMut<Time<Virtual>>,
|
||||
sid: Res<SimulationId>,
|
||||
mut exit: EventWriter<AppExit>,
|
||||
) {
|
||||
match trigger.event() {
|
||||
SimulationControlEvent::Pause => {
|
||||
debug!("Pausing simulation");
|
||||
time.pause();
|
||||
}
|
||||
SimulationControlEvent::Unpause => {
|
||||
debug!("Unpausing simulation");
|
||||
time.unpause();
|
||||
}
|
||||
SimulationControlEvent::UpdateSpeed(speed) => {
|
||||
debug!("Update simulation speed to {}", speed);
|
||||
time.set_relative_speed(*speed);
|
||||
}
|
||||
SimulationControlEvent::Exit => {
|
||||
debug!("Exiting simulation, id={:?}", *sid);
|
||||
exit.send(AppExit::Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn runner(
|
||||
app: &mut App,
|
||||
wait: Option<Duration>,
|
||||
rx: &mut mpsc::Receiver<Box<SimulationHandle>>,
|
||||
) -> Result<Option<Duration>, AppExit> {
|
||||
let start_time = Instant::now();
|
||||
|
||||
if let Err(e) = rx.try_recv().map(|mut handle| handle(app)) {
|
||||
match e {
|
||||
mpsc::TryRecvError::Empty => {}
|
||||
mpsc::TryRecvError::Disconnected => {
|
||||
error!("Simulation handle channel disconnected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.update();
|
||||
|
||||
if let Some(exit) = app.should_exit() {
|
||||
return Err(exit);
|
||||
};
|
||||
|
||||
let end_time = Instant::now();
|
||||
|
||||
if let Some(wait) = wait {
|
||||
let exe_time = end_time - start_time;
|
||||
if exe_time < wait {
|
||||
return Ok(Some(wait - exe_time));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_simulation_manager() {
|
||||
let manager = SimulationManager::default();
|
||||
assert_eq!(manager.count(), 0);
|
||||
|
||||
if let Ok(_) = manager.start_simulation(SimulationBuilder::default().id("0".to_string())) {
|
||||
assert_eq!(manager.count(), 1);
|
||||
}
|
||||
|
||||
if let Ok(_) = manager.start_simulation(SimulationBuilder::default().id("1".to_string())) {
|
||||
assert_eq!(manager.count(), 2);
|
||||
}
|
||||
|
||||
if let Ok(_) = manager.exit_simulation("0".to_string()) {
|
||||
assert_eq!(manager.count(), 1);
|
||||
}
|
||||
|
||||
if let Ok(_) = manager.exit_simulation("1".to_string()) {
|
||||
assert_eq!(manager.count(), 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "rtss_trackside"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bevy_core = {workspace = true}
|
||||
bevy_ecs = {workspace = true}
|
||||
bevy_app = {workspace = true}
|
||||
bevy_time = {workspace = true}
|
||||
|
||||
rtss_log = { path = "../rtss_log" }
|
||||
rtss_common = { path = "../rtss_common" }
|
|
@ -1,10 +0,0 @@
|
|||
mod components;
|
||||
mod events;
|
||||
mod plugin;
|
||||
mod resources;
|
||||
mod systems;
|
||||
pub use components::*;
|
||||
pub use events::*;
|
||||
pub use plugin::*;
|
||||
pub use resources::*;
|
||||
pub use systems::*;
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
|
||||
rtsa_log = { path = "../crates/rtsa_log" }
|
||||
rtsa_api = { path = "crates/rtsa_api" }
|
||||
rtsa_db = { path = "../crates/rtsa_db" }
|
||||
serde = { workspace = true }
|
||||
config = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
enum_dispatch = { workspace = true }
|
||||
anyhow = { workspace = true }
|
|
@ -11,11 +11,11 @@ ENV TZ=Asia/Shanghai
|
|||
# 复制时区信息到系统时区目录
|
||||
RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /rtss_sim
|
||||
COPY ./target/x86_64-unknown-linux-musl/release/rtss_simulation ./rtss_sim
|
||||
WORKDIR /rtsa
|
||||
COPY ./target/x86_64-unknown-linux-musl/release/manager ./rtsa_m
|
||||
COPY ./conf/* ./conf/
|
||||
COPY ./migrations/* ./migrations/
|
||||
|
||||
EXPOSE 8765
|
||||
|
||||
CMD ["sh", "-c", "./rtss_sim db migrate && ./rtss_sim serve"]
|
||||
CMD ["sh", "-c", "./rtsa_m db migrate && ./rtsa_m serve"]
|
|
@ -2,4 +2,4 @@
|
|||
url = "postgresql://joylink:Joylink@0503@localhost:5432/joylink"
|
||||
|
||||
[sso]
|
||||
base_url = "http://192.168.33.233/rtss-server"
|
||||
base_url = "http://192.168.33.233/rtsa-server"
|
|
@ -5,4 +5,4 @@ url = "postgresql://joylink:Joylink@0503@10.11.11.2:5432/joylink"
|
|||
level = "debug"
|
||||
|
||||
[sso]
|
||||
base_url = "http://192.168.33.233/rtss-server"
|
||||
base_url = "http://192.168.33.233/rtsa-server"
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "rtss_api"
|
||||
name = "rtsa_api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@ -19,9 +19,6 @@ base64 = "0.22.1"
|
|||
sysinfo = "0.31.3"
|
||||
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls", "json"] }
|
||||
|
||||
bevy_ecs = { workspace = true }
|
||||
rtss_log = { path = "../rtss_log" }
|
||||
rtss_sim_manage = { path = "../rtss_sim_manage" }
|
||||
rtss_trackside = { path = "../rtss_trackside" }
|
||||
rtss_db = { path = "../rtss_db" }
|
||||
rtss_dto = { path = "../rtss_dto" }
|
||||
rtsa_log = { path = "../../../crates/rtsa_log" }
|
||||
rtsa_db = { path = "../../../crates/rtsa_db" }
|
||||
rtsa_dto = { path = "../../../crates/rtsa_dto" }
|
|
@ -1,6 +1,6 @@
|
|||
use async_graphql::{InputObject, InputType, OutputType, SimpleObject};
|
||||
use rtss_db::{common::TableColumn, model::DraftDataColumn};
|
||||
use rtss_dto::common::IscsStyle;
|
||||
use rtsa_db::{common::TableColumn, model::DraftDataColumn};
|
||||
use rtsa_dto::common::IscsStyle;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
|
@ -10,7 +10,7 @@ pub trait DataOptions: InputType + OutputType + Serialize + DeserializeOwned {
|
|||
|
||||
impl DataOptions for Value {
|
||||
fn to_data_options_filter_clause(&self) -> String {
|
||||
format!("options @> '{}'", self)
|
||||
format!("{} @> '{}'", DraftDataColumn::Options.name(), self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_iscs_data_options_serialize() {
|
||||
rtss_log::Logging::default().init();
|
||||
rtsa_log::Logging::default().init();
|
||||
let options = IscsDataOptions {
|
||||
style: IscsStyle::DaShiZhiNeng,
|
||||
};
|
|
@ -3,15 +3,15 @@ use async_graphql::{ComplexObject, Context, InputObject, Object, SimpleObject};
|
|||
use base64::prelude::*;
|
||||
use base64::Engine;
|
||||
use chrono::NaiveDateTime;
|
||||
use rtss_db::DraftDataAccessor;
|
||||
use rtss_db::RtssDbAccessor;
|
||||
use rtss_dto::common::{DataType, Role};
|
||||
use rtsa_db::DraftDataAccessor;
|
||||
use rtsa_db::RtsaDbAccessor;
|
||||
use rtsa_dto::common::{DataType, Role};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::apis::{PageDto, PageQueryDto};
|
||||
use crate::loader::RtssDbLoader;
|
||||
use crate::loader::RtsaDbLoader;
|
||||
|
||||
use super::common::{DataOptions, IscsDataOptions};
|
||||
use super::data_options_def::{DataOptions, IscsDataOptions};
|
||||
use super::release_data::ReleaseDataId;
|
||||
use super::user::UserId;
|
||||
|
||||
|
@ -33,7 +33,7 @@ impl DraftDataQuery {
|
|||
paging: PageQueryDto,
|
||||
query: DraftDataFilterDto<Value>,
|
||||
) -> async_graphql::Result<PageDto<DraftDataDto>> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let paging_result = db_accessor
|
||||
.paging_query_draft_data(query.into(), paging.into())
|
||||
.await?;
|
||||
|
@ -53,7 +53,7 @@ impl DraftDataQuery {
|
|||
.await?;
|
||||
query.user_id = user.id_i32();
|
||||
query.data_type = Some(DataType::Iscs);
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let paging_result = db_accessor
|
||||
.paging_query_draft_data(query.into(), paging.into())
|
||||
.await?;
|
||||
|
@ -67,7 +67,7 @@ impl DraftDataQuery {
|
|||
paging: PageQueryDto,
|
||||
mut query: DraftDataFilterDto<IscsDataOptions>,
|
||||
) -> async_graphql::Result<PageDto<DraftIscsDataDto>> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
query.data_type = Some(DataType::Iscs);
|
||||
let paging_result = db_accessor
|
||||
.paging_query_draft_data(query.into(), paging.into())
|
||||
|
@ -77,7 +77,7 @@ impl DraftDataQuery {
|
|||
/// 根据id获取草稿数据
|
||||
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
||||
async fn draft_data(&self, ctx: &Context<'_>, id: i32) -> async_graphql::Result<DraftDataDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let draft_data = db_accessor.query_draft_data_by_id(id).await?;
|
||||
Ok(draft_data.into())
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ impl DraftDataQuery {
|
|||
.query_user(&ctx.data::<Token>()?.0)
|
||||
.await?;
|
||||
let user_id = user.id_i32();
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let exist = db_accessor
|
||||
.is_draft_data_exist(user_id, &data_type, &name)
|
||||
.await?;
|
||||
|
@ -115,8 +115,8 @@ impl DraftDataMutation {
|
|||
.data::<UserAuthCache>()?
|
||||
.query_user(&ctx.data::<Token>()?.0)
|
||||
.await?;
|
||||
input = input.with_user_id(user.id_i32());
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
input = input.with_data_type_and_user_id(DataType::Iscs, user.id_i32());
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let draft_data = db_accessor.create_draft_data(input.into()).await?;
|
||||
Ok(draft_data.into())
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ impl DraftDataMutation {
|
|||
id: i32,
|
||||
name: String,
|
||||
) -> async_graphql::Result<DraftDataDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let draft_data = db_accessor.update_draft_data_name(id, &name).await?;
|
||||
Ok(draft_data.into())
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ impl DraftDataMutation {
|
|||
id: i32,
|
||||
data: String, // base64编码的数据
|
||||
) -> async_graphql::Result<DraftDataDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let bytes = BASE64_STANDARD
|
||||
.decode(data)
|
||||
.map_err(|e| async_graphql::Error::new(format!("base64 decode error: {}", e)))?;
|
||||
|
@ -156,7 +156,7 @@ impl DraftDataMutation {
|
|||
id: i32,
|
||||
is_shared: bool,
|
||||
) -> async_graphql::Result<DraftDataDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let draft_data = db_accessor.set_draft_data_shared(id, is_shared).await?;
|
||||
Ok(draft_data.into())
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ impl DraftDataMutation {
|
|||
ctx: &Context<'_>,
|
||||
id: Vec<i32>,
|
||||
) -> async_graphql::Result<bool> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
db_accessor.delete_draft_data(id.as_slice()).await?;
|
||||
Ok(true)
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ impl DraftDataMutation {
|
|||
id: i32,
|
||||
release_data_id: i32,
|
||||
) -> async_graphql::Result<DraftDataDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let draft_data = db_accessor
|
||||
.set_default_release_data_id(id, release_data_id)
|
||||
.await?;
|
||||
|
@ -198,7 +198,7 @@ impl DraftDataMutation {
|
|||
.query_user(&ctx.data::<Token>()?.0)
|
||||
.await?;
|
||||
let user_id = user.id_i32();
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let draft_data = db_accessor.save_as_new_draft(id, &name, user_id).await?;
|
||||
Ok(draft_data.into())
|
||||
}
|
||||
|
@ -207,6 +207,8 @@ impl DraftDataMutation {
|
|||
#[derive(Debug, InputObject)]
|
||||
#[graphql(concrete(name = "CreateDraftIscsDto", params(IscsDataOptions)))]
|
||||
pub struct CreateDraftDataDto<T: DataOptions> {
|
||||
#[graphql(skip)]
|
||||
pub data_type: Option<DataType>,
|
||||
pub name: String,
|
||||
pub options: Option<T>,
|
||||
#[graphql(skip)]
|
||||
|
@ -214,17 +216,18 @@ pub struct CreateDraftDataDto<T: DataOptions> {
|
|||
}
|
||||
|
||||
impl<T: DataOptions> CreateDraftDataDto<T> {
|
||||
pub fn with_user_id(mut self, id: i32) -> Self {
|
||||
pub fn with_data_type_and_user_id(mut self, data_type: DataType, id: i32) -> Self {
|
||||
self.data_type = Some(data_type);
|
||||
self.user_id = Some(id);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DataOptions> From<CreateDraftDataDto<T>> for rtss_db::CreateDraftData {
|
||||
impl<T: DataOptions> From<CreateDraftDataDto<T>> for rtsa_db::CreateDraftData {
|
||||
fn from(value: CreateDraftDataDto<T>) -> Self {
|
||||
let cdd = Self::new(
|
||||
&value.name,
|
||||
DataType::Iscs,
|
||||
value.data_type.expect("need data_type"),
|
||||
value.user_id.expect("CreateDraftDataDto need user_id"),
|
||||
);
|
||||
if value.options.is_some() {
|
||||
|
@ -243,12 +246,12 @@ pub struct UserDraftDataFilterDto<T: DataOptions> {
|
|||
pub user_id: i32,
|
||||
pub name: Option<String>,
|
||||
/// 数据类型,在某个具体类型查询时不传,传了也不生效
|
||||
pub data_type: Option<rtss_dto::common::DataType>,
|
||||
pub data_type: Option<rtsa_dto::common::DataType>,
|
||||
pub options: Option<T>,
|
||||
pub is_shared: Option<bool>,
|
||||
}
|
||||
|
||||
impl<T: DataOptions> From<UserDraftDataFilterDto<T>> for rtss_db::DraftDataQuery {
|
||||
impl<T: DataOptions> From<UserDraftDataFilterDto<T>> for rtsa_db::DraftDataQuery {
|
||||
fn from(value: UserDraftDataFilterDto<T>) -> Self {
|
||||
Self {
|
||||
user_id: Some(value.user_id),
|
||||
|
@ -274,7 +277,7 @@ pub struct DraftDataFilterDto<T: DataOptions> {
|
|||
pub options: Option<T>,
|
||||
}
|
||||
|
||||
impl<T: DataOptions> From<DraftDataFilterDto<T>> for rtss_db::DraftDataQuery {
|
||||
impl<T: DataOptions> From<DraftDataFilterDto<T>> for rtsa_db::DraftDataQuery {
|
||||
fn from(value: DraftDataFilterDto<T>) -> Self {
|
||||
Self {
|
||||
user_id: value.user_id,
|
||||
|
@ -310,7 +313,7 @@ impl DraftDataDto {
|
|||
ctx: &Context<'_>,
|
||||
) -> async_graphql::Result<Option<String>> {
|
||||
if let Some(version_id) = self.default_release_data_id {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtsaDbLoader>>();
|
||||
let name = loader.load_one(ReleaseDataId::new(version_id)).await?;
|
||||
Ok(name)
|
||||
} else {
|
||||
|
@ -320,14 +323,14 @@ impl DraftDataDto {
|
|||
|
||||
/// 获取用户name
|
||||
async fn user_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtsaDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.user_id)).await?;
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rtss_db::model::DraftDataModel> for DraftDataDto {
|
||||
fn from(value: rtss_db::model::DraftDataModel) -> Self {
|
||||
impl From<rtsa_db::model::DraftDataModel> for DraftDataDto {
|
||||
fn from(value: rtsa_db::model::DraftDataModel) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
|
@ -351,8 +354,8 @@ pub struct DraftIscsDataDto {
|
|||
pub options: Option<IscsDataOptions>,
|
||||
}
|
||||
|
||||
impl From<rtss_db::model::DraftDataModel> for DraftIscsDataDto {
|
||||
fn from(value: rtss_db::model::DraftDataModel) -> Self {
|
||||
impl From<rtsa_db::model::DraftDataModel> for DraftIscsDataDto {
|
||||
fn from(value: rtsa_db::model::DraftDataModel) -> Self {
|
||||
Self {
|
||||
options: value
|
||||
.options
|
|
@ -0,0 +1,228 @@
|
|||
use crate::{
|
||||
apis::{PageDto, PageQueryDto},
|
||||
loader::RtsaDbLoader,
|
||||
user_auth::{RoleGuard, Token, UserAuthCache},
|
||||
};
|
||||
use async_graphql::{
|
||||
dataloader::DataLoader, ComplexObject, Context, InputObject, Object, SimpleObject,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use rtsa_db::{CreateFeature, FeatureAccessor, RtsaDbAccessor, UpdateFeature};
|
||||
use rtsa_dto::common::FeatureType;
|
||||
use rtsa_dto::common::Role;
|
||||
use serde_json::Value;
|
||||
|
||||
use super::{
|
||||
feature_config_def::{FeatureConfig, UrFeatureConfig},
|
||||
user::UserId,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FeatureQuery;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FeatureMutation;
|
||||
|
||||
#[Object]
|
||||
impl FeatureQuery {
|
||||
/// 分页查询功能feature(系统管理)
|
||||
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
|
||||
async fn feature_paging(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
page: PageQueryDto,
|
||||
query: FeatureQueryDto,
|
||||
) -> async_graphql::Result<PageDto<FeatureDto>> {
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
let paging = dba
|
||||
.paging_query_features(page.into(), &query.into())
|
||||
.await?;
|
||||
Ok(paging.into())
|
||||
}
|
||||
|
||||
/// id获取功能feature
|
||||
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
||||
async fn feature(&self, ctx: &Context<'_>, id: i32) -> async_graphql::Result<FeatureDto> {
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
let feature = dba.get_feature(id).await?;
|
||||
Ok(feature.into())
|
||||
}
|
||||
|
||||
/// id列表获取功能feature列表
|
||||
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
||||
async fn features(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
ids: Vec<i32>,
|
||||
) -> async_graphql::Result<Vec<FeatureDto>> {
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
let features = dba.get_features(ids.as_slice()).await?;
|
||||
Ok(features.into_iter().map(|f| f.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[Object]
|
||||
impl FeatureMutation {
|
||||
/// 上下架功能feature
|
||||
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
|
||||
async fn publish_feature(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
id: i32,
|
||||
is_published: bool,
|
||||
) -> async_graphql::Result<FeatureDto> {
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
let feature = dba.set_feature_published(id, is_published).await?;
|
||||
Ok(feature.into())
|
||||
}
|
||||
|
||||
/// 创建城轨仿真功能feature
|
||||
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
|
||||
async fn create_ur_feature(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
mut input: CreateFeatureDto<UrFeatureConfig>,
|
||||
) -> async_graphql::Result<FeatureDto> {
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
let user = ctx
|
||||
.data::<UserAuthCache>()?
|
||||
.query_user(&ctx.data::<Token>()?.0)
|
||||
.await?;
|
||||
input = input.with_feature_type_and_user_id(FeatureType::Ur, user.id_i32());
|
||||
let feature = dba.create_feature(&input.into()).await?;
|
||||
Ok(feature.into())
|
||||
}
|
||||
|
||||
/// 更新城轨仿真功能feature
|
||||
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
|
||||
async fn update_ur_feature(
|
||||
&self,
|
||||
ctx: &Context<'_>,
|
||||
input: UpdateFeatureDto<UrFeatureConfig>,
|
||||
) -> async_graphql::Result<FeatureDto> {
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
let feature = dba.update_feature(&input.into()).await?;
|
||||
Ok(feature.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, InputObject)]
|
||||
#[graphql(concrete(name = "UpdateUrFeatureDto", params(UrFeatureConfig)))]
|
||||
pub struct UpdateFeatureDto<T: FeatureConfig> {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub config: T,
|
||||
#[graphql(skip)]
|
||||
pub user_id: Option<i32>,
|
||||
}
|
||||
|
||||
impl<T: FeatureConfig> From<UpdateFeatureDto<T>> for UpdateFeature {
|
||||
fn from(value: UpdateFeatureDto<T>) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
description: value.description,
|
||||
config: serde_json::to_value(&value.config).expect("config is to_value failed"),
|
||||
updater_id: value.user_id.expect("user_id must be set"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, InputObject)]
|
||||
#[graphql(concrete(name = "CreateUrFeatureDto", params(UrFeatureConfig)))]
|
||||
pub struct CreateFeatureDto<T: FeatureConfig> {
|
||||
#[graphql(skip)]
|
||||
pub feature_type: Option<FeatureType>,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub config: T,
|
||||
#[graphql(skip)]
|
||||
pub user_id: Option<i32>,
|
||||
}
|
||||
|
||||
impl<T: FeatureConfig> From<CreateFeatureDto<T>> for CreateFeature {
|
||||
fn from(value: CreateFeatureDto<T>) -> Self {
|
||||
Self {
|
||||
feature_type: value.feature_type.expect("feature_type must be set"),
|
||||
name: value.name,
|
||||
description: value.description,
|
||||
config: serde_json::to_value(&value.config).expect("config is to_value failed"),
|
||||
creator_id: value.user_id.expect("user_id must be set"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FeatureConfig> CreateFeatureDto<T> {
|
||||
fn with_feature_type_and_user_id(mut self, feature_type: FeatureType, uid: i32) -> Self {
|
||||
self.feature_type = Some(feature_type);
|
||||
self.user_id = Some(uid);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, InputObject)]
|
||||
pub struct FeatureQueryDto {
|
||||
pub name: Option<String>,
|
||||
pub feature_type: Option<i32>,
|
||||
pub is_published: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<FeatureQueryDto> for rtsa_db::FeaturePagingFilter {
|
||||
fn from(value: FeatureQueryDto) -> Self {
|
||||
Self {
|
||||
name: value.name,
|
||||
feature_type: value.feature_type,
|
||||
is_published: value.is_published,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, SimpleObject)]
|
||||
#[graphql(complex)]
|
||||
pub struct FeatureDto {
|
||||
pub id: i32,
|
||||
pub feature_type: FeatureType,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub config: Value,
|
||||
pub is_published: bool,
|
||||
pub creator_id: i32,
|
||||
pub updater_id: i32,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[ComplexObject]
|
||||
impl FeatureDto {
|
||||
/// 创建用户name
|
||||
async fn creator_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtsaDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.creator_id)).await?;
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
/// 更新用户name
|
||||
async fn updater_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtsaDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.updater_id)).await?;
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rtsa_db::model::FeatureModel> for FeatureDto {
|
||||
fn from(value: rtsa_db::model::FeatureModel) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
feature_type: value.feature_type,
|
||||
name: value.name,
|
||||
description: value.description,
|
||||
config: value.config,
|
||||
is_published: value.is_published,
|
||||
creator_id: value.creator_id,
|
||||
updater_id: value.updater_id,
|
||||
created_at: value.created_at.naive_local(),
|
||||
updated_at: value.updated_at.naive_local(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use async_graphql::{InputObject, InputType, OutputType, SimpleObject};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
pub trait FeatureConfig: InputType + OutputType + Serialize + DeserializeOwned {}
|
||||
|
||||
impl FeatureConfig for Value {}
|
||||
|
||||
/// UR功能配置
|
||||
#[derive(Debug, Clone, InputObject, SimpleObject, Serialize, Deserialize)]
|
||||
#[graphql(input_name = "UrFeatureConfigInput")]
|
||||
pub struct UrFeatureConfig {
|
||||
/// 电子地图id
|
||||
pub ems: Vec<i32>,
|
||||
}
|
||||
|
||||
impl FeatureConfig for UrFeatureConfig {}
|
|
@ -3,16 +3,14 @@ use draft_data::{DraftDataMutation, DraftDataQuery};
|
|||
use feature::{FeatureMutation, FeatureQuery};
|
||||
use release_data::{ReleaseDataMutation, ReleaseDataQuery};
|
||||
|
||||
mod simulation_definition;
|
||||
mod sys_info;
|
||||
use simulation_definition::*;
|
||||
use user::{UserMutation, UserQuery};
|
||||
|
||||
mod common;
|
||||
mod data_options_def;
|
||||
mod draft_data;
|
||||
mod feature;
|
||||
mod feature_config_def;
|
||||
mod release_data;
|
||||
mod simulation;
|
||||
mod user;
|
||||
|
||||
#[derive(Default, MergedObject)]
|
||||
|
@ -27,7 +25,7 @@ pub struct Mutation(
|
|||
);
|
||||
|
||||
#[derive(Enum, Copy, Clone, Default, Eq, PartialEq, Debug)]
|
||||
#[graphql(remote = "rtss_db::common::SortOrder")]
|
||||
#[graphql(remote = "rtsa_db::common::SortOrder")]
|
||||
pub enum SortOrder {
|
||||
#[default]
|
||||
Asc,
|
||||
|
@ -42,7 +40,7 @@ pub struct PageQueryDto {
|
|||
pub items_per_page: i32,
|
||||
}
|
||||
|
||||
impl From<PageQueryDto> for rtss_db::common::PageQuery {
|
||||
impl From<PageQueryDto> for rtsa_db::common::PageQuery {
|
||||
fn from(value: PageQueryDto) -> Self {
|
||||
Self {
|
||||
page: value.page,
|
||||
|
@ -76,8 +74,8 @@ impl<T: OutputType> PageDto<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: OutputType, M: Into<T>> From<rtss_db::common::PageResult<M>> for PageDto<T> {
|
||||
fn from(value: rtss_db::common::PageResult<M>) -> Self {
|
||||
impl<T: OutputType, M: Into<T>> From<rtsa_db::common::PageResult<M>> for PageDto<T> {
|
||||
fn from(value: rtsa_db::common::PageResult<M>) -> Self {
|
||||
Self::new(
|
||||
value.total,
|
||||
value.data.into_iter().map(|m| m.into()).collect(),
|
|
@ -5,16 +5,16 @@ use async_graphql::dataloader::*;
|
|||
use async_graphql::{ComplexObject, Context, InputObject, Object, SimpleObject};
|
||||
use base64::prelude::*;
|
||||
use chrono::NaiveDateTime;
|
||||
use rtss_db::model::*;
|
||||
use rtss_db::prelude::*;
|
||||
use rtss_db::{model::ReleaseDataModel, ReleaseDataAccessor, RtssDbAccessor};
|
||||
use rtss_dto::common::{DataType, Role};
|
||||
use rtsa_db::model::*;
|
||||
use rtsa_db::prelude::*;
|
||||
use rtsa_db::{model::ReleaseDataModel, ReleaseDataAccessor, RtsaDbAccessor};
|
||||
use rtsa_dto::common::{DataType, Role};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::apis::draft_data::DraftDataDto;
|
||||
use crate::loader::RtssDbLoader;
|
||||
use crate::loader::RtsaDbLoader;
|
||||
|
||||
use super::common::{DataOptions, IscsDataOptions};
|
||||
use super::data_options_def::{DataOptions, IscsDataOptions};
|
||||
use super::user::UserId;
|
||||
use super::{PageDto, PageQueryDto};
|
||||
|
||||
|
@ -36,7 +36,7 @@ impl ReleaseDataQuery {
|
|||
page: PageQueryDto,
|
||||
query: ReleaseTypedDataFilterDto<Value>,
|
||||
) -> async_graphql::Result<PageDto<ReleaseDataDto>> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let paging = db_accessor
|
||||
.paging_query_release_data_list(query.into(), page.into())
|
||||
.await?;
|
||||
|
@ -51,7 +51,7 @@ impl ReleaseDataQuery {
|
|||
page: PageQueryDto,
|
||||
mut query: ReleaseTypedDataFilterDto<IscsDataOptions>,
|
||||
) -> async_graphql::Result<PageDto<ReleaseIscsDataWithoutVersionDto>> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
query.data_type = Some(DataType::Iscs);
|
||||
let paging = db_accessor
|
||||
.paging_query_release_data_list(query.into(), page.into())
|
||||
|
@ -66,7 +66,7 @@ impl ReleaseDataQuery {
|
|||
ctx: &Context<'_>,
|
||||
id: i32,
|
||||
) -> async_graphql::Result<ReleaseDataWithUsedVersionDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let model = db_accessor.query_release_data_with_used_version(id).await?;
|
||||
Ok(model.into())
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ impl ReleaseDataQuery {
|
|||
data_type: DataType,
|
||||
name: String,
|
||||
) -> async_graphql::Result<bool> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let result = db_accessor
|
||||
.is_release_data_name_exist(data_type, &name)
|
||||
.await?;
|
||||
|
@ -94,7 +94,7 @@ impl ReleaseDataQuery {
|
|||
data_id: i32,
|
||||
page: PageQueryDto,
|
||||
) -> async_graphql::Result<PageDto<ReleaseDataVersionDto>> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let paging = db_accessor
|
||||
.paging_query_release_data_version_list(data_id, page.into())
|
||||
.await?;
|
||||
|
@ -108,7 +108,7 @@ impl ReleaseDataQuery {
|
|||
ctx: &Context<'_>,
|
||||
version_id: i32,
|
||||
) -> async_graphql::Result<ReleaseDataVersionDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let model = db_accessor
|
||||
.query_release_data_version_by_id(version_id)
|
||||
.await?;
|
||||
|
@ -132,7 +132,7 @@ impl ReleaseDataMutation {
|
|||
.query_user(&ctx.data::<Token>()?.0)
|
||||
.await?;
|
||||
let user_id = user.id_i32();
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let result = db_accessor
|
||||
.release_new_from_draft(draft_id, &name, &description, Some(user_id))
|
||||
.await?;
|
||||
|
@ -152,7 +152,7 @@ impl ReleaseDataMutation {
|
|||
.query_user(&ctx.data::<Token>()?.0)
|
||||
.await?;
|
||||
let user_id = user.id_i32();
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let result = db_accessor
|
||||
.release_to_existing(draft_id, &description, Some(user_id))
|
||||
.await?;
|
||||
|
@ -167,7 +167,7 @@ impl ReleaseDataMutation {
|
|||
id: i32,
|
||||
name: String,
|
||||
) -> async_graphql::Result<ReleaseDataDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let result = db_accessor.update_release_data_name(id, &name).await?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ impl ReleaseDataMutation {
|
|||
id: i32,
|
||||
is_published: bool,
|
||||
) -> async_graphql::Result<ReleaseDataDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let result = db_accessor
|
||||
.set_release_data_published(id, is_published)
|
||||
.await?;
|
||||
|
@ -195,7 +195,7 @@ impl ReleaseDataMutation {
|
|||
id: i32,
|
||||
version_id: i32,
|
||||
) -> async_graphql::Result<ReleaseDataDto> {
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let result = db_accessor
|
||||
.set_release_data_used_version(id, version_id)
|
||||
.await?;
|
||||
|
@ -214,7 +214,7 @@ impl ReleaseDataMutation {
|
|||
.query_user(&ctx.data::<Token>()?.0)
|
||||
.await?;
|
||||
let user_id = user.id_i32();
|
||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
||||
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||
let result = db_accessor
|
||||
.create_draft_from_release_version(version_id, user_id)
|
||||
.await?;
|
||||
|
@ -235,7 +235,7 @@ pub struct ReleaseTypedDataFilterDto<T: DataOptions> {
|
|||
pub is_published: Option<bool>,
|
||||
}
|
||||
|
||||
impl<T: DataOptions> From<ReleaseTypedDataFilterDto<T>> for rtss_db::ReleaseDataQuery {
|
||||
impl<T: DataOptions> From<ReleaseTypedDataFilterDto<T>> for rtsa_db::ReleaseDataQuery {
|
||||
fn from(value: ReleaseTypedDataFilterDto<T>) -> Self {
|
||||
Self {
|
||||
name: value.name,
|
||||
|
@ -265,7 +265,7 @@ pub struct ReleaseDataDto {
|
|||
impl ReleaseDataDto {
|
||||
async fn description(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
if let Some(version_id) = self.used_version_id {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtsaDbLoader>>();
|
||||
let description = loader
|
||||
.load_one(ReleaseDataVersionId::new(version_id))
|
||||
.await?;
|
||||
|
@ -277,7 +277,7 @@ impl ReleaseDataDto {
|
|||
|
||||
/// 获取用户name
|
||||
async fn user_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtsaDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.user_id)).await?;
|
||||
Ok(name)
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ impl ReleaseDataId {
|
|||
}
|
||||
}
|
||||
|
||||
impl Loader<ReleaseDataId> for RtssDbLoader {
|
||||
impl Loader<ReleaseDataId> for RtsaDbLoader {
|
||||
type Value = String;
|
||||
type Error = Arc<DbAccessError>;
|
||||
|
||||
|
@ -326,7 +326,7 @@ impl ReleaseDataVersionId {
|
|||
}
|
||||
}
|
||||
|
||||
impl Loader<ReleaseDataVersionId> for RtssDbLoader {
|
||||
impl Loader<ReleaseDataVersionId> for RtsaDbLoader {
|
||||
type Value = String;
|
||||
type Error = Arc<DbAccessError>;
|
||||
|
||||
|
@ -388,7 +388,7 @@ pub struct ReleaseDataVersionDto {
|
|||
impl ReleaseDataVersionDto {
|
||||
/// 获取用户name
|
||||
async fn user_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtssDbLoader>>();
|
||||
let loader = ctx.data_unchecked::<DataLoader<RtsaDbLoader>>();
|
||||
let name = loader.load_one(UserId::new(self.user_id)).await?;
|
||||
Ok(name)
|
||||
}
|
|
@ -2,14 +2,14 @@ use std::{collections::HashMap, sync::Arc};
|
|||
|
||||
use async_graphql::{dataloader::Loader, Context, InputObject, Object, SimpleObject};
|
||||
use chrono::NaiveDateTime;
|
||||
use rtss_db::{DbAccessError, RtssDbAccessor, UserAccessor};
|
||||
use rtsa_db::{DbAccessError, RtsaDbAccessor, UserAccessor};
|
||||
|
||||
use crate::{
|
||||
loader::RtssDbLoader,
|
||||
loader::RtsaDbLoader,
|
||||
user_auth::{build_jwt, Claims, RoleGuard, Token, UserAuthCache, UserInfoDto},
|
||||
UserAuthClient,
|
||||
};
|
||||
use rtss_dto::common::Role;
|
||||
use rtsa_dto::common::Role;
|
||||
|
||||
use super::{PageDto, PageQueryDto};
|
||||
|
||||
|
@ -47,7 +47,7 @@ impl UserQuery {
|
|||
page: PageQueryDto,
|
||||
query: UserQueryDto,
|
||||
) -> async_graphql::Result<PageDto<UserDto>> {
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
let paging = dba.query_user_page(page.into(), query.into()).await?;
|
||||
Ok(paging.into())
|
||||
}
|
||||
|
@ -63,12 +63,12 @@ impl UserMutation {
|
|||
async fn sync_user(&self, ctx: &Context<'_>) -> async_graphql::Result<bool> {
|
||||
let http_client = ctx.data::<UserAuthClient>()?;
|
||||
let users = http_client.query_all_users(ctx.data::<Token>()?).await?;
|
||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
||||
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||
dba.sync_user(
|
||||
users
|
||||
.into_iter()
|
||||
.map(|u| u.into())
|
||||
.collect::<Vec<rtss_db::SyncUserInfo>>()
|
||||
.collect::<Vec<rtsa_db::SyncUserInfo>>()
|
||||
.as_slice(),
|
||||
)
|
||||
.await?;
|
||||
|
@ -85,7 +85,7 @@ pub struct UserQueryDto {
|
|||
pub roles: Option<Vec<Role>>,
|
||||
}
|
||||
|
||||
impl From<UserQueryDto> for rtss_db::UserPageFilter {
|
||||
impl From<UserQueryDto> for rtsa_db::UserPageFilter {
|
||||
fn from(value: UserQueryDto) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
|
@ -122,8 +122,8 @@ impl From<UserInfoDto> for UserDto {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<rtss_db::model::UserModel> for UserDto {
|
||||
fn from(value: rtss_db::model::UserModel) -> Self {
|
||||
impl From<rtsa_db::model::UserModel> for UserDto {
|
||||
fn from(value: rtsa_db::model::UserModel) -> Self {
|
||||
Self {
|
||||
id: value.id,
|
||||
name: value.username,
|
||||
|
@ -147,7 +147,7 @@ impl UserId {
|
|||
}
|
||||
}
|
||||
|
||||
impl Loader<UserId> for RtssDbLoader {
|
||||
impl Loader<UserId> for RtsaDbLoader {
|
||||
type Value = String;
|
||||
type Error = Arc<DbAccessError>;
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
/// 数据库加载器
|
||||
pub struct RtsaDbLoader {
|
||||
pub(crate) db_accessor: rtsa_db::RtsaDbAccessor,
|
||||
}
|
||||
|
||||
impl RtsaDbLoader {
|
||||
pub fn new(db_accessor: rtsa_db::RtsaDbAccessor) -> Self {
|
||||
Self { db_accessor }
|
||||
}
|
||||
}
|
|
@ -10,12 +10,13 @@ use axum::{
|
|||
};
|
||||
use dataloader::DataLoader;
|
||||
use http::{playground_source, GraphQLPlaygroundConfig};
|
||||
use rtss_log::tracing::{debug, info};
|
||||
use rtsa_db::RtsaDbAccessor;
|
||||
use rtsa_log::tracing::{debug, info};
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::cors::CorsLayer;
|
||||
|
||||
use crate::apis::{Mutation, Query};
|
||||
use crate::loader::RtssDbLoader;
|
||||
use crate::loader::RtsaDbLoader;
|
||||
use crate::user_auth;
|
||||
|
||||
pub use crate::user_auth::UserAuthClient;
|
||||
|
@ -47,7 +48,12 @@ impl ServerConfig {
|
|||
}
|
||||
|
||||
pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
|
||||
let schema = new_schema(config.clone()).await;
|
||||
let client = config
|
||||
.user_auth_client
|
||||
.clone()
|
||||
.expect("user auth client not configured");
|
||||
let dba = rtsa_db::get_db_accessor(&config.database_url).await;
|
||||
let schema = new_schema(SchemaOptions::new(client, dba));
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(graphiql).post(graphql_handler))
|
||||
|
@ -70,7 +76,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
|
|||
}
|
||||
|
||||
async fn graphql_handler(
|
||||
State(schema): State<RtssAppSchema>,
|
||||
State(schema): State<RtsaAppSchema>,
|
||||
headers: HeaderMap,
|
||||
req: GraphQLRequest,
|
||||
) -> GraphQLResponse {
|
||||
|
@ -86,20 +92,54 @@ async fn graphiql() -> impl IntoResponse {
|
|||
Html(playground_source(GraphQLPlaygroundConfig::new("/")))
|
||||
}
|
||||
|
||||
pub type RtssAppSchema = Schema<Query, Mutation, EmptySubscription>;
|
||||
pub type RtsaAppSchema = Schema<Query, Mutation, EmptySubscription>;
|
||||
|
||||
pub async fn new_schema(config: ServerConfig) -> RtssAppSchema {
|
||||
let client = config
|
||||
.user_auth_client
|
||||
.expect("user auth client not configured");
|
||||
let user_info_cache = crate::user_auth::UserAuthCache::new(client.clone());
|
||||
let dba = rtss_db::get_db_accessor(&config.database_url).await;
|
||||
let loader = RtssDbLoader::new(dba.clone());
|
||||
pub struct SchemaOptions {
|
||||
pub user_auth_client: UserAuthClient,
|
||||
pub user_info_cache: user_auth::UserAuthCache,
|
||||
pub rtsa_dba: RtsaDbAccessor,
|
||||
}
|
||||
|
||||
impl SchemaOptions {
|
||||
pub fn new(user_auth_client: UserAuthClient, rtsa_dba: RtsaDbAccessor) -> Self {
|
||||
let user_info_cache = user_auth::UserAuthCache::new(user_auth_client.clone());
|
||||
Self {
|
||||
user_auth_client,
|
||||
user_info_cache,
|
||||
rtsa_dba,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_schema(options: SchemaOptions) -> RtsaAppSchema {
|
||||
let loader = RtsaDbLoader::new(options.rtsa_dba.clone());
|
||||
Schema::build(Query::default(), Mutation::default(), EmptySubscription)
|
||||
.data(client)
|
||||
.data(user_info_cache)
|
||||
.data(dba)
|
||||
.data(options.user_auth_client)
|
||||
.data(options.user_info_cache)
|
||||
.data(options.rtsa_dba)
|
||||
.data(DataLoader::new(loader, tokio::spawn))
|
||||
// .data(MutexSimulationManager::default())
|
||||
.finish()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_new_schema() {
|
||||
let dba =
|
||||
rtsa_db::get_db_accessor("postgresql://joylink:Joylink@0503@localhost:5432/joylink")
|
||||
.await;
|
||||
let _ = new_schema(SchemaOptions::new(
|
||||
crate::UserAuthClient {
|
||||
base_url: "".to_string(),
|
||||
login_url: "".to_string(),
|
||||
logout_url: "".to_string(),
|
||||
user_info_url: "".to_string(),
|
||||
sync_user_url: "".to_string(),
|
||||
},
|
||||
dba,
|
||||
));
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_jwt() {
|
||||
rtss_log::Logging::default().init();
|
||||
rtsa_log::Logging::default().init();
|
||||
let claim = Claims::new(5);
|
||||
let jwt = build_jwt(claim).unwrap();
|
||||
println!("jwt: {}", jwt.0);
|
|
@ -6,8 +6,8 @@ use std::{
|
|||
use async_graphql::Guard;
|
||||
use axum::http::HeaderMap;
|
||||
use chrono::{DateTime, Local};
|
||||
use rtss_dto::common::Role;
|
||||
use rtss_log::tracing::error;
|
||||
use rtsa_dto::common::Role;
|
||||
use rtsa_log::tracing::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod jwt_auth;
|
||||
|
@ -225,7 +225,7 @@ fn parse_to_date_time(s: &str) -> chrono::DateTime<Local> {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
impl From<UserInfoDto> for rtss_db::SyncUserInfo {
|
||||
impl From<UserInfoDto> for rtsa_db::SyncUserInfo {
|
||||
fn from(user_info: UserInfoDto) -> Self {
|
||||
Self {
|
||||
id: user_info.id_i32(),
|
||||
|
@ -276,9 +276,6 @@ impl UserAuthCache {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Ok;
|
||||
|
||||
use rtss_log::tracing::Level;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -302,25 +299,25 @@ mod tests {
|
|||
println!("{:?}", dt);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_user_auth_cache() -> anyhow::Result<()> {
|
||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
let client = UserAuthClient {
|
||||
base_url: "http://192.168.33.233/rtss-server".to_string(),
|
||||
login_url: "/api/login".to_string(),
|
||||
logout_url: "/api/login/logout".to_string(),
|
||||
user_info_url: "/api/login/getUserInfo".to_string(),
|
||||
sync_user_url: "/api/userinfo/list/all".to_string(),
|
||||
};
|
||||
let cache = UserAuthCache::new(client.clone());
|
||||
let token = cache.client.login(LoginInfo::default()).await?;
|
||||
let user = cache.query_user(&token).await?;
|
||||
println!("token: {}, {:?}", token, user);
|
||||
assert_eq!(cache.len(), 1);
|
||||
// #[tokio::test]
|
||||
// async fn test_user_auth_cache() -> anyhow::Result<()> {
|
||||
// rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||
// let client = UserAuthClient {
|
||||
// base_url: "http://192.168.33.233/rtsa-server".to_string(),
|
||||
// login_url: "/api/login".to_string(),
|
||||
// logout_url: "/api/login/logout".to_string(),
|
||||
// user_info_url: "/api/login/getUserInfo".to_string(),
|
||||
// sync_user_url: "/api/userinfo/list/all".to_string(),
|
||||
// };
|
||||
// let cache = UserAuthCache::new(client.clone());
|
||||
// let token = cache.client.login(LoginInfo::default()).await?;
|
||||
// let user = cache.query_user(&token).await?;
|
||||
// println!("token: {}, {:?}", token, user);
|
||||
// assert_eq!(cache.len(), 1);
|
||||
|
||||
let user_list = client.query_all_users(&Token(token)).await?;
|
||||
println!("{:?}", user_list);
|
||||
// let user_list = client.query_all_users(&Token(token)).await?;
|
||||
// println!("{:?}", user_list);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
|
@ -20,9 +20,9 @@ pub struct Log {
|
|||
level: String,
|
||||
}
|
||||
|
||||
impl From<Log> for rtss_log::Logging {
|
||||
impl From<Log> for rtsa_log::Logging {
|
||||
fn from(log: Log) -> Self {
|
||||
rtss_log::Logging {
|
||||
rtsa_log::Logging {
|
||||
level: log.level.parse().unwrap(),
|
||||
..Default::default()
|
||||
}
|
||||
|
@ -69,9 +69,9 @@ impl AppConfig {
|
|||
// 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)
|
||||
// Add in settings from the environment (with a prefix of rtsa_SIM)
|
||||
// Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key
|
||||
.add_source(Environment::with_prefix("RTSS_SIM").separator("_"))
|
||||
.add_source(Environment::with_prefix("rtsa_SIM").separator("_"))
|
||||
// You may also programmatically change settings
|
||||
// .set_override("database.url", "postgres://")?
|
||||
// build the configuration
|
|
@ -1,10 +1,12 @@
|
|||
use clap::Parser;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
|
||||
use crate::{app_config, db::DbSubCommand, CmdExecutor};
|
||||
use crate::{app_config, CmdExecutor};
|
||||
|
||||
use super::DbSubCommand;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "rtss-sim", version, author, about, long_about = None)]
|
||||
#[command(name = "rtsa-sim", version, author, about, long_about = None)]
|
||||
pub struct Cmd {
|
||||
#[command(subcommand)]
|
||||
pub cmd: SubCommand,
|
||||
|
@ -29,11 +31,11 @@ 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();
|
||||
let log: rtsa_log::Logging = app_config.log.into();
|
||||
log.init();
|
||||
rtss_api::serve(
|
||||
rtss_api::ServerConfig::new(&app_config.database.url, app_config.server.port)
|
||||
.with_user_auth_client(rtss_api::UserAuthClient {
|
||||
rtsa_api::serve(
|
||||
rtsa_api::ServerConfig::new(&app_config.database.url, app_config.server.port)
|
||||
.with_user_auth_client(rtsa_api::UserAuthClient {
|
||||
base_url: app_config.sso.base_url,
|
||||
login_url: app_config.sso.login_url,
|
||||
logout_url: app_config.sso.logout_url,
|
|
@ -22,6 +22,6 @@ 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
|
||||
rtsa_db::run_migrations(&app_config.database.url).await
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
mod app_config;
|
||||
mod cmd;
|
||||
mod db;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
mod app_config;
|
||||
mod commands;
|
||||
|
||||
pub use commands::*;
|
|
@ -1,5 +1,5 @@
|
|||
use clap::Parser;
|
||||
use rtss_simulation::{Cmd, CmdExecutor};
|
||||
use manager::{Cmd, CmdExecutor};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
|
@ -1 +1 @@
|
|||
DROP SCHEMA rtss CASCADE;
|
||||
DROP SCHEMA rtsa CASCADE;
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
-- 初始化数据库SCHEMA(所有轨道交通信号系统仿真的表、类型等都在rtss SCHEMA下)
|
||||
CREATE SCHEMA rtss;
|
||||
-- 初始化数据库SCHEMA(所有轨道交通信号系统仿真的表、类型等都在rtsa SCHEMA下)
|
||||
CREATE SCHEMA rtsa;
|
||||
|
||||
-- 创建mqtt客户端id序列
|
||||
CREATE SEQUENCE rtsa.mqtt_client_id_seq;
|
||||
|
||||
-- 创建用户表
|
||||
CREATE TABLE
|
||||
rtss.user (
|
||||
rtsa.user (
|
||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||
username VARCHAR(128) NOT NULL, -- 用户名
|
||||
password VARCHAR(128) NOT NULL, -- 密码
|
||||
|
@ -15,40 +18,40 @@ CREATE TABLE
|
|||
);
|
||||
|
||||
-- 创建用户名称索引
|
||||
CREATE INDEX ON rtss.user (username);
|
||||
CREATE INDEX ON rtsa.user (username);
|
||||
|
||||
-- 创建用户邮箱索引
|
||||
CREATE INDEX ON rtss.user (email);
|
||||
CREATE INDEX ON rtsa.user (email);
|
||||
|
||||
-- 创建用户手机号索引
|
||||
CREATE INDEX ON rtss.user (mobile);
|
||||
CREATE INDEX ON rtsa.user (mobile);
|
||||
|
||||
-- 创建用户角色索引
|
||||
CREATE INDEX ON rtss.user USING GIN (roles);
|
||||
CREATE INDEX ON rtsa.user USING GIN (roles);
|
||||
|
||||
-- 注释用户表
|
||||
COMMENT ON TABLE rtss.user IS '用户表';
|
||||
COMMENT ON TABLE rtsa.user IS '用户表';
|
||||
|
||||
-- 注释用户表字段
|
||||
COMMENT ON COLUMN rtss.user.id IS 'id 自增主键';
|
||||
COMMENT ON COLUMN rtsa.user.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.username IS '用户名';
|
||||
COMMENT ON COLUMN rtsa.user.username IS '用户名';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.password IS '密码';
|
||||
COMMENT ON COLUMN rtsa.user.password IS '密码';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.email IS '邮箱';
|
||||
COMMENT ON COLUMN rtsa.user.email IS '邮箱';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.mobile IS '手机号';
|
||||
COMMENT ON COLUMN rtsa.user.mobile IS '手机号';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.roles IS '角色列表';
|
||||
COMMENT ON COLUMN rtsa.user.roles IS '角色列表';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN rtsa.user.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.user.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN rtsa.user.updated_at IS '更新时间';
|
||||
|
||||
-- 创建草稿数据表
|
||||
CREATE TABLE
|
||||
rtss.draft_data (
|
||||
rtsa.draft_data (
|
||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||
name VARCHAR(128) NOT NULL, -- 草稿名称
|
||||
data_type INT NOT NULL, -- 数据类型
|
||||
|
@ -59,44 +62,44 @@ CREATE TABLE
|
|||
is_shared BOOLEAN NOT NULL DEFAULT FALSE, -- 是否共享
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
|
||||
FOREIGN KEY (user_id) REFERENCES rtss.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
FOREIGN KEY (user_id) REFERENCES rtsa.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
UNIQUE (name, data_type, user_id) -- 一个用户的某个类型的草稿名称唯一
|
||||
);
|
||||
|
||||
-- 创建草稿数据用户索引
|
||||
CREATE INDEX ON rtss.draft_data (user_id);
|
||||
CREATE INDEX ON rtsa.draft_data (user_id);
|
||||
|
||||
-- 创建草稿数据类型索引
|
||||
CREATE INDEX ON rtss.draft_data (data_type);
|
||||
CREATE INDEX ON rtsa.draft_data (data_type);
|
||||
|
||||
-- 创建草稿数据配置项索引
|
||||
CREATE INDEX ON rtss.draft_data USING GIN (options);
|
||||
CREATE INDEX ON rtsa.draft_data USING GIN (options);
|
||||
|
||||
-- 注释草稿数据表
|
||||
COMMENT ON TABLE rtss.draft_data IS '草稿数据表';
|
||||
COMMENT ON TABLE rtsa.draft_data IS '草稿数据表';
|
||||
|
||||
-- 注释草稿数据表字段
|
||||
COMMENT ON COLUMN rtss.draft_data.id IS 'id 自增主键';
|
||||
COMMENT ON COLUMN rtsa.draft_data.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.draft_data.name IS '草稿名称';
|
||||
COMMENT ON COLUMN rtsa.draft_data.name IS '草稿名称';
|
||||
|
||||
COMMENT ON COLUMN rtss.draft_data.data_type IS '数据类型';
|
||||
COMMENT ON COLUMN rtsa.draft_data.data_type IS '数据类型';
|
||||
|
||||
COMMENT ON COLUMN rtss.draft_data.options IS '数据相关的参数项或配置项';
|
||||
COMMENT ON COLUMN rtsa.draft_data.options IS '数据相关的参数项或配置项';
|
||||
|
||||
COMMENT ON COLUMN rtss.draft_data.data IS '草稿数据';
|
||||
COMMENT ON COLUMN rtsa.draft_data.data IS '草稿数据';
|
||||
|
||||
COMMENT ON COLUMN rtss.draft_data.user_id IS '创建用户id';
|
||||
COMMENT ON COLUMN rtsa.draft_data.user_id IS '创建用户id';
|
||||
|
||||
COMMENT ON COLUMN rtss.draft_data.is_shared IS '是否共享';
|
||||
COMMENT ON COLUMN rtsa.draft_data.is_shared IS '是否共享';
|
||||
|
||||
COMMENT ON COLUMN rtss.draft_data.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN rtsa.draft_data.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.draft_data.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN rtsa.draft_data.updated_at IS '更新时间';
|
||||
|
||||
-- 创建发布数据表
|
||||
CREATE TABLE
|
||||
rtss.release_data (
|
||||
rtsa.release_data (
|
||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||
name VARCHAR(128) NOT NULL, -- 发布数据名称(数据唯一标识)
|
||||
data_type INT NOT NULL, -- 数据类型
|
||||
|
@ -106,47 +109,47 @@ CREATE TABLE
|
|||
is_published BOOLEAN NOT NULL DEFAULT TRUE, -- 是否上架
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
|
||||
FOREIGN KEY (user_id) REFERENCES rtss.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
FOREIGN KEY (user_id) REFERENCES rtsa.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
UNIQUE(data_type, name) -- 数据类型和名称唯一
|
||||
);
|
||||
|
||||
-- 创建发布数据名称索引
|
||||
CREATE INDEX ON rtss.release_data (name);
|
||||
CREATE INDEX ON rtsa.release_data (name);
|
||||
|
||||
-- 创建发布数据用户索引
|
||||
CREATE INDEX ON rtss.release_data (user_id);
|
||||
CREATE INDEX ON rtsa.release_data (user_id);
|
||||
|
||||
-- 创建发布数据类型索引
|
||||
CREATE INDEX ON rtss.release_data (data_type);
|
||||
CREATE INDEX ON rtsa.release_data (data_type);
|
||||
|
||||
-- 创建发布数据配置项索引
|
||||
CREATE INDEX ON rtss.release_data USING GIN (options);
|
||||
CREATE INDEX ON rtsa.release_data USING GIN (options);
|
||||
|
||||
-- 注释发布数据表
|
||||
COMMENT ON TABLE rtss.release_data IS '发布数据表';
|
||||
COMMENT ON TABLE rtsa.release_data IS '发布数据表';
|
||||
|
||||
-- 注释发布数据表字段
|
||||
COMMENT ON COLUMN rtss.release_data.id IS 'id 自增主键';
|
||||
COMMENT ON COLUMN rtsa.release_data.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data.name IS '发布数据名称(数据唯一标识)';
|
||||
COMMENT ON COLUMN rtsa.release_data.name IS '发布数据名称(数据唯一标识)';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data.data_type IS '数据类型';
|
||||
COMMENT ON COLUMN rtsa.release_data.data_type IS '数据类型';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data.options IS '数据相关的参数项或配置项';
|
||||
COMMENT ON COLUMN rtsa.release_data.options IS '数据相关的参数项或配置项';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data.used_version_id IS '使用的版本数据id';
|
||||
COMMENT ON COLUMN rtsa.release_data.used_version_id IS '使用的版本数据id';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data.user_id IS '发布/更新用户id';
|
||||
COMMENT ON COLUMN rtsa.release_data.user_id IS '发布/更新用户id';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data.is_published IS '是否上架';
|
||||
COMMENT ON COLUMN rtsa.release_data.is_published IS '是否上架';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN rtsa.release_data.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN rtsa.release_data.updated_at IS '更新时间';
|
||||
|
||||
-- 创建发布数据版本表
|
||||
CREATE TABLE
|
||||
rtss.release_data_version (
|
||||
rtsa.release_data_version (
|
||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||
release_data_id INT NOT NULL, -- 发布数据id
|
||||
options JSONB NULL, -- 数据相关的参数项或配置项
|
||||
|
@ -154,46 +157,46 @@ CREATE TABLE
|
|||
description TEXT NOT NULL, -- 版本描述
|
||||
user_id INT NOT NULL, -- 发布用户id
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||
FOREIGN KEY (user_id) REFERENCES rtss.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
FOREIGN KEY (release_data_id) REFERENCES rtss.release_data (id) ON DELETE CASCADE
|
||||
FOREIGN KEY (user_id) REFERENCES rtsa.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
FOREIGN KEY (release_data_id) REFERENCES rtsa.release_data (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 创建发布数据版本发布数据索引
|
||||
CREATE INDEX ON rtss.release_data_version (release_data_id);
|
||||
CREATE INDEX ON rtsa.release_data_version (release_data_id);
|
||||
|
||||
-- 创建发布数据版本用户索引
|
||||
CREATE INDEX ON rtss.release_data_version (user_id);
|
||||
CREATE INDEX ON rtsa.release_data_version (user_id);
|
||||
|
||||
-- 创建发布数据版本配置项索引
|
||||
CREATE INDEX ON rtss.release_data_version USING GIN (options);
|
||||
CREATE INDEX ON rtsa.release_data_version USING GIN (options);
|
||||
|
||||
-- 创建发布数据当前版本外键
|
||||
ALTER TABLE rtss.release_data ADD FOREIGN KEY (used_version_id) REFERENCES rtss.release_data_version (id) ON DELETE SET NULL;
|
||||
ALTER TABLE rtsa.release_data ADD FOREIGN KEY (used_version_id) REFERENCES rtsa.release_data_version (id) ON DELETE SET NULL;
|
||||
|
||||
-- 创建草稿数据默认发布数据外键
|
||||
ALTER TABLE rtss.draft_data ADD FOREIGN KEY (default_release_data_id) REFERENCES rtss.release_data (id) ON DELETE SET NULL;
|
||||
ALTER TABLE rtsa.draft_data ADD FOREIGN KEY (default_release_data_id) REFERENCES rtsa.release_data (id) ON DELETE SET NULL;
|
||||
|
||||
-- 注释发布数据版本表
|
||||
COMMENT ON TABLE rtss.release_data_version IS '发布数据版本表';
|
||||
COMMENT ON TABLE rtsa.release_data_version IS '发布数据版本表';
|
||||
|
||||
-- 注释发布数据版本表字段
|
||||
COMMENT ON COLUMN rtss.release_data_version.id IS 'id 自增主键';
|
||||
COMMENT ON COLUMN rtsa.release_data_version.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data_version.release_data_id IS '发布数据id';
|
||||
COMMENT ON COLUMN rtsa.release_data_version.release_data_id IS '发布数据id';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data_version.options IS '数据相关的参数项或配置项';
|
||||
COMMENT ON COLUMN rtsa.release_data_version.options IS '数据相关的参数项或配置项';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data_version.data IS '数据';
|
||||
COMMENT ON COLUMN rtsa.release_data_version.data IS '数据';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data_version.description IS '版本描述';
|
||||
COMMENT ON COLUMN rtsa.release_data_version.description IS '版本描述';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data_version.user_id IS '发布用户id';
|
||||
COMMENT ON COLUMN rtsa.release_data_version.user_id IS '发布用户id';
|
||||
|
||||
COMMENT ON COLUMN rtss.release_data_version.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN rtsa.release_data_version.created_at IS '创建时间';
|
||||
|
||||
-- 创建feature表
|
||||
CREATE TABLE
|
||||
rtss.feature (
|
||||
rtsa.feature (
|
||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||
feature_type INT NOT NULL, -- feature类型
|
||||
name VARCHAR(128) NOT NULL UNIQUE, -- feature名称
|
||||
|
@ -204,68 +207,68 @@ CREATE TABLE
|
|||
updater_id INT NOT NULL, -- 更新用户id
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
|
||||
FOREIGN KEY (creator_id) REFERENCES rtss.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
FOREIGN KEY (updater_id) REFERENCES rtss.user (id) ON DELETE CASCADE -- 用户外键
|
||||
FOREIGN KEY (creator_id) REFERENCES rtsa.user (id) ON DELETE CASCADE, -- 用户外键
|
||||
FOREIGN KEY (updater_id) REFERENCES rtsa.user (id) ON DELETE CASCADE -- 用户外键
|
||||
);
|
||||
|
||||
-- 创建feature类型索引
|
||||
CREATE INDEX ON rtss.feature (feature_type);
|
||||
CREATE INDEX ON rtsa.feature (feature_type);
|
||||
|
||||
-- 创建feature名称索引
|
||||
CREATE INDEX ON rtss.feature (name);
|
||||
CREATE INDEX ON rtsa.feature (name);
|
||||
|
||||
-- 注释仿真feature表
|
||||
COMMENT ON TABLE rtss.feature IS 'feature表';
|
||||
COMMENT ON TABLE rtsa.feature IS 'feature表';
|
||||
|
||||
-- 注释仿真feature表字段
|
||||
COMMENT ON COLUMN rtss.feature.id IS 'id 自增主键';
|
||||
COMMENT ON COLUMN rtsa.feature.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.feature_type IS 'feature类型';
|
||||
COMMENT ON COLUMN rtsa.feature.feature_type IS 'feature类型';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.name IS 'feature名称';
|
||||
COMMENT ON COLUMN rtsa.feature.name IS 'feature名称';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.description IS 'feature描述';
|
||||
COMMENT ON COLUMN rtsa.feature.description IS 'feature描述';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.config IS 'feature配置';
|
||||
COMMENT ON COLUMN rtsa.feature.config IS 'feature配置';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.is_published IS '是否上架';
|
||||
COMMENT ON COLUMN rtsa.feature.is_published IS '是否上架';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.creator_id IS '创建用户id';
|
||||
COMMENT ON COLUMN rtsa.feature.creator_id IS '创建用户id';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN rtsa.feature.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.feature.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN rtsa.feature.updated_at IS '更新时间';
|
||||
|
||||
-- 创建用户配置表
|
||||
CREATE TABLE
|
||||
rtss.user_config (
|
||||
rtsa.user_config (
|
||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||
user_id INT NOT NULL, -- 用户id
|
||||
config_type INT NOT NULL, -- 配置类型
|
||||
config BYTEA NOT NULL, -- 配置
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
|
||||
FOREIGN KEY (user_id) REFERENCES rtss.user (id) ON DELETE CASCADE -- 用户外键
|
||||
FOREIGN KEY (user_id) REFERENCES rtsa.user (id) ON DELETE CASCADE -- 用户外键
|
||||
);
|
||||
|
||||
-- 创建用户配置用户索引
|
||||
CREATE INDEX ON rtss.user_config (user_id);
|
||||
CREATE INDEX ON rtsa.user_config (user_id);
|
||||
|
||||
-- 创建用户配置类型索引
|
||||
CREATE INDEX ON rtss.user_config (config_type);
|
||||
CREATE INDEX ON rtsa.user_config (config_type);
|
||||
|
||||
-- 注释用户feature配置表
|
||||
COMMENT ON TABLE rtss.user_config IS '用户feature配置表';
|
||||
COMMENT ON TABLE rtsa.user_config IS '用户feature配置表';
|
||||
|
||||
-- 注释用户feature配置表字段
|
||||
COMMENT ON COLUMN rtss.user_config.id IS 'id 自增主键';
|
||||
COMMENT ON COLUMN rtsa.user_config.id IS 'id 自增主键';
|
||||
|
||||
COMMENT ON COLUMN rtss.user_config.user_id IS '用户id';
|
||||
COMMENT ON COLUMN rtsa.user_config.user_id IS '用户id';
|
||||
|
||||
COMMENT ON COLUMN rtss.user_config.config_type IS '配置类型';
|
||||
COMMENT ON COLUMN rtsa.user_config.config_type IS '配置类型';
|
||||
|
||||
COMMENT ON COLUMN rtss.user_config.config IS '配置';
|
||||
COMMENT ON COLUMN rtsa.user_config.config IS '配置';
|
||||
|
||||
COMMENT ON COLUMN rtss.user_config.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN rtsa.user_config.created_at IS '创建时间';
|
||||
|
||||
COMMENT ON COLUMN rtss.user_config.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN rtsa.user_config.updated_at IS '更新时间';
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1f53057b3f87790ef27c91399a5bb7e890f05549
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 02d9ad3b44876fc470e460bb6975c3d08f698b1b
|
|
@ -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 }
|
||||
|
||||
rtsa_log = { path = "../crates/rtsa_log" }
|
||||
rtsa_dto = { path = "../crates/rtsa_dto" }
|
||||
rtsa_db = { path = "../crates/rtsa_db" }
|
||||
rtsa_mqtt = { path = "../crates/rtsa_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"
|
|
@ -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 rtsa_log::Logging {
|
||||
fn from(log: Log) -> Self {
|
||||
rtsa_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 rtsa_SIM)
|
||||
// Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key
|
||||
.add_source(Environment::with_prefix("rtsa_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 = "rtsa-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: rtsa_log::Logging = app_config.log.into();
|
||||
log.init();
|
||||
// 数据库访问器初始化
|
||||
rtsa_db::init_default_db_accessor(&app_config.database.url).await;
|
||||
// mqtt客户端初始化
|
||||
let cli_id = rtsa_db::get_default_db_accessor()
|
||||
.get_next_mqtt_client_id()
|
||||
.await?;
|
||||
let mqtt_cli_options =
|
||||
rtsa_mqtt::MqttClientOptions::new(&format!("rtsa{}", cli_id), &app_config.mqtt.url)
|
||||
.set_credentials(&app_config.mqtt.username, &app_config.mqtt.password);
|
||||
rtsa_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");
|
||||
rtsa_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<rtsa_dto::simulation::Operation>,
|
||||
rx: Option<broadcast::Receiver<rtsa_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<rtsa_dto::simulation::Operation> {
|
||||
self.tx.clone()
|
||||
}
|
||||
|
||||
pub fn subscribe(&mut self) -> broadcast::Receiver<rtsa_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 rtsa_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 rtsa_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<rtsa_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: rtsa_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 rtsa_dto::simulation::OperationType;
|
||||
use rtsa_log::tracing::Level;
|
||||
|
||||
#[test]
|
||||
fn test_new_simulation() {
|
||||
rtsa_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(rtsa_dto::simulation::Operation {
|
||||
otype: rtsa_dto::simulation::OperationType::SetSpeed as i32,
|
||||
param: Some(rtsa_dto::simulation::operation::Param::SetSpeedParam(
|
||||
rtsa_dto::simulation::SetSpeedParam { speed: 2.0 },
|
||||
)),
|
||||
});
|
||||
let clone2 = simulation1.clone();
|
||||
thread::spawn(move || {
|
||||
sleep(Duration::from_millis(100));
|
||||
info!("send pause");
|
||||
clone2.send_operation(rtsa_dto::simulation::Operation {
|
||||
otype: OperationType::Pause as i32,
|
||||
param: None,
|
||||
});
|
||||
sleep(Duration::from_millis(200));
|
||||
info!("send unpause");
|
||||
clone2.send_operation(rtsa_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 rtsa_dto::simulation::{operation, OperationType, SetSpeedParam};
|
||||
use rtsa_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<rtsa_dto::simulation::Operation>);
|
||||
|
||||
impl RxResource {
|
||||
fn new(rx: broadcast::Receiver<rtsa_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;
|
|
@ -1,5 +1,6 @@
|
|||
use bevy_ecs::bundle::Bundle;
|
||||
use rtss_common::Uid;
|
||||
|
||||
use crate::components::Uid;
|
||||
|
||||
use super::{PsdState, TurnoutState, TwoNormalPositionsTransform};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue