diff --git a/.vscode/settings.json b/.vscode/settings.json index a6253a9..eefcdbe 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,9 @@ { "cSpell.words": [ + "hashbrown", + "Hasher", "rtss", - "timestep" + "timestep", + "trackside" ] } diff --git a/Cargo.lock b/Cargo.lock index 058a337..5d189ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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]] diff --git a/Cargo.toml b/Cargo.toml index 5d1b9d8..d5247cf 100644 --- a/Cargo.toml +++ b/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" } diff --git a/README.md b/README.md index fb5467b..d699a7b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # 项目描述 轨道交通信号系统仿真模块 +## 三种架构(先按第一种实现) + +1. 服务端服务进程运行多个仿真,客户端处理显示(和之前的方式一致) +2. 服务端通过网络启动多个运行单个仿真的进程(可以容器启动),客户端还是只处理显示 +3. 仿真逻辑通过WASM的形式最终打包到前端,客户端就既有逻辑又有显示(减少仿真状态的网络传输开销),后端只负责路由操作(应该和一般游戏架构类似,服务端核心在同步的处理) + ## 环境设置说明 ### 安装 Rust diff --git a/crates/rtss_api/Cargo.toml b/crates/rtss_api/Cargo.toml new file mode 100644 index 0000000..2d237e0 --- /dev/null +++ b/crates/rtss_api/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rtss_api" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/crates/rtss_api/src/lib.rs b/crates/rtss_api/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/crates/rtss_api/src/lib.rs @@ -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); + } +} diff --git a/crates/rtss_ci/Cargo.toml b/crates/rtss_ci/Cargo.toml new file mode 100644 index 0000000..48a78bd --- /dev/null +++ b/crates/rtss_ci/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rtss_ci" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/crates/rtss_ci/src/lib.rs b/crates/rtss_ci/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/crates/rtss_ci/src/lib.rs @@ -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); + } +} diff --git a/crates/rtss_core/Cargo.toml b/crates/rtss_core/Cargo.toml new file mode 100644 index 0000000..4c2fed7 --- /dev/null +++ b/crates/rtss_core/Cargo.toml @@ -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" } diff --git a/crates/rtss_core/src/config_plugins.rs b/crates/rtss_core/src/config_plugins.rs new file mode 100644 index 0000000..25cbe1c --- /dev/null +++ b/crates/rtss_core/src/config_plugins.rs @@ -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) { + for plugin in plugins { + match plugin { + AvailablePlugins::TrackSideEquipmentPlugin => { + app.add_plugins(TrackSideEquipmentPlugin); + } + } + } +} diff --git a/crates/rtss_core/src/lib.rs b/crates/rtss_core/src/lib.rs new file mode 100644 index 0000000..71fe151 --- /dev/null +++ b/crates/rtss_core/src/lib.rs @@ -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); + } +} diff --git a/crates/rtss_core/src/simulation.rs b/crates/rtss_core/src/simulation.rs new file mode 100644 index 0000000..ca921be --- /dev/null +++ b/crates/rtss_core/src/simulation.rs @@ -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>>, +} + +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, + rx: &mut mpsc::Receiver>, +) -> Result, 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`](bevy_time::fixed::Fixed) + pub(crate) loop_duration: Duration, + /// 仿真所需插件 + pub(crate) plugins: Vec, +} + +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) -> 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::::from_max_delta( + builder.loop_duration.mul_f32(2f32), + )) + .insert_resource(Time::::from_duration(builder.loop_duration)) + .insert_resource(SimulationId::new(builder.id)) + .insert_resource(SimulationStatus::default()) + .add_event::() + .observe(simulation_status_control); + // 添加仿真所需插件 + add_needed_plugins(&mut app, builder.plugins); + Simulation { app } + } +} + +pub fn simulation_status_control( + trigger: Trigger, + mut time: ResMut>, + sid: Res, + mut exit: EventWriter, +) { + 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); + } +} diff --git a/crates/rtss_iscs/Cargo.toml b/crates/rtss_iscs/Cargo.toml new file mode 100644 index 0000000..a99cf7b --- /dev/null +++ b/crates/rtss_iscs/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rtss_iscs" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/crates/rtss_iscs/src/lib.rs b/crates/rtss_iscs/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/crates/rtss_iscs/src/lib.rs @@ -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); + } +} diff --git a/crates/rtss_log/Cargo.toml b/crates/rtss_log/Cargo.toml new file mode 100644 index 0000000..801dce0 --- /dev/null +++ b/crates/rtss_log/Cargo.toml @@ -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" diff --git a/crates/rtss_log/src/lib.rs b/crates/rtss_log/src/lib.rs new file mode 100644 index 0000000..ecdb4a1 --- /dev/null +++ b/crates/rtss_log/src/lib.rs @@ -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::()) + .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::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"); + } +} diff --git a/crates/rtss_trackside/Cargo.toml b/crates/rtss_trackside/Cargo.toml new file mode 100644 index 0000000..da09a99 --- /dev/null +++ b/crates/rtss_trackside/Cargo.toml @@ -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" } diff --git a/src/components/equipment.rs b/crates/rtss_trackside/src/components/equipment.rs similarity index 100% rename from src/components/equipment.rs rename to crates/rtss_trackside/src/components/equipment.rs diff --git a/src/components/mod.rs b/crates/rtss_trackside/src/components/mod.rs similarity index 100% rename from src/components/mod.rs rename to crates/rtss_trackside/src/components/mod.rs diff --git a/src/events/equipment.rs b/crates/rtss_trackside/src/events/equipment.rs similarity index 100% rename from src/events/equipment.rs rename to crates/rtss_trackside/src/events/equipment.rs diff --git a/crates/rtss_trackside/src/events/mod.rs b/crates/rtss_trackside/src/events/mod.rs new file mode 100644 index 0000000..3d75eaa --- /dev/null +++ b/crates/rtss_trackside/src/events/mod.rs @@ -0,0 +1,2 @@ +mod equipment; +pub use equipment::*; diff --git a/src/lib.rs b/crates/rtss_trackside/src/lib.rs similarity index 78% rename from src/lib.rs rename to crates/rtss_trackside/src/lib.rs index a33a27d..28529c4 100644 --- a/src/lib.rs +++ b/crates/rtss_trackside/src/lib.rs @@ -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::*; diff --git a/crates/rtss_trackside/src/plugin.rs b/crates/rtss_trackside/src/plugin.rs new file mode 100644 index 0000000..047053a --- /dev/null +++ b/crates/rtss_trackside/src/plugin.rs @@ -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::() + .add_systems(Startup, loading) + .add_systems( + FixedUpdate, + (two_normal_position_transform, turnout_state_update).chain(), + ) + .observe(handle_turnout_control); + } +} diff --git a/src/resources/mod.rs b/crates/rtss_trackside/src/resources/mod.rs similarity index 56% rename from src/resources/mod.rs rename to crates/rtss_trackside/src/resources/mod.rs index a207dcc..f3c76bd 100644 --- a/src/resources/mod.rs +++ b/crates/rtss_trackside/src/resources/mod.rs @@ -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, } -#[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, } } diff --git a/crates/rtss_trackside/src/systems/common.rs b/crates/rtss_trackside/src/systems/common.rs new file mode 100644 index 0000000..d813c43 --- /dev/null +++ b/crates/rtss_trackside/src/systems/common.rs @@ -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; + } + } +} diff --git a/src/systems/loading.rs b/crates/rtss_trackside/src/systems/loading.rs similarity index 100% rename from src/systems/loading.rs rename to crates/rtss_trackside/src/systems/loading.rs diff --git a/src/systems/mod.rs b/crates/rtss_trackside/src/systems/mod.rs similarity index 66% rename from src/systems/mod.rs rename to crates/rtss_trackside/src/systems/mod.rs index 662e47a..25919fa 100644 --- a/src/systems/mod.rs +++ b/crates/rtss_trackside/src/systems/mod.rs @@ -1,6 +1,6 @@ -mod control; +mod common; mod loading; mod turnout; -pub use control::*; +pub use common::*; pub use loading::*; pub use turnout::*; diff --git a/src/systems/turnout.rs b/crates/rtss_trackside/src/systems/turnout.rs similarity index 66% rename from src/systems/turnout.rs rename to crates/rtss_trackside/src/systems/turnout.rs index a5c2738..f170ed9 100644 --- a/src/systems/turnout.rs +++ b/crates/rtss_trackside/src/systems/turnout.rs @@ -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; - } - } -} diff --git a/src/events/mod.rs b/src/events/mod.rs deleted file mode 100644 index 2ac4e22..0000000 --- a/src/events/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod equipment; -use bevy_ecs::event::Event; -pub use equipment::*; - -#[derive(Event, Debug)] -pub enum SimulationControlEvent { - Pause, - Unpause, - Exit, -} diff --git a/src/main.rs b/src/main.rs index 8b2457b..713aca4 100644 --- a/src/main.rs +++ b/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> = 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 = 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)); } diff --git a/src/simulation.rs b/src/simulation.rs deleted file mode 100644 index 962a033..0000000 --- a/src/simulation.rs +++ /dev/null @@ -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::::from_max_delta(Duration::from_secs(5))) - .insert_resource(Time::::from_duration(Duration::from_millis(1000))) - .insert_resource(SimulationId::new(id.clone())) - .insert_resource(EquipmentUidEntityMapping::default()) - .insert_resource(SimulationConfig::default()) - .add_event::() - .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); - } -} diff --git a/src/systems/control.rs b/src/systems/control.rs deleted file mode 100644 index b23100e..0000000 --- a/src/systems/control.rs +++ /dev/null @@ -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>, - mut commands: Commands, - res_uid_mapping: Res, -) { - 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, - mut time: ResMut>, - sid: Res, - mut exit: EventWriter, -) { - 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); - } - } -}