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::any::Any;

use bytes::{BytesMut, BufMut, Buf};

use crate::common::{pdu_header_record::{PDUHeaderRecord, PDUType, ProtocolFamily}, entity_id_record::EntityIDRecord, pdu::PDU, dis_error::DISError};

#[derive(Copy, Clone, Debug,)]
/// Event Report PDU as defined in IEEE 1278.1 standard. Used to communicate an important event during the simulation.
pub struct EventReportPDU {
    pub pdu_header_record: PDUHeaderRecord,
    pub originating_entity_id_record: EntityIDRecord,
    pub receviving_entity_id_record: EntityIDRecord,
    pub event_type_field: EventType,
    pub padding: u32,
    pub number_of_fixed_datum_records_field: u32,
    pub number_of_variable_datum_records_field: u32,
    pub fixed_datum_records_field: u64,
    pub variable_datum_records_field: u64,
}

impl EventReportPDU {
    /// Provides a EventReportPDU signalling the creation of a new entity.
    /// 
    /// # Examples
    /// 
    /// Creating a blank EventReportPDU:
    /// 
    /// ```
    /// let event_report_pdu = EventReportPDU::default();
    /// ```
    /// 
    pub fn default() -> Self {
        EventReportPDU {
            pdu_header_record: PDUHeaderRecord::default(PDUType::EventReport, ProtocolFamily::SimulationManagement, 56),
            originating_entity_id_record: EntityIDRecord::default(1),
            receviving_entity_id_record: EntityIDRecord::default(2),
            event_type_field: EventType::EntityInitialization,
            padding: 0,
            number_of_fixed_datum_records_field: 0,
            number_of_variable_datum_records_field: 0,
            fixed_datum_records_field: 0,
            variable_datum_records_field: 0,
        }
    }
}

impl PDU for EventReportPDU {
    /// Fills a BytesMut struct with a EventReportPDU serialised into binary. This buffer is then ready to be sent via
    /// UDP to the simluation network.
    fn serialise(&self, buf: &mut BytesMut) {
        self.pdu_header_record.serialize(buf);
        self.originating_entity_id_record.serialize(buf);
        self.receviving_entity_id_record.serialize(buf);
        buf.put_u32(self.event_type_field as u32);
        buf.put_u32(self.padding);
        buf.put_u32(self.number_of_fixed_datum_records_field);
        buf.put_u32(self.number_of_variable_datum_records_field);
        buf.put_u64(self.fixed_datum_records_field);
        buf.put_u64(self.variable_datum_records_field);
    }

    fn deserialise(mut buffer: BytesMut) -> Result<Self, crate::common::dis_error::DISError> where Self: Sized {
        let pdu_header = PDUHeaderRecord::decode(&mut buffer);
        if pdu_header.pdu_type == PDUType::EventReport {
            let originating_entity_id = EntityIDRecord::decode(&mut buffer);
            let receviving_entity_id = EntityIDRecord::decode(&mut buffer);
            let event_type = EventType::from_u32(buffer.get_u32());
            let padding = buffer.get_u32();
            let num_of_fixed_records = buffer.get_u32();
            let num_of_variable_records = buffer.get_u32();
            let mut fixed_records: u64 = 0;
            for _record in 0..num_of_fixed_records as usize {
                fixed_records += buffer.get_u64();
            }
            let mut variable_records: u64 = 0;
            for _record in 0..num_of_variable_records as usize {
                variable_records += buffer.get_u64();
            }            
            
            return Ok(EventReportPDU {
                pdu_header_record: pdu_header,
                originating_entity_id_record: originating_entity_id,
                receviving_entity_id_record: receviving_entity_id,
                event_type_field: event_type,
                padding,
                number_of_fixed_datum_records_field: num_of_fixed_records,
                number_of_variable_datum_records_field: num_of_variable_records,
                fixed_datum_records_field: fixed_records,
                variable_datum_records_field: variable_records,
            })
        } else {
            Err(DISError::InvalidDISHeader)
        }
    }

    fn as_any(&self) -> &dyn Any {
        self
      }

    fn deserialise_without_header(mut buffer: BytesMut, pdu_header: PDUHeaderRecord) -> Result<Self, DISError> where Self: Sized {
        let originating_entity_id = EntityIDRecord::decode(&mut buffer);
            let receviving_entity_id = EntityIDRecord::decode(&mut buffer);
            let event_type = EventType::from_u32(buffer.get_u32());
            let padding = buffer.get_u32();
            let num_of_fixed_records = buffer.get_u32();
            let num_of_variable_records = buffer.get_u32();
            let mut fixed_records: u64 = 0;
            for _record in 0..num_of_fixed_records as usize {
                fixed_records += buffer.get_u64();
            }
            let mut variable_records: u64 = 0;
            for _record in 0..num_of_variable_records as usize {
                variable_records += buffer.get_u64();
            }            
            
            return Ok(EventReportPDU {
                pdu_header_record: pdu_header,
                originating_entity_id_record: originating_entity_id,
                receviving_entity_id_record: receviving_entity_id,
                event_type_field: event_type,
                padding,
                number_of_fixed_datum_records_field: num_of_fixed_records,
                number_of_variable_datum_records_field: num_of_variable_records,
                fixed_datum_records_field: fixed_records,
                variable_datum_records_field: variable_records,
            })
    }
}

#[derive(Debug, Clone, Copy)]
/// Enum to represent the type of event being reported.
pub enum EventType {
    Other = 0,
    Unused = 1,
    IndirectOrCASFire = 10,
    MinefieldEntry = 11,
    MinefieldDetonantion = 12,
    VehicleMasterPowerOn = 13,
    VehicleMasterPowerOff = 14,
    AggregateStateChangeRequest = 15,
    RanOutOfAmmunition = 2,
    KilledInAction = 3,
    Damage = 4,
    MobilityDisabled = 5,
    FireDisabled = 6,
    RanOutOfFuel = 7,
    EntityInitialization = 8,
    RequestForIndirectOrCASFire = 9,
}

impl EventType {
    pub fn from_u32(data: u32) -> EventType {
        match data {
            0 => EventType::Other,
            1 => EventType::Unused,
            10 => EventType::IndirectOrCASFire,
            11 => EventType::MinefieldEntry,
            12 => EventType::MinefieldDetonantion,
            13 => EventType::VehicleMasterPowerOn,
            14 => EventType::VehicleMasterPowerOff,
            15 => EventType::AggregateStateChangeRequest,
            2 => EventType::RanOutOfAmmunition,
            3 => EventType::KilledInAction,
            4 => EventType::Damage,
            5 => EventType::MobilityDisabled,
            6 => EventType::FireDisabled,
            7 => EventType::RanOutOfFuel,
            8 => EventType::EntityInitialization,
            9  => EventType::RequestForIndirectOrCASFire,
            _ => EventType::Other
            
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{common::pdu_header_record::{PDUHeaderRecord, PDUType, ProtocolFamily}, simulation_management::event_report_pdu::EventReportPDU, };

    #[test]
    fn header_creation() {
        let event_report_pdu = EventReportPDU::default();
        let header = PDUHeaderRecord::default(PDUType::EventReport, ProtocolFamily::SimulationManagement, 448/8);
        assert_eq!(header.protocol_version, event_report_pdu.pdu_header_record.protocol_version);
        assert_eq!(header.exercise_id, event_report_pdu.pdu_header_record.exercise_id);
        assert_eq!(header.pdu_type, event_report_pdu.pdu_header_record.pdu_type);
        assert_eq!(header.protocol_family, event_report_pdu.pdu_header_record.protocol_family);
        assert_eq!(header.timestamp, event_report_pdu.pdu_header_record.timestamp);
        assert_eq!(header.length, event_report_pdu.pdu_header_record.length);
        assert_eq!(header.padding, event_report_pdu .pdu_header_record.padding);
    }
}