1use crate::common::model::{
2 EntityId, EntityType, Location, Orientation, PduBody, VariableParameter, VectorF32,
3};
4use crate::common::{BodyInfo, Interaction};
5use crate::constants::{FOUR_OCTETS, TWELVE_OCTETS, VARIABLE_PARAMETER_RECORD_LENGTH};
6use crate::entity_state::builder::EntityStateBuilder;
7use crate::enumerations::{
8 AirPlatformAppearance, AppearanceEntityOrObjectState, CulturalFeatureAppearance,
9 DeadReckoningAlgorithm, EntityCapabilities, EntityKind, EntityMarkingCharacterSet,
10 EnvironmentalAppearance, ExpendableAppearance, ForceId, LandPlatformAppearance,
11 LifeFormsAppearance, MunitionAppearance, PduType, PlatformDomain, RadioAppearance,
12 SensorEmitterAppearance, SpacePlatformAppearance, SubsurfacePlatformAppearance,
13 SupplyAppearance, SurfacePlatformAppearance,
14};
15use crate::DisError;
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18use std::str::FromStr;
19
20const BASE_ENTITY_STATE_BODY_LENGTH: u16 = 132;
21
22#[allow(dead_code)]
24pub enum EntityStateValidationError {
25 SomeFieldNotOkError,
26}
27
28#[derive(Clone, Debug, Default, PartialEq)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub struct EntityState {
34 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>,
46}
47
48impl EntityState {
49 #[must_use]
50 pub fn builder() -> EntityStateBuilder {
51 EntityStateBuilder::new()
52 }
53
54 #[must_use]
55 pub fn into_builder(self) -> EntityStateBuilder {
56 EntityStateBuilder::new_from_body(self)
57 }
58
59 #[must_use]
60 pub fn into_pdu_body(self) -> PduBody {
61 PduBody::EntityState(self)
62 }
63}
64
65impl BodyInfo for EntityState {
66 fn body_length(&self) -> u16 {
67 BASE_ENTITY_STATE_BODY_LENGTH
68 + (VARIABLE_PARAMETER_RECORD_LENGTH * (self.variable_parameters.len() as u16))
69 }
70
71 fn body_type(&self) -> PduType {
72 PduType::EntityState
73 }
74}
75
76impl Interaction for EntityState {
77 fn originator(&self) -> Option<&EntityId> {
78 Some(&self.entity_id)
79 }
80
81 fn receiver(&self) -> Option<&EntityId> {
82 None
83 }
84}
85
86#[derive(Copy, Clone, Debug, PartialEq)]
88#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89pub enum EntityAppearance {
90 LandPlatform(LandPlatformAppearance),
91 AirPlatform(AirPlatformAppearance),
92 SurfacePlatform(SurfacePlatformAppearance),
93 SubsurfacePlatform(SubsurfacePlatformAppearance),
94 SpacePlatform(SpacePlatformAppearance),
95 Munition(MunitionAppearance),
96 LifeForms(LifeFormsAppearance),
97 Environmental(EnvironmentalAppearance),
98 CulturalFeature(CulturalFeatureAppearance),
99 Supply(SupplyAppearance),
100 Radio(RadioAppearance),
101 Expendable(ExpendableAppearance),
102 SensorEmitter(SensorEmitterAppearance),
103 Unspecified([u8; FOUR_OCTETS]),
104}
105
106impl Default for EntityAppearance {
107 fn default() -> Self {
108 Self::Unspecified(0u32.to_be_bytes())
109 }
110}
111
112impl EntityAppearance {
113 #[must_use]
114 #[allow(clippy::match_same_arms)]
115 pub fn from_bytes(appearance: u32, entity_type: &EntityType) -> Self {
116 match (entity_type.kind, entity_type.domain) {
117 (EntityKind::Other, _) => EntityAppearance::Unspecified(appearance.to_be_bytes()),
118 (EntityKind::Platform, PlatformDomain::Land) => {
119 EntityAppearance::LandPlatform(LandPlatformAppearance::from(appearance))
120 }
121 (EntityKind::Platform, PlatformDomain::Air) => {
122 EntityAppearance::AirPlatform(AirPlatformAppearance::from(appearance))
123 }
124 (EntityKind::Platform, PlatformDomain::Surface) => {
125 EntityAppearance::SurfacePlatform(SurfacePlatformAppearance::from(appearance))
126 }
127 (EntityKind::Platform, PlatformDomain::Subsurface) => {
128 EntityAppearance::SubsurfacePlatform(SubsurfacePlatformAppearance::from(appearance))
129 }
130 (EntityKind::Platform, PlatformDomain::Space) => {
131 EntityAppearance::SpacePlatform(SpacePlatformAppearance::from(appearance))
132 }
133 (EntityKind::Munition, _) => {
134 EntityAppearance::Munition(MunitionAppearance::from(appearance))
135 }
136 (EntityKind::LifeForm, _) => {
137 EntityAppearance::LifeForms(LifeFormsAppearance::from(appearance))
138 }
139 (EntityKind::Environmental, _) => {
140 EntityAppearance::Environmental(EnvironmentalAppearance::from(appearance))
141 }
142 (EntityKind::CulturalFeature, _) => {
143 EntityAppearance::CulturalFeature(CulturalFeatureAppearance::from(appearance))
144 }
145 (EntityKind::Supply, _) => EntityAppearance::Supply(SupplyAppearance::from(appearance)),
146 (EntityKind::Radio, _) => EntityAppearance::Radio(RadioAppearance::from(appearance)),
147 (EntityKind::Expendable, _) => {
148 EntityAppearance::Expendable(ExpendableAppearance::from(appearance))
149 }
150 (EntityKind::SensorEmitter, _) => {
151 EntityAppearance::SensorEmitter(SensorEmitterAppearance::from(appearance))
152 }
153 (_, _) => EntityAppearance::Unspecified(appearance.to_be_bytes()),
154 }
155 }
156
157 #[must_use]
158 pub const fn record_length(&self) -> u16 {
159 FOUR_OCTETS as u16
160 }
161
162 #[must_use]
163 pub fn state(&self) -> Option<AppearanceEntityOrObjectState> {
164 match self {
165 EntityAppearance::LandPlatform(appearance) => Some(appearance.state),
166 EntityAppearance::AirPlatform(appearance) => Some(appearance.state),
167 EntityAppearance::SurfacePlatform(appearance) => Some(appearance.state),
168 EntityAppearance::SubsurfacePlatform(appearance) => Some(appearance.state),
169 EntityAppearance::SpacePlatform(appearance) => Some(appearance.state),
170 EntityAppearance::Munition(appearance) => Some(appearance.state),
171 EntityAppearance::LifeForms(appearance) => Some(appearance.state),
172 EntityAppearance::Environmental(appearance) => Some(appearance.state),
173 EntityAppearance::CulturalFeature(appearance) => Some(appearance.state),
174 EntityAppearance::Supply(appearance) => Some(appearance.state),
175 EntityAppearance::Radio(appearance) => Some(appearance.state),
176 EntityAppearance::Expendable(appearance) => Some(appearance.state),
177 EntityAppearance::SensorEmitter(appearance) => Some(appearance.state),
178 EntityAppearance::Unspecified(_) => None,
179 }
180 }
181
182 #[must_use]
183 pub fn is_frozen(&self) -> Option<bool> {
184 match self {
185 EntityAppearance::LandPlatform(appearance) => Some(appearance.is_frozen),
186 EntityAppearance::AirPlatform(appearance) => Some(appearance.is_frozen),
187 EntityAppearance::SurfacePlatform(appearance) => Some(appearance.is_frozen),
188 EntityAppearance::SubsurfacePlatform(appearance) => Some(appearance.is_frozen),
189 EntityAppearance::SpacePlatform(appearance) => Some(appearance.is_frozen),
190 EntityAppearance::Munition(appearance) => Some(appearance.is_frozen),
191 EntityAppearance::LifeForms(appearance) => Some(appearance.is_frozen),
192 EntityAppearance::Environmental(appearance) => Some(appearance.is_frozen),
193 EntityAppearance::CulturalFeature(appearance) => Some(appearance.is_frozen),
194 EntityAppearance::Supply(appearance) => Some(appearance.is_frozen),
195 EntityAppearance::Radio(appearance) => Some(appearance.is_frozen),
196 EntityAppearance::Expendable(appearance) => Some(appearance.is_frozen),
197 EntityAppearance::SensorEmitter(appearance) => Some(appearance.is_frozen),
198 EntityAppearance::Unspecified(_) => None,
199 }
200 }
201}
202
203impl From<&EntityAppearance> for u32 {
204 #[allow(clippy::match_same_arms)]
205 fn from(value: &EntityAppearance) -> Self {
206 match value {
207 EntityAppearance::LandPlatform(appearance) => u32::from(*appearance),
208 EntityAppearance::AirPlatform(appearance) => u32::from(*appearance),
209 EntityAppearance::SurfacePlatform(appearance) => u32::from(*appearance),
210 EntityAppearance::SubsurfacePlatform(appearance) => u32::from(*appearance),
211 EntityAppearance::SpacePlatform(appearance) => u32::from(*appearance),
212 EntityAppearance::Munition(appearance) => u32::from(*appearance),
213 EntityAppearance::LifeForms(appearance) => u32::from(*appearance),
214 EntityAppearance::Environmental(appearance) => u32::from(*appearance),
215 EntityAppearance::CulturalFeature(appearance) => u32::from(*appearance),
216 EntityAppearance::Supply(appearance) => u32::from(*appearance),
217 EntityAppearance::Radio(appearance) => u32::from(*appearance),
218 EntityAppearance::Expendable(appearance) => u32::from(*appearance),
219 EntityAppearance::SensorEmitter(appearance) => u32::from(*appearance),
220 EntityAppearance::Unspecified(appearance) => u32::from_be_bytes(*appearance),
221 }
222 }
223}
224
225#[derive(Clone, Debug, PartialEq)]
227#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
228pub struct EntityMarking {
229 pub marking_character_set: EntityMarkingCharacterSet,
230 pub marking_string: String, }
232
233impl EntityMarking {
234 pub fn new(marking: impl Into<String>, character_set: EntityMarkingCharacterSet) -> Self {
235 Self {
236 marking_character_set: character_set,
237 marking_string: marking.into(),
238 }
239 }
240
241 pub fn new_ascii<S: Into<String>>(marking: S) -> Self {
242 EntityMarking::new(marking.into(), EntityMarkingCharacterSet::ASCII)
243 }
244
245 #[allow(clippy::return_self_not_must_use)]
246 pub fn with_marking<S: Into<String>>(mut self, marking: S) -> Self {
247 self.marking_string = marking.into();
248 self
249 }
250
251 #[must_use]
252 pub fn record_length(&self) -> u16 {
253 TWELVE_OCTETS as u16
254 }
255}
256
257impl Default for EntityMarking {
258 fn default() -> Self {
259 Self {
260 marking_character_set: EntityMarkingCharacterSet::ASCII,
261 marking_string: String::from("Marking"),
262 }
263 }
264}
265
266impl FromStr for EntityMarking {
267 type Err = DisError;
268
269 fn from_str(s: &str) -> Result<Self, Self::Err> {
270 if s.len() <= 11 {
271 Ok(Self {
272 marking_character_set: EntityMarkingCharacterSet::ASCII,
273 marking_string: s.to_string(),
274 })
275 } else {
276 Err(DisError::ParseError(format!(
277 "String is too long for EntityMarking. Found {}, max 11 allowed.",
278 s.len()
279 )))
280 }
281 }
282}
283
284#[derive(Clone, Default, Debug, PartialEq)]
286#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
287pub struct DrParameters {
288 pub algorithm: DeadReckoningAlgorithm,
289 pub other_parameters: DrOtherParameters,
290 pub linear_acceleration: VectorF32,
291 pub angular_velocity: VectorF32,
292}
293
294impl DrParameters {
295 #[must_use]
296 pub fn with_algorithm(mut self, algorithm: DeadReckoningAlgorithm) -> Self {
297 self.algorithm = algorithm;
298 self
299 }
300
301 #[must_use]
302 pub fn with_parameters(mut self, parameters: DrOtherParameters) -> Self {
303 self.other_parameters = parameters;
304 self
305 }
306
307 #[must_use]
308 pub fn with_linear_acceleration(mut self, linear_acceleration: VectorF32) -> Self {
309 self.linear_acceleration = linear_acceleration;
310 self
311 }
312
313 #[must_use]
314 pub fn with_angular_velocity(mut self, angular_velocity: VectorF32) -> Self {
315 self.angular_velocity = angular_velocity;
316 self
317 }
318}
319
320#[derive(Clone, Debug, PartialEq)]
322#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
323#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
324pub enum DrOtherParameters {
325 None([u8; 15]),
326 LocalEulerAngles(DrEulerAngles),
327 WorldOrientationQuaternion(DrWorldOrientationQuaternion),
328}
329
330impl Default for DrOtherParameters {
331 fn default() -> Self {
332 Self::None([0u8; 15])
333 }
334}
335
336#[derive(Clone, Default, Debug, PartialEq)]
338#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
339pub struct DrEulerAngles {
340 pub local_yaw: f32,
341 pub local_pitch: f32,
342 pub local_roll: f32,
343}
344
345impl DrEulerAngles {
346 #[must_use]
347 pub fn with_local_yaw(mut self, local_yaw: f32) -> Self {
348 self.local_yaw = local_yaw;
349 self
350 }
351
352 #[must_use]
353 pub fn with_local_pitch(mut self, local_pitch: f32) -> Self {
354 self.local_pitch = local_pitch;
355 self
356 }
357
358 #[must_use]
359 pub fn with_local_roll(mut self, local_roll: f32) -> Self {
360 self.local_roll = local_roll;
361 self
362 }
363}
364
365#[derive(Clone, Default, Debug, PartialEq)]
367#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
368pub struct DrWorldOrientationQuaternion {
369 pub nil: u16,
370 pub x: f32,
371 pub y: f32,
372 pub z: f32,
373}
374
375impl DrWorldOrientationQuaternion {
376 #[must_use]
377 pub fn with_nil(mut self, nil: u16) -> Self {
378 self.nil = nil;
379 self
380 }
381
382 #[must_use]
383 pub fn with_x(mut self, x: f32) -> Self {
384 self.x = x;
385 self
386 }
387
388 #[must_use]
389 pub fn with_y(mut self, y: f32) -> Self {
390 self.y = y;
391 self
392 }
393
394 #[must_use]
395 pub fn with_z(mut self, z: f32) -> Self {
396 self.z = z;
397 self
398 }
399}