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