1use crate::common::entity_state::model::{
2 DrEulerAngles, DrOtherParameters, DrWorldOrientationQuaternion, EntityAppearance,
3};
4use crate::common::entity_state::model::{DrParameters, EntityMarking, EntityState};
5use crate::common::model::EntityType;
6use crate::common::{Serialize, SerializePdu, SupportedVersion};
7use crate::enumerations::{DrParametersType, ForceId};
8use crate::v6::entity_state::model::EntityCapabilities;
9use bytes::{BufMut, BytesMut};
10
11impl SerializePdu for EntityState {
12 fn serialize_pdu(&self, version: SupportedVersion, buf: &mut BytesMut) -> u16 {
13 let entity_id_bytes = self.entity_id.serialize(buf);
14 let force_id_bytes = self.force_id.serialize(buf);
15 buf.put_u8(self.variable_parameters.len() as u8);
16
17 let entity_type_bytes = self.entity_type.serialize(buf);
18 let alt_entity_type_bytes = self.alternative_entity_type.serialize(buf);
19
20 let linear_velocity_bytes = self.entity_linear_velocity.serialize(buf);
21 let location_bytes = self.entity_location.serialize(buf);
22 let orientation_bytes = self.entity_orientation.serialize(buf);
23
24 let appearance_bytes = self.entity_appearance.serialize(buf);
25 let dr_params_bytes = self.dead_reckoning_parameters.serialize(buf);
26
27 let marking_bytes = self.entity_marking.serialize(buf);
28 let capabilities_bytes = match version {
29 SupportedVersion::V6 => {
30 let capabilities: EntityCapabilities = self.entity_capabilities.into();
31 capabilities.serialize(buf)
32 }
33 SupportedVersion::V7 => {
34 buf.put_u32(self.entity_capabilities.into());
35 4
36 }
37 SupportedVersion::Unsupported => {
38 buf.put_u32(0u32);
39 4
40 }
41 };
42
43 let variable_params_bytes: u16 = self
44 .variable_parameters
45 .iter()
46 .map(|param| param.serialize(buf))
47 .sum();
48
49 entity_id_bytes
50 + force_id_bytes
51 + 1
52 + entity_type_bytes
53 + alt_entity_type_bytes
54 + linear_velocity_bytes
55 + location_bytes
56 + orientation_bytes
57 + appearance_bytes
58 + dr_params_bytes
59 + capabilities_bytes
60 + marking_bytes
61 + variable_params_bytes
62 }
63}
64
65impl Serialize for EntityAppearance {
66 fn serialize(&self, buf: &mut BytesMut) -> u16 {
67 let appearance: u32 = u32::from(self);
68 buf.put_u32(appearance);
69 4
70 }
71}
72
73impl Serialize for DrParameters {
74 fn serialize(&self, buf: &mut BytesMut) -> u16 {
75 buf.put_u8(self.algorithm.into());
76 let other_parameters_bytes = self.other_parameters.serialize(buf);
77 let lin_acc_bytes = self.linear_acceleration.serialize(buf);
78 let ang_vel_bytes = self.angular_velocity.serialize(buf);
79 1 + other_parameters_bytes + lin_acc_bytes + ang_vel_bytes
80 }
81}
82
83impl Serialize for DrOtherParameters {
84 fn serialize(&self, buf: &mut BytesMut) -> u16 {
85 match self {
86 DrOtherParameters::None(bytes) => {
87 for x in bytes {
88 buf.put_u8(*x);
89 }
90 15
91 }
92 DrOtherParameters::LocalEulerAngles(params) => params.serialize(buf),
93 DrOtherParameters::WorldOrientationQuaternion(params) => params.serialize(buf),
94 }
95 }
96}
97
98impl Serialize for DrEulerAngles {
99 fn serialize(&self, buf: &mut BytesMut) -> u16 {
100 buf.put_u8(DrParametersType::LocalEulerAngles_Yaw_Pitch_Roll_.into());
101 buf.put_u16(0u16);
102 buf.put_f32(self.local_yaw);
103 buf.put_f32(self.local_pitch);
104 buf.put_f32(self.local_roll);
105 15
106 }
107}
108
109impl Serialize for DrWorldOrientationQuaternion {
110 fn serialize(&self, buf: &mut BytesMut) -> u16 {
111 buf.put_u8(DrParametersType::WorldOrientationQuaternion.into());
112 buf.put_u16(self.nil);
113 buf.put_f32(self.x);
114 buf.put_f32(self.y);
115 buf.put_f32(self.z);
116 15
117 }
118}
119
120impl Serialize for ForceId {
121 fn serialize(&self, buf: &mut BytesMut) -> u16 {
122 let force_id = *self;
123 buf.put_u8(force_id.into());
124 1
125 }
126}
127
128impl Serialize for EntityType {
129 fn serialize(&self, buf: &mut BytesMut) -> u16 {
130 buf.put_u8(self.kind.into());
131 buf.put_u8(self.domain.into());
132 buf.put_u16(self.country.into());
133 buf.put_u8(self.category);
134 buf.put_u8(self.subcategory);
135 buf.put_u8(self.specific);
136 buf.put_u8(self.extra);
137 8
138 }
139}
140
141impl Serialize for EntityMarking {
142 fn serialize(&self, buf: &mut BytesMut) -> u16 {
143 buf.put_u8(self.marking_character_set.into());
144 let num_pad = 11 - self.marking_string.len();
145 let marking = self.marking_string.clone(); buf.put_slice(&marking.into_bytes()[..]);
148 (0..num_pad).for_each(|_i| buf.put_u8(0x20));
149 12
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use crate::common::entity_state::model::{
156 DrOtherParameters, DrParameters, EntityAppearance, EntityMarking, EntityState,
157 };
158 use crate::common::model::{
159 ArticulatedPart, EntityId, EntityType, Location, Orientation, Pdu, PduHeader,
160 VariableParameter, VectorF32,
161 };
162 use crate::common::Serialize;
163 use crate::enumerations::{
164 AirPlatformAppearance, AppearanceAntiCollisionDayNight, AppearanceCanopy, AppearanceDamage,
165 AppearanceEntityOrObjectState, AppearanceNVGMode, AppearanceNavigationPositionBrightness,
166 AppearancePaintScheme, AppearanceTrailingEffects,
167 };
168 use crate::enumerations::{
169 ArticulatedPartsTypeClass, ArticulatedPartsTypeMetric, ChangeIndicator, Country,
170 DeadReckoningAlgorithm, EntityKind, EntityMarkingCharacterSet, ForceId, PduType,
171 PlatformDomain,
172 };
173 use bytes::BytesMut;
174
175 #[test]
176 fn entity_marking() {
177 let marking = EntityMarking {
178 marking_character_set: EntityMarkingCharacterSet::ASCII,
179 marking_string: "EYE 10".to_string(),
180 };
181 let mut buf = BytesMut::with_capacity(11);
182
183 marking.serialize(&mut buf);
184
185 let expected: [u8; 12] = [
186 0x01, 0x45, 0x59, 0x45, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20,
187 ];
188 assert_eq!(buf.as_ref(), expected.as_ref());
189 }
190
191 #[test]
192 fn articulated_part() {
193 let articulated_part = VariableParameter::Articulated(ArticulatedPart {
194 change_indicator: ChangeIndicator::from(0u8),
195 attachment_id: 0,
196 type_class: ArticulatedPartsTypeClass::LandingGear,
197 type_metric: ArticulatedPartsTypeMetric::Position,
198 parameter_value: 1.0,
199 });
200
201 let mut buf = BytesMut::with_capacity(11);
202
203 articulated_part.serialize(&mut buf);
204
205 let expected: [u8; 16] = [
206 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00,
207 0x00, 0x00,
208 ];
209 assert_eq!(buf.as_ref(), expected.as_ref());
210 }
211
212 #[test]
213 #[allow(clippy::too_many_lines)]
214 fn entity_state_pdu() {
215 let header = PduHeader::new_v6(1, PduType::EntityState);
216
217 let body = EntityState::builder()
218 .with_entity_id(EntityId::new(500, 900, 14))
219 .with_force_id(ForceId::Friendly)
220 .with_entity_type(EntityType {
221 kind: EntityKind::Platform, domain: PlatformDomain::Air, country: Country::Netherlands_NLD_, category: 50, subcategory: 4, specific: 4, extra: 0
222 })
223 .with_alternative_entity_type(EntityType {
224 kind: EntityKind::Platform, domain: PlatformDomain::Air, country: Country::Netherlands_NLD_, category: 50, subcategory: 4, specific: 4, extra: 0
225 })
226 .with_velocity(VectorF32 {
227 first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
228 })
229 .with_location(Location {
230 x_coordinate: 0f64, y_coordinate : 0f64, z_coordinate: 0f64
231 })
232 .with_orientation(Orientation {
233 psi: 0f32, theta: 0f32, phi: 0f32
234 })
235 .with_appearance(EntityAppearance::AirPlatform(AirPlatformAppearance {
236 paint_scheme: AppearancePaintScheme::UniformColor,
237 propulsion_killed: false,
238 nvg_mode: AppearanceNVGMode::default(),
239 damage: AppearanceDamage::default(),
240 is_smoke_emanating: true,
241 is_engine_emitting_smoke: false,
242 trailing_effects: AppearanceTrailingEffects::None,
243 canopy_troop_door: AppearanceCanopy::NotApplicable,
244 landing_lights_on: false,
245 navigation_lights_on: false,
246 anticollision_lights_on: false,
247 is_flaming: false,
248 afterburner_on: false,
249 lower_anticollision_light_on: false,
250 upper_anticollision_light_on: false,
251 anticollision_light_day_night: AppearanceAntiCollisionDayNight::Day,
252 is_blinking: false,
253 is_frozen: false,
254 power_plant_on: false,
255 state: AppearanceEntityOrObjectState::Active,
256 formation_lights_on: false,
257 landing_gear_extended: false,
258 cargo_doors_opened: false,
259 navigation_position_brightness: AppearanceNavigationPositionBrightness::Dim,
260 spot_search_light_1_on: false,
261 interior_lights_on: false,
262 reverse_thrust_engaged: false,
263 weightonwheels: false,
264 }))
265 .with_dead_reckoning_parameters(DrParameters {
266 algorithm: DeadReckoningAlgorithm::DRM_RVW_HighSpeedOrManeuveringEntityWithExtrapolationOfOrientation,
267 other_parameters: DrOtherParameters::None([0u8;15]),
268 linear_acceleration: VectorF32 {
269 first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
270 },
271 angular_velocity: VectorF32 {
272 first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
273 }
274 })
275 .with_marking(EntityMarking {
276 marking_character_set: EntityMarkingCharacterSet::ASCII,
277 marking_string: "EYE 10".to_string()
278 })
279 .with_capabilities_flags(false, false, false, false)
280
281 .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
282 change_indicator: ChangeIndicator::from(0u8),
283 attachment_id: 0,
284 type_class: ArticulatedPartsTypeClass::LandingGear,
285 type_metric: ArticulatedPartsTypeMetric::Position,
286 parameter_value: 1.0
287 }))
288 .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
289 change_indicator: ChangeIndicator::from(0u8),
290 attachment_id: 0,
291 type_class: ArticulatedPartsTypeClass::PrimaryTurretNumber1,
292 type_metric: ArticulatedPartsTypeMetric::Azimuth,
293 parameter_value: 2.0
294 }))
295 .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
296 change_indicator: ChangeIndicator::from(0u8),
297 attachment_id: 0,
298 type_class: ArticulatedPartsTypeClass::PrimaryTurretNumber1,
299 type_metric: ArticulatedPartsTypeMetric::AzimuthRate,
300 parameter_value: 3.0
301 }))
302 .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
303 change_indicator: ChangeIndicator::from(0u8),
304 attachment_id: 0,
305 type_class: ArticulatedPartsTypeClass::PrimaryGunNumber1,
306 type_metric: ArticulatedPartsTypeMetric::Elevation,
307 parameter_value: 4.0
308 }))
309 .build()
310 .into_pdu_body();
311 let pdu = Pdu::finalize_from_parts(header, body, 0);
312
313 let mut buf = BytesMut::with_capacity(208);
314
315 pdu.serialize(&mut buf).unwrap();
316
317 let expected: [u8; 208] = [
318 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x01, 0xf4,
319 0x03, 0x84, 0x00, 0x0e, 0x01, 0x04, 0x01, 0x02, 0x00, 0x99, 0x32, 0x04, 0x04, 0x00,
320 0x01, 0x02, 0x00, 0x99, 0x32, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
322 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
325 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x00, 0x01, 0x45, 0x59, 0x45, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20,
328 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x3f, 0x80,
329 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0b,
330 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331 0x10, 0x0c, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332 0x00, 0x00, 0x11, 0x4d, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
333 ];
334
335 assert_eq!(buf.as_ref(), expected.as_ref());
336 }
337}