From 9b9d47488b363c629cd6645ff1cbad50c1b38d6e Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Wed, 14 Aug 2024 16:20:07 -0400
Subject: [PATCH 01/11] Added BSM objects
---
JMayer.Example.WindowsService/BSM/BSM.cs | 185 ++++++++++++++++++
JMayer.Example.WindowsService/BSM/BSMPDU.cs | 28 +++
.../BSM/BSMParser.cs | 49 +++++
.../BSM/BaggageTagDetails.cs | 62 ++++++
JMayer.Example.WindowsService/BSM/ITypeB.cs | 19 ++
.../BSM/OutboundFlight.cs | 107 ++++++++++
.../BSM/PassengerName.cs | 80 ++++++++
.../BSM/VersionSupplementaryData.cs | 82 ++++++++
8 files changed, 612 insertions(+)
create mode 100644 JMayer.Example.WindowsService/BSM/BSM.cs
create mode 100644 JMayer.Example.WindowsService/BSM/BSMPDU.cs
create mode 100644 JMayer.Example.WindowsService/BSM/BSMParser.cs
create mode 100644 JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
create mode 100644 JMayer.Example.WindowsService/BSM/ITypeB.cs
create mode 100644 JMayer.Example.WindowsService/BSM/OutboundFlight.cs
create mode 100644 JMayer.Example.WindowsService/BSM/PassengerName.cs
create mode 100644 JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
diff --git a/JMayer.Example.WindowsService/BSM/BSM.cs b/JMayer.Example.WindowsService/BSM/BSM.cs
new file mode 100644
index 0000000..eedcd96
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/BSM.cs
@@ -0,0 +1,185 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class represents a simplified version of a baggage source message.
+///
+///
+/// A baggage source message contains the passenger information, outbound flight information,
+/// the baggage checked in at the ticket counter and other data. The outbound system uses this
+/// to bind a bag scanned at a scanner to a flight and to an end point in the system.
+///
+public class BSM : ITypeB
+{
+ ///
+ /// The constant for the Add change of status.
+ ///
+ public const string Add = "ADD";
+
+ ///
+ /// The property gets the baggage tag details.
+ ///
+ public BaggageTagDetails? BaggageTagDetails { get; private set; }
+
+ ///
+ /// The constant for the change change of status.
+ ///
+ public const string Change = "CHG";
+
+ ///
+ /// The property gets the change of status.
+ ///
+ [Required]
+ public string ChangeOfStatus { get; private set; } = string.Empty;
+
+ ///
+ /// The constant for the delete change of status.
+ ///
+ public const string Delete = "DEL";
+
+ ///
+ /// The constant for the end of BSM.
+ ///
+ public const string EndOfBSM = "ENDBSM";
+
+ ///
+ /// The property gets the outbound flight information.
+ ///
+ public OutboundFlight? OutboundFlight { get; private set; }
+
+ ///
+ /// The property gets the passenger name.
+ ///
+ public PassengerName? PassengerName { get; private set; }
+
+ ///
+ /// The property gets when the BSM was received.
+ ///
+ public DateTime ReceivedOn { get; init; } = DateTime.Now;
+
+ ///
+ /// The constant for the start of BSM.
+ ///
+ public const string StartOfBSM = "BSM";
+
+ ///
+ /// The property gets the version supplementary data.
+ ///
+ public VersionSupplementaryData? VersionSupplementaryData { get; private set; }
+
+ ///
+ /// The method returns the change of status from the BSM.
+ ///
+ /// The BSM to examine.
+ /// The change of status.
+ private static string GetChangeOfStatus(string bsm)
+ {
+ int index = bsm.IndexOf('.');
+
+ if (index > -1)
+ {
+ string header = bsm.Substring(0, index);
+
+ if (header.Contains(Change))
+ {
+ return Change;
+ }
+ else if (header.Contains(Delete))
+ {
+ return Delete;
+ }
+ else
+ {
+ return Add;
+ }
+ }
+
+ return string.Empty;
+ }
+
+ ///
+ public void Parse(string typeBString)
+ {
+ ChangeOfStatus = GetChangeOfStatus(typeBString);
+
+ //Remove the start & end identifiers because they're no longer needed.
+ typeBString = typeBString.Replace(StartOfBSM, string.Empty);
+ typeBString = typeBString.Replace(EndOfBSM, string.Empty);
+
+ int totalBytesProcessed = 0;
+
+ do
+ {
+ int startIndex = typeBString.IndexOf('.');
+
+ //Dot was not found so exit.
+ if (startIndex > -1)
+ {
+ break;
+ }
+
+ int endIndex = typeBString.IndexOf('.', startIndex + 1);
+
+ //If the next dot is not found then assume this is the last line.
+ if (endIndex == -1)
+ {
+ endIndex = typeBString.Length;
+ }
+
+ string line = typeBString.Substring(startIndex, endIndex - startIndex);
+
+ if (line.StartsWith(OutboundFlight.DotFElement))
+ {
+ OutboundFlight = new OutboundFlight();
+ OutboundFlight.Parse(line);
+ }
+ else if (line.StartsWith(BaggageTagDetails.DotNElement))
+ {
+ BaggageTagDetails = new BaggageTagDetails();
+ BaggageTagDetails.Parse(line);
+ }
+ else if (line.StartsWith(PassengerName.DotPElement))
+ {
+ PassengerName = new PassengerName();
+ PassengerName.Parse(line);
+ }
+ else if (line.StartsWith(VersionSupplementaryData.DotVElement))
+ {
+ VersionSupplementaryData = new VersionSupplementaryData();
+ VersionSupplementaryData.Parse(line);
+ }
+
+ totalBytesProcessed += line.Length;
+
+ } while (totalBytesProcessed < typeBString.Length);
+ }
+
+ ///
+ public string ToTypeB()
+ {
+ string dotElements = string.Empty;
+
+ if (OutboundFlight != null)
+ {
+ dotElements += OutboundFlight.ToTypeB();
+ }
+
+ if (BaggageTagDetails != null)
+ {
+ dotElements += BaggageTagDetails.ToTypeB();
+ }
+
+ if (PassengerName != null)
+ {
+ dotElements += PassengerName.ToTypeB();
+ }
+
+ if (VersionSupplementaryData != null)
+ {
+ dotElements += VersionSupplementaryData.ToTypeB();
+ }
+
+ return $"{StartOfBSM}{Environment.NewLine}{ChangeOfStatus}{Environment.NewLine}{dotElements}{EndOfBSM}{Environment.NewLine}";
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/BSMPDU.cs b/JMayer.Example.WindowsService/BSM/BSMPDU.cs
new file mode 100644
index 0000000..6a80730
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/BSMPDU.cs
@@ -0,0 +1,28 @@
+using JMayer.Net.ProtocolDataUnit;
+using System.ComponentModel.DataAnnotations;
+using System.Text;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class represents a BSM PDU.
+///
+public class BSMPDU : PDU
+{
+ ///
+ /// The property gets/sets the BSM string.
+ ///
+ public BSM BSM { get; init; } = new();
+
+ ///
+ public override byte[] ToBytes()
+ {
+ return Encoding.ASCII.GetBytes(BSM.ToTypeB());
+ }
+
+ ///
+ public override List Validate()
+ {
+ return ValidateDataAnnotations();
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/BSMParser.cs b/JMayer.Example.WindowsService/BSM/BSMParser.cs
new file mode 100644
index 0000000..7490b5f
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/BSMParser.cs
@@ -0,0 +1,49 @@
+using JMayer.Net.ProtocolDataUnit;
+using System.Text;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class manages parsing the BSM.
+///
+public class BSMParser : PDUParser
+{
+ ///
+ protected override PDUParserResult SubClassParse(byte[] bytes)
+ {
+ int totalBytesProcessed = 0;
+ List pdus = [];
+
+ string bytesAsString = Encoding.ASCII.GetString(bytes);
+
+ do
+ {
+ int startIndex = bytesAsString.IndexOf(BSM.StartOfBSM, totalBytesProcessed);
+
+ //Start was not found so exit.
+ if (startIndex == -1)
+ {
+ break;
+ }
+
+ int endIndex = bytesAsString.IndexOf(BSM.EndOfBSM, startIndex);
+
+ //End was not found or start is actual end, exit.
+ if (endIndex == -1 || startIndex == endIndex)
+ {
+ break;
+ }
+
+ string bsmString = bytesAsString.Substring(startIndex, endIndex - startIndex);
+
+ BSMPDU pdu = new();
+ pdu.BSM.Parse(bsmString);
+
+ pdus.Add(pdu);
+ totalBytesProcessed += endIndex - startIndex;
+
+ } while (totalBytesProcessed < bytes.Length);
+
+ return new PDUParserResult(pdus, totalBytesProcessed);
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs b/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
new file mode 100644
index 0000000..a21646b
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
@@ -0,0 +1,62 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class represents the baggage tag details in the BSM.
+///
+public class BaggageTagDetails : ITypeB
+{
+ ///
+ /// The property gets the number of baggage tag numbers.
+ ///
+ public int Count
+ {
+ get => BaggageTagNumbers.Count;
+ }
+
+ ///
+ /// The property gets a list of baggage tag numbers.
+ ///
+ ///
+ [Length(1, 999)]
+ public List BaggageTagNumbers { get; init; } = [];
+
+ ///
+ /// The constant for the .N element.
+ ///
+ public const string DotNElement = ".N";
+
+ ///
+ public void Parse(string typeBString)
+ {
+ //Remove the identifier and new line.
+ typeBString = typeBString.Replace($"{DotNElement}/", string.Empty);
+ typeBString = typeBString.Replace(Environment.NewLine, string.Empty);
+
+ if (typeBString.Length is 13)
+ {
+ if (long.TryParse(typeBString.AsSpan(0, 10), out long iataNumber) && int.TryParse(typeBString.AsSpan(10, 3), out int length))
+ {
+ for (int index = 0; index < length; index++)
+ {
+ string iataString = (iataNumber + index).ToString().PadLeft(10, '0');
+ BaggageTagNumbers.Add(iataString);
+ }
+ }
+ }
+ }
+
+ ///
+ public string ToTypeB()
+ {
+ if (Count == 0)
+ {
+ return string.Empty;
+ }
+ else
+ {
+ return $"{DotNElement}/{BaggageTagNumbers[0]}{Count:D3}{Environment.NewLine}";
+ }
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/ITypeB.cs b/JMayer.Example.WindowsService/BSM/ITypeB.cs
new file mode 100644
index 0000000..aa235b9
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/ITypeB.cs
@@ -0,0 +1,19 @@
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The interface contains common methods for the type B string format.
+///
+public interface ITypeB
+{
+ ///
+ /// The method parses a type B string.
+ ///
+ /// The type B string to parse.
+ void Parse(string typeBString);
+
+ ///
+ /// The method returns the object in the type B string format.
+ ///
+ /// A string in the type B format.
+ string ToTypeB();
+}
diff --git a/JMayer.Example.WindowsService/BSM/OutboundFlight.cs b/JMayer.Example.WindowsService/BSM/OutboundFlight.cs
new file mode 100644
index 0000000..e78d879
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/OutboundFlight.cs
@@ -0,0 +1,107 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class represents the outbound flight information in the BSM.
+///
+public class OutboundFlight : ITypeB
+{
+ ///
+ /// The property gets the airline for the flight.
+ ///
+ [Required]
+ [RegularExpression("^[A-Z0-9]{2}$", ErrorMessage = "The airline must be 2 alphanumeric characters.")]
+ public string Airline { get; private set; } = string.Empty;
+
+ ///
+ /// The property gets the class of travel for passenger for this flight.
+ ///
+ [RegularExpression("^([A-Z]{1})?$", ErrorMessage = "The class of travel must be 1 capital letter or empty.")]
+ public string ClassOfTravel { get; private set; } = string.Empty;
+
+ ///
+ /// The property gets the destination for this flight.
+ ///
+ [RegularExpression("^([A-Z]{3})?$", ErrorMessage = "The destination must be 3 letters or empty.")]
+ public string Destination { get; private set; } = string.Empty;
+
+ ///
+ /// The constant for the .F element.
+ ///
+ public const string DotFElement = ".F";
+
+ ///
+ /// The property gets the date this flight flies.
+ ///
+ ///
+ /// This will be formatted like JAN01.
+ ///
+ [Required]
+ [RegularExpression("^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)[0-9]{2}$", ErrorMessage = "The flight date must be the first three letters of the month, capitalized, followed by the day of month, 2 digits.")]
+ public string FlightDate { get; private set; } = string.Empty;
+
+ ///
+ /// The property gets the identifier for the flight.
+ ///
+ [Required]
+ [RegularExpression("^([0-9]{4})|([0-9]{4}[A-Z]{1})$", ErrorMessage = "The flight number must be 4 digits and optionally a capital letter.")]
+ public string FlightNumber { get; private set; } = string.Empty;
+
+ ///
+ public void Parse(string typeBString)
+ {
+ //Remove the identifier so the elements can be broken apart with Split().
+ typeBString = typeBString.Replace($"{DotFElement}/", string.Empty);
+ typeBString = typeBString.Replace(Environment.NewLine, string.Empty);
+
+ string[] elements = typeBString.Split('/');
+
+ //Handle parsing the airline and flight number.
+ if (elements.Length > 0 && !string.IsNullOrEmpty(elements[0]))
+ {
+ string airlineAndFlight = elements[0];
+
+ if (airlineAndFlight.Length >= 6)
+ {
+ Airline = airlineAndFlight.Substring(0, 2);
+ FlightNumber = airlineAndFlight.Substring(2, airlineAndFlight.Length - 2);
+ }
+ }
+
+ //Handle parsing the flight date.
+ if (elements.Length > 1 && !string.IsNullOrEmpty(elements[1]))
+ {
+ FlightDate = elements[1];
+ }
+
+ //Handle parsing the destination.
+ if (elements.Length > 2 && !string.IsNullOrEmpty(elements[2]))
+ {
+ Destination = elements[2];
+ }
+
+ //Handle parsing the class of travel.
+ if (elements.Length > 3 && !string.IsNullOrEmpty(elements[3]))
+ {
+ ClassOfTravel = elements[3];
+ }
+ }
+
+ ///
+ public string ToTypeB()
+ {
+ if (!string.IsNullOrEmpty(ClassOfTravel))
+ {
+ return $"{DotFElement}/{Airline}{FlightNumber}/{FlightDate}/{Destination}/{ClassOfTravel}{Environment.NewLine}";
+ }
+ else if (!string.IsNullOrEmpty(Destination))
+ {
+ return $"{DotFElement}/{Airline}{FlightNumber}/{FlightDate}/{Destination}{Environment.NewLine}";
+ }
+ else
+ {
+ return $"{DotFElement}/{Airline}{FlightNumber}/{FlightDate}{Environment.NewLine}";
+ }
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/PassengerName.cs b/JMayer.Example.WindowsService/BSM/PassengerName.cs
new file mode 100644
index 0000000..dd27170
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/PassengerName.cs
@@ -0,0 +1,80 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.RegularExpressions;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class represents the passenger name in the BSM.
+///
+public class PassengerName : ITypeB
+{
+ ///
+ /// The constant for the .P element.
+ ///
+ public const string DotPElement = ".P";
+
+ ///
+ /// The property gets/sets the given names for the passenger.
+ ///
+ public List GivenNames { get; init; } = [];
+
+ ///
+ /// The property gets/sets the surnaame for the passenger.
+ ///
+ [Required]
+ public string SurName { get; set; } = string.Empty;
+
+ ///
+ public void Parse(string typeBString)
+ {
+ //Remove the identifier so the elements can be broken apart with Split().
+ typeBString = typeBString.Replace($"{DotPElement}/", string.Empty);
+ typeBString = typeBString.Replace(Environment.NewLine , string.Empty);
+
+ string[] elements = typeBString.Split('/');
+
+ //Handle parsing the surname.
+ if (elements.Length > 0 && !string.IsNullOrEmpty(elements[0]))
+ {
+ //The number of given names can be infront of the surname as either a 1 or 2 digit number
+ //so remove the number if it exists.
+ if (Regex.IsMatch(elements[0].AsSpan(0, 2), "^\\d$"))
+ {
+ SurName = elements[0].Substring(2);
+ }
+ else if (Regex.IsMatch(elements[0].AsSpan(0, 1), "^\\d$"))
+ {
+ SurName = elements[0].Substring(1);
+ }
+ else
+ {
+ SurName = elements[0];
+ }
+ }
+
+ //The rest of the elements will be the given names so add them to the list.
+ if (elements.Length > 1)
+ {
+ for (int index = 1; index < elements.Length; index++)
+ {
+ if (!string.IsNullOrEmpty(elements[index]))
+ {
+ GivenNames.Add(elements[index]);
+ }
+ }
+ }
+ }
+
+ ///
+ public string ToTypeB()
+ {
+ string givenNames = string.Empty;
+
+ foreach (string givenName in GivenNames)
+ {
+ givenNames += $"/{givenName}";
+ }
+
+ return $"{DotPElement}/{GivenNames.Count}{SurName}{givenNames}";
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs b/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
new file mode 100644
index 0000000..c761d6d
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
@@ -0,0 +1,82 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class represents the version & supplementary data in the BSM.
+///
+public class VersionSupplementaryData : ITypeB
+{
+ ///
+ /// The property gets the aiport who sent the BSM.
+ ///
+ [Required]
+ [RegularExpression("^([A-Z]{3})?$", ErrorMessage = "The airport code must be 3 letters or empty.")]
+ public string AirportCode { get; private set; } = string.Empty;
+
+ ///
+ /// The property gets the baggage source indicator (local, transfer, remote or terminating).
+ ///
+ ///
+ [Required]
+ [RegularExpression("^(L|R|X|T)$", ErrorMessage = "The baggage source indicator must be L, R, X or T.")]
+ public string BaggageSourceIndicator { get; private set; } = string.Empty;
+
+ ///
+ /// The property gets the version number for the data dictionary.
+ ///
+ [Required]
+ public int DataDictionaryVersionNumber { get; private set; }
+
+ ///
+ /// The constant for the .V element.
+ ///
+ public const string DotVElement = ".V";
+
+ ///
+ /// The constant for the local baggage source indicator.
+ ///
+ public const string LocalBaggageSourceIndicator = "L";
+
+ ///
+ /// The constant for the remote baggage source indicator.
+ ///
+ public const string RemoteBaggageSourceIndicator = "R";
+
+ ///
+ /// The constant for the terminating baggage source indicator.
+ ///
+ public const string TerminatingBaggageSourceIndicator = "X";
+
+ ///
+ /// The constant for the transfer baggage source indicator.
+ ///
+ public const string TransferBaggageSourceIndicator = "T";
+
+ ///
+ public void Parse(string typeBString)
+ {
+ //Remove the identifier so the elements can be broken apart with Split().
+ typeBString = typeBString.Replace($"{DotVElement}/", string.Empty);
+ typeBString = typeBString.Replace(Environment.NewLine, string.Empty);
+
+ string[] elements = typeBString.Split('/');
+
+ if (elements.Length > 0 && elements[0].Length is 5)
+ {
+ if (int.TryParse(elements[0].AsSpan(0, 1), out int dataDictionaryVersionNumber))
+ {
+ DataDictionaryVersionNumber = dataDictionaryVersionNumber;
+ }
+
+ BaggageSourceIndicator = elements[0].Substring(1, 1);
+ AirportCode = elements[0].Substring(2, 3);
+ }
+ }
+
+ ///
+ public string ToTypeB()
+ {
+ return $"{DotVElement}/{DataDictionaryVersionNumber}{BaggageSourceIndicator}{AirportCode}{Environment.NewLine}";
+ }
+}
From 13d1d4b6b2912d61c584713a2a217e55c0a03231 Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Thu, 15 Aug 2024 15:07:38 -0400
Subject: [PATCH 02/11] Various fixes; initial parser unit test
---
JMayer.Example.WindowsService.sln | 8 +-
.../BSM/BMSEqualityComparer.cs | 35 ++
JMayer.Example.WindowsService/BSM/BSM.cs | 57 +-
JMayer.Example.WindowsService/BSM/BSMPDU.cs | 24 +-
.../BSM/BSMParser.cs | 15 +-
.../BSM/BaggageTagDetailEqualityComparer.cs | 47 ++
.../BSM/BaggageTagDetails.cs | 1 -
.../BSM/DateTimeExtensions.cs | 15 +
.../BSM/OutboundFlight.cs | 14 +-
.../BSM/OutboundFlightEqualityComparer.cs | 36 ++
.../BSM/PassengerNameEqualityComparer.cs | 47 ++
.../BSM/VersionSupplementaryData.cs | 6 +-
...ersionSupplementaryDataEqualityComparer.cs | 34 +
TestProject/Test/BSMParserUnitTest.cs | 594 ++++++++++++++++++
TestProject/TestProject.csproj | 27 +
15 files changed, 918 insertions(+), 42 deletions(-)
create mode 100644 JMayer.Example.WindowsService/BSM/BMSEqualityComparer.cs
create mode 100644 JMayer.Example.WindowsService/BSM/BaggageTagDetailEqualityComparer.cs
create mode 100644 JMayer.Example.WindowsService/BSM/DateTimeExtensions.cs
create mode 100644 JMayer.Example.WindowsService/BSM/OutboundFlightEqualityComparer.cs
create mode 100644 JMayer.Example.WindowsService/BSM/PassengerNameEqualityComparer.cs
create mode 100644 JMayer.Example.WindowsService/BSM/VersionSupplementaryDataEqualityComparer.cs
create mode 100644 TestProject/Test/BSMParserUnitTest.cs
create mode 100644 TestProject/TestProject.csproj
diff --git a/JMayer.Example.WindowsService.sln b/JMayer.Example.WindowsService.sln
index 27d2a9d..4d39097 100644
--- a/JMayer.Example.WindowsService.sln
+++ b/JMayer.Example.WindowsService.sln
@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35208.52
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JMayer.Example.WindowsService", "JMayer.Example.WindowsService\JMayer.Example.WindowsService.csproj", "{0BC4DB3A-032F-43BA-BC39-380442F0BED0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JMayer.Example.WindowsService", "JMayer.Example.WindowsService\JMayer.Example.WindowsService.csproj", "{0BC4DB3A-032F-43BA-BC39-380442F0BED0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{69EB99BC-2876-4339-AAFE-2AB2B406C0BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +17,10 @@ Global
{0BC4DB3A-032F-43BA-BC39-380442F0BED0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BC4DB3A-032F-43BA-BC39-380442F0BED0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BC4DB3A-032F-43BA-BC39-380442F0BED0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {69EB99BC-2876-4339-AAFE-2AB2B406C0BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {69EB99BC-2876-4339-AAFE-2AB2B406C0BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {69EB99BC-2876-4339-AAFE-2AB2B406C0BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {69EB99BC-2876-4339-AAFE-2AB2B406C0BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/JMayer.Example.WindowsService/BSM/BMSEqualityComparer.cs b/JMayer.Example.WindowsService/BSM/BMSEqualityComparer.cs
new file mode 100644
index 0000000..64a6e81
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/BMSEqualityComparer.cs
@@ -0,0 +1,35 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class manages comparing two BSM objects.
+///
+public class BMSEqualityComparer : IEqualityComparer
+{
+ ///
+ public bool Equals(BSM? x, BSM? y)
+ {
+ if (x == null || y == null)
+ {
+ return false;
+ }
+
+ BaggageTagDetailEqualityComparer baggageTagDetailEqualityComparer = new();
+ OutboundFlightEqualityComparer outboundFlightEqualityComparer = new();
+ PassengerNameEqualityComparer passengerNameEqualityComparer = new();
+ VersionSupplementaryDataEqualityComparer versionSupplementaryDataEqualityComparer = new();
+
+ return baggageTagDetailEqualityComparer.Equals(x.BaggageTagDetails, y.BaggageTagDetails)
+ && x.ChangeOfStatus == y.ChangeOfStatus
+ && outboundFlightEqualityComparer.Equals(x.OutboundFlight, y.OutboundFlight)
+ && passengerNameEqualityComparer.Equals(x.PassengerName, y.PassengerName)
+ && versionSupplementaryDataEqualityComparer.Equals(x.VersionSupplementaryData, y.VersionSupplementaryData);
+ }
+
+ ///
+ public int GetHashCode([DisallowNull] BSM obj)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/BSM.cs b/JMayer.Example.WindowsService/BSM/BSM.cs
index eedcd96..b7fa8b5 100644
--- a/JMayer.Example.WindowsService/BSM/BSM.cs
+++ b/JMayer.Example.WindowsService/BSM/BSM.cs
@@ -20,7 +20,7 @@ public class BSM : ITypeB
///
/// The property gets the baggage tag details.
///
- public BaggageTagDetails? BaggageTagDetails { get; private set; }
+ public BaggageTagDetails? BaggageTagDetails { get; set; }
///
/// The constant for the change change of status.
@@ -31,7 +31,7 @@ public class BSM : ITypeB
/// The property gets the change of status.
///
[Required]
- public string ChangeOfStatus { get; private set; } = string.Empty;
+ public string ChangeOfStatus { get; set; } = string.Empty;
///
/// The constant for the delete change of status.
@@ -46,12 +46,12 @@ public class BSM : ITypeB
///
/// The property gets the outbound flight information.
///
- public OutboundFlight? OutboundFlight { get; private set; }
+ public OutboundFlight? OutboundFlight { get; set; }
///
/// The property gets the passenger name.
///
- public PassengerName? PassengerName { get; private set; }
+ public PassengerName? PassengerName { get; set; }
///
/// The property gets when the BSM was received.
@@ -66,7 +66,7 @@ public class BSM : ITypeB
///
/// The property gets the version supplementary data.
///
- public VersionSupplementaryData? VersionSupplementaryData { get; private set; }
+ public VersionSupplementaryData? VersionSupplementaryData { get; set; }
///
/// The method returns the change of status from the BSM.
@@ -75,46 +75,47 @@ public class BSM : ITypeB
/// The change of status.
private static string GetChangeOfStatus(string bsm)
{
- int index = bsm.IndexOf('.');
+ string changeOfStatus = bsm.Substring(0, 3);
- if (index > -1)
+ if (changeOfStatus == Change)
{
- string header = bsm.Substring(0, index);
-
- if (header.Contains(Change))
- {
- return Change;
- }
- else if (header.Contains(Delete))
- {
- return Delete;
- }
- else
- {
- return Add;
- }
+ return Change;
+ }
+ else if (changeOfStatus == Delete)
+ {
+ return Delete;
+ }
+ else
+ {
+ return Add;
}
-
- return string.Empty;
}
///
public void Parse(string typeBString)
{
- ChangeOfStatus = GetChangeOfStatus(typeBString);
+ //Remove the end identifier because it's no longer needed.
+ //This needs to be removed before start else you end up with only END.
+ typeBString = typeBString.Replace($"{EndOfBSM}{Environment.NewLine}", string.Empty);
+ typeBString = typeBString.Replace(EndOfBSM, string.Empty);
- //Remove the start & end identifiers because they're no longer needed.
+ //Remove the start identifier because it's no longer needed.
+ typeBString = typeBString.Replace($"{StartOfBSM}{Environment.NewLine}", string.Empty);
typeBString = typeBString.Replace(StartOfBSM, string.Empty);
- typeBString = typeBString.Replace(EndOfBSM, string.Empty);
+
+ //Get the change of status and then remove it because its no longer needed
+ ChangeOfStatus = GetChangeOfStatus(typeBString);
+ typeBString = typeBString.Replace($"{ChangeOfStatus}{Environment.NewLine}", string.Empty);
+ typeBString = typeBString.Replace(ChangeOfStatus, string.Empty);
int totalBytesProcessed = 0;
do
{
- int startIndex = typeBString.IndexOf('.');
+ int startIndex = typeBString.IndexOf('.', totalBytesProcessed);
//Dot was not found so exit.
- if (startIndex > -1)
+ if (startIndex == -1)
{
break;
}
diff --git a/JMayer.Example.WindowsService/BSM/BSMPDU.cs b/JMayer.Example.WindowsService/BSM/BSMPDU.cs
index 6a80730..9fd1fcc 100644
--- a/JMayer.Example.WindowsService/BSM/BSMPDU.cs
+++ b/JMayer.Example.WindowsService/BSM/BSMPDU.cs
@@ -23,6 +23,28 @@ public override byte[] ToBytes()
///
public override List Validate()
{
- return ValidateDataAnnotations();
+ List validationResults = [];
+
+ if (BSM.BaggageTagDetails != null)
+ {
+ Validator.TryValidateObject(BSM.BaggageTagDetails, new ValidationContext(BSM.BaggageTagDetails), validationResults, validateAllProperties: true);
+ }
+
+ if (BSM.OutboundFlight != null)
+ {
+ Validator.TryValidateObject(BSM.OutboundFlight, new ValidationContext(BSM.OutboundFlight), validationResults, validateAllProperties: true);
+ }
+
+ if (BSM.PassengerName != null)
+ {
+ Validator.TryValidateObject(BSM.PassengerName, new ValidationContext(BSM.PassengerName), validationResults, validateAllProperties: true);
+ }
+
+ if (BSM.VersionSupplementaryData != null)
+ {
+ Validator.TryValidateObject(BSM.VersionSupplementaryData, new ValidationContext(BSM.VersionSupplementaryData), validationResults, validateAllProperties: true);
+ }
+
+ return validationResults;
}
}
diff --git a/JMayer.Example.WindowsService/BSM/BSMParser.cs b/JMayer.Example.WindowsService/BSM/BSMParser.cs
index 7490b5f..a34597c 100644
--- a/JMayer.Example.WindowsService/BSM/BSMParser.cs
+++ b/JMayer.Example.WindowsService/BSM/BSMParser.cs
@@ -28,12 +28,25 @@ protected override PDUParserResult SubClassParse(byte[] bytes)
int endIndex = bytesAsString.IndexOf(BSM.EndOfBSM, startIndex);
- //End was not found or start is actual end, exit.
+ //End was not found or start is actually the end, exit.
if (endIndex == -1 || startIndex == endIndex)
{
break;
}
+ //Add the length of the end of BSM so the end is included.
+ endIndex += BSM.EndOfBSM.Length;
+
+ //Ensures the new line is included, if it exists.
+ if (endIndex < bytesAsString.Length && bytesAsString[endIndex] == '\n')
+ {
+ endIndex++;
+ }
+ else if (endIndex + 1 < bytesAsString.Length && bytesAsString[endIndex] == '\r' && bytesAsString[endIndex + 1] == '\n')
+ {
+ endIndex += 2;
+ }
+
string bsmString = bytesAsString.Substring(startIndex, endIndex - startIndex);
BSMPDU pdu = new();
diff --git a/JMayer.Example.WindowsService/BSM/BaggageTagDetailEqualityComparer.cs b/JMayer.Example.WindowsService/BSM/BaggageTagDetailEqualityComparer.cs
new file mode 100644
index 0000000..59e5741
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/BaggageTagDetailEqualityComparer.cs
@@ -0,0 +1,47 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class manages comparing two BaggageTagDetail objects.
+///
+public class BaggageTagDetailEqualityComparer : IEqualityComparer
+{
+ ///
+ public bool Equals(BaggageTagDetails? x, BaggageTagDetails? y)
+ {
+ if (x == null && y == null)
+ {
+ return true;
+ }
+ else if (x != null && y != null)
+ {
+ if (x.Count != y.Count)
+ {
+ return false;
+ }
+ else
+ {
+ foreach (string tag in x.BaggageTagNumbers)
+ {
+ if (!y.BaggageTagNumbers.Contains(tag))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ public int GetHashCode([DisallowNull] BaggageTagDetails obj)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs b/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
index a21646b..ca8ab8a 100644
--- a/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
+++ b/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
@@ -18,7 +18,6 @@ public int Count
///
/// The property gets a list of baggage tag numbers.
///
- ///
[Length(1, 999)]
public List BaggageTagNumbers { get; init; } = [];
diff --git a/JMayer.Example.WindowsService/BSM/DateTimeExtensions.cs b/JMayer.Example.WindowsService/BSM/DateTimeExtensions.cs
new file mode 100644
index 0000000..60beb33
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/DateTimeExtensions.cs
@@ -0,0 +1,15 @@
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The statis class contains extension methods for the DateTime.
+///
+public static class DateTimeExtensions
+{
+ ///
+ /// The method returns the date time as a string in the 01JAN format.
+ ///
+ /// The date time to be formatted.
+ /// A formatted string.
+ public static string ToDayMonthFormat(this DateTime dateTime)
+ => $"{dateTime.Day:00}{dateTime.ToString("MMMM").Substring(0, 3).ToUpper()}";
+}
diff --git a/JMayer.Example.WindowsService/BSM/OutboundFlight.cs b/JMayer.Example.WindowsService/BSM/OutboundFlight.cs
index e78d879..c7dda73 100644
--- a/JMayer.Example.WindowsService/BSM/OutboundFlight.cs
+++ b/JMayer.Example.WindowsService/BSM/OutboundFlight.cs
@@ -12,19 +12,19 @@ public class OutboundFlight : ITypeB
///
[Required]
[RegularExpression("^[A-Z0-9]{2}$", ErrorMessage = "The airline must be 2 alphanumeric characters.")]
- public string Airline { get; private set; } = string.Empty;
+ public string Airline { get; set; } = string.Empty;
///
/// The property gets the class of travel for passenger for this flight.
///
[RegularExpression("^([A-Z]{1})?$", ErrorMessage = "The class of travel must be 1 capital letter or empty.")]
- public string ClassOfTravel { get; private set; } = string.Empty;
+ public string ClassOfTravel { get; set; } = string.Empty;
///
/// The property gets the destination for this flight.
///
[RegularExpression("^([A-Z]{3})?$", ErrorMessage = "The destination must be 3 letters or empty.")]
- public string Destination { get; private set; } = string.Empty;
+ public string Destination { get; set; } = string.Empty;
///
/// The constant for the .F element.
@@ -35,18 +35,18 @@ public class OutboundFlight : ITypeB
/// The property gets the date this flight flies.
///
///
- /// This will be formatted like JAN01.
+ /// This will be formatted like 01JAN.
///
[Required]
- [RegularExpression("^(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)[0-9]{2}$", ErrorMessage = "The flight date must be the first three letters of the month, capitalized, followed by the day of month, 2 digits.")]
- public string FlightDate { get; private set; } = string.Empty;
+ [RegularExpression("^[0-9]{2}(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$", ErrorMessage = "The flight date must be the day of the month (2 digits) followed by the first three letters of the month (capitalized).")]
+ public string FlightDate { get; set; } = string.Empty;
///
/// The property gets the identifier for the flight.
///
[Required]
[RegularExpression("^([0-9]{4})|([0-9]{4}[A-Z]{1})$", ErrorMessage = "The flight number must be 4 digits and optionally a capital letter.")]
- public string FlightNumber { get; private set; } = string.Empty;
+ public string FlightNumber { get; set; } = string.Empty;
///
public void Parse(string typeBString)
diff --git a/JMayer.Example.WindowsService/BSM/OutboundFlightEqualityComparer.cs b/JMayer.Example.WindowsService/BSM/OutboundFlightEqualityComparer.cs
new file mode 100644
index 0000000..0715ea0
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/OutboundFlightEqualityComparer.cs
@@ -0,0 +1,36 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class manages comparing two OutboundFlight objects.
+///
+public class OutboundFlightEqualityComparer : IEqualityComparer
+{
+ ///
+ public bool Equals(OutboundFlight? x, OutboundFlight? y)
+ {
+ if (x == null && y == null)
+ {
+ return true;
+ }
+ else if (x != null && y != null)
+ {
+ return x.Airline == y.Airline
+ && x.ClassOfTravel == y.ClassOfTravel
+ && x.Destination == y.Destination
+ && x.FlightDate == y.FlightDate
+ && x.FlightNumber == y.FlightNumber;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ public int GetHashCode([DisallowNull] OutboundFlight obj)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/PassengerNameEqualityComparer.cs b/JMayer.Example.WindowsService/BSM/PassengerNameEqualityComparer.cs
new file mode 100644
index 0000000..23a59db
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/PassengerNameEqualityComparer.cs
@@ -0,0 +1,47 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class manages comparing two PassengerName objects.
+///
+public class PassengerNameEqualityComparer : IEqualityComparer
+{
+ ///
+ public bool Equals(PassengerName? x, PassengerName? y)
+ {
+ if (x == null && y == null)
+ {
+ return true;
+ }
+ else if (x != null && y != null)
+ {
+ if (x.SurName != y.SurName || x.GivenNames.Count != y.GivenNames.Count)
+ {
+ return false;
+ }
+ else
+ {
+ foreach (string givenName in x.GivenNames)
+ {
+ if (!y.GivenNames.Contains(givenName))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ public int GetHashCode([DisallowNull] PassengerName obj)
+ {
+ throw new NotImplementedException();
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs b/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
index c761d6d..62605a0 100644
--- a/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
+++ b/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
@@ -12,7 +12,7 @@ public class VersionSupplementaryData : ITypeB
///
[Required]
[RegularExpression("^([A-Z]{3})?$", ErrorMessage = "The airport code must be 3 letters or empty.")]
- public string AirportCode { get; private set; } = string.Empty;
+ public string AirportCode { get; set; } = string.Empty;
///
/// The property gets the baggage source indicator (local, transfer, remote or terminating).
@@ -20,13 +20,13 @@ public class VersionSupplementaryData : ITypeB
///
[Required]
[RegularExpression("^(L|R|X|T)$", ErrorMessage = "The baggage source indicator must be L, R, X or T.")]
- public string BaggageSourceIndicator { get; private set; } = string.Empty;
+ public string BaggageSourceIndicator { get; set; } = string.Empty;
///
/// The property gets the version number for the data dictionary.
///
[Required]
- public int DataDictionaryVersionNumber { get; private set; }
+ public int DataDictionaryVersionNumber { get; set; }
///
/// The constant for the .V element.
diff --git a/JMayer.Example.WindowsService/BSM/VersionSupplementaryDataEqualityComparer.cs b/JMayer.Example.WindowsService/BSM/VersionSupplementaryDataEqualityComparer.cs
new file mode 100644
index 0000000..0f6c3d5
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/VersionSupplementaryDataEqualityComparer.cs
@@ -0,0 +1,34 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class manages comparing two VersionSupplementaryData objects.
+///
+public class VersionSupplementaryDataEqualityComparer : IEqualityComparer
+{
+ ///
+ public bool Equals(VersionSupplementaryData? x, VersionSupplementaryData? y)
+ {
+ if (x == null && y == null)
+ {
+ return true;
+ }
+ else if (x != null && y != null)
+ {
+ return x.AirportCode == y.AirportCode
+ && x.BaggageSourceIndicator == y.BaggageSourceIndicator
+ && x.DataDictionaryVersionNumber == y.DataDictionaryVersionNumber;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ public int GetHashCode([DisallowNull] VersionSupplementaryData obj)
+ {
+ return obj.GetHashCode();
+ }
+}
diff --git a/TestProject/Test/BSMParserUnitTest.cs b/TestProject/Test/BSMParserUnitTest.cs
new file mode 100644
index 0000000..ba60b03
--- /dev/null
+++ b/TestProject/Test/BSMParserUnitTest.cs
@@ -0,0 +1,594 @@
+using JMayer.Example.WindowsService.BSM;
+using JMayer.Net.ProtocolDataUnit;
+using System.Text;
+
+namespace TestProject.Test;
+
+///
+/// The class manages testing the BSM parser.
+///
+public class BSMParserUnitTest
+{
+ ///
+ /// The constant for the .F airline.
+ ///
+ private const string DotFAirline = "AA";
+
+ ///
+ /// The constant for the .F class of travel.
+ ///
+ private const string DotFClassOfTravel = "A";
+
+ ///
+ /// The constant for the .F destination.
+ ///
+ private const string DotFDestination = "MSY";
+
+ ///
+ /// The constant for the .F flight number.
+ ///
+ private const string DotFFlightNumber = "1234";
+
+ ///
+ /// The constant for the .F invalid flight number.
+ ///
+ private const string DotFInvalidFlightNumber = "ABCD";
+
+ ///
+ /// The constant for the .N tag number.
+ ///
+ private const string DotNTagNumber = "0001123456";
+
+ ///
+ /// The constant for the .P given name.
+ ///
+ private const string DotPGivenName = "PASSENGER";
+
+ ///
+ /// The constant for the .P surname.
+ ///
+ private const string DotPSurName = "TEST";
+
+ ///
+ /// The constant for the .V airport code.
+ ///
+ private const string DotVAirportCode = "MCO";
+
+ ///
+ /// The method verifies a BSM with all fields set can be parsed.
+ ///
+ [Fact]
+ public void VerifyAllFields()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.True(result.PDUs[0].IsValid, "The BSM is not valid.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm, ((BSMPDU)result.PDUs[0]).BSM), "The BSM does not equal the BSM in the parser results.");
+ }
+
+ ///
+ /// The method verifies a BSM with a bad .F airline will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotFAirlineValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline.ToLower(),
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(OutboundFlight.Airline), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
+ ///
+ /// The method verifies a BSM with a bad .F class of travel will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotFClassOfTravelValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel.ToLower(),
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(OutboundFlight.ClassOfTravel), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
+ ///
+ /// The method verifies a BSM with a bad .F destination will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotFDestinationValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination.ToLower(),
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(OutboundFlight.Destination), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
+ ///
+ /// The method verifies a BSM with a bad .F flight number will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotFFlightNumberValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFInvalidFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(OutboundFlight.FlightNumber), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
+ ///
+ /// The method verifies a BSM with a .F element with no class of travel can be parsed.
+ ///
+ [Fact]
+ public void VerifyDotFNoClassOfTravel()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.True(result.PDUs[0].IsValid, "The BSM is not valid.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm, ((BSMPDU)result.PDUs[0]).BSM), "The BSM does not equal the BSM in the parser results.");
+ }
+
+ ///
+ /// The method verifies a BSM with a .F element with no destination can be parsed; class of travel
+ /// will also not be set.
+ ///
+ [Fact]
+ public void VerifyDotFNoDestination()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.True(result.PDUs[0].IsValid, "The BSM is not valid.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm, ((BSMPDU)result.PDUs[0]).BSM), "The BSM does not equal the BSM in the parser results.");
+ }
+
+ ///
+ /// The method verifies a BSM with a .N element with multiple tags can be parsed.
+ ///
+ [Fact]
+ public void VerifyDotNMultipleTags()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber, "0001123457", "0001123458", "0001123459", "0001123460"],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.True(result.PDUs[0].IsValid, "The BSM is not valid.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm, ((BSMPDU)result.PDUs[0]).BSM), "The BSM does not equal the BSM in the parser results.");
+ }
+
+ ///
+ /// The method verifies a BSM with a .P element with multiple given names can be parsed.
+ ///
+ [Fact]
+ public void VerifyDotPMultipleGivenNames()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = ["Given Name 1", "Given Name 2", "Given Name 3"],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.True(result.PDUs[0].IsValid, "The BSM is not valid.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm, ((BSMPDU)result.PDUs[0]).BSM), "The BSM does not equal the BSM in the parser results.");
+ }
+
+ ///
+ /// The method verifies a BSM with a .P element with no given names can be parsed.
+ ///
+ [Fact]
+ public void VerifyDotPNoGivenNames()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.True(result.PDUs[0].IsValid, "The BSM is not valid.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm, ((BSMPDU)result.PDUs[0]).BSM), "The BSM does not equal the BSM in the parser results.");
+ }
+
+ ///
+ /// The method verfies multiple BSMs can be parsed.
+ ///
+ [Fact]
+ public void VerifyMultipleBSMs()
+ {
+ BSM bsm1 = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [$"{DotPGivenName}1"],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSM bsm2 = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = ["0001123457"],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [$"{DotPGivenName}2"],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = 1,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm1.ToTypeB() + bsm2.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Equal(2, result.PDUs.Count);
+ Assert.IsType(result.PDUs[0]);
+ Assert.IsType(result.PDUs[1]);
+ Assert.True(result.PDUs[0].IsValid, "BSM 1 is not valid.");
+ Assert.True(result.PDUs[1].IsValid, "BSM 2 is not valid.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm1, ((BSMPDU)result.PDUs[0]).BSM), "BSM 1 does not equal the first BSM in the parser results.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm2, ((BSMPDU)result.PDUs[1]).BSM), "BSM 2 does not equal the second BSM in the parser results.");
+ }
+}
diff --git a/TestProject/TestProject.csproj b/TestProject/TestProject.csproj
new file mode 100644
index 0000000..53e930b
--- /dev/null
+++ b/TestProject/TestProject.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 3ae89f07bbd5cd72b3511c2de28495aafa453c69 Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Fri, 16 Aug 2024 15:48:43 -0400
Subject: [PATCH 03/11] Finished BSM Parser unit test; various fixes
---
.../BSM/BaggageTagDetails.cs | 4 +
.../BSM/OutboundFlight.cs | 6 +-
.../BSM/PassengerName.cs | 4 +-
.../BSM/VersionSupplementaryData.cs | 4 +-
TestProject/Test/BSMParserUnitTest.cs | 364 +++++++++++++++++-
5 files changed, 361 insertions(+), 21 deletions(-)
diff --git a/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs b/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
index ca8ab8a..52492dc 100644
--- a/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
+++ b/JMayer.Example.WindowsService/BSM/BaggageTagDetails.cs
@@ -18,6 +18,10 @@ public int Count
///
/// The property gets a list of baggage tag numbers.
///
+ ///
+ /// Index 0 must be the first tag and index N must be the last tag. Each tag
+ /// after the first must increment by 1.
+ ///
[Length(1, 999)]
public List BaggageTagNumbers { get; init; } = [];
diff --git a/JMayer.Example.WindowsService/BSM/OutboundFlight.cs b/JMayer.Example.WindowsService/BSM/OutboundFlight.cs
index c7dda73..ecab295 100644
--- a/JMayer.Example.WindowsService/BSM/OutboundFlight.cs
+++ b/JMayer.Example.WindowsService/BSM/OutboundFlight.cs
@@ -11,7 +11,7 @@ public class OutboundFlight : ITypeB
/// The property gets the airline for the flight.
///
[Required]
- [RegularExpression("^[A-Z0-9]{2}$", ErrorMessage = "The airline must be 2 alphanumeric characters.")]
+ [RegularExpression("^[A-Z0-9]{2}$", ErrorMessage = "The airline must be 2 alphanumeric characters; the letters must be capital.")]
public string Airline { get; set; } = string.Empty;
///
@@ -23,7 +23,7 @@ public class OutboundFlight : ITypeB
///
/// The property gets the destination for this flight.
///
- [RegularExpression("^([A-Z]{3})?$", ErrorMessage = "The destination must be 3 letters or empty.")]
+ [RegularExpression("^([A-Z]{3})?$", ErrorMessage = "The destination must be 3 capital letters or empty.")]
public string Destination { get; set; } = string.Empty;
///
@@ -38,7 +38,7 @@ public class OutboundFlight : ITypeB
/// This will be formatted like 01JAN.
///
[Required]
- [RegularExpression("^[0-9]{2}(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$", ErrorMessage = "The flight date must be the day of the month (2 digits) followed by the first three letters of the month (capitalized).")]
+ [RegularExpression("^[0-9]{2}(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)$", ErrorMessage = "The flight date must be the day of the month (2 digits) followed by the first three capital letters of the month.")]
public string FlightDate { get; set; } = string.Empty;
///
diff --git a/JMayer.Example.WindowsService/BSM/PassengerName.cs b/JMayer.Example.WindowsService/BSM/PassengerName.cs
index dd27170..3fcf4ba 100644
--- a/JMayer.Example.WindowsService/BSM/PassengerName.cs
+++ b/JMayer.Example.WindowsService/BSM/PassengerName.cs
@@ -38,11 +38,11 @@ public void Parse(string typeBString)
{
//The number of given names can be infront of the surname as either a 1 or 2 digit number
//so remove the number if it exists.
- if (Regex.IsMatch(elements[0].AsSpan(0, 2), "^\\d$"))
+ if (elements[0].Length >= 2 && Regex.IsMatch(elements[0].AsSpan(0, 2), "^\\d$"))
{
SurName = elements[0].Substring(2);
}
- else if (Regex.IsMatch(elements[0].AsSpan(0, 1), "^\\d$"))
+ else if (elements[0].Length >= 1 && Regex.IsMatch(elements[0].AsSpan(0, 1), "^\\d$"))
{
SurName = elements[0].Substring(1);
}
diff --git a/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs b/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
index 62605a0..f1893b4 100644
--- a/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
+++ b/JMayer.Example.WindowsService/BSM/VersionSupplementaryData.cs
@@ -11,13 +11,12 @@ public class VersionSupplementaryData : ITypeB
/// The property gets the aiport who sent the BSM.
///
[Required]
- [RegularExpression("^([A-Z]{3})?$", ErrorMessage = "The airport code must be 3 letters or empty.")]
+ [RegularExpression("^[A-Z]{3}$", ErrorMessage = "The airport code must be 3 capital letters.")]
public string AirportCode { get; set; } = string.Empty;
///
/// The property gets the baggage source indicator (local, transfer, remote or terminating).
///
- ///
[Required]
[RegularExpression("^(L|R|X|T)$", ErrorMessage = "The baggage source indicator must be L, R, X or T.")]
public string BaggageSourceIndicator { get; set; } = string.Empty;
@@ -26,6 +25,7 @@ public class VersionSupplementaryData : ITypeB
/// The property gets the version number for the data dictionary.
///
[Required]
+ [Range(1, 9)]
public int DataDictionaryVersionNumber { get; set; }
///
diff --git a/TestProject/Test/BSMParserUnitTest.cs b/TestProject/Test/BSMParserUnitTest.cs
index ba60b03..d52f6a8 100644
--- a/TestProject/Test/BSMParserUnitTest.cs
+++ b/TestProject/Test/BSMParserUnitTest.cs
@@ -29,6 +29,11 @@ public class BSMParserUnitTest
///
private const string DotFFlightNumber = "1234";
+ ///
+ /// The constant for the .F invalid flight date.
+ ///
+ private const string DotFInvalidFlightDate = "JAN01";
+
///
/// The constant for the .F invalid flight number.
///
@@ -54,6 +59,21 @@ public class BSMParserUnitTest
///
private const string DotVAirportCode = "MCO";
+ ///
+ /// The constant for the .V data dictionary version number.
+ ///
+ private const int DotVDataDictionaryVersionNumber = 1;
+
+ ///
+ /// The constant for the .V invalid baggage source indicator.
+ ///
+ private const string DotVInvalidBaggageSourceIndicator = "A";
+
+ ///
+ /// The constant for the .V invalid data dictionary version number.
+ ///
+ private const int DotVInvalidDataDictionaryVersionNumber = 0;
+
///
/// The method verifies a BSM with all fields set can be parsed.
///
@@ -84,7 +104,7 @@ public void VerifyAllFields()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -130,7 +150,7 @@ public void VerifyDotFAirlineValidation()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -177,7 +197,7 @@ public void VerifyDotFClassOfTravelValidation()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -224,7 +244,7 @@ public void VerifyDotFDestinationValidation()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -241,6 +261,53 @@ public void VerifyDotFDestinationValidation()
Assert.Contains(nameof(OutboundFlight.Destination), result.PDUs[0].ValidationResults.First().MemberNames);
}
+ ///
+ /// The method verifies a BSM with a bad .F flight date will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotFFlightDateValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DotFInvalidFlightDate,
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(OutboundFlight.FlightDate), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
///
/// The method verifies a BSM with a bad .F flight number will cause a validation issue.
///
@@ -271,7 +338,7 @@ public void VerifyDotFFlightNumberValidation()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -317,7 +384,7 @@ public void VerifyDotFNoClassOfTravel()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -362,7 +429,7 @@ public void VerifyDotFNoDestination()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -408,7 +475,7 @@ public void VerifyDotNMultipleTags()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -424,6 +491,56 @@ public void VerifyDotNMultipleTags()
Assert.True(new BMSEqualityComparer().Equals(bsm, ((BSMPDU)result.PDUs[0]).BSM), "The BSM does not equal the BSM in the parser results.");
}
+ ///
+ /// The method verifies a BSM with a badly formatted .N will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotNTagValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ //Increasing Count from 001 to 0001 to force .N to have too many characters. That will create a range validation issue
+ //because the parser will fail to parse the .N element which leave the list empty.
+ bsmString = bsmString.Replace($"{DotNTagNumber}{bsm.BaggageTagDetails.Count:D3}", $"{DotNTagNumber}{bsm.BaggageTagDetails.Count:D4}");
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(BaggageTagDetails.BaggageTagNumbers), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
///
/// The method verifies a BSM with a .P element with multiple given names can be parsed.
///
@@ -454,7 +571,7 @@ public void VerifyDotPMultipleGivenNames()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -499,7 +616,7 @@ public void VerifyDotPNoGivenNames()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -515,6 +632,194 @@ public void VerifyDotPNoGivenNames()
Assert.True(new BMSEqualityComparer().Equals(bsm, ((BSMPDU)result.PDUs[0]).BSM), "The BSM does not equal the BSM in the parser results.");
}
+ ///
+ /// The method verifies a BSM with a bad .P surname will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotPSurNameValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = string.Empty,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(PassengerName.SurName), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
+ ///
+ /// The method verifies a BSM with a bad .V airport code will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotVAirportCodeValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode.ToLower(),
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(VersionSupplementaryData.AirportCode), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
+ ///
+ /// The method verifies a BSM with a bad .V baggage source indicator will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotVBaggageSourceIndicatorValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = DotVInvalidBaggageSourceIndicator,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(VersionSupplementaryData.BaggageSourceIndicator), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
+ ///
+ /// The method verifies a BSM with a bad .V data dictionary version number will cause a validation issue.
+ ///
+ [Fact]
+ public void VerifyDotVDataDictionaryVersionNumberValidation()
+ {
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [DotNTagNumber],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [DotPGivenName],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = DotVInvalidDataDictionaryVersionNumber,
+ },
+ };
+
+ BSMParser parser = new();
+ string bsmString = bsm.ToTypeB();
+ byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
+
+ PDUParserResult result = parser.Parse(bsmBytes);
+
+ Assert.Single(result.PDUs);
+ Assert.IsType(result.PDUs[0]);
+ Assert.False(result.PDUs[0].IsValid, "The BSM is valid. It's expected to be invalid.");
+ Assert.Single(result.PDUs[0].ValidationResults);
+ Assert.Contains(nameof(VersionSupplementaryData.DataDictionaryVersionNumber), result.PDUs[0].ValidationResults.First().MemberNames);
+ }
+
///
/// The method verfies multiple BSMs can be parsed.
///
@@ -545,7 +850,7 @@ public void VerifyMultipleBSMs()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
@@ -573,22 +878,53 @@ public void VerifyMultipleBSMs()
{
AirportCode = DotVAirportCode,
BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
- DataDictionaryVersionNumber = 1,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
+ },
+ };
+
+ BSM bsm3 = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = ["0001123458"],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = DotFAirline,
+ ClassOfTravel = DotFClassOfTravel,
+ Destination = DotFDestination,
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = DotFFlightNumber,
+ },
+ PassengerName = new()
+ {
+ GivenNames = [$"{DotPGivenName}2"],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
},
};
BSMParser parser = new();
- string bsmString = bsm1.ToTypeB() + bsm2.ToTypeB();
+ string bsmString = bsm1.ToTypeB() + bsm2.ToTypeB() + bsm3.ToTypeB();
byte[] bsmBytes = Encoding.ASCII.GetBytes(bsmString);
PDUParserResult result = parser.Parse(bsmBytes);
- Assert.Equal(2, result.PDUs.Count);
+ Assert.Equal(3, result.PDUs.Count);
Assert.IsType(result.PDUs[0]);
Assert.IsType(result.PDUs[1]);
+ Assert.IsType(result.PDUs[2]);
Assert.True(result.PDUs[0].IsValid, "BSM 1 is not valid.");
Assert.True(result.PDUs[1].IsValid, "BSM 2 is not valid.");
+ Assert.True(result.PDUs[2].IsValid, "BSM 3 is not valid.");
Assert.True(new BMSEqualityComparer().Equals(bsm1, ((BSMPDU)result.PDUs[0]).BSM), "BSM 1 does not equal the first BSM in the parser results.");
Assert.True(new BMSEqualityComparer().Equals(bsm2, ((BSMPDU)result.PDUs[1]).BSM), "BSM 2 does not equal the second BSM in the parser results.");
+ Assert.True(new BMSEqualityComparer().Equals(bsm3, ((BSMPDU)result.PDUs[2]).BSM), "BSM 3 does not equal the third BSM in the parser results.");
}
}
From dfcea7ccc67770e897ce6075a3c40b503a74a58f Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Fri, 16 Aug 2024 17:28:01 -0400
Subject: [PATCH 04/11] Added Workers
---
.../BSMClientWorker.cs | 93 +++++++++++++++
.../BSMServerConnectionWorker.cs | 110 ++++++++++++++++++
.../BSMServerOutputWorker.cs | 81 +++++++++++++
JMayer.Example.WindowsService/Program.cs | 14 ++-
JMayer.Example.WindowsService/Worker.cs | 24 ----
5 files changed, 297 insertions(+), 25 deletions(-)
create mode 100644 JMayer.Example.WindowsService/BSMClientWorker.cs
create mode 100644 JMayer.Example.WindowsService/BSMServerConnectionWorker.cs
create mode 100644 JMayer.Example.WindowsService/BSMServerOutputWorker.cs
delete mode 100644 JMayer.Example.WindowsService/Worker.cs
diff --git a/JMayer.Example.WindowsService/BSMClientWorker.cs b/JMayer.Example.WindowsService/BSMClientWorker.cs
new file mode 100644
index 0000000..f6fdc15
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSMClientWorker.cs
@@ -0,0 +1,93 @@
+using JMayer.Example.WindowsService.BSM;
+using JMayer.Net;
+using JMayer.Net.ProtocolDataUnit;
+
+namespace JMayer.Example.WindowsService;
+
+///
+/// The class manages connecting to the server and processes BSMs received from the server.
+///
+internal class BSMClientWorker : BackgroundService
+{
+ ///
+ /// Used to log activity for the service.
+ ///
+ private readonly ILogger _logger;
+
+ ///
+ /// Used to manage TCP/IP communication.
+ ///
+ private readonly IClient _client;
+
+ ///
+ /// The dependency injection constructor.
+ ///
+ /// Used to log activity for the service.
+ /// Used to manage TCP/IP communication.
+ public BSMClientWorker(ILogger logger, IClient client)
+ {
+ _logger = logger;
+ _client = client;
+ }
+
+ ///
+ /// The method manages connecting to the server and processes BSMs received from the server.
+ ///
+ /// Used to cancel the task when the service stops.
+ /// A Task object for the async.
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ if (!_client.IsConnected)
+ {
+ try
+ {
+ await _client.ConnectAsync("127.0.0.1", 55555, stoppingToken);
+ _logger.LogInformation("The client connected to the BSM server.");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "The client failed to connect to the BSM server.");
+ }
+ }
+ else
+ {
+ List pdus = await _client.ReceiveAndParseAsync(stoppingToken);
+
+ foreach (BSMPDU pdu in pdus.Cast())
+ {
+ if (pdu.IsValid)
+ {
+ _logger.LogInformation("The client received a valid BSM from the server. {BSM}", pdu.BSM.ToTypeB());
+ }
+ else
+ {
+ _logger.LogWarning("The client received an invalid BSM from the server. {BSM}", pdu.BSM.ToTypeB());
+ }
+ }
+ }
+
+ await Task.Delay(1000);
+ }
+ }
+
+ ///
+ ///
+ /// This is overriden so the client can disconnect from the server.
+ ///
+ public override async Task StopAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ _client.Disconnect();
+ _logger.LogInformation("The client disconnect from the server.");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "The client failed to disconnect from the server; it may have already been disconnected by the server.");
+ }
+
+ await base.StopAsync(cancellationToken);
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSMServerConnectionWorker.cs b/JMayer.Example.WindowsService/BSMServerConnectionWorker.cs
new file mode 100644
index 0000000..ebc62d7
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSMServerConnectionWorker.cs
@@ -0,0 +1,110 @@
+using JMayer.Net;
+
+namespace JMayer.Example.WindowsService;
+
+///
+/// The class manages starting/stopping the server & receiving remote connections from the clients.
+///
+///
+/// The server code is split between two workers because you want to accept client connections
+/// as frequently as possible; on a real server, multiple client connections may be queued to be
+/// accepted but if the worker accepts a connection, does other stuff and then sleeps for a second
+/// or more, the queued clients are missing BSMs.
+///
+internal class BSMServerConnectionWorker : BackgroundService
+{
+ ///
+ /// Used to log activity for the service.
+ ///
+ private readonly ILogger _logger;
+
+ ///
+ /// Used to manage TCP/IP communication.
+ ///
+ private readonly IServer _server;
+
+ ///
+ /// The port to monitor.
+ ///
+ public const int Port = 55555;
+
+ ///
+ /// The dependency injection constructor.
+ ///
+ /// Used to log activity for the service.
+ /// Used to manage TCP/IP communication.
+ public BSMServerConnectionWorker(ILogger logger, IServer server)
+ {
+ _logger = logger;
+ _server = server;
+ }
+
+ ///
+ /// The method handles starting the server & receiving remote connections from the clients.
+ ///
+ /// Used to cancel the task when the service stops.
+ /// A Task object for the async.
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _server.ConnectionStaleMode = ConnectionStaleMode.LastSent;
+ _server.ConnectionTimeout = 20;
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ //Start the server if not ready.
+ if (!_server.IsReady)
+ {
+ try
+ {
+ _server.Start(Port);
+ _logger.LogInformation("The BSM server is listen on {Port} port.", Port);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "The BSM server failed to listen to the {Port} port", Port);
+ }
+ }
+ //Accept the client connections.
+ else
+ {
+ try
+ {
+ Guid id = await _server.AcceptIncomingConnectionAsync(stoppingToken);
+
+ if (id != Guid.Empty)
+ {
+ _logger.LogInformation("The BSM server accepted a remote client connection.");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "The BSM server failed to accept a remote client connection.");
+ }
+ }
+
+ await Task.Delay(10, stoppingToken);
+ }
+ }
+
+ ///
+ ///
+ /// This is overriden so the server can stop listening on the port.
+ ///
+ public override async Task StopAsync(CancellationToken cancellationToken)
+ {
+ if (_server.IsReady)
+ {
+ try
+ {
+ _server.Stop();
+ _logger.LogInformation("The BSM server has stopped.");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "The BSM server failed to stop; the service will still stop.");
+ }
+ }
+
+ await base.StopAsync(cancellationToken);
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSMServerOutputWorker.cs b/JMayer.Example.WindowsService/BSMServerOutputWorker.cs
new file mode 100644
index 0000000..34443e0
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSMServerOutputWorker.cs
@@ -0,0 +1,81 @@
+using JMayer.Example.WindowsService.BSM;
+using JMayer.Net;
+
+namespace JMayer.Example.WindowsService;
+
+///
+/// The class manages stale connections and sending the BSMs to the clients.
+///
+internal class BSMServerOutputWorker : BackgroundService
+{
+ ///
+ /// Used to log activity for the service.
+ ///
+ private readonly ILogger _logger;
+
+ ///
+ /// Used to manage TCP/IP communication.
+ ///
+ private readonly IServer _server;
+
+ ///
+ /// The dependency injection constructor.
+ ///
+ /// Used to log activity for the service.
+ /// Used to manage TCP/IP communication.
+ public BSMServerOutputWorker(ILogger logger, IServer server)
+ {
+ _logger = logger;
+ _server = server;
+ }
+
+ ///
+ /// The class manages client connections and sending BSMs to the clients.
+ ///
+ /// Used to cancel the task when the service stops.
+ /// A Task object for the async.
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ if (_server.IsReady)
+ {
+ //Manage stale remote clients.
+ List connectionIds = _server.GetStaleRemoteConnections();
+
+ if (connectionIds.Count > 0)
+ {
+ _logger.LogInformation("The BSM server detected stale remote clients; will attempt to disconnect.");
+
+ foreach (Guid connectionId in connectionIds)
+ {
+ try
+ {
+ _server.Disconnect(connectionId);
+ }
+ catch { }
+ }
+ }
+
+ //Generate a BSM & sends it to the remote clients.
+ try
+ {
+ BSM.BSM bsm = new(); //Need to generate; should make a class to manage the generation.
+ BSMPDU pdu = new()
+ {
+ BSM = bsm,
+ };
+
+ await _server.SendToAllAsync(pdu, stoppingToken);
+ _logger.LogInformation("The BSM server sent a BSM to the remote clients. {BSM}", pdu.BSM.ToTypeB());
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "The BSM server failed to send the BSM to the remote clients.");
+ }
+ }
+
+ await Task.Delay(10_000, stoppingToken);
+ }
+ }
+}
diff --git a/JMayer.Example.WindowsService/Program.cs b/JMayer.Example.WindowsService/Program.cs
index a2c8451..e1b6b2d 100644
--- a/JMayer.Example.WindowsService/Program.cs
+++ b/JMayer.Example.WindowsService/Program.cs
@@ -1,7 +1,19 @@
using JMayer.Example.WindowsService;
+using JMayer.Example.WindowsService.BSM;
+using JMayer.Net.TcpIp;
var builder = Host.CreateApplicationBuilder(args);
-builder.Services.AddHostedService();
+builder.Services.AddWindowsService(options =>
+{
+ options.ServiceName = "Example BSM Service";
+});
+
+builder.Services.AddSingleton();
+builder.Services.AddSingleton();
+builder.Services.AddHostedService();
+builder.Services.AddHostedService();
+builder.Services.AddHostedService();
+
var host = builder.Build();
host.Run();
diff --git a/JMayer.Example.WindowsService/Worker.cs b/JMayer.Example.WindowsService/Worker.cs
deleted file mode 100644
index b15d9f2..0000000
--- a/JMayer.Example.WindowsService/Worker.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace JMayer.Example.WindowsService
-{
- public class Worker : BackgroundService
- {
- private readonly ILogger _logger;
-
- public Worker(ILogger logger)
- {
- _logger = logger;
- }
-
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- while (!stoppingToken.IsCancellationRequested)
- {
- if (_logger.IsEnabled(LogLevel.Information))
- {
- _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
- }
- await Task.Delay(1000, stoppingToken);
- }
- }
- }
-}
From 1620e31bd87e3d4316a334c8a493abcc157d0d78 Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Sat, 17 Aug 2024 10:59:41 -0400
Subject: [PATCH 05/11] Added IATA Generator & Unit Test
---
.../BSM/IATAGenerator.cs | 53 ++++++++++++++
TestProject/Test/IATAGeneratorUnitTest.cs | 69 +++++++++++++++++++
2 files changed, 122 insertions(+)
create mode 100644 JMayer.Example.WindowsService/BSM/IATAGenerator.cs
create mode 100644 TestProject/Test/IATAGeneratorUnitTest.cs
diff --git a/JMayer.Example.WindowsService/BSM/IATAGenerator.cs b/JMayer.Example.WindowsService/BSM/IATAGenerator.cs
new file mode 100644
index 0000000..42800ce
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/IATAGenerator.cs
@@ -0,0 +1,53 @@
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class manages generating an IATA (10 digit tag number).
+///
+public class IATAGenerator
+{
+ ///
+ /// Keeps track of the sequence number to be used.
+ ///
+ private int _sequenceNumber = MinSequenceNumber;
+
+ ///
+ /// The property gets/sets the alphanumeric airline code.
+ ///
+ ///
+ /// Used by the BSM generator.
+ ///
+ public string AirlineAlphaNumericCode { get; init; } = string.Empty;
+
+ ///
+ /// The property gets/sets the numeric airline code used by the IATA.
+ ///
+ public string AirlineNumericCode { get; init; } = string.Empty;
+
+ ///
+ /// The constant for the maximum sequence number.
+ ///
+ public const int MaxSequenceNumber = 999999;
+
+ ///
+ /// The constant for the minimum sequence number.
+ ///
+ public const int MinSequenceNumber = 1;
+
+ ///
+ /// The method returns the next IATA.
+ ///
+ /// The IATA.
+ public string Generate()
+ {
+ string iata = $"0{AirlineNumericCode}{_sequenceNumber.ToString().PadLeft(6, '0')}";
+
+ _sequenceNumber++;
+
+ if (_sequenceNumber > MaxSequenceNumber)
+ {
+ _sequenceNumber = MinSequenceNumber;
+ }
+
+ return iata;
+ }
+}
diff --git a/TestProject/Test/IATAGeneratorUnitTest.cs b/TestProject/Test/IATAGeneratorUnitTest.cs
new file mode 100644
index 0000000..6618b8c
--- /dev/null
+++ b/TestProject/Test/IATAGeneratorUnitTest.cs
@@ -0,0 +1,69 @@
+using JMayer.Example.WindowsService.BSM;
+
+namespace TestProject.Test;
+
+///
+/// The class manages testing the IATA generator.
+///
+public class IATAGeneratorUnitTest
+{
+ ///
+ /// The constant for the airline numeric code.
+ ///
+ private const string AirlineNumericCode = "001";
+
+ ///
+ /// The method verifies an IATA will be generated.
+ ///
+ [Fact]
+ public void VerifyGeneration()
+ {
+ IATAGenerator iataGenerator = new()
+ {
+ AirlineNumericCode = AirlineNumericCode,
+ };
+ string iata = iataGenerator.Generate();
+
+ Assert.Equal($"0{iataGenerator.AirlineNumericCode}{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", iata);
+ }
+
+ ///
+ /// The method verifies if 3 IATAs are generated, each is incremented by 1.
+ ///
+ [Fact]
+ public void VerifyIncrementByOne()
+ {
+ IATAGenerator iataGenerator = new()
+ {
+ AirlineNumericCode = AirlineNumericCode,
+ };
+
+ string iata1 = iataGenerator.Generate();
+ string iata2 = iataGenerator.Generate();
+ string iata3 = iataGenerator.Generate();
+
+ Assert.Equal($"0{iataGenerator.AirlineNumericCode}{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", iata1);
+ Assert.Equal($"0{iataGenerator.AirlineNumericCode}{(IATAGenerator.MinSequenceNumber + 1).ToString().PadLeft(6, '0')}", iata2);
+ Assert.Equal($"0{iataGenerator.AirlineNumericCode}{(IATAGenerator.MinSequenceNumber + 2).ToString().PadLeft(6, '0')}", iata3);
+ }
+
+ ///
+ /// The method verifies the generator will rollover after 999999 IATAs are generated.
+ ///
+ [Fact]
+ public void VerifyRollover()
+ {
+ string iata = string.Empty;
+ IATAGenerator iataGenerator = new()
+ {
+ AirlineNumericCode = AirlineNumericCode,
+ };
+
+ for (int index = 0; index <= IATAGenerator.MaxSequenceNumber; index++)
+ {
+ iata = iataGenerator.Generate();
+ }
+
+ Assert.Equal($"0{iataGenerator.AirlineNumericCode}{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", iata);
+ }
+}
From d63d4664f705780aed3f5fd7cb66f869d43dc66c Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Sat, 17 Aug 2024 13:33:05 -0400
Subject: [PATCH 06/11] Added BSM Generator & Unit Test
---
.../BSM/BSMGenerator.cs | 230 ++++++++++++++++++
.../BSM/IATAGenerator.cs | 4 +
TestProject/Test/BSMGeneratorUnitTest.cs | 225 +++++++++++++++++
3 files changed, 459 insertions(+)
create mode 100644 JMayer.Example.WindowsService/BSM/BSMGenerator.cs
create mode 100644 TestProject/Test/BSMGeneratorUnitTest.cs
diff --git a/JMayer.Example.WindowsService/BSM/BSMGenerator.cs b/JMayer.Example.WindowsService/BSM/BSMGenerator.cs
new file mode 100644
index 0000000..7b1f0a2
--- /dev/null
+++ b/JMayer.Example.WindowsService/BSM/BSMGenerator.cs
@@ -0,0 +1,230 @@
+namespace JMayer.Example.WindowsService.BSM;
+
+///
+/// The class manages generating a BSM.
+///
+public class BSMGenerator
+{
+ ///
+ /// The class of travels.
+ ///
+ private readonly string[] _classOfTravels =
+ [
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z"
+ ];
+
+ ///
+ /// The destinations.
+ ///
+ private readonly string[] _destinations =
+ [
+ "CAE",
+ "BNA",
+ "LBB",
+ "MSY",
+ "OAK",
+ "PIE",
+ "RSW",
+ "VPS",
+ ];
+
+ ///
+ /// The destination being used.
+ ///
+ private int _destinationIndex = 0;
+
+ ///
+ /// The flight number for the BSM.
+ ///
+ private int _flightNumber = MinFlightNumber;
+
+ ///
+ /// Used to generate IATAs.
+ ///
+ private readonly IATAGenerator[] _iataGenerators =
+ [
+ new IATAGenerator() { AirlineAlphaNumericCode = "AA", AirlineNumericCode = "001", },
+ new IATAGenerator() { AirlineAlphaNumericCode = "DL", AirlineNumericCode = "006", },
+ new IATAGenerator() { AirlineAlphaNumericCode = "UA", AirlineNumericCode = "016", },
+ new IATAGenerator() { AirlineAlphaNumericCode = "WN", AirlineNumericCode = "526", },
+ ];
+
+ ///
+ /// The IATA generator being used.
+ ///
+ private int _iataGeneratorIndex = 0;
+
+ ///
+ /// The passenger count.
+ ///
+ private int _passengerCount = MinPassengerCount;
+
+ ///
+ /// The constant for the .P given name.
+ ///
+ private const string DotPGivenName = "PASSENGER";
+
+ ///
+ /// The constant for the .P surname.
+ ///
+ private const string DotPSurName = "TEST";
+
+ ///
+ /// The constant for the .V airport code.
+ ///
+ private const string DotVAirportCode = "MCO";
+
+ ///
+ /// The constant for the .V data dictionary version number.
+ ///
+ private const int DotVDataDictionaryVersionNumber = 1;
+
+ ///
+ /// The constant for the maximum flight number.
+ ///
+ public const int MaxFlightNumber = 9999;
+
+ ///
+ /// The constant for the minimum flight number.
+ ///
+ public const int MinFlightNumber = 1;
+
+ ///
+ /// The constant for the maximum number of passengers.
+ ///
+ public const int MaxPassengerCount = 100;
+
+ ///
+ /// The constant for the minimum number of passengers.
+ ///
+ public const int MinPassengerCount = 1;
+
+ ///
+ /// The default constructor.
+ ///
+ public BSMGenerator()
+ {
+ SetRandomDestination();
+ }
+
+ ///
+ /// The method returns the next BSM.
+ ///
+ /// The BSM.
+ public BSM Generate()
+ {
+ IATAGenerator generator = _iataGenerators[_iataGeneratorIndex];
+
+ BSM bsm = new()
+ {
+ BaggageTagDetails = new()
+ {
+ BaggageTagNumbers = [generator.Generate()],
+ },
+ ChangeOfStatus = BSM.Add,
+ OutboundFlight = new()
+ {
+ Airline = generator.AirlineAlphaNumericCode,
+ ClassOfTravel = _classOfTravels[new Random(DateTime.Now.Second).Next(0, _classOfTravels.Length - 1)],
+ Destination = _destinations[_destinationIndex],
+ FlightDate = DateTime.Today.ToDayMonthFormat(),
+ FlightNumber = _flightNumber.ToString().PadLeft(4, '0'),
+ },
+ PassengerName = new()
+ {
+ GivenNames = [$"{DotPGivenName}{_passengerCount}"],
+ SurName = DotPSurName,
+ },
+ VersionSupplementaryData = new()
+ {
+ AirportCode = DotVAirportCode,
+ BaggageSourceIndicator = VersionSupplementaryData.LocalBaggageSourceIndicator,
+ DataDictionaryVersionNumber = DotVDataDictionaryVersionNumber,
+ },
+ };
+
+ IncrementPassengerCount();
+
+ return bsm;
+ }
+
+ ///
+ /// The method increments the flight number.
+ ///
+ private void IncrementFlightNumber()
+ {
+ _flightNumber++;
+
+ if (_flightNumber > MaxFlightNumber)
+ {
+ _flightNumber = MinFlightNumber;
+ }
+ }
+
+ ///
+ /// The method increments to the next IATA generator.
+ ///
+ private void IncrementIATAGenerator()
+ {
+ _iataGeneratorIndex++;
+
+ if (_iataGeneratorIndex == _iataGenerators.Length)
+ {
+ _iataGeneratorIndex = 0;
+ }
+ }
+
+ ///
+ /// The method increments the passenger count.
+ ///
+ ///
+ /// When the maximum number of passengers are reached, the flight number is incremented,
+ /// a new IATA generator is used and a new destination is randomly chosen. The idea is the
+ /// generator will generate X BSMs for a flight and then a new one is used.
+ ///
+ private void IncrementPassengerCount()
+ {
+ _passengerCount++;
+
+ if (_passengerCount > MaxPassengerCount)
+ {
+ _passengerCount = MinPassengerCount;
+ IncrementFlightNumber();
+ IncrementIATAGenerator();
+ SetRandomDestination();
+ }
+ }
+
+ ///
+ /// The method sets a random destination.
+ ///
+ private void SetRandomDestination()
+ {
+ _destinationIndex = new Random(DateTime.Now.Second).Next(0, _destinations.Length - 1);
+ }
+}
diff --git a/JMayer.Example.WindowsService/BSM/IATAGenerator.cs b/JMayer.Example.WindowsService/BSM/IATAGenerator.cs
index 42800ce..8f82873 100644
--- a/JMayer.Example.WindowsService/BSM/IATAGenerator.cs
+++ b/JMayer.Example.WindowsService/BSM/IATAGenerator.cs
@@ -37,6 +37,10 @@ public class IATAGenerator
/// The method returns the next IATA.
///
/// The IATA.
+ ///
+ /// The IATA format is the first digit is 0 or 2-9, the next 3 digits are the airline numeric code
+ /// and the last 6 digits is a sequence number from 1-999999.
+ ///
public string Generate()
{
string iata = $"0{AirlineNumericCode}{_sequenceNumber.ToString().PadLeft(6, '0')}";
diff --git a/TestProject/Test/BSMGeneratorUnitTest.cs b/TestProject/Test/BSMGeneratorUnitTest.cs
new file mode 100644
index 0000000..17461b5
--- /dev/null
+++ b/TestProject/Test/BSMGeneratorUnitTest.cs
@@ -0,0 +1,225 @@
+using JMayer.Example.WindowsService.BSM;
+
+namespace TestProject.Test;
+
+///
+/// The class manages testing the BSM generator.
+///
+public class BSMGeneratorUnitTest
+{
+ ///
+ /// The constant for the .P given name.
+ ///
+ private const string DotPGivenName = "PASSENGER";
+
+ ///
+ /// The constant for the .P surname.
+ ///
+ private const string DotPSurName = "TEST";
+
+ ///
+ /// The constant for the .V airport code.
+ ///
+ private const string DotVAirportCode = "MCO";
+
+ ///
+ /// The constant for the .V data dictionary version number.
+ ///
+ private const int DotVDataDictionaryVersionNumber = 1;
+
+ ///
+ /// The constant for the maximum number of airlines used by the BSM generator.
+ ///
+ private const int MaxAirline = 4;
+
+ ///
+ /// The method verifies the generator will rollover to the first airline after the maximum number is generated.
+ ///
+ [Fact]
+ public void VerifyAirlineRollover()
+ {
+ BSM bsm = new();
+ BSMGenerator bsmGenerator = new();
+ int maxPassengers = BSMGenerator.MaxPassengerCount * MaxAirline;
+
+ for (int index = 0; index <= maxPassengers; index++)
+ {
+ bsm = bsmGenerator.Generate();
+ }
+
+ //Verify the baggage tag details.
+ Assert.NotNull(bsm.BaggageTagDetails);
+ Assert.Single(bsm.BaggageTagDetails.BaggageTagNumbers);
+ Assert.Equal($"0001{(BSMGenerator.MaxPassengerCount + 1).ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
+
+ //Verify the change of status.
+ Assert.Equal(BSM.Add, bsm.ChangeOfStatus);
+
+ //Verify the outbound flight.
+ Assert.NotNull(bsm.OutboundFlight);
+ Assert.Equal("AA", bsm.OutboundFlight.Airline);
+ Assert.NotEmpty(bsm.OutboundFlight.ClassOfTravel); //Class of Travel is randomly set so just make sure its set to something.
+ Assert.NotEmpty(bsm.OutboundFlight.Destination); //Destination is randomly set so just make sure its set to something.
+ Assert.Equal(DateTime.Today.ToDayMonthFormat(), bsm.OutboundFlight.FlightDate);
+ Assert.Equal((MaxAirline + 1).ToString().PadLeft(4, '0'), bsm.OutboundFlight.FlightNumber);
+
+ //Verify the passenger name.
+ Assert.NotNull(bsm.PassengerName);
+ Assert.Single(bsm.PassengerName.GivenNames);
+ Assert.Equal($"{DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
+ Assert.Equal(DotPSurName, bsm.PassengerName.SurName);
+
+ //Verify the version supplementary data.
+ Assert.NotNull(bsm.VersionSupplementaryData);
+ Assert.Equal(DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
+ Assert.Equal(VersionSupplementaryData.LocalBaggageSourceIndicator, bsm.VersionSupplementaryData.BaggageSourceIndicator);
+ Assert.Equal(DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
+ }
+
+ ///
+ /// The method verifies the generator will rollover the flight number after the maximum number is generated.
+ ///
+ [Fact]
+ public void VerifyFlightNumberRollover()
+ {
+ BSM bsm = new();
+ BSMGenerator bsmGenerator = new();
+ int maxPassengers = BSMGenerator.MaxPassengerCount * BSMGenerator.MaxFlightNumber;
+
+ for (int index = 0; index <= maxPassengers; index++)
+ {
+ bsm = bsmGenerator.Generate();
+ }
+
+ //Verify the baggage tag details.
+ Assert.NotNull(bsm.BaggageTagDetails);
+ Assert.Single(bsm.BaggageTagDetails.BaggageTagNumbers);
+ Assert.NotEmpty(bsm.BaggageTagDetails.BaggageTagNumbers[0]); //999,900 passengers are generated and I don't think there's an easy to know what the IATA number will be so just make sure its set.
+
+ //Verify the change of status.
+ Assert.Equal(BSM.Add, bsm.ChangeOfStatus);
+
+ //Verify the outbound flight.
+ Assert.NotNull(bsm.OutboundFlight);
+ Assert.NotEmpty(bsm.OutboundFlight.Airline); //999,900 passengers are generated and I don't think there's an easy to know what the airline will be so just make sure its set.
+ Assert.NotEmpty(bsm.OutboundFlight.ClassOfTravel); //Class of Travel is randomly set so just make sure its set to something.
+ Assert.NotEmpty(bsm.OutboundFlight.Destination); //Destination is randomly set so just make sure its set to something.
+ Assert.Equal(DateTime.Today.ToDayMonthFormat(), bsm.OutboundFlight.FlightDate);
+ Assert.Equal(BSMGenerator.MinFlightNumber.ToString().PadLeft(4, '0'), bsm.OutboundFlight.FlightNumber);
+
+ //Verify the passenger name.
+ Assert.NotNull(bsm.PassengerName);
+ Assert.Single(bsm.PassengerName.GivenNames);
+ Assert.Equal($"{DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
+ Assert.Equal(DotPSurName, bsm.PassengerName.SurName);
+
+ //Verify the version supplementary data.
+ Assert.NotNull(bsm.VersionSupplementaryData);
+ Assert.Equal(DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
+ Assert.Equal(VersionSupplementaryData.LocalBaggageSourceIndicator, bsm.VersionSupplementaryData.BaggageSourceIndicator);
+ Assert.Equal(DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
+ }
+
+ ///
+ /// The class verifies a BSM is generated.
+ ///
+ [Fact]
+ public void VerifyGeneration()
+ {
+ BSMGenerator bsmGenerator = new();
+ BSM bsm = bsmGenerator.Generate();
+
+ //Verify the baggage tag details.
+ Assert.NotNull(bsm.BaggageTagDetails);
+ Assert.Single(bsm.BaggageTagDetails.BaggageTagNumbers);
+ Assert.Equal($"0001{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
+
+ //Verify the change of status.
+ Assert.Equal(BSM.Add, bsm.ChangeOfStatus);
+
+ //Verify the outbound flight.
+ Assert.NotNull(bsm.OutboundFlight);
+ Assert.Equal("AA", bsm.OutboundFlight.Airline);
+ Assert.NotEmpty(bsm.OutboundFlight.ClassOfTravel); //Class of Travel is randomly set so just make sure its set to something.
+ Assert.NotEmpty(bsm.OutboundFlight.Destination); //Destination is randomly set so just make sure its set to something.
+ Assert.Equal(DateTime.Today.ToDayMonthFormat(), bsm.OutboundFlight.FlightDate);
+ Assert.Equal(BSMGenerator.MinFlightNumber.ToString().PadLeft(4, '0'), bsm.OutboundFlight.FlightNumber);
+
+ //Verify the passenger name.
+ Assert.NotNull(bsm.PassengerName);
+ Assert.Single(bsm.PassengerName.GivenNames);
+ Assert.Equal($"{DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
+ Assert.Equal(DotPSurName, bsm.PassengerName.SurName);
+
+ //Verify the version supplementary data.
+ Assert.NotNull(bsm.VersionSupplementaryData);
+ Assert.Equal(DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
+ Assert.Equal(VersionSupplementaryData.LocalBaggageSourceIndicator, bsm.VersionSupplementaryData.BaggageSourceIndicator);
+ Assert.Equal(DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
+ }
+
+ ///
+ /// The method verifies the passenger increments by one on each generation.
+ ///
+ [Fact]
+ public void VerifyPassengerIncrementsByOne()
+ {
+ BSMGenerator bsmGenerator = new();
+ BSM bsm1 = bsmGenerator.Generate();
+ BSM bsm2 = bsmGenerator.Generate();
+ BSM bsm3 = bsmGenerator.Generate();
+
+ Assert.NotNull(bsm1.PassengerName);
+ Assert.Equal($"{DotPGivenName}1", bsm1.PassengerName.GivenNames[0]);
+
+ Assert.NotNull(bsm2.PassengerName);
+ Assert.Equal($"{DotPGivenName}2", bsm2.PassengerName.GivenNames[0]);
+
+ Assert.NotNull(bsm3.PassengerName);
+ Assert.Equal($"{DotPGivenName}3", bsm3.PassengerName.GivenNames[0]);
+ }
+
+ ///
+ /// The method verifies the generator will rollover the passengers after the maximum number is generated. It also verifies a new airline and flight number are generated.
+ ///
+ [Fact]
+ public void VerifyPassengerRollover()
+ {
+ BSM bsm = new();
+ BSMGenerator bsmGenerator = new();
+
+ for (int index = 0; index <= BSMGenerator.MaxPassengerCount; index++)
+ {
+ bsm = bsmGenerator.Generate();
+ }
+
+ //Verify the baggage tag details.
+ Assert.NotNull(bsm.BaggageTagDetails);
+ Assert.Single(bsm.BaggageTagDetails.BaggageTagNumbers);
+ //The sequence used by the BSM generator is 001, 006, 016, 526 so on the first passenger rollover, 006 will be used next.
+ Assert.Equal($"0006{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
+
+ //Verify the change of status.
+ Assert.Equal(BSM.Add, bsm.ChangeOfStatus);
+
+ //Verify the outbound flight.
+ Assert.NotNull(bsm.OutboundFlight);
+ Assert.Equal("DL", bsm.OutboundFlight.Airline); //The sequence used by the BSM generator is AA, DL, UA, WN so on the first passenger rollover, DL will be used next.
+ Assert.NotEmpty(bsm.OutboundFlight.ClassOfTravel); //Class of Travel is randomly set so just make sure its set to something.
+ Assert.NotEmpty(bsm.OutboundFlight.Destination); //Destination is randomly set so just make sure its set to something.
+ Assert.Equal(DateTime.Today.ToDayMonthFormat(), bsm.OutboundFlight.FlightDate);
+ Assert.Equal((BSMGenerator.MinFlightNumber + 1).ToString().PadLeft(4, '0'), bsm.OutboundFlight.FlightNumber);
+
+ //Verify the passenger name.
+ Assert.NotNull(bsm.PassengerName);
+ Assert.Single(bsm.PassengerName.GivenNames);
+ Assert.Equal($"{DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
+ Assert.Equal(DotPSurName, bsm.PassengerName.SurName);
+
+ //Verify the version supplementary data.
+ Assert.NotNull(bsm.VersionSupplementaryData);
+ Assert.Equal(DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
+ Assert.Equal(VersionSupplementaryData.LocalBaggageSourceIndicator, bsm.VersionSupplementaryData.BaggageSourceIndicator);
+ Assert.Equal(DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
+ }
+}
From c93763d547f6ca7c29576320bb97bca87affd5d1 Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Sat, 17 Aug 2024 13:34:26 -0400
Subject: [PATCH 07/11] Worker uses BSM Generator; various fixes
---
.../BSM/PassengerName.cs | 2 +-
.../BSMServerConnectionWorker.cs | 3 --
.../BSMServerOutputWorker.cs | 47 ++++++++++---------
JMayer.Example.WindowsService/Program.cs | 6 ++-
4 files changed, 31 insertions(+), 27 deletions(-)
diff --git a/JMayer.Example.WindowsService/BSM/PassengerName.cs b/JMayer.Example.WindowsService/BSM/PassengerName.cs
index 3fcf4ba..7b51b5b 100644
--- a/JMayer.Example.WindowsService/BSM/PassengerName.cs
+++ b/JMayer.Example.WindowsService/BSM/PassengerName.cs
@@ -75,6 +75,6 @@ public string ToTypeB()
givenNames += $"/{givenName}";
}
- return $"{DotPElement}/{GivenNames.Count}{SurName}{givenNames}";
+ return $"{DotPElement}/{GivenNames.Count}{SurName}{givenNames}{Environment.NewLine}";
}
}
diff --git a/JMayer.Example.WindowsService/BSMServerConnectionWorker.cs b/JMayer.Example.WindowsService/BSMServerConnectionWorker.cs
index ebc62d7..92098ed 100644
--- a/JMayer.Example.WindowsService/BSMServerConnectionWorker.cs
+++ b/JMayer.Example.WindowsService/BSMServerConnectionWorker.cs
@@ -46,9 +46,6 @@ public BSMServerConnectionWorker(ILogger logger, ISer
/// A Task object for the async.
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
- _server.ConnectionStaleMode = ConnectionStaleMode.LastSent;
- _server.ConnectionTimeout = 20;
-
while (!stoppingToken.IsCancellationRequested)
{
//Start the server if not ready.
diff --git a/JMayer.Example.WindowsService/BSMServerOutputWorker.cs b/JMayer.Example.WindowsService/BSMServerOutputWorker.cs
index 34443e0..5afc531 100644
--- a/JMayer.Example.WindowsService/BSMServerOutputWorker.cs
+++ b/JMayer.Example.WindowsService/BSMServerOutputWorker.cs
@@ -8,6 +8,11 @@ namespace JMayer.Example.WindowsService;
///
internal class BSMServerOutputWorker : BackgroundService
{
+ ///
+ /// Used to generate BSMs.
+ ///
+ private readonly BSMGenerator _bsmGenerator;
+
///
/// Used to log activity for the service.
///
@@ -21,10 +26,12 @@ internal class BSMServerOutputWorker : BackgroundService
///
/// The dependency injection constructor.
///
+ /// Used to generate BSMs.
/// Used to log activity for the service.
/// Used to manage TCP/IP communication.
- public BSMServerOutputWorker(ILogger logger, IServer server)
+ public BSMServerOutputWorker(BSMGenerator bsmGenerator, ILogger logger, IServer server)
{
+ _bsmGenerator = bsmGenerator;
_logger = logger;
_server = server;
}
@@ -38,8 +45,23 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
- if (_server.IsReady)
- {
+ if (_server.IsReady && _server.ConnectionCount > 0)
+ {
+ //Generate a BSM & sends it to the remote clients.
+ try
+ {
+ BSMPDU pdu = new()
+ {
+ BSM = _bsmGenerator.Generate(),
+ };
+ await _server.SendToAllAsync(pdu, stoppingToken);
+ _logger.LogInformation("The BSM server sent a BSM to the remote clients. {BSM}", pdu.BSM.ToTypeB());
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "The BSM server failed to send the BSM to the remote clients.");
+ }
+
//Manage stale remote clients.
List connectionIds = _server.GetStaleRemoteConnections();
@@ -56,26 +78,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
catch { }
}
}
-
- //Generate a BSM & sends it to the remote clients.
- try
- {
- BSM.BSM bsm = new(); //Need to generate; should make a class to manage the generation.
- BSMPDU pdu = new()
- {
- BSM = bsm,
- };
-
- await _server.SendToAllAsync(pdu, stoppingToken);
- _logger.LogInformation("The BSM server sent a BSM to the remote clients. {BSM}", pdu.BSM.ToTypeB());
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "The BSM server failed to send the BSM to the remote clients.");
- }
}
- await Task.Delay(10_000, stoppingToken);
+ await Task.Delay(5_000, stoppingToken);
}
}
}
diff --git a/JMayer.Example.WindowsService/Program.cs b/JMayer.Example.WindowsService/Program.cs
index e1b6b2d..2990674 100644
--- a/JMayer.Example.WindowsService/Program.cs
+++ b/JMayer.Example.WindowsService/Program.cs
@@ -1,5 +1,6 @@
using JMayer.Example.WindowsService;
using JMayer.Example.WindowsService.BSM;
+using JMayer.Net;
using JMayer.Net.TcpIp;
var builder = Host.CreateApplicationBuilder(args);
@@ -8,8 +9,9 @@
options.ServiceName = "Example BSM Service";
});
-builder.Services.AddSingleton();
-builder.Services.AddSingleton();
+builder.Services.AddSingleton();
+builder.Services.AddSingleton(new TcpIpClient(new BSMParser()));
+builder.Services.AddSingleton(new TcpIpServer(new BSMParser()) { ConnectionStaleMode = ConnectionStaleMode.LastSent, ConnectionTimeout = 60 });
builder.Services.AddHostedService();
builder.Services.AddHostedService();
builder.Services.AddHostedService();
From 20d2159566abb8f6e5638c7e745856659c81017b Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Sat, 17 Aug 2024 13:35:55 -0400
Subject: [PATCH 08/11] Switch client to use constant port number
---
JMayer.Example.WindowsService/BSMClientWorker.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/JMayer.Example.WindowsService/BSMClientWorker.cs b/JMayer.Example.WindowsService/BSMClientWorker.cs
index f6fdc15..a670454 100644
--- a/JMayer.Example.WindowsService/BSMClientWorker.cs
+++ b/JMayer.Example.WindowsService/BSMClientWorker.cs
@@ -43,7 +43,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
- await _client.ConnectAsync("127.0.0.1", 55555, stoppingToken);
+ await _client.ConnectAsync("127.0.0.1", BSMServerConnectionWorker.Port, stoppingToken);
_logger.LogInformation("The client connected to the BSM server.");
}
catch (Exception ex)
From 1a7dc6fdb33a3fd95471a24b0f3ebb39e0f02fec Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Sat, 17 Aug 2024 13:43:00 -0400
Subject: [PATCH 09/11] Simplified variable name
---
JMayer.Example.WindowsService/BSMServerOutputWorker.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/JMayer.Example.WindowsService/BSMServerOutputWorker.cs b/JMayer.Example.WindowsService/BSMServerOutputWorker.cs
index 5afc531..7ccadd7 100644
--- a/JMayer.Example.WindowsService/BSMServerOutputWorker.cs
+++ b/JMayer.Example.WindowsService/BSMServerOutputWorker.cs
@@ -63,17 +63,17 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
}
//Manage stale remote clients.
- List connectionIds = _server.GetStaleRemoteConnections();
+ List ids = _server.GetStaleRemoteConnections();
- if (connectionIds.Count > 0)
+ if (ids.Count > 0)
{
_logger.LogInformation("The BSM server detected stale remote clients; will attempt to disconnect.");
- foreach (Guid connectionId in connectionIds)
+ foreach (Guid id in ids)
{
try
{
- _server.Disconnect(connectionId);
+ _server.Disconnect(id);
}
catch { }
}
From fe99cf2eec7760aa34aeaa7af1897f5fd79c2dd8 Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Sat, 17 Aug 2024 13:58:23 -0400
Subject: [PATCH 10/11] Adding Constants; reorganization of them
---
.../BSM/BSMGenerator.cs | 56 ++++++++++++---
TestProject/Test/BSMGeneratorUnitTest.cs | 70 +++++++------------
2 files changed, 73 insertions(+), 53 deletions(-)
diff --git a/JMayer.Example.WindowsService/BSM/BSMGenerator.cs b/JMayer.Example.WindowsService/BSM/BSMGenerator.cs
index 7b1f0a2..fd128b4 100644
--- a/JMayer.Example.WindowsService/BSM/BSMGenerator.cs
+++ b/JMayer.Example.WindowsService/BSM/BSMGenerator.cs
@@ -68,10 +68,10 @@ public class BSMGenerator
///
private readonly IATAGenerator[] _iataGenerators =
[
- new IATAGenerator() { AirlineAlphaNumericCode = "AA", AirlineNumericCode = "001", },
- new IATAGenerator() { AirlineAlphaNumericCode = "DL", AirlineNumericCode = "006", },
- new IATAGenerator() { AirlineAlphaNumericCode = "UA", AirlineNumericCode = "016", },
- new IATAGenerator() { AirlineAlphaNumericCode = "WN", AirlineNumericCode = "526", },
+ new IATAGenerator() { AirlineAlphaNumericCode = AmericanAirlineAlphaCode, AirlineNumericCode = AmericanAirlinesNumericCode, },
+ new IATAGenerator() { AirlineAlphaNumericCode = DeltaAlphaCode, AirlineNumericCode = DeltaNumericCode, },
+ new IATAGenerator() { AirlineAlphaNumericCode = UnitedAirlinesAlphaCode, AirlineNumericCode = UnitedAirlinesNumericCode, },
+ new IATAGenerator() { AirlineAlphaNumericCode = SouthwestAlpaCode, AirlineNumericCode = SouthwestNumericCode, },
];
///
@@ -84,25 +84,45 @@ public class BSMGenerator
///
private int _passengerCount = MinPassengerCount;
+ ///
+ /// The constant for the AA alphanumeric code.
+ ///
+ public const string AmericanAirlineAlphaCode = "AA";
+
+ ///
+ /// The constant for the AA numeric code.
+ ///
+ public const string AmericanAirlinesNumericCode = "001";
+
+ ///
+ /// The constant for the DL alphanumeric code.
+ ///
+ public const string DeltaAlphaCode = "DL";
+
+ ///
+ /// The constant for the DL numeric code.
+ ///
+ public const string DeltaNumericCode = "006";
+
///
/// The constant for the .P given name.
///
- private const string DotPGivenName = "PASSENGER";
+ public const string DotPGivenName = "PASSENGER";
///
/// The constant for the .P surname.
///
- private const string DotPSurName = "TEST";
+ public const string DotPSurName = "TEST";
///
/// The constant for the .V airport code.
///
- private const string DotVAirportCode = "MCO";
+ public const string DotVAirportCode = "MCO";
///
/// The constant for the .V data dictionary version number.
///
- private const int DotVDataDictionaryVersionNumber = 1;
+ public const int DotVDataDictionaryVersionNumber = 1;
///
/// The constant for the maximum flight number.
@@ -124,6 +144,26 @@ public class BSMGenerator
///
public const int MinPassengerCount = 1;
+ ///
+ /// The constant for the WN alphanumeric code.
+ ///
+ public const string SouthwestAlpaCode = "WN";
+
+ ///
+ /// The constant for the WN numeric code.
+ ///
+ public const string SouthwestNumericCode = "526";
+
+ ///
+ /// The constant for the UA alphanumeric code.
+ ///
+ public const string UnitedAirlinesAlphaCode = "UA";
+
+ ///
+ /// The constant for the UA numeric code.
+ ///
+ public const string UnitedAirlinesNumericCode = "016";
+
///
/// The default constructor.
///
diff --git a/TestProject/Test/BSMGeneratorUnitTest.cs b/TestProject/Test/BSMGeneratorUnitTest.cs
index 17461b5..c6f1ee0 100644
--- a/TestProject/Test/BSMGeneratorUnitTest.cs
+++ b/TestProject/Test/BSMGeneratorUnitTest.cs
@@ -7,26 +7,6 @@ namespace TestProject.Test;
///
public class BSMGeneratorUnitTest
{
- ///
- /// The constant for the .P given name.
- ///
- private const string DotPGivenName = "PASSENGER";
-
- ///
- /// The constant for the .P surname.
- ///
- private const string DotPSurName = "TEST";
-
- ///
- /// The constant for the .V airport code.
- ///
- private const string DotVAirportCode = "MCO";
-
- ///
- /// The constant for the .V data dictionary version number.
- ///
- private const int DotVDataDictionaryVersionNumber = 1;
-
///
/// The constant for the maximum number of airlines used by the BSM generator.
///
@@ -50,14 +30,14 @@ public void VerifyAirlineRollover()
//Verify the baggage tag details.
Assert.NotNull(bsm.BaggageTagDetails);
Assert.Single(bsm.BaggageTagDetails.BaggageTagNumbers);
- Assert.Equal($"0001{(BSMGenerator.MaxPassengerCount + 1).ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
+ Assert.Equal($"0{BSMGenerator.AmericanAirlinesNumericCode}{(BSMGenerator.MaxPassengerCount + 1).ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
//Verify the change of status.
Assert.Equal(BSM.Add, bsm.ChangeOfStatus);
//Verify the outbound flight.
Assert.NotNull(bsm.OutboundFlight);
- Assert.Equal("AA", bsm.OutboundFlight.Airline);
+ Assert.Equal(BSMGenerator.AmericanAirlineAlphaCode, bsm.OutboundFlight.Airline);
Assert.NotEmpty(bsm.OutboundFlight.ClassOfTravel); //Class of Travel is randomly set so just make sure its set to something.
Assert.NotEmpty(bsm.OutboundFlight.Destination); //Destination is randomly set so just make sure its set to something.
Assert.Equal(DateTime.Today.ToDayMonthFormat(), bsm.OutboundFlight.FlightDate);
@@ -66,14 +46,14 @@ public void VerifyAirlineRollover()
//Verify the passenger name.
Assert.NotNull(bsm.PassengerName);
Assert.Single(bsm.PassengerName.GivenNames);
- Assert.Equal($"{DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
- Assert.Equal(DotPSurName, bsm.PassengerName.SurName);
+ Assert.Equal($"{BSMGenerator.DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
+ Assert.Equal(BSMGenerator.DotPSurName, bsm.PassengerName.SurName);
//Verify the version supplementary data.
Assert.NotNull(bsm.VersionSupplementaryData);
- Assert.Equal(DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
+ Assert.Equal(BSMGenerator.DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
Assert.Equal(VersionSupplementaryData.LocalBaggageSourceIndicator, bsm.VersionSupplementaryData.BaggageSourceIndicator);
- Assert.Equal(DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
+ Assert.Equal(BSMGenerator.DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
}
///
@@ -110,14 +90,14 @@ public void VerifyFlightNumberRollover()
//Verify the passenger name.
Assert.NotNull(bsm.PassengerName);
Assert.Single(bsm.PassengerName.GivenNames);
- Assert.Equal($"{DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
- Assert.Equal(DotPSurName, bsm.PassengerName.SurName);
+ Assert.Equal($"{BSMGenerator.DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
+ Assert.Equal(BSMGenerator.DotPSurName, bsm.PassengerName.SurName);
//Verify the version supplementary data.
Assert.NotNull(bsm.VersionSupplementaryData);
- Assert.Equal(DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
+ Assert.Equal(BSMGenerator.DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
Assert.Equal(VersionSupplementaryData.LocalBaggageSourceIndicator, bsm.VersionSupplementaryData.BaggageSourceIndicator);
- Assert.Equal(DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
+ Assert.Equal(BSMGenerator.DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
}
///
@@ -132,14 +112,14 @@ public void VerifyGeneration()
//Verify the baggage tag details.
Assert.NotNull(bsm.BaggageTagDetails);
Assert.Single(bsm.BaggageTagDetails.BaggageTagNumbers);
- Assert.Equal($"0001{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
+ Assert.Equal($"0{BSMGenerator.AmericanAirlinesNumericCode}{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
//Verify the change of status.
Assert.Equal(BSM.Add, bsm.ChangeOfStatus);
//Verify the outbound flight.
Assert.NotNull(bsm.OutboundFlight);
- Assert.Equal("AA", bsm.OutboundFlight.Airline);
+ Assert.Equal(BSMGenerator.AmericanAirlineAlphaCode, bsm.OutboundFlight.Airline);
Assert.NotEmpty(bsm.OutboundFlight.ClassOfTravel); //Class of Travel is randomly set so just make sure its set to something.
Assert.NotEmpty(bsm.OutboundFlight.Destination); //Destination is randomly set so just make sure its set to something.
Assert.Equal(DateTime.Today.ToDayMonthFormat(), bsm.OutboundFlight.FlightDate);
@@ -148,14 +128,14 @@ public void VerifyGeneration()
//Verify the passenger name.
Assert.NotNull(bsm.PassengerName);
Assert.Single(bsm.PassengerName.GivenNames);
- Assert.Equal($"{DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
- Assert.Equal(DotPSurName, bsm.PassengerName.SurName);
+ Assert.Equal($"{BSMGenerator.DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
+ Assert.Equal(BSMGenerator.DotPSurName, bsm.PassengerName.SurName);
//Verify the version supplementary data.
Assert.NotNull(bsm.VersionSupplementaryData);
- Assert.Equal(DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
+ Assert.Equal(BSMGenerator.DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
Assert.Equal(VersionSupplementaryData.LocalBaggageSourceIndicator, bsm.VersionSupplementaryData.BaggageSourceIndicator);
- Assert.Equal(DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
+ Assert.Equal(BSMGenerator.DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
}
///
@@ -170,13 +150,13 @@ public void VerifyPassengerIncrementsByOne()
BSM bsm3 = bsmGenerator.Generate();
Assert.NotNull(bsm1.PassengerName);
- Assert.Equal($"{DotPGivenName}1", bsm1.PassengerName.GivenNames[0]);
+ Assert.Equal($"{BSMGenerator.DotPGivenName}1", bsm1.PassengerName.GivenNames[0]);
Assert.NotNull(bsm2.PassengerName);
- Assert.Equal($"{DotPGivenName}2", bsm2.PassengerName.GivenNames[0]);
+ Assert.Equal($"{BSMGenerator.DotPGivenName}2", bsm2.PassengerName.GivenNames[0]);
Assert.NotNull(bsm3.PassengerName);
- Assert.Equal($"{DotPGivenName}3", bsm3.PassengerName.GivenNames[0]);
+ Assert.Equal($"{BSMGenerator.DotPGivenName}3", bsm3.PassengerName.GivenNames[0]);
}
///
@@ -197,14 +177,14 @@ public void VerifyPassengerRollover()
Assert.NotNull(bsm.BaggageTagDetails);
Assert.Single(bsm.BaggageTagDetails.BaggageTagNumbers);
//The sequence used by the BSM generator is 001, 006, 016, 526 so on the first passenger rollover, 006 will be used next.
- Assert.Equal($"0006{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
+ Assert.Equal($"0{BSMGenerator.DeltaNumericCode}{IATAGenerator.MinSequenceNumber.ToString().PadLeft(6, '0')}", bsm.BaggageTagDetails.BaggageTagNumbers[0]);
//Verify the change of status.
Assert.Equal(BSM.Add, bsm.ChangeOfStatus);
//Verify the outbound flight.
Assert.NotNull(bsm.OutboundFlight);
- Assert.Equal("DL", bsm.OutboundFlight.Airline); //The sequence used by the BSM generator is AA, DL, UA, WN so on the first passenger rollover, DL will be used next.
+ Assert.Equal(BSMGenerator.DeltaAlphaCode, bsm.OutboundFlight.Airline); //The sequence used by the BSM generator is AA, DL, UA, WN so on the first passenger rollover, DL will be used next.
Assert.NotEmpty(bsm.OutboundFlight.ClassOfTravel); //Class of Travel is randomly set so just make sure its set to something.
Assert.NotEmpty(bsm.OutboundFlight.Destination); //Destination is randomly set so just make sure its set to something.
Assert.Equal(DateTime.Today.ToDayMonthFormat(), bsm.OutboundFlight.FlightDate);
@@ -213,13 +193,13 @@ public void VerifyPassengerRollover()
//Verify the passenger name.
Assert.NotNull(bsm.PassengerName);
Assert.Single(bsm.PassengerName.GivenNames);
- Assert.Equal($"{DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
- Assert.Equal(DotPSurName, bsm.PassengerName.SurName);
+ Assert.Equal($"{BSMGenerator.DotPGivenName}1", bsm.PassengerName.GivenNames[0]);
+ Assert.Equal(BSMGenerator.DotPSurName, bsm.PassengerName.SurName);
//Verify the version supplementary data.
Assert.NotNull(bsm.VersionSupplementaryData);
- Assert.Equal(DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
+ Assert.Equal(BSMGenerator.DotVAirportCode, bsm.VersionSupplementaryData.AirportCode);
Assert.Equal(VersionSupplementaryData.LocalBaggageSourceIndicator, bsm.VersionSupplementaryData.BaggageSourceIndicator);
- Assert.Equal(DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
+ Assert.Equal(BSMGenerator.DotVDataDictionaryVersionNumber, bsm.VersionSupplementaryData.DataDictionaryVersionNumber);
}
}
From dabf7356a36800fcba842f4c12e765e8dcb19118 Mon Sep 17 00:00:00 2001
From: jmayer913 <72579603+jmayer913@users.noreply.github.com>
Date: Sat, 17 Aug 2024 14:02:27 -0400
Subject: [PATCH 11/11] Added github runner action
---
workflows/mainworkflow.yml | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 workflows/mainworkflow.yml
diff --git a/workflows/mainworkflow.yml b/workflows/mainworkflow.yml
new file mode 100644
index 0000000..c8c3466
--- /dev/null
+++ b/workflows/mainworkflow.yml
@@ -0,0 +1,29 @@
+# This workflow will build a .NET project
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
+
+name: MainWorkflow
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ build:
+
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.0.x
+ - name: Restore dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build --configuration Release --no-restore
+ - name: Test
+ run: dotnet test --verbosity normal