Skip to main content

dis_rs/common/entity_state/
writer.rs

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(); // clone necessary because into_bytes consumes self.
146
147        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::BodyRaw;
156    use crate::common::Serialize;
157    use crate::common::entity_state::model::{
158        DrOtherParameters, DrParameters, EntityAppearance, EntityMarking, EntityState,
159    };
160    use crate::common::model::{
161        ArticulatedPart, EntityId, EntityType, Location, Orientation, Pdu, PduHeader, TimeUnits,
162        Timestamp, VariableParameter, VectorF32,
163    };
164    use crate::enumerations::{
165        AirPlatformAppearance, AppearanceAntiCollisionDayNight, AppearanceCanopy, AppearanceDamage,
166        AppearanceEntityOrObjectState, AppearanceNVGMode, AppearanceNavigationPositionBrightness,
167        AppearancePaintScheme, AppearanceTrailingEffects,
168    };
169    use crate::enumerations::{
170        ArticulatedPartsTypeClass, ArticulatedPartsTypeMetric, Country, DeadReckoningAlgorithm,
171        EntityKind, EntityMarkingCharacterSet, ForceId, PduType, PlatformDomain,
172    };
173    use alloc::string::ToString;
174    use bytes::BytesMut;
175
176    #[test]
177    fn entity_marking() {
178        let marking = EntityMarking {
179            marking_character_set: EntityMarkingCharacterSet::ASCII,
180            marking_string: "EYE 10".to_string(),
181        };
182        let mut buf = BytesMut::with_capacity(11);
183
184        marking.serialize(&mut buf);
185
186        let expected: [u8; 12] = [
187            0x01, 0x45, 0x59, 0x45, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20,
188        ];
189        assert_eq!(buf.as_ref(), expected.as_ref());
190    }
191
192    #[test]
193    fn articulated_part() {
194        let articulated_part = VariableParameter::Articulated(ArticulatedPart {
195            change_indicator: 0,
196            attachment_id: 0,
197            type_class: ArticulatedPartsTypeClass::LandingGear,
198            type_metric: ArticulatedPartsTypeMetric::Position,
199            parameter_value: 1.0,
200        });
201
202        let mut buf = BytesMut::with_capacity(11);
203
204        articulated_part.serialize(&mut buf);
205
206        let expected: [u8; 16] = [
207            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00,
208            0x00, 0x00,
209        ];
210        assert_eq!(buf.as_ref(), expected.as_ref());
211    }
212
213    #[test]
214    #[allow(clippy::too_many_lines)]
215    fn entity_state_pdu() {
216        let header = PduHeader::new_v6(1, PduType::EntityState);
217
218        let body = EntityState::builder()
219            .with_entity_id(EntityId::new(500, 900, 14))
220            .with_force_id(ForceId::Friendly)
221            .with_entity_type(EntityType {
222                kind: EntityKind::Platform, domain: PlatformDomain::Air, country: Country::Netherlands_NLD_, category: 50, subcategory: 4, specific: 4, extra: 0
223            })
224            .with_alternative_entity_type(EntityType {
225                kind: EntityKind::Platform, domain: PlatformDomain::Air, country: Country::Netherlands_NLD_, category: 50, subcategory: 4, specific: 4, extra: 0
226            })
227            .with_velocity(VectorF32 {
228                first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
229            })
230            .with_location(Location {
231                x_coordinate: 0f64, y_coordinate : 0f64, z_coordinate: 0f64
232            })
233            .with_orientation(Orientation {
234                psi: 0f32, theta: 0f32, phi: 0f32
235            })
236            .with_appearance(EntityAppearance::AirPlatform(AirPlatformAppearance {
237                paint_scheme: AppearancePaintScheme::Camouflage,
238                propulsion_killed: false,
239                nvg_mode: AppearanceNVGMode::default(),
240                damage: AppearanceDamage::default(),
241                is_smoke_emanating: true,
242                is_engine_emitting_smoke: false,
243                trailing_effects: AppearanceTrailingEffects::Medium,
244                canopy_troop_door: AppearanceCanopy::NotApplicable,
245                landing_lights_on: false,
246                navigation_lights_on: false,
247                anticollision_lights_on: true,
248                is_flaming: false,
249                afterburner_on: true,
250                lower_anticollision_light_on: false,
251                upper_anticollision_light_on: false,
252                anticollision_light_day_night: AppearanceAntiCollisionDayNight::Night,
253                is_blinking: false,
254                is_frozen: false,
255                power_plant_on: false,
256                state: AppearanceEntityOrObjectState::Deactivated,
257                formation_lights_on: false,
258                landing_gear_extended: false,
259                cargo_doors_opened: true,
260                navigation_position_brightness: AppearanceNavigationPositionBrightness::Dim,
261                spot_search_light_1_on: false,
262                interior_lights_on: false,
263                reverse_thrust_engaged: false,
264                weightonwheels: true,
265            }))
266            .with_dead_reckoning_parameters(DrParameters {
267                algorithm: DeadReckoningAlgorithm::DRM_RVW_HighSpeedOrManeuveringEntityWithExtrapolationOfOrientation,
268                other_parameters: DrOtherParameters::None([0u8;15]),
269                linear_acceleration: VectorF32 {
270                    first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
271                },
272                angular_velocity: VectorF32 {
273                    first_vector_component: 0f32, second_vector_component: 0f32, third_vector_component: 0f32
274                }
275            })
276            .with_marking(EntityMarking {
277                marking_character_set: EntityMarkingCharacterSet::ASCII,
278                marking_string: "EYE 10".to_string()
279            })
280            .with_capabilities_flags(false, false, false, false)
281            .with_variable_parameter(VariableParameter::Articulated(ArticulatedPart {
282                change_indicator: 0,
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: 0,
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: 0,
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: 0,
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(
312            header,
313            body,
314            Timestamp::Absolute(TimeUnits::new(35_791_394).unwrap()),
315        );
316
317        let mut buf = BytesMut::with_capacity(208);
318
319        pdu.serialize(&mut buf).unwrap();
320
321        let expected: [u8; 208] = [
322            0x06, 0x01, 0x01, 0x01, 0x04, 0x44, 0x44, 0x45, 0x00, 0xd0, 0x00, 0x00, 0x01, 0xf4,
323            0x03, 0x84, 0x00, 0x0e, 0x01, 0x04, 0x01, 0x02, 0x00, 0x99, 0x32, 0x04, 0x04, 0x00,
324            0x01, 0x02, 0x00, 0x99, 0x32, 0x04, 0x04, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328            0x84, 0x89, 0x41, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
330            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
331            0x00, 0x00, 0x01, 0x45, 0x59, 0x45, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20,
332            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x3f, 0x80,
333            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0b,
334            0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335            0x10, 0x0c, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336            0x00, 0x00, 0x11, 0x4d, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337        ];
338
339        assert_eq!(buf.as_ref(), expected.as_ref());
340    }
341}