dis-rs 0.13.0

An implementation of the Distributed Interactive Simulation protocol (IEEE-1278.1) in Rust. This main crate contains PDU implementations and facilities to read/write PDUs from Rust data structures to the wire format and vice versa. It supports versions 6 and 7 of the protocol.
Documentation
use crate::common::electromagnetic_emission::model::{
    Beam, ElectromagneticEmission, EmitterSystem, FundamentalParameterData, JammingTechnique,
    TrackJam,
};
use crate::common::model::{PduBody, PduHeader};
use crate::common::parser;
use crate::common::parser::{entity_id, event_id, vec3_f32};
use crate::enumerations::{
    BeamStatusBeamState, ElectromagneticEmissionBeamFunction,
    ElectromagneticEmissionStateUpdateIndicator, EmitterName, EmitterSystemFunction,
    HighDensityTrackJam,
};
use crate::BodyRaw;
use nom::multi::count;
use nom::number::complete::{be_f32, be_u16, be_u8};
use nom::IResult;
use nom::Parser;

pub(crate) fn emission_body(_header: &PduHeader) -> impl Fn(&[u8]) -> IResult<&[u8], PduBody> + '_ {
    move |input| {
        let (input, emitting_entity_id) = entity_id(input)?;
        let (input, event_id) = event_id(input)?;
        let (input, status_update_indicator) = be_u8(input)?;
        let (input, no_of_systems) = be_u8(input)?;
        let (input, _pad_16) = be_u16(input)?;

        let (input, mut emitter_systems) =
            count(emitter_system, no_of_systems as usize).parse(input)?;

        let body = ElectromagneticEmission::builder()
            .with_emitting_entity_id(emitting_entity_id)
            .with_event_id(event_id)
            .with_state_update_indicator(ElectromagneticEmissionStateUpdateIndicator::from(
                status_update_indicator,
            ))
            .with_emitter_systems(&mut emitter_systems)
            .build();

        Ok((input, body.into_pdu_body()))
    }
}

pub(crate) fn emitter_system(input: &[u8]) -> IResult<&[u8], EmitterSystem> {
    let (input, _system_data_length) = be_u8(input)?;
    let (input, no_of_beams) = be_u8(input)?;
    let (input, _pad_16) = be_u16(input)?;
    let (input, name) = be_u16(input)?;
    let (input, function) = be_u8(input)?;
    let (input, number) = be_u8(input)?;
    let (input, location) = vec3_f32(input)?;

    let (input, mut beams) = count(beam, no_of_beams as usize).parse(input)?;

    let system = EmitterSystem::new()
        .with_name(EmitterName::from(name))
        .with_function(EmitterSystemFunction::from(function))
        .with_number(number)
        .with_location(location)
        .with_beams(&mut beams);

    Ok((input, system))
}

pub(crate) fn beam(input: &[u8]) -> IResult<&[u8], Beam> {
    let (input, _data_length) = be_u8(input)?;
    let (input, number) = be_u8(input)?;
    let (input, parameter_index) = be_u16(input)?;
    let (input, fundamental_parameter_data) = fundamental_parameter_data(input)?;
    let (input, beam_data) = parser::beam_data(input)?;
    let (input, function) = be_u8(input)?;
    let (input, no_of_targets) = be_u8(input)?;
    let (input, high_density_track_jam) = be_u8(input)?;
    let (input, status) = be_u8(input)?;
    let (input, jamming_technique) = jamming_technique(input)?;
    let (input, mut track_jams) = count(track_jam, no_of_targets as usize).parse(input)?;

    let beam = Beam::new()
        .with_number(number)
        .with_parameter_index(parameter_index)
        .with_parameter_data(fundamental_parameter_data)
        .with_beam_data(beam_data)
        .with_beam_function(ElectromagneticEmissionBeamFunction::from(function))
        .with_high_density_track_jam(HighDensityTrackJam::from(high_density_track_jam))
        .with_beam_status(BeamStatusBeamState::from(status))
        .with_jamming_technique(jamming_technique)
        .with_track_jams(&mut track_jams);

    Ok((input, beam))
}

pub(crate) fn fundamental_parameter_data(input: &[u8]) -> IResult<&[u8], FundamentalParameterData> {
    let (input, frequency) = be_f32(input)?;
    let (input, frequency_range) = be_f32(input)?;
    let (input, effective_power) = be_f32(input)?;
    let (input, pulse_repetition_frequency) = be_f32(input)?;
    let (input, pulse_width) = be_f32(input)?;

    let data = FundamentalParameterData::new()
        .with_frequency(frequency)
        .with_frequency_range(frequency_range)
        .with_effective_power(effective_power)
        .with_pulse_repetition_frequency(pulse_repetition_frequency)
        .with_pulse_width(pulse_width);

    Ok((input, data))
}

pub(crate) fn jamming_technique(input: &[u8]) -> IResult<&[u8], JammingTechnique> {
    let (input, kind) = be_u8(input)?;
    let (input, category) = be_u8(input)?;
    let (input, subcategory) = be_u8(input)?;
    let (input, specific) = be_u8(input)?;

    let technique = JammingTechnique::new()
        .with_kind(kind)
        .with_category(category)
        .with_subcategory(subcategory)
        .with_specific(specific);

    Ok((input, technique))
}

pub(crate) fn track_jam(input: &[u8]) -> IResult<&[u8], TrackJam> {
    let (input, entity_id) = entity_id(input)?;
    let (input, emitter_number) = be_u8(input)?;
    let (input, beam_number) = be_u8(input)?;

    let track = TrackJam::new()
        .with_entity_id(entity_id)
        .with_emitter(emitter_number)
        .with_beam(beam_number);

    Ok((input, track))
}

#[cfg(test)]
mod tests {
    use crate::common::model::{EntityId, PduBody};
    use crate::common::parser::parse_pdu;
    use crate::enumerations::*;

    #[test]
    fn parse_pdu_emission_basic() {
        let bytes: [u8; 48] = [
            0x06, 0x01, 0x17, 0x06, 0xaa, 0x58, 0xbe, 0xc1, 0x00, 0x30, 0x00, 0x00, 0x01, 0xf4,
            0x2b, 0x67, 0x00, 0x27, 0x01, 0xf4, 0x2b, 0x67, 0x01, 0xd2, 0x00, 0x01, 0x00, 0x00,
            0x05, 0x00, 0x00, 0x00, 0x4d, 0x58, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x41, 0x70, 0x00, 0x00,
        ];

        let pdu = parse_pdu(&bytes);
        assert!(pdu.is_ok());
        let pdu = pdu.unwrap();
        assert_eq!(pdu.header.pdu_type, PduType::ElectromagneticEmission);
        assert_eq!(pdu.header.pdu_length, 48u16);
        if let PduBody::ElectromagneticEmission(pdu) = pdu.body {
            assert_eq!(pdu.emitting_entity_id.simulation_address.site_id, 500u16);
            assert_eq!(
                pdu.emitting_entity_id.simulation_address.application_id,
                11111u16
            );
            assert_eq!(pdu.emitting_entity_id.entity_id, 39u16);
            assert_eq!(pdu.emitter_systems.len(), 1);
            assert_eq!(
                pdu.emitter_systems.first().unwrap().name,
                EmitterName::Unnamed_19800
            );
            assert_eq!(
                pdu.emitter_systems.first().unwrap().function,
                EmitterSystemFunction::EarlyWarningSurveillance_2
            );
            assert_eq!(pdu.emitter_systems.first().unwrap().number, 1u8);
            assert_eq!(
                pdu.emitter_systems
                    .first()
                    .unwrap()
                    .location
                    .first_vector_component,
                0f32
            );
            assert_eq!(
                pdu.emitter_systems
                    .first()
                    .unwrap()
                    .location
                    .second_vector_component,
                0f32
            );
            assert_eq!(
                pdu.emitter_systems
                    .first()
                    .unwrap()
                    .location
                    .third_vector_component,
                15f32
            );
        } else {
            panic!();
        }
    }

    #[test]
    fn parse_pdu_emission_with_tracks() {
        let bytes: [u8; 228] = [
            0x06, 0x01, 0x17, 0x06, 0xb4, 0x97, 0xa2, 0xe3, 0x00, 0xe4, 0x00, 0x00, 0x01, 0xf4,
            0x2b, 0x67, 0x00, 0x3e, 0x01, 0xf4, 0x2b, 0x67, 0x02, 0x41, 0x00, 0x01, 0x00, 0x00,
            0x32, 0x03, 0x00, 0x00, 0x7d, 0xaf, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x0f, 0x02, 0x00, 0x7a, 0x4f, 0xb2, 0xd0, 0x5e,
            0x00, 0x00, 0x00, 0x00, 0x42, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x3e, 0x05, 0xd5, 0xff, 0x3c, 0x0e, 0xfa, 0x35, 0x3f, 0x30, 0x95, 0x12,
            0x3c, 0x0e, 0xfa, 0x35, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x01, 0xf4, 0x2b, 0x67, 0x00, 0x47, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x64,
            0x4f, 0xb2, 0xd0, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x42, 0xf0, 0x00, 0x00, 0x45, 0x9c,
            0x40, 0x00, 0x42, 0x48, 0x00, 0x00, 0x3e, 0x05, 0xd6, 0x14, 0x3c, 0x0e, 0xfa, 0x35,
            0x3f, 0x30, 0x95, 0x54, 0x3c, 0x0e, 0xfa, 0x35, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x2b, 0x67, 0x00, 0x47, 0x00, 0x00,
            0x0f, 0x03, 0x00, 0xd4, 0x4f, 0xb2, 0xd0, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x42, 0xf0,
            0x00, 0x00, 0x43, 0xbb, 0x8c, 0x01, 0x40, 0xa0, 0x00, 0x00, 0x3e, 0x05, 0xd6, 0x14,
            0x3c, 0x0e, 0xfa, 0x35, 0x3f, 0x30, 0x95, 0x54, 0x3c, 0x0e, 0xfa, 0x35, 0x00, 0x00,
            0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf4, 0x2b, 0x67,
            0x00, 0x47, 0x00, 0x00,
        ];

        let pdu = parse_pdu(&bytes);
        assert!(pdu.is_ok());
        let pdu = pdu.unwrap();
        assert_eq!(pdu.header.pdu_type, PduType::ElectromagneticEmission);
        assert_eq!(pdu.header.pdu_length, 228u16);
        if let PduBody::ElectromagneticEmission(pdu) = pdu.body {
            assert_eq!(pdu.emitting_entity_id.simulation_address.site_id, 500u16);
            assert_eq!(
                pdu.emitting_entity_id.simulation_address.application_id,
                11111u16
            );
            assert_eq!(pdu.emitting_entity_id.entity_id, 62u16);
            assert_eq!(pdu.emitter_systems.len(), 1);

            let emitter = pdu.emitter_systems.first().unwrap();
            assert_eq!(emitter.name, EmitterName::Unnamed_32175);
            assert_eq!(emitter.function, EmitterSystemFunction::Multifunction_1);
            assert_eq!(emitter.number, 1u8);
            assert_eq!(emitter.beams.len(), 3usize);
            assert_eq!(emitter.location.first_vector_component, 0f32);
            assert_eq!(emitter.location.second_vector_component, 0f32);
            assert_eq!(emitter.location.third_vector_component, 5f32);

            let beam = emitter.beams.first().unwrap();
            assert_eq!(beam.number, 2u8);
            assert_eq!(beam.parameter_index, 122u16);
            assert_eq!(
                beam.beam_function,
                ElectromagneticEmissionBeamFunction::Illumination
            );
            assert_eq!(
                beam.high_density_track_jam,
                HighDensityTrackJam::NotSelected
            );
            assert_eq!(beam.track_jam_data.len(), 1usize);

            let track = beam.track_jam_data.first().unwrap();
            assert_eq!(track.beam, 0u8);
            assert_eq!(track.emitter, 0u8);
            assert_eq!(track.entity_id, EntityId::new(500u16, 11111u16, 71u16));
        } else {
            panic!();
        }
    }
}