dis_rust/warfare/
detonation_pdu.rs

1//     dis-rust - A rust implementation of the DIS simulation protocol.
2//     Copyright (C) 2022 Thomas Mann
3// 
4//     This software is dual-licensed. It is available under the conditions of
5//     the GNU Affero General Public License (see the LICENSE file included) 
6//     or under a commercial license (email contact@coffeebreakdevs.com for
7//     details).
8
9use std::any::Any;
10
11use bytes::{BytesMut, BufMut, Buf};
12
13use crate::common::{entity_coordinate_vector_record::EntityCoordinateVectorRecord, entity_id_record::EntityIDRecord, event_id_record::EventIDRecord, pdu::PDU, pdu_header_record::{PDUHeaderRecord, PDUType, ProtocolFamily}, velocity_vector_record::VelocityVectorRecord, world_coordinate_record::WorldCoordinateRecord, dis_error::DISError};
14
15use super::burst_descriptor_record::BurstDescriptorRecord;
16
17#[derive(Copy, Clone, Debug,)]
18/// Detonation PDU as defined in IEEE 1278.1 standard. Used to communicate a munition detonating during the simulation.
19pub struct DetonationPDU {
20    pub pdu_header_record: PDUHeaderRecord,
21    pub firing_entity_id_record: EntityIDRecord,
22    pub target_entity_id_record: EntityIDRecord,
23    pub munition_id_record: EntityIDRecord,
24    pub event_id_record: EventIDRecord,
25    pub velocity_record: VelocityVectorRecord,
26    pub location_in_world_record: WorldCoordinateRecord,
27    pub burst_descriptor_record: BurstDescriptorRecord,
28    pub location_in_entity_coordinates_record: EntityCoordinateVectorRecord,
29    pub detonation_result_field: DetonationResult,
30    pub number_of_articulation_parameters_field: u8,
31    pub padding: u16,
32    pub articulation_parameter_record: f32 // TODO: Needs a proper struct
33    
34}
35
36impl DetonationPDU {
37    /// Provides a blank DetonationPDU with all IDs set to 1 (as 0 is not allowed within the DIS protocol).
38    /// 
39    /// # Examples
40    /// 
41    /// Creating a blank DetonationPDU:
42    /// 
43    /// ```
44    /// let detonation_pdu = DetonationPDU::default();
45    /// ```
46    /// 
47    /// Populating a bank DetonationPDU's firing_entity_id_record (this should match the
48    /// firing_entity_id_record in the FirePDU used to launch the munition):
49    /// 
50    /// ```
51    /// let mut detonation_pdu = DetonationPDU::default();
52    /// detonation_pdu.firing_entity_id_record = EntityIDRecord::default(2);
53    /// ```
54    /// 
55    pub fn default() -> Self {
56        DetonationPDU {
57            pdu_header_record: PDUHeaderRecord::default(PDUType::Detonation, ProtocolFamily::Warfare, 768/8), //TODO: Length needs calculating dynamically
58            firing_entity_id_record: EntityIDRecord::default(1),
59            target_entity_id_record: EntityIDRecord::default(2),
60            munition_id_record: EntityIDRecord::default(3),
61            event_id_record: EventIDRecord::default(1),
62            location_in_world_record: WorldCoordinateRecord::new(0.0, 0.0, 0.0),
63            burst_descriptor_record: BurstDescriptorRecord::default(),
64            velocity_record: VelocityVectorRecord::new(570.0, 0.0, 0.0),
65            location_in_entity_coordinates_record: EntityCoordinateVectorRecord::new(0.0, 0.0, 0.0),
66            detonation_result_field: DetonationResult::AirBurst,
67            number_of_articulation_parameters_field:0,
68            padding: 0,
69            articulation_parameter_record: 0.0 
70        }
71    }
72}
73
74impl PDU for DetonationPDU {
75    /// Fills a BytesMut struct with a FirePDU serialised into binary. This buffer is then ready to be sent via
76    /// UDP to the simluation network.
77    fn serialise(&self, buf: &mut BytesMut) {
78        self.pdu_header_record.serialize(buf);
79        self.firing_entity_id_record.serialize(buf);
80        self.target_entity_id_record.serialize(buf);
81        self.munition_id_record.serialize(buf);
82        self.event_id_record.serialize(buf);
83        self.velocity_record.serialize(buf);
84        self.location_in_world_record.serialize(buf);        
85        self.burst_descriptor_record.serialize(buf);
86        self.location_in_entity_coordinates_record.serialize(buf);
87        buf.put_u8(self.detonation_result_field as u8);
88        buf.put_u8(self.number_of_articulation_parameters_field);
89        buf.put_u16(self.padding);
90    }
91
92    fn deserialise(mut buffer: BytesMut) -> Result<Self, crate::common::dis_error::DISError> where Self: Sized {
93        let pdu_header = PDUHeaderRecord::decode(&mut buffer);
94        if pdu_header.pdu_type == PDUType::Detonation {
95            let firing_entity_id = EntityIDRecord::decode(&mut buffer);
96            let target_entity_id = EntityIDRecord::decode(&mut buffer);
97            let munition_entity_id = EntityIDRecord::decode(&mut buffer);
98            let event_id = EventIDRecord::decode(&mut buffer);
99            let velocity = VelocityVectorRecord::decode(&mut buffer);
100            let location = WorldCoordinateRecord::decode(&mut buffer);
101            let burst_descriptor = BurstDescriptorRecord::decode(&mut buffer);
102            let location_in_entity_coordinates = EntityCoordinateVectorRecord::decode(&mut buffer);
103            let detonation_result = DetonationResult::from_u8(buffer.get_u8());
104            let num_articulation_params = buffer.get_u8();
105            let padding = buffer.get_u16();
106            return Ok(DetonationPDU {
107                pdu_header_record: pdu_header,
108                firing_entity_id_record: firing_entity_id,
109                target_entity_id_record: target_entity_id,
110                munition_id_record: munition_entity_id,
111                event_id_record: event_id,
112                velocity_record: velocity,
113                location_in_world_record: location,
114                burst_descriptor_record: burst_descriptor,
115                location_in_entity_coordinates_record: location_in_entity_coordinates,
116                detonation_result_field: detonation_result,
117                number_of_articulation_parameters_field: num_articulation_params,
118                padding: padding,
119                articulation_parameter_record: 0.0,
120            })
121        } else {
122            Err(DISError::InvalidDISHeader)
123        }
124    }
125
126    fn as_any(&self) -> &dyn Any {
127        self
128      }
129
130    fn deserialise_without_header(mut buffer: BytesMut, pdu_header: PDUHeaderRecord) -> Result<Self, DISError> where Self: Sized {
131        let firing_entity_id = EntityIDRecord::decode(&mut buffer);
132            let target_entity_id = EntityIDRecord::decode(&mut buffer);
133            let munition_entity_id = EntityIDRecord::decode(&mut buffer);
134            let event_id = EventIDRecord::decode(&mut buffer);
135            let velocity = VelocityVectorRecord::decode(&mut buffer);
136            let location = WorldCoordinateRecord::decode(&mut buffer);
137            let burst_descriptor = BurstDescriptorRecord::decode(&mut buffer);
138            let location_in_entity_coordinates = EntityCoordinateVectorRecord::decode(&mut buffer);
139            let detonation_result = DetonationResult::from_u8(buffer.get_u8());
140            let num_articulation_params = buffer.get_u8();
141            let padding = buffer.get_u16();
142            return Ok(DetonationPDU {
143                pdu_header_record: pdu_header,
144                firing_entity_id_record: firing_entity_id,
145                target_entity_id_record: target_entity_id,
146                munition_id_record: munition_entity_id,
147                event_id_record: event_id,
148                velocity_record: velocity,
149                location_in_world_record: location,
150                burst_descriptor_record: burst_descriptor,
151                location_in_entity_coordinates_record: location_in_entity_coordinates,
152                detonation_result_field: detonation_result,
153                number_of_articulation_parameters_field: num_articulation_params,
154                padding: padding,
155                articulation_parameter_record: 0.0,
156            })
157    }
158}
159
160#[derive(Debug, Clone, Copy)]
161/// The result of the detonation. Note that damage caused by the detonation is
162/// the responability of the entity being damaged.
163pub enum DetonationResult {
164    Other = 0,
165    EntityImpact = 1,
166    ArmourPiercingHit = 10,
167    SmallDirtBlast = 11,
168    MediumDirtBlast = 12,
169    LargeDirtBlast = 13,
170    SmallWaterBlast = 14,
171    MediumWaterBlast = 15,
172    LargeWaterBlast = 16,
173    AirHit = 17,
174    SmallBuildingHit = 18,
175    MediumBuildingHit = 19,
176    EntityProximateDetonation = 2,
177    LargeBuildingHit = 20,
178    MineClearingLineCharge = 21,
179    EnvironmentalObjectImpact = 22,
180    EnvironmentalObjectProximateDetonation = 23,
181    WaterImpact = 24,
182    AirBurst = 25,
183    GroundImpact = 3,
184    GroundImpactDetonation = 4,
185    Detonation = 5,
186    None = 6,
187    SmallHighExplosiveHit = 7,
188    MediumHighExplosiveHit = 8,
189    LargeHighExplosiveHit = 9
190}
191
192impl DetonationResult {
193    pub fn from_u8(value: u8) -> DetonationResult {
194        match value {
195            0 => DetonationResult::Other,
196            1 => DetonationResult::EntityImpact,
197            2 => DetonationResult::EntityProximateDetonation,
198            3 => DetonationResult::GroundImpact,
199            4 => DetonationResult::GroundImpactDetonation,
200            5 => DetonationResult::Detonation,
201            6 => DetonationResult::None,
202            7 => DetonationResult::SmallHighExplosiveHit,
203            8 => DetonationResult::MediumHighExplosiveHit,
204            9 => DetonationResult::LargeHighExplosiveHit,
205            10 => DetonationResult::ArmourPiercingHit,
206            11 => DetonationResult::SmallDirtBlast,
207            12 => DetonationResult::MediumDirtBlast,
208            13 => DetonationResult::LargeDirtBlast,
209            14 => DetonationResult::SmallWaterBlast,
210            15 => DetonationResult::MediumWaterBlast,
211            16 => DetonationResult::LargeWaterBlast,
212            17 => DetonationResult::AirHit,
213            18 => DetonationResult::SmallBuildingHit,
214            19 => DetonationResult::MediumBuildingHit,
215            20 => DetonationResult::LargeBuildingHit,
216            21 => DetonationResult::MineClearingLineCharge,
217            22 => DetonationResult::EnvironmentalObjectImpact,
218            23 => DetonationResult::EntityProximateDetonation,
219            24 => DetonationResult::WaterImpact,
220            25 => DetonationResult::AirBurst,
221            _ => DetonationResult::Other
222        }
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use crate::{common::pdu_header_record::{PDUHeaderRecord, PDUType, ProtocolFamily}, warfare::detonation_pdu::DetonationPDU};
229
230    #[test]
231    fn header_creation() {
232        let detonation_pdu = DetonationPDU::default();
233        let header = PDUHeaderRecord::default(PDUType::Detonation, ProtocolFamily::Warfare, 768/8);
234        assert_eq!(header.protocol_version, detonation_pdu.pdu_header_record.protocol_version);
235        assert_eq!(header.exercise_id, detonation_pdu.pdu_header_record.exercise_id);
236        assert_eq!(header.pdu_type, detonation_pdu.pdu_header_record.pdu_type);
237        assert_eq!(header.protocol_family, detonation_pdu.pdu_header_record.protocol_family);
238        assert_eq!(header.timestamp, detonation_pdu.pdu_header_record.timestamp);
239        assert_eq!(header.length, detonation_pdu.pdu_header_record.length);
240        assert_eq!(header.padding, detonation_pdu .pdu_header_record.padding);
241    }
242}