代码结构重构;
初步实现多仿真管理结构
This commit is contained in:
parent
072e6ba262
commit
e4db3c32df
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -1,6 +1,9 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"hashbrown",
|
||||
"Hasher",
|
||||
"rtss",
|
||||
"timestep"
|
||||
"timestep",
|
||||
"trackside"
|
||||
]
|
||||
}
|
||||
|
112
Cargo.lock
generated
112
Cargo.lock
generated
@ -30,12 +30,6 @@ version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937"
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.13.0"
|
||||
@ -135,21 +129,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_log"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52f2c0c374af59007396793a51f747f6b10d74ca4acfb080ce0ade267118827b"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"bevy_app",
|
||||
"bevy_ecs",
|
||||
"bevy_utils",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
"tracing-wasm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_macro_utils"
|
||||
version = "0.14.1"
|
||||
@ -295,6 +274,25 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
@ -307,6 +305,12 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
@ -510,6 +514,26 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.6"
|
||||
@ -555,14 +579,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "rtss_simulation"
|
||||
name = "rtss_api"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rtss_ci"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rtss_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_core",
|
||||
"bevy_ecs",
|
||||
"bevy_log",
|
||||
"bevy_time",
|
||||
"rayon",
|
||||
"rtss_log",
|
||||
"rtss_trackside",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_iscs"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rtss_log"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-wasm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_simulation"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rtss_core",
|
||||
"rtss_log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtss_trackside"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_core",
|
||||
"bevy_ecs",
|
||||
"bevy_time",
|
||||
"rtss_log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
11
Cargo.toml
11
Cargo.toml
@ -5,9 +5,16 @@ edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.dependencies]
|
||||
bevy_app = "0.14.1"
|
||||
bevy_core = "0.14.1"
|
||||
bevy_ecs = "0.14.1"
|
||||
bevy_log = "0.14.1"
|
||||
bevy_time = "0.14.1"
|
||||
rayon = "1.10.0"
|
||||
|
||||
[dependencies]
|
||||
rtss_core = { path = "crates/rtss_core" }
|
||||
rtss_log = { path = "crates/rtss_log" }
|
||||
|
@ -1,6 +1,12 @@
|
||||
# 项目描述
|
||||
轨道交通信号系统仿真模块
|
||||
|
||||
## 三种架构(先按第一种实现)
|
||||
|
||||
1. 服务端服务进程运行多个仿真,客户端处理显示(和之前的方式一致)
|
||||
2. 服务端通过网络启动多个运行单个仿真的进程(可以容器启动),客户端还是只处理显示
|
||||
3. 仿真逻辑通过WASM的形式最终打包到前端,客户端就既有逻辑又有显示(减少仿真状态的网络传输开销),后端只负责路由操作(应该和一般游戏架构类似,服务端核心在同步的处理)
|
||||
|
||||
## 环境设置说明
|
||||
|
||||
### 安装 Rust
|
||||
|
6
crates/rtss_api/Cargo.toml
Normal file
6
crates/rtss_api/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "rtss_api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
14
crates/rtss_api/src/lib.rs
Normal file
14
crates/rtss_api/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
}
|
6
crates/rtss_ci/Cargo.toml
Normal file
6
crates/rtss_ci/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "rtss_ci"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
14
crates/rtss_ci/src/lib.rs
Normal file
14
crates/rtss_ci/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
}
|
14
crates/rtss_core/Cargo.toml
Normal file
14
crates/rtss_core/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "rtss_core"
|
||||
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}
|
||||
|
||||
rtss_log = { path = "../rtss_log" }
|
||||
rtss_trackside = { path = "../rtss_trackside" }
|
17
crates/rtss_core/src/config_plugins.rs
Normal file
17
crates/rtss_core/src/config_plugins.rs
Normal file
@ -0,0 +1,17 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
crates/rtss_core/src/lib.rs
Normal file
19
crates/rtss_core/src/lib.rs
Normal file
@ -0,0 +1,19 @@
|
||||
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);
|
||||
}
|
||||
}
|
327
crates/rtss_core/src/simulation.rs
Normal file
327
crates/rtss_core/src/simulation.rs
Normal file
@ -0,0 +1,327 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::mpsc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use bevy_app::{prelude::*, PluginsState};
|
||||
use bevy_ecs::{
|
||||
event::{Event, EventWriter},
|
||||
observer::Trigger,
|
||||
system::{Res, ResMut, Resource},
|
||||
};
|
||||
use bevy_time::{prelude::*, TimePlugin};
|
||||
use rtss_log::tracing::{debug, error};
|
||||
|
||||
use crate::{add_needed_plugins, AvailablePlugins};
|
||||
|
||||
pub struct SimulationManager {
|
||||
txs: HashMap<String, mpsc::Sender<Box<SimulationHandle>>>,
|
||||
}
|
||||
|
||||
impl Default for SimulationManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl SimulationManager {
|
||||
fn new() -> Self {
|
||||
let tx = HashMap::new();
|
||||
SimulationManager { txs: tx }
|
||||
}
|
||||
|
||||
pub fn count(&self) -> usize {
|
||||
self.txs.len()
|
||||
}
|
||||
|
||||
pub fn start_simulation(&mut self, builder: SimulationBuilder) {
|
||||
let (tx, mut rx) = mpsc::channel();
|
||||
let id = builder.id.clone();
|
||||
|
||||
rayon::spawn(move || {
|
||||
let wait = Some(builder.loop_duration);
|
||||
let mut sim = crate::Simulation::new(builder);
|
||||
sim.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,
|
||||
}
|
||||
}
|
||||
});
|
||||
sim.run();
|
||||
});
|
||||
self.txs.insert(id, tx);
|
||||
}
|
||||
|
||||
pub fn exit_simulation(&mut self, id: String) {
|
||||
debug!("exit simulation, id={}", id);
|
||||
if let Some(tx) = self.txs.remove(&id) {
|
||||
if let Err(e) = tx.send(Box::new(|app: &mut App| {
|
||||
app.world_mut().trigger(SimulationControlEvent::Exit);
|
||||
})) {
|
||||
error!(
|
||||
"Failed to send exit event to simulation, id={}, error={:?}",
|
||||
id, e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pause_simulation(&mut self, id: String) {
|
||||
if let Some(tx) = self.txs.get(&id) {
|
||||
if let Err(e) = tx.send(Box::new(|app: &mut App| {
|
||||
app.world_mut().trigger(SimulationControlEvent::Pause);
|
||||
})) {
|
||||
error!(
|
||||
"Failed to send pause event to simulation, id={}, error={:?}",
|
||||
id, e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resume_simulation(&mut self, id: String) {
|
||||
if let Some(tx) = self.txs.get(&id) {
|
||||
if let Err(e) = tx.send(Box::new(|app: &mut App| {
|
||||
app.world_mut().trigger(SimulationControlEvent::Unpause);
|
||||
})) {
|
||||
error!(
|
||||
"Failed to send resume event to simulation, id={}, error={:?}",
|
||||
id, e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_simulation_speed(&mut self, id: String, speed: f32) {
|
||||
if let Some(tx) = self.txs.get(&id) {
|
||||
if let Err(e) = tx.send(Box::new(move |app: &mut App| {
|
||||
app.world_mut()
|
||||
.trigger(SimulationControlEvent::UpdateSpeed(speed));
|
||||
})) {
|
||||
error!(
|
||||
"Failed to send update speed event to simulation, id={}, error={:?}",
|
||||
id, e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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(20),
|
||||
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)]
|
||||
pub enum SimulationControlEvent {
|
||||
Pause,
|
||||
Unpause,
|
||||
UpdateSpeed(f32),
|
||||
Exit,
|
||||
}
|
||||
|
||||
/// 仿真运行模式
|
||||
pub enum SimulationRunMode {
|
||||
SingleSimulationMain,
|
||||
SingleSimulationThread,
|
||||
MultiSimulationThread,
|
||||
}
|
||||
|
||||
pub struct Simulation {
|
||||
app: App,
|
||||
}
|
||||
|
||||
impl Deref for Simulation {
|
||||
type Target = App;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.app
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Simulation {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.app
|
||||
}
|
||||
}
|
||||
|
||||
pub type SimulationHandle = dyn FnMut(&mut App) + Send;
|
||||
|
||||
impl Simulation {
|
||||
pub fn new(builder: SimulationBuilder) -> Self {
|
||||
let mut app = App::new();
|
||||
// 初始化仿真App
|
||||
app.add_plugins(TimePlugin)
|
||||
.insert_resource(Time::<Virtual>::from_max_delta(
|
||||
builder.loop_duration.mul_f32(2f32),
|
||||
))
|
||||
.insert_resource(Time::<Fixed>::from_duration(builder.loop_duration))
|
||||
.insert_resource(SimulationId::new(builder.id))
|
||||
.insert_resource(SimulationStatus::default())
|
||||
.add_event::<SimulationControlEvent>()
|
||||
.observe(simulation_status_control);
|
||||
// 添加仿真所需插件
|
||||
add_needed_plugins(&mut app, builder.plugins);
|
||||
Simulation { app }
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_simulation_manager() {
|
||||
let mut manager = SimulationManager::default();
|
||||
assert_eq!(manager.count(), 0);
|
||||
|
||||
manager.start_simulation(SimulationBuilder::default().id("0".to_string()));
|
||||
assert_eq!(manager.count(), 1);
|
||||
|
||||
manager.start_simulation(SimulationBuilder::default().id("1".to_string()));
|
||||
assert_eq!(manager.count(), 2);
|
||||
|
||||
manager.exit_simulation("0".to_string());
|
||||
assert_eq!(manager.count(), 1);
|
||||
|
||||
manager.exit_simulation("1".to_string());
|
||||
assert_eq!(manager.count(), 0);
|
||||
}
|
||||
}
|
6
crates/rtss_iscs/Cargo.toml
Normal file
6
crates/rtss_iscs/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "rtss_iscs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
14
crates/rtss_iscs/src/lib.rs
Normal file
14
crates/rtss_iscs/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
}
|
14
crates/rtss_log/Cargo.toml
Normal file
14
crates/rtss_log/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "rtss_log"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||
tracing-subscriber = { version = "0.3.1", features = [
|
||||
"registry",
|
||||
"env-filter",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
tracing-wasm = "0.2.1"
|
83
crates/rtss_log/src/lib.rs
Normal file
83
crates/rtss_log/src/lib.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use std::error::Error;
|
||||
|
||||
pub use tracing;
|
||||
use tracing::Level;
|
||||
pub use tracing_subscriber;
|
||||
|
||||
use tracing_subscriber::{
|
||||
filter::{FromEnvError, ParseError},
|
||||
layer::SubscriberExt,
|
||||
registry::Registry,
|
||||
EnvFilter,
|
||||
};
|
||||
|
||||
/// 日志配置
|
||||
pub struct Logging {
|
||||
pub filter: String,
|
||||
pub level: Level,
|
||||
}
|
||||
|
||||
impl Default for Logging {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
filter: "wgpu=error,naga=warn".to_string(),
|
||||
level: Level::INFO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Logging {
|
||||
pub fn init(&self) {
|
||||
let finished_subscriber;
|
||||
let subscriber = Registry::default();
|
||||
let default_filter = { format!("{},{}", self.level, self.filter) };
|
||||
let filter_layer = EnvFilter::try_from_default_env()
|
||||
.or_else(|from_env_error| {
|
||||
_ = from_env_error
|
||||
.source()
|
||||
.and_then(|source| source.downcast_ref::<ParseError>())
|
||||
.map(|parse_err| {
|
||||
// we cannot use the `error!` macro here because the logger is not ready yet.
|
||||
eprintln!("LogPlugin failed to parse filter from env: {}", parse_err);
|
||||
});
|
||||
|
||||
Ok::<EnvFilter, FromEnvError>(EnvFilter::builder().parse_lossy(&default_filter))
|
||||
})
|
||||
.unwrap();
|
||||
let subscriber = subscriber.with(filter_layer);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let fmt_layer = tracing_subscriber::fmt::Layer::default().with_writer(std::io::stderr);
|
||||
finished_subscriber = subscriber.with(fmt_layer);
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
subscriber = subscriber.with(tracing_wasm::WASMLayer::new(
|
||||
tracing_wasm::WASMLayerConfig::default(),
|
||||
));
|
||||
}
|
||||
tracing::subscriber::set_global_default(finished_subscriber)
|
||||
.expect("setting default subscriber failed");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let logging = Logging {
|
||||
level: Level::WARN,
|
||||
..Default::default()
|
||||
};
|
||||
logging.init();
|
||||
trace!("test trace");
|
||||
debug!("test debug");
|
||||
info!("test info");
|
||||
warn!("test warn");
|
||||
error!("test error");
|
||||
}
|
||||
}
|
11
crates/rtss_trackside/Cargo.toml
Normal file
11
crates/rtss_trackside/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[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" }
|
2
crates/rtss_trackside/src/events/mod.rs
Normal file
2
crates/rtss_trackside/src/events/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod equipment;
|
||||
pub use equipment::*;
|
@ -1,10 +1,10 @@
|
||||
mod components;
|
||||
mod events;
|
||||
mod plugin;
|
||||
mod resources;
|
||||
mod simulation;
|
||||
mod systems;
|
||||
pub use components::*;
|
||||
pub use events::*;
|
||||
pub use plugin::*;
|
||||
pub use resources::*;
|
||||
pub use simulation::*;
|
||||
pub use systems::*;
|
24
crates/rtss_trackside/src/plugin.rs
Normal file
24
crates/rtss_trackside/src/plugin.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use bevy_app::{FixedUpdate, Plugin, Startup};
|
||||
use bevy_ecs::schedule::IntoSystemConfigs;
|
||||
|
||||
use crate::{
|
||||
handle_turnout_control, loading, turnout_state_update, two_normal_position_transform,
|
||||
EquipmentUidEntityMapping, SimulationConfig, TurnoutControlEvent,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TrackSideEquipmentPlugin;
|
||||
|
||||
impl Plugin for TrackSideEquipmentPlugin {
|
||||
fn build(&self, app: &mut bevy_app::App) {
|
||||
app.insert_resource(EquipmentUidEntityMapping::default())
|
||||
.insert_resource(SimulationConfig::default())
|
||||
.add_event::<TurnoutControlEvent>()
|
||||
.add_systems(Startup, loading)
|
||||
.add_systems(
|
||||
FixedUpdate,
|
||||
(two_normal_position_transform, turnout_state_update).chain(),
|
||||
)
|
||||
.observe(handle_turnout_control);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, ops::Deref};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bevy_ecs::{entity::Entity, system::Resource};
|
||||
|
||||
@ -8,27 +8,8 @@ pub struct EquipmentUidEntityMapping {
|
||||
pub turnout_mapping: HashMap<String, Entity>,
|
||||
}
|
||||
|
||||
#[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 SimulationConfig {
|
||||
// 仿真倍速
|
||||
pub simulation_speed: f32,
|
||||
// 道岔转换总时间,单位ms
|
||||
pub turnout_transform_time: i32,
|
||||
}
|
||||
@ -36,7 +17,6 @@ pub struct SimulationConfig {
|
||||
impl Default for SimulationConfig {
|
||||
fn default() -> Self {
|
||||
SimulationConfig {
|
||||
simulation_speed: 1.0,
|
||||
turnout_transform_time: 3500,
|
||||
}
|
||||
}
|
31
crates/rtss_trackside/src/systems/common.rs
Normal file
31
crates/rtss_trackside/src/systems/common.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use bevy_ecs::{entity::Entity, system::Query};
|
||||
use rtss_log::tracing::debug;
|
||||
|
||||
use crate::{TwoNormalPositionsConversion, Uid};
|
||||
|
||||
pub const TWO_NORMAL_POSITION_MIN: i32 = 0;
|
||||
pub const TWO_NORMAL_POSITION_MAX: i32 = 100;
|
||||
// 两常态位置转换系统
|
||||
pub fn two_normal_position_transform(
|
||||
mut query: Query<(Entity, &Uid, &mut TwoNormalPositionsConversion)>,
|
||||
) {
|
||||
for (entity, uid, mut conversion) in &mut query {
|
||||
debug!(
|
||||
"Entity: {:?}, Uid: {:?}, Conversion: {:?}",
|
||||
entity, uid, conversion
|
||||
);
|
||||
if conversion.velocity == 0f32 {
|
||||
continue;
|
||||
}
|
||||
let p = conversion.position + conversion.velocity as i32;
|
||||
if p > TWO_NORMAL_POSITION_MAX {
|
||||
conversion.position = TWO_NORMAL_POSITION_MAX;
|
||||
conversion.velocity = TWO_NORMAL_POSITION_MIN as f32;
|
||||
} else if p < TWO_NORMAL_POSITION_MIN {
|
||||
conversion.position = TWO_NORMAL_POSITION_MIN;
|
||||
conversion.velocity = 0 as f32;
|
||||
} else {
|
||||
conversion.position = p;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
mod control;
|
||||
mod common;
|
||||
mod loading;
|
||||
mod turnout;
|
||||
pub use control::*;
|
||||
pub use common::*;
|
||||
pub use loading::*;
|
||||
pub use turnout::*;
|
@ -1,10 +1,9 @@
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
observer::Trigger,
|
||||
system::{Query, Res, ResMut},
|
||||
};
|
||||
use bevy_log::debug;
|
||||
use bevy_time::{Fixed, Time};
|
||||
use rtss_log::tracing::debug;
|
||||
|
||||
use crate::{
|
||||
events::TurnoutControlEvent, SimulationConfig, TurnoutState, TwoNormalPositionsConversion, Uid,
|
||||
@ -21,7 +20,7 @@ pub fn handle_turnout_control(
|
||||
.get_mut(trigger.entity())
|
||||
.expect("通过entity获取道岔异常");
|
||||
let v = calculate_avg_velocity(
|
||||
TWO_NORMAL_POSITION_MAX as f32,
|
||||
crate::TWO_NORMAL_POSITION_MAX as f32,
|
||||
config.turnout_transform_time as f32,
|
||||
time.timestep().as_millis() as f32,
|
||||
);
|
||||
@ -67,30 +66,3 @@ pub fn turnout_state_update(
|
||||
fn calculate_avg_velocity(total_distance: f32, total_time: f32, time_step: f32) -> f32 {
|
||||
total_distance / (total_time / time_step)
|
||||
}
|
||||
|
||||
const TWO_NORMAL_POSITION_MIN: i32 = 0;
|
||||
const TWO_NORMAL_POSITION_MAX: i32 = 100;
|
||||
// 两常态位置转换系统
|
||||
pub fn two_normal_position_transform(
|
||||
mut query: Query<(Entity, &Uid, &mut TwoNormalPositionsConversion)>,
|
||||
) {
|
||||
for (entity, uid, mut conversion) in &mut query {
|
||||
debug!(
|
||||
"Entity: {:?}, Uid: {:?}, Conversion: {:?}",
|
||||
entity, uid, conversion
|
||||
);
|
||||
if conversion.velocity == 0f32 {
|
||||
continue;
|
||||
}
|
||||
let p = conversion.position + conversion.velocity as i32;
|
||||
if p > TWO_NORMAL_POSITION_MAX {
|
||||
conversion.position = TWO_NORMAL_POSITION_MAX;
|
||||
conversion.velocity = TWO_NORMAL_POSITION_MIN as f32;
|
||||
} else if p < TWO_NORMAL_POSITION_MIN {
|
||||
conversion.position = TWO_NORMAL_POSITION_MIN;
|
||||
conversion.velocity = 0 as f32;
|
||||
} else {
|
||||
conversion.position = p;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
mod equipment;
|
||||
use bevy_ecs::event::Event;
|
||||
pub use equipment::*;
|
||||
|
||||
#[derive(Event, Debug)]
|
||||
pub enum SimulationControlEvent {
|
||||
Pause,
|
||||
Unpause,
|
||||
Exit,
|
||||
}
|
46
src/main.rs
46
src/main.rs
@ -1,15 +1,41 @@
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::{
|
||||
thread::{self},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use rtss_core::SimulationBuilder;
|
||||
|
||||
fn main() {
|
||||
let mut list: Vec<JoinHandle<()>> = Vec::new();
|
||||
for i in 0..100 {
|
||||
let thread_handler = thread::spawn(move || {
|
||||
let mut sim = rtss_simulation::Simulation::new(i.to_string());
|
||||
sim.run();
|
||||
});
|
||||
list.push(thread_handler);
|
||||
rtss_log::Logging {
|
||||
level: rtss_log::tracing::Level::DEBUG,
|
||||
..Default::default()
|
||||
}
|
||||
for handler in list {
|
||||
handler.join().unwrap();
|
||||
.init();
|
||||
let mut manager = rtss_core::SimulationManager::default();
|
||||
// manager.start_simulation(SimulationBuilder::default().id("0".to_string()));
|
||||
// thread::sleep(Duration::from_secs(3));
|
||||
// manager.exit_simulation("0".to_string());
|
||||
// let mut list: Vec<Simulation> = Vec::new();
|
||||
let max = 1;
|
||||
for i in 0..max {
|
||||
manager.start_simulation(
|
||||
SimulationBuilder::default()
|
||||
.id(i.to_string())
|
||||
.loop_duration(Duration::from_millis(500))
|
||||
.plugins(vec![rtss_core::AvailablePlugins::TrackSideEquipmentPlugin]),
|
||||
);
|
||||
}
|
||||
thread::sleep(Duration::from_secs(3));
|
||||
for i in 0..max {
|
||||
manager.update_simulation_speed(i.to_string(), 2f32);
|
||||
}
|
||||
// thread::sleep(Duration::from_secs(3));
|
||||
// for i in 0..max {
|
||||
// manager.resume_simulation(i.to_string());
|
||||
// }
|
||||
thread::sleep(Duration::from_secs(3));
|
||||
for i in 0..max {
|
||||
manager.exit_simulation(i.to_string());
|
||||
}
|
||||
thread::sleep(Duration::from_secs(3));
|
||||
}
|
||||
|
@ -1,56 +0,0 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{
|
||||
control_simulation, handle_turnout_control, loading, simulation_status_control,
|
||||
turnout_state_update, two_normal_position_transform, EquipmentUidEntityMapping,
|
||||
SimulationConfig, SimulationControlEvent, SimulationId,
|
||||
};
|
||||
use bevy_app::{prelude::*, ScheduleRunnerPlugin};
|
||||
use bevy_ecs::schedule::IntoSystemConfigs;
|
||||
use bevy_log::{debug, LogPlugin};
|
||||
use bevy_time::{prelude::*, TimePlugin};
|
||||
|
||||
pub struct Simulation {
|
||||
id: String,
|
||||
pub app: App,
|
||||
}
|
||||
|
||||
impl Simulation {
|
||||
pub fn new(id: String) -> Self {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(ScheduleRunnerPlugin::run_loop(Duration::from_millis(1000)))
|
||||
.add_plugins(LogPlugin {
|
||||
level: bevy_log::Level::DEBUG,
|
||||
..Default::default()
|
||||
})
|
||||
.add_plugins(TimePlugin)
|
||||
.insert_resource(Time::<Virtual>::from_max_delta(Duration::from_secs(5)))
|
||||
.insert_resource(Time::<Fixed>::from_duration(Duration::from_millis(1000)))
|
||||
.insert_resource(SimulationId::new(id.clone()))
|
||||
.insert_resource(EquipmentUidEntityMapping::default())
|
||||
.insert_resource(SimulationConfig::default())
|
||||
.add_event::<SimulationControlEvent>()
|
||||
.add_systems(Startup, loading)
|
||||
.add_systems(Update, control_simulation)
|
||||
.add_systems(
|
||||
FixedUpdate,
|
||||
(two_normal_position_transform, turnout_state_update).chain(),
|
||||
)
|
||||
.observe(handle_turnout_control)
|
||||
.observe(simulation_status_control);
|
||||
Simulation { id, app }
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
self.app.run();
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
debug!("close simulation id={}", self.id);
|
||||
self.app.world_mut().trigger(SimulationControlEvent::Exit);
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
use bevy_app::AppExit;
|
||||
use bevy_ecs::{
|
||||
event::EventWriter,
|
||||
observer::Trigger,
|
||||
system::{Commands, Res, ResMut},
|
||||
};
|
||||
use bevy_log::debug;
|
||||
use bevy_time::{Time, Virtual};
|
||||
|
||||
use crate::{EquipmentUidEntityMapping, SimulationControlEvent, TurnoutControlEvent};
|
||||
|
||||
pub fn control_simulation(
|
||||
time: ResMut<Time<Virtual>>,
|
||||
mut commands: Commands,
|
||||
res_uid_mapping: Res<EquipmentUidEntityMapping>,
|
||||
) {
|
||||
res_uid_mapping
|
||||
.turnout_mapping
|
||||
.iter()
|
||||
.for_each(|(_uid, entity)| {
|
||||
// debug!("Uid: {:?}, Entity: {:?}", uid, entity);
|
||||
commands.trigger_targets(TurnoutControlEvent::FC, *entity);
|
||||
});
|
||||
if time.elapsed_seconds() > 7.0 {
|
||||
debug!("send exit event");
|
||||
commands.trigger(SimulationControlEvent::Exit);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulation_status_control(
|
||||
trigger: Trigger<SimulationControlEvent>,
|
||||
mut time: ResMut<Time<Virtual>>,
|
||||
sid: Res<crate::SimulationId>,
|
||||
mut exit: EventWriter<AppExit>,
|
||||
) {
|
||||
match trigger.event() {
|
||||
SimulationControlEvent::Pause => {
|
||||
debug!("Pausing simulation");
|
||||
time.pause();
|
||||
}
|
||||
SimulationControlEvent::Unpause => {
|
||||
debug!("Unpausing simulation");
|
||||
time.unpause();
|
||||
}
|
||||
SimulationControlEvent::Exit => {
|
||||
debug!("Exiting simulation, id={:?}", *sid);
|
||||
exit.send(AppExit::Success);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user