open_dis_rust/
warfare.rs

1//     open-dis-rust - Rust implementation of the IEEE-1278.1 Distributed Interactive Simulation
2//     Copyright (C) 2025 Cameron Howell
3//
4//     Licensed under the BSD-2-Clause License
5
6//! The Warfare protocol family
7
8use crate::{
9    common::{
10        GenericHeader, SerializedLength,
11        data_types::{
12            ClockTime, EntityCoordinateVector, EntityId, EntityType, EventId, LinearVelocity,
13            VariableParameter, WorldCoordinate, directed_energy_damage::DirectedEnergyDamage,
14            munition_descriptor::MunitionDescriptor,
15        },
16        enums::{DEFirePulseShape, DetonationResult, PduType, ProtocolFamily},
17        pdu::Pdu,
18        pdu_header::PduHeader,
19    },
20    define_pdu,
21};
22
23define_pdu! {
24    #[derive(Debug)]
25    /// Implemented according to IEEE 1278.1-2012 §7.3.2
26    pub struct FirePdu {
27        header: PduHeader,
28        pdu_type: PduType::Fire,
29        protocol_family: ProtocolFamily::Warfare,
30        fields: {
31            pub firing_entity_id: EntityId,
32            pub target_entity_id: EntityId,
33            pub munition_expendable_id: EntityId,
34            pub event_id: EventId,
35            pub fire_mission_index: u32,
36            pub location_in_world_coordinates: WorldCoordinate,
37            pub descriptor: MunitionDescriptor,
38            pub velocity: LinearVelocity,
39            pub range: f32,
40        }
41    }
42}
43
44define_pdu! {
45    #[derive(Debug)]
46    /// Implemented according to IEEE 1278.1-2012 §7.3.3
47    pub struct DetonationPdu {
48        header: PduHeader,
49        pdu_type: PduType::Detonation,
50        protocol_family: ProtocolFamily::Warfare,
51        fields: {
52            pub firing_entity_id: EntityId,
53            pub target_entity_id: EntityId,
54            pub exploding_entity_id: EntityId,
55            pub event_id: EventId,
56            pub velocity: LinearVelocity,
57            pub location_in_world_coordinates: WorldCoordinate,
58            pub descriptor: MunitionDescriptor,
59            pub location_in_entitys_coordinates: EntityCoordinateVector,
60            pub detonation_result: DetonationResult,
61            pub number_of_variable_parameters: u8,
62            padding: u16,
63            pub variable_parameters: Vec<VariableParameter>,
64        }
65    }
66}
67
68define_pdu! {
69    #[derive(Debug)]
70    /// Implemented according to IEEE 1278.1-2012 §7.3.4
71    pub struct DirectedEnergyFirePdu {
72        header: PduHeader,
73        pdu_type: PduType::DirectedEnergyFire,
74        protocol_family: ProtocolFamily::Warfare,
75        fields: {
76            pub firing_entity_id: EntityId,
77            pub event_id: EventId,
78            pub munition_type: EntityType,
79            pub shot_start_time: ClockTime,
80            pub cumulative_shot_time: f32,
81            pub aperture_emitter_location: EntityCoordinateVector,
82            pub aperture_diameter: f32,
83            pub wavelength: f32,
84            padding: u32,
85            pub pulse_repetition_frequency: f32,
86            pub pulse_width: f32,
87            pub flags: u16,
88            pub pulse_shape: DEFirePulseShape,
89            padding2: u8,
90            padding3: u32,
91            padding4: u16,
92            pub number_of_de_records: u16,
93            pub damage_descriptions: Vec<DirectedEnergyDamage>,
94        }
95    }
96}
97
98define_pdu! {
99    #[derive(Debug)]
100    /// Implemented according to IEEE 1278.1-2012 §7.3.5
101    pub struct EntityDamageStatusPdu {
102        header: PduHeader,
103        pdu_type: PduType::EntityDamageStatus,
104        protocol_family: ProtocolFamily::Warfare,
105        fields: {
106            pub damaged_entity_id: EntityId,
107            padding: u16,
108            padding2: u16,
109            pub number_of_damage_descriptions: u16,
110            pub damage_descriptions: Vec<DirectedEnergyDamage>,
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use crate::common::{constants::BITS_PER_BYTE, pdu::Pdu};
119    use bytes::BytesMut;
120
121    mod fire_pdu_tests {
122        use super::*;
123
124        #[test]
125        fn cast_to_any() {
126            let fire_pdu = FirePdu::new();
127            let any_pdu = fire_pdu.as_any();
128
129            assert!(any_pdu.is::<FirePdu>());
130        }
131
132        #[test]
133        fn serialize_then_deserialize() {
134            let mut pdu = FirePdu::new();
135            let mut serialize_buf = BytesMut::new();
136            let _ = pdu.serialize(&mut serialize_buf);
137
138            let mut deserialize_buf = serialize_buf.freeze();
139            let new_pdu = FirePdu::deserialize(&mut deserialize_buf).unwrap_or_default();
140            assert_eq!(new_pdu.header, pdu.header);
141        }
142
143        #[test]
144        fn check_default_pdu_length() {
145            const DEFAULT_LENGTH: u16 = 768 / 8;
146            let pdu = FirePdu::new();
147            assert_eq!(pdu.header().length, DEFAULT_LENGTH);
148        }
149    }
150
151    mod detonation_pdu_tests {
152        use super::*;
153
154        #[test]
155        fn cast_to_any() {
156            let detonation_pdu = DetonationPdu::new();
157            let any_pdu = detonation_pdu.as_any();
158
159            assert!(any_pdu.is::<DetonationPdu>());
160        }
161
162        #[test]
163        fn serialize_then_deserialize() {
164            let mut pdu = DetonationPdu::new();
165            let mut serialize_buf = BytesMut::new();
166            let _ = pdu.serialize(&mut serialize_buf);
167
168            let mut deserialize_buf = serialize_buf.freeze();
169            let new_pdu = DetonationPdu::deserialize(&mut deserialize_buf).unwrap_or_default();
170            assert_eq!(new_pdu.header, pdu.header);
171        }
172
173        #[test]
174        fn check_default_pdu_length() {
175            const DEFAULT_LENGTH: u16 = 832 / BITS_PER_BYTE;
176            let pdu = DetonationPdu::new();
177            assert_eq!(pdu.header().length, DEFAULT_LENGTH);
178        }
179    }
180
181    mod directed_energy_fire_pdu_tests {
182        use super::*;
183
184        #[test]
185        fn cast_to_any() {
186            let pdu = DirectedEnergyFirePdu::new();
187            let any_pdu = pdu.as_any();
188
189            assert!(any_pdu.is::<DirectedEnergyFirePdu>());
190        }
191
192        #[test]
193        fn serialize_then_deserialize() {
194            let mut pdu = DirectedEnergyFirePdu::new();
195            let mut serialize_buf = BytesMut::new();
196            let _ = pdu.serialize(&mut serialize_buf);
197
198            let mut deserialize_buf = serialize_buf.freeze();
199            let new_pdu =
200                DirectedEnergyFirePdu::deserialize(&mut deserialize_buf).unwrap_or_default();
201            assert_eq!(new_pdu.header, pdu.header);
202        }
203
204        #[test]
205        fn check_default_pdu_length() {
206            const DEFAULT_LENGTH: u16 = 704 / BITS_PER_BYTE;
207            let pdu = DirectedEnergyFirePdu::new();
208            assert_eq!(pdu.header().length, DEFAULT_LENGTH);
209        }
210    }
211
212    mod entity_damage_status_pdu_tests {
213        use super::*;
214
215        #[test]
216        fn cast_to_any() {
217            let entity_damage_status_pdu = EntityDamageStatusPdu::new();
218            let any_pdu = entity_damage_status_pdu.as_any();
219
220            assert!(any_pdu.is::<EntityDamageStatusPdu>());
221        }
222
223        #[test]
224        fn serialize_then_deserialize() {
225            let mut pdu = EntityDamageStatusPdu::new();
226            let mut serialize_buf = BytesMut::new();
227            let _ = pdu.serialize(&mut serialize_buf);
228
229            let mut deserialize_buf = serialize_buf.freeze();
230            let new_pdu =
231                EntityDamageStatusPdu::deserialize(&mut deserialize_buf).unwrap_or_default();
232            assert_eq!(new_pdu.header, pdu.header);
233        }
234
235        #[test]
236        fn check_default_pdu_length() {
237            const DEFAULT_LENGTH: u16 = 192 / 8;
238            let pdu = EntityDamageStatusPdu::new();
239            assert_eq!(pdu.header().length, DEFAULT_LENGTH);
240        }
241    }
242}