道岔仿真逻辑完善

多线程多app实验
This commit is contained in:
soul-walker 2024-08-15 00:07:21 +08:00
parent 3487b8eb1c
commit 072e6ba262
7 changed files with 110 additions and 35 deletions

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"cSpell.words": [
"rtss",
"timestep"
]
}

View File

@ -10,12 +10,12 @@ impl Default for Uid {
}
// 两常态位置转换组件,用于像道岔位置,屏蔽门位置等
#[derive(Component, Debug, Clone, PartialEq, Eq, Default)]
#[derive(Component, Debug, Clone, PartialEq, Default)]
pub struct TwoNormalPositionsConversion {
// 当前实际位置,百分比值,0-100
pub position: i32,
// 当前转换速度
pub velocity: i32,
pub velocity: f32,
}
// 道岔设备状态组件

View File

@ -1,3 +1,15 @@
use std::thread::{self, JoinHandle};
fn main() {
rtss_simulation::Simulation::new().app.run();
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);
}
for handler in list {
handler.join().unwrap();
}
}

View File

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::{collections::HashMap, ops::Deref};
use bevy_ecs::{entity::Entity, system::Resource};
@ -7,3 +7,37 @@ use bevy_ecs::{entity::Entity, system::Resource};
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,
}
impl Default for SimulationConfig {
fn default() -> Self {
SimulationConfig {
simulation_speed: 1.0,
turnout_transform_time: 3500,
}
}
}

View File

@ -3,25 +3,20 @@ use std::time::Duration;
use crate::{
control_simulation, handle_turnout_control, loading, simulation_status_control,
turnout_state_update, two_normal_position_transform, EquipmentUidEntityMapping,
SimulationControlEvent,
SimulationConfig, SimulationControlEvent, SimulationId,
};
use bevy_app::{prelude::*, ScheduleRunnerPlugin};
use bevy_ecs::schedule::IntoSystemConfigs;
use bevy_log::LogPlugin;
use bevy_log::{debug, LogPlugin};
use bevy_time::{prelude::*, TimePlugin};
pub struct Simulation {
id: String,
pub app: App,
}
impl Default for Simulation {
fn default() -> Self {
Self::new()
}
}
impl Simulation {
pub fn new() -> Self {
pub fn new(id: String) -> Self {
let mut app = App::new();
app.add_plugins(ScheduleRunnerPlugin::run_loop(Duration::from_millis(1000)))
.add_plugins(LogPlugin {
@ -31,7 +26,9 @@ impl Simulation {
.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)
@ -41,6 +38,19 @@ impl Simulation {
)
.observe(handle_turnout_control)
.observe(simulation_status_control);
Simulation { app }
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);
}
}

View File

@ -21,18 +21,16 @@ pub fn control_simulation(
// debug!("Uid: {:?}, Entity: {:?}", uid, entity);
commands.trigger_targets(TurnoutControlEvent::FC, *entity);
});
if time.elapsed_seconds() > 13.0 {
if time.elapsed_seconds() > 7.0 {
debug!("send exit event");
commands.trigger(SimulationControlEvent::Exit);
}
// if time.elapsed_seconds() > 10.0 {
// commands.trigger(SimulationControlEvent::Pause);
// }
}
pub fn simulation_status_control(
trigger: Trigger<SimulationControlEvent>,
mut time: ResMut<Time<Virtual>>,
sid: Res<crate::SimulationId>,
mut exit: EventWriter<AppExit>,
) {
match trigger.event() {
@ -45,7 +43,7 @@ pub fn simulation_status_control(
time.unpause();
}
SimulationControlEvent::Exit => {
debug!("Exiting simulation");
debug!("Exiting simulation, id={:?}", *sid);
exit.send(AppExit::Success);
}
}

View File

@ -1,33 +1,41 @@
use bevy_ecs::{
entity::Entity,
observer::Trigger,
system::{Query, ResMut},
system::{Query, Res, ResMut},
};
use bevy_log::debug;
use bevy_time::{Fixed, Time};
use crate::{events::TurnoutControlEvent, TurnoutState, TwoNormalPositionsConversion, Uid};
use crate::{
events::TurnoutControlEvent, SimulationConfig, TurnoutState, TwoNormalPositionsConversion, Uid,
};
// 道岔控制事件处理系统
pub fn handle_turnout_control(
trigger: Trigger<TurnoutControlEvent>,
time: ResMut<Time<Fixed>>,
config: Res<SimulationConfig>,
mut query: Query<(&Uid, &mut TurnoutState, &mut TwoNormalPositionsConversion)>,
) {
let (uid, mut state, mut conversion) = query
.get_mut(trigger.entity())
.expect("通过entity获取道岔异常");
let v = calculate_avg_velocity(
TWO_NORMAL_POSITION_MAX as f32,
config.turnout_transform_time as f32,
time.timestep().as_millis() as f32,
);
match trigger.event() {
TurnoutControlEvent::DC => {
state.dc = true;
state.fc = false;
conversion.velocity = -3000 / time.timestep().as_millis() as i32;
conversion.velocity = -v;
debug!("道岔定操处理:{:?}", uid);
}
TurnoutControlEvent::FC => {
state.dc = false;
state.fc = true;
conversion.velocity = 3000 / time.timestep().as_millis() as i32;
conversion.velocity = v;
debug!("道岔反操处理uid={:?}, conversion={:?}", uid, conversion);
}
}
@ -42,19 +50,26 @@ pub fn turnout_state_update(
"更新道岔状态Uid={:?}, State={:?}, Conversion={:?}",
uid, state, conversion
);
if conversion.velocity != 0 {
state.db = false;
state.fb = false;
} else if conversion.position == 0 {
if conversion.position == 0 {
state.db = true;
state.fb = false;
} else if conversion.position == 100 {
state.db = false;
state.fb = true;
} else {
state.db = false;
state.fb = false;
}
}
}
// 计算平均速度
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)>,
@ -64,16 +79,16 @@ pub fn two_normal_position_transform(
"Entity: {:?}, Uid: {:?}, Conversion: {:?}",
entity, uid, conversion
);
if conversion.velocity == 0 {
if conversion.velocity == 0f32 {
continue;
}
let p = conversion.position + conversion.velocity;
if p > 100 {
conversion.position = 100;
conversion.velocity = 0;
} else if p < 0 {
conversion.position = 0;
conversion.velocity = 0;
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;
}