dis_rs/common/aggregate_state/
model.rs1use crate::aggregate_state::builder::AggregateStateBuilder;
2use crate::common::{BodyInfo, Interaction};
3use crate::constants::{EIGHT_OCTETS, FOUR_OCTETS, THIRTY_TWO_OCTETS, TWO_OCTETS};
4use crate::entity_state::model::EntityAppearance;
5use crate::enumerations::{
6 AggregateStateAggregateKind, AggregateStateAggregateState, AggregateStateFormation,
7 AggregateStateSpecific, AggregateStateSubcategory, Country, EntityMarkingCharacterSet, ForceId,
8 PduType, PlatformDomain,
9};
10use crate::model::{
11 BASE_VARIABLE_DATUM_LENGTH, EntityId, EntityType, Location, Orientation, PduBody,
12 VariableDatum, VectorF32, length_padded_to_num,
13};
14use crate::{BodyRaw, DisError};
15use alloc::{
16 format,
17 string::{String, ToString},
18 vec::Vec,
19};
20use core::{fmt::Display, str::FromStr};
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24pub(crate) const BASE_AGGREGATE_STATE_BODY_LENGTH: u16 = 124;
25
26#[derive(Clone, Debug, Default, PartialEq)]
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31pub struct AggregateState {
32 pub aggregate_id: EntityId,
33 pub force_id: ForceId,
34 pub aggregate_state: AggregateStateAggregateState,
35 pub aggregate_type: AggregateType,
36 pub formation: AggregateStateFormation,
37 pub aggregate_marking: AggregateMarking,
38 pub dimensions: VectorF32,
39 pub orientation: Orientation,
40 pub center_of_mass: Location,
41 pub velocity: VectorF32,
42 pub aggregates: Vec<EntityId>,
43 pub entities: Vec<EntityId>,
44 pub silent_aggregate_systems: Vec<SilentAggregateSystem>,
45 pub silent_entity_systems: Vec<SilentEntitySystem>,
46 pub variable_datums: Vec<VariableDatum>,
47}
48
49impl BodyRaw for AggregateState {
50 type Builder = AggregateStateBuilder;
51
52 fn builder() -> AggregateStateBuilder {
53 AggregateStateBuilder::new()
54 }
55
56 fn into_builder(self) -> AggregateStateBuilder {
57 AggregateStateBuilder::new_from_body(self)
58 }
59
60 fn into_pdu_body(self) -> PduBody {
61 PduBody::AggregateState(self)
62 }
63}
64
65pub(crate) fn aggregate_state_intermediate_length_padding(
70 aggregates: &[EntityId],
71 entities: &[EntityId],
72) -> (u16, u16) {
73 let intermediate_length = BASE_AGGREGATE_STATE_BODY_LENGTH
74 + aggregates.iter().map(crate::model::EntityId::record_length ).sum::<u16>() + entities.iter().map(crate::model::EntityId::record_length ).sum::<u16>(); let padding_length = intermediate_length % (FOUR_OCTETS as u16); (intermediate_length + padding_length, padding_length)
78}
79
80impl BodyInfo for AggregateState {
81 fn body_length(&self) -> u16 {
82 let (intermediate_length, _padding_length) =
83 aggregate_state_intermediate_length_padding(&self.aggregates, &self.entities);
84 intermediate_length
85 + self.silent_aggregate_systems.iter().map(SilentAggregateSystem::record_length ).sum::<u16>()
87 + self.silent_entity_systems.iter().map(SilentEntitySystem::record_length ).sum::<u16>()
89 + (self.variable_datums.iter().map(|datum| {
91 let padded_record = length_padded_to_num(
92 BASE_VARIABLE_DATUM_LENGTH as usize + datum.datum_value.len(),
93 EIGHT_OCTETS);
94 padded_record.record_length as u16
95 } ).sum::<u16>())
96 }
97
98 fn body_type(&self) -> PduType {
99 PduType::AggregateState
100 }
101}
102
103impl Interaction for AggregateState {
104 fn originator(&self) -> Option<&EntityId> {
105 None
106 }
107
108 fn receiver(&self) -> Option<&EntityId> {
109 None
110 }
111}
112
113#[derive(Clone, Debug, Default, PartialEq)]
115#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116pub struct AggregateMarking {
117 pub marking_character_set: EntityMarkingCharacterSet,
118 pub marking_string: String, }
120
121impl AggregateMarking {
122 #[must_use]
123 pub fn new(marking: String, character_set: EntityMarkingCharacterSet) -> Self {
124 Self {
125 marking_character_set: character_set,
126 marking_string: marking,
127 }
128 }
129
130 pub fn new_ascii<S: Into<String>>(marking: S) -> Self {
131 AggregateMarking::new(marking.into(), EntityMarkingCharacterSet::ASCII)
132 }
133
134 #[allow(clippy::return_self_not_must_use)]
135 pub fn with_marking<S: Into<String>>(mut self, marking: S) -> Self {
136 self.marking_string = marking.into();
137 self
138 }
139
140 #[must_use]
141 pub fn record_length(&self) -> u16 {
142 THIRTY_TWO_OCTETS as u16
143 }
144}
145
146impl Display for AggregateMarking {
147 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148 f.write_str(self.marking_string.as_str())
149 }
150}
151
152impl FromStr for AggregateMarking {
153 type Err = DisError;
154
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 if s.len() <= 31 {
157 Ok(Self {
158 marking_character_set: EntityMarkingCharacterSet::ASCII,
159 marking_string: s.to_string(),
160 })
161 } else {
162 Err(DisError::ParseError(format!(
163 "String is too long for AggregateMarking. Found {}, max 31 allowed.",
164 s.len()
165 )))
166 }
167 }
168}
169
170#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
172#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
173pub struct AggregateType {
174 pub aggregate_kind: AggregateStateAggregateKind,
175 pub domain: PlatformDomain,
176 pub country: Country,
177 pub category: u8,
178 pub subcategory: AggregateStateSubcategory,
179 pub specific: AggregateStateSpecific,
180 pub extra: u8,
181}
182
183impl AggregateType {
184 #[must_use]
185 pub fn with_aggregate_kind(mut self, aggregate_kind: AggregateStateAggregateKind) -> Self {
186 self.aggregate_kind = aggregate_kind;
187 self
188 }
189
190 #[must_use]
191 pub fn with_domain(mut self, domain: PlatformDomain) -> Self {
192 self.domain = domain;
193 self
194 }
195
196 #[must_use]
197 pub fn with_country(mut self, country: Country) -> Self {
198 self.country = country;
199 self
200 }
201
202 #[must_use]
203 pub fn with_category(mut self, category: u8) -> Self {
204 self.category = category;
205 self
206 }
207
208 #[must_use]
209 pub fn with_subcategory(mut self, subcategory: AggregateStateSubcategory) -> Self {
210 self.subcategory = subcategory;
211 self
212 }
213
214 #[must_use]
215 pub fn with_specific(mut self, specific: AggregateStateSpecific) -> Self {
216 self.specific = specific;
217 self
218 }
219
220 #[must_use]
221 pub fn with_extra(mut self, extra: u8) -> Self {
222 self.extra = extra;
223 self
224 }
225
226 #[must_use]
227 pub fn record_length(&self) -> u16 {
228 EIGHT_OCTETS as u16
229 }
230}
231
232impl Display for AggregateType {
233 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
234 write!(
235 f,
236 "{}:{}:{}:{}:{}:{}:{}",
237 u8::from(self.aggregate_kind),
238 u8::from(self.domain),
239 u16::from(self.country),
240 self.category,
241 u8::from(self.subcategory),
242 u8::from(self.specific),
243 self.extra
244 )
245 }
246}
247
248#[allow(clippy::get_first)]
249impl FromStr for AggregateType {
250 type Err = DisError;
251
252 fn from_str(s: &str) -> Result<Self, Self::Err> {
253 const NUM_DIGITS: usize = 7;
254 let ss = s.split(':').collect::<Vec<&str>>();
255 if ss.len() != NUM_DIGITS {
256 return Err(DisError::ParseError(format!(
257 "AggregateType string pattern does contain not precisely {NUM_DIGITS} digits"
258 )));
259 }
260 Ok(Self {
261 aggregate_kind: ss
262 .get(0)
263 .expect("Impossible - checked for correct number of digits")
264 .parse::<u8>()
265 .map_err(|_| DisError::ParseError("Invalid kind digit".to_string()))?
266 .into(),
267 domain: ss
268 .get(1)
269 .expect("Impossible - checked for correct number of digits")
270 .parse::<u8>()
271 .map_err(|_| DisError::ParseError("Invalid domain digit".to_string()))?
272 .into(),
273 country: ss
274 .get(2)
275 .expect("Impossible - checked for correct number of digits")
276 .parse::<u16>()
277 .map_err(|_| DisError::ParseError("Invalid country digit".to_string()))?
278 .into(),
279 category: ss
280 .get(3)
281 .expect("Impossible - checked for correct number of digits")
282 .parse::<u8>()
283 .map_err(|_| DisError::ParseError("Invalid category digit".to_string()))?,
284 subcategory: ss
285 .get(4)
286 .expect("Impossible - checked for correct number of digits")
287 .parse::<u8>()
288 .map_err(|_| DisError::ParseError("Invalid subcategory digit".to_string()))?
289 .into(),
290 specific: ss
291 .get(5)
292 .expect("Impossible - checked for correct number of digits")
293 .parse::<u8>()
294 .map_err(|_| DisError::ParseError("Invalid specific digit".to_string()))?
295 .into(),
296 extra: ss
297 .get(6)
298 .expect("Impossible - checked for correct number of digits")
299 .parse::<u8>()
300 .map_err(|_| DisError::ParseError("Invalid extra digit".to_string()))?,
301 })
302 }
303}
304
305impl TryFrom<&str> for AggregateType {
306 type Error = DisError;
307
308 fn try_from(value: &str) -> Result<Self, Self::Error> {
309 AggregateType::from_str(value)
310 }
311}
312
313impl TryFrom<String> for AggregateType {
314 type Error = DisError;
315
316 fn try_from(value: String) -> Result<Self, Self::Error> {
317 TryFrom::<&str>::try_from(&value)
318 }
319}
320
321#[derive(Clone, Debug, Default, PartialEq)]
323#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
324pub struct SilentAggregateSystem {
325 pub number_of_aggregates: u16,
326 pub aggregate_type: AggregateType,
327}
328
329impl SilentAggregateSystem {
330 #[must_use]
331 pub fn with_number_of_aggregates(mut self, number_of_aggregates: u16) -> Self {
332 self.number_of_aggregates = number_of_aggregates;
333 self
334 }
335
336 #[must_use]
337 pub fn with_aggregate_type(mut self, aggregate_type: AggregateType) -> Self {
338 self.aggregate_type = aggregate_type;
339 self
340 }
341
342 #[must_use]
343 pub fn record_length(&self) -> u16 {
344 FOUR_OCTETS as u16 + self.aggregate_type.record_length()
345 }
346}
347
348#[derive(Clone, Debug, Default, PartialEq)]
350#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
351pub struct SilentEntitySystem {
352 pub number_of_entities: u16,
353 pub entity_type: EntityType,
354 pub appearances: Vec<EntityAppearance>,
355}
356
357impl SilentEntitySystem {
358 #[must_use]
359 pub fn with_number_of_entities(mut self, number_of_entities: u16) -> Self {
360 self.number_of_entities = number_of_entities;
361 self
362 }
363
364 #[must_use]
365 pub fn with_entity_type(mut self, entity_type: EntityType) -> Self {
366 self.entity_type = entity_type;
367 self
368 }
369
370 #[must_use]
371 pub fn with_appearance(mut self, appearance: EntityAppearance) -> Self {
372 self.appearances.push(appearance);
373 self
374 }
375
376 #[must_use]
377 pub fn with_appearances(mut self, appearances: Vec<EntityAppearance>) -> Self {
378 self.appearances = appearances;
379 self
380 }
381
382 #[must_use]
383 pub fn record_length(&self) -> u16 {
384 TWO_OCTETS as u16
385 + self.entity_type.record_length()
386 + self
387 .appearances
388 .iter()
389 .map(crate::entity_state::model::EntityAppearance::record_length)
390 .sum::<u16>()
391 }
392}