mirror of
https://github.com/seemueller-io/yachtpit.git
synced 2025-09-08 22:46:45 +00:00
Modularize (#1)
* configure workspaces * Modularize domain logic by creating a new `models` crate. * Moved `LoadingPlugin` and `MenuPlugin` from `core` to a new `ui` module. Updated imports accordingly. * add theme for instruments * trunk serve works, remove audio and textures * remove loading indicator and assets * rename models to systems * seperate systems and components from models * Refactor instrument cluster to leverage reusable composition utilities. --------- Co-authored-by: geoffsee <>
This commit is contained in:
19
crates/systems/Cargo.toml
Normal file
19
crates/systems/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "systems"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
bevy = { workspace = true, features = [
|
||||
"bevy_asset",
|
||||
"bevy_color",
|
||||
"bevy_core_pipeline",
|
||||
"bevy_render",
|
||||
"bevy_sprite",
|
||||
"bevy_text",
|
||||
"bevy_ui",
|
||||
"bevy_window",
|
||||
] }
|
||||
rand = { version = "0.8.3" }
|
||||
components = { path = "../components" }
|
15
crates/systems/src/lib.rs
Normal file
15
crates/systems/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
pub mod player;
|
||||
pub mod systems;
|
||||
pub mod yacht_systems;
|
||||
|
||||
// Re-export components from the components crate
|
||||
pub use components::{
|
||||
setup_instrument_cluster, update_instrument_displays, update_yacht_data, YachtData,
|
||||
SpeedGauge, DepthGauge, CompassGauge, EngineStatus, NavigationDisplay,
|
||||
InstrumentCluster, GpsIndicator, RadarIndicator, AisIndicator, SystemDisplay
|
||||
};
|
||||
|
||||
pub use player::{get_yacht_systems, setup_instrument_cluster_system, PlayerPlugin};
|
||||
pub use yacht_systems::{create_yacht_systems, AisSystem, GpsSystem, RadarSystem, SystemInteraction, SystemStatus, YachtSystem};
|
27
crates/systems/src/player.rs
Normal file
27
crates/systems/src/player.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use bevy::prelude::*;
|
||||
use components::{setup_instrument_cluster, YachtData, update_yacht_data, update_instrument_displays};
|
||||
use super::yacht_systems::{create_yacht_systems, YachtSystem};
|
||||
|
||||
pub struct PlayerPlugin;
|
||||
|
||||
/// This plugin handles the futuristic yacht instrument cluster
|
||||
/// The main app should handle state management and system registration
|
||||
impl Plugin for PlayerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<YachtData>()
|
||||
.add_systems(
|
||||
Update,
|
||||
(update_yacht_data, update_instrument_displays)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup function for instrument cluster - to be called by the main app
|
||||
pub fn setup_instrument_cluster_system() -> impl Fn(Commands) {
|
||||
setup_instrument_cluster
|
||||
}
|
||||
|
||||
/// Initialize yacht systems - returns the systems for registration
|
||||
pub fn get_yacht_systems() -> Vec<Box<dyn YachtSystem>> {
|
||||
create_yacht_systems()
|
||||
}
|
149
crates/systems/src/systems.rs
Normal file
149
crates/systems/src/systems.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
use bevy::prelude::*;
|
||||
use components::{YachtData, GpsIndicator, RadarIndicator, AisIndicator, SystemDisplay};
|
||||
|
||||
/// Resource to track which system is currently selected
|
||||
#[derive(Resource, Default)]
|
||||
pub struct SelectedSystem {
|
||||
pub current: Option<SystemType>,
|
||||
}
|
||||
|
||||
/// Types of navigation systems available
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum SystemType {
|
||||
Gps,
|
||||
Radar,
|
||||
Ais,
|
||||
}
|
||||
|
||||
/// Handles user interactions with system indicator buttons
|
||||
pub fn handle_system_interactions(
|
||||
mut selected_system: ResMut<SelectedSystem>,
|
||||
mut interaction_query: Query<
|
||||
(&Interaction, &mut BackgroundColor, Option<&GpsIndicator>, Option<&RadarIndicator>, Option<&AisIndicator>),
|
||||
(Changed<Interaction>, With<Button>),
|
||||
>,
|
||||
) {
|
||||
for (interaction, mut background_color, gps, radar, ais) in &mut interaction_query {
|
||||
match *interaction {
|
||||
Interaction::Pressed => {
|
||||
if gps.is_some() {
|
||||
selected_system.current = Some(SystemType::Gps);
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
||||
} else if radar.is_some() {
|
||||
selected_system.current = Some(SystemType::Radar);
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
||||
} else if ais.is_some() {
|
||||
selected_system.current = Some(SystemType::Ais);
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
||||
}
|
||||
}
|
||||
Interaction::Hovered => {
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.15, 0.15, 0.2));
|
||||
}
|
||||
Interaction::None => {
|
||||
*background_color = BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the system display area with detailed information about the selected system
|
||||
pub fn update_system_display(
|
||||
selected_system: Res<SelectedSystem>,
|
||||
mut display_query: Query<&mut Text, With<SystemDisplay>>,
|
||||
yacht_data: Res<YachtData>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
if let Ok(mut text) = display_query.single_mut() {
|
||||
match selected_system.current {
|
||||
Some(SystemType::Gps) => {
|
||||
text.0 = format!(
|
||||
"GPS NAVIGATION SYSTEM\n\n\
|
||||
Position: 43°38'19.5\"N 1°26'58.3\"W\n\
|
||||
Heading: {:.0}°\n\
|
||||
Speed: {:.1} knots\n\
|
||||
Course Over Ground: {:.0}°\n\
|
||||
Satellites: 12 connected\n\
|
||||
HDOP: 0.8 (Excellent)\n\
|
||||
\n\
|
||||
Next Waypoint: MONACO HARBOR\n\
|
||||
Distance: 127.3 NM\n\
|
||||
ETA: 10h 12m",
|
||||
yacht_data.heading,
|
||||
yacht_data.speed,
|
||||
yacht_data.heading + 5.0
|
||||
);
|
||||
}
|
||||
Some(SystemType::Radar) => {
|
||||
let sweep_angle = (time.elapsed_secs() * 60.0) % 360.0;
|
||||
text.0 = format!(
|
||||
"RADAR SYSTEM - 12 NM RANGE\n\n\
|
||||
Status: ACTIVE\n\
|
||||
Sweep: {:.0}°\n\
|
||||
Gain: AUTO\n\
|
||||
Sea Clutter: -15 dB\n\
|
||||
Rain Clutter: OFF\n\
|
||||
\n\
|
||||
CONTACTS DETECTED:\n\
|
||||
• Vessel 1: 2.3 NM @ 045° (15 kts)\n\
|
||||
• Vessel 2: 5.7 NM @ 180° (8 kts)\n\
|
||||
• Land Mass: 8.2 NM @ 270°\n\
|
||||
• Buoy: 1.1 NM @ 315°",
|
||||
sweep_angle
|
||||
);
|
||||
}
|
||||
Some(SystemType::Ais) => {
|
||||
text.0 = format!(
|
||||
"AIS - AUTOMATIC IDENTIFICATION SYSTEM\n\n\
|
||||
Status: RECEIVING\n\
|
||||
Own Ship MMSI: 123456789\n\
|
||||
\n\
|
||||
NEARBY VESSELS:\n\
|
||||
\n\
|
||||
🛥️ M/Y SERENITY\n\
|
||||
MMSI: 987654321\n\
|
||||
Distance: 2.1 NM @ 045°\n\
|
||||
Speed: 12.5 kts\n\
|
||||
Course: 180°\n\
|
||||
\n\
|
||||
🚢 CARGO VESSEL ATLANTIS\n\
|
||||
MMSI: 456789123\n\
|
||||
Distance: 5.8 NM @ 270°\n\
|
||||
Speed: 18.2 kts\n\
|
||||
Course: 090°\n\
|
||||
\n\
|
||||
⛵ S/Y WIND DANCER\n\
|
||||
MMSI: 789123456\n\
|
||||
Distance: 1.3 NM @ 135°\n\
|
||||
Speed: 6.8 kts\n\
|
||||
Course: 225°"
|
||||
);
|
||||
}
|
||||
None => {
|
||||
text.0 = "Select a system above to view details".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_system_type_enum() {
|
||||
let gps = SystemType::Gps;
|
||||
let radar = SystemType::Radar;
|
||||
let ais = SystemType::Ais;
|
||||
|
||||
assert_ne!(gps, radar);
|
||||
assert_ne!(radar, ais);
|
||||
assert_ne!(ais, gps);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_selected_system_default() {
|
||||
let selected_system = SelectedSystem::default();
|
||||
assert_eq!(selected_system.current, None);
|
||||
}
|
||||
}
|
406
crates/systems/src/yacht_systems.rs
Normal file
406
crates/systems/src/yacht_systems.rs
Normal file
@@ -0,0 +1,406 @@
|
||||
//! Concrete implementations of yacht systems using the SystemManager abstraction
|
||||
//!
|
||||
//! This module provides implementations of the YachtSystem trait for GPS, Radar, and AIS systems,
|
||||
//! bridging the existing functionality with the new higher-level abstraction.
|
||||
|
||||
use bevy::prelude::*;
|
||||
use components::YachtData;
|
||||
|
||||
/// Status of a yacht system
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SystemStatus {
|
||||
Active,
|
||||
Inactive,
|
||||
Error(String),
|
||||
Maintenance,
|
||||
}
|
||||
|
||||
/// Interaction types for yacht systems
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SystemInteraction {
|
||||
Select,
|
||||
Toggle,
|
||||
Reset,
|
||||
Configure(String, String),
|
||||
}
|
||||
|
||||
/// Common trait for all yacht systems
|
||||
pub trait YachtSystem: Send + Sync {
|
||||
fn id(&self) -> &'static str;
|
||||
fn display_name(&self) -> &'static str;
|
||||
fn update(&mut self, yacht_data: &YachtData, time: &Time);
|
||||
fn render_display(&self, yacht_data: &YachtData) -> String;
|
||||
fn handle_interaction(&mut self, interaction: SystemInteraction) -> bool;
|
||||
fn status(&self) -> SystemStatus;
|
||||
}
|
||||
|
||||
/// GPS Navigation System implementation
|
||||
pub struct GpsSystem {
|
||||
status: SystemStatus,
|
||||
satellites_connected: u8,
|
||||
hdop: f32,
|
||||
}
|
||||
|
||||
impl GpsSystem {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
status: SystemStatus::Active,
|
||||
satellites_connected: 12,
|
||||
hdop: 0.8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl YachtSystem for GpsSystem {
|
||||
fn id(&self) -> &'static str {
|
||||
"gps"
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &'static str {
|
||||
"GPS Navigation"
|
||||
}
|
||||
|
||||
fn update(&mut self, _yacht_data: &YachtData, time: &Time) {
|
||||
// Simulate satellite connection variations
|
||||
let t = time.elapsed_secs();
|
||||
self.satellites_connected = (12.0 + (t * 0.1).sin() * 2.0).max(8.0) as u8;
|
||||
self.hdop = 0.8 + (t * 0.05).sin() * 0.2;
|
||||
}
|
||||
|
||||
fn render_display(&self, yacht_data: &YachtData) -> String {
|
||||
format!(
|
||||
"GPS NAVIGATION SYSTEM\n\n\
|
||||
Position: 43°38'19.5\"N 1°26'58.3\"W\n\
|
||||
Heading: {:.0}°\n\
|
||||
Speed: {:.1} knots\n\
|
||||
Course Over Ground: {:.0}°\n\
|
||||
Satellites: {} connected\n\
|
||||
HDOP: {:.1} ({})\n\
|
||||
\n\
|
||||
Next Waypoint: MONACO HARBOR\n\
|
||||
Distance: 127.3 NM\n\
|
||||
ETA: 10h 12m",
|
||||
yacht_data.heading,
|
||||
yacht_data.speed,
|
||||
yacht_data.heading + 5.0,
|
||||
self.satellites_connected,
|
||||
self.hdop,
|
||||
if self.hdop < 1.0 { "Excellent" } else if self.hdop < 2.0 { "Good" } else { "Fair" }
|
||||
)
|
||||
}
|
||||
|
||||
fn handle_interaction(&mut self, interaction: SystemInteraction) -> bool {
|
||||
match interaction {
|
||||
SystemInteraction::Select => {
|
||||
self.status = SystemStatus::Active;
|
||||
true
|
||||
}
|
||||
SystemInteraction::Reset => {
|
||||
self.satellites_connected = 12;
|
||||
self.hdop = 0.8;
|
||||
true
|
||||
}
|
||||
SystemInteraction::Toggle => {
|
||||
self.status = match self.status {
|
||||
SystemStatus::Active => SystemStatus::Inactive,
|
||||
SystemStatus::Inactive => SystemStatus::Active,
|
||||
_ => SystemStatus::Active,
|
||||
};
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn status(&self) -> SystemStatus {
|
||||
self.status.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Radar System implementation
|
||||
pub struct RadarSystem {
|
||||
status: SystemStatus,
|
||||
range_nm: f32,
|
||||
gain: String,
|
||||
sea_clutter_db: i8,
|
||||
rain_clutter: bool,
|
||||
sweep_angle: f32,
|
||||
}
|
||||
|
||||
impl RadarSystem {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
status: SystemStatus::Active,
|
||||
range_nm: 12.0,
|
||||
gain: "AUTO".to_string(),
|
||||
sea_clutter_db: -15,
|
||||
rain_clutter: false,
|
||||
sweep_angle: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl YachtSystem for RadarSystem {
|
||||
fn id(&self) -> &'static str {
|
||||
"radar"
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &'static str {
|
||||
"Radar System"
|
||||
}
|
||||
|
||||
fn update(&mut self, _yacht_data: &YachtData, time: &Time) {
|
||||
// Update radar sweep angle
|
||||
self.sweep_angle = (time.elapsed_secs() * 60.0) % 360.0;
|
||||
}
|
||||
|
||||
fn render_display(&self, _yacht_data: &YachtData) -> String {
|
||||
format!(
|
||||
"RADAR SYSTEM - {:.0} NM RANGE\n\n\
|
||||
Status: {}\n\
|
||||
Sweep: {:.0}°\n\
|
||||
Gain: {}\n\
|
||||
Sea Clutter: {} dB\n\
|
||||
Rain Clutter: {}\n\
|
||||
\n\
|
||||
CONTACTS DETECTED:\n\
|
||||
• Vessel 1: 2.3 NM @ 045° (15 kts)\n\
|
||||
• Vessel 2: 5.7 NM @ 180° (8 kts)\n\
|
||||
• Land Mass: 8.2 NM @ 270°\n\
|
||||
• Buoy: 1.1 NM @ 315°",
|
||||
self.range_nm,
|
||||
match self.status {
|
||||
SystemStatus::Active => "ACTIVE",
|
||||
SystemStatus::Inactive => "STANDBY",
|
||||
SystemStatus::Error(_) => "ERROR",
|
||||
SystemStatus::Maintenance => "MAINTENANCE",
|
||||
},
|
||||
self.sweep_angle,
|
||||
self.gain,
|
||||
self.sea_clutter_db,
|
||||
if self.rain_clutter { "ON" } else { "OFF" }
|
||||
)
|
||||
}
|
||||
|
||||
fn handle_interaction(&mut self, interaction: SystemInteraction) -> bool {
|
||||
match interaction {
|
||||
SystemInteraction::Select => {
|
||||
self.status = SystemStatus::Active;
|
||||
true
|
||||
}
|
||||
SystemInteraction::Configure(key, value) => {
|
||||
match key.as_str() {
|
||||
"range" => {
|
||||
if let Ok(range) = value.parse::<f32>() {
|
||||
self.range_nm = range.clamp(1.0, 48.0);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
"gain" => {
|
||||
self.gain = value;
|
||||
true
|
||||
}
|
||||
"sea_clutter" => {
|
||||
if let Ok(db) = value.parse::<i8>() {
|
||||
self.sea_clutter_db = db.clamp(-30, 0);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
"rain_clutter" => {
|
||||
self.rain_clutter = value.to_lowercase() == "on" || value == "true";
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
SystemInteraction::Reset => {
|
||||
self.range_nm = 12.0;
|
||||
self.gain = "AUTO".to_string();
|
||||
self.sea_clutter_db = -15;
|
||||
self.rain_clutter = false;
|
||||
true
|
||||
}
|
||||
SystemInteraction::Toggle => {
|
||||
self.status = match self.status {
|
||||
SystemStatus::Active => SystemStatus::Inactive,
|
||||
SystemStatus::Inactive => SystemStatus::Active,
|
||||
_ => SystemStatus::Active,
|
||||
};
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn status(&self) -> SystemStatus {
|
||||
self.status.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// AIS (Automatic Identification System) implementation
|
||||
pub struct AisSystem {
|
||||
status: SystemStatus,
|
||||
own_mmsi: u32,
|
||||
receiving: bool,
|
||||
}
|
||||
|
||||
impl AisSystem {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
status: SystemStatus::Active,
|
||||
own_mmsi: 123456789,
|
||||
receiving: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl YachtSystem for AisSystem {
|
||||
fn id(&self) -> &'static str {
|
||||
"ais"
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &'static str {
|
||||
"AIS System"
|
||||
}
|
||||
|
||||
fn update(&mut self, _yacht_data: &YachtData, _time: &Time) {
|
||||
// AIS system is relatively static, but we could simulate
|
||||
// vessel movements or signal strength variations here
|
||||
}
|
||||
|
||||
fn render_display(&self, _yacht_data: &YachtData) -> String {
|
||||
format!(
|
||||
"AIS - AUTOMATIC IDENTIFICATION SYSTEM\n\n\
|
||||
Status: {}\n\
|
||||
Own Ship MMSI: {}\n\
|
||||
\n\
|
||||
NEARBY VESSELS:\n\
|
||||
\n\
|
||||
🛥️ M/Y SERENITY\n\
|
||||
MMSI: 987654321\n\
|
||||
Distance: 2.1 NM @ 045°\n\
|
||||
Speed: 12.5 kts\n\
|
||||
Course: 180°\n\
|
||||
\n\
|
||||
🚢 CARGO VESSEL ATLANTIS\n\
|
||||
MMSI: 456789123\n\
|
||||
Distance: 5.8 NM @ 270°\n\
|
||||
Speed: 18.2 kts\n\
|
||||
Course: 090°\n\
|
||||
\n\
|
||||
⛵ S/Y WIND DANCER\n\
|
||||
MMSI: 789123456\n\
|
||||
Distance: 1.3 NM @ 135°\n\
|
||||
Speed: 6.8 kts\n\
|
||||
Course: 225°",
|
||||
if self.receiving { "RECEIVING" } else { "STANDBY" },
|
||||
self.own_mmsi
|
||||
)
|
||||
}
|
||||
|
||||
fn handle_interaction(&mut self, interaction: SystemInteraction) -> bool {
|
||||
match interaction {
|
||||
SystemInteraction::Select => {
|
||||
self.status = SystemStatus::Active;
|
||||
self.receiving = true;
|
||||
true
|
||||
}
|
||||
SystemInteraction::Configure(key, value) => {
|
||||
match key.as_str() {
|
||||
"mmsi" => {
|
||||
if let Ok(mmsi) = value.parse::<u32>() {
|
||||
self.own_mmsi = mmsi;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
SystemInteraction::Toggle => {
|
||||
self.receiving = !self.receiving;
|
||||
self.status = if self.receiving {
|
||||
SystemStatus::Active
|
||||
} else {
|
||||
SystemStatus::Inactive
|
||||
};
|
||||
true
|
||||
}
|
||||
SystemInteraction::Reset => {
|
||||
self.own_mmsi = 123456789;
|
||||
self.receiving = true;
|
||||
self.status = SystemStatus::Active;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn status(&self) -> SystemStatus {
|
||||
self.status.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to create and register all yacht systems
|
||||
pub fn create_yacht_systems() -> Vec<Box<dyn YachtSystem>> {
|
||||
vec![
|
||||
Box::new(GpsSystem::new()),
|
||||
Box::new(RadarSystem::new()),
|
||||
Box::new(AisSystem::new()),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_gps_system() {
|
||||
let gps = GpsSystem::new();
|
||||
assert_eq!(gps.id(), "gps");
|
||||
assert_eq!(gps.display_name(), "GPS Navigation");
|
||||
assert_eq!(gps.status(), SystemStatus::Active);
|
||||
|
||||
let yacht_data = YachtData::default();
|
||||
let display = gps.render_display(&yacht_data);
|
||||
assert!(display.contains("GPS NAVIGATION SYSTEM"));
|
||||
assert!(display.contains("Satellites: 12 connected"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_radar_system() {
|
||||
let mut radar = RadarSystem::new();
|
||||
assert_eq!(radar.id(), "radar");
|
||||
assert_eq!(radar.display_name(), "Radar System");
|
||||
|
||||
// Test configuration
|
||||
assert!(radar.handle_interaction(SystemInteraction::Configure("range".to_string(), "24".to_string())));
|
||||
let display = radar.render_display(&YachtData::default());
|
||||
assert!(display.contains("24 NM RANGE"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ais_system() {
|
||||
let mut ais = AisSystem::new();
|
||||
assert_eq!(ais.id(), "ais");
|
||||
assert_eq!(ais.display_name(), "AIS System");
|
||||
|
||||
// Test toggle
|
||||
assert!(ais.handle_interaction(SystemInteraction::Toggle));
|
||||
assert_eq!(ais.status(), SystemStatus::Inactive);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_yacht_systems() {
|
||||
let systems = create_yacht_systems();
|
||||
assert_eq!(systems.len(), 3);
|
||||
|
||||
let ids: Vec<&str> = systems.iter().map(|s| s.id()).collect();
|
||||
assert!(ids.contains(&"gps"));
|
||||
assert!(ids.contains(&"radar"));
|
||||
assert!(ids.contains(&"ais"));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user