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 bytes::{BytesMut, BufMut, Buf};
use num_derive::FromPrimitive;    

use crate::common::entity_type_record::{Country, EntityTypeRecord, Kind};

#[derive(Copy, Clone, Debug, PartialEq)]
/// Burst Descriptor Record as defined in IEEE 1278.1 standard. Used to communicate the firing of a round or burst of ammunition during the simulation.
pub struct BurstDescriptorRecord {
    pub munition_record: EntityTypeRecord,
    pub warhead_field: Warhead,
    pub fuse_field: Fuse,
    pub quantity_field: u16,
    pub rate_field: u16
}

impl BurstDescriptorRecord {
    /// Provides a new Burst Descriptor Record. Enforces the Entity Type Record's kind field
    /// to be King::Munition
    /// 
    /// # Examples
    /// 
    /// Creating a BurstDescriptorRecord for a single HE round with a contact fuse:
    /// 
    /// ```
    /// let burst_descriptor_record = BurstDescriptorRecord::new(
    ///    munition_record: EntityTypeRecord::default(),
    ///    warhead: Warhead::HighExplosive,
    ///    fuse: Fuse::Contact,
    ///    quantity: 1,
    ///    rate: 1
    /// );
    /// ```
    /// 
    pub fn new(munition_record: EntityTypeRecord,
        warhead: Warhead,
        fuse: Fuse,
        quantity: u16,
        rate: u16) -> Self {
        if munition_record.kind_field != Kind::Munition {
            println!("Warning - munition kind must be munition!"); // TODO: Make log
        }
        BurstDescriptorRecord {
            munition_record,
            warhead_field: warhead,
            fuse_field: fuse,
            quantity_field: quantity,
            rate_field: rate
        }
    }

    /// Provides a default Burst Descriptor Record for a single, generic kinetic round.
    /// 
    /// # Examples
    /// 
    /// Creating a default BurstDescriptorRecord:
    /// 
    /// ```
    /// let burst_descriptor_record = BurstDescriptorRecord::deafault();
    /// ```
    /// 
    pub fn default() -> Self{
        BurstDescriptorRecord {
            munition_record: EntityTypeRecord{
                kind_field: Kind::Munition,
                domain_field: 1,
                country_field: Country::Other,
                category_field: 2,
                subcategory_field: 0,
                specific_field: 0,
                extra_field: 0},
            warhead_field: Warhead::Kinetic,
            fuse_field: Fuse::Other,
            quantity_field: 1,
            rate_field: 1
        }
    }

    /// Fills a BytesMut struct with a Burst Descriptor Record serialised into binary. This buffer is then ready to be sent via
    /// UDP to the simluation network.
    pub fn serialize(&self, buf: &mut BytesMut) {
        self.munition_record.serialize(buf);
        buf.put_u16(self.warhead_field as u16);
        buf.put_u16(self.fuse_field as u16);
        buf.put_u16(self.quantity_field);
        buf.put_u16(self.rate_field);
    }

    pub fn decode(buf: &mut BytesMut) -> BurstDescriptorRecord {
        BurstDescriptorRecord {
            munition_record: EntityTypeRecord::decode(buf),
            warhead_field: Warhead::from_u16(buf.get_u16()),
            fuse_field: Fuse::from_u16(buf.get_u16()),
            quantity_field: buf.get_u16(),
            rate_field: buf.get_u16(),
        }
    }
}

#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq, PartialOrd)]
/// Enum to represent the type of warhead that the munition is carrying.
pub enum Warhead {
    Other = 0000,
    Cargo = 0010,
    FuelAirExplosive = 0020,
    GlassBlads = 0030,
    OneUm = 0031,
    FiveUm = 0032,
    TenUm = 0033,
    HighExplosive = 1000,
    PlasticHighExplosive = 1001,
    IncendiaryHighExplosive = 1002,
    FragmentationHighExplosive = 1003,
    AntitankHighExplosive = 1004,
    BombletsHighExplosive = 1005,
    // ...
    Smoke = 2000,
    Illumination = 3000,
    Practice = 4000,
    Kinetic = 5000,
    Mines = 6000,
    Nuclear = 7000,
    // ...
    Chemical = 8000,
    // ...
    Biological = 9000
    // ...
}

impl Warhead {
    pub fn from_u16(value: u16) -> Warhead {
        match value {
            0 => Warhead::Other,
            10 => Warhead::Cargo,
            20 => Warhead::FuelAirExplosive,
            30 => Warhead::GlassBlads,
            31 => Warhead::OneUm,
            32 => Warhead::FiveUm,
            33 => Warhead::TenUm,
            1000 => Warhead::HighExplosive,
            1001 => Warhead::PlasticHighExplosive,
            1002 => Warhead::IncendiaryHighExplosive,
            1003 => Warhead::FragmentationHighExplosive,
            1004 => Warhead::AntitankHighExplosive,
            1005 => Warhead::BombletsHighExplosive,
            2000 => Warhead::Smoke,
            3000 => Warhead::Illumination,
            4000 => Warhead::Practice,
            5000 => Warhead::Kinetic,
            6000 => Warhead::Mines,
            7000 => Warhead::Nuclear,
            8000 => Warhead::Chemical,
            9000 => Warhead::Biological,
            _ => Warhead::Other
        }
    }
}

#[derive(FromPrimitive, Debug, Copy, Clone, PartialEq)]
/// Enum to represent the type of fuse that the munition is carrying.
pub enum Fuse {
    Other = 0000,
    IntelligentInfluence = 0010,
    Sensor = 0020,
    SelfDestruct = 0030,
    UltraQuick = 0040,
    Body = 0050,
    DeepIntrustion = 0060,
    Multifunction = 0100,
    PointDetonantion = 0200,
    BaseDetonantion = 0300,
    Contact = 1000,
    // ...
    Timed = 2000,
    // ...
    Proximity = 3000,
    // ...
    Command = 4000,
    CommandElectronicRemotelySet = 4100,
    Altitude = 5000,
    AltitudeRadioAltimeter = 5100,
    AltitudeAirBurst = 5200,
    Depth = 6000,
    Acoustic = 7000,
    Pressure = 8000,
    PressureDelay = 8010,
    Inert = 8100,
    Dummy = 8110,
    Practice = 8120,
    PlugRepresenting = 8130,
    Training = 8150,
    Pyrotechnic = 9000,
    PyrotechnicDelay = 9010,
    Electromechanical = 9100
    // ...
}

impl Fuse {
    pub fn from_u16(value: u16) -> Fuse {
        match value {
            0 => Fuse::Other,
            10 => Fuse::IntelligentInfluence,
            20 => Fuse::Sensor,
            30 => Fuse::SelfDestruct,
            40 => Fuse::UltraQuick,
            50 => Fuse::Body,
            60 => Fuse::DeepIntrustion,
            100 => Fuse::Multifunction,
            200 => Fuse::PointDetonantion,
            300 => Fuse::BaseDetonantion,
            1000 => Fuse::Contact,
            2000 => Fuse::Timed,
            3000 => Fuse::Proximity,
            4000 => Fuse::Command,
            4100 => Fuse::CommandElectronicRemotelySet,
            5000 => Fuse::Altitude,
            5100 => Fuse::AltitudeRadioAltimeter,
            5200 => Fuse::AltitudeAirBurst,
            6000 => Fuse::Depth,
            7000 => Fuse::Acoustic,
            8000 => Fuse::Pressure,
            8010 => Fuse::PressureDelay,
            8100 => Fuse::Inert,
            8110 => Fuse::Dummy,
            8120 => Fuse::Practice,
            8130 => Fuse::PlugRepresenting,
            8150 => Fuse::Training,
            9000 => Fuse::Pyrotechnic,
            9010 => Fuse::PyrotechnicDelay,
            9100 => Fuse::Electromechanical,
            _ => Fuse::Other
        }
    }
}