dis_rust/warfare/
fire_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 crate::common::{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};
12use bytes::{BytesMut, BufMut, Buf};
13
14use super::burst_descriptor_record::BurstDescriptorRecord;
15
16#[derive(Copy, Clone, Debug)]
17/// Fire PDU as defined in IEEE 1278.1 standard. Used to communicate a weapon firing during the simulation.
18pub struct FirePDU {
19    pub pdu_header_record: PDUHeaderRecord,
20    pub firing_entity_id_record: EntityIDRecord,
21    pub target_entity_id_record: EntityIDRecord,
22    pub munition_id_record: EntityIDRecord,
23    pub event_id_record: EventIDRecord,
24    pub fire_mission_index_field: u32,
25    pub location_in_world_record: WorldCoordinateRecord,
26    pub burst_descriptor_record: BurstDescriptorRecord,
27    pub velocity_record: VelocityVectorRecord,
28    pub range_field: f32
29}
30
31impl FirePDU {
32    /// Provides a blank FirePDU with all IDs set to 1 (as 0 is not allowed within the DIS protocol).
33    /// 
34    /// # Examples
35    /// 
36    /// Creating a blank FirePDU:
37    /// 
38    /// ```
39    /// let fire_pdu = FirePDU::default();
40    /// ```
41    /// 
42    /// Populating a bank FirePDU's firing_entity_id_record:
43    /// 
44    /// ```
45    /// let mut fire_pdu = FirePDU::default();
46    /// fire_pdu.firing_entity_id_record = EntityIDRecord::default(2);
47    /// ```
48    /// 
49    pub fn default() -> Self {
50        FirePDU {
51            pdu_header_record: PDUHeaderRecord::default(PDUType::Fire, ProtocolFamily::Warfare, 96),
52            firing_entity_id_record: EntityIDRecord::default(1),
53            target_entity_id_record: EntityIDRecord::default(1),
54            munition_id_record: EntityIDRecord::default(1),
55            event_id_record: EventIDRecord::default(1),
56            fire_mission_index_field: 0,
57            location_in_world_record: WorldCoordinateRecord::new(0.0, 0.0, 0.0),
58            burst_descriptor_record: BurstDescriptorRecord::default(),
59            velocity_record: VelocityVectorRecord::new(0.0, 0.0, 0.0),
60            range_field: 0.0
61        }
62    }        
63}
64
65impl PDU for FirePDU {
66    /// Fills a BytesMut struct with a FirePDU serialised into binary. This buffer is then ready to be sent via
67    /// UDP to the simluation network.
68    fn serialise(&self, buf: &mut BytesMut) {
69        self.pdu_header_record.serialize(buf);
70        self.firing_entity_id_record.serialize(buf);
71        self.target_entity_id_record.serialize(buf);
72        self.munition_id_record.serialize(buf);
73        self.event_id_record.serialize(buf);
74        buf.put_u32(self.fire_mission_index_field);
75        self.location_in_world_record.serialize(buf);
76        self.burst_descriptor_record.serialize(buf);
77        self.velocity_record.serialize(buf);
78        buf.put_f32(self.range_field);
79    }
80
81    fn deserialise(mut buffer: BytesMut) -> Result<Self, crate::common::dis_error::DISError> where Self: Sized {
82        let pdu_header = PDUHeaderRecord::decode(&mut buffer);
83        if pdu_header.pdu_type == PDUType::Fire {
84            let firing_entity_id = EntityIDRecord::decode(&mut buffer);
85            let target_entity_id = EntityIDRecord::decode(&mut buffer);
86            let munition_entity_id = EntityIDRecord::decode(&mut buffer);
87            let event_id = EventIDRecord::decode(&mut buffer);
88            let fire_mission = buffer.get_u32();
89            let location = WorldCoordinateRecord::decode(&mut buffer);
90            let burst_descriptor = BurstDescriptorRecord::decode(&mut buffer);
91            let velocity = VelocityVectorRecord::decode(&mut buffer);
92            let range = buffer.get_f32();
93            return Ok(FirePDU {
94                pdu_header_record: pdu_header,
95                firing_entity_id_record: firing_entity_id,
96                target_entity_id_record: target_entity_id,
97                munition_id_record: munition_entity_id,
98                event_id_record: event_id,
99                fire_mission_index_field: fire_mission,
100                location_in_world_record: location,
101                burst_descriptor_record: burst_descriptor,
102                velocity_record: velocity,
103                range_field: range,
104            })
105        } else {
106            Err(DISError::InvalidDISHeader)
107        }
108    }
109
110    fn as_any(&self) -> &dyn Any {
111        self
112      }
113
114    fn deserialise_without_header(mut buffer: BytesMut, pdu_header: PDUHeaderRecord) -> Result<Self, DISError> where Self: Sized {
115        let firing_entity_id = EntityIDRecord::decode(&mut buffer);
116            let target_entity_id = EntityIDRecord::decode(&mut buffer);
117            let munition_entity_id = EntityIDRecord::decode(&mut buffer);
118            let event_id = EventIDRecord::decode(&mut buffer);
119            let fire_mission = buffer.get_u32();
120            let location = WorldCoordinateRecord::decode(&mut buffer);
121            let burst_descriptor = BurstDescriptorRecord::decode(&mut buffer);
122            let velocity = VelocityVectorRecord::decode(&mut buffer);
123            let range = buffer.get_f32();
124            return Ok(FirePDU {
125                pdu_header_record: pdu_header,
126                firing_entity_id_record: firing_entity_id,
127                target_entity_id_record: target_entity_id,
128                munition_id_record: munition_entity_id,
129                event_id_record: event_id,
130                fire_mission_index_field: fire_mission,
131                location_in_world_record: location,
132                burst_descriptor_record: burst_descriptor,
133                velocity_record: velocity,
134                range_field: range,
135            })
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use bytes::BytesMut;
142
143    use crate::{common::{pdu_header_record::{PDUHeaderRecord, PDUType, ProtocolFamily}, pdu::PDU}, warfare::fire_pdu::FirePDU};
144
145    #[test]
146    fn header_creation() {
147        let firing_pdu = FirePDU::default();
148        let header = PDUHeaderRecord::default(PDUType::Fire, ProtocolFamily::Warfare, 768/8);
149        assert_eq!(header.protocol_version, firing_pdu.pdu_header_record.protocol_version);
150        assert_eq!(header.exercise_id, firing_pdu.pdu_header_record.exercise_id);
151        assert_eq!(header.pdu_type, firing_pdu.pdu_header_record.pdu_type);
152        assert_eq!(header.protocol_family, firing_pdu.pdu_header_record.protocol_family);
153        assert_eq!(header.timestamp, firing_pdu.pdu_header_record.timestamp);
154        assert_eq!(header.length, firing_pdu.pdu_header_record.length);
155        assert_eq!(header.padding, firing_pdu.pdu_header_record.padding);
156    }
157
158    #[test]
159    fn header_deserialise() {
160        let fire_pdu = FirePDU::default();
161        let mut buffer = BytesMut::new();
162        fire_pdu.serialise(&mut buffer);
163
164        let new_fire_pdu = FirePDU::deserialise(buffer).unwrap();
165        assert_eq!(new_fire_pdu.pdu_header_record, fire_pdu.pdu_header_record);
166    }
167}