use crate::common::model::{
EntityId, EntityType, Location, Orientation, PduBody, VariableParameter, VectorF32,
};
use crate::common::{BodyInfo, Interaction};
use crate::constants::{FOUR_OCTETS, TWELVE_OCTETS, VARIABLE_PARAMETER_RECORD_LENGTH};
use crate::entity_state::builder::EntityStateBuilder;
use crate::enumerations::{
AirPlatformAppearance, AppearanceEntityOrObjectState, CulturalFeatureAppearance,
DeadReckoningAlgorithm, EntityCapabilities, EntityKind, EntityMarkingCharacterSet,
EnvironmentalAppearance, ExpendableAppearance, ForceId, LandPlatformAppearance,
LifeFormsAppearance, MunitionAppearance, PduType, PlatformDomain, RadioAppearance,
SensorEmitterAppearance, SpacePlatformAppearance, SubsurfacePlatformAppearance,
SupplyAppearance, SurfacePlatformAppearance,
};
use crate::{BodyRaw, DisError};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::str::FromStr;
const BASE_ENTITY_STATE_BODY_LENGTH: u16 = 132;
#[allow(dead_code)]
pub enum EntityStateValidationError {
SomeFieldNotOkError,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EntityState {
pub entity_id: EntityId, pub force_id: ForceId, pub entity_type: EntityType, pub alternative_entity_type: EntityType, pub entity_linear_velocity: VectorF32, pub entity_location: Location, pub entity_orientation: Orientation, pub entity_appearance: EntityAppearance, pub dead_reckoning_parameters: DrParameters, pub entity_marking: EntityMarking, pub entity_capabilities: EntityCapabilities, pub variable_parameters: Vec<VariableParameter>,
}
impl BodyRaw for EntityState {
type Builder = EntityStateBuilder;
fn builder() -> Self::Builder {
Self::Builder::new()
}
fn into_builder(self) -> Self::Builder {
Self::Builder::new_from_body(self)
}
fn into_pdu_body(self) -> PduBody {
PduBody::EntityState(self)
}
}
impl BodyInfo for EntityState {
fn body_length(&self) -> u16 {
BASE_ENTITY_STATE_BODY_LENGTH
+ (VARIABLE_PARAMETER_RECORD_LENGTH * (self.variable_parameters.len() as u16))
}
fn body_type(&self) -> PduType {
PduType::EntityState
}
}
impl Interaction for EntityState {
fn originator(&self) -> Option<&EntityId> {
Some(&self.entity_id)
}
fn receiver(&self) -> Option<&EntityId> {
None
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum EntityAppearance {
LandPlatform(LandPlatformAppearance),
AirPlatform(AirPlatformAppearance),
SurfacePlatform(SurfacePlatformAppearance),
SubsurfacePlatform(SubsurfacePlatformAppearance),
SpacePlatform(SpacePlatformAppearance),
Munition(MunitionAppearance),
LifeForms(LifeFormsAppearance),
Environmental(EnvironmentalAppearance),
CulturalFeature(CulturalFeatureAppearance),
Supply(SupplyAppearance),
Radio(RadioAppearance),
Expendable(ExpendableAppearance),
SensorEmitter(SensorEmitterAppearance),
Unspecified([u8; FOUR_OCTETS]),
}
impl Default for EntityAppearance {
fn default() -> Self {
Self::Unspecified(0u32.to_be_bytes())
}
}
impl EntityAppearance {
#[must_use]
#[allow(clippy::match_same_arms)]
pub fn from_bytes(appearance: u32, entity_type: &EntityType) -> Self {
match (entity_type.kind, entity_type.domain) {
(EntityKind::Other, _) => EntityAppearance::Unspecified(appearance.to_be_bytes()),
(EntityKind::Platform, PlatformDomain::Land) => {
EntityAppearance::LandPlatform(LandPlatformAppearance::from(appearance))
}
(EntityKind::Platform, PlatformDomain::Air) => {
EntityAppearance::AirPlatform(AirPlatformAppearance::from(appearance))
}
(EntityKind::Platform, PlatformDomain::Surface) => {
EntityAppearance::SurfacePlatform(SurfacePlatformAppearance::from(appearance))
}
(EntityKind::Platform, PlatformDomain::Subsurface) => {
EntityAppearance::SubsurfacePlatform(SubsurfacePlatformAppearance::from(appearance))
}
(EntityKind::Platform, PlatformDomain::Space) => {
EntityAppearance::SpacePlatform(SpacePlatformAppearance::from(appearance))
}
(EntityKind::Munition, _) => {
EntityAppearance::Munition(MunitionAppearance::from(appearance))
}
(EntityKind::LifeForm, _) => {
EntityAppearance::LifeForms(LifeFormsAppearance::from(appearance))
}
(EntityKind::Environmental, _) => {
EntityAppearance::Environmental(EnvironmentalAppearance::from(appearance))
}
(EntityKind::CulturalFeature, _) => {
EntityAppearance::CulturalFeature(CulturalFeatureAppearance::from(appearance))
}
(EntityKind::Supply, _) => EntityAppearance::Supply(SupplyAppearance::from(appearance)),
(EntityKind::Radio, _) => EntityAppearance::Radio(RadioAppearance::from(appearance)),
(EntityKind::Expendable, _) => {
EntityAppearance::Expendable(ExpendableAppearance::from(appearance))
}
(EntityKind::SensorEmitter, _) => {
EntityAppearance::SensorEmitter(SensorEmitterAppearance::from(appearance))
}
(_, _) => EntityAppearance::Unspecified(appearance.to_be_bytes()),
}
}
#[must_use]
pub const fn record_length(&self) -> u16 {
FOUR_OCTETS as u16
}
#[must_use]
pub fn state(&self) -> Option<AppearanceEntityOrObjectState> {
match self {
EntityAppearance::LandPlatform(appearance) => Some(appearance.state),
EntityAppearance::AirPlatform(appearance) => Some(appearance.state),
EntityAppearance::SurfacePlatform(appearance) => Some(appearance.state),
EntityAppearance::SubsurfacePlatform(appearance) => Some(appearance.state),
EntityAppearance::SpacePlatform(appearance) => Some(appearance.state),
EntityAppearance::Munition(appearance) => Some(appearance.state),
EntityAppearance::LifeForms(appearance) => Some(appearance.state),
EntityAppearance::Environmental(appearance) => Some(appearance.state),
EntityAppearance::CulturalFeature(appearance) => Some(appearance.state),
EntityAppearance::Supply(appearance) => Some(appearance.state),
EntityAppearance::Radio(appearance) => Some(appearance.state),
EntityAppearance::Expendable(appearance) => Some(appearance.state),
EntityAppearance::SensorEmitter(appearance) => Some(appearance.state),
EntityAppearance::Unspecified(_) => None,
}
}
#[must_use]
pub fn is_frozen(&self) -> Option<bool> {
match self {
EntityAppearance::LandPlatform(appearance) => Some(appearance.is_frozen),
EntityAppearance::AirPlatform(appearance) => Some(appearance.is_frozen),
EntityAppearance::SurfacePlatform(appearance) => Some(appearance.is_frozen),
EntityAppearance::SubsurfacePlatform(appearance) => Some(appearance.is_frozen),
EntityAppearance::SpacePlatform(appearance) => Some(appearance.is_frozen),
EntityAppearance::Munition(appearance) => Some(appearance.is_frozen),
EntityAppearance::LifeForms(appearance) => Some(appearance.is_frozen),
EntityAppearance::Environmental(appearance) => Some(appearance.is_frozen),
EntityAppearance::CulturalFeature(appearance) => Some(appearance.is_frozen),
EntityAppearance::Supply(appearance) => Some(appearance.is_frozen),
EntityAppearance::Radio(appearance) => Some(appearance.is_frozen),
EntityAppearance::Expendable(appearance) => Some(appearance.is_frozen),
EntityAppearance::SensorEmitter(appearance) => Some(appearance.is_frozen),
EntityAppearance::Unspecified(_) => None,
}
}
}
impl From<&EntityAppearance> for u32 {
#[allow(clippy::match_same_arms)]
fn from(value: &EntityAppearance) -> Self {
match value {
EntityAppearance::LandPlatform(appearance) => u32::from(*appearance),
EntityAppearance::AirPlatform(appearance) => u32::from(*appearance),
EntityAppearance::SurfacePlatform(appearance) => u32::from(*appearance),
EntityAppearance::SubsurfacePlatform(appearance) => u32::from(*appearance),
EntityAppearance::SpacePlatform(appearance) => u32::from(*appearance),
EntityAppearance::Munition(appearance) => u32::from(*appearance),
EntityAppearance::LifeForms(appearance) => u32::from(*appearance),
EntityAppearance::Environmental(appearance) => u32::from(*appearance),
EntityAppearance::CulturalFeature(appearance) => u32::from(*appearance),
EntityAppearance::Supply(appearance) => u32::from(*appearance),
EntityAppearance::Radio(appearance) => u32::from(*appearance),
EntityAppearance::Expendable(appearance) => u32::from(*appearance),
EntityAppearance::SensorEmitter(appearance) => u32::from(*appearance),
EntityAppearance::Unspecified(appearance) => u32::from_be_bytes(*appearance),
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EntityMarking {
pub marking_character_set: EntityMarkingCharacterSet,
pub marking_string: String, }
impl EntityMarking {
pub fn new(marking: impl Into<String>, character_set: EntityMarkingCharacterSet) -> Self {
Self {
marking_character_set: character_set,
marking_string: marking.into(),
}
}
pub fn new_ascii<S: Into<String>>(marking: S) -> Self {
EntityMarking::new(marking.into(), EntityMarkingCharacterSet::ASCII)
}
#[allow(clippy::return_self_not_must_use)]
pub fn with_marking<S: Into<String>>(mut self, marking: S) -> Self {
self.marking_string = marking.into();
self
}
#[must_use]
pub fn record_length(&self) -> u16 {
TWELVE_OCTETS as u16
}
}
impl Default for EntityMarking {
fn default() -> Self {
Self {
marking_character_set: EntityMarkingCharacterSet::ASCII,
marking_string: String::from("Marking"),
}
}
}
impl FromStr for EntityMarking {
type Err = DisError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() <= 11 {
Ok(Self {
marking_character_set: EntityMarkingCharacterSet::ASCII,
marking_string: s.to_string(),
})
} else {
Err(DisError::ParseError(format!(
"String is too long for EntityMarking. Found {}, max 11 allowed.",
s.len()
)))
}
}
}
#[derive(Clone, Default, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DrParameters {
pub algorithm: DeadReckoningAlgorithm,
pub other_parameters: DrOtherParameters,
pub linear_acceleration: VectorF32,
pub angular_velocity: VectorF32,
}
impl DrParameters {
#[must_use]
pub fn with_algorithm(mut self, algorithm: DeadReckoningAlgorithm) -> Self {
self.algorithm = algorithm;
self
}
#[must_use]
pub fn with_parameters(mut self, parameters: DrOtherParameters) -> Self {
self.other_parameters = parameters;
self
}
#[must_use]
pub fn with_linear_acceleration(mut self, linear_acceleration: VectorF32) -> Self {
self.linear_acceleration = linear_acceleration;
self
}
#[must_use]
pub fn with_angular_velocity(mut self, angular_velocity: VectorF32) -> Self {
self.angular_velocity = angular_velocity;
self
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum DrOtherParameters {
None([u8; 15]),
LocalEulerAngles(DrEulerAngles),
WorldOrientationQuaternion(DrWorldOrientationQuaternion),
}
impl Default for DrOtherParameters {
fn default() -> Self {
Self::None([0u8; 15])
}
}
#[derive(Clone, Default, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DrEulerAngles {
pub local_yaw: f32,
pub local_pitch: f32,
pub local_roll: f32,
}
impl DrEulerAngles {
#[must_use]
pub fn with_local_yaw(mut self, local_yaw: f32) -> Self {
self.local_yaw = local_yaw;
self
}
#[must_use]
pub fn with_local_pitch(mut self, local_pitch: f32) -> Self {
self.local_pitch = local_pitch;
self
}
#[must_use]
pub fn with_local_roll(mut self, local_roll: f32) -> Self {
self.local_roll = local_roll;
self
}
}
#[derive(Clone, Default, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DrWorldOrientationQuaternion {
pub nil: u16,
pub x: f32,
pub y: f32,
pub z: f32,
}
impl DrWorldOrientationQuaternion {
#[must_use]
pub fn with_nil(mut self, nil: u16) -> Self {
self.nil = nil;
self
}
#[must_use]
pub fn with_x(mut self, x: f32) -> Self {
self.x = x;
self
}
#[must_use]
pub fn with_y(mut self, y: f32) -> Self {
self.y = y;
self
}
#[must_use]
pub fn with_z(mut self, z: f32) -> Self {
self.z = z;
self
}
}