1#![allow(clippy::must_use_candidate)]
8
9use bytes::{Buf, BufMut, BytesMut};
10use chrono::{Timelike, Utc};
11use modular_bitfield::prelude::*;
12use num_derive::FromPrimitive;
13
14use crate::common::enums::{
15 ActiveInterrogationIndicator, CoupledExtensionIndicator, DetonationTypeIndicator,
16 FireTypeIndicator, IntercomAttachedIndicator, LVCIndicator, PduStatusIFFSimulationMode,
17 RadioAttachedIndicator, TransferredEntityIndicator,
18};
19
20#[bitfield(bits = 8)]
21#[derive(Copy, Clone, Debug, PartialEq, Eq)]
22pub struct PduStatusRecord {
23 pub tei: TransferredEntityIndicator,
24 pub lvc: LVCIndicator,
25 pub cei: CoupledExtensionIndicator,
26 pub bit4_5: B2,
27 #[skip]
28 __reserved: B2,
29}
30
31impl PduStatusRecord {
32 #[must_use]
33 pub fn new_zero() -> Self {
34 Self::new()
35 }
36
37 #[must_use]
38 pub fn get_dti(&self) -> DetonationTypeIndicator {
39 DetonationTypeIndicator::from(self.bit4_5())
40 }
41
42 pub fn set_dti(&mut self, dti: DetonationTypeIndicator) {
43 self.set_bit4_5(dti.into());
44 }
45
46 #[must_use]
47 pub fn get_rai(&self) -> RadioAttachedIndicator {
48 RadioAttachedIndicator::from(self.bit4_5())
49 }
50
51 pub fn set_rai(&mut self, rai: RadioAttachedIndicator) {
52 self.set_bit4_5(rai.into());
53 }
54
55 #[must_use]
56 pub fn get_iai(&self) -> IntercomAttachedIndicator {
57 IntercomAttachedIndicator::from(self.bit4_5())
58 }
59
60 pub fn set_iai(&mut self, iai: IntercomAttachedIndicator) {
61 self.set_bit4_5(iai.into());
62 }
63
64 #[must_use]
65 pub fn get_fti(&self) -> FireTypeIndicator {
66 let bit4 = self.bit4_5() & 0b01;
67 if bit4 == 0 {
68 FireTypeIndicator::Munition
69 } else {
70 FireTypeIndicator::Expendable
71 }
72 }
73
74 pub fn set_fti(&mut self, fti: FireTypeIndicator) {
75 let mut v = self.bit4_5();
76 v = (v & 0b10) | (fti as u8 & 0x01);
77 self.set_bit4_5(v);
78 }
79
80 #[must_use]
81 pub fn get_ism(&self) -> PduStatusIFFSimulationMode {
82 let bit4 = self.bit4_5() & 0b01;
83 if bit4 == 0 {
84 PduStatusIFFSimulationMode::Regeneration
85 } else {
86 PduStatusIFFSimulationMode::Interactive
87 }
88 }
89
90 pub fn set_ism(&mut self, ism: PduStatusIFFSimulationMode) {
91 let mut v = self.bit4_5();
92 v = (v & 0b10) | (ism as u8 & 0x01);
93 self.set_bit4_5(v);
94 }
95
96 #[must_use]
97 pub fn get_aii(&self) -> ActiveInterrogationIndicator {
98 let bit5 = (self.bit4_5() >> 1) & 0b01;
99 if bit5 == 0 {
100 ActiveInterrogationIndicator::NotActive
101 } else {
102 ActiveInterrogationIndicator::Active
103 }
104 }
105
106 pub fn set_aii(&mut self, aii: ActiveInterrogationIndicator) {
107 let mut v = self.bit4_5();
108 v = (v & 0b01) | ((aii as u8) << 1);
109 self.set_bit4_5(v);
110 }
111
112 #[must_use]
113 pub fn to_u8(&self) -> u8 {
114 self.into_bytes()[0]
115 }
116
117 #[must_use]
118 pub fn from_u8(b: u8) -> Self {
119 Self::from_bytes([b])
120 }
121}
122
123impl Default for PduStatusRecord {
124 fn default() -> Self {
125 Self::new_zero()
126 }
127}
128
129#[derive(Copy, Clone, Debug, Default, PartialEq)]
130pub struct PduHeader {
131 pub protocol_version: ProtocolVersion,
133 pub exercise_id: u8,
135 pub pdu_type: PduType,
137 pub protocol_family: ProtocolFamily,
139 pub timestamp: u32,
141 pub length: u16,
143 pub status_record: PduStatusRecord,
145}
146
147impl PduHeader {
148 #[must_use]
149 pub fn new(
150 pdu_type: PduType,
151 protocol_family: ProtocolFamily,
152 exercise_id: u8,
153 length: u16,
154 ) -> Self {
155 PduHeader {
156 protocol_version: ProtocolVersion::IEEE1278_1_2012,
157 exercise_id,
158 pdu_type,
159 protocol_family,
160 timestamp: PduHeader::calculate_dis_timestamp(),
161 length,
162 status_record: PduStatusRecord::default(),
163 }
164 }
165
166 #[must_use]
167 pub fn default(pdu_type: PduType, protocol_family: ProtocolFamily, length: u16) -> Self {
168 PduHeader {
169 protocol_version: ProtocolVersion::IEEE1278_1_2012,
170 exercise_id: 1,
171 pdu_type,
172 protocol_family,
173 timestamp: PduHeader::calculate_dis_timestamp(),
174 length,
175 status_record: PduStatusRecord::default(),
176 }
177 }
178
179 #[must_use]
181 #[allow(
182 clippy::cast_precision_loss,
183 clippy::cast_possible_truncation,
184 clippy::cast_sign_loss
185 )]
186 pub fn calculate_dis_timestamp() -> u32 {
187 let minute_curr = u64::from((Utc::now().minute() * 60) * 1_000_000);
188 let second_curr = u64::from(Utc::now().second() * 1_000_000);
189 let nanosecond_curr = u64::from(Utc::now().nanosecond() / 1000);
190 let dis_time = (second_curr + minute_curr + nanosecond_curr) as f32 / 1.68;
191 dis_time as u32
192 }
193
194 pub fn serialize(&self, buf: &mut BytesMut) {
195 buf.put_u8(self.protocol_version as u8);
196 buf.put_u8(self.exercise_id);
197 buf.put_u8(self.pdu_type as u8);
198 buf.put_u8(self.protocol_family as u8);
199 buf.put_u32(self.timestamp);
200 buf.put_u16(self.length);
201 buf.put_u8(self.status_record.to_u8());
202 }
203
204 fn deserialize_protocol_version(data: u8) -> ProtocolVersion {
205 match data {
206 1 => ProtocolVersion::DIS_PDUv1,
207 2 => ProtocolVersion::IEEE1278_1993,
208 3 => ProtocolVersion::DIS_PDUv2_Third_Draft,
209 4 => ProtocolVersion::DIS_PDUv2_Fourth_Draft_Revised,
210 5 => ProtocolVersion::IEEE1278_1_1995,
211 6 => ProtocolVersion::IEEE1278_1A_1998,
212 7 => ProtocolVersion::IEEE1278_1_2012,
213 _ => ProtocolVersion::Other,
214 }
215 }
216
217 pub fn deserialize(buf: &mut BytesMut) -> PduHeader {
218 PduHeader {
219 protocol_version: PduHeader::deserialize_protocol_version(buf.get_u8()),
220 exercise_id: buf.get_u8(),
221 pdu_type: PduHeader::deserialize_pdu_type(buf.get_u8()),
222 protocol_family: PduHeader::deserialize_protocol_family(buf.get_u8()),
223 timestamp: buf.get_u32(),
224 length: buf.get_u16(),
225 status_record: PduStatusRecord::from_u8(buf.get_u8()),
226 }
227 }
228
229 #[must_use]
230 pub fn deserialize_pdu_type(data: u8) -> PduType {
231 match data {
232 1 => PduType::EntityState,
233 2 => PduType::Fire,
234 3 => PduType::Detonation,
235 4 => PduType::Collision,
236 5 => PduType::ServiceRequest,
237 6 => PduType::ResupplyOffer,
238 7 => PduType::ResupplyReceived,
239 8 => PduType::ResupplyCancel,
240 9 => PduType::RepairComplete,
241 10 => PduType::RepairResponse,
242 11 => PduType::CreateEntity,
243 12 => PduType::RemoveEntity,
244 13 => PduType::StartResume,
245 14 => PduType::StopFreeze,
246 15 => PduType::Acknowledge,
247 16 => PduType::ActionRequest,
248 17 => PduType::ActionResponse,
249 18 => PduType::DataQuery,
250 19 => PduType::SetData,
251 20 => PduType::Data,
252 21 => PduType::EventReport,
253 22 => PduType::Comment,
254 23 => PduType::ElectromagneticEmission,
255 24 => PduType::Designator,
256 25 => PduType::Transmitter,
257 26 => PduType::Signal,
258 27 => PduType::Receiver,
259 28 => PduType::IFF,
260 29 => PduType::UnderwaterAcoustic,
261 30 => PduType::SupplementalEmission,
262 31 => PduType::IntercomSignal,
263 32 => PduType::IntercomControl,
264 33 => PduType::AggregateState,
265 34 => PduType::IsGroupOf,
266 35 => PduType::TransferOwnership,
267 36 => PduType::IsPartOf,
268 37 => PduType::MinefieldState,
269 38 => PduType::MinefieldQuery,
270 39 => PduType::MinefieldData,
271 40 => PduType::MinefieldResponseNack,
272 41 => PduType::EnvironmentalProcess,
273 42 => PduType::GriddedData,
274 43 => PduType::PointObjectState,
275 44 => PduType::LinearObjectState,
276 45 => PduType::ArealObjectState,
277 46 => PduType::TSPI,
278 47 => PduType::Appearance,
279 48 => PduType::ArticulatedParts,
280 49 => PduType::LEFire,
281 50 => PduType::LEDetonation,
282 51 => PduType::CreateEntityReliable,
283 52 => PduType::RemoveEntityReliable,
284 53 => PduType::StartResumeReliable,
285 54 => PduType::StopFreezeReliable,
286 55 => PduType::AcknowledgeReliable,
287 56 => PduType::ActionRequestReliable,
288 57 => PduType::ActionResponseReliable,
289 58 => PduType::DataQueryReliable,
290 59 => PduType::SetDataReliable,
291 60 => PduType::DataReliable,
292 61 => PduType::EventReportReliable,
293 62 => PduType::CommentReliable,
294 63 => PduType::RecordReliable,
295 64 => PduType::SetRecordReliable,
296 65 => PduType::RecordQueryReliable,
297 66 => PduType::CollisionElastic,
298 67 => PduType::EntityStateUpdate,
299 68 => PduType::DirectedEnergyFire,
300 69 => PduType::EntityDamageStatus,
301 70 => PduType::InformationOperationsAction,
302 71 => PduType::InformationOperationsReport,
303 72 => PduType::Attribute,
304 _ => PduType::Other,
305 }
306 }
307
308 #[must_use]
309 fn deserialize_protocol_family(data: u8) -> ProtocolFamily {
310 match data {
311 1 => ProtocolFamily::EntityInformation,
312 2 => ProtocolFamily::Warfare,
313 3 => ProtocolFamily::Logistics,
314 4 => ProtocolFamily::RadioCommunications,
315 5 => ProtocolFamily::SimulationManagement,
316 6 => ProtocolFamily::DistributedEmissionRegeneration,
317 7 => ProtocolFamily::EntityManagement,
318 8 => ProtocolFamily::Minefield,
319 9 => ProtocolFamily::SyntheticEnvironment,
320 10 => ProtocolFamily::SimulationManagementWithReliability,
321 11 => ProtocolFamily::LiveEntityInformationInteraction,
322 12 => ProtocolFamily::NonRealTime,
323 13 => ProtocolFamily::InformationOperations,
324 _ => ProtocolFamily::Other,
325 }
326 }
327}
328
329#[derive(Copy, Clone, Debug, Default, FromPrimitive, PartialEq)]
330pub enum ProtocolFamily {
331 #[default]
332 Other = 0,
333 EntityInformation = 1,
334 Warfare = 2,
335 Logistics = 3,
336 RadioCommunications = 4,
337 SimulationManagement = 5,
338 DistributedEmissionRegeneration = 6,
339 EntityManagement = 7,
340 Minefield = 8,
341 SyntheticEnvironment = 9,
342 SimulationManagementWithReliability = 10,
343 LiveEntityInformationInteraction = 11,
344 NonRealTime = 12,
345 InformationOperations = 13,
346}
347
348#[derive(Copy, Clone, Debug, Default, FromPrimitive, PartialEq)]
349#[allow(non_camel_case_types)]
350pub enum ProtocolVersion {
351 #[default]
352 Other = 0,
353 DIS_PDUv1 = 1,
354 IEEE1278_1993 = 2,
355 DIS_PDUv2_Third_Draft = 3,
356 DIS_PDUv2_Fourth_Draft_Revised = 4,
357 IEEE1278_1_1995 = 5,
358 IEEE1278_1A_1998 = 6,
359 IEEE1278_1_2012 = 7,
360}
361
362#[derive(Copy, Clone, Debug, Default, FromPrimitive, PartialEq)]
363pub enum PduType {
364 #[default]
365 Other = 0,
366 EntityState = 1,
367 Fire = 2,
368 Detonation = 3,
369 Collision = 4,
370 ServiceRequest = 5,
371 ResupplyOffer = 6,
372 ResupplyReceived = 7,
373 ResupplyCancel = 8,
374 RepairComplete = 9,
375 RepairResponse = 10,
376 CreateEntity = 11,
377 RemoveEntity = 12,
378 StartResume = 13,
379 StopFreeze = 14,
380 Acknowledge = 15,
381 ActionRequest = 16,
382 ActionResponse = 17,
383 DataQuery = 18,
384 SetData = 19,
385 Data = 20,
386 EventReport = 21,
387 Comment = 22,
388 ElectromagneticEmission = 23,
389 Designator = 24,
390 Transmitter = 25,
391 Signal = 26,
392 Receiver = 27,
393 IFF = 28,
394 UnderwaterAcoustic = 29,
395 SupplementalEmission = 30,
396 IntercomSignal = 31,
397 IntercomControl = 32,
398 AggregateState = 33,
399 IsGroupOf = 34,
400 TransferOwnership = 35,
401 IsPartOf = 36,
402 MinefieldState = 37,
403 MinefieldQuery = 38,
404 MinefieldData = 39,
405 MinefieldResponseNack = 40,
406 EnvironmentalProcess = 41,
407 GriddedData = 42,
408 PointObjectState = 43,
409 LinearObjectState = 44,
410 ArealObjectState = 45,
411 TSPI = 46,
412 Appearance = 47,
413 ArticulatedParts = 48,
414 LEFire = 49,
415 LEDetonation = 50,
416 CreateEntityReliable = 51,
417 RemoveEntityReliable = 52,
418 StartResumeReliable = 53,
419 StopFreezeReliable = 54,
420 AcknowledgeReliable = 55,
421 ActionRequestReliable = 56,
422 ActionResponseReliable = 57,
423 DataQueryReliable = 58,
424 SetDataReliable = 59,
425 DataReliable = 60,
426 EventReportReliable = 61,
427 CommentReliable = 62,
428 RecordReliable = 63,
429 SetRecordReliable = 64,
430 RecordQueryReliable = 65,
431 CollisionElastic = 66,
432 EntityStateUpdate = 67,
433 DirectedEnergyFire = 68,
434 EntityDamageStatus = 69,
435 InformationOperationsAction = 70,
436 InformationOperationsReport = 71,
437 Attribute = 72,
438}