dis_rs/common/is_group_of/
model.rs

1use crate::common::{BodyInfo, Interaction};
2use crate::entity_state::model::EntityAppearance;
3use crate::enumerations::{IsGroupOfGroupedEntityCategory, PduType};
4use crate::is_group_of::builder::IsGroupOfBuilder;
5use crate::model::{EntityId, PduBody};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9const BASE_IS_GROUP_OF_BODY_LENGTH: u16 = 28;
10
11/// 5.9.3 `IsGroupOf` PDU
12///
13/// 7.8.3 `IsGroupOf` PDU
14///
15/// The `Vec` `groups` of `GroupEntityDescription` must be of the
16/// same enum value as indicated by `grouped_entity_category`.
17/// This is not enforced and thus left up to the user.
18#[derive(Clone, Debug, Default, PartialEq)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20pub struct IsGroupOf {
21    pub group_id: EntityId,
22    pub grouped_entity_category: IsGroupOfGroupedEntityCategory,
23    pub group_reference_point: GroupReferencePoint,
24    pub descriptions: Vec<GroupEntityDescription>,
25}
26
27impl IsGroupOf {
28    #[must_use]
29    pub fn builder() -> IsGroupOfBuilder {
30        IsGroupOfBuilder::new()
31    }
32
33    #[must_use]
34    pub fn into_builder(self) -> IsGroupOfBuilder {
35        IsGroupOfBuilder::new_from_body(self)
36    }
37
38    #[must_use]
39    pub fn into_pdu_body(self) -> PduBody {
40        PduBody::IsGroupOf(self)
41    }
42}
43
44impl BodyInfo for IsGroupOf {
45    fn body_length(&self) -> u16 {
46        BASE_IS_GROUP_OF_BODY_LENGTH
47            + self
48                .descriptions
49                .iter()
50                .map(GroupEntityDescription::record_length)
51                .sum::<u16>()
52    }
53
54    fn body_type(&self) -> PduType {
55        PduType::IsGroupOf
56    }
57}
58
59impl Interaction for IsGroupOf {
60    fn originator(&self) -> Option<&EntityId> {
61        Some(&self.group_id)
62    }
63
64    fn receiver(&self) -> Option<&EntityId> {
65        None
66    }
67}
68
69/// Custom defined record.
70#[derive(Clone, Debug, Default, PartialEq)]
71#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
72pub struct GroupReferencePoint {
73    pub latitude: f64,
74    pub longitude: f64,
75}
76
77impl GroupReferencePoint {
78    #[must_use]
79    pub fn with_latitude(mut self, latitude: f64) -> Self {
80        self.latitude = latitude;
81        self
82    }
83
84    #[must_use]
85    pub fn with_longitude(mut self, longitude: f64) -> Self {
86        self.longitude = longitude;
87        self
88    }
89
90    #[must_use]
91    pub const fn record_length(&self) -> u16 {
92        16
93    }
94}
95
96/// Wrapper enum for UID 213 and the respective
97/// Group Entity Description (GED) records
98#[derive(Clone, Debug, Default, PartialEq)]
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100pub enum GroupEntityDescription {
101    #[default]
102    Undefined,
103    BasicGroundCombatVehicle(GEDRecord1),
104    EnhancedGroundCombatVehicle(GEDRecord2),
105    BasicGroundCombatSoldier(GEDRecord3),
106    EnhancedGroundCombatSoldier(GEDRecord4),
107    BasicRotorWingAircraft(GEDRecord5),
108    EnhancedRotorWingAircraft(GEDRecord6),
109    BasicFixedWingAircraft(GEDRecord7),
110    EnhancedFixedWingAircraft(GEDRecord8),
111    GroundLogisticsVehicle(GEDRecord9),
112}
113
114impl GroupEntityDescription {
115    #[must_use]
116    pub const fn record_length(&self) -> u16 {
117        match self {
118            GroupEntityDescription::Undefined => 0,
119            GroupEntityDescription::BasicGroundCombatVehicle(ged) => ged.record_length(),
120            GroupEntityDescription::EnhancedGroundCombatVehicle(ged) => ged.record_length(),
121            GroupEntityDescription::BasicGroundCombatSoldier(ged) => ged.record_length(),
122            GroupEntityDescription::EnhancedGroundCombatSoldier(ged) => ged.record_length(),
123            GroupEntityDescription::BasicRotorWingAircraft(ged) => ged.record_length(),
124            GroupEntityDescription::EnhancedRotorWingAircraft(ged) => ged.record_length(),
125            GroupEntityDescription::BasicFixedWingAircraft(ged) => ged.record_length(),
126            GroupEntityDescription::EnhancedFixedWingAircraft(ged) => ged.record_length(),
127            GroupEntityDescription::GroundLogisticsVehicle(ged) => ged.record_length(),
128        }
129    }
130}
131
132#[derive(Clone, Debug, Default, PartialEq)]
133#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
134pub struct GEDEntityLocation {
135    pub x_offset: u16,
136    pub y_offset: u16,
137    pub z_offset: u16,
138}
139
140impl GEDEntityLocation {
141    #[must_use]
142    pub const fn record_length(&self) -> u16 {
143        6
144    }
145}
146
147#[derive(Clone, Debug, Default, PartialEq)]
148#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
149pub struct GEDEntityOrientation {
150    pub psi: u8,
151    pub theta: u8,
152    pub phi: u8,
153}
154
155impl GEDEntityOrientation {
156    #[must_use]
157    pub const fn record_length(&self) -> u16 {
158        3
159    }
160}
161
162/// UID 215
163#[derive(Clone, Debug, Default, PartialEq)]
164#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
165pub struct GEDRecord1 {
166    pub entity_id: u16,
167    pub location: GEDEntityLocation,
168    pub appearance: EntityAppearance,
169    pub orientation: GEDEntityOrientation,
170    pub speed: u8,
171    pub turret_azimuth: u8,
172    pub gun_elevation: u8,
173    pub turret_slew_rate: u8,
174    pub gun_elevation_rate: u8,
175}
176
177impl GEDRecord1 {
178    #[must_use]
179    pub const fn record_length(&self) -> u16 {
180        self.location.record_length()
181            + self.orientation.record_length()
182            + self.appearance.record_length()
183            + 7
184    }
185}
186
187/// UID 216
188#[derive(Clone, Debug, Default, PartialEq)]
189#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
190pub struct GEDRecord2 {
191    pub basic_ground_combat_vehicle: GEDRecord1,
192    pub fuel_status: u8,
193    pub ground_maintenance_status: u8,
194    pub primary_ammunition: u8,
195    pub secondary_ammunition: u8,
196}
197
198impl GEDRecord2 {
199    #[must_use]
200    pub const fn record_length(&self) -> u16 {
201        self.basic_ground_combat_vehicle.record_length() + 4
202    }
203}
204
205/// UID 217
206#[derive(Clone, Debug, Default, PartialEq)]
207#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
208pub struct GEDRecord3 {
209    pub entity_id: u16,
210    pub location: GEDEntityLocation,
211    pub appearance: EntityAppearance,
212    pub orientation: GEDEntityOrientation,
213    pub speed: u8,
214    pub head_azimuth: u8,
215    pub head_elevation: u8,
216    pub head_scan_rate: u8,
217    pub head_elevation_rate: u8,
218}
219
220impl GEDRecord3 {
221    #[must_use]
222    pub const fn record_length(&self) -> u16 {
223        self.location.record_length()
224            + self.orientation.record_length()
225            + self.appearance.record_length()
226            + 7
227    }
228}
229
230/// UID 218
231#[derive(Clone, Debug, Default, PartialEq)]
232#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233pub struct GEDRecord4 {
234    pub basic_ground_combat_soldier: GEDRecord3,
235    pub water_status: u8,
236    pub reset_status: u8,
237    pub primary_ammunition: u8,
238    pub secondary_ammunition: u8,
239}
240
241impl GEDRecord4 {
242    #[must_use]
243    pub const fn record_length(&self) -> u16 {
244        self.basic_ground_combat_soldier.record_length() + 4
245    }
246}
247
248/// UID 219
249#[derive(Clone, Debug, Default, PartialEq)]
250#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
251pub struct GEDRecord5 {
252    pub entity_id: u16,
253    pub location: GEDEntityLocation,
254    pub appearance: EntityAppearance,
255    pub orientation: GEDEntityOrientation,
256    pub fuel_status: u8,
257    pub movement_horizontal_deviation: u8,
258    pub movement_vertical_deviation: u8,
259    pub movement_speed: u16,
260    pub turret_azimuth: u8,
261    pub gun_elevation: u8,
262    pub turret_scan_rate: u8,
263    pub gun_elevation_rate: u8,
264}
265
266impl GEDRecord5 {
267    #[must_use]
268    pub const fn record_length(&self) -> u16 {
269        self.location.record_length()
270            + self.orientation.record_length()
271            + self.appearance.record_length()
272            + 11
273    }
274}
275
276/// UID 220
277#[derive(Clone, Debug, Default, PartialEq)]
278#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
279pub struct GEDRecord6 {
280    pub basic_rotor_wing_aircraft: GEDRecord5,
281    pub supplemental_fuel_status: u8,
282    pub air_maintenance_status: u8,
283    pub primary_ammunition: u8,
284    pub secondary_ammunition: u8,
285}
286
287impl GEDRecord6 {
288    #[must_use]
289    pub const fn record_length(&self) -> u16 {
290        self.basic_rotor_wing_aircraft.record_length() + 4
291    }
292}
293
294/// UID 221
295#[derive(Clone, Debug, Default, PartialEq)]
296#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
297pub struct GEDRecord7 {
298    pub entity_id: u16,
299    pub location: GEDEntityLocation,
300    pub appearance: EntityAppearance,
301    pub orientation: GEDEntityOrientation,
302    pub fuel_status: u8,
303    pub movement_horizontal_deviation: u8,
304    pub movement_vertical_deviation: u8,
305    pub movement_speed: u16,
306}
307
308impl GEDRecord7 {
309    #[must_use]
310    pub const fn record_length(&self) -> u16 {
311        self.location.record_length()
312            + self.orientation.record_length()
313            + self.appearance.record_length()
314            + 7
315    }
316}
317
318/// UID 222
319#[derive(Clone, Debug, Default, PartialEq)]
320#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
321pub struct GEDRecord8 {
322    pub basic_fixed_wing_aircraft: GEDRecord7,
323    pub supplemental_fuel_status: u8,
324    pub air_maintenance_status: u8,
325    pub primary_ammunition: u8,
326    pub secondary_ammunition: u8,
327}
328
329impl GEDRecord8 {
330    #[must_use]
331    pub const fn record_length(&self) -> u16 {
332        self.basic_fixed_wing_aircraft.record_length() + 4
333    }
334}
335
336/// UID 223
337#[derive(Clone, Debug, Default, PartialEq)]
338#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
339pub struct GEDRecord9 {
340    pub entity_id: u16,
341    pub location: GEDEntityLocation,
342    pub appearance: EntityAppearance,
343    pub orientation: GEDEntityOrientation,
344    pub speed: u16,
345}
346
347impl GEDRecord9 {
348    #[must_use]
349    pub const fn record_length(&self) -> u16 {
350        self.location.record_length()
351            + self.orientation.record_length()
352            + self.appearance.record_length()
353            + 4
354    }
355}