1use crate::DisError;
2use crate::acknowledge_r::model::AcknowledgeR;
3use crate::action_request_r::model::ActionRequestR;
4use crate::action_response_r::model::ActionResponseR;
5use crate::aggregate_state::model::AggregateState;
6use crate::comment_r::model::CommentR;
7use crate::common::acknowledge::model::Acknowledge;
8use crate::common::action_request::model::ActionRequest;
9use crate::common::action_response::model::ActionResponse;
10use crate::common::attribute::model::Attribute;
11use crate::common::collision::model::Collision;
12use crate::common::collision_elastic::model::CollisionElastic;
13use crate::common::comment::model::Comment;
14use crate::common::create_entity::model::CreateEntity;
15use crate::common::data::model::Data;
16use crate::common::data_query::model::DataQuery;
17use crate::common::designator::model::Designator;
18use crate::common::detonation::model::Detonation;
19use crate::common::electromagnetic_emission::model::ElectromagneticEmission;
20use crate::common::entity_state::model::EntityState;
21use crate::common::entity_state_update::model::EntityStateUpdate;
22use crate::common::event_report::model::EventReport;
23use crate::common::fire::model::Fire;
24use crate::common::iff::model::Iff;
25use crate::common::other::model::Other;
26use crate::common::receiver::model::Receiver;
27use crate::common::remove_entity::model::RemoveEntity;
28use crate::common::set_data::model::SetData;
29use crate::common::signal::model::Signal;
30use crate::common::start_resume::model::StartResume;
31use crate::common::stop_freeze::model::StopFreeze;
32pub use crate::common::timestamp::{TimeUnits, Timestamp};
33use crate::common::transmitter::model::Transmitter;
34use crate::common::{BodyInfo, Interaction};
35use crate::constants::{
36 EIGHT_OCTETS, FIFTEEN_OCTETS, NO_REMAINDER, PDU_HEADER_LEN_BYTES, SIX_OCTETS,
37};
38use crate::create_entity_r::model::CreateEntityR;
39use crate::data_query_r::model::DataQueryR;
40use crate::data_r::model::DataR;
41use crate::enumerations::{
42 ArticulatedPartsTypeClass, ArticulatedPartsTypeMetric, AttachedPartDetachedIndicator,
43 AttachedParts, ChangeIndicator, EntityAssociationAssociationStatus,
44 EntityAssociationGroupMemberType, EntityAssociationPhysicalAssociationType,
45 EntityAssociationPhysicalConnectionType, SeparationPreEntityIndicator,
46 SeparationReasonForSeparation, StationName,
47};
48use crate::enumerations::{
49 Country, EntityKind, ExplosiveMaterialCategories, MunitionDescriptorFuse,
50 MunitionDescriptorWarhead, PduType, PlatformDomain, ProtocolFamily, ProtocolVersion,
51 VariableRecordType,
52};
53use crate::event_report_r::model::EventReportR;
54use crate::fixed_parameters::{NO_APPLIC, NO_ENTITY, NO_SITE};
55use crate::is_group_of::model::IsGroupOf;
56use crate::is_part_of::model::IsPartOf;
57use crate::record_query_r::model::RecordQueryR;
58use crate::record_r::model::RecordR;
59use crate::remove_entity_r::model::RemoveEntityR;
60use crate::repair_complete::model::RepairComplete;
61use crate::repair_response::model::RepairResponse;
62use crate::resupply_cancel::model::ResupplyCancel;
63use crate::resupply_offer::model::ResupplyOffer;
64use crate::resupply_received::model::ResupplyReceived;
65use crate::sees::model::SEES;
66use crate::service_request::model::ServiceRequest;
67use crate::set_data_r::model::SetDataR;
68use crate::set_record_r::model::SetRecordR;
69use crate::start_resume_r::model::StartResumeR;
70use crate::stop_freeze_r::model::StopFreezeR;
71use crate::transfer_ownership::model::TransferOwnership;
72use crate::underwater_acoustic::model::UnderwaterAcoustic;
73use alloc::{
74 format,
75 string::{String, ToString},
76 vec::Vec,
77};
78use core::{fmt::Display, str::FromStr};
79#[cfg(feature = "serde")]
80use serde::{Deserialize, Serialize};
81
82pub use crate::v7::model::PduStatus;
83
84#[derive(Clone, Debug, PartialEq)]
85#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
86pub struct Pdu {
87 pub header: PduHeader,
88 pub body: PduBody,
89}
90
91impl Pdu {
92 #[must_use]
93 pub fn finalize_from_parts(header: PduHeader, body: PduBody, timestamp: Timestamp) -> Self {
94 Self {
95 header: header
96 .with_pdu_type(body.body_type())
97 .with_timestamp(timestamp)
98 .with_length(body.body_length()),
99 body,
100 }
101 }
102
103 #[must_use]
104 pub fn pdu_length(&self) -> u16 {
105 PDU_HEADER_LEN_BYTES + self.body.body_length()
106 }
107}
108
109impl Interaction for Pdu {
110 fn originator(&self) -> Option<&EntityId> {
111 self.body.originator()
112 }
113
114 fn receiver(&self) -> Option<&EntityId> {
115 self.body.receiver()
116 }
117}
118
119#[derive(Copy, Clone, Debug, PartialEq)]
121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122pub struct PduHeader {
123 pub protocol_version: ProtocolVersion,
124 pub exercise_id: u8,
125 pub pdu_type: PduType,
126 pub protocol_family: ProtocolFamily,
127 pub timestamp: Timestamp,
128 pub pdu_length: u16,
129 pub pdu_status: Option<PduStatus>,
130 pub padding: u16,
131}
132
133impl PduHeader {
134 #[must_use]
135 pub fn new(protocol_version: ProtocolVersion, exercise_id: u8, pdu_type: PduType) -> Self {
136 let protocol_family = pdu_type.into();
137 Self {
138 protocol_version,
139 exercise_id,
140 pdu_type,
141 protocol_family,
142 timestamp: Timestamp::default(),
143 pdu_length: 0u16,
144 pdu_status: None,
145 padding: 0u16,
146 }
147 }
148
149 #[must_use]
150 pub fn new_v6(exercise_id: u8, pdu_type: PduType) -> Self {
151 PduHeader::new(ProtocolVersion::IEEE1278_1A1998, exercise_id, pdu_type)
152 }
153
154 #[must_use]
155 pub fn new_v7(exercise_id: u8, pdu_type: PduType) -> Self {
156 PduHeader::new(ProtocolVersion::IEEE1278_12012, exercise_id, pdu_type)
157 }
158
159 #[must_use]
160 pub fn with_pdu_type(mut self, pdu_type: PduType) -> Self {
161 self.protocol_family = pdu_type.into();
162 self.pdu_type = pdu_type;
163 self
164 }
165
166 #[must_use]
167 pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self {
168 self.timestamp = timestamp;
169 self
170 }
171
172 #[must_use]
173 pub fn with_length(mut self, body_length: u16) -> Self {
174 self.pdu_length = PDU_HEADER_LEN_BYTES + body_length;
175 self
176 }
177
178 #[must_use]
179 pub fn with_pdu_status(mut self, pdu_status: PduStatus) -> Self {
180 self.pdu_status = Some(pdu_status);
181 self
182 }
183}
184
185#[derive(Clone, Debug, PartialEq)]
186#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
187#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
188#[cfg_attr(feature = "serde", serde(tag = "type"))]
189pub enum PduBody {
190 Other(Other),
191 EntityState(EntityState),
192 Fire(Fire),
193 Detonation(Detonation),
194 Collision(Collision),
195 ServiceRequest(ServiceRequest),
196 ResupplyOffer(ResupplyOffer),
197 ResupplyReceived(ResupplyReceived),
198 ResupplyCancel(ResupplyCancel),
199 RepairComplete(RepairComplete),
200 RepairResponse(RepairResponse),
201 CreateEntity(CreateEntity),
202 RemoveEntity(RemoveEntity),
203 StartResume(StartResume),
204 StopFreeze(StopFreeze),
205 Acknowledge(Acknowledge),
206 ActionRequest(ActionRequest),
207 ActionResponse(ActionResponse),
208 DataQuery(DataQuery),
209 SetData(SetData),
210 Data(Data),
211 EventReport(EventReport),
212 Comment(Comment),
213 ElectromagneticEmission(ElectromagneticEmission),
214 Designator(Designator),
215 Transmitter(Transmitter),
216 Signal(Signal),
217 Receiver(Receiver),
218 IFF(Iff),
219 UnderwaterAcoustic(UnderwaterAcoustic),
220 SupplementalEmissionEntityState(SEES),
221 IntercomSignal,
222 IntercomControl,
223 AggregateState(AggregateState),
224 IsGroupOf(IsGroupOf),
225 TransferOwnership(TransferOwnership),
226 IsPartOf(IsPartOf),
227 MinefieldState,
228 MinefieldQuery,
229 MinefieldData,
230 MinefieldResponseNACK,
231 EnvironmentalProcess,
232 GriddedData,
233 PointObjectState,
234 LinearObjectState,
235 ArealObjectState,
236 TSPI,
237 Appearance,
238 ArticulatedParts,
239 LEFire,
240 LEDetonation,
241 CreateEntityR(CreateEntityR),
242 RemoveEntityR(RemoveEntityR),
243 StartResumeR(StartResumeR),
244 StopFreezeR(StopFreezeR),
245 AcknowledgeR(AcknowledgeR),
246 ActionRequestR(ActionRequestR),
247 ActionResponseR(ActionResponseR),
248 DataQueryR(DataQueryR),
249 SetDataR(SetDataR),
250 DataR(DataR),
251 EventReportR(EventReportR),
252 CommentR(CommentR),
253 RecordR(RecordR),
254 SetRecordR(SetRecordR),
255 RecordQueryR(RecordQueryR),
256 CollisionElastic(CollisionElastic),
257 EntityStateUpdate(EntityStateUpdate),
258 DirectedEnergyFire,
259 EntityDamageStatus,
260 InformationOperationsAction,
261 InformationOperationsReport,
262 Attribute(Attribute),
263}
264
265impl BodyInfo for PduBody {
266 #[allow(clippy::match_same_arms)]
267 fn body_length(&self) -> u16 {
268 match self {
269 PduBody::Other(body) => body.body_length(),
270 PduBody::EntityState(body) => body.body_length(),
271 PduBody::Fire(body) => body.body_length(),
272 PduBody::Detonation(body) => body.body_length(),
273 PduBody::Collision(body) => body.body_length(),
274 PduBody::ServiceRequest(body) => body.body_length(),
275 PduBody::ResupplyOffer(body) => body.body_length(),
276 PduBody::ResupplyReceived(body) => body.body_length(),
277 PduBody::ResupplyCancel(body) => body.body_length(),
278 PduBody::RepairComplete(body) => body.body_length(),
279 PduBody::RepairResponse(body) => body.body_length(),
280 PduBody::CreateEntity(body) => body.body_length(),
281 PduBody::RemoveEntity(body) => body.body_length(),
282 PduBody::StartResume(body) => body.body_length(),
283 PduBody::StopFreeze(body) => body.body_length(),
284 PduBody::Acknowledge(body) => body.body_length(),
285 PduBody::ActionRequest(body) => body.body_length(),
286 PduBody::ActionResponse(body) => body.body_length(),
287 PduBody::DataQuery(body) => body.body_length(),
288 PduBody::SetData(body) => body.body_length(),
289 PduBody::Data(body) => body.body_length(),
290 PduBody::EventReport(body) => body.body_length(),
291 PduBody::Comment(body) => body.body_length(),
292 PduBody::ElectromagneticEmission(body) => body.body_length(),
293 PduBody::Designator(body) => body.body_length(),
294 PduBody::Transmitter(body) => body.body_length(),
295 PduBody::Signal(body) => body.body_length(),
296 PduBody::Receiver(body) => body.body_length(),
297 PduBody::IFF(body) => body.body_length(),
298 PduBody::UnderwaterAcoustic(body) => body.body_length(),
299 PduBody::SupplementalEmissionEntityState(body) => body.body_length(),
300 PduBody::IntercomSignal => 0,
301 PduBody::IntercomControl => 0,
302 PduBody::AggregateState(body) => body.body_length(),
303 PduBody::IsGroupOf(body) => body.body_length(),
304 PduBody::TransferOwnership(body) => body.body_length(),
305 PduBody::IsPartOf(body) => body.body_length(),
306 PduBody::MinefieldState => 0,
307 PduBody::MinefieldQuery => 0,
308 PduBody::MinefieldData => 0,
309 PduBody::MinefieldResponseNACK => 0,
310 PduBody::EnvironmentalProcess => 0,
311 PduBody::GriddedData => 0,
312 PduBody::PointObjectState => 0,
313 PduBody::LinearObjectState => 0,
314 PduBody::ArealObjectState => 0,
315 PduBody::TSPI => 0,
316 PduBody::Appearance => 0,
317 PduBody::ArticulatedParts => 0,
318 PduBody::LEFire => 0,
319 PduBody::LEDetonation => 0,
320 PduBody::CreateEntityR(body) => body.body_length(),
321 PduBody::RemoveEntityR(body) => body.body_length(),
322 PduBody::StartResumeR(body) => body.body_length(),
323 PduBody::StopFreezeR(body) => body.body_length(),
324 PduBody::AcknowledgeR(body) => body.body_length(),
325 PduBody::ActionRequestR(body) => body.body_length(),
326 PduBody::ActionResponseR(body) => body.body_length(),
327 PduBody::DataQueryR(body) => body.body_length(),
328 PduBody::SetDataR(body) => body.body_length(),
329 PduBody::DataR(body) => body.body_length(),
330 PduBody::EventReportR(body) => body.body_length(),
331 PduBody::CommentR(body) => body.body_length(),
332 PduBody::RecordR(body) => body.body_length(),
333 PduBody::SetRecordR(body) => body.body_length(),
334 PduBody::RecordQueryR(body) => body.body_length(),
335 PduBody::CollisionElastic(body) => body.body_length(),
336 PduBody::EntityStateUpdate(body) => body.body_length(),
337 PduBody::DirectedEnergyFire => 0,
338 PduBody::EntityDamageStatus => 0,
339 PduBody::InformationOperationsAction => 0,
340 PduBody::InformationOperationsReport => 0,
341 PduBody::Attribute(body) => body.body_length(),
342 }
343 }
344
345 fn body_type(&self) -> PduType {
346 match self {
347 PduBody::Other(body) => body.body_type(),
348 PduBody::EntityState(body) => body.body_type(),
349 PduBody::Fire(body) => body.body_type(),
350 PduBody::Detonation(body) => body.body_type(),
351 PduBody::Collision(body) => body.body_type(),
352 PduBody::ServiceRequest(body) => body.body_type(),
353 PduBody::ResupplyOffer(body) => body.body_type(),
354 PduBody::ResupplyReceived(body) => body.body_type(),
355 PduBody::ResupplyCancel(body) => body.body_type(),
356 PduBody::RepairComplete(body) => body.body_type(),
357 PduBody::RepairResponse(body) => body.body_type(),
358 PduBody::CreateEntity(body) => body.body_type(),
359 PduBody::RemoveEntity(body) => body.body_type(),
360 PduBody::StartResume(body) => body.body_type(),
361 PduBody::StopFreeze(body) => body.body_type(),
362 PduBody::Acknowledge(body) => body.body_type(),
363 PduBody::ActionRequest(body) => body.body_type(),
364 PduBody::ActionResponse(body) => body.body_type(),
365 PduBody::DataQuery(body) => body.body_type(),
366 PduBody::SetData(body) => body.body_type(),
367 PduBody::Data(body) => body.body_type(),
368 PduBody::EventReport(body) => body.body_type(),
369 PduBody::Comment(body) => body.body_type(),
370 PduBody::ElectromagneticEmission(body) => body.body_type(),
371 PduBody::Designator(body) => body.body_type(),
372 PduBody::Transmitter(body) => body.body_type(),
373 PduBody::Signal(body) => body.body_type(),
374 PduBody::Receiver(body) => body.body_type(),
375 PduBody::IFF(body) => body.body_type(),
376 PduBody::UnderwaterAcoustic(body) => body.body_type(),
377 PduBody::SupplementalEmissionEntityState(body) => body.body_type(),
378 PduBody::IntercomSignal => PduType::IntercomSignal,
379 PduBody::IntercomControl => PduType::IntercomControl,
380 PduBody::AggregateState(body) => body.body_type(),
381 PduBody::IsGroupOf(body) => body.body_type(),
382 PduBody::TransferOwnership(body) => body.body_type(),
383 PduBody::IsPartOf(body) => body.body_type(),
384 PduBody::MinefieldState => PduType::MinefieldState,
385 PduBody::MinefieldQuery => PduType::MinefieldQuery,
386 PduBody::MinefieldData => PduType::MinefieldData,
387 PduBody::MinefieldResponseNACK => PduType::MinefieldResponseNACK,
388 PduBody::EnvironmentalProcess => PduType::EnvironmentalProcess,
389 PduBody::GriddedData => PduType::GriddedData,
390 PduBody::PointObjectState => PduType::PointObjectState,
391 PduBody::LinearObjectState => PduType::LinearObjectState,
392 PduBody::ArealObjectState => PduType::ArealObjectState,
393 PduBody::TSPI => PduType::TSPI,
394 PduBody::Appearance => PduType::Appearance,
395 PduBody::ArticulatedParts => PduType::ArticulatedParts,
396 PduBody::LEFire => PduType::LEFire,
397 PduBody::LEDetonation => PduType::LEDetonation,
398 PduBody::CreateEntityR(body) => body.body_type(),
399 PduBody::RemoveEntityR(body) => body.body_type(),
400 PduBody::StartResumeR(body) => body.body_type(),
401 PduBody::StopFreezeR(body) => body.body_type(),
402 PduBody::AcknowledgeR(body) => body.body_type(),
403 PduBody::ActionRequestR(body) => body.body_type(),
404 PduBody::ActionResponseR(body) => body.body_type(),
405 PduBody::DataQueryR(body) => body.body_type(),
406 PduBody::SetDataR(body) => body.body_type(),
407 PduBody::DataR(body) => body.body_type(),
408 PduBody::EventReportR(body) => body.body_type(),
409 PduBody::CommentR(body) => body.body_type(),
410 PduBody::RecordR(body) => body.body_type(),
411 PduBody::SetRecordR(body) => body.body_type(),
412 PduBody::RecordQueryR(body) => body.body_type(),
413 PduBody::CollisionElastic(body) => body.body_type(),
414 PduBody::EntityStateUpdate(body) => body.body_type(),
415 PduBody::DirectedEnergyFire => PduType::DirectedEnergyFire,
416 PduBody::EntityDamageStatus => PduType::EntityDamageStatus,
417 PduBody::InformationOperationsAction => PduType::InformationOperationsAction,
418 PduBody::InformationOperationsReport => PduType::InformationOperationsReport,
419 PduBody::Attribute(body) => body.body_type(),
420 }
421 }
422}
423
424impl Interaction for PduBody {
425 #[allow(clippy::match_same_arms)]
426 fn originator(&self) -> Option<&EntityId> {
427 match self {
428 PduBody::Other(body) => body.originator(),
429 PduBody::EntityState(body) => body.originator(),
430 PduBody::Fire(body) => body.originator(),
431 PduBody::Detonation(body) => body.originator(),
432 PduBody::Collision(body) => body.originator(),
433 PduBody::ServiceRequest(body) => body.originator(),
434 PduBody::ResupplyOffer(body) => body.originator(),
435 PduBody::ResupplyReceived(body) => body.originator(),
436 PduBody::ResupplyCancel(body) => body.originator(),
437 PduBody::RepairComplete(body) => body.originator(),
438 PduBody::RepairResponse(body) => body.originator(),
439 PduBody::CreateEntity(body) => body.originator(),
440 PduBody::RemoveEntity(body) => body.originator(),
441 PduBody::StartResume(body) => body.originator(),
442 PduBody::StopFreeze(body) => body.originator(),
443 PduBody::Acknowledge(body) => body.originator(),
444 PduBody::ActionRequest(body) => body.originator(),
445 PduBody::ActionResponse(body) => body.originator(),
446 PduBody::DataQuery(body) => body.originator(),
447 PduBody::SetData(body) => body.originator(),
448 PduBody::Data(body) => body.originator(),
449 PduBody::EventReport(body) => body.originator(),
450 PduBody::Comment(body) => body.originator(),
451 PduBody::ElectromagneticEmission(body) => body.originator(),
452 PduBody::Designator(body) => body.originator(),
453 PduBody::Transmitter(body) => body.originator(),
454 PduBody::Signal(body) => body.originator(),
455 PduBody::Receiver(body) => body.originator(),
456 PduBody::IFF(body) => body.originator(),
457 PduBody::UnderwaterAcoustic(body) => body.originator(),
458 PduBody::SupplementalEmissionEntityState(body) => body.originator(),
459 PduBody::IntercomSignal => None,
460 PduBody::IntercomControl => None,
461 PduBody::AggregateState(body) => body.originator(),
462 PduBody::IsGroupOf(body) => body.originator(),
463 PduBody::TransferOwnership(body) => body.originator(),
464 PduBody::IsPartOf(body) => body.originator(),
465 PduBody::MinefieldState => None,
466 PduBody::MinefieldQuery => None,
467 PduBody::MinefieldData => None,
468 PduBody::MinefieldResponseNACK => None,
469 PduBody::EnvironmentalProcess => None,
470 PduBody::GriddedData => None,
471 PduBody::PointObjectState => None,
472 PduBody::LinearObjectState => None,
473 PduBody::ArealObjectState => None,
474 PduBody::TSPI => None,
475 PduBody::Appearance => None,
476 PduBody::ArticulatedParts => None,
477 PduBody::LEFire => None,
478 PduBody::LEDetonation => None,
479 PduBody::CreateEntityR(body) => body.originator(),
480 PduBody::RemoveEntityR(body) => body.originator(),
481 PduBody::StartResumeR(body) => body.originator(),
482 PduBody::StopFreezeR(body) => body.originator(),
483 PduBody::AcknowledgeR(body) => body.originator(),
484 PduBody::ActionRequestR(body) => body.originator(),
485 PduBody::ActionResponseR(body) => body.originator(),
486 PduBody::DataQueryR(body) => body.originator(),
487 PduBody::SetDataR(body) => body.originator(),
488 PduBody::DataR(body) => body.originator(),
489 PduBody::EventReportR(body) => body.originator(),
490 PduBody::CommentR(body) => body.originator(),
491 PduBody::RecordR(body) => body.originator(),
492 PduBody::SetRecordR(body) => body.originator(),
493 PduBody::RecordQueryR(body) => body.originator(),
494 PduBody::CollisionElastic(body) => body.originator(),
495 PduBody::EntityStateUpdate(body) => body.originator(),
496 PduBody::DirectedEnergyFire => None,
497 PduBody::EntityDamageStatus => None,
498 PduBody::InformationOperationsAction => None,
499 PduBody::InformationOperationsReport => None,
500 PduBody::Attribute(body) => body.originator(),
501 }
502 }
503
504 #[allow(clippy::match_same_arms)]
505 fn receiver(&self) -> Option<&EntityId> {
506 match self {
507 PduBody::Other(body) => body.receiver(),
508 PduBody::EntityState(body) => body.receiver(),
509 PduBody::Fire(body) => body.receiver(),
510 PduBody::Detonation(body) => body.receiver(),
511 PduBody::Collision(body) => body.receiver(),
512 PduBody::ServiceRequest(body) => body.receiver(),
513 PduBody::ResupplyOffer(body) => body.receiver(),
514 PduBody::ResupplyReceived(body) => body.receiver(),
515 PduBody::ResupplyCancel(body) => body.receiver(),
516 PduBody::RepairComplete(body) => body.receiver(),
517 PduBody::RepairResponse(body) => body.receiver(),
518 PduBody::CreateEntity(body) => body.receiver(),
519 PduBody::RemoveEntity(body) => body.receiver(),
520 PduBody::StartResume(body) => body.receiver(),
521 PduBody::StopFreeze(body) => body.receiver(),
522 PduBody::Acknowledge(body) => body.receiver(),
523 PduBody::ActionRequest(body) => body.receiver(),
524 PduBody::ActionResponse(body) => body.receiver(),
525 PduBody::DataQuery(body) => body.receiver(),
526 PduBody::SetData(body) => body.receiver(),
527 PduBody::Data(body) => body.receiver(),
528 PduBody::EventReport(body) => body.receiver(),
529 PduBody::Comment(body) => body.receiver(),
530 PduBody::ElectromagneticEmission(body) => body.receiver(),
531 PduBody::Designator(body) => body.receiver(),
532 PduBody::Transmitter(body) => body.receiver(),
533 PduBody::Signal(body) => body.receiver(),
534 PduBody::Receiver(body) => body.receiver(),
535 PduBody::IFF(body) => body.receiver(),
536 PduBody::UnderwaterAcoustic(body) => body.receiver(),
537 PduBody::SupplementalEmissionEntityState(body) => body.receiver(),
538 PduBody::IntercomSignal => None,
539 PduBody::IntercomControl => None,
540 PduBody::AggregateState(body) => body.receiver(),
541 PduBody::IsGroupOf(body) => body.receiver(),
542 PduBody::TransferOwnership(body) => body.receiver(),
543 PduBody::IsPartOf(body) => body.receiver(),
544 PduBody::MinefieldState => None,
545 PduBody::MinefieldQuery => None,
546 PduBody::MinefieldData => None,
547 PduBody::MinefieldResponseNACK => None,
548 PduBody::EnvironmentalProcess => None,
549 PduBody::GriddedData => None,
550 PduBody::PointObjectState => None,
551 PduBody::LinearObjectState => None,
552 PduBody::ArealObjectState => None,
553 PduBody::TSPI => None,
554 PduBody::Appearance => None,
555 PduBody::ArticulatedParts => None,
556 PduBody::LEFire => None,
557 PduBody::LEDetonation => None,
558 PduBody::CreateEntityR(body) => body.receiver(),
559 PduBody::RemoveEntityR(body) => body.receiver(),
560 PduBody::StartResumeR(body) => body.receiver(),
561 PduBody::StopFreezeR(body) => body.receiver(),
562 PduBody::AcknowledgeR(body) => body.receiver(),
563 PduBody::ActionRequestR(body) => body.receiver(),
564 PduBody::ActionResponseR(body) => body.receiver(),
565 PduBody::DataQueryR(body) => body.receiver(),
566 PduBody::SetDataR(body) => body.receiver(),
567 PduBody::DataR(body) => body.receiver(),
568 PduBody::EventReportR(body) => body.receiver(),
569 PduBody::CommentR(body) => body.receiver(),
570 PduBody::RecordR(body) => body.receiver(),
571 PduBody::SetRecordR(body) => body.receiver(),
572 PduBody::RecordQueryR(body) => body.receiver(),
573 PduBody::CollisionElastic(body) => body.receiver(),
574 PduBody::EntityStateUpdate(body) => body.receiver(),
575 PduBody::DirectedEnergyFire => None,
576 PduBody::EntityDamageStatus => None,
577 PduBody::InformationOperationsAction => None,
578 PduBody::InformationOperationsReport => None,
579 PduBody::Attribute(body) => body.receiver(),
580 }
581 }
582}
583
584impl From<PduType> for ProtocolFamily {
585 #[allow(clippy::match_same_arms)]
586 fn from(pdu_type: PduType) -> Self {
587 match pdu_type {
588 PduType::Other => ProtocolFamily::Other,
589 PduType::EntityState => ProtocolFamily::EntityInformationInteraction,
590 PduType::Fire => ProtocolFamily::Warfare,
591 PduType::Detonation => ProtocolFamily::Warfare,
592 PduType::Collision => ProtocolFamily::EntityInformationInteraction,
593 PduType::ServiceRequest => ProtocolFamily::Logistics,
594 PduType::ResupplyOffer => ProtocolFamily::Logistics,
595 PduType::ResupplyReceived => ProtocolFamily::Logistics,
596 PduType::ResupplyCancel => ProtocolFamily::Logistics,
597 PduType::RepairComplete => ProtocolFamily::Logistics,
598 PduType::RepairResponse => ProtocolFamily::Logistics,
599 PduType::CreateEntity => ProtocolFamily::SimulationManagement,
600 PduType::RemoveEntity => ProtocolFamily::SimulationManagement,
601 PduType::StartResume => ProtocolFamily::SimulationManagement,
602 PduType::StopFreeze => ProtocolFamily::SimulationManagement,
603 PduType::Acknowledge => ProtocolFamily::SimulationManagement,
604 PduType::ActionRequest => ProtocolFamily::SimulationManagement,
605 PduType::ActionResponse => ProtocolFamily::SimulationManagement,
606 PduType::DataQuery => ProtocolFamily::SimulationManagement,
607 PduType::SetData => ProtocolFamily::SimulationManagement,
608 PduType::Data => ProtocolFamily::SimulationManagement,
609 PduType::EventReport => ProtocolFamily::SimulationManagement,
610 PduType::Comment => ProtocolFamily::SimulationManagement,
611 PduType::ElectromagneticEmission => ProtocolFamily::DistributedEmissionRegeneration,
612 PduType::Designator => ProtocolFamily::DistributedEmissionRegeneration,
613 PduType::Transmitter => ProtocolFamily::RadioCommunications,
614 PduType::Signal => ProtocolFamily::RadioCommunications,
615 PduType::Receiver => ProtocolFamily::RadioCommunications,
616 PduType::IFF => ProtocolFamily::DistributedEmissionRegeneration,
617 PduType::UnderwaterAcoustic => ProtocolFamily::DistributedEmissionRegeneration,
618 PduType::SupplementalEmissionEntityState => {
619 ProtocolFamily::DistributedEmissionRegeneration
620 }
621 PduType::IntercomSignal => ProtocolFamily::RadioCommunications,
622 PduType::IntercomControl => ProtocolFamily::RadioCommunications,
623 PduType::AggregateState => ProtocolFamily::EntityManagement,
624 PduType::IsGroupOf => ProtocolFamily::EntityManagement,
625 PduType::TransferOwnership => ProtocolFamily::EntityManagement,
626 PduType::IsPartOf => ProtocolFamily::EntityManagement,
627 PduType::MinefieldState => ProtocolFamily::Minefield,
628 PduType::MinefieldQuery => ProtocolFamily::Minefield,
629 PduType::MinefieldData => ProtocolFamily::Minefield,
630 PduType::MinefieldResponseNACK => ProtocolFamily::Minefield,
631 PduType::EnvironmentalProcess => ProtocolFamily::SyntheticEnvironment,
632 PduType::GriddedData => ProtocolFamily::SyntheticEnvironment,
633 PduType::PointObjectState => ProtocolFamily::SyntheticEnvironment,
634 PduType::LinearObjectState => ProtocolFamily::SyntheticEnvironment,
635 PduType::ArealObjectState => ProtocolFamily::SyntheticEnvironment,
636 PduType::TSPI => ProtocolFamily::LiveEntity_LE_InformationInteraction,
637 PduType::Appearance => ProtocolFamily::LiveEntity_LE_InformationInteraction,
638 PduType::ArticulatedParts => ProtocolFamily::LiveEntity_LE_InformationInteraction,
639 PduType::LEFire => ProtocolFamily::LiveEntity_LE_InformationInteraction,
640 PduType::LEDetonation => ProtocolFamily::LiveEntity_LE_InformationInteraction,
641 PduType::CreateEntityR => ProtocolFamily::SimulationManagementWithReliability,
642 PduType::RemoveEntityR => ProtocolFamily::SimulationManagementWithReliability,
643 PduType::StartResumeR => ProtocolFamily::SimulationManagementWithReliability,
644 PduType::StopFreezeR => ProtocolFamily::SimulationManagementWithReliability,
645 PduType::AcknowledgeR => ProtocolFamily::SimulationManagementWithReliability,
646 PduType::ActionRequestR => ProtocolFamily::SimulationManagementWithReliability,
647 PduType::ActionResponseR => ProtocolFamily::SimulationManagementWithReliability,
648 PduType::DataQueryR => ProtocolFamily::SimulationManagementWithReliability,
649 PduType::SetDataR => ProtocolFamily::SimulationManagementWithReliability,
650 PduType::DataR => ProtocolFamily::SimulationManagementWithReliability,
651 PduType::EventReportR => ProtocolFamily::SimulationManagementWithReliability,
652 PduType::CommentR => ProtocolFamily::SimulationManagementWithReliability,
653 PduType::RecordR => ProtocolFamily::SimulationManagementWithReliability,
654 PduType::SetRecordR => ProtocolFamily::SimulationManagementWithReliability,
655 PduType::RecordQueryR => ProtocolFamily::SimulationManagementWithReliability,
656 PduType::CollisionElastic => ProtocolFamily::EntityInformationInteraction,
657 PduType::EntityStateUpdate => ProtocolFamily::EntityInformationInteraction,
658 PduType::DirectedEnergyFire => ProtocolFamily::Warfare,
659 PduType::EntityDamageStatus => ProtocolFamily::Warfare,
660 PduType::InformationOperationsAction => ProtocolFamily::InformationOperations,
661 PduType::InformationOperationsReport => ProtocolFamily::InformationOperations,
662 PduType::Attribute => ProtocolFamily::EntityInformationInteraction,
663 PduType::Unspecified(unspecified_value) => {
664 ProtocolFamily::Unspecified(unspecified_value)
665 }
666 }
667 }
668}
669
670#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
672#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
673pub struct SimulationAddress {
674 pub site_id: u16,
675 pub application_id: u16,
676}
677
678impl SimulationAddress {
679 #[must_use]
680 pub fn new(site_id: u16, application_id: u16) -> Self {
681 SimulationAddress {
682 site_id,
683 application_id,
684 }
685 }
686}
687
688impl Default for SimulationAddress {
689 fn default() -> Self {
690 Self {
691 site_id: NO_SITE,
692 application_id: NO_APPLIC,
693 }
694 }
695}
696
697impl Display for SimulationAddress {
698 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
699 write!(f, "{}:{}", self.site_id, self.application_id)
700 }
701}
702
703#[allow(clippy::get_first)]
704impl TryFrom<&[&str]> for SimulationAddress {
705 type Error = DisError;
706
707 fn try_from(value: &[&str]) -> Result<Self, Self::Error> {
708 const NUM_DIGITS: usize = 2;
709 if value.len() != NUM_DIGITS {
710 return Err(DisError::ParseError(format!(
711 "SimulationAddress string pattern does not contain precisely {NUM_DIGITS} digits"
712 )));
713 }
714 Ok(Self {
715 site_id: value
716 .get(0)
717 .expect("Impossible - checked for correct number of digits")
718 .parse::<u16>()
719 .map_err(|_| DisError::ParseError("Invalid site id digit".to_string()))?,
720 application_id: value
721 .get(1)
722 .expect("Impossible - checked for correct number of digits")
723 .parse::<u16>()
724 .map_err(|_| DisError::ParseError("Invalid application id digit".to_string()))?,
725 })
726 }
727}
728
729impl FromStr for SimulationAddress {
730 type Err = DisError;
731
732 fn from_str(s: &str) -> Result<Self, Self::Err> {
733 let ss = s.split(':').collect::<Vec<&str>>();
734 Self::try_from(ss.as_slice())
735 }
736}
737
738impl TryFrom<&str> for SimulationAddress {
739 type Error = DisError;
740
741 fn try_from(value: &str) -> Result<Self, Self::Error> {
742 SimulationAddress::from_str(value)
743 }
744}
745
746impl TryFrom<String> for SimulationAddress {
747 type Error = DisError;
748
749 fn try_from(value: String) -> Result<Self, Self::Error> {
750 TryFrom::<&str>::try_from(&value)
751 }
752}
753
754#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
757#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
758pub struct EntityId {
759 pub simulation_address: SimulationAddress,
760 pub entity_id: u16,
761}
762
763impl EntityId {
764 #[must_use]
765 pub fn new(site_id: u16, application_id: u16, entity_id: u16) -> Self {
766 Self {
767 simulation_address: SimulationAddress {
768 site_id,
769 application_id,
770 },
771 entity_id,
772 }
773 }
774
775 #[must_use]
776 pub fn new_sim_address(simulation_address: SimulationAddress, entity_id: u16) -> Self {
777 Self {
778 simulation_address,
779 entity_id,
780 }
781 }
782
783 #[must_use]
784 pub fn new_simulation_identifier(simulation_address: SimulationAddress) -> Self {
785 Self {
786 simulation_address,
787 entity_id: NO_ENTITY,
788 }
789 }
790
791 #[must_use]
792 #[allow(clippy::cast_possible_truncation)]
793 pub fn record_length(&self) -> u16 {
794 SIX_OCTETS as u16
795 }
796}
797
798impl Default for EntityId {
799 fn default() -> Self {
800 Self {
801 simulation_address: SimulationAddress::default(),
802 entity_id: NO_ENTITY,
803 }
804 }
805}
806
807impl Display for EntityId {
808 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
809 write!(f, "{}:{}", self.simulation_address, self.entity_id)
810 }
811}
812
813#[allow(clippy::get_first)]
814impl FromStr for EntityId {
815 type Err = DisError;
816
817 fn from_str(s: &str) -> Result<Self, Self::Err> {
818 const NUM_DIGITS: usize = 3;
819 let mut ss = s.split(':').collect::<Vec<&str>>();
820 if ss.len() != NUM_DIGITS {
821 return Err(DisError::ParseError(format!(
822 "EntityId string pattern does not contain precisely {NUM_DIGITS} digits"
823 )));
824 }
825 let entity_id = ss
826 .pop()
827 .expect("Impossible - checked for correct number of digits")
828 .parse::<u16>()
829 .map_err(|_| DisError::ParseError("Invalid entity id digit".to_string()))?;
830 Ok(Self {
831 simulation_address: ss.as_slice().try_into()?,
832 entity_id,
833 })
834 }
835}
836
837impl TryFrom<&str> for EntityId {
838 type Error = DisError;
839
840 fn try_from(value: &str) -> Result<Self, Self::Error> {
841 EntityId::from_str(value)
842 }
843}
844
845impl TryFrom<String> for EntityId {
846 type Error = DisError;
847
848 fn try_from(value: String) -> Result<Self, Self::Error> {
849 TryFrom::<&str>::try_from(&value)
850 }
851}
852
853#[derive(Copy, Clone, Debug, PartialEq)]
854#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
855pub struct EventId {
856 pub simulation_address: SimulationAddress,
857 pub event_id: u16,
858}
859
860impl EventId {
861 #[must_use]
862 pub fn new(site_id: u16, application_id: u16, event_id: u16) -> Self {
863 Self {
864 simulation_address: SimulationAddress {
865 site_id,
866 application_id,
867 },
868 event_id,
869 }
870 }
871
872 #[must_use]
873 pub fn new_sim_address(simulation_address: SimulationAddress, event_id: u16) -> Self {
874 Self {
875 simulation_address,
876 event_id,
877 }
878 }
879}
880
881impl Default for EventId {
882 fn default() -> Self {
883 Self {
884 simulation_address: SimulationAddress::default(),
885 event_id: NO_ENTITY,
886 }
887 }
888}
889
890impl Display for EventId {
891 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
892 write!(f, "{}:{}", self.simulation_address, self.event_id)
893 }
894}
895
896#[allow(clippy::get_first)]
897impl FromStr for EventId {
898 type Err = DisError;
899
900 fn from_str(s: &str) -> Result<Self, Self::Err> {
901 const NUM_DIGITS: usize = 3;
902 let mut ss = s.split(':').collect::<Vec<&str>>();
903 if ss.len() != NUM_DIGITS {
904 return Err(DisError::ParseError(format!(
905 "EventId string pattern does not contain precisely {NUM_DIGITS} digits"
906 )));
907 }
908 let event_id = ss
909 .pop()
910 .expect("Impossible - checked for correct number of digits")
911 .parse::<u16>()
912 .map_err(|_| DisError::ParseError("Invalid event id digit".to_string()))?;
913 Ok(Self {
914 simulation_address: ss.as_slice().try_into()?,
915 event_id,
916 })
917 }
918}
919
920impl TryFrom<&str> for EventId {
921 type Error = DisError;
922
923 fn try_from(value: &str) -> Result<Self, Self::Error> {
924 EventId::from_str(value)
925 }
926}
927
928impl TryFrom<String> for EventId {
929 type Error = DisError;
930
931 fn try_from(value: String) -> Result<Self, Self::Error> {
932 TryFrom::<&str>::try_from(&value)
933 }
934}
935
936#[derive(Copy, Clone, Default, Debug, PartialEq)]
939#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
940pub struct VectorF32 {
941 pub first_vector_component: f32,
942 pub second_vector_component: f32,
943 pub third_vector_component: f32,
944}
945
946impl VectorF32 {
947 #[must_use]
948 pub fn new(first: f32, second: f32, third: f32) -> Self {
949 VectorF32 {
950 first_vector_component: first,
951 second_vector_component: second,
952 third_vector_component: third,
953 }
954 }
955
956 #[must_use]
957 pub fn with_first(mut self, first: f32) -> Self {
958 self.first_vector_component = first;
959 self
960 }
961
962 #[must_use]
963 pub fn with_second(mut self, second: f32) -> Self {
964 self.second_vector_component = second;
965 self
966 }
967
968 #[must_use]
969 pub fn with_third(mut self, third: f32) -> Self {
970 self.third_vector_component = third;
971 self
972 }
973}
974
975#[derive(Copy, Clone, Debug, Default, PartialEq)]
978#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
979pub struct Location {
980 pub x_coordinate: f64,
981 pub y_coordinate: f64,
982 pub z_coordinate: f64,
983}
984
985impl Location {
986 #[must_use]
987 pub fn new(x: f64, y: f64, z: f64) -> Self {
988 Location {
989 x_coordinate: x,
990 y_coordinate: y,
991 z_coordinate: z,
992 }
993 }
994
995 #[must_use]
996 pub fn with_x(mut self, x: f64) -> Self {
997 self.x_coordinate = x;
998 self
999 }
1000
1001 #[must_use]
1002 pub fn with_y(mut self, y: f64) -> Self {
1003 self.y_coordinate = y;
1004 self
1005 }
1006
1007 #[must_use]
1008 pub fn with_z(mut self, z: f64) -> Self {
1009 self.z_coordinate = z;
1010 self
1011 }
1012}
1013
1014#[derive(Copy, Clone, Default, Debug, PartialEq)]
1017#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1018pub struct Orientation {
1019 pub psi: f32,
1020 pub theta: f32,
1021 pub phi: f32,
1022}
1023
1024impl Orientation {
1025 #[must_use]
1026 #[allow(clippy::similar_names)]
1027 pub fn new(psi: f32, theta: f32, phi: f32) -> Self {
1028 Orientation { psi, theta, phi }
1029 }
1030
1031 #[must_use]
1032 pub fn with_psi(mut self, psi: f32) -> Self {
1033 self.psi = psi;
1034 self
1035 }
1036
1037 #[must_use]
1038 pub fn with_theta(mut self, theta: f32) -> Self {
1039 self.theta = theta;
1040 self
1041 }
1042
1043 #[must_use]
1044 pub fn with_phi(mut self, phi: f32) -> Self {
1045 self.phi = phi;
1046 self
1047 }
1048}
1049
1050#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
1052#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1053pub struct EntityType {
1054 pub kind: EntityKind,
1055 pub domain: PlatformDomain,
1056 pub country: Country,
1057 pub category: u8,
1058 pub subcategory: u8,
1059 pub specific: u8,
1060 pub extra: u8,
1061}
1062
1063impl EntityType {
1064 #[must_use]
1065 pub fn with_kind(mut self, kind: EntityKind) -> Self {
1066 self.kind = kind;
1067 self
1068 }
1069
1070 #[must_use]
1071 pub fn with_domain(mut self, domain: PlatformDomain) -> Self {
1072 self.domain = domain;
1073 self
1074 }
1075
1076 #[must_use]
1077 pub fn with_country(mut self, country: Country) -> Self {
1078 self.country = country;
1079 self
1080 }
1081
1082 #[must_use]
1083 pub fn with_category(mut self, category: u8) -> Self {
1084 self.category = category;
1085 self
1086 }
1087
1088 #[must_use]
1089 pub fn with_subcategory(mut self, subcategory: u8) -> Self {
1090 self.subcategory = subcategory;
1091 self
1092 }
1093
1094 #[must_use]
1095 pub fn with_specific(mut self, specific: u8) -> Self {
1096 self.specific = specific;
1097 self
1098 }
1099
1100 #[must_use]
1101 pub fn with_extra(mut self, extra: u8) -> Self {
1102 self.extra = extra;
1103 self
1104 }
1105
1106 #[must_use]
1107 #[allow(clippy::cast_possible_truncation)]
1108 pub fn record_length(&self) -> u16 {
1109 EIGHT_OCTETS as u16
1110 }
1111}
1112
1113impl Display for EntityType {
1114 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1115 write!(
1116 f,
1117 "{}:{}:{}:{}:{}:{}:{}",
1118 u8::from(self.kind),
1119 u8::from(self.domain),
1120 u16::from(self.country),
1121 self.category,
1122 self.subcategory,
1123 self.specific,
1124 self.extra
1125 )
1126 }
1127}
1128
1129#[allow(clippy::get_first)]
1130impl FromStr for EntityType {
1131 type Err = DisError;
1132
1133 fn from_str(s: &str) -> Result<Self, Self::Err> {
1134 const NUM_DIGITS: usize = 7;
1135 let ss = s.split(':').collect::<Vec<&str>>();
1136 if ss.len() != NUM_DIGITS {
1137 return Err(DisError::ParseError(format!(
1138 "EntityType string pattern does not contain precisely {NUM_DIGITS} digits"
1139 )));
1140 }
1141 Ok(Self {
1142 kind: ss
1143 .get(0)
1144 .expect("Impossible - checked for correct number of digits")
1145 .parse::<u8>()
1146 .map_err(|_| DisError::ParseError("Invalid kind digit".to_string()))?
1147 .into(),
1148 domain: ss
1149 .get(1)
1150 .expect("Impossible - checked for correct number of digits")
1151 .parse::<u8>()
1152 .map_err(|_| DisError::ParseError("Invalid domain digit".to_string()))?
1153 .into(),
1154 country: ss
1155 .get(2)
1156 .expect("Impossible - checked for correct number of digits")
1157 .parse::<u16>()
1158 .map_err(|_| DisError::ParseError("Invalid country digit".to_string()))?
1159 .into(),
1160 category: ss
1161 .get(3)
1162 .expect("Impossible - checked for correct number of digits")
1163 .parse::<u8>()
1164 .map_err(|_| DisError::ParseError("Invalid category digit".to_string()))?,
1165 subcategory: ss
1166 .get(4)
1167 .expect("Impossible - checked for correct number of digits")
1168 .parse::<u8>()
1169 .map_err(|_| DisError::ParseError("Invalid subcategory digit".to_string()))?,
1170 specific: ss
1171 .get(5)
1172 .expect("Impossible - checked for correct number of digits")
1173 .parse::<u8>()
1174 .map_err(|_| DisError::ParseError("Invalid specific digit".to_string()))?,
1175 extra: ss
1176 .get(6)
1177 .expect("Impossible - checked for correct number of digits")
1178 .parse::<u8>()
1179 .map_err(|_| DisError::ParseError("Invalid extra digit".to_string()))?,
1180 })
1181 }
1182}
1183
1184impl TryFrom<&str> for EntityType {
1185 type Error = DisError;
1186
1187 fn try_from(value: &str) -> Result<Self, Self::Error> {
1188 EntityType::from_str(value)
1189 }
1190}
1191
1192impl TryFrom<String> for EntityType {
1193 type Error = DisError;
1194
1195 fn try_from(value: String) -> Result<Self, Self::Error> {
1196 TryFrom::<&str>::try_from(&value)
1197 }
1198}
1199
1200#[derive(Clone, Default, Debug, PartialEq)]
1202#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1203pub struct MunitionDescriptor {
1204 pub entity_type: EntityType,
1205 pub warhead: MunitionDescriptorWarhead,
1206 pub fuse: MunitionDescriptorFuse,
1207 pub quantity: u16,
1208 pub rate: u16,
1209}
1210
1211impl MunitionDescriptor {
1212 #[must_use]
1213 pub fn with_entity_type(mut self, entity_type: EntityType) -> Self {
1214 self.entity_type = entity_type;
1215 self
1216 }
1217
1218 #[must_use]
1219 pub fn with_warhead(mut self, warhead: MunitionDescriptorWarhead) -> Self {
1220 self.warhead = warhead;
1221 self
1222 }
1223
1224 #[must_use]
1225 pub fn with_fuse(mut self, fuse: MunitionDescriptorFuse) -> Self {
1226 self.fuse = fuse;
1227 self
1228 }
1229
1230 #[must_use]
1231 pub fn with_quantity(mut self, quantity: u16) -> Self {
1232 self.quantity = quantity;
1233 self
1234 }
1235
1236 #[must_use]
1237 pub fn with_rate(mut self, rate: u16) -> Self {
1238 self.rate = rate;
1239 self
1240 }
1241}
1242
1243#[derive(Clone, Default, Debug, PartialEq)]
1245#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1246pub struct ExplosionDescriptor {
1247 pub entity_type: EntityType,
1248 pub explosive_material: ExplosiveMaterialCategories,
1249 pub explosive_force: f32,
1250}
1251
1252impl ExplosionDescriptor {
1253 #[must_use]
1254 pub fn with_entity_type(mut self, entity_type: EntityType) -> Self {
1255 self.entity_type = entity_type;
1256 self
1257 }
1258
1259 #[must_use]
1260 pub fn with_explosive_material(
1261 mut self,
1262 explosive_material: ExplosiveMaterialCategories,
1263 ) -> Self {
1264 self.explosive_material = explosive_material;
1265 self
1266 }
1267
1268 #[must_use]
1269 pub fn with_explosive_force(mut self, explosive_force: f32) -> Self {
1270 self.explosive_force = explosive_force;
1271 self
1272 }
1273}
1274
1275#[derive(Clone, Default, Debug, PartialEq)]
1277#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1278pub struct ExpendableDescriptor {
1279 pub entity_type: EntityType,
1280}
1281
1282impl ExpendableDescriptor {
1283 #[must_use]
1284 pub fn with_entity_type(mut self, entity_type: EntityType) -> Self {
1285 self.entity_type = entity_type;
1286 self
1287 }
1288}
1289
1290#[derive(Copy, Clone, Default, Debug, PartialEq)]
1292#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1293pub struct ClockTime {
1294 pub hour: i32,
1295 pub time_past_hour: u32,
1296}
1297
1298impl ClockTime {
1299 #[must_use]
1300 pub fn new(hour: i32, time_past_hour: u32) -> Self {
1301 Self {
1302 hour,
1303 time_past_hour,
1304 }
1305 }
1306}
1307
1308#[derive(Clone, Default, Debug, PartialEq)]
1310pub struct DatumSpecification {
1311 pub fixed_datum_records: Vec<FixedDatum>,
1312 pub variable_datum_records: Vec<VariableDatum>,
1313}
1314
1315impl DatumSpecification {
1316 #[must_use]
1317 pub fn new(
1318 fixed_datum_records: Vec<FixedDatum>,
1319 variable_datum_records: Vec<VariableDatum>,
1320 ) -> Self {
1321 Self {
1322 fixed_datum_records,
1323 variable_datum_records,
1324 }
1325 }
1326}
1327
1328pub const FIXED_DATUM_LENGTH: u16 = 8;
1329pub const BASE_VARIABLE_DATUM_LENGTH: u16 = 8;
1330
1331#[derive(Clone, Debug, PartialEq)]
1333#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1334pub struct FixedDatum {
1335 pub datum_id: VariableRecordType,
1336 pub datum_value: u32,
1337}
1338
1339impl FixedDatum {
1340 #[must_use]
1341 pub fn new(datum_id: VariableRecordType, datum_value: u32) -> Self {
1342 Self {
1343 datum_id,
1344 datum_value,
1345 }
1346 }
1347}
1348
1349#[derive(Clone, Debug, PartialEq)]
1351#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1352pub struct VariableDatum {
1353 pub datum_id: VariableRecordType,
1354 pub datum_value: Vec<u8>,
1355}
1356
1357impl VariableDatum {
1358 #[must_use]
1359 pub fn new(datum_id: VariableRecordType, datum_value: Vec<u8>) -> Self {
1360 Self {
1361 datum_id,
1362 datum_value,
1363 }
1364 }
1365}
1366
1367#[derive(Debug)]
1370pub struct PaddedRecordLengths {
1371 pub data_length: usize,
1372 pub padding_length: usize,
1373 pub record_length: usize,
1374}
1375
1376impl PaddedRecordLengths {
1377 #[must_use]
1378 pub fn new(
1379 data_length_bytes: usize,
1380 padding_length_bytes: usize,
1381 record_length_bytes: usize,
1382 ) -> Self {
1383 Self {
1384 data_length: data_length_bytes,
1385 padding_length: padding_length_bytes,
1386 record_length: record_length_bytes,
1387 }
1388 }
1389}
1390
1391pub(crate) fn length_padded_to_num(data_length: usize, pad_to_num: usize) -> PaddedRecordLengths {
1398 let data_remaining = data_length % pad_to_num;
1399 let padding_num = if data_remaining == 0 {
1400 0usize
1401 } else {
1402 pad_to_num - data_remaining
1403 };
1404 let record_length = data_length + padding_num;
1405 assert_eq!(
1406 record_length % pad_to_num,
1407 NO_REMAINDER,
1408 "The length for the data record is not aligned to {pad_to_num} octets. Data length is {data_length} octets."
1409 );
1410
1411 PaddedRecordLengths::new(data_length, padding_num, record_length)
1412}
1413
1414#[derive(Clone, Debug, PartialEq)]
1416#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1417pub enum VariableParameter {
1418 Articulated(ArticulatedPart),
1419 Attached(AttachedPart),
1420 Separation(SeparationParameter),
1421 EntityType(EntityTypeParameter),
1422 EntityAssociation(EntityAssociationParameter),
1423 Unspecified(u8, [u8; FIFTEEN_OCTETS]),
1424}
1425
1426#[derive(Copy, Clone, Debug, Default, PartialEq)]
1428#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1429pub struct ArticulatedPart {
1430 pub change_indicator: u8,
1431 pub attachment_id: u16,
1432 pub type_metric: ArticulatedPartsTypeMetric,
1433 pub type_class: ArticulatedPartsTypeClass,
1434 pub parameter_value: f32,
1435}
1436
1437impl ArticulatedPart {
1438 #[must_use]
1439 pub fn with_change_indicator(mut self, change_indicator: u8) -> Self {
1440 self.change_indicator = change_indicator;
1441 self
1442 }
1443
1444 #[must_use]
1445 pub fn with_attachment_id(mut self, attachment_id: u16) -> Self {
1446 self.attachment_id = attachment_id;
1447 self
1448 }
1449
1450 #[must_use]
1451 pub fn with_type_metric(mut self, type_metric: ArticulatedPartsTypeMetric) -> Self {
1452 self.type_metric = type_metric;
1453 self
1454 }
1455
1456 #[must_use]
1457 pub fn with_type_class(mut self, type_class: ArticulatedPartsTypeClass) -> Self {
1458 self.type_class = type_class;
1459 self
1460 }
1461
1462 #[must_use]
1463 pub fn with_parameter_value(mut self, parameter_value: f32) -> Self {
1464 self.parameter_value = parameter_value;
1465 self
1466 }
1467
1468 #[must_use]
1469 pub fn to_variable_parameter(self) -> VariableParameter {
1470 VariableParameter::Articulated(self)
1471 }
1472}
1473
1474#[derive(Copy, Clone, Debug, Default, PartialEq)]
1476#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1477pub struct AttachedPart {
1478 pub detached_indicator: AttachedPartDetachedIndicator,
1479 pub attachment_id: u16,
1480 pub parameter_type: AttachedParts,
1481 pub attached_part_type: EntityType,
1482}
1483
1484impl AttachedPart {
1485 #[must_use]
1486 pub fn with_detached_indicator(
1487 mut self,
1488 detached_indicator: AttachedPartDetachedIndicator,
1489 ) -> Self {
1490 self.detached_indicator = detached_indicator;
1491 self
1492 }
1493
1494 #[must_use]
1495 pub fn with_attachment_id(mut self, attachment_id: u16) -> Self {
1496 self.attachment_id = attachment_id;
1497 self
1498 }
1499
1500 #[must_use]
1501 pub fn with_parameter_type(mut self, parameter_type: AttachedParts) -> Self {
1502 self.parameter_type = parameter_type;
1503 self
1504 }
1505
1506 #[must_use]
1507 pub fn with_attached_part_type(mut self, attached_part_type: EntityType) -> Self {
1508 self.attached_part_type = attached_part_type;
1509 self
1510 }
1511
1512 #[must_use]
1513 pub fn to_variable_parameter(self) -> VariableParameter {
1514 VariableParameter::Attached(self)
1515 }
1516}
1517
1518#[derive(Copy, Clone, Debug, Default, PartialEq)]
1520#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1521pub struct SeparationParameter {
1522 pub reason: SeparationReasonForSeparation,
1523 pub pre_entity_indicator: SeparationPreEntityIndicator,
1524 pub parent_entity_id: EntityId,
1525 pub station_name: StationName,
1526 pub station_number: u16,
1527}
1528
1529impl SeparationParameter {
1530 #[must_use]
1531 pub fn with_reason(mut self, reason: SeparationReasonForSeparation) -> Self {
1532 self.reason = reason;
1533 self
1534 }
1535
1536 #[must_use]
1537 pub fn with_pre_entity_indicator(
1538 mut self,
1539 pre_entity_indicator: SeparationPreEntityIndicator,
1540 ) -> Self {
1541 self.pre_entity_indicator = pre_entity_indicator;
1542 self
1543 }
1544
1545 #[must_use]
1546 pub fn with_parent_entity_id(mut self, parent_entity_id: EntityId) -> Self {
1547 self.parent_entity_id = parent_entity_id;
1548 self
1549 }
1550
1551 #[must_use]
1552 pub fn with_station_name(mut self, station_name: StationName) -> Self {
1553 self.station_name = station_name;
1554 self
1555 }
1556
1557 #[must_use]
1558 pub fn with_station_number(mut self, station_number: u16) -> Self {
1559 self.station_number = station_number;
1560 self
1561 }
1562
1563 #[must_use]
1564 pub fn to_variable_parameter(self) -> VariableParameter {
1565 VariableParameter::Separation(self)
1566 }
1567}
1568
1569#[derive(Copy, Clone, Debug, Default, PartialEq)]
1571#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1572pub struct EntityTypeParameter {
1573 pub change_indicator: ChangeIndicator,
1574 pub entity_type: EntityType,
1575}
1576
1577impl EntityTypeParameter {
1578 #[must_use]
1579 pub fn with_change_indicator(mut self, change_indicator: ChangeIndicator) -> Self {
1580 self.change_indicator = change_indicator;
1581 self
1582 }
1583
1584 #[must_use]
1585 pub fn with_entity_type(mut self, entity_type: EntityType) -> Self {
1586 self.entity_type = entity_type;
1587 self
1588 }
1589
1590 #[must_use]
1591 pub fn to_variable_parameter(self) -> VariableParameter {
1592 VariableParameter::EntityType(self)
1593 }
1594}
1595
1596#[derive(Copy, Clone, Debug, Default, PartialEq)]
1598#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1599pub struct EntityAssociationParameter {
1600 pub change_indicator: ChangeIndicator,
1601 pub association_status: EntityAssociationAssociationStatus,
1602 pub association_type: EntityAssociationPhysicalAssociationType,
1603 pub entity_id: EntityId,
1604 pub own_station_location: StationName,
1605 pub physical_connection_type: EntityAssociationPhysicalConnectionType,
1606 pub group_member_type: EntityAssociationGroupMemberType,
1607 pub group_number: u16,
1608}
1609
1610impl EntityAssociationParameter {
1611 #[must_use]
1612 pub fn with_change_indicator(mut self, change_indicator: ChangeIndicator) -> Self {
1613 self.change_indicator = change_indicator;
1614 self
1615 }
1616
1617 #[must_use]
1618 pub fn with_association_status(
1619 mut self,
1620 association_status: EntityAssociationAssociationStatus,
1621 ) -> Self {
1622 self.association_status = association_status;
1623 self
1624 }
1625
1626 #[must_use]
1627 pub fn with_association_type(
1628 mut self,
1629 association_type: EntityAssociationPhysicalAssociationType,
1630 ) -> Self {
1631 self.association_type = association_type;
1632 self
1633 }
1634
1635 #[must_use]
1636 pub fn with_entity_id(mut self, entity_id: EntityId) -> Self {
1637 self.entity_id = entity_id;
1638 self
1639 }
1640
1641 #[must_use]
1642 pub fn with_own_station_location(mut self, own_station_location: StationName) -> Self {
1643 self.own_station_location = own_station_location;
1644 self
1645 }
1646
1647 #[must_use]
1648 pub fn with_physical_connection_type(
1649 mut self,
1650 physical_connection_type: EntityAssociationPhysicalConnectionType,
1651 ) -> Self {
1652 self.physical_connection_type = physical_connection_type;
1653 self
1654 }
1655
1656 #[must_use]
1657 pub fn with_group_member_type(
1658 mut self,
1659 group_member_type: EntityAssociationGroupMemberType,
1660 ) -> Self {
1661 self.group_member_type = group_member_type;
1662 self
1663 }
1664
1665 #[must_use]
1666 pub fn with_group_number(mut self, group_number: u16) -> Self {
1667 self.group_number = group_number;
1668 self
1669 }
1670
1671 #[must_use]
1672 pub fn to_variable_parameter(self) -> VariableParameter {
1673 VariableParameter::EntityAssociation(self)
1674 }
1675}
1676
1677#[derive(Copy, Clone, Default, Debug, PartialEq)]
1679#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1680pub struct BeamData {
1681 pub azimuth_center: f32,
1682 pub azimuth_sweep: f32,
1683 pub elevation_center: f32,
1684 pub elevation_sweep: f32,
1685 pub sweep_sync: f32,
1686}
1687
1688impl BeamData {
1689 #[must_use]
1690 pub fn new() -> Self {
1691 Self::default()
1692 }
1693
1694 #[must_use]
1695 pub fn with_azimuth_center(mut self, azimuth_center: f32) -> Self {
1696 self.azimuth_center = azimuth_center;
1697 self
1698 }
1699
1700 #[must_use]
1701 pub fn with_azimuth_sweep(mut self, azimuth_sweep: f32) -> Self {
1702 self.azimuth_sweep = azimuth_sweep;
1703 self
1704 }
1705
1706 #[must_use]
1707 pub fn with_elevation_center(mut self, elevation_center: f32) -> Self {
1708 self.elevation_center = elevation_center;
1709 self
1710 }
1711
1712 #[must_use]
1713 pub fn with_elevation_sweep(mut self, elevation_sweep: f32) -> Self {
1714 self.elevation_sweep = elevation_sweep;
1715 self
1716 }
1717
1718 #[must_use]
1719 pub fn with_sweep_sync(mut self, sweep_sync: f32) -> Self {
1720 self.sweep_sync = sweep_sync;
1721 self
1722 }
1723}
1724
1725pub const SUPPLY_QUANTITY_RECORD_LENGTH: u16 = 12;
1726
1727#[derive(Clone, Debug, Default, PartialEq)]
1729#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1730pub struct SupplyQuantity {
1731 pub supply_type: EntityType,
1732 pub quantity: f32,
1733}
1734
1735impl SupplyQuantity {
1736 #[must_use]
1737 pub fn with_supply_type(mut self, supply_type: EntityType) -> Self {
1738 self.supply_type = supply_type;
1739 self
1740 }
1741
1742 #[must_use]
1743 pub fn with_quantity(mut self, quantity: f32) -> Self {
1744 self.quantity = quantity;
1745 self
1746 }
1747}
1748
1749pub const BASE_RECORD_SPEC_RECORD_LENGTH: u16 = 16;
1750
1751#[derive(Clone, Debug, Default, PartialEq)]
1753#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1754pub struct RecordSpecification {
1755 pub record_sets: Vec<RecordSet>,
1756}
1757
1758impl RecordSpecification {
1759 #[must_use]
1760 pub fn with_record_set(mut self, record: RecordSet) -> Self {
1761 self.record_sets.push(record);
1762 self
1763 }
1764
1765 #[must_use]
1766 pub fn with_record_sets(mut self, records: Vec<RecordSet>) -> Self {
1767 self.record_sets = records;
1768 self
1769 }
1770}
1771
1772#[derive(Clone, Debug, Default, PartialEq)]
1774#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1775pub struct RecordSet {
1776 pub record_id: VariableRecordType,
1777 pub record_serial_number: u32,
1778 pub record_length_bytes: u16,
1779 pub records: Vec<Vec<u8>>,
1780}
1781
1782impl RecordSet {
1783 #[must_use]
1784 pub fn with_record_id(mut self, record_id: VariableRecordType) -> Self {
1785 self.record_id = record_id;
1786 self
1787 }
1788
1789 #[must_use]
1790 pub fn with_record_serial_number(mut self, record_serial_number: u32) -> Self {
1791 self.record_serial_number = record_serial_number;
1792 self
1793 }
1794
1795 #[must_use]
1800 #[allow(clippy::cast_possible_truncation)]
1801 pub fn with_record(mut self, record: Vec<u8>) -> Self {
1802 self.record_length_bytes = record.len() as u16;
1803 self.records.push(record);
1804 self
1805 }
1806
1807 #[must_use]
1810 pub fn with_records(mut self, records: Vec<Vec<u8>>) -> Self {
1811 self.record_length_bytes = if let Some(record) = records.first() {
1812 record.len()
1813 } else {
1814 0
1815 } as u16;
1816 self.records = records;
1817 self
1818 }
1819}
1820
1821#[cfg(test)]
1822mod tests {
1823 use super::*;
1824
1825 const ENTITY_TYPE_STR: &str = "0:1:2:3:4:5:6";
1826 const ENTITY_TYPE_STR_INVALID: &str = "0,1,2,3,4,5,6";
1827 const ENTITY_TYPE_STR_INVALID_EXTRA: &str = "0:1:2:3:4:5:six";
1828 const ENTITY_TYPE_STR_NOT_SEVEN_DIGITS: &str = "0:1:2:3:4:5";
1829 const ENTITY_TYPE: EntityType = EntityType {
1830 kind: EntityKind::Other,
1831 domain: PlatformDomain::Land,
1832 country: Country::Albania_ALB_,
1833 category: 3,
1834 subcategory: 4,
1835 specific: 5,
1836 extra: 6,
1837 };
1838 const SIMULATION_ADDRESS_STR: &str = "0:1";
1839 const SIMULATION_ADDRESS_STR_INVALID: &str = "0,1";
1840 const SIMULATION_ADDRESS_STR_INVALID_APPLICATION_ID: &str = "0:one";
1841 const SIMULATION_ADDRESS_STR_NOT_TWO_DIGITS: &str = "0";
1842 const SIMULATION_ADDRESS: SimulationAddress = SimulationAddress {
1843 site_id: 0,
1844 application_id: 1,
1845 };
1846 const ENTITY_ID_STR: &str = "0:1:2";
1847 const ENTITY_ID_STR_INVALID: &str = "0,1,2";
1848 const ENTITY_ID_STR_INVALID_ENTITY_ID: &str = "0:1:two";
1849 const ENTITY_ID_STR_NOT_THREE_DIGITS: &str = "0:1";
1850 const ENTITY_ID: EntityId = EntityId {
1851 simulation_address: SIMULATION_ADDRESS,
1852 entity_id: 2,
1853 };
1854 const EVENT_ID_STR: &str = "0:1:2";
1855 const EVENT_ID_STR_INVALID: &str = "0,1,2";
1856 const EVENT_ID_STR_INVALID_EVENT_ID: &str = "0:1:two";
1857 const EVENT_ID_STR_NOT_THREE_DIGITS: &str = "0:1";
1858 const EVENT_ID: EventId = EventId {
1859 simulation_address: SIMULATION_ADDRESS,
1860 event_id: 2,
1861 };
1862
1863 #[test]
1864 fn entity_type_display() {
1865 assert_eq!(ENTITY_TYPE_STR, ENTITY_TYPE.to_string());
1866 }
1867
1868 #[test]
1869 fn entity_type_from_str() {
1870 assert_eq!(EntityType::from_str(ENTITY_TYPE_STR).unwrap(), ENTITY_TYPE);
1871 let err = EntityType::from_str(ENTITY_TYPE_STR_INVALID);
1872 assert!(err.is_err());
1873 assert!(matches!(err, Err(DisError::ParseError(_))));
1874 assert_eq!(
1875 err.unwrap_err().to_string(),
1876 "EntityType string pattern does not contain precisely 7 digits"
1877 );
1878 let err = EntityType::from_str(ENTITY_TYPE_STR_INVALID_EXTRA);
1879 assert!(err.is_err());
1880 assert!(matches!(err, Err(DisError::ParseError(_))));
1881 assert_eq!(err.unwrap_err().to_string(), "Invalid extra digit");
1882 }
1883
1884 #[test]
1885 fn entity_type_from_str_not_seven_digits() {
1886 let err = EntityType::from_str(ENTITY_TYPE_STR_NOT_SEVEN_DIGITS);
1887 assert!(err.is_err());
1888 assert!(matches!(err, Err(DisError::ParseError(_))));
1889 assert_eq!(
1890 err.unwrap_err().to_string(),
1891 "EntityType string pattern does not contain precisely 7 digits"
1892 );
1893 }
1894
1895 #[test]
1896 fn entity_type_try_from_str() {
1897 assert_eq!(
1898 TryInto::<EntityType>::try_into(ENTITY_TYPE_STR).unwrap(),
1899 ENTITY_TYPE
1900 );
1901 let err = TryInto::<EntityType>::try_into(ENTITY_TYPE_STR_INVALID);
1902 assert!(err.is_err());
1903 assert!(matches!(err, Err(DisError::ParseError(_))));
1904 assert_eq!(
1905 err.unwrap_err().to_string(),
1906 "EntityType string pattern does not contain precisely 7 digits"
1907 );
1908 let err = TryInto::<EntityType>::try_into(ENTITY_TYPE_STR_INVALID_EXTRA);
1909 assert!(err.is_err());
1910 assert!(matches!(err, Err(DisError::ParseError(_))));
1911 assert_eq!(err.unwrap_err().to_string(), "Invalid extra digit");
1912 }
1913
1914 #[test]
1915 fn entity_type_try_from_string() {
1916 assert_eq!(
1917 TryInto::<EntityType>::try_into(ENTITY_TYPE_STR.to_string()).unwrap(),
1918 ENTITY_TYPE
1919 );
1920 let err = TryInto::<EntityType>::try_into(ENTITY_TYPE_STR_INVALID.to_string());
1921 assert!(err.is_err());
1922 assert!(matches!(err, Err(DisError::ParseError(_))));
1923 assert_eq!(
1924 err.unwrap_err().to_string(),
1925 "EntityType string pattern does not contain precisely 7 digits"
1926 );
1927 let err = TryInto::<EntityType>::try_into(ENTITY_TYPE_STR_INVALID_EXTRA.to_string());
1928 assert!(err.is_err());
1929 assert!(matches!(err, Err(DisError::ParseError(_))));
1930 assert_eq!(err.unwrap_err().to_string(), "Invalid extra digit");
1931 }
1932
1933 #[test]
1934 fn simulation_address_display() {
1935 assert_eq!(SIMULATION_ADDRESS_STR, SIMULATION_ADDRESS.to_string());
1936 }
1937
1938 #[test]
1939 fn simulation_address_from_str() {
1940 assert_eq!(
1941 SimulationAddress::from_str(SIMULATION_ADDRESS_STR).unwrap(),
1942 SIMULATION_ADDRESS
1943 );
1944 let err = SimulationAddress::from_str(SIMULATION_ADDRESS_STR_INVALID);
1945 assert!(err.is_err());
1946 assert!(matches!(err, Err(DisError::ParseError(_))));
1947 assert_eq!(
1948 err.unwrap_err().to_string(),
1949 "SimulationAddress string pattern does not contain precisely 2 digits"
1950 );
1951 let err = SimulationAddress::from_str(SIMULATION_ADDRESS_STR_INVALID_APPLICATION_ID);
1952 assert!(err.is_err());
1953 assert!(matches!(err, Err(DisError::ParseError(_))));
1954 assert_eq!(err.unwrap_err().to_string(), "Invalid application id digit");
1955 }
1956
1957 #[test]
1958 fn simulation_address_from_str_not_two_digits() {
1959 let err = SimulationAddress::from_str(SIMULATION_ADDRESS_STR_NOT_TWO_DIGITS);
1960 assert!(err.is_err());
1961 assert!(matches!(err, Err(DisError::ParseError(_))));
1962 assert_eq!(
1963 err.unwrap_err().to_string(),
1964 "SimulationAddress string pattern does not contain precisely 2 digits"
1965 );
1966 }
1967
1968 #[test]
1969 fn simulation_address_try_from_str() {
1970 assert_eq!(
1971 TryInto::<SimulationAddress>::try_into(SIMULATION_ADDRESS_STR).unwrap(),
1972 SIMULATION_ADDRESS
1973 );
1974 let err = TryInto::<SimulationAddress>::try_into(SIMULATION_ADDRESS_STR_INVALID);
1975 assert!(err.is_err());
1976 assert!(matches!(err, Err(DisError::ParseError(_))));
1977 assert_eq!(
1978 err.unwrap_err().to_string(),
1979 "SimulationAddress string pattern does not contain precisely 2 digits"
1980 );
1981 let err =
1982 TryInto::<SimulationAddress>::try_into(SIMULATION_ADDRESS_STR_INVALID_APPLICATION_ID);
1983 assert!(err.is_err());
1984 assert!(matches!(err, Err(DisError::ParseError(_))));
1985 assert_eq!(err.unwrap_err().to_string(), "Invalid application id digit");
1986 }
1987
1988 #[test]
1989 fn simulation_address_try_from_string() {
1990 assert_eq!(
1991 TryInto::<SimulationAddress>::try_into(SIMULATION_ADDRESS_STR.to_string()).unwrap(),
1992 SIMULATION_ADDRESS
1993 );
1994 let err =
1995 TryInto::<SimulationAddress>::try_into(SIMULATION_ADDRESS_STR_INVALID.to_string());
1996 assert!(err.is_err());
1997 assert!(matches!(err, Err(DisError::ParseError(_))));
1998 assert_eq!(
1999 err.unwrap_err().to_string(),
2000 "SimulationAddress string pattern does not contain precisely 2 digits"
2001 );
2002 let err = TryInto::<SimulationAddress>::try_into(
2003 SIMULATION_ADDRESS_STR_INVALID_APPLICATION_ID.to_string(),
2004 );
2005 assert!(err.is_err());
2006 assert!(matches!(err, Err(DisError::ParseError(_))));
2007 assert_eq!(err.unwrap_err().to_string(), "Invalid application id digit");
2008 }
2009
2010 #[test]
2011 fn entity_id_display() {
2012 assert_eq!(ENTITY_ID_STR, ENTITY_ID.to_string());
2013 }
2014
2015 #[test]
2016 fn entity_id_from_str() {
2017 assert_eq!(EntityId::from_str(ENTITY_ID_STR).unwrap(), ENTITY_ID);
2018 let err = EntityId::from_str(ENTITY_ID_STR_INVALID);
2019 assert!(err.is_err());
2020 assert!(matches!(err, Err(DisError::ParseError(_))));
2021 assert_eq!(
2022 err.unwrap_err().to_string(),
2023 "EntityId string pattern does not contain precisely 3 digits"
2024 );
2025 let err = EntityId::from_str(ENTITY_ID_STR_INVALID_ENTITY_ID);
2026 assert!(err.is_err());
2027 assert!(matches!(err, Err(DisError::ParseError(_))));
2028 assert_eq!(err.unwrap_err().to_string(), "Invalid entity id digit");
2029 }
2030
2031 #[test]
2032 fn entity_id_from_str_not_three_digits() {
2033 let err = EntityId::from_str(ENTITY_ID_STR_NOT_THREE_DIGITS);
2034 assert!(err.is_err());
2035 assert!(matches!(err, Err(DisError::ParseError(_))));
2036 assert_eq!(
2037 err.unwrap_err().to_string(),
2038 "EntityId string pattern does not contain precisely 3 digits"
2039 );
2040 }
2041
2042 #[test]
2043 fn entity_id_try_from_str() {
2044 assert_eq!(
2045 TryInto::<EntityId>::try_into(ENTITY_ID_STR).unwrap(),
2046 ENTITY_ID
2047 );
2048 let err = TryInto::<EntityId>::try_into(ENTITY_ID_STR_INVALID);
2049 assert!(err.is_err());
2050 assert!(matches!(err, Err(DisError::ParseError(_))));
2051 assert_eq!(
2052 err.unwrap_err().to_string(),
2053 "EntityId string pattern does not contain precisely 3 digits"
2054 );
2055 let err = TryInto::<EntityId>::try_into(ENTITY_ID_STR_INVALID_ENTITY_ID);
2056 assert!(err.is_err());
2057 assert!(matches!(err, Err(DisError::ParseError(_))));
2058 assert_eq!(err.unwrap_err().to_string(), "Invalid entity id digit");
2059 }
2060
2061 #[test]
2062 fn entity_id_try_from_string() {
2063 assert_eq!(
2064 TryInto::<EntityId>::try_into(ENTITY_ID_STR.to_string()).unwrap(),
2065 ENTITY_ID
2066 );
2067 let err = TryInto::<EntityId>::try_into(ENTITY_ID_STR_INVALID.to_string());
2068 assert!(err.is_err());
2069 assert!(matches!(err, Err(DisError::ParseError(_))));
2070 assert_eq!(
2071 err.unwrap_err().to_string(),
2072 "EntityId string pattern does not contain precisely 3 digits"
2073 );
2074 let err = TryInto::<EntityId>::try_into(ENTITY_ID_STR_INVALID_ENTITY_ID.to_string());
2075 assert!(err.is_err());
2076 assert!(matches!(err, Err(DisError::ParseError(_))));
2077 assert_eq!(err.unwrap_err().to_string(), "Invalid entity id digit");
2078 }
2079
2080 #[test]
2081 fn event_id_display() {
2082 assert_eq!(EVENT_ID_STR, EVENT_ID.to_string());
2083 }
2084
2085 #[test]
2086 fn event_id_from_str() {
2087 assert_eq!(EventId::from_str(EVENT_ID_STR).unwrap(), EVENT_ID);
2088 let err = EventId::from_str(EVENT_ID_STR_INVALID);
2089 assert!(err.is_err());
2090 assert!(matches!(err, Err(DisError::ParseError(_))));
2091 assert_eq!(
2092 err.unwrap_err().to_string(),
2093 "EventId string pattern does not contain precisely 3 digits"
2094 );
2095 let err = EventId::from_str(EVENT_ID_STR_INVALID_EVENT_ID);
2096 assert!(err.is_err());
2097 assert!(matches!(err, Err(DisError::ParseError(_))));
2098 assert_eq!(err.unwrap_err().to_string(), "Invalid event id digit");
2099 }
2100
2101 #[test]
2102 fn event_id_from_str_not_three_digits() {
2103 let err = EventId::from_str(EVENT_ID_STR_NOT_THREE_DIGITS);
2104 assert!(err.is_err());
2105 assert!(matches!(err, Err(DisError::ParseError(_))));
2106 assert_eq!(
2107 err.unwrap_err().to_string(),
2108 "EventId string pattern does not contain precisely 3 digits"
2109 );
2110 }
2111
2112 #[test]
2113 fn event_id_try_from_str() {
2114 assert_eq!(
2115 TryInto::<EventId>::try_into(EVENT_ID_STR).unwrap(),
2116 EVENT_ID
2117 );
2118 let err = TryInto::<EventId>::try_into(EVENT_ID_STR_INVALID);
2119 assert!(err.is_err());
2120 assert!(matches!(err, Err(DisError::ParseError(_))));
2121 assert_eq!(
2122 err.unwrap_err().to_string(),
2123 "EventId string pattern does not contain precisely 3 digits"
2124 );
2125 let err = TryInto::<EventId>::try_into(EVENT_ID_STR_INVALID_EVENT_ID);
2126 assert!(err.is_err());
2127 assert!(matches!(err, Err(DisError::ParseError(_))));
2128 assert_eq!(err.unwrap_err().to_string(), "Invalid event id digit");
2129 }
2130
2131 #[test]
2132 fn event_id_try_from_string() {
2133 assert_eq!(
2134 TryInto::<EventId>::try_into(EVENT_ID_STR.to_string()).unwrap(),
2135 EVENT_ID
2136 );
2137 let err = TryInto::<EventId>::try_into(EVENT_ID_STR_INVALID.to_string());
2138 assert!(err.is_err());
2139 assert!(matches!(err, Err(DisError::ParseError(_))));
2140 assert_eq!(
2141 err.unwrap_err().to_string(),
2142 "EventId string pattern does not contain precisely 3 digits"
2143 );
2144 let err = TryInto::<EventId>::try_into(EVENT_ID_STR_INVALID_EVENT_ID.to_string());
2145 assert!(err.is_err());
2146 assert!(matches!(err, Err(DisError::ParseError(_))));
2147 assert_eq!(err.unwrap_err().to_string(), "Invalid event id digit");
2148 }
2149}