open_dis_rust/
minefield.rs

1//     open-dis-rust - Rust implementation of the IEEE-1278.1 Distributed Interactive Simulation
2//                     (DIS) application protocol
3//     Copyright (C) 2025 Cameron Howell
4//
5//     Licensed under the BSD 2-Clause License
6
7//! The Minefield protocol family
8
9use crate::{
10    common::{
11        GenericHeader, Pdu, PduHeader, SerializedLength,
12        data_types::{
13            ClockTime, EntityCoordinateVector, EntityId, EntityType, EulerAngles, WorldCoordinate,
14            minefield_identifier::MinefieldIdentifier, point::Point,
15        },
16        enums::{
17            ForceId, MinefieldSensorTypes, MinefieldStateProtocolMode, PduType, ProtocolFamily,
18        },
19    },
20    define_pdu,
21};
22
23define_pdu! {
24    #[derive(Debug)]
25    /// Implemented according to IEEE 1278.1-2012 §7.9.2
26    pub struct MinefieldStatePdu {
27        header: PduHeader,
28        pdu_type: PduType::MinefieldState,
29        protocol_family: ProtocolFamily::Minefield,
30        fields: {
31            pub minefield_id: MinefieldIdentifier,
32            pub minefield_sequence: u16,
33            pub force_id: ForceId,
34            pub number_of_perimeter_points: u8,
35            pub minefield_type: EntityType,
36            pub number_of_mine_types: u16,
37            pub minefield_location: WorldCoordinate,
38            pub minefield_orientation: EulerAngles,
39            pub appearance: u16,
40            pub protocol_mode: MinefieldStateProtocolMode,
41            pub perimeter_points: Vec<Point>,
42            pub mine_type: Vec<EntityType>,
43        }
44    }
45}
46
47define_pdu! {
48    #[derive(Debug)]
49    /// Implemented according to IEEE 1278.1-2012 §7.9.3
50    pub struct MinefieldQueryPdu {
51        header: PduHeader,
52        pdu_type: PduType::MinefieldQuery,
53        protocol_family: ProtocolFamily::Minefield,
54        fields: {
55            pub minefield_id: MinefieldIdentifier,
56            pub requesting_entity_id: EntityId,
57            pub request_id: u8,
58            pub number_of_perimeter_points: u8,
59            padding: u8,
60            pub number_of_sensor_types: u8,
61            pub data_filter: u32,
62            pub requested_mine_type: EntityType,
63            pub requested_perimeter_points: Vec<Point>,
64            pub sensor_types: Vec<MinefieldSensorTypes>,
65        }
66    }
67}
68
69define_pdu! {
70    #[derive(Debug)]
71    /// Implemented according to IEEE 1278.1-2012 §7.9.4
72    pub struct MinefieldDataPdu {
73        header: PduHeader,
74        pdu_type: PduType::MinefieldData,
75        protocol_family: ProtocolFamily::Minefield,
76        fields: {
77            pub minefield_id: MinefieldIdentifier,
78            pub requesting_entity_id: EntityId,
79            pub minefield_sequence_number: u16,
80            pub request_id: u8,
81            pub pdu_sequence_number: u8,
82            pub number_of_pdus: u8,
83            pub number_of_mines_in_this_pdu: u8,
84            pub number_of_sensor_types: u8,
85            padding: u8,
86            pub data_filter: u32,
87            pub mine_type: EntityType,
88            pub sensor_types: Vec<MinefieldSensorTypes>,
89            pub mine_location: Vec<EntityCoordinateVector>,
90            pub ground_burial_depth_offset: Vec<Option<f32>>,
91            pub water_burial_depth_offset: Vec<Option<f32>>,
92            pub snow_burial_depth_offset: Vec<Option<f32>>,
93            pub mine_orientation: Vec<Option<EulerAngles>>,
94            pub thermal_contrast: Vec<Option<f32>>,
95            pub reflectance: Vec<Option<f32>>,
96            pub mine_emplacement_time: Vec<Option<ClockTime>>,
97            pub mine_entity_id: Vec<Option<u16>>,
98            pub fusing: Vec<Option<u16>>,
99            pub scalar_detection_coefficient: Vec<Option<u8>>,
100            pub paint_scheme: Vec<Option<u8>>,
101            pub number_of_trip_wires: Vec<Option<u8>>,
102            pub number_of_vertices: Vec<Option<u8>>,
103            pub vertices: Vec<Option<Vec<EntityCoordinateVector>>>,
104        }
105    }
106}
107
108define_pdu! {
109    #[derive(Debug)]
110    /// Implemented according to IEEE 1278.1-2012 §7.9.5
111    pub struct MinefieldResponseNackPdu {
112        header: PduHeader,
113        pdu_type: PduType::MinefieldResponseNack,
114        protocol_family: ProtocolFamily::Minefield,
115        fields: {
116            pub minefield_id: EntityId,
117            pub requesting_entity_id: EntityId,
118            pub request_id: u8,
119            pub number_of_missing_pdus: u8,
120            pub missing_pdu_sequence_numbers: Vec<u64>,
121        }
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    use crate::common::{constants::BITS_PER_BYTE, pdu::Pdu};
129    use bytes::BytesMut;
130
131    mod minefield_state_pdu_tests {
132        use super::*;
133
134        #[test]
135        fn cast_to_any() {
136            let pdu = MinefieldStatePdu::new();
137            let any_pdu = pdu.as_any();
138
139            assert!(any_pdu.is::<MinefieldStatePdu>());
140        }
141
142        #[test]
143        fn serialize_then_deserialize() {
144            let mut pdu = MinefieldStatePdu::new();
145            let mut serialize_buf = BytesMut::new();
146            let _ = pdu.serialize(&mut serialize_buf);
147
148            let mut deserialize_buf = serialize_buf.freeze();
149            let new_pdu = MinefieldStatePdu::deserialize(&mut deserialize_buf).unwrap_or_default();
150            assert_eq!(new_pdu.header, pdu.header);
151        }
152
153        #[test]
154        fn check_default_pdu_length() {
155            const DEFAULT_LENGTH: u16 = 576 / BITS_PER_BYTE;
156            let pdu = MinefieldStatePdu::new();
157            assert_eq!(pdu.header().length, DEFAULT_LENGTH);
158        }
159    }
160
161    mod minefield_query_pdu_tests {
162        use super::*;
163
164        #[test]
165        fn cast_to_any() {
166            let pdu = MinefieldQueryPdu::new();
167            let any_pdu = pdu.as_any();
168
169            assert!(any_pdu.is::<MinefieldQueryPdu>());
170        }
171
172        #[test]
173        fn serialize_then_deserialize() {
174            let mut pdu = MinefieldQueryPdu::new();
175            let mut serialize_buf = BytesMut::new();
176            let _ = pdu.serialize(&mut serialize_buf);
177
178            let mut deserialize_buf = serialize_buf.freeze();
179            let new_pdu = MinefieldQueryPdu::deserialize(&mut deserialize_buf).unwrap_or_default();
180            assert_eq!(new_pdu.header, pdu.header);
181        }
182
183        #[test]
184        fn check_default_pdu_length() {
185            const DEFAULT_LENGTH: u16 = 320 / BITS_PER_BYTE;
186            let pdu = MinefieldQueryPdu::new();
187            assert_eq!(pdu.header().length, DEFAULT_LENGTH);
188        }
189    }
190
191    mod minefield_data_pdu_tests {
192        use super::*;
193
194        #[test]
195        fn cast_to_any() {
196            let pdu = MinefieldDataPdu::new();
197            let any_pdu = pdu.as_any();
198
199            assert!(any_pdu.is::<MinefieldDataPdu>());
200        }
201
202        #[test]
203        fn serialize_then_deserialize() {
204            let mut pdu = MinefieldDataPdu::new();
205            let mut serialize_buf = BytesMut::new();
206            let _ = pdu.serialize(&mut serialize_buf);
207
208            let mut deserialize_buf = serialize_buf.freeze();
209            let new_pdu = MinefieldDataPdu::deserialize(&mut deserialize_buf).unwrap_or_default();
210            assert_eq!(new_pdu.header, pdu.header);
211        }
212
213        #[test]
214        fn check_default_pdu_length() {
215            const DEFAULT_LENGTH: u16 = 352 / BITS_PER_BYTE;
216            let pdu = MinefieldDataPdu::new();
217            assert_eq!(pdu.header().length, DEFAULT_LENGTH);
218        }
219    }
220
221    mod minefield_response_nack_pdu_tests {
222        use super::*;
223
224        #[test]
225        fn cast_to_any() {
226            let pdu = MinefieldResponseNackPdu::new();
227            let any_pdu = pdu.as_any();
228
229            assert!(any_pdu.is::<MinefieldResponseNackPdu>());
230        }
231
232        #[test]
233        fn serialize_then_deserialize() {
234            let mut pdu = MinefieldResponseNackPdu::new();
235            let mut serialize_buf = BytesMut::new();
236            let _ = pdu.serialize(&mut serialize_buf);
237
238            let mut deserialize_buf = serialize_buf.freeze();
239            let new_pdu =
240                MinefieldResponseNackPdu::deserialize(&mut deserialize_buf).unwrap_or_default();
241            assert_eq!(new_pdu.header, pdu.header);
242        }
243
244        #[test]
245        fn check_default_pdu_length() {
246            const DEFAULT_LENGTH: u16 = 208 / BITS_PER_BYTE;
247            let pdu = MinefieldResponseNackPdu::new();
248            assert_eq!(pdu.header().length, DEFAULT_LENGTH);
249        }
250    }
251}