use std::any::Any;
use bytes::{BytesMut, BufMut, Buf};
use crate::common::{entity_coordinate_vector_record::EntityCoordinateVectorRecord, entity_id_record::EntityIDRecord, event_id_record::EventIDRecord, pdu::PDU, pdu_header_record::{PDUHeaderRecord, PDUType, ProtocolFamily}, velocity_vector_record::VelocityVectorRecord, world_coordinate_record::WorldCoordinateRecord, dis_error::DISError};
use super::burst_descriptor_record::BurstDescriptorRecord;
#[derive(Copy, Clone, Debug,)]
pub struct DetonationPDU {
pub pdu_header_record: PDUHeaderRecord,
pub firing_entity_id_record: EntityIDRecord,
pub target_entity_id_record: EntityIDRecord,
pub munition_id_record: EntityIDRecord,
pub event_id_record: EventIDRecord,
pub velocity_record: VelocityVectorRecord,
pub location_in_world_record: WorldCoordinateRecord,
pub burst_descriptor_record: BurstDescriptorRecord,
pub location_in_entity_coordinates_record: EntityCoordinateVectorRecord,
pub detonation_result_field: DetonationResult,
pub number_of_articulation_parameters_field: u8,
pub padding: u16,
pub articulation_parameter_record: f32
}
impl DetonationPDU {
pub fn default() -> Self {
DetonationPDU {
pdu_header_record: PDUHeaderRecord::default(PDUType::Detonation, ProtocolFamily::Warfare, 768/8), firing_entity_id_record: EntityIDRecord::default(1),
target_entity_id_record: EntityIDRecord::default(2),
munition_id_record: EntityIDRecord::default(3),
event_id_record: EventIDRecord::default(1),
location_in_world_record: WorldCoordinateRecord::new(0.0, 0.0, 0.0),
burst_descriptor_record: BurstDescriptorRecord::default(),
velocity_record: VelocityVectorRecord::new(570.0, 0.0, 0.0),
location_in_entity_coordinates_record: EntityCoordinateVectorRecord::new(0.0, 0.0, 0.0),
detonation_result_field: DetonationResult::AirBurst,
number_of_articulation_parameters_field:0,
padding: 0,
articulation_parameter_record: 0.0
}
}
}
impl PDU for DetonationPDU {
fn serialise(&self, buf: &mut BytesMut) {
self.pdu_header_record.serialize(buf);
self.firing_entity_id_record.serialize(buf);
self.target_entity_id_record.serialize(buf);
self.munition_id_record.serialize(buf);
self.event_id_record.serialize(buf);
self.velocity_record.serialize(buf);
self.location_in_world_record.serialize(buf);
self.burst_descriptor_record.serialize(buf);
self.location_in_entity_coordinates_record.serialize(buf);
buf.put_u8(self.detonation_result_field as u8);
buf.put_u8(self.number_of_articulation_parameters_field);
buf.put_u16(self.padding);
}
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::Detonation {
let firing_entity_id = EntityIDRecord::decode(&mut buffer);
let target_entity_id = EntityIDRecord::decode(&mut buffer);
let munition_entity_id = EntityIDRecord::decode(&mut buffer);
let event_id = EventIDRecord::decode(&mut buffer);
let velocity = VelocityVectorRecord::decode(&mut buffer);
let location = WorldCoordinateRecord::decode(&mut buffer);
let burst_descriptor = BurstDescriptorRecord::decode(&mut buffer);
let location_in_entity_coordinates = EntityCoordinateVectorRecord::decode(&mut buffer);
let detonation_result = DetonationResult::from_u8(buffer.get_u8());
let num_articulation_params = buffer.get_u8();
let padding = buffer.get_u16();
return Ok(DetonationPDU {
pdu_header_record: pdu_header,
firing_entity_id_record: firing_entity_id,
target_entity_id_record: target_entity_id,
munition_id_record: munition_entity_id,
event_id_record: event_id,
velocity_record: velocity,
location_in_world_record: location,
burst_descriptor_record: burst_descriptor,
location_in_entity_coordinates_record: location_in_entity_coordinates,
detonation_result_field: detonation_result,
number_of_articulation_parameters_field: num_articulation_params,
padding: padding,
articulation_parameter_record: 0.0,
})
} 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 firing_entity_id = EntityIDRecord::decode(&mut buffer);
let target_entity_id = EntityIDRecord::decode(&mut buffer);
let munition_entity_id = EntityIDRecord::decode(&mut buffer);
let event_id = EventIDRecord::decode(&mut buffer);
let velocity = VelocityVectorRecord::decode(&mut buffer);
let location = WorldCoordinateRecord::decode(&mut buffer);
let burst_descriptor = BurstDescriptorRecord::decode(&mut buffer);
let location_in_entity_coordinates = EntityCoordinateVectorRecord::decode(&mut buffer);
let detonation_result = DetonationResult::from_u8(buffer.get_u8());
let num_articulation_params = buffer.get_u8();
let padding = buffer.get_u16();
return Ok(DetonationPDU {
pdu_header_record: pdu_header,
firing_entity_id_record: firing_entity_id,
target_entity_id_record: target_entity_id,
munition_id_record: munition_entity_id,
event_id_record: event_id,
velocity_record: velocity,
location_in_world_record: location,
burst_descriptor_record: burst_descriptor,
location_in_entity_coordinates_record: location_in_entity_coordinates,
detonation_result_field: detonation_result,
number_of_articulation_parameters_field: num_articulation_params,
padding: padding,
articulation_parameter_record: 0.0,
})
}
}
#[derive(Debug, Clone, Copy)]
pub enum DetonationResult {
Other = 0,
EntityImpact = 1,
ArmourPiercingHit = 10,
SmallDirtBlast = 11,
MediumDirtBlast = 12,
LargeDirtBlast = 13,
SmallWaterBlast = 14,
MediumWaterBlast = 15,
LargeWaterBlast = 16,
AirHit = 17,
SmallBuildingHit = 18,
MediumBuildingHit = 19,
EntityProximateDetonation = 2,
LargeBuildingHit = 20,
MineClearingLineCharge = 21,
EnvironmentalObjectImpact = 22,
EnvironmentalObjectProximateDetonation = 23,
WaterImpact = 24,
AirBurst = 25,
GroundImpact = 3,
GroundImpactDetonation = 4,
Detonation = 5,
None = 6,
SmallHighExplosiveHit = 7,
MediumHighExplosiveHit = 8,
LargeHighExplosiveHit = 9
}
impl DetonationResult {
pub fn from_u8(value: u8) -> DetonationResult {
match value {
0 => DetonationResult::Other,
1 => DetonationResult::EntityImpact,
2 => DetonationResult::EntityProximateDetonation,
3 => DetonationResult::GroundImpact,
4 => DetonationResult::GroundImpactDetonation,
5 => DetonationResult::Detonation,
6 => DetonationResult::None,
7 => DetonationResult::SmallHighExplosiveHit,
8 => DetonationResult::MediumHighExplosiveHit,
9 => DetonationResult::LargeHighExplosiveHit,
10 => DetonationResult::ArmourPiercingHit,
11 => DetonationResult::SmallDirtBlast,
12 => DetonationResult::MediumDirtBlast,
13 => DetonationResult::LargeDirtBlast,
14 => DetonationResult::SmallWaterBlast,
15 => DetonationResult::MediumWaterBlast,
16 => DetonationResult::LargeWaterBlast,
17 => DetonationResult::AirHit,
18 => DetonationResult::SmallBuildingHit,
19 => DetonationResult::MediumBuildingHit,
20 => DetonationResult::LargeBuildingHit,
21 => DetonationResult::MineClearingLineCharge,
22 => DetonationResult::EnvironmentalObjectImpact,
23 => DetonationResult::EntityProximateDetonation,
24 => DetonationResult::WaterImpact,
25 => DetonationResult::AirBurst,
_ => DetonationResult::Other
}
}
}
#[cfg(test)]
mod tests {
use crate::{common::pdu_header_record::{PDUHeaderRecord, PDUType, ProtocolFamily}, warfare::detonation_pdu::DetonationPDU};
#[test]
fn header_creation() {
let detonation_pdu = DetonationPDU::default();
let header = PDUHeaderRecord::default(PDUType::Detonation, ProtocolFamily::Warfare, 768/8);
assert_eq!(header.protocol_version, detonation_pdu.pdu_header_record.protocol_version);
assert_eq!(header.exercise_id, detonation_pdu.pdu_header_record.exercise_id);
assert_eq!(header.pdu_type, detonation_pdu.pdu_header_record.pdu_type);
assert_eq!(header.protocol_family, detonation_pdu.pdu_header_record.protocol_family);
assert_eq!(header.timestamp, detonation_pdu.pdu_header_record.timestamp);
assert_eq!(header.length, detonation_pdu.pdu_header_record.length);
assert_eq!(header.padding, detonation_pdu .pdu_header_record.padding);
}
}