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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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}