1#![allow(clippy::must_use_candidate)]
9
10use bytes::{Buf, BufMut, BytesMut};
11use chrono::{Timelike, Utc};
12use modular_bitfield::prelude::*;
13
14use crate::{
15 common::{
16 GenericHeader, SerializedLength,
17 enums::{
18 ActiveInterrogationIndicator, CoupledExtensionIndicator, DetonationTypeIndicator,
19 FireTypeIndicator, IntercomAttachedIndicator, LVCIndicator, PduStatusIFFSimulationMode,
20 PduType, ProtocolFamily, ProtocolVersion, RadioAttachedIndicator,
21 TransferredEntityIndicator,
22 },
23 },
24 pdu_macro::{FieldDeserialize, FieldLen, FieldSerialize},
25};
26
27#[bitfield(bits = 8)]
28#[derive(Copy, Clone, Debug, PartialEq, Eq)]
29pub struct PduStatusRecord {
30 pub tei: TransferredEntityIndicator,
31 pub lvc: LVCIndicator,
32 pub cei: CoupledExtensionIndicator,
33 pub bit4_5: B2,
34 #[skip]
35 __reserved: B2,
36}
37
38impl PduStatusRecord {
39 #[must_use]
40 pub const fn new_zero() -> Self {
41 Self::new()
42 }
43
44 #[must_use]
45 pub fn get_dti(&self) -> DetonationTypeIndicator {
46 DetonationTypeIndicator::from(self.bit4_5())
47 }
48
49 pub fn set_dti(&mut self, dti: DetonationTypeIndicator) {
50 self.set_bit4_5(dti.into());
51 }
52
53 #[must_use]
54 pub fn get_rai(&self) -> RadioAttachedIndicator {
55 RadioAttachedIndicator::from(self.bit4_5())
56 }
57
58 pub fn set_rai(&mut self, rai: RadioAttachedIndicator) {
59 self.set_bit4_5(rai.into());
60 }
61
62 #[must_use]
63 pub fn get_iai(&self) -> IntercomAttachedIndicator {
64 IntercomAttachedIndicator::from(self.bit4_5())
65 }
66
67 pub fn set_iai(&mut self, iai: IntercomAttachedIndicator) {
68 self.set_bit4_5(iai.into());
69 }
70
71 #[must_use]
72 pub fn get_fti(&self) -> FireTypeIndicator {
73 let bit4 = self.bit4_5() & 0b01;
74 if bit4 == 0 {
75 FireTypeIndicator::Munition
76 } else {
77 FireTypeIndicator::Expendable
78 }
79 }
80
81 pub fn set_fti(&mut self, fti: FireTypeIndicator) {
82 let mut v = self.bit4_5();
83 v = (v & 0b10) | (fti as u8 & 0x01);
84 self.set_bit4_5(v);
85 }
86
87 #[must_use]
88 pub fn get_ism(&self) -> PduStatusIFFSimulationMode {
89 let bit4 = self.bit4_5() & 0b01;
90 if bit4 == 0 {
91 PduStatusIFFSimulationMode::Regeneration
92 } else {
93 PduStatusIFFSimulationMode::Interactive
94 }
95 }
96
97 pub fn set_ism(&mut self, ism: PduStatusIFFSimulationMode) {
98 let mut v = self.bit4_5();
99 v = (v & 0b10) | (ism as u8 & 0x01);
100 self.set_bit4_5(v);
101 }
102
103 #[must_use]
104 pub fn get_aii(&self) -> ActiveInterrogationIndicator {
105 let bit5 = (self.bit4_5() >> 1) & 0b01;
106 if bit5 == 0 {
107 ActiveInterrogationIndicator::NotActive
108 } else {
109 ActiveInterrogationIndicator::Active
110 }
111 }
112
113 pub fn set_aii(&mut self, aii: ActiveInterrogationIndicator) {
114 let mut v = self.bit4_5();
115 v = (v & 0b01) | ((aii as u8) << 1);
116 self.set_bit4_5(v);
117 }
118
119 #[must_use]
120 pub const fn to_u8(&self) -> u8 {
121 self.into_bytes()[0]
122 }
123
124 #[must_use]
125 pub const fn from_u8(b: u8) -> Self {
126 Self::from_bytes([b])
127 }
128}
129
130impl Default for PduStatusRecord {
131 fn default() -> Self {
132 Self::new_zero()
133 }
134}
135
136#[derive(Copy, Clone, Debug, PartialEq, Eq)]
137pub struct PduHeader {
138 pub protocol_version: ProtocolVersion,
140 pub exercise_id: u8,
142 pub pdu_type: PduType,
144 pub protocol_family: ProtocolFamily,
146 pub timestamp: u32,
148 pub length: u16,
150 pub status_record: PduStatusRecord,
152 padding: u8,
154}
155
156impl Default for PduHeader {
157 fn default() -> Self {
158 Self {
159 protocol_version: ProtocolVersion::IEEE1278_1_2012,
160 exercise_id: 1,
161 pdu_type: PduType::default(),
162 protocol_family: ProtocolFamily::default(),
163 timestamp: Self::calculate_dis_timestamp(),
164 length: 0,
165 status_record: PduStatusRecord::default(),
166 padding: 0,
167 }
168 }
169}
170
171impl GenericHeader for PduHeader {
172 fn pdu_type(&self) -> PduType {
173 self.pdu_type
174 }
175
176 fn set_pdu_type(&mut self, value: PduType) {
177 self.pdu_type = value;
178 }
179
180 fn protocol_family(&self) -> ProtocolFamily {
181 self.protocol_family
182 }
183
184 fn set_protocol_family(&mut self, value: ProtocolFamily) {
185 self.protocol_family = value;
186 }
187
188 fn length(&self) -> u16 {
189 self.length
190 }
191
192 fn set_length(&mut self, value: u16) {
193 self.length = value;
194 }
195
196 fn serialize(&self, buf: &mut BytesMut) {
197 buf.put_u8(self.protocol_version as u8);
198 buf.put_u8(self.exercise_id);
199 buf.put_u8(self.pdu_type as u8);
200 buf.put_u8(self.protocol_family as u8);
201 buf.put_u32(self.timestamp);
202 buf.put_u16(self.length);
203 buf.put_u8(self.status_record.to_u8());
204 buf.put_u8(self.padding);
205 }
206
207 fn deserialize<B: Buf>(buf: &mut B) -> Self {
208 Self {
209 protocol_version: ProtocolVersion::deserialize(buf),
210 exercise_id: buf.get_u8(),
211 pdu_type: PduType::deserialize(buf),
212 protocol_family: ProtocolFamily::deserialize(buf),
213 timestamp: buf.get_u32(),
214 length: buf.get_u16(),
215 status_record: PduStatusRecord::from_u8(buf.get_u8()),
216 padding: buf.get_u8(),
217 }
218 }
219}
220
221impl PduHeader {
222 #[must_use]
223 pub fn new(
224 pdu_type: PduType,
225 protocol_family: ProtocolFamily,
226 exercise_id: u8,
227 length: u16,
228 ) -> Self {
229 Self {
230 protocol_version: ProtocolVersion::IEEE1278_1_2012,
231 exercise_id,
232 pdu_type,
233 protocol_family,
234 timestamp: Self::calculate_dis_timestamp(),
235 length,
236 status_record: PduStatusRecord::default(),
237 padding: 0,
238 }
239 }
240
241 #[must_use]
243 #[allow(
244 clippy::cast_precision_loss,
245 clippy::cast_possible_truncation,
246 clippy::cast_sign_loss
247 )]
248 pub fn calculate_dis_timestamp() -> u32 {
249 let minute_curr = u64::from((Utc::now().minute() * 60) * 1_000_000);
250 let second_curr = u64::from(Utc::now().second() * 1_000_000);
251 let nanosecond_curr = u64::from(Utc::now().nanosecond() / 1000);
252 let dis_time = (second_curr + minute_curr + nanosecond_curr) as f32 / 1.68;
253 dis_time as u32
254 }
255
256 #[must_use]
259 pub fn get_pdu_type(bytes: &[u8]) -> Option<PduType> {
260 if bytes.len() < Self::LENGTH {
261 return None;
262 }
263 let mut buf = &bytes[..Self::LENGTH];
264 Some(Self::deserialize(&mut buf).pdu_type)
265 }
266}
267
268impl FieldSerialize for PduHeader {
269 fn serialize_field(&self, buf: &mut BytesMut) {
270 self.serialize(buf);
271 }
272}
273
274impl FieldDeserialize for PduHeader {
275 fn deserialize_field<B: Buf>(buf: &mut B) -> Self {
276 Self::deserialize(buf)
277 }
278}
279
280impl FieldLen for PduHeader {
281 fn field_len(&self) -> usize {
282 Self::LENGTH
283 }
284}
285
286impl SerializedLength for PduHeader {
287 const LENGTH: usize = 12;
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn get_pdu_type_returns_correct_type() {
296 let mut buf = BytesMut::new();
297 let header = PduHeader::new(PduType::EntityState, ProtocolFamily::default(), 1, 12);
298 header.serialize(&mut buf);
299 assert_eq!(PduHeader::get_pdu_type(&buf), Some(PduType::EntityState));
300 }
301
302 #[test]
303 fn get_pdu_type_too_short_returns_none() {
304 assert_eq!(PduHeader::get_pdu_type(&[0u8; 11]), None);
305 }
306
307 #[test]
308 fn get_pdu_type_ignores_trailing_bytes() {
309 let mut buf = BytesMut::new();
310 let header = PduHeader::new(PduType::Detonation, ProtocolFamily::default(), 1, 12);
311 header.serialize(&mut buf);
312 buf.extend_from_slice(&[0xFF; 100]); assert_eq!(PduHeader::get_pdu_type(&buf), Some(PduType::Detonation));
314 }
315
316 #[test]
317 fn get_pdu_type_all_zeros_returns_other() {
318 assert_eq!(PduHeader::get_pdu_type(&[0u8; 12]), Some(PduType::Other));
320 }
321}