open_dis_rust/common/
pdu_header.rs

1//     open-dis-rust - Rust implementation of the IEEE 1278.1-2012 Distributed Interactive
2//                     Simulation (DIS) application protocol
3//     Copyright (C) 2023 Cameron Howell
4//
5//     Licensed under the BSD 2-Clause License
6
7#![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    /// The version of the protocol
132    pub protocol_version: ProtocolVersion,
133    /// Exercise ID
134    pub exercise_id: u8,
135    /// Type of PDU, unique for each PDU class
136    pub pdu_type: PduType,
137    /// Value that refers to the protocol family
138    pub protocol_family: ProtocolFamily,
139    /// Timestamp value
140    pub timestamp: u32,
141    /// Length, in bytes, of the PDU
142    pub length: u16,
143    /// PDU status record
144    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    /// Gets the current time in terms of IEEE-1278.1 DIS time units
180    #[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}