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::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}