1use std::collections::HashSet;
2use std::net::Ipv4Addr;
3use std::{fmt::Display, str::FromStr};
4
5use crate::messages::*;
6use crate::structs::{RadioFrequency, TransponderCode};
7use crate::{aircraft_config::AircraftConfig, errors::FsdMessageParseError};
8use chrono::{DateTime, Utc};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub enum ClientCapability {
12 Version,
13 ATCInfo,
14 ModelDesc,
15 ACConfig,
16 VisUpdate,
17 RadarUpdate,
18 ATCMulti,
19 SecPos,
20 IcaoEq,
21 FastPos,
22 OngoingCoord,
23 InterimPos,
24 Stealth,
25 Teamspeak,
26 NewATIS,
27 Mumble,
28 GlobalData,
29 Simulated,
30 ObsPilot,
31}
32impl FromStr for ClientCapability {
33 type Err = FsdMessageParseError;
34 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 match s.to_uppercase().as_str() {
36 "VERSION" => Ok(ClientCapability::Version),
37 "ATCINFO" => Ok(ClientCapability::ATCInfo),
38 "MODELDESC" => Ok(ClientCapability::ModelDesc),
39 "ACCONFIG" => Ok(ClientCapability::ACConfig),
40 "VISUPDATE" => Ok(ClientCapability::VisUpdate),
41 "RADARUPDATE" => Ok(ClientCapability::RadarUpdate),
42 "ATCMULTI" => Ok(ClientCapability::ATCMulti),
43 "SECPOS" => Ok(ClientCapability::SecPos),
44 "ICAOEQ" => Ok(ClientCapability::IcaoEq),
45 "FASTPOS" => Ok(ClientCapability::FastPos),
46 "ONGOINGCOORD" => Ok(ClientCapability::OngoingCoord),
47 "INTERIMPOS" => Ok(ClientCapability::InterimPos),
48 "STEALTH" => Ok(ClientCapability::Stealth),
49 "TEAMSPEAK" => Ok(ClientCapability::Teamspeak),
50 "NEWATIS" => Ok(ClientCapability::NewATIS),
51 "MUMBLE" => Ok(ClientCapability::Mumble),
52 "GLOBALDATA" => Ok(ClientCapability::GlobalData),
53 "SIMULATED" => Ok(ClientCapability::Simulated),
54 "OBSPILOT" => Ok(ClientCapability::ObsPilot),
55 _ => Err(FsdMessageParseError::InvalidClientCapability(s.to_string())),
56 }
57 }
58}
59impl Display for ClientCapability {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match *self {
62 ClientCapability::ACConfig => write!(f, "ACCONFIG"),
63 ClientCapability::ATCInfo => write!(f, "ATCINFO"),
64 ClientCapability::ModelDesc => write!(f, "MODELDESC"),
65 ClientCapability::Version => write!(f, "VERSION"),
66 ClientCapability::VisUpdate => write!(f, "VISUPDATE"),
67 ClientCapability::RadarUpdate => write!(f, "RADARUPDATE"),
68 ClientCapability::ATCMulti => write!(f, "ATCMULTI"),
69 ClientCapability::SecPos => write!(f, "SECPOS"),
70 ClientCapability::IcaoEq => write!(f, "ICAOEQ"),
71 ClientCapability::FastPos => write!(f, "FASTPOS"),
72 ClientCapability::OngoingCoord => write!(f, "ONGOINGCOORD"),
73 ClientCapability::InterimPos => write!(f, "INTERIMPOS"),
74 ClientCapability::Stealth => write!(f, "STEALTH"),
75 ClientCapability::Teamspeak => write!(f, "TEAMSPEAK"),
76 ClientCapability::NewATIS => write!(f, "NEWATIS"),
77 ClientCapability::Mumble => write!(f, "MUMBLE"),
78 ClientCapability::GlobalData => write!(f, "GLOBALDATA"),
79 ClientCapability::Simulated => write!(f, "SIMULATED"),
80 ClientCapability::ObsPilot => write!(f, "OBSPILOT"),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub enum AtcRating {
87 Observer = 1,
88 S1,
89 S2,
90 S3,
91 C1,
92 C2,
93 C3,
94 I1,
95 I2,
96 I3,
97 Supervisor,
98 Administrator,
99}
100impl FromStr for AtcRating {
101 type Err = FsdMessageParseError;
102 fn from_str(s: &str) -> Result<Self, Self::Err> {
103 let rating_u8 =
104 u8::from_str(s).map_err(|_| FsdMessageParseError::InvalidRating(s.to_string()))?;
105 match rating_u8 {
106 1 => Ok(AtcRating::Observer),
107 2 => Ok(AtcRating::S1),
108 3 => Ok(AtcRating::S2),
109 4 => Ok(AtcRating::S3),
110 5 => Ok(AtcRating::C1),
111 6 => Ok(AtcRating::C2),
112 7 => Ok(AtcRating::C3),
113 8 => Ok(AtcRating::I1),
114 9 => Ok(AtcRating::I2),
115 10 => Ok(AtcRating::I3),
116 11 => Ok(AtcRating::Supervisor),
117 12 => Ok(AtcRating::Administrator),
118 _ => Err(FsdMessageParseError::InvalidRating(s.to_string())),
119 }
120 }
121}
122
123#[derive(Debug, Clone, Copy)]
124pub enum PilotRating {
125 Student = 1,
126 VFR,
127 IFR,
128 Instructor,
129 Supervisor,
130}
131
132impl FromStr for PilotRating {
133 type Err = FsdMessageParseError;
134 fn from_str(s: &str) -> Result<Self, Self::Err> {
135 let rating_u8 =
136 u8::from_str(s).map_err(|_| FsdMessageParseError::InvalidRating(s.to_string()))?;
137 match rating_u8 {
138 1 => Ok(PilotRating::Student),
139 2 => Ok(PilotRating::VFR),
140 3 => Ok(PilotRating::IFR),
141 4 => Ok(PilotRating::Instructor),
142 5 => Ok(PilotRating::Supervisor),
143 _ => Err(FsdMessageParseError::InvalidRating(s.to_string())),
144 }
145 }
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub enum ProtocolRevision {
151 Classic = 9,
153 VatsimNoAuth = 10,
155 VatsimAuth = 100,
157 Vatsim2022 = 101,
159}
160impl FromStr for ProtocolRevision {
161 type Err = FsdMessageParseError;
162 fn from_str(s: &str) -> Result<Self, Self::Err> {
163 match s {
164 "1" | "9" => Ok(Self::Classic),
165 "10" => Ok(Self::VatsimNoAuth),
166 "100" => Ok(Self::VatsimAuth),
167 "101" => Ok(Self::Vatsim2022),
168 _ => Err(FsdMessageParseError::InvalidProtocolRevison(s.to_string())),
169 }
170 }
171}
172
173#[derive(Debug, Clone, Copy)]
174pub enum SimulatorType {
175 Unknown,
176 MSFS95,
177 MSFS98,
178 MSCFS,
179 MSFS2000,
180 MSCFS2,
181 MSFS2002,
182 MSCFS3,
183 MSFS2004,
184 MSFSX,
185 MSFS,
186 MSFS2024,
187 XPLANE8,
188 XPLANE9,
189 XPLANE10,
190 XPLANE11,
191 XPLANE12,
192 P3Dv1,
193 P3Dv2,
194 P3Dv3,
195 P3Dv4,
196 P3Dv5,
197 FlightGear,
198}
199impl<S: AsRef<str>> From<S> for SimulatorType {
200 fn from(value: S) -> Self {
201 match value.as_ref() {
202 "1" => SimulatorType::MSFS95,
203 "2" => SimulatorType::MSFS98,
204 "3" => SimulatorType::MSCFS,
205 "4" => SimulatorType::MSFS2000,
206 "5" => SimulatorType::MSCFS2,
207 "6" => SimulatorType::MSFS2002,
208 "7" => SimulatorType::MSCFS3,
209 "8" => SimulatorType::MSFS2004,
210 "9" => SimulatorType::MSFSX,
211 "10" => SimulatorType::MSFS,
212 "11" => SimulatorType::MSFS2024,
213 "12" => SimulatorType::XPLANE8,
214 "13" => SimulatorType::XPLANE9,
215 "14" => SimulatorType::XPLANE10,
216 "15" => SimulatorType::XPLANE11,
217 "16" => SimulatorType::XPLANE12,
218 "17" => SimulatorType::P3Dv1,
219 "18" => SimulatorType::P3Dv2,
220 "19" => SimulatorType::P3Dv3,
221 "20" => SimulatorType::P3Dv4,
222 "21" => SimulatorType::P3Dv5,
223 "22" => SimulatorType::FlightGear,
224 _ => SimulatorType::Unknown,
225 }
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
230pub enum FlightRules {
231 DVFR,
232 SVFR,
233 VFR,
234 IFR,
235}
236
237impl FromStr for FlightRules {
238 type Err = FsdMessageParseError;
239 fn from_str(s: &str) -> Result<Self, Self::Err> {
240 let s = s.to_uppercase();
241 match s.as_str() {
242 "D" => Ok(FlightRules::DVFR),
243 "S" => Ok(FlightRules::SVFR),
244 "V" => Ok(FlightRules::VFR),
245 "I" => Ok(FlightRules::IFR),
246 _ => Err(FsdMessageParseError::InvalidFlightRules(s)),
247 }
248 }
249}
250
251impl Display for FlightRules {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 match *self {
254 FlightRules::DVFR => write!(f, "D"),
255 FlightRules::VFR => write!(f, "V"),
256 FlightRules::SVFR => write!(f, "S"),
257 FlightRules::IFR => write!(f, "I"),
258 }
259 }
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq)]
263pub enum AtcType {
264 Observer,
265 FlightServiceStation,
266 Delivery,
267 Ground,
268 Tower,
269 Approach,
270 Centre,
271}
272
273impl FromStr for AtcType {
274 type Err = FsdMessageParseError;
275 fn from_str(s: &str) -> Result<Self, Self::Err> {
276 match s {
277 "0" => Ok(AtcType::Observer),
278 "1" => Ok(AtcType::FlightServiceStation),
279 "2" => Ok(AtcType::Delivery),
280 "3" => Ok(AtcType::Ground),
281 "4" => Ok(AtcType::Tower),
282 "5" => Ok(AtcType::Approach),
283 "6" => Ok(AtcType::Centre),
284 _ => Err(FsdMessageParseError::InvalidAtcType(s.to_string())),
285 }
286 }
287}
288
289#[derive(Debug, Clone, Copy)]
290pub enum TransponderMode {
291 Standby,
292 ModeC,
293 Ident,
294}
295impl FromStr for TransponderMode {
296 type Err = FsdMessageParseError;
297 fn from_str(s: &str) -> Result<Self, Self::Err> {
298 match s {
299 "S" => Ok(TransponderMode::Standby),
300 "N" => Ok(TransponderMode::ModeC),
301 "Y" => Ok(TransponderMode::Ident),
302 _ => Err(FsdMessageParseError::InvalidTransponderMode(s.to_string())),
303 }
304 }
305}
306impl Display for TransponderMode {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 match *self {
309 Self::Standby => write!(f, "S"),
310 Self::ModeC => write!(f, "N"),
311 Self::Ident => write!(f, "Y"),
312 }
313 }
314}
315
316#[derive(Clone, Debug)]
317pub enum FsdMessageType {
318 AtcRegisterMessage(AtcRegisterMessage),
319 PilotRegisterMessage(PilotRegisterMessage),
320 AtcDeregisterMessage(AtcDeregisterMessage),
321 PilotDeregisterMessage(PilotDeregisterMessage),
322 AtcPositionUpdateMessage(AtcPositionUpdateMessage),
323 AtcSecondaryVisCentreMessage(AtcSecondaryVisCentreMessage),
324 PilotPositionUpdateMessage(PilotPositionUpdateMessage),
325 AuthenticationChallengeMessage(AuthenticationChallengeMessage),
326 AuthenticationResponseMessage(AuthenticationResponseMessage),
327 TextMessage(TextMessage),
328 FrequencyMessage(FrequencyMessage),
329 ChangeServerMessage(ChangeServerMessage),
330 InitialServerHandshakeMessage(InitialServerHandshakeMessage),
331 InitialClientHandshakeMessage(InitialClientHandshakeMessage),
332 SendFastPositionUpdatesMessage(SendFastPositionUpdatesMessage),
333 VelocityPositionStoppedMessage(VelocityPositionStoppedMessage),
334 VelocityPositionSlowMessage(VelocityPositionSlowMessage),
335 VelocityPositionFastMessage(VelocityPositionFastMessage),
336 KillMessage(KillMessage),
337 MetarRequestMessage(MetarRequestMessage),
338 MetarResponseMessage(MetarResponseMessage),
339 PingMessage(PingMessage),
340 PongMessage(PongMessage),
341 PlaneInfoRequestMessage(PlaneInfoRequestMessage),
342 PlaneInfoResponseMessage(PlaneInfoResponseMessage),
343 FsdErrorMessage(FsdErrorMessage),
344 FlightPlanMessage(FlightPlanMessage),
345 FlightPlanAmendmentMessage(FlightPlanAmendmentMessage),
346 FSInnPlaneInformationRequestMessage,
347 FSInnPlaneInformationResponseMessage,
348 ServerHeartbeat,
349 ClientQueryMessage(ClientQueryMessage),
350 ClientQueryResponseMessage(ClientQueryResponseMessage),
351 HandoffOfferMessage(HandoffOfferMessage),
352 HandoffAcceptMessage(HandoffAcceptMessage),
353 SharedStateMessage(SharedStateMessage),
354}
355
356impl FsdMessageType {
357 pub(crate) fn identify(message: &str) -> Result<FsdMessageType, FsdMessageParseError> {
358 let fields: Vec<&str> = message.split(':').collect();
359 if fields.len() < 2 {
360 if fields[0].starts_with("#DA") {
361 return Ok(Self::AtcDeregisterMessage(fields.as_slice().try_into()?));
362 }
363 if fields[0].starts_with("#DP") {
364 return Ok(Self::PilotDeregisterMessage(fields.as_slice().try_into()?));
365 }
366 return Err(FsdMessageParseError::UnknownMessageType(
367 message.to_string(),
368 ));
369 }
370 if fields[0].starts_with("#DA") {
371 return Ok(Self::AtcDeregisterMessage(fields.as_slice().try_into()?));
372 }
373 if fields[0].starts_with("#DP") {
374 return Ok(Self::PilotDeregisterMessage(fields.as_slice().try_into()?));
375 }
376 if fields[0].starts_with("#AA") {
377 return Ok(Self::AtcRegisterMessage(fields.as_slice().try_into()?));
378 }
379 if fields[0].starts_with("#AP") {
380 return Ok(Self::PilotRegisterMessage(fields.as_slice().try_into()?));
381 }
382
383 if fields[0].starts_with('%') {
384 return Ok(Self::AtcPositionUpdateMessage(
385 fields.as_slice().try_into()?,
386 ));
387 }
388 if fields[0].starts_with('\'') {
389 return Ok(Self::AtcSecondaryVisCentreMessage(
390 fields.as_slice().try_into()?,
391 ));
392 }
393 if fields[0].starts_with('@') {
394 return Ok(Self::PilotPositionUpdateMessage(
395 fields.as_slice().try_into()?,
396 ));
397 }
398 if fields[0].starts_with("$ZC") {
399 return Ok(Self::AuthenticationChallengeMessage(
400 fields.as_slice().try_into()?,
401 ));
402 }
403 if fields[0].starts_with("$ZR") {
404 return Ok(Self::AuthenticationResponseMessage(
405 fields.as_slice().try_into()?,
406 ));
407 }
408 if fields[0].starts_with("$ER") {
409 return Ok(Self::FsdErrorMessage(fields.as_slice().try_into()?));
410 }
411 if fields[0].starts_with("$HO") {
412 return Ok(Self::HandoffOfferMessage(fields.as_slice().try_into()?));
413 }
414 if fields[0].starts_with("$HA") {
415 return Ok(Self::HandoffAcceptMessage(fields.as_slice().try_into()?));
416 }
417 if fields[0].starts_with("#TM") {
418 if fields[1].starts_with('@') {
419 return Ok(Self::FrequencyMessage(fields.as_slice().try_into()?));
420 }
421
422 return Ok(Self::TextMessage(fields.as_slice().try_into()?));
423 }
424 if fields[0].starts_with("$XX") {
425 return Ok(Self::ChangeServerMessage(fields.as_slice().try_into()?));
426 }
427 if fields[0].starts_with("$FP") {
428 return Ok(Self::FlightPlanMessage(fields.as_slice().try_into()?));
429 }
430 if fields[0].starts_with("$AM") {
431 return Ok(Self::FlightPlanAmendmentMessage(
432 fields.as_slice().try_into()?,
433 ));
434 }
435 if fields[0].starts_with("$DI") {
436 return Ok(Self::InitialServerHandshakeMessage(
437 fields.as_slice().try_into()?,
438 ));
439 }
440 if fields[0].starts_with("$ID") {
441 return Ok(Self::InitialClientHandshakeMessage(
442 fields.as_slice().try_into()?,
443 ));
444 }
445 if fields[0].starts_with("$SF") {
446 return Ok(Self::SendFastPositionUpdatesMessage(
447 fields.as_slice().try_into()?,
448 ));
449 }
450 if fields[0].starts_with("#ST") {
451 return Ok(Self::VelocityPositionStoppedMessage(
452 fields.as_slice().try_into()?,
453 ));
454 }
455 if fields[0].starts_with("#DL") {
456 return Ok(Self::ServerHeartbeat);
457 }
458 if fields[0].starts_with("#SL") {
459 return Ok(Self::VelocityPositionSlowMessage(
460 fields.as_slice().try_into()?,
461 ));
462 }
463 if fields[0].starts_with("#PC") {
464 return Ok(Self::SharedStateMessage(fields.as_slice().try_into()?));
465 }
466 if fields[0].starts_with('^') {
467 return Ok(Self::VelocityPositionFastMessage(
468 fields.as_slice().try_into()?,
469 ));
470 }
471 if fields[0].starts_with("$!!") {
472 return Ok(Self::KillMessage(fields.as_slice().try_into()?));
473 }
474 if fields[0].starts_with("$AX") {
475 return Ok(Self::MetarRequestMessage(fields.as_slice().try_into()?));
476 }
477 if fields[0].starts_with("$AR") {
478 return Ok(Self::MetarResponseMessage(fields.as_slice().try_into()?));
479 }
480 if fields[0].starts_with("$CQ") {
481 return Ok(Self::ClientQueryMessage(fields.as_slice().try_into()?));
482 }
483 if fields[0].starts_with("$CR") {
484 return Ok(Self::ClientQueryResponseMessage(
485 fields.as_slice().try_into()?,
486 ));
487 }
488 if fields[0].starts_with("$PI") {
489 return Ok(Self::PingMessage(fields.as_slice().try_into()?));
490 }
491 if fields[0].starts_with("$PO") {
492 return Ok(Self::PongMessage(fields.as_slice().try_into()?));
493 }
494 if fields[0].starts_with("#SB") {
495 if fields[2] == "PIR" {
496 return Ok(Self::PlaneInfoRequestMessage(fields.as_slice().try_into()?));
497 }
498 if fields[2] == "PI" {
499 return Ok(Self::PlaneInfoResponseMessage(
500 fields.as_slice().try_into()?,
501 ));
502 }
503 if fields[2] == "FSIPI" {
504 return Ok(Self::FSInnPlaneInformationResponseMessage);
505 }
506 if fields[2] == "FSIPIR" {
507 return Ok(Self::FSInnPlaneInformationRequestMessage);
508 }
509 }
510
511 Err(FsdMessageParseError::UnknownMessageType(
512 message.to_string(),
513 ))
514 }
515}
516impl Display for FsdMessageType {
517 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
518 match self {
519 FsdMessageType::AtcRegisterMessage(m) => m.fmt(f),
520 FsdMessageType::PilotRegisterMessage(m) => m.fmt(f),
521 FsdMessageType::AtcDeregisterMessage(m) => m.fmt(f),
522 FsdMessageType::PilotDeregisterMessage(m) => m.fmt(f),
523 FsdMessageType::AtcPositionUpdateMessage(m) => m.fmt(f),
524 FsdMessageType::AtcSecondaryVisCentreMessage(m) => m.fmt(f),
525 FsdMessageType::PilotPositionUpdateMessage(m) => m.fmt(f),
526 FsdMessageType::AuthenticationChallengeMessage(m) => m.fmt(f),
527 FsdMessageType::AuthenticationResponseMessage(m) => m.fmt(f),
528 FsdMessageType::TextMessage(m) => m.fmt(f),
529 FsdMessageType::FrequencyMessage(m) => m.fmt(f),
530 FsdMessageType::ChangeServerMessage(m) => m.fmt(f),
531 FsdMessageType::InitialServerHandshakeMessage(m) => m.fmt(f),
532 FsdMessageType::InitialClientHandshakeMessage(m) => m.fmt(f),
533 FsdMessageType::SendFastPositionUpdatesMessage(m) => m.fmt(f),
534 FsdMessageType::VelocityPositionStoppedMessage(m) => m.fmt(f),
535 FsdMessageType::VelocityPositionSlowMessage(m) => m.fmt(f),
536 FsdMessageType::VelocityPositionFastMessage(m) => m.fmt(f),
537 FsdMessageType::KillMessage(m) => m.fmt(f),
538 FsdMessageType::MetarRequestMessage(m) => m.fmt(f),
539 FsdMessageType::MetarResponseMessage(m) => m.fmt(f),
540 FsdMessageType::PingMessage(m) => m.fmt(f),
541 FsdMessageType::PongMessage(m) => m.fmt(f),
542 FsdMessageType::PlaneInfoRequestMessage(m) => m.fmt(f),
543 FsdMessageType::PlaneInfoResponseMessage(m) => m.fmt(f),
544 FsdMessageType::FsdErrorMessage(m) => m.fmt(f),
545 FsdMessageType::FlightPlanMessage(m) => m.fmt(f),
546 FsdMessageType::FlightPlanAmendmentMessage(m) => m.fmt(f),
547 m @ FsdMessageType::FSInnPlaneInformationRequestMessage => m.fmt(f),
548 m @ FsdMessageType::FSInnPlaneInformationResponseMessage => m.fmt(f),
549 m @ FsdMessageType::ServerHeartbeat => m.fmt(f),
550 FsdMessageType::ClientQueryMessage(m) => m.fmt(f),
551 FsdMessageType::ClientQueryResponseMessage(m) => m.fmt(f),
552 FsdMessageType::HandoffOfferMessage(m) => m.fmt(f),
553 FsdMessageType::HandoffAcceptMessage(m) => m.fmt(f),
554 FsdMessageType::SharedStateMessage(m) => m.fmt(f),
555 }
556 }
557}
558
559#[allow(unused)]
560#[derive(Clone, Debug)]
561pub enum ClientQueryType {
562 IsValidATC {
563 atc_callsign: String,
564 }, Capabilities, Com1Freq, RealName, Server, ATIS, PublicIP, INF, FlightPlan {
573 aircraft_callsign: String,
574 }, ForceBeaconCode {
576 code: TransponderCode,
577 }, RequestRelief, CancelRequestRelief, HelpRequest {
581 message: Option<String>,
582 }, CancelHelpRequest {
584 message: Option<String>,
585 }, WhoHas {
587 aircraft_callsign: String,
588 }, InitiateTrack {
590 aircraft_callsign: String,
591 }, AcceptHandoff {
593 aircraft_callsign: String,
594 atc_callsign: String,
595 }, DropTrack {
597 aircraft_callsign: String,
598 }, SetFinalAltitude {
600 aircraft_callsign: String,
601 altitude: i32,
602 }, SetTempAltitude {
604 aircraft_callsign: String,
605 altitude: i32,
606 }, SetBeaconCode {
608 aircraft_callsign: String,
609 code: TransponderCode,
610 }, SetScratchpad {
612 aircraft_callsign: String,
613 contents: ScratchPad,
614 }, SetVoiceType {
616 aircraft_callsign: String,
617 voice_capability: VoiceCapability,
618 }, AircraftConfigurationRequest, AircraftConfigurationResponse {
621 aircraft_config: AircraftConfig,
622 }, SimTime {
624 time: DateTime<Utc>,
625 }, NewInfo {
627 atis_letter: char,
628 }, NewATIS {
630 atis_letter: char,
631 surface_wind: String,
632 pressure: String,
633 }, SetGlobalData {
636 aircraft_callsign: String,
637 contents: String,
638 }, }
640
641impl Display for ClientQueryType {
642 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
643 match self {
644 ClientQueryType::IsValidATC { atc_callsign } => write!(f, "ATC:{}", atc_callsign),
645 ClientQueryType::Capabilities => write!(f, "CAPS"),
646 ClientQueryType::Com1Freq => write!(f, "C?"),
647 ClientQueryType::RealName => write!(f, "RN"),
648 ClientQueryType::Server => write!(f, "SV"),
649 ClientQueryType::ATIS => write!(f, "ATIS"),
650 ClientQueryType::PublicIP => write!(f, "IP"),
651 ClientQueryType::INF => write!(f, "INF"),
652 ClientQueryType::FlightPlan { aircraft_callsign } => {
653 write!(f, "FP:{}", aircraft_callsign)
654 }
655 ClientQueryType::RequestRelief => write!(f, "BY"),
656 ClientQueryType::CancelRequestRelief => write!(f, "HI"),
657 ClientQueryType::HelpRequest { message: None } => write!(f, "HLP"),
658 ClientQueryType::HelpRequest { message: Some(msg) } => write!(f, "HLP:{msg}"),
659 ClientQueryType::CancelHelpRequest { message: None } => write!(f, "NOHLP"),
660 ClientQueryType::CancelHelpRequest { message: Some(msg) } => write!(f, "NOHLP:{msg}"),
661 ClientQueryType::WhoHas { aircraft_callsign } => write!(f, "WH:{}", aircraft_callsign),
662 ClientQueryType::InitiateTrack { aircraft_callsign } => {
663 write!(f, "IT:{}", aircraft_callsign)
664 }
665 ClientQueryType::AcceptHandoff {
666 aircraft_callsign,
667 atc_callsign,
668 } => {
669 write!(f, "HT:{}:{}", aircraft_callsign, atc_callsign)
670 }
671 ClientQueryType::DropTrack { aircraft_callsign } => {
672 write!(f, "DR:{}", aircraft_callsign)
673 }
674 ClientQueryType::SetFinalAltitude {
675 aircraft_callsign,
676 altitude,
677 } => write!(f, "FA:{}:{}", aircraft_callsign, altitude),
678 ClientQueryType::SetTempAltitude {
679 aircraft_callsign,
680 altitude,
681 } => write!(f, "TA:{}:{}", aircraft_callsign, altitude),
682 ClientQueryType::SetBeaconCode {
683 aircraft_callsign,
684 code,
685 } => {
686 write!(f, "BC:{}:{}", aircraft_callsign, code)
687 }
688 ClientQueryType::ForceBeaconCode { code } => {
689 write!(f, "IPC:W:852:{}", code.as_bcd_format())
690 }
691 ClientQueryType::SetScratchpad {
692 aircraft_callsign,
693 contents,
694 } => {
695 write!(f, "SC:{}:{}", aircraft_callsign, contents)
696 }
697 ClientQueryType::SetVoiceType {
698 aircraft_callsign,
699 voice_capability,
700 } => {
701 write!(f, "VT:{}:{}", aircraft_callsign, voice_capability)
702 }
703 ClientQueryType::AircraftConfigurationRequest => {
704 write!(f, "ACC:{{\"request\":\"full\"}}")
705 }
706 ClientQueryType::AircraftConfigurationResponse { aircraft_config } => {
707 write!(f, "ACC:{}", aircraft_config)
708 }
709 ClientQueryType::NewATIS {
710 atis_letter,
711 surface_wind,
712 pressure,
713 } => {
714 write!(
715 f,
716 "NEWATIS:ATIS {}: {} - {}",
717 atis_letter, surface_wind, pressure
718 )
719 }
720 ClientQueryType::NewInfo { atis_letter } => {
721 write!(f, "NEWINFO:{}", atis_letter)
722 }
723 ClientQueryType::SimTime { time } => {
724 write!(f, "SIMTIME:{}", time.format("Y%m%d%H%M%S"))
725 }
726 ClientQueryType::SetGlobalData {
727 aircraft_callsign,
728 contents,
729 } => {
730 write!(f, "GD:{}:{}", aircraft_callsign, contents)
731 }
732 }
733 }
734}
735
736#[allow(unused)]
737#[derive(Clone, Debug)]
738pub enum AtisLine {
739 VoiceServer(String),
740 TextLine(String),
741 LogoffTime(Option<u16>),
742 EndMarker(usize),
743}
744impl Display for AtisLine {
745 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
746 match self {
747 AtisLine::VoiceServer(voice_server) => write!(f, "V:{}", voice_server),
748 AtisLine::TextLine(text) => write!(f, "T:{}", text),
749 AtisLine::LogoffTime(Some(time)) => write!(f, "Z:{:04}z", time),
750 AtisLine::LogoffTime(None) => write!(f, "Z:z"),
751 AtisLine::EndMarker(num_lines) => write!(f, "E:{}", num_lines),
752 }
753 }
754}
755
756#[allow(unused)]
757#[derive(Clone, Debug)]
758pub enum ClientResponseType {
759 Com1Freq {
760 frequency: RadioFrequency,
761 },
762 ATIS {
763 atis_line: AtisLine,
764 },
765 RealName {
766 name: String,
767 sector_file: String,
768 rating: u8,
769 },
770 Capabilities {
771 capabilities: Vec<ClientCapability>,
772 },
773 PublicIP {
774 ip_address: String,
775 },
776 Server {
777 hostname_or_ip_address: String,
778 },
779 IsValidATC {
780 atc_callsign: String,
781 valid_atc: bool,
782 },
783}
784impl Display for ClientResponseType {
785 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
786 match self {
787 ClientResponseType::Com1Freq { frequency } => {
788 write!(f, "C?:{}", frequency.to_human_readable_string())
789 }
790 ClientResponseType::ATIS { atis_line } => write!(f, "ATIS:{}", atis_line),
791 ClientResponseType::RealName {
792 name,
793 sector_file,
794 rating,
795 } => {
796 write!(f, "RN:{}:{}:{}", name, sector_file, rating)
797 }
798 ClientResponseType::Capabilities { capabilities } => {
799 write!(f, "CAPS:")?;
800 let mut capabilities = capabilities.iter().peekable();
801 while let Some(capability) = capabilities.next() {
802 write!(f, "{}=1", capability)?;
803 if capabilities.peek().is_some() {
804 write!(f, ":")?;
805 }
806 }
807 Ok(())
808 }
809 ClientResponseType::PublicIP { ip_address } => write!(f, "IP:{}", ip_address),
810 ClientResponseType::Server {
811 hostname_or_ip_address,
812 } => write!(f, "SV:{}", hostname_or_ip_address),
813 ClientResponseType::IsValidATC {
814 atc_callsign,
815 valid_atc,
816 } => {
817 let valid = if *valid_atc { 'Y' } else { 'N' };
818 write!(f, "ATC:{}:{}", valid, atc_callsign)
819 }
820 }
821 }
822}
823
824#[allow(unused)]
825#[derive(Clone, Debug)]
826pub enum SharedStateType {
827 Version,
828 ID,
829 DI,
830 IHave {
831 aircraft_callsign: String,
832 },
833 ScratchPad {
834 aircraft_callsign: String,
835 contents: ScratchPad,
836 },
837 TempAltitude {
838 aircraft_callsign: String,
839 altitude: i32,
840 },
841 FinalAltitude {
842 aircraft_callsign: String,
843 altitude: i32,
844 },
845 VoiceType {
846 aircraft_callsign: String,
847 voice_capability: VoiceCapability,
848 },
849 BeaconCode {
850 aircraft_callsign: String,
851 code: TransponderCode,
852 },
853 HandoffCancel {
854 aircraft_callsign: String,
855 },
856 FlightStrip {
857 aircraft_callsign: String,
858 format: Option<i32>,
859 contents: Option<Vec<String>>,
860 },
861 PushToDepartureList {
862 aircraft_callsign: String,
863 },
864 PointOut {
865 aircraft_callsign: String,
866 },
867 LandLine {
868 landline_type: LandLineType,
869 landline_command: LandLineCommand,
870 },
871 GlobalData {
872 aircraft_callsign: String,
873 contents: String,
874 },
875}
876
877impl Display for SharedStateType {
878 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
879 match self {
880 SharedStateType::Version => write!(f, "VER"),
881 SharedStateType::ID => write!(f, "ID"),
882 SharedStateType::DI => write!(f, "DI"),
883 SharedStateType::IHave { aircraft_callsign } => write!(f, "IH:{}", aircraft_callsign),
884 SharedStateType::ScratchPad {
885 aircraft_callsign,
886 contents,
887 } => {
888 write!(f, "SC:{}:{}", aircraft_callsign, contents)
889 }
890 SharedStateType::TempAltitude {
891 aircraft_callsign,
892 altitude,
893 } => {
894 write!(f, "TA:{}:{}", aircraft_callsign, altitude)
895 }
896 SharedStateType::FinalAltitude {
897 aircraft_callsign,
898 altitude,
899 } => {
900 write!(f, "FA:{}:{}", aircraft_callsign, altitude)
901 }
902 SharedStateType::VoiceType {
903 aircraft_callsign,
904 voice_capability,
905 } => {
906 write!(f, "VT:{}:{}", aircraft_callsign, voice_capability)
907 }
908 SharedStateType::BeaconCode {
909 aircraft_callsign,
910 code,
911 } => write!(f, "BC:{}:{}", aircraft_callsign, code),
912 SharedStateType::HandoffCancel { aircraft_callsign } => {
913 write!(f, "HC:{}", aircraft_callsign)
914 }
915 SharedStateType::PointOut { aircraft_callsign } => {
916 write!(f, "PT:{}", aircraft_callsign)
917 }
918 SharedStateType::PushToDepartureList { aircraft_callsign } => {
919 write!(f, "DP:{}", aircraft_callsign)
920 }
921 SharedStateType::FlightStrip {
922 aircraft_callsign,
923 format,
924 contents,
925 } => {
926 write!(f, "ST:{aircraft_callsign}")?;
927 if let Some(format) = *format {
928 write!(f, ":{format}")?;
929 }
930 if let Some(contents) = contents {
931 for item in contents {
932 write!(f, ":{item}")?;
933 }
934 }
935 Ok(())
936 }
937 SharedStateType::LandLine {
938 landline_type,
939 landline_command,
940 } => match (*landline_type, *landline_command) {
941 (LandLineType::Intercom, LandLineCommand::Request { ip_address, port }) => {
942 write!(f, "IC:{ip_address}:{port}")
943 }
944 (LandLineType::Intercom, LandLineCommand::Approve { ip_address, port }) => {
945 write!(f, "IK:{ip_address}:{port}")
946 }
947 (LandLineType::Intercom, LandLineCommand::Reject) => write!(f, "IB"),
948 (LandLineType::Intercom, LandLineCommand::End) => write!(f, "EC"),
949
950 (LandLineType::Override, LandLineCommand::Request { ip_address, port }) => {
951 write!(f, "OV:{ip_address}:{port}")
952 }
953 (LandLineType::Override, LandLineCommand::Approve { ip_address, port }) => {
954 write!(f, "OK:{ip_address}:{port}")
955 }
956 (LandLineType::Override, LandLineCommand::Reject) => write!(f, "OB"),
957 (LandLineType::Override, LandLineCommand::End) => write!(f, "EO"),
958
959 (LandLineType::Monitor, LandLineCommand::Request { ip_address, port }) => {
960 write!(f, "MN:{ip_address}:{port}")
961 }
962 (LandLineType::Monitor, LandLineCommand::Approve { ip_address, port }) => {
963 write!(f, "MK:{ip_address}:{port}")
964 }
965 (LandLineType::Monitor, LandLineCommand::Reject) => write!(f, "MB"),
966 (LandLineType::Monitor, LandLineCommand::End) => write!(f, "EM"),
967 },
968 SharedStateType::GlobalData {
969 aircraft_callsign,
970 contents,
971 } => write!(f, "GD:{aircraft_callsign}:{contents}"),
972 }
973 }
974}
975
976#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
977pub enum LandLineType {
978 Intercom,
979 Override,
980 Monitor,
981}
982
983#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
984pub enum LandLineCommand {
985 Request { ip_address: Ipv4Addr, port: u16 },
986 Approve { ip_address: Ipv4Addr, port: u16 },
987 Reject,
988 End,
989}
990
991#[derive(Debug, Clone, Copy)]
992pub enum Operator {
993 Exactly,
994 OrLess,
995 OrGreater,
996}
997impl Display for Operator {
998 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
999 match *self {
1000 Self::Exactly => write!(f, "="),
1001 Self::OrLess => write!(f, "-"),
1002 Self::OrGreater => write!(f, "+"),
1003 }
1004 }
1005}
1006
1007#[derive(Debug, Copy, Clone)]
1008pub enum GroundState {
1009 NoState,
1010 OnFrequency,
1011 DeIcing,
1012 Startup,
1013 Pushback,
1014 Taxi,
1015 LineUp,
1016 TakeOff,
1017 TaxiIn,
1018 OnBlock,
1019}
1020impl Display for GroundState {
1021 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1022 match self {
1023 Self::NoState => write!(f, "NSTS"),
1024 Self::OnFrequency => write!(f, "ONFREQ"),
1025 Self::DeIcing => write!(f, "DE-ICE"),
1026 Self::Startup => write!(f, "STUP"),
1027 Self::Pushback => write!(f, "PUSH"),
1028 Self::Taxi => write!(f, "TAXI"),
1029 Self::LineUp => write!(f, "LINEUP"),
1030 Self::TakeOff => write!(f, "DEPA"),
1031 Self::TaxiIn => write!(f, "TXIN"),
1032 Self::OnBlock => write!(f, "PARK"),
1033 }
1034 }
1035}
1036
1037#[derive(Debug, Clone)]
1038pub enum ScratchPad {
1039 PlainTextOrDirect(String),
1040 RateOfClimbDescent(u32),
1041 Heading(u32),
1042 Speed(u32),
1043 Mach(u32),
1044 SpeedOperator(Operator),
1045 RateOfClimbDescentOperator(Operator),
1046 Stand(String),
1047 CancelledStand,
1048 ManualStand(String, String),
1049 CancelledManualStand,
1050 ClearanceReceived,
1051 ClearanceCancelled,
1052 GroundState(GroundState),
1053}
1054impl FromStr for ScratchPad {
1055 type Err = FsdMessageParseError;
1056 fn from_str(s: &str) -> Result<Self, Self::Err> {
1057 if let Some(Ok(h)) = s.strip_prefix('H').map(str::parse) {
1059 return Ok(Self::Heading(h));
1060 }
1061 if let Some(Ok(r)) = s.strip_prefix('R').map(str::parse) {
1062 return Ok(Self::RateOfClimbDescent(r));
1063 }
1064 if let Some(Ok(speed)) = s.strip_prefix('S').map(str::parse) {
1065 return Ok(Self::Speed(speed));
1066 }
1067 if let Some(Ok(m)) = s.strip_prefix('M').map(str::parse) {
1068 return Ok(Self::Mach(m));
1069 }
1070 if s.len() > 6 && s.starts_with("GRP/S/") {
1071 return Ok(Self::Stand(s[6..].to_string()));
1072 }
1073 if s.len() > 6 && s.starts_with("GRP/M/") {
1074 let mut split = s.split('/');
1075 return Ok(Self::ManualStand(
1076 split.next().unwrap_or("ZZZZ").to_string(),
1077 split.next().unwrap_or("").to_string(),
1078 ));
1079 }
1080 match s {
1081 "/ASP=/" => Ok(Self::SpeedOperator(Operator::Exactly)),
1083 "/ASP-/" => Ok(Self::SpeedOperator(Operator::OrLess)),
1084 "/ASP+/" => Ok(Self::SpeedOperator(Operator::OrGreater)),
1085 "/ARC=/" => Ok(Self::RateOfClimbDescentOperator(Operator::Exactly)),
1086 "/ARC-/" => Ok(Self::RateOfClimbDescentOperator(Operator::OrLess)),
1087 "/ARC+/" => Ok(Self::RateOfClimbDescentOperator(Operator::OrGreater)),
1088 "GRP/S/" => Ok(Self::CancelledStand),
1090 "GRP/M/" => Ok(Self::CancelledManualStand),
1091 "NSTS" => Ok(Self::GroundState(GroundState::NoState)),
1093 "NOSTATE" => Ok(Self::GroundState(GroundState::NoState)),
1095 "ONFREQ" => Ok(Self::GroundState(GroundState::OnFrequency)),
1096 "DE-ICE" => Ok(Self::GroundState(GroundState::DeIcing)),
1097 "STUP" | "ST-UP" => Ok(Self::GroundState(GroundState::Startup)),
1098 "PUSH" => Ok(Self::GroundState(GroundState::Pushback)),
1099 "TAXI" => Ok(Self::GroundState(GroundState::Taxi)),
1100 "LINEUP" => Ok(Self::GroundState(GroundState::LineUp)),
1101 "TXIN" => Ok(Self::GroundState(GroundState::TaxiIn)),
1102 "DEPA" => Ok(Self::GroundState(GroundState::TakeOff)),
1103 "PARK" => Ok(Self::GroundState(GroundState::OnBlock)),
1104 "CLEA" => Ok(Self::ClearanceReceived),
1105 "NOTC" => Ok(Self::ClearanceCancelled),
1106 text => Ok(ScratchPad::PlainTextOrDirect(text.to_string())),
1107 }
1108 }
1109}
1110impl Display for ScratchPad {
1111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1112 match self {
1113 Self::RateOfClimbDescent(r) => write!(f, "R{r}"),
1114 Self::Heading(h) => write!(f, "H{h}"),
1115 Self::Speed(speed) => write!(f, "S{speed}"),
1116 Self::Mach(m) => write!(f, "{m}"),
1117 Self::SpeedOperator(op) => write!(f, "/ASP{op}/"),
1118 Self::RateOfClimbDescentOperator(op) => write!(f, "/ARC{op}/"),
1119 Self::PlainTextOrDirect(text) => write!(f, "{text}"),
1120 Self::Stand(stand) => write!(f, "GRP/S/{stand}"),
1121 Self::CancelledStand => write!(f, "GRP/S/"),
1122 Self::ManualStand(icao, stand) => write!(f, "GRP/M/{icao}/{stand}"),
1123 Self::CancelledManualStand => write!(f, "GRP/M"),
1124 Self::GroundState(gs) => gs.fmt(f),
1125 Self::ClearanceReceived => write!(f, "CLEA"),
1126 Self::ClearanceCancelled => write!(f, "NOTC"),
1127 }
1128 }
1129}
1130
1131#[derive(Debug, Clone, Copy)]
1132pub enum VoiceCapability {
1133 Unknown,
1134 Voice,
1135 Text,
1136 Receive,
1137}
1138impl FromStr for VoiceCapability {
1139 type Err = FsdMessageParseError;
1140 fn from_str(s: &str) -> Result<Self, Self::Err> {
1141 if s.is_empty() {
1142 return Ok(VoiceCapability::Unknown);
1143 }
1144 let s = s.to_lowercase();
1145 match s.as_str() {
1146 "v" => Ok(VoiceCapability::Voice),
1147 "t" => Ok(VoiceCapability::Text),
1148 "r" => Ok(VoiceCapability::Receive),
1149 _ => Err(FsdMessageParseError::InvalidVoiceCapability(s)),
1150 }
1151 }
1152}
1153impl Display for VoiceCapability {
1154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1155 match *self {
1156 VoiceCapability::Unknown => write!(f, ""),
1157 VoiceCapability::Voice => write!(f, "v"),
1158 VoiceCapability::Text => write!(f, "t"),
1159 VoiceCapability::Receive => write!(f, "r"),
1160 }
1161 }
1162}