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