Compare commits
6 Commits
3798572b0c
...
f90e4048ef
Author | SHA1 | Date | |
---|---|---|---|
|
f90e4048ef | ||
|
50ed464ff2 | ||
|
505230b26c | ||
|
2e1eecfdad | ||
|
4d8c0e0bad | ||
|
7226904e04 |
@ -50,7 +50,7 @@ jobs:
|
|||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
gitea.joylink.club/joylink/rtss-simulation:local-test
|
gitea.joylink.club/joylink/rtsam:lt
|
||||||
- name: 发布到本地测试环境
|
- name: 发布到本地测试环境
|
||||||
uses: https://gitea.joylink.club/appleboy/ssh-action@v1.0.3
|
uses: https://gitea.joylink.club/appleboy/ssh-action@v1.0.3
|
||||||
with:
|
with:
|
||||||
@ -59,6 +59,6 @@ jobs:
|
|||||||
username: ${{ secrets.LOCAL_233_SSH_USER }}
|
username: ${{ secrets.LOCAL_233_SSH_USER }}
|
||||||
password: ${{ secrets.LOCAL_233_SSH_PASSWORD }}
|
password: ${{ secrets.LOCAL_233_SSH_PASSWORD }}
|
||||||
script: |
|
script: |
|
||||||
docker rm -f rtss-simulation || echo "rtss-simulation not exist"
|
docker rm -f rtsa-manage || echo "rtsa-manage not exist"
|
||||||
docker pull gitea.joylink.club/joylink/rtss-simulation:local-test
|
docker pull gitea.joylink.club/joylink/rtsam:lt
|
||||||
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 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
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
.env
|
||||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,3 +1,3 @@
|
|||||||
[submodule "rtss-proto-msg"]
|
[submodule "rtsa-proto-msg"]
|
||||||
path = rtss-proto-msg
|
path = rtsa-proto-msg
|
||||||
url = https://gitea.joylink.club/joylink/rtss-proto-msg.git
|
url = https://gitea.joylink.club/joylink/rtsa-proto-msg.git
|
||||||
|
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@ -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}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
@ -2,6 +2,8 @@
|
|||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"chrono",
|
"chrono",
|
||||||
"cpus",
|
"cpus",
|
||||||
|
"dashmap",
|
||||||
|
"eventloop",
|
||||||
"Graphi",
|
"Graphi",
|
||||||
"graphiql",
|
"graphiql",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@ -11,7 +13,12 @@
|
|||||||
"Joylink",
|
"Joylink",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"mplj",
|
"mplj",
|
||||||
|
"Mqtt",
|
||||||
|
"mqttbytes",
|
||||||
"Neng",
|
"Neng",
|
||||||
|
"nextval",
|
||||||
|
"oneshot",
|
||||||
|
"otype",
|
||||||
"plpgsql",
|
"plpgsql",
|
||||||
"prost",
|
"prost",
|
||||||
"proto",
|
"proto",
|
||||||
@ -19,7 +26,10 @@
|
|||||||
"protos",
|
"protos",
|
||||||
"repr",
|
"repr",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rtss",
|
"rtsa",
|
||||||
|
"rtsa",
|
||||||
|
"rumqtt",
|
||||||
|
"rumqttc",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
383
Cargo.lock
generated
383
Cargo.lock
generated
@ -121,9 +121,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.88"
|
version = "1.0.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356"
|
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arraydeque"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ascii_utils"
|
name = "ascii_utils"
|
||||||
@ -133,9 +139,9 @@ checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-executor"
|
name = "async-executor"
|
||||||
version = "1.13.0"
|
version = "1.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7"
|
checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-task",
|
"async-task",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
@ -270,9 +276,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.81"
|
version = "0.1.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -325,7 +331,7 @@ dependencies = [
|
|||||||
"sync_wrapper 1.0.1",
|
"sync_wrapper 1.0.1",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
"tower",
|
"tower 0.4.13",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -369,7 +375,7 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"serde",
|
"serde",
|
||||||
"tower",
|
"tower 0.4.13",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -564,7 +570,7 @@ dependencies = [
|
|||||||
"ahash",
|
"ahash",
|
||||||
"bevy_utils_proc_macros",
|
"bevy_utils_proc_macros",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
@ -619,9 +625,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.1"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -658,9 +664,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.17"
|
version = "4.5.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@ -668,9 +674,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.17"
|
version = "4.5.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@ -680,9 +686,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.13"
|
version = "4.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -713,14 +719,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "config"
|
name = "config"
|
||||||
version = "0.14.0"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
|
checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"json5",
|
"json5",
|
||||||
"lazy_static",
|
|
||||||
"nom",
|
"nom",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"ron",
|
"ron",
|
||||||
@ -728,7 +733,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"toml",
|
"toml",
|
||||||
"yaml-rust",
|
"yaml-rust2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -776,6 +781,16 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"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]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.7"
|
version = "0.8.7"
|
||||||
@ -1066,9 +1081,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.0"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@ -1236,12 +1251,6 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
@ -1253,13 +1262,22 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashlink"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashlink"
|
name = "hashlink"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1407,10 +1425,10 @@ dependencies = [
|
|||||||
"http",
|
"http",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rustls",
|
"rustls 0.23.13",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls 0.26.0",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
@ -1430,7 +1448,7 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower 0.4.13",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@ -1481,7 +1499,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1579,12 +1597,6 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linked-hash-map"
|
|
||||||
version = "0.5.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
@ -1613,7 +1625,22 @@ version = "0.12.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
|
checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "manager"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"config",
|
||||||
|
"enum_dispatch",
|
||||||
|
"rtsa_api",
|
||||||
|
"rtsa_db",
|
||||||
|
"rtsa_log",
|
||||||
|
"serde",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1817,13 +1844,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-multimap"
|
name = "openssl-probe"
|
||||||
version = "0.6.0"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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 = [
|
dependencies = [
|
||||||
"dlv-list",
|
"dlv-list",
|
||||||
"hashbrown 0.13.2",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2119,7 +2152,7 @@ dependencies = [
|
|||||||
"quinn-proto",
|
"quinn-proto",
|
||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls 0.23.13",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
@ -2136,7 +2169,7 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
"ring",
|
"ring",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls 0.23.13",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
@ -2301,7 +2334,7 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn",
|
"quinn",
|
||||||
"rustls",
|
"rustls 0.23.13",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
@ -2309,7 +2342,7 @@ dependencies = [
|
|||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"sync_wrapper 1.0.1",
|
"sync_wrapper 1.0.1",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls 0.26.0",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@ -2367,7 +2400,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtss_api"
|
name = "rtsa_api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -2376,15 +2409,12 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bevy_ecs",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rtss_db",
|
"rtsa_db",
|
||||||
"rtss_dto",
|
"rtsa_dto",
|
||||||
"rtss_log",
|
"rtsa_log",
|
||||||
"rtss_sim_manage",
|
|
||||||
"rtss_trackside",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
@ -2393,23 +2423,13 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtss_ci"
|
name = "rtsa_db"
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_common"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bevy_ecs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_db"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"rtss_dto",
|
"lazy_static",
|
||||||
"rtss_log",
|
"rtsa_dto",
|
||||||
|
"rtsa_log",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
@ -2417,22 +2437,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtss_dto"
|
name = "rtsa_dto"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-graphql",
|
"async-graphql",
|
||||||
"prost",
|
"prost",
|
||||||
"prost-build",
|
"prost-build",
|
||||||
|
"prost-types",
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtss_iscs"
|
name = "rtsa_log"
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_log"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -2441,52 +2458,43 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtss_sim_manage"
|
name = "rtsa_mqtt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"async-trait",
|
||||||
"bevy_core",
|
"bytes",
|
||||||
"bevy_ecs",
|
"lazy_static",
|
||||||
"bevy_time",
|
"rtsa_log",
|
||||||
"rayon",
|
"rumqttc",
|
||||||
"rtss_common",
|
|
||||||
"rtss_log",
|
|
||||||
"rtss_trackside",
|
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rtss_simulation"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"clap",
|
|
||||||
"config",
|
|
||||||
"enum_dispatch",
|
|
||||||
"rtss_api",
|
|
||||||
"rtss_db",
|
|
||||||
"rtss_log",
|
|
||||||
"serde",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tower 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rtss_trackside"
|
name = "rumqttc"
|
||||||
version = "0.1.0"
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1568e15fab2d546f940ed3a21f48bbbd1c494c90c99c4481339364a497f94a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bytes",
|
||||||
"bevy_core",
|
"flume",
|
||||||
"bevy_ecs",
|
"futures-util",
|
||||||
"bevy_time",
|
"log",
|
||||||
"rtss_common",
|
"rustls-native-certs",
|
||||||
"rtss_log",
|
"rustls-pemfile",
|
||||||
|
"rustls-webpki",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls 0.25.0",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-ini"
|
name = "rust-ini"
|
||||||
version = "0.19.0"
|
version = "0.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
|
checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"ordered-multimap",
|
"ordered-multimap",
|
||||||
@ -2517,6 +2525,20 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"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]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.13"
|
version = "0.23.13"
|
||||||
@ -2531,6 +2553,19 @@ dependencies = [
|
|||||||
"zeroize",
|
"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]]
|
[[package]]
|
||||||
name = "rustls-pemfile"
|
name = "rustls-pemfile"
|
||||||
version = "2.1.3"
|
version = "2.1.3"
|
||||||
@ -2570,6 +2605,15 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
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]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -2577,19 +2621,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "security-framework"
|
||||||
version = "1.0.210"
|
version = "2.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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 = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.210"
|
version = "1.0.214"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2598,9 +2665,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.125"
|
version = "1.0.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -2698,6 +2765,29 @@ dependencies = [
|
|||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simulation"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bevy_app",
|
||||||
|
"bevy_core",
|
||||||
|
"bevy_ecs",
|
||||||
|
"bevy_time",
|
||||||
|
"clap",
|
||||||
|
"config",
|
||||||
|
"enum_dispatch",
|
||||||
|
"lazy_static",
|
||||||
|
"rayon",
|
||||||
|
"rtsa_db",
|
||||||
|
"rtsa_dto",
|
||||||
|
"rtsa_log",
|
||||||
|
"rtsa_mqtt",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@ -2796,8 +2886,8 @@ dependencies = [
|
|||||||
"futures-intrusive",
|
"futures-intrusive",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown",
|
||||||
"hashlink",
|
"hashlink 0.9.1",
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
@ -3019,9 +3109,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.76"
|
version = "2.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
|
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3072,18 +3162,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.63"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3157,9 +3247,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.40.0"
|
version = "1.41.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998"
|
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -3182,13 +3272,24 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.26.0"
|
version = "0.26.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls",
|
"rustls 0.23.13",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
@ -3291,6 +3392,20 @@ dependencies = [
|
|||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "tower-http"
|
name = "tower-http"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@ -3417,9 +3532,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typeid"
|
name = "typeid"
|
||||||
version = "1.0.0"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf"
|
checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
@ -3935,12 +4050,14 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust2"
|
||||||
version = "0.4.5"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map",
|
"arraydeque",
|
||||||
|
"encoding_rs",
|
||||||
|
"hashlink 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
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]
|
[workspace]
|
||||||
members = ["crates/*"]
|
members = ["crates/*", "manager/crates/*", "manager", "simulation"]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bevy_app = "0.14"
|
bevy_app = "0.14"
|
||||||
@ -14,25 +9,21 @@ bevy_core = "0.14"
|
|||||||
bevy_ecs = "0.14"
|
bevy_ecs = "0.14"
|
||||||
bevy_time = "0.14"
|
bevy_time = "0.14"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
tokio = { version = "1.40", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.41.0", features = ["macros", "rt-multi-thread"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0.65"
|
||||||
sqlx = { version = "0.8", features = [
|
sqlx = { version = "0.8", features = [
|
||||||
"runtime-tokio",
|
"runtime-tokio",
|
||||||
"postgres",
|
"postgres",
|
||||||
"json",
|
"json",
|
||||||
"chrono",
|
"chrono",
|
||||||
] }
|
] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0.214", features = ["derive"] }
|
||||||
serde_json = "1.0.125"
|
serde_json = "1.0.132"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0.91"
|
||||||
|
async-trait = "0.1.83"
|
||||||
[dependencies]
|
bytes = "1.8.0"
|
||||||
tokio = { version = "1.39.3", features = ["macros", "rt-multi-thread"] }
|
lazy_static = "1.5.0"
|
||||||
rtss_log = { path = "crates/rtss_log" }
|
config = "0.14.1"
|
||||||
rtss_api = { path = "crates/rtss_api" }
|
clap = { version = "4.5.20", features = ["derive"] }
|
||||||
rtss_db = { path = "crates/rtss_db" }
|
|
||||||
serde = { workspace = true }
|
|
||||||
config = "0.14.0"
|
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
|
||||||
enum_dispatch = "0.3"
|
enum_dispatch = "0.3"
|
||||||
anyhow = { workspace = true }
|
tower = { version = "0.5.1", features = ["util"] }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[default.extend-words]
|
[default.extend-words]
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
extend-exclude = ["README.md", "rtss-proto-msg/*"]
|
extend-exclude = ["README.md", "rtsa-proto-msg/*"]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rtss_db"
|
name = "rtsa_db"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@ -17,6 +17,7 @@ sqlx = { workspace = true, features = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
] }
|
] }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
lazy_static = { workspace = true }
|
||||||
|
|
||||||
rtss_dto = { path = "../rtss_dto" }
|
rtsa_dto = { path = "../rtsa_dto" }
|
||||||
rtss_log = { path = "../rtss_log" }
|
rtsa_log = { path = "../rtsa_log" }
|
@ -1,7 +1,7 @@
|
|||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
use rtss_dto::common::DataType;
|
use rtsa_dto::common::DataType;
|
||||||
use rtss_log::tracing::debug;
|
use rtsa_log::tracing::debug;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -10,7 +10,7 @@ use crate::{
|
|||||||
DbAccessError,
|
DbAccessError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::RtssDbAccessor;
|
use super::RtsaDbAccessor;
|
||||||
|
|
||||||
/// 草稿数据管理
|
/// 草稿数据管理
|
||||||
#[allow(async_fn_in_trait)]
|
#[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(
|
async fn paging_query_draft_data(
|
||||||
&self,
|
&self,
|
||||||
query: DraftDataQuery,
|
query: DraftDataQuery,
|
||||||
@ -436,8 +436,8 @@ mod tests {
|
|||||||
use crate::{SyncUserInfo, UserAccessor};
|
use crate::{SyncUserInfo, UserAccessor};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use rtss_dto::common::{IscsStyle, Role};
|
use rtsa_dto::common::{IscsStyle, Role};
|
||||||
use rtss_log::tracing::Level;
|
use rtsa_log::tracing::Level;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{types::chrono::Local, PgPool};
|
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.
|
// You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here.
|
||||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
async fn basic_use_test(pool: PgPool) -> Result<(), DbAccessError> {
|
async fn basic_use_test(pool: PgPool) -> Result<(), DbAccessError> {
|
||||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||||
let accessor = crate::db_access::RtssDbAccessor::new(pool);
|
let accessor = crate::db_access::RtsaDbAccessor::new(pool);
|
||||||
// 同步10个用户
|
// 同步10个用户
|
||||||
let mut users = vec![];
|
let mut users = vec![];
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
@ -458,7 +458,7 @@ mod tests {
|
|||||||
id: i + 1,
|
id: i + 1,
|
||||||
name: format!("user{}", i + 1),
|
name: format!("user{}", i + 1),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
@ -1,4 +1,4 @@
|
|||||||
use rtss_dto::common::FeatureType;
|
use rtsa_dto::common::FeatureType;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -7,7 +7,7 @@ use crate::{
|
|||||||
DbAccessError,
|
DbAccessError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::RtssDbAccessor;
|
use super::RtsaDbAccessor;
|
||||||
|
|
||||||
/// 功能特性管理
|
/// 功能特性管理
|
||||||
#[allow(async_fn_in_trait)]
|
#[allow(async_fn_in_trait)]
|
||||||
@ -34,7 +34,7 @@ pub trait FeatureAccessor {
|
|||||||
async fn get_features(&self, ids: &[i32]) -> Result<Vec<FeatureModel>, DbAccessError>;
|
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> {
|
async fn create_feature(&self, feature: &CreateFeature) -> Result<FeatureModel, DbAccessError> {
|
||||||
let table = FeatureColumn::Table.name();
|
let table = FeatureColumn::Table.name();
|
||||||
let feature_type_column = FeatureColumn::FeatureType.name();
|
let feature_type_column = FeatureColumn::FeatureType.name();
|
||||||
@ -217,8 +217,8 @@ impl FeaturePagingFilter {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use rtss_dto::common::Role;
|
use rtsa_dto::common::Role;
|
||||||
use rtss_log::tracing::Level;
|
use rtsa_log::tracing::Level;
|
||||||
use sqlx::{types::chrono::Local, PgPool};
|
use sqlx::{types::chrono::Local, PgPool};
|
||||||
|
|
||||||
use crate::{SyncUserInfo, UserAccessor};
|
use crate::{SyncUserInfo, UserAccessor};
|
||||||
@ -227,8 +227,8 @@ mod tests {
|
|||||||
|
|
||||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
async fn test_basic_use(pool: PgPool) -> Result<(), DbAccessError> {
|
async fn test_basic_use(pool: PgPool) -> Result<(), DbAccessError> {
|
||||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||||
let accessor = crate::db_access::RtssDbAccessor::new(pool);
|
let accessor = crate::db_access::RtsaDbAccessor::new(pool);
|
||||||
// 同步10个用户
|
// 同步10个用户
|
||||||
let mut users = vec![];
|
let mut users = vec![];
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
@ -236,7 +236,7 @@ mod tests {
|
|||||||
id: i + 1,
|
id: i + 1,
|
||||||
name: format!("user{}", i + 1),
|
name: format!("user{}", i + 1),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
@ -260,7 +260,7 @@ mod tests {
|
|||||||
println!("create feature: {:?}", feature);
|
println!("create feature: {:?}", feature);
|
||||||
assert_eq!(feature.creator_id, creator_id);
|
assert_eq!(feature.creator_id, creator_id);
|
||||||
assert_eq!(feature.updater_id, creator_id);
|
assert_eq!(feature.updater_id, creator_id);
|
||||||
assert_eq!(feature.is_published, true);
|
assert!(feature.is_published);
|
||||||
assert_eq!(feature.config, create_config);
|
assert_eq!(feature.config, create_config);
|
||||||
let feature_id = feature.id;
|
let feature_id = feature.id;
|
||||||
// 获取功能特性测试
|
// 获取功能特性测试
|
||||||
@ -269,7 +269,7 @@ mod tests {
|
|||||||
assert_eq!(feature.id, feature_id);
|
assert_eq!(feature.id, feature_id);
|
||||||
assert_eq!(feature.creator_id, creator_id);
|
assert_eq!(feature.creator_id, creator_id);
|
||||||
assert_eq!(feature.updater_id, creator_id);
|
assert_eq!(feature.updater_id, creator_id);
|
||||||
assert_eq!(feature.is_published, true);
|
assert!(feature.is_published);
|
||||||
assert_eq!(feature.config, create_config);
|
assert_eq!(feature.config, create_config);
|
||||||
// 更新测试
|
// 更新测试
|
||||||
let updater_id = 2;
|
let updater_id = 2;
|
||||||
@ -279,7 +279,7 @@ mod tests {
|
|||||||
name: "test update".to_string(),
|
name: "test update".to_string(),
|
||||||
description: "test update".to_string(),
|
description: "test update".to_string(),
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
updater_id: updater_id,
|
updater_id,
|
||||||
};
|
};
|
||||||
let feature = accessor.update_feature(&feature).await?;
|
let feature = accessor.update_feature(&feature).await?;
|
||||||
println!("update feature: {:?}", feature);
|
println!("update feature: {:?}", feature);
|
||||||
@ -289,7 +289,7 @@ mod tests {
|
|||||||
// 上下架测试
|
// 上下架测试
|
||||||
let feature = accessor.set_feature_published(feature_id, false).await?;
|
let feature = accessor.set_feature_published(feature_id, false).await?;
|
||||||
println!("set feature published: {:?}", feature);
|
println!("set feature published: {:?}", feature);
|
||||||
assert_eq!(feature.is_published, false);
|
assert!(!feature.is_published);
|
||||||
// 分页查询测试
|
// 分页查询测试
|
||||||
// 创建10个功能特性,5个发布的,5个未发布的
|
// 创建10个功能特性,5个发布的,5个未发布的
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
82
crates/rtsa_db/src/db_access/mod.rs
Normal file
82
crates/rtsa_db/src/db_access/mod.rs
Normal file
@ -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 serde_json::Value;
|
||||||
use sqlx::{types::chrono, Postgres};
|
use sqlx::{types::chrono, Postgres};
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ use crate::{
|
|||||||
DbAccessError,
|
DbAccessError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{CreateDraftData, DraftDataAccessor, RtssDbAccessor};
|
use super::{CreateDraftData, DraftDataAccessor, RtsaDbAccessor};
|
||||||
|
|
||||||
#[allow(async_fn_in_trait)]
|
#[allow(async_fn_in_trait)]
|
||||||
pub trait ReleaseDataAccessor {
|
pub trait ReleaseDataAccessor {
|
||||||
@ -109,7 +109,7 @@ pub struct ReleaseFromDraftResult {
|
|||||||
pub struct ReleaseDataQuery {
|
pub struct ReleaseDataQuery {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub user_id: Option<i32>,
|
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 options: Option<Value>,
|
||||||
pub is_published: Option<bool>,
|
pub is_published: Option<bool>,
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ impl ReleaseDataQuery {
|
|||||||
self
|
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.data_type = Some(data_type);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -202,7 +202,7 @@ pub struct CreateReleaseVersionData {
|
|||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RtssDbAccessor {
|
impl RtsaDbAccessor {
|
||||||
async fn insert_release_data_version<'e, 'c: 'e, E>(
|
async fn insert_release_data_version<'e, 'c: 'e, E>(
|
||||||
&self,
|
&self,
|
||||||
data: CreateReleaseVersionData,
|
data: CreateReleaseVersionData,
|
||||||
@ -237,7 +237,7 @@ impl RtssDbAccessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReleaseDataAccessor for RtssDbAccessor {
|
impl ReleaseDataAccessor for RtsaDbAccessor {
|
||||||
async fn release_new_from_draft(
|
async fn release_new_from_draft(
|
||||||
&self,
|
&self,
|
||||||
draft_id: i32,
|
draft_id: i32,
|
||||||
@ -645,12 +645,12 @@ impl ReleaseDataAccessor for RtssDbAccessor {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{CreateDraftData, DraftDataAccessor, RtssDbAccessor, SyncUserInfo, UserAccessor};
|
use crate::{CreateDraftData, DraftDataAccessor, RtsaDbAccessor, SyncUserInfo, UserAccessor};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use rtss_dto::common::{IscsStyle, Role};
|
use rtsa_dto::common::{IscsStyle, Role};
|
||||||
use rtss_log::tracing::Level;
|
use rtsa_log::tracing::Level;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::PgPool;
|
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.
|
// You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here.
|
||||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
async fn test_basic_use(pool: PgPool) -> Result<(), DbAccessError> {
|
async fn test_basic_use(pool: PgPool) -> Result<(), DbAccessError> {
|
||||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||||
let accessor = RtssDbAccessor::new(pool);
|
let accessor = RtsaDbAccessor::new(pool);
|
||||||
// 同步10个用户
|
// 同步10个用户
|
||||||
let mut users = vec![];
|
let mut users = vec![];
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
@ -704,7 +704,7 @@ mod tests {
|
|||||||
id: i + 1,
|
id: i + 1,
|
||||||
name: format!("user{}", i + 1),
|
name: format!("user{}", i + 1),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
@ -717,7 +717,7 @@ mod tests {
|
|||||||
let data = "test".as_bytes();
|
let data = "test".as_bytes();
|
||||||
let draft = accessor
|
let draft = accessor
|
||||||
.create_draft_data(
|
.create_draft_data(
|
||||||
CreateDraftData::new("test", rtss_dto::common::DataType::Iscs, 1)
|
CreateDraftData::new("test", rtsa_dto::common::DataType::Iscs, 1)
|
||||||
.with_options(
|
.with_options(
|
||||||
serde_json::to_value(IscsDataOptions {
|
serde_json::to_value(IscsDataOptions {
|
||||||
style: IscsStyle::DaShiZhiNeng,
|
style: IscsStyle::DaShiZhiNeng,
|
||||||
@ -745,7 +745,7 @@ mod tests {
|
|||||||
|
|
||||||
// 发布新版本
|
// 发布新版本
|
||||||
let (release_data, version1) = accessor
|
let (release_data, version1) = accessor
|
||||||
.release_new_from_draft(draft.id, name, &description, None)
|
.release_new_from_draft(draft.id, name, description, None)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(release_data.name, name);
|
assert_eq!(release_data.name, name);
|
||||||
// 检查使用版本是刚刚发布的版本
|
// 检查使用版本是刚刚发布的版本
|
||||||
@ -759,7 +759,7 @@ mod tests {
|
|||||||
// 检查版本描述
|
// 检查版本描述
|
||||||
assert_eq!(version1.description, description);
|
assert_eq!(version1.description, description);
|
||||||
// 检查上架状态
|
// 检查上架状态
|
||||||
assert_eq!(release_data.is_published, true);
|
assert!(release_data.is_published);
|
||||||
// 检查草稿数据的默认发布数据id
|
// 检查草稿数据的默认发布数据id
|
||||||
let draft = accessor.query_draft_data_by_id(draft.id).await?;
|
let draft = accessor.query_draft_data_by_id(draft.id).await?;
|
||||||
assert_eq!(draft.default_release_data_id, Some(release_data.id));
|
assert_eq!(draft.default_release_data_id, Some(release_data.id));
|
||||||
@ -767,9 +767,9 @@ mod tests {
|
|||||||
|
|
||||||
// name重复检查
|
// name重复检查
|
||||||
let exist = accessor
|
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?;
|
.await?;
|
||||||
assert_eq!(exist, true);
|
assert!(exist);
|
||||||
|
|
||||||
// 修改草稿数据
|
// 修改草稿数据
|
||||||
let data = "test2".as_bytes();
|
let data = "test2".as_bytes();
|
||||||
@ -798,7 +798,7 @@ mod tests {
|
|||||||
let release_data = accessor
|
let release_data = accessor
|
||||||
.set_release_data_published(release_data.id, false)
|
.set_release_data_published(release_data.id, false)
|
||||||
.await?;
|
.await?;
|
||||||
assert_eq!(release_data.is_published, false);
|
assert!(!release_data.is_published);
|
||||||
assert!(release_data.updated_at > release_data.created_at);
|
assert!(release_data.updated_at > release_data.created_at);
|
||||||
println!("更新发布数据上架状态测试成功");
|
println!("更新发布数据上架状态测试成功");
|
||||||
|
|
||||||
@ -849,7 +849,7 @@ mod tests {
|
|||||||
let data = "test data".as_bytes();
|
let data = "test data".as_bytes();
|
||||||
let draft = accessor
|
let draft = accessor
|
||||||
.create_draft_data(
|
.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?;
|
.await?;
|
||||||
let (release_data, _) = accessor
|
let (release_data, _) = accessor
|
||||||
@ -883,7 +883,7 @@ mod tests {
|
|||||||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||||
assert_eq!(page_result.total, 2);
|
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 = PageQuery::new(1, 10);
|
||||||
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
let page_result = accessor.paging_query_release_data_list(query, page).await?;
|
||||||
assert_eq!(page_result.total, 8);
|
assert_eq!(page_result.total, 8);
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
DbAccessError,
|
DbAccessError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::RtssDbAccessor;
|
use super::RtsaDbAccessor;
|
||||||
|
|
||||||
/// 草稿数据管理
|
/// 草稿数据管理
|
||||||
#[allow(async_fn_in_trait)]
|
#[allow(async_fn_in_trait)]
|
||||||
@ -96,7 +96,7 @@ pub struct SyncUserInfo {
|
|||||||
pub updated_at: Option<DateTime<Local>>,
|
pub updated_at: Option<DateTime<Local>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RtssDbAccessor {
|
impl RtsaDbAccessor {
|
||||||
/// 首次同步用户数据
|
/// 首次同步用户数据
|
||||||
async fn sync_new_users(&self, users: &[SyncUserInfo]) -> Result<(), DbAccessError> {
|
async fn sync_new_users(&self, users: &[SyncUserInfo]) -> Result<(), DbAccessError> {
|
||||||
let table = UserColumn::Table.name();
|
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> {
|
async fn sync_user(&self, users: &[SyncUserInfo]) -> Result<(), DbAccessError> {
|
||||||
self.check_and_sync_user(users).await
|
self.check_and_sync_user(users).await
|
||||||
}
|
}
|
||||||
@ -271,7 +271,7 @@ impl UserAccessor for RtssDbAccessor {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use rtss_log::tracing::Level;
|
use rtsa_log::tracing::Level;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
@ -293,14 +293,14 @@ mod tests {
|
|||||||
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
#[sqlx::test(migrator = "crate::MIGRATOR")]
|
||||||
async fn test_sync_user(pool: PgPool) -> Result<(), DbAccessError> {
|
async fn test_sync_user(pool: PgPool) -> Result<(), DbAccessError> {
|
||||||
// 日志初始化
|
// 日志初始化
|
||||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||||
let accessor = RtssDbAccessor::new(pool);
|
let accessor = RtsaDbAccessor::new(pool);
|
||||||
let users = vec![
|
let users = vec![
|
||||||
SyncUserInfo {
|
SyncUserInfo {
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "test1".to_string(),
|
name: "test1".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
@ -310,7 +310,7 @@ mod tests {
|
|||||||
id: 2,
|
id: 2,
|
||||||
name: "test2".to_string(),
|
name: "test2".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::Admin]).unwrap(),
|
roles: serde_json::to_value(vec![Role::Admin]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
||||||
@ -342,7 +342,7 @@ mod tests {
|
|||||||
id: 1,
|
id: 1,
|
||||||
name: "test1".to_string(),
|
name: "test1".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: Some("walker@163.com".to_string()),
|
email: Some("walker@163.com".to_string()),
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now() - Duration::from_secs(60),
|
created_at: Local::now() - Duration::from_secs(60),
|
||||||
@ -352,7 +352,7 @@ mod tests {
|
|||||||
id: 2,
|
id: 2,
|
||||||
name: "test2".to_string(),
|
name: "test2".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::Admin, Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::Admin, Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: Some("123456789".to_string()),
|
mobile: Some("123456789".to_string()),
|
||||||
created_at: Local::now() - Duration::from_secs(60),
|
created_at: Local::now() - Duration::from_secs(60),
|
||||||
@ -362,7 +362,7 @@ mod tests {
|
|||||||
id: 3,
|
id: 3,
|
||||||
name: "test3".to_string(),
|
name: "test3".to_string(),
|
||||||
password: "password".to_string(),
|
password: "password".to_string(),
|
||||||
roles: serde_json::to_value(&vec![Role::User]).unwrap(),
|
roles: serde_json::to_value(vec![Role::User]).unwrap(),
|
||||||
email: None,
|
email: None,
|
||||||
mobile: None,
|
mobile: None,
|
||||||
created_at: Local::now(),
|
created_at: Local::now(),
|
@ -1,4 +1,4 @@
|
|||||||
use rtss_dto::common::{DataType, FeatureType, Role};
|
use rtsa_dto::common::{DataType, FeatureType, Role};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use sqlx::types::{
|
use sqlx::types::{
|
||||||
chrono::{DateTime, Local},
|
chrono::{DateTime, Local},
|
||||||
@ -7,6 +7,18 @@ use sqlx::types::{
|
|||||||
|
|
||||||
use crate::common::TableColumn;
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum UserColumn {
|
pub enum UserColumn {
|
||||||
Table,
|
Table,
|
||||||
@ -34,7 +46,7 @@ pub struct UserModel {
|
|||||||
pub updated_at: DateTime<Local>,
|
pub updated_at: DateTime<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 数据库表 rtss.draft_data 列映射
|
/// 数据库表 rtsa.draft_data 列映射
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DraftDataColumn {
|
pub enum DraftDataColumn {
|
||||||
Table,
|
Table,
|
||||||
@ -68,7 +80,7 @@ pub struct DraftDataModel {
|
|||||||
pub updated_at: DateTime<Local>,
|
pub updated_at: DateTime<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 数据库表 rtss.release_data 列映射
|
/// 数据库表 rtsa.release_data 列映射
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ReleaseDataColumn {
|
pub enum ReleaseDataColumn {
|
||||||
Table,
|
Table,
|
||||||
@ -99,9 +111,9 @@ pub struct ReleaseDataModel {
|
|||||||
pub updated_at: DateTime<Local>,
|
pub updated_at: DateTime<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 数据库表 rtss.release_data_version 列映射
|
/// 数据库表 rtsa.release_data_version 列映射
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum ReleaseDataVersionColumn {
|
pub enum ReleaseDataVersionColumn {
|
||||||
Table,
|
Table,
|
||||||
Id,
|
Id,
|
||||||
ReleaseDataId,
|
ReleaseDataId,
|
||||||
@ -125,10 +137,10 @@ pub struct ReleaseDataVersionModel {
|
|||||||
pub created_at: DateTime<Local>,
|
pub created_at: DateTime<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 数据库表 rtss.feature 列映射
|
/// 数据库表 rtsa.feature 列映射
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) enum FeatureColumn {
|
pub enum FeatureColumn {
|
||||||
Table,
|
Table,
|
||||||
Id,
|
Id,
|
||||||
FeatureType,
|
FeatureType,
|
||||||
@ -157,10 +169,10 @@ pub struct FeatureModel {
|
|||||||
pub updated_at: DateTime<Local>,
|
pub updated_at: DateTime<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 数据库表 rtss.user_config 列映射
|
/// 数据库表 rtsa.user_config 列映射
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) enum UserConfigColumn {
|
pub enum UserConfigColumn {
|
||||||
Table,
|
Table,
|
||||||
Id,
|
Id,
|
||||||
UserId,
|
UserId,
|
||||||
@ -183,7 +195,7 @@ pub struct UserConfigModel {
|
|||||||
impl TableColumn for UserColumn {
|
impl TableColumn for UserColumn {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
UserColumn::Table => "rtss.user",
|
UserColumn::Table => "rtsa.user",
|
||||||
UserColumn::Id => "id",
|
UserColumn::Id => "id",
|
||||||
UserColumn::Username => "username",
|
UserColumn::Username => "username",
|
||||||
UserColumn::Password => "password",
|
UserColumn::Password => "password",
|
||||||
@ -199,7 +211,7 @@ impl TableColumn for UserColumn {
|
|||||||
impl TableColumn for DraftDataColumn {
|
impl TableColumn for DraftDataColumn {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
DraftDataColumn::Table => "rtss.draft_data",
|
DraftDataColumn::Table => "rtsa.draft_data",
|
||||||
DraftDataColumn::Id => "id",
|
DraftDataColumn::Id => "id",
|
||||||
DraftDataColumn::Name => "name",
|
DraftDataColumn::Name => "name",
|
||||||
DraftDataColumn::DataType => "data_type",
|
DraftDataColumn::DataType => "data_type",
|
||||||
@ -217,7 +229,7 @@ impl TableColumn for DraftDataColumn {
|
|||||||
impl TableColumn for ReleaseDataColumn {
|
impl TableColumn for ReleaseDataColumn {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
ReleaseDataColumn::Table => "rtss.release_data",
|
ReleaseDataColumn::Table => "rtsa.release_data",
|
||||||
ReleaseDataColumn::Id => "id",
|
ReleaseDataColumn::Id => "id",
|
||||||
ReleaseDataColumn::Name => "name",
|
ReleaseDataColumn::Name => "name",
|
||||||
ReleaseDataColumn::DataType => "data_type",
|
ReleaseDataColumn::DataType => "data_type",
|
||||||
@ -234,7 +246,7 @@ impl TableColumn for ReleaseDataColumn {
|
|||||||
impl TableColumn for ReleaseDataVersionColumn {
|
impl TableColumn for ReleaseDataVersionColumn {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
ReleaseDataVersionColumn::Table => "rtss.release_data_version",
|
ReleaseDataVersionColumn::Table => "rtsa.release_data_version",
|
||||||
ReleaseDataVersionColumn::Id => "id",
|
ReleaseDataVersionColumn::Id => "id",
|
||||||
ReleaseDataVersionColumn::ReleaseDataId => "release_data_id",
|
ReleaseDataVersionColumn::ReleaseDataId => "release_data_id",
|
||||||
ReleaseDataVersionColumn::Options => "options",
|
ReleaseDataVersionColumn::Options => "options",
|
||||||
@ -249,7 +261,7 @@ impl TableColumn for ReleaseDataVersionColumn {
|
|||||||
impl TableColumn for FeatureColumn {
|
impl TableColumn for FeatureColumn {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
FeatureColumn::Table => "rtss.feature",
|
FeatureColumn::Table => "rtsa.feature",
|
||||||
FeatureColumn::Id => "id",
|
FeatureColumn::Id => "id",
|
||||||
FeatureColumn::FeatureType => "feature_type",
|
FeatureColumn::FeatureType => "feature_type",
|
||||||
FeatureColumn::Name => "name",
|
FeatureColumn::Name => "name",
|
||||||
@ -267,7 +279,7 @@ impl TableColumn for FeatureColumn {
|
|||||||
impl TableColumn for UserConfigColumn {
|
impl TableColumn for UserConfigColumn {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
UserConfigColumn::Table => "rtss.user_config",
|
UserConfigColumn::Table => "rtsa.user_config",
|
||||||
UserConfigColumn::Id => "id",
|
UserConfigColumn::Id => "id",
|
||||||
UserConfigColumn::UserId => "user_id",
|
UserConfigColumn::UserId => "user_id",
|
||||||
UserConfigColumn::ConfigType => "config_type",
|
UserConfigColumn::ConfigType => "config_type",
|
@ -1,10 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rtss_dto"
|
name = "rtsa_dto"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
prost = "0.13"
|
prost = "0.13"
|
||||||
|
prost-types = "0.13"
|
||||||
async-graphql = { version = "7.0.7", features = ["chrono"] }
|
async-graphql = { version = "7.0.7", features = ["chrono"] }
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
@ -16,7 +16,7 @@ fn main() {
|
|||||||
{
|
{
|
||||||
std::env::set_var(
|
std::env::set_var(
|
||||||
"PROTOC",
|
"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()
|
Config::new()
|
||||||
@ -39,11 +39,12 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.compile_protos(
|
.compile_protos(
|
||||||
&[
|
&[
|
||||||
"../../rtss-proto-msg/src/em_data.proto",
|
"../../rtsa-proto-msg/src/em_data.proto",
|
||||||
"../../rtss-proto-msg/src/common.proto",
|
"../../rtsa-proto-msg/src/common.proto",
|
||||||
"../../rtss-proto-msg/src/iscs_graphic_data.proto",
|
"../../rtsa-proto-msg/src/iscs_graphic_data.proto",
|
||||||
|
"../../rtsa-proto-msg/src/simulation.proto",
|
||||||
],
|
],
|
||||||
&["../../rtss-proto-msg/src/"],
|
&["../../rtsa-proto-msg/src/"],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -51,5 +52,5 @@ fn main() {
|
|||||||
Command::new("cargo")
|
Command::new("cargo")
|
||||||
.args(["fmt"])
|
.args(["fmt"])
|
||||||
.status()
|
.status()
|
||||||
.expect("Failed to run cargo fmt on rtss-dto");
|
.expect("Failed to run cargo fmt on rtsa-dto");
|
||||||
}
|
}
|
27
crates/rtsa_dto/src/lib.rs
Normal file
27
crates/rtsa_dto/src/lib.rs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
662
crates/rtsa_dto/src/pb/iscs_graphic_data.rs
Normal file
662
crates/rtsa_dto/src/pb/iscs_graphic_data.rs
Normal file
@ -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 common;
|
||||||
pub mod em_data;
|
pub mod em_data;
|
||||||
|
pub mod simulation;
|
77
crates/rtsa_dto/src/pb/simulation.rs
Normal file
77
crates/rtsa_dto/src/pb/simulation.rs
Normal file
@ -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]
|
[package]
|
||||||
name = "rtss_log"
|
name = "rtsa_log"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
15
crates/rtsa_mqtt/Cargo.toml
Normal file
15
crates/rtsa_mqtt/Cargo.toml
Normal file
@ -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" }
|
16
crates/rtsa_mqtt/src/error.rs
Normal file
16
crates/rtsa_mqtt/src/error.rs
Normal file
@ -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,
|
||||||
|
}
|
512
crates/rtsa_mqtt/src/lib.rs
Normal file
512
crates/rtsa_mqtt/src/lib.rs
Normal file
@ -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());
|
||||||
|
}
|
||||||
|
}
|
247
crates/rtsa_mqtt/src/service.rs
Normal file
247
crates/rtsa_mqtt/src/service.rs
Normal file
@ -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::*;
|
|
15
manager/Cargo.toml
Normal file
15
manager/Cargo.toml
Normal file
@ -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
|
RUN cp /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
|
||||||
WORKDIR /rtss_sim
|
WORKDIR /rtsa
|
||||||
COPY ./target/x86_64-unknown-linux-musl/release/rtss_simulation ./rtss_sim
|
COPY ./target/x86_64-unknown-linux-musl/release/manager ./rtsa_m
|
||||||
COPY ./conf/* ./conf/
|
COPY ./conf/* ./conf/
|
||||||
COPY ./migrations/* ./migrations/
|
COPY ./migrations/* ./migrations/
|
||||||
|
|
||||||
EXPOSE 8765
|
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"
|
url = "postgresql://joylink:Joylink@0503@localhost:5432/joylink"
|
||||||
|
|
||||||
[sso]
|
[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"
|
level = "debug"
|
||||||
|
|
||||||
[sso]
|
[sso]
|
||||||
base_url = "http://192.168.33.233/rtss-server"
|
base_url = "http://192.168.33.233/rtsa-server"
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rtss_api"
|
name = "rtsa_api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@ -19,9 +19,6 @@ base64 = "0.22.1"
|
|||||||
sysinfo = "0.31.3"
|
sysinfo = "0.31.3"
|
||||||
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls", "json"] }
|
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls", "json"] }
|
||||||
|
|
||||||
bevy_ecs = { workspace = true }
|
rtsa_log = { path = "../../../crates/rtsa_log" }
|
||||||
rtss_log = { path = "../rtss_log" }
|
rtsa_db = { path = "../../../crates/rtsa_db" }
|
||||||
rtss_sim_manage = { path = "../rtss_sim_manage" }
|
rtsa_dto = { path = "../../../crates/rtsa_dto" }
|
||||||
rtss_trackside = { path = "../rtss_trackside" }
|
|
||||||
rtss_db = { path = "../rtss_db" }
|
|
||||||
rtss_dto = { path = "../rtss_dto" }
|
|
@ -1,6 +1,6 @@
|
|||||||
use async_graphql::{InputObject, InputType, OutputType, SimpleObject};
|
use async_graphql::{InputObject, InputType, OutputType, SimpleObject};
|
||||||
use rtss_db::{common::TableColumn, model::DraftDataColumn};
|
use rtsa_db::{common::TableColumn, model::DraftDataColumn};
|
||||||
use rtss_dto::common::IscsStyle;
|
use rtsa_dto::common::IscsStyle;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ pub trait DataOptions: InputType + OutputType + Serialize + DeserializeOwned {
|
|||||||
|
|
||||||
impl DataOptions for Value {
|
impl DataOptions for Value {
|
||||||
fn to_data_options_filter_clause(&self) -> String {
|
fn to_data_options_filter_clause(&self) -> String {
|
||||||
format!("options @> '{}'", self)
|
format!("{} @> '{}'", DraftDataColumn::Options.name(), self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iscs_data_options_serialize() {
|
fn test_iscs_data_options_serialize() {
|
||||||
rtss_log::Logging::default().init();
|
rtsa_log::Logging::default().init();
|
||||||
let options = IscsDataOptions {
|
let options = IscsDataOptions {
|
||||||
style: IscsStyle::DaShiZhiNeng,
|
style: IscsStyle::DaShiZhiNeng,
|
||||||
};
|
};
|
@ -3,15 +3,15 @@ use async_graphql::{ComplexObject, Context, InputObject, Object, SimpleObject};
|
|||||||
use base64::prelude::*;
|
use base64::prelude::*;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use rtss_db::DraftDataAccessor;
|
use rtsa_db::DraftDataAccessor;
|
||||||
use rtss_db::RtssDbAccessor;
|
use rtsa_db::RtsaDbAccessor;
|
||||||
use rtss_dto::common::{DataType, Role};
|
use rtsa_dto::common::{DataType, Role};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::apis::{PageDto, PageQueryDto};
|
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::release_data::ReleaseDataId;
|
||||||
use super::user::UserId;
|
use super::user::UserId;
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ impl DraftDataQuery {
|
|||||||
paging: PageQueryDto,
|
paging: PageQueryDto,
|
||||||
query: DraftDataFilterDto<Value>,
|
query: DraftDataFilterDto<Value>,
|
||||||
) -> async_graphql::Result<PageDto<DraftDataDto>> {
|
) -> async_graphql::Result<PageDto<DraftDataDto>> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let paging_result = db_accessor
|
let paging_result = db_accessor
|
||||||
.paging_query_draft_data(query.into(), paging.into())
|
.paging_query_draft_data(query.into(), paging.into())
|
||||||
.await?;
|
.await?;
|
||||||
@ -53,7 +53,7 @@ impl DraftDataQuery {
|
|||||||
.await?;
|
.await?;
|
||||||
query.user_id = user.id_i32();
|
query.user_id = user.id_i32();
|
||||||
query.data_type = Some(DataType::Iscs);
|
query.data_type = Some(DataType::Iscs);
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let paging_result = db_accessor
|
let paging_result = db_accessor
|
||||||
.paging_query_draft_data(query.into(), paging.into())
|
.paging_query_draft_data(query.into(), paging.into())
|
||||||
.await?;
|
.await?;
|
||||||
@ -67,7 +67,7 @@ impl DraftDataQuery {
|
|||||||
paging: PageQueryDto,
|
paging: PageQueryDto,
|
||||||
mut query: DraftDataFilterDto<IscsDataOptions>,
|
mut query: DraftDataFilterDto<IscsDataOptions>,
|
||||||
) -> async_graphql::Result<PageDto<DraftIscsDataDto>> {
|
) -> async_graphql::Result<PageDto<DraftIscsDataDto>> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
query.data_type = Some(DataType::Iscs);
|
query.data_type = Some(DataType::Iscs);
|
||||||
let paging_result = db_accessor
|
let paging_result = db_accessor
|
||||||
.paging_query_draft_data(query.into(), paging.into())
|
.paging_query_draft_data(query.into(), paging.into())
|
||||||
@ -77,7 +77,7 @@ impl DraftDataQuery {
|
|||||||
/// 根据id获取草稿数据
|
/// 根据id获取草稿数据
|
||||||
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
#[graphql(guard = "RoleGuard::new(Role::User)")]
|
||||||
async fn draft_data(&self, ctx: &Context<'_>, id: i32) -> async_graphql::Result<DraftDataDto> {
|
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?;
|
let draft_data = db_accessor.query_draft_data_by_id(id).await?;
|
||||||
Ok(draft_data.into())
|
Ok(draft_data.into())
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ impl DraftDataQuery {
|
|||||||
.query_user(&ctx.data::<Token>()?.0)
|
.query_user(&ctx.data::<Token>()?.0)
|
||||||
.await?;
|
.await?;
|
||||||
let user_id = user.id_i32();
|
let user_id = user.id_i32();
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let exist = db_accessor
|
let exist = db_accessor
|
||||||
.is_draft_data_exist(user_id, &data_type, &name)
|
.is_draft_data_exist(user_id, &data_type, &name)
|
||||||
.await?;
|
.await?;
|
||||||
@ -115,8 +115,8 @@ impl DraftDataMutation {
|
|||||||
.data::<UserAuthCache>()?
|
.data::<UserAuthCache>()?
|
||||||
.query_user(&ctx.data::<Token>()?.0)
|
.query_user(&ctx.data::<Token>()?.0)
|
||||||
.await?;
|
.await?;
|
||||||
input = input.with_user_id(user.id_i32());
|
input = input.with_data_type_and_user_id(DataType::Iscs, user.id_i32());
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let draft_data = db_accessor.create_draft_data(input.into()).await?;
|
let draft_data = db_accessor.create_draft_data(input.into()).await?;
|
||||||
Ok(draft_data.into())
|
Ok(draft_data.into())
|
||||||
}
|
}
|
||||||
@ -128,7 +128,7 @@ impl DraftDataMutation {
|
|||||||
id: i32,
|
id: i32,
|
||||||
name: String,
|
name: String,
|
||||||
) -> async_graphql::Result<DraftDataDto> {
|
) -> 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?;
|
let draft_data = db_accessor.update_draft_data_name(id, &name).await?;
|
||||||
Ok(draft_data.into())
|
Ok(draft_data.into())
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ impl DraftDataMutation {
|
|||||||
id: i32,
|
id: i32,
|
||||||
data: String, // base64编码的数据
|
data: String, // base64编码的数据
|
||||||
) -> async_graphql::Result<DraftDataDto> {
|
) -> async_graphql::Result<DraftDataDto> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let bytes = BASE64_STANDARD
|
let bytes = BASE64_STANDARD
|
||||||
.decode(data)
|
.decode(data)
|
||||||
.map_err(|e| async_graphql::Error::new(format!("base64 decode error: {}", e)))?;
|
.map_err(|e| async_graphql::Error::new(format!("base64 decode error: {}", e)))?;
|
||||||
@ -156,7 +156,7 @@ impl DraftDataMutation {
|
|||||||
id: i32,
|
id: i32,
|
||||||
is_shared: bool,
|
is_shared: bool,
|
||||||
) -> async_graphql::Result<DraftDataDto> {
|
) -> 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?;
|
let draft_data = db_accessor.set_draft_data_shared(id, is_shared).await?;
|
||||||
Ok(draft_data.into())
|
Ok(draft_data.into())
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ impl DraftDataMutation {
|
|||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
id: Vec<i32>,
|
id: Vec<i32>,
|
||||||
) -> async_graphql::Result<bool> {
|
) -> 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?;
|
db_accessor.delete_draft_data(id.as_slice()).await?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ impl DraftDataMutation {
|
|||||||
id: i32,
|
id: i32,
|
||||||
release_data_id: i32,
|
release_data_id: i32,
|
||||||
) -> async_graphql::Result<DraftDataDto> {
|
) -> async_graphql::Result<DraftDataDto> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let draft_data = db_accessor
|
let draft_data = db_accessor
|
||||||
.set_default_release_data_id(id, release_data_id)
|
.set_default_release_data_id(id, release_data_id)
|
||||||
.await?;
|
.await?;
|
||||||
@ -198,7 +198,7 @@ impl DraftDataMutation {
|
|||||||
.query_user(&ctx.data::<Token>()?.0)
|
.query_user(&ctx.data::<Token>()?.0)
|
||||||
.await?;
|
.await?;
|
||||||
let user_id = user.id_i32();
|
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?;
|
let draft_data = db_accessor.save_as_new_draft(id, &name, user_id).await?;
|
||||||
Ok(draft_data.into())
|
Ok(draft_data.into())
|
||||||
}
|
}
|
||||||
@ -207,6 +207,8 @@ impl DraftDataMutation {
|
|||||||
#[derive(Debug, InputObject)]
|
#[derive(Debug, InputObject)]
|
||||||
#[graphql(concrete(name = "CreateDraftIscsDto", params(IscsDataOptions)))]
|
#[graphql(concrete(name = "CreateDraftIscsDto", params(IscsDataOptions)))]
|
||||||
pub struct CreateDraftDataDto<T: DataOptions> {
|
pub struct CreateDraftDataDto<T: DataOptions> {
|
||||||
|
#[graphql(skip)]
|
||||||
|
pub data_type: Option<DataType>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub options: Option<T>,
|
pub options: Option<T>,
|
||||||
#[graphql(skip)]
|
#[graphql(skip)]
|
||||||
@ -214,17 +216,18 @@ pub struct CreateDraftDataDto<T: DataOptions> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: DataOptions> CreateDraftDataDto<T> {
|
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.user_id = Some(id);
|
||||||
self
|
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 {
|
fn from(value: CreateDraftDataDto<T>) -> Self {
|
||||||
let cdd = Self::new(
|
let cdd = Self::new(
|
||||||
&value.name,
|
&value.name,
|
||||||
DataType::Iscs,
|
value.data_type.expect("need data_type"),
|
||||||
value.user_id.expect("CreateDraftDataDto need user_id"),
|
value.user_id.expect("CreateDraftDataDto need user_id"),
|
||||||
);
|
);
|
||||||
if value.options.is_some() {
|
if value.options.is_some() {
|
||||||
@ -243,12 +246,12 @@ pub struct UserDraftDataFilterDto<T: DataOptions> {
|
|||||||
pub user_id: i32,
|
pub user_id: i32,
|
||||||
pub name: Option<String>,
|
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 options: Option<T>,
|
||||||
pub is_shared: Option<bool>,
|
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 {
|
fn from(value: UserDraftDataFilterDto<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
user_id: Some(value.user_id),
|
user_id: Some(value.user_id),
|
||||||
@ -274,7 +277,7 @@ pub struct DraftDataFilterDto<T: DataOptions> {
|
|||||||
pub options: Option<T>,
|
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 {
|
fn from(value: DraftDataFilterDto<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
user_id: value.user_id,
|
user_id: value.user_id,
|
||||||
@ -310,7 +313,7 @@ impl DraftDataDto {
|
|||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
) -> async_graphql::Result<Option<String>> {
|
) -> async_graphql::Result<Option<String>> {
|
||||||
if let Some(version_id) = self.default_release_data_id {
|
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?;
|
let name = loader.load_one(ReleaseDataId::new(version_id)).await?;
|
||||||
Ok(name)
|
Ok(name)
|
||||||
} else {
|
} else {
|
||||||
@ -320,14 +323,14 @@ impl DraftDataDto {
|
|||||||
|
|
||||||
/// 获取用户name
|
/// 获取用户name
|
||||||
async fn user_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
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?;
|
let name = loader.load_one(UserId::new(self.user_id)).await?;
|
||||||
Ok(name)
|
Ok(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<rtss_db::model::DraftDataModel> for DraftDataDto {
|
impl From<rtsa_db::model::DraftDataModel> for DraftDataDto {
|
||||||
fn from(value: rtss_db::model::DraftDataModel) -> Self {
|
fn from(value: rtsa_db::model::DraftDataModel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: value.id,
|
id: value.id,
|
||||||
name: value.name,
|
name: value.name,
|
||||||
@ -351,8 +354,8 @@ pub struct DraftIscsDataDto {
|
|||||||
pub options: Option<IscsDataOptions>,
|
pub options: Option<IscsDataOptions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<rtss_db::model::DraftDataModel> for DraftIscsDataDto {
|
impl From<rtsa_db::model::DraftDataModel> for DraftIscsDataDto {
|
||||||
fn from(value: rtss_db::model::DraftDataModel) -> Self {
|
fn from(value: rtsa_db::model::DraftDataModel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
options: value
|
options: value
|
||||||
.options
|
.options
|
228
manager/crates/rtsa_api/src/apis/feature.rs
Normal file
228
manager/crates/rtsa_api/src/apis/feature.rs
Normal file
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
manager/crates/rtsa_api/src/apis/feature_config_def.rs
Normal file
17
manager/crates/rtsa_api/src/apis/feature_config_def.rs
Normal file
@ -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 feature::{FeatureMutation, FeatureQuery};
|
||||||
use release_data::{ReleaseDataMutation, ReleaseDataQuery};
|
use release_data::{ReleaseDataMutation, ReleaseDataQuery};
|
||||||
|
|
||||||
mod simulation_definition;
|
|
||||||
mod sys_info;
|
mod sys_info;
|
||||||
use simulation_definition::*;
|
|
||||||
use user::{UserMutation, UserQuery};
|
use user::{UserMutation, UserQuery};
|
||||||
|
|
||||||
mod common;
|
mod data_options_def;
|
||||||
mod draft_data;
|
mod draft_data;
|
||||||
mod feature;
|
mod feature;
|
||||||
|
mod feature_config_def;
|
||||||
mod release_data;
|
mod release_data;
|
||||||
mod simulation;
|
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
#[derive(Default, MergedObject)]
|
#[derive(Default, MergedObject)]
|
||||||
@ -27,7 +25,7 @@ pub struct Mutation(
|
|||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Enum, Copy, Clone, Default, Eq, PartialEq, Debug)]
|
#[derive(Enum, Copy, Clone, Default, Eq, PartialEq, Debug)]
|
||||||
#[graphql(remote = "rtss_db::common::SortOrder")]
|
#[graphql(remote = "rtsa_db::common::SortOrder")]
|
||||||
pub enum SortOrder {
|
pub enum SortOrder {
|
||||||
#[default]
|
#[default]
|
||||||
Asc,
|
Asc,
|
||||||
@ -42,7 +40,7 @@ pub struct PageQueryDto {
|
|||||||
pub items_per_page: i32,
|
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 {
|
fn from(value: PageQueryDto) -> Self {
|
||||||
Self {
|
Self {
|
||||||
page: value.page,
|
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> {
|
impl<T: OutputType, M: Into<T>> From<rtsa_db::common::PageResult<M>> for PageDto<T> {
|
||||||
fn from(value: rtss_db::common::PageResult<M>) -> Self {
|
fn from(value: rtsa_db::common::PageResult<M>) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
value.total,
|
value.total,
|
||||||
value.data.into_iter().map(|m| m.into()).collect(),
|
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 async_graphql::{ComplexObject, Context, InputObject, Object, SimpleObject};
|
||||||
use base64::prelude::*;
|
use base64::prelude::*;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use rtss_db::model::*;
|
use rtsa_db::model::*;
|
||||||
use rtss_db::prelude::*;
|
use rtsa_db::prelude::*;
|
||||||
use rtss_db::{model::ReleaseDataModel, ReleaseDataAccessor, RtssDbAccessor};
|
use rtsa_db::{model::ReleaseDataModel, ReleaseDataAccessor, RtsaDbAccessor};
|
||||||
use rtss_dto::common::{DataType, Role};
|
use rtsa_dto::common::{DataType, Role};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::apis::draft_data::DraftDataDto;
|
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::user::UserId;
|
||||||
use super::{PageDto, PageQueryDto};
|
use super::{PageDto, PageQueryDto};
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ impl ReleaseDataQuery {
|
|||||||
page: PageQueryDto,
|
page: PageQueryDto,
|
||||||
query: ReleaseTypedDataFilterDto<Value>,
|
query: ReleaseTypedDataFilterDto<Value>,
|
||||||
) -> async_graphql::Result<PageDto<ReleaseDataDto>> {
|
) -> async_graphql::Result<PageDto<ReleaseDataDto>> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let paging = db_accessor
|
let paging = db_accessor
|
||||||
.paging_query_release_data_list(query.into(), page.into())
|
.paging_query_release_data_list(query.into(), page.into())
|
||||||
.await?;
|
.await?;
|
||||||
@ -51,7 +51,7 @@ impl ReleaseDataQuery {
|
|||||||
page: PageQueryDto,
|
page: PageQueryDto,
|
||||||
mut query: ReleaseTypedDataFilterDto<IscsDataOptions>,
|
mut query: ReleaseTypedDataFilterDto<IscsDataOptions>,
|
||||||
) -> async_graphql::Result<PageDto<ReleaseIscsDataWithoutVersionDto>> {
|
) -> async_graphql::Result<PageDto<ReleaseIscsDataWithoutVersionDto>> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
query.data_type = Some(DataType::Iscs);
|
query.data_type = Some(DataType::Iscs);
|
||||||
let paging = db_accessor
|
let paging = db_accessor
|
||||||
.paging_query_release_data_list(query.into(), page.into())
|
.paging_query_release_data_list(query.into(), page.into())
|
||||||
@ -66,7 +66,7 @@ impl ReleaseDataQuery {
|
|||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
id: i32,
|
id: i32,
|
||||||
) -> async_graphql::Result<ReleaseDataWithUsedVersionDto> {
|
) -> 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?;
|
let model = db_accessor.query_release_data_with_used_version(id).await?;
|
||||||
Ok(model.into())
|
Ok(model.into())
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ impl ReleaseDataQuery {
|
|||||||
data_type: DataType,
|
data_type: DataType,
|
||||||
name: String,
|
name: String,
|
||||||
) -> async_graphql::Result<bool> {
|
) -> async_graphql::Result<bool> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let result = db_accessor
|
let result = db_accessor
|
||||||
.is_release_data_name_exist(data_type, &name)
|
.is_release_data_name_exist(data_type, &name)
|
||||||
.await?;
|
.await?;
|
||||||
@ -94,7 +94,7 @@ impl ReleaseDataQuery {
|
|||||||
data_id: i32,
|
data_id: i32,
|
||||||
page: PageQueryDto,
|
page: PageQueryDto,
|
||||||
) -> async_graphql::Result<PageDto<ReleaseDataVersionDto>> {
|
) -> async_graphql::Result<PageDto<ReleaseDataVersionDto>> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let paging = db_accessor
|
let paging = db_accessor
|
||||||
.paging_query_release_data_version_list(data_id, page.into())
|
.paging_query_release_data_version_list(data_id, page.into())
|
||||||
.await?;
|
.await?;
|
||||||
@ -108,7 +108,7 @@ impl ReleaseDataQuery {
|
|||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
version_id: i32,
|
version_id: i32,
|
||||||
) -> async_graphql::Result<ReleaseDataVersionDto> {
|
) -> async_graphql::Result<ReleaseDataVersionDto> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let model = db_accessor
|
let model = db_accessor
|
||||||
.query_release_data_version_by_id(version_id)
|
.query_release_data_version_by_id(version_id)
|
||||||
.await?;
|
.await?;
|
||||||
@ -132,7 +132,7 @@ impl ReleaseDataMutation {
|
|||||||
.query_user(&ctx.data::<Token>()?.0)
|
.query_user(&ctx.data::<Token>()?.0)
|
||||||
.await?;
|
.await?;
|
||||||
let user_id = user.id_i32();
|
let user_id = user.id_i32();
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let result = db_accessor
|
let result = db_accessor
|
||||||
.release_new_from_draft(draft_id, &name, &description, Some(user_id))
|
.release_new_from_draft(draft_id, &name, &description, Some(user_id))
|
||||||
.await?;
|
.await?;
|
||||||
@ -152,7 +152,7 @@ impl ReleaseDataMutation {
|
|||||||
.query_user(&ctx.data::<Token>()?.0)
|
.query_user(&ctx.data::<Token>()?.0)
|
||||||
.await?;
|
.await?;
|
||||||
let user_id = user.id_i32();
|
let user_id = user.id_i32();
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let result = db_accessor
|
let result = db_accessor
|
||||||
.release_to_existing(draft_id, &description, Some(user_id))
|
.release_to_existing(draft_id, &description, Some(user_id))
|
||||||
.await?;
|
.await?;
|
||||||
@ -167,7 +167,7 @@ impl ReleaseDataMutation {
|
|||||||
id: i32,
|
id: i32,
|
||||||
name: String,
|
name: String,
|
||||||
) -> async_graphql::Result<ReleaseDataDto> {
|
) -> 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?;
|
let result = db_accessor.update_release_data_name(id, &name).await?;
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ impl ReleaseDataMutation {
|
|||||||
id: i32,
|
id: i32,
|
||||||
is_published: bool,
|
is_published: bool,
|
||||||
) -> async_graphql::Result<ReleaseDataDto> {
|
) -> async_graphql::Result<ReleaseDataDto> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let result = db_accessor
|
let result = db_accessor
|
||||||
.set_release_data_published(id, is_published)
|
.set_release_data_published(id, is_published)
|
||||||
.await?;
|
.await?;
|
||||||
@ -195,7 +195,7 @@ impl ReleaseDataMutation {
|
|||||||
id: i32,
|
id: i32,
|
||||||
version_id: i32,
|
version_id: i32,
|
||||||
) -> async_graphql::Result<ReleaseDataDto> {
|
) -> async_graphql::Result<ReleaseDataDto> {
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let result = db_accessor
|
let result = db_accessor
|
||||||
.set_release_data_used_version(id, version_id)
|
.set_release_data_used_version(id, version_id)
|
||||||
.await?;
|
.await?;
|
||||||
@ -214,7 +214,7 @@ impl ReleaseDataMutation {
|
|||||||
.query_user(&ctx.data::<Token>()?.0)
|
.query_user(&ctx.data::<Token>()?.0)
|
||||||
.await?;
|
.await?;
|
||||||
let user_id = user.id_i32();
|
let user_id = user.id_i32();
|
||||||
let db_accessor = ctx.data::<RtssDbAccessor>()?;
|
let db_accessor = ctx.data::<RtsaDbAccessor>()?;
|
||||||
let result = db_accessor
|
let result = db_accessor
|
||||||
.create_draft_from_release_version(version_id, user_id)
|
.create_draft_from_release_version(version_id, user_id)
|
||||||
.await?;
|
.await?;
|
||||||
@ -235,7 +235,7 @@ pub struct ReleaseTypedDataFilterDto<T: DataOptions> {
|
|||||||
pub is_published: Option<bool>,
|
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 {
|
fn from(value: ReleaseTypedDataFilterDto<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: value.name,
|
name: value.name,
|
||||||
@ -265,7 +265,7 @@ pub struct ReleaseDataDto {
|
|||||||
impl ReleaseDataDto {
|
impl ReleaseDataDto {
|
||||||
async fn description(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
async fn description(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||||
if let Some(version_id) = self.used_version_id {
|
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
|
let description = loader
|
||||||
.load_one(ReleaseDataVersionId::new(version_id))
|
.load_one(ReleaseDataVersionId::new(version_id))
|
||||||
.await?;
|
.await?;
|
||||||
@ -277,7 +277,7 @@ impl ReleaseDataDto {
|
|||||||
|
|
||||||
/// 获取用户name
|
/// 获取用户name
|
||||||
async fn user_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
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?;
|
let name = loader.load_one(UserId::new(self.user_id)).await?;
|
||||||
Ok(name)
|
Ok(name)
|
||||||
}
|
}
|
||||||
@ -294,7 +294,7 @@ impl ReleaseDataId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loader<ReleaseDataId> for RtssDbLoader {
|
impl Loader<ReleaseDataId> for RtsaDbLoader {
|
||||||
type Value = String;
|
type Value = String;
|
||||||
type Error = Arc<DbAccessError>;
|
type Error = Arc<DbAccessError>;
|
||||||
|
|
||||||
@ -326,7 +326,7 @@ impl ReleaseDataVersionId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loader<ReleaseDataVersionId> for RtssDbLoader {
|
impl Loader<ReleaseDataVersionId> for RtsaDbLoader {
|
||||||
type Value = String;
|
type Value = String;
|
||||||
type Error = Arc<DbAccessError>;
|
type Error = Arc<DbAccessError>;
|
||||||
|
|
||||||
@ -388,7 +388,7 @@ pub struct ReleaseDataVersionDto {
|
|||||||
impl ReleaseDataVersionDto {
|
impl ReleaseDataVersionDto {
|
||||||
/// 获取用户name
|
/// 获取用户name
|
||||||
async fn user_name(&self, ctx: &Context<'_>) -> async_graphql::Result<Option<String>> {
|
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?;
|
let name = loader.load_one(UserId::new(self.user_id)).await?;
|
||||||
Ok(name)
|
Ok(name)
|
||||||
}
|
}
|
@ -2,14 +2,14 @@ use std::{collections::HashMap, sync::Arc};
|
|||||||
|
|
||||||
use async_graphql::{dataloader::Loader, Context, InputObject, Object, SimpleObject};
|
use async_graphql::{dataloader::Loader, Context, InputObject, Object, SimpleObject};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use rtss_db::{DbAccessError, RtssDbAccessor, UserAccessor};
|
use rtsa_db::{DbAccessError, RtsaDbAccessor, UserAccessor};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
loader::RtssDbLoader,
|
loader::RtsaDbLoader,
|
||||||
user_auth::{build_jwt, Claims, RoleGuard, Token, UserAuthCache, UserInfoDto},
|
user_auth::{build_jwt, Claims, RoleGuard, Token, UserAuthCache, UserInfoDto},
|
||||||
UserAuthClient,
|
UserAuthClient,
|
||||||
};
|
};
|
||||||
use rtss_dto::common::Role;
|
use rtsa_dto::common::Role;
|
||||||
|
|
||||||
use super::{PageDto, PageQueryDto};
|
use super::{PageDto, PageQueryDto};
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ impl UserQuery {
|
|||||||
page: PageQueryDto,
|
page: PageQueryDto,
|
||||||
query: UserQueryDto,
|
query: UserQueryDto,
|
||||||
) -> async_graphql::Result<PageDto<UserDto>> {
|
) -> 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?;
|
let paging = dba.query_user_page(page.into(), query.into()).await?;
|
||||||
Ok(paging.into())
|
Ok(paging.into())
|
||||||
}
|
}
|
||||||
@ -63,12 +63,12 @@ impl UserMutation {
|
|||||||
async fn sync_user(&self, ctx: &Context<'_>) -> async_graphql::Result<bool> {
|
async fn sync_user(&self, ctx: &Context<'_>) -> async_graphql::Result<bool> {
|
||||||
let http_client = ctx.data::<UserAuthClient>()?;
|
let http_client = ctx.data::<UserAuthClient>()?;
|
||||||
let users = http_client.query_all_users(ctx.data::<Token>()?).await?;
|
let users = http_client.query_all_users(ctx.data::<Token>()?).await?;
|
||||||
let dba = ctx.data::<RtssDbAccessor>()?;
|
let dba = ctx.data::<RtsaDbAccessor>()?;
|
||||||
dba.sync_user(
|
dba.sync_user(
|
||||||
users
|
users
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|u| u.into())
|
.map(|u| u.into())
|
||||||
.collect::<Vec<rtss_db::SyncUserInfo>>()
|
.collect::<Vec<rtsa_db::SyncUserInfo>>()
|
||||||
.as_slice(),
|
.as_slice(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -85,7 +85,7 @@ pub struct UserQueryDto {
|
|||||||
pub roles: Option<Vec<Role>>,
|
pub roles: Option<Vec<Role>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UserQueryDto> for rtss_db::UserPageFilter {
|
impl From<UserQueryDto> for rtsa_db::UserPageFilter {
|
||||||
fn from(value: UserQueryDto) -> Self {
|
fn from(value: UserQueryDto) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: value.id,
|
id: value.id,
|
||||||
@ -122,8 +122,8 @@ impl From<UserInfoDto> for UserDto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<rtss_db::model::UserModel> for UserDto {
|
impl From<rtsa_db::model::UserModel> for UserDto {
|
||||||
fn from(value: rtss_db::model::UserModel) -> Self {
|
fn from(value: rtsa_db::model::UserModel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: value.id,
|
id: value.id,
|
||||||
name: value.username,
|
name: value.username,
|
||||||
@ -147,7 +147,7 @@ impl UserId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loader<UserId> for RtssDbLoader {
|
impl Loader<UserId> for RtsaDbLoader {
|
||||||
type Value = String;
|
type Value = String;
|
||||||
type Error = Arc<DbAccessError>;
|
type Error = Arc<DbAccessError>;
|
||||||
|
|
10
manager/crates/rtsa_api/src/loader/mod.rs
Normal file
10
manager/crates/rtsa_api/src/loader/mod.rs
Normal file
@ -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 dataloader::DataLoader;
|
||||||
use http::{playground_source, GraphQLPlaygroundConfig};
|
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 tokio::net::TcpListener;
|
||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
|
|
||||||
use crate::apis::{Mutation, Query};
|
use crate::apis::{Mutation, Query};
|
||||||
use crate::loader::RtssDbLoader;
|
use crate::loader::RtsaDbLoader;
|
||||||
use crate::user_auth;
|
use crate::user_auth;
|
||||||
|
|
||||||
pub use crate::user_auth::UserAuthClient;
|
pub use crate::user_auth::UserAuthClient;
|
||||||
@ -47,7 +48,12 @@ impl ServerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
|
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()
|
let app = Router::new()
|
||||||
.route("/", get(graphiql).post(graphql_handler))
|
.route("/", get(graphiql).post(graphql_handler))
|
||||||
@ -70,7 +76,7 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn graphql_handler(
|
async fn graphql_handler(
|
||||||
State(schema): State<RtssAppSchema>,
|
State(schema): State<RtsaAppSchema>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
req: GraphQLRequest,
|
req: GraphQLRequest,
|
||||||
) -> GraphQLResponse {
|
) -> GraphQLResponse {
|
||||||
@ -86,20 +92,54 @@ async fn graphiql() -> impl IntoResponse {
|
|||||||
Html(playground_source(GraphQLPlaygroundConfig::new("/")))
|
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 {
|
pub struct SchemaOptions {
|
||||||
let client = config
|
pub user_auth_client: UserAuthClient,
|
||||||
.user_auth_client
|
pub user_info_cache: user_auth::UserAuthCache,
|
||||||
.expect("user auth client not configured");
|
pub rtsa_dba: RtsaDbAccessor,
|
||||||
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());
|
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)
|
Schema::build(Query::default(), Mutation::default(), EmptySubscription)
|
||||||
.data(client)
|
.data(options.user_auth_client)
|
||||||
.data(user_info_cache)
|
.data(options.user_info_cache)
|
||||||
.data(dba)
|
.data(options.rtsa_dba)
|
||||||
.data(DataLoader::new(loader, tokio::spawn))
|
.data(DataLoader::new(loader, tokio::spawn))
|
||||||
// .data(MutexSimulationManager::default())
|
// .data(MutexSimulationManager::default())
|
||||||
.finish()
|
.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]
|
#[test]
|
||||||
fn test_jwt() {
|
fn test_jwt() {
|
||||||
rtss_log::Logging::default().init();
|
rtsa_log::Logging::default().init();
|
||||||
let claim = Claims::new(5);
|
let claim = Claims::new(5);
|
||||||
let jwt = build_jwt(claim).unwrap();
|
let jwt = build_jwt(claim).unwrap();
|
||||||
println!("jwt: {}", jwt.0);
|
println!("jwt: {}", jwt.0);
|
@ -6,8 +6,8 @@ use std::{
|
|||||||
use async_graphql::Guard;
|
use async_graphql::Guard;
|
||||||
use axum::http::HeaderMap;
|
use axum::http::HeaderMap;
|
||||||
use chrono::{DateTime, Local};
|
use chrono::{DateTime, Local};
|
||||||
use rtss_dto::common::Role;
|
use rtsa_dto::common::Role;
|
||||||
use rtss_log::tracing::error;
|
use rtsa_log::tracing::error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod jwt_auth;
|
mod jwt_auth;
|
||||||
@ -225,7 +225,7 @@ fn parse_to_date_time(s: &str) -> chrono::DateTime<Local> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UserInfoDto> for rtss_db::SyncUserInfo {
|
impl From<UserInfoDto> for rtsa_db::SyncUserInfo {
|
||||||
fn from(user_info: UserInfoDto) -> Self {
|
fn from(user_info: UserInfoDto) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: user_info.id_i32(),
|
id: user_info.id_i32(),
|
||||||
@ -276,9 +276,6 @@ impl UserAuthCache {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anyhow::Ok;
|
|
||||||
|
|
||||||
use rtss_log::tracing::Level;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -302,25 +299,25 @@ mod tests {
|
|||||||
println!("{:?}", dt);
|
println!("{:?}", dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
// #[tokio::test]
|
||||||
async fn test_user_auth_cache() -> anyhow::Result<()> {
|
// async fn test_user_auth_cache() -> anyhow::Result<()> {
|
||||||
rtss_log::Logging::default().with_level(Level::DEBUG).init();
|
// rtsa_log::Logging::default().with_level(Level::DEBUG).init();
|
||||||
let client = UserAuthClient {
|
// let client = UserAuthClient {
|
||||||
base_url: "http://192.168.33.233/rtss-server".to_string(),
|
// base_url: "http://192.168.33.233/rtsa-server".to_string(),
|
||||||
login_url: "/api/login".to_string(),
|
// login_url: "/api/login".to_string(),
|
||||||
logout_url: "/api/login/logout".to_string(),
|
// logout_url: "/api/login/logout".to_string(),
|
||||||
user_info_url: "/api/login/getUserInfo".to_string(),
|
// user_info_url: "/api/login/getUserInfo".to_string(),
|
||||||
sync_user_url: "/api/userinfo/list/all".to_string(),
|
// sync_user_url: "/api/userinfo/list/all".to_string(),
|
||||||
};
|
// };
|
||||||
let cache = UserAuthCache::new(client.clone());
|
// let cache = UserAuthCache::new(client.clone());
|
||||||
let token = cache.client.login(LoginInfo::default()).await?;
|
// let token = cache.client.login(LoginInfo::default()).await?;
|
||||||
let user = cache.query_user(&token).await?;
|
// let user = cache.query_user(&token).await?;
|
||||||
println!("token: {}, {:?}", token, user);
|
// println!("token: {}, {:?}", token, user);
|
||||||
assert_eq!(cache.len(), 1);
|
// assert_eq!(cache.len(), 1);
|
||||||
|
|
||||||
let user_list = client.query_all_users(&Token(token)).await?;
|
// let user_list = client.query_all_users(&Token(token)).await?;
|
||||||
println!("{:?}", user_list);
|
// println!("{:?}", user_list);
|
||||||
|
|
||||||
Ok(())
|
// Ok(())
|
||||||
}
|
// }
|
||||||
}
|
}
|
@ -20,9 +20,9 @@ pub struct Log {
|
|||||||
level: String,
|
level: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Log> for rtss_log::Logging {
|
impl From<Log> for rtsa_log::Logging {
|
||||||
fn from(log: Log) -> Self {
|
fn from(log: Log) -> Self {
|
||||||
rtss_log::Logging {
|
rtsa_log::Logging {
|
||||||
level: log.level.parse().unwrap(),
|
level: log.level.parse().unwrap(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@ -69,9 +69,9 @@ impl AppConfig {
|
|||||||
// Default to 'dev' env
|
// Default to 'dev' env
|
||||||
// Note that this file is _optional_
|
// Note that this file is _optional_
|
||||||
.add_source(File::with_name(&format!("{dir}/{run_mode}")).required(true))
|
.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
|
// 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
|
// You may also programmatically change settings
|
||||||
// .set_override("database.url", "postgres://")?
|
// .set_override("database.url", "postgres://")?
|
||||||
// build the configuration
|
// build the configuration
|
@ -1,10 +1,12 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
use crate::{app_config, db::DbSubCommand, CmdExecutor};
|
use crate::{app_config, CmdExecutor};
|
||||||
|
|
||||||
|
use super::DbSubCommand;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "rtss-sim", version, author, about, long_about = None)]
|
#[command(name = "rtsa-sim", version, author, about, long_about = None)]
|
||||||
pub struct Cmd {
|
pub struct Cmd {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub cmd: SubCommand,
|
pub cmd: SubCommand,
|
||||||
@ -29,11 +31,11 @@ impl CmdExecutor for ServerOpts {
|
|||||||
async fn execute(&self) -> anyhow::Result<()> {
|
async fn execute(&self) -> anyhow::Result<()> {
|
||||||
let app_config =
|
let app_config =
|
||||||
app_config::AppConfig::new(&self.config_path).expect("Failed to load 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();
|
log.init();
|
||||||
rtss_api::serve(
|
rtsa_api::serve(
|
||||||
rtss_api::ServerConfig::new(&app_config.database.url, app_config.server.port)
|
rtsa_api::ServerConfig::new(&app_config.database.url, app_config.server.port)
|
||||||
.with_user_auth_client(rtss_api::UserAuthClient {
|
.with_user_auth_client(rtsa_api::UserAuthClient {
|
||||||
base_url: app_config.sso.base_url,
|
base_url: app_config.sso.base_url,
|
||||||
login_url: app_config.sso.login_url,
|
login_url: app_config.sso.login_url,
|
||||||
logout_url: app_config.sso.logout_url,
|
logout_url: app_config.sso.logout_url,
|
@ -22,6 +22,6 @@ impl CmdExecutor for MigrateOpts {
|
|||||||
async fn execute(&self) -> anyhow::Result<()> {
|
async fn execute(&self) -> anyhow::Result<()> {
|
||||||
let app_config =
|
let app_config =
|
||||||
app_config::AppConfig::new(&self.config_path).expect("Failed to load 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 cmd;
|
||||||
mod db;
|
mod db;
|
||||||
|
|
4
manager/src/lib.rs
Normal file
4
manager/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
mod app_config;
|
||||||
|
mod commands;
|
||||||
|
|
||||||
|
pub use commands::*;
|
@ -1,5 +1,5 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rtss_simulation::{Cmd, CmdExecutor};
|
use manager::{Cmd, CmdExecutor};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
@ -1 +1 @@
|
|||||||
DROP SCHEMA rtss CASCADE;
|
DROP SCHEMA rtsa CASCADE;
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
-- 初始化数据库SCHEMA(所有轨道交通信号系统仿真的表、类型等都在rtss SCHEMA下)
|
-- 初始化数据库SCHEMA(所有轨道交通信号系统仿真的表、类型等都在rtsa SCHEMA下)
|
||||||
CREATE SCHEMA rtss;
|
CREATE SCHEMA rtsa;
|
||||||
|
|
||||||
|
-- 创建mqtt客户端id序列
|
||||||
|
CREATE SEQUENCE rtsa.mqtt_client_id_seq;
|
||||||
|
|
||||||
-- 创建用户表
|
-- 创建用户表
|
||||||
CREATE TABLE
|
CREATE TABLE
|
||||||
rtss.user (
|
rtsa.user (
|
||||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||||
username VARCHAR(128) NOT NULL, -- 用户名
|
username VARCHAR(128) NOT NULL, -- 用户名
|
||||||
password 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
|
CREATE TABLE
|
||||||
rtss.draft_data (
|
rtsa.draft_data (
|
||||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||||
name VARCHAR(128) NOT NULL, -- 草稿名称
|
name VARCHAR(128) NOT NULL, -- 草稿名称
|
||||||
data_type INT NOT NULL, -- 数据类型
|
data_type INT NOT NULL, -- 数据类型
|
||||||
@ -59,44 +62,44 @@ CREATE TABLE
|
|||||||
is_shared BOOLEAN NOT NULL DEFAULT FALSE, -- 是否共享
|
is_shared BOOLEAN NOT NULL DEFAULT FALSE, -- 是否共享
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||||
updated_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) -- 一个用户的某个类型的草稿名称唯一
|
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
|
CREATE TABLE
|
||||||
rtss.release_data (
|
rtsa.release_data (
|
||||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||||
name VARCHAR(128) NOT NULL, -- 发布数据名称(数据唯一标识)
|
name VARCHAR(128) NOT NULL, -- 发布数据名称(数据唯一标识)
|
||||||
data_type INT NOT NULL, -- 数据类型
|
data_type INT NOT NULL, -- 数据类型
|
||||||
@ -106,47 +109,47 @@ CREATE TABLE
|
|||||||
is_published BOOLEAN NOT NULL DEFAULT TRUE, -- 是否上架
|
is_published BOOLEAN NOT NULL DEFAULT TRUE, -- 是否上架
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||||
updated_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) -- 数据类型和名称唯一
|
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
|
CREATE TABLE
|
||||||
rtss.release_data_version (
|
rtsa.release_data_version (
|
||||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||||
release_data_id INT NOT NULL, -- 发布数据id
|
release_data_id INT NOT NULL, -- 发布数据id
|
||||||
options JSONB NULL, -- 数据相关的参数项或配置项
|
options JSONB NULL, -- 数据相关的参数项或配置项
|
||||||
@ -154,46 +157,46 @@ CREATE TABLE
|
|||||||
description TEXT NOT NULL, -- 版本描述
|
description TEXT NOT NULL, -- 版本描述
|
||||||
user_id INT NOT NULL, -- 发布用户id
|
user_id INT NOT NULL, -- 发布用户id
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
created_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, -- 用户外键
|
||||||
FOREIGN KEY (release_data_id) REFERENCES rtss.release_data (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表
|
-- 创建feature表
|
||||||
CREATE TABLE
|
CREATE TABLE
|
||||||
rtss.feature (
|
rtsa.feature (
|
||||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||||
feature_type INT NOT NULL, -- feature类型
|
feature_type INT NOT NULL, -- feature类型
|
||||||
name VARCHAR(128) NOT NULL UNIQUE, -- feature名称
|
name VARCHAR(128) NOT NULL UNIQUE, -- feature名称
|
||||||
@ -204,68 +207,68 @@ CREATE TABLE
|
|||||||
updater_id INT NOT NULL, -- 更新用户id
|
updater_id INT NOT NULL, -- 更新用户id
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||||
updated_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 (creator_id) REFERENCES rtsa.user (id) ON DELETE CASCADE, -- 用户外键
|
||||||
FOREIGN KEY (updater_id) REFERENCES rtss.user (id) ON DELETE CASCADE -- 用户外键
|
FOREIGN KEY (updater_id) REFERENCES rtsa.user (id) ON DELETE CASCADE -- 用户外键
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 创建feature类型索引
|
-- 创建feature类型索引
|
||||||
CREATE INDEX ON rtss.feature (feature_type);
|
CREATE INDEX ON rtsa.feature (feature_type);
|
||||||
|
|
||||||
-- 创建feature名称索引
|
-- 创建feature名称索引
|
||||||
CREATE INDEX ON rtss.feature (name);
|
CREATE INDEX ON rtsa.feature (name);
|
||||||
|
|
||||||
-- 注释仿真feature表
|
-- 注释仿真feature表
|
||||||
COMMENT ON TABLE rtss.feature IS 'feature表';
|
COMMENT ON TABLE rtsa.feature IS 'feature表';
|
||||||
|
|
||||||
-- 注释仿真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
|
CREATE TABLE
|
||||||
rtss.user_config (
|
rtsa.user_config (
|
||||||
id SERIAL PRIMARY KEY, -- id 自增主键
|
id SERIAL PRIMARY KEY, -- id 自增主键
|
||||||
user_id INT NOT NULL, -- 用户id
|
user_id INT NOT NULL, -- 用户id
|
||||||
config_type INT NOT NULL, -- 配置类型
|
config_type INT NOT NULL, -- 配置类型
|
||||||
config BYTEA NOT NULL, -- 配置
|
config BYTEA NOT NULL, -- 配置
|
||||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
|
||||||
updated_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配置表
|
-- 注释用户feature配置表
|
||||||
COMMENT ON TABLE rtss.user_config IS '用户feature配置表';
|
COMMENT ON TABLE rtsa.user_config IS '用户feature配置表';
|
||||||
|
|
||||||
-- 注释用户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 '更新时间';
|
||||||
|
1
rtsa-proto-msg
Submodule
1
rtsa-proto-msg
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 1f53057b3f87790ef27c91399a5bb7e890f05549
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 02d9ad3b44876fc470e460bb6975c3d08f698b1b
|
|
24
simulation/Cargo.toml
Normal file
24
simulation/Cargo.toml
Normal file
@ -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" }
|
9
simulation/conf/default.toml
Normal file
9
simulation/conf/default.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[database]
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "debug"
|
||||||
|
|
||||||
|
[mqtt]
|
||||||
|
url = "http://localhost:8080"
|
||||||
|
username = "admin"
|
||||||
|
password = "admin"
|
7
simulation/conf/dev.toml
Normal file
7
simulation/conf/dev.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[database]
|
||||||
|
url = "postgresql://joylink:Joylink@0503@localhost:5432/joylink"
|
||||||
|
|
||||||
|
[mqtt]
|
||||||
|
url = "tcp://localhost:1883"
|
||||||
|
username = "rtsa"
|
||||||
|
password = "Joylink@0503"
|
10
simulation/conf/local_test.toml
Normal file
10
simulation/conf/local_test.toml
Normal file
@ -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"
|
87
simulation/src/app_config.rs
Normal file
87
simulation/src/app_config.rs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
48
simulation/src/commands/cmd.rs
Normal file
48
simulation/src/commands/cmd.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
29
simulation/src/commands/db.rs
Normal file
29
simulation/src/commands/db.rs
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
12
simulation/src/commands/mod.rs
Normal file
12
simulation/src/commands/mod.rs
Normal file
@ -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<()>;
|
||||||
|
}
|
93
simulation/src/components/mod.rs
Normal file
93
simulation/src/components/mod.rs
Normal file
@ -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));
|
||||||
|
}
|
||||||
|
}
|
9
simulation/src/error.rs
Normal file
9
simulation/src/error.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum SimulationError {
|
||||||
|
#[error("未知的仿真错误")]
|
||||||
|
Unknown,
|
||||||
|
#[error("创建错误: {0}")]
|
||||||
|
CreateError(String),
|
||||||
|
}
|
6
simulation/src/lib.rs
Normal file
6
simulation/src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pub mod app_config;
|
||||||
|
pub mod commands;
|
||||||
|
pub mod components;
|
||||||
|
pub mod error;
|
||||||
|
pub mod manage;
|
||||||
|
pub mod modules;
|
9
simulation/src/main.rs
Normal file
9
simulation/src/main.rs
Normal file
@ -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(())
|
||||||
|
}
|
18
simulation/src/manage/data_loading_plugin.rs
Normal file
18
simulation/src/manage/data_loading_plugin.rs
Normal file
@ -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);
|
||||||
|
}
|
5
simulation/src/manage/mod.rs
Normal file
5
simulation/src/manage/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod data_loading_plugin;
|
||||||
|
mod simulation;
|
||||||
|
mod simulation_control_plugin;
|
||||||
|
|
||||||
|
pub use simulation::{Simulation, SimulationOptions};
|
157
simulation/src/manage/simulation.rs
Normal file
157
simulation/src/manage/simulation.rs
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
simulation/src/manage/simulation_control_plugin.rs
Normal file
61
simulation/src/manage/simulation_control_plugin.rs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
simulation/src/modules/ci/mod.rs
Normal file
3
simulation/src/modules/ci/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod plugin;
|
||||||
|
|
||||||
|
pub use plugin::CiPlugin;
|
10
simulation/src/modules/ci/plugin.rs
Normal file
10
simulation/src/modules/ci/plugin.rs
Normal file
@ -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!()
|
||||||
|
}
|
||||||
|
}
|
2
simulation/src/modules/mod.rs
Normal file
2
simulation/src/modules/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod ci;
|
||||||
|
pub mod trackside;
|
@ -1,5 +1,6 @@
|
|||||||
use bevy_ecs::bundle::Bundle;
|
use bevy_ecs::bundle::Bundle;
|
||||||
use rtss_common::Uid;
|
|
||||||
|
use crate::components::Uid;
|
||||||
|
|
||||||
use super::{PsdState, TurnoutState, TwoNormalPositionsTransform};
|
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
Block a user