dis-rust 0.2.10

A rust implementation of the DIS simulation protocol.
Documentation
//     dis-rust - A rust implementation of the DIS simulation protocol.
//     Copyright (C) 2022 Thomas Mann
// 
//     This software is dual-licensed. It is available under the conditions of
//     the GNU Affero General Public License (see the LICENSE file included) 
//     or under a commercial license (email contact@coffeebreakdevs.com for
//     details).

use std::u8;

use bytes::{BytesMut, BufMut, Buf};
use chrono::{Utc, Timelike};
use num_derive::FromPrimitive;    

#[derive(Copy, Clone, Debug, PartialEq)]
/// PDU Header Record as defined in IEEE 1278.1 standard. Used to communicate PDU information during the simulation.
pub struct PDUHeaderRecord {
    pub protocol_version: ProtocolVersion,
    pub exercise_id: u8,
    pub pdu_type: PDUType,
    pub protocol_family: ProtocolFamily,
    pub timestamp: u32,
    pub length: u16,
    pub padding: u16
}

impl PDUHeaderRecord {
    /// Provides a function to create a new PDUHeaderRecord. 
    /// Timestamps are automatically generated using the current system time.
    pub fn new(pdu_type: PDUType, protocol_family: ProtocolFamily, exercise_id: u8, length: u16,) -> Self {
        PDUHeaderRecord {
            protocol_version: ProtocolVersion::DIS_PDUv2_Fourth_Draft_Revised,
            exercise_id,
            pdu_type,
            protocol_family,
            timestamp: PDUHeaderRecord::calculate_dis_timestamp() as u32,
            length: length as u16,
            padding: 0 as u16
        }
    }

    /// Provides a function to create a default PDUHeaderRecord. The header uses exercise ID 1.
    /// Timestamps are automatically generated using the current system time.
    pub fn default(pdu_type: PDUType, protocol_family: ProtocolFamily, length: u16,) -> Self {
        PDUHeaderRecord {
            protocol_version: ProtocolVersion::DIS_PDUv2_Fourth_Draft_Revised,
            exercise_id: 1,
            pdu_type,
            protocol_family,
            timestamp: PDUHeaderRecord::calculate_dis_timestamp() as u32,
            length: length as u16,
            padding: 0 as u16
        }
    }

    /// Calculates the timestamp in DIS time units (1.68 micro-seconds) within a micro-second
    pub fn calculate_dis_timestamp() -> u32 {
        let current_minute = ((Utc::now().minute() * 60) * 1e6 as u32) as u64 ;
        let current_second = (Utc::now().second() * 1e6 as u32) as u64;
        let current_nanos = (Utc::now().nanosecond() / 1000) as u64;
        let dis_units = (current_second + current_minute + current_nanos) as f32 / 1.68;
        dis_units as u32
    }

    /// Fills a BytesMut struct with a PDUHeaderRecord serialised into binary. This buffer is then ready to be sent via
    /// UDP to the simluation network.
    pub fn serialize(&self, buf: &mut BytesMut) {
        buf.put_u8(self.protocol_version as u8);
        buf.put_u8(self.exercise_id as u8);
        buf.put_u8(self.pdu_type as u8);
        buf.put_u8(self.protocol_family as u8);
        buf.put_u32(self.timestamp as u32);
        buf.put_u16(self.length as u16);
        buf.put_u16(self.padding as u16);
    }

    fn decode_protocol_version(data: u8) -> ProtocolVersion {
        match data {
            1 => ProtocolVersion::DIS_PDUv1,
            2 => ProtocolVersion::IEEE1278,
            3 => ProtocolVersion::DIS_PDUv2_Third_Draft,
            4 => ProtocolVersion::DIS_PDUv2_Fourth_Draft_Revised,
            5 => ProtocolVersion::IEEE1278_1,
            _ => ProtocolVersion::Other
        }
    }

    pub fn decode(buf: &mut BytesMut) -> PDUHeaderRecord {
        PDUHeaderRecord { 
            protocol_version: PDUHeaderRecord::decode_protocol_version(buf.get_u8()), 
            exercise_id: buf.get_u8(), 
            pdu_type: PDUHeaderRecord::decode_pdu_type(buf.get_u8()), 
            protocol_family: PDUHeaderRecord::decode_protocol_family(buf.get_u8()), 
            timestamp: buf.get_u32(), 
            length: buf.get_u16(), 
            padding: buf.get_u16()
        }
    }

    fn decode_pdu_type(data: u8) -> PDUType {
        match data {
            1 => PDUType::EntityState,
            2 => PDUType::Fire,
            3 => PDUType::Detonation,
            4 => PDUType::Collision,
            5 => PDUType::ServiceRequest,
            6 => PDUType::ResupplyOffer,
            7 => PDUType::ResupplyReceived,
            8 => PDUType::ResupplyCancel,
            9 => PDUType::RepairComplete,
            10 => PDUType::RepairRepsonse,
            11 => PDUType::CreateEntity,
            12 => PDUType::RemoveEntity,
            13 => PDUType::StartResume,
            14 => PDUType::StopFreeze,
            15 => PDUType::Acknowledge,
            16 => PDUType::ActionRequest,
            17 => PDUType::ActionReponse,
            18 => PDUType::DataQuery,
            19 => PDUType::SetData,
            20 => PDUType::Data,
            21 => PDUType::EventReport,
            22 => PDUType::Comment,
            23 => PDUType::ElectromagneticEmission,
            24 => PDUType::Designator,
            25 => PDUType::Transmitter,
            26 => PDUType::Signal,
            27 => PDUType::Recevier,
            // ...
            _ => PDUType::Other
        }
    }

    fn decode_protocol_family(data: u8) -> ProtocolFamily {
        match data {
            1 => ProtocolFamily::EntityInformation,
            2 => ProtocolFamily::Warfare,
            3 => ProtocolFamily::Logistics,
            4 => ProtocolFamily::RadioCommunications,
            5 => ProtocolFamily::SimulationManagement,
            6 => ProtocolFamily::DistributedEmissionRegeneration,
            _ => ProtocolFamily::Other
        }
    }
    
}


#[derive(Copy, Clone, Debug, FromPrimitive, PartialEq)]
/// Enum to represent the Protocol Family the PDU header is recording.
pub enum ProtocolFamily {
    Other = 0,
    EntityInformation = 1,
    Warfare = 2,
    Logistics = 3,
    RadioCommunications = 4,
    SimulationManagement = 5,
    DistributedEmissionRegeneration = 6
}

#[derive(Copy, Clone, Debug, FromPrimitive, PartialEq)]
#[allow(non_camel_case_types)]
/// Enum to represent the Protocol Version the PDU header is recording.
pub enum ProtocolVersion {
    Other = 0,
    DIS_PDUv1 = 1,
    IEEE1278 = 2,
    DIS_PDUv2_Third_Draft = 3,
    DIS_PDUv2_Fourth_Draft_Revised = 4,
    IEEE1278_1 = 5
}

#[derive(Copy, Clone, Debug, FromPrimitive, PartialEq)]
/// Enum to represent the PDU Type the PDU header is recording.
pub enum PDUType {
    Other = 0,
    EntityState = 1,
    Fire = 2,
    Detonation = 3,
    Collision = 4,
    ServiceRequest = 5,
    ResupplyOffer = 6,
    ResupplyReceived = 7,
    ResupplyCancel = 8,
    RepairComplete = 9,
    RepairRepsonse = 10,
    CreateEntity = 11,
    RemoveEntity = 12,
    StartResume = 13,
    StopFreeze = 14,
    Acknowledge = 15,
    ActionRequest = 16,
    ActionReponse = 17,
    DataQuery = 18,
    SetData = 19,
    Data = 20,
    EventReport = 21,
    Comment = 22,
    ElectromagneticEmission = 23,
    Designator = 24,
    Transmitter = 25,
    Signal = 26,
    Recevier = 27,
    // ..
}