From f2da98f2312f91230ed40e819198626c202bebd1 Mon Sep 17 00:00:00 2001 From: Howard van Rooijen Date: Mon, 16 Jun 2025 21:26:55 +0100 Subject: [PATCH 01/17] Update NuGet packages and refactor test lifecycle Updated several NuGet packages in `Ais.Net.Models.Specs.csproj` to their latest versions, including `coverlet.msbuild`, `Microsoft.NET.Test.Sdk`, `NUnit3TestAdapter`, `Reqnroll.NUnit`, and `Shouldly`. Added `[NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)]` to `AisMessageType18Feature` for improved test fixture management. Refactored `FeatureSetupAsync` and `FeatureTearDownAsync` methods to be static, enhancing test setup practices. Enhanced `TestInitializeAsync`, `ScenarioStartAsync`, and `ScenarioCleanupAsync` methods for better error handling and resource management. Updated `packages.lock.json` to reflect new package versions and modified `Ais.Net.Models.csproj` to upgrade `Endjin.RecommendedPractices.GitHub` from `2.1.13` to `2.1.18`. --- .../Ais.Net.Models.Specs.csproj | 10 +- .../Features/AisMessageType18.feature.cs | 68 +++-- .../Ais.Net.Models.Specs/packages.lock.json | 243 ++++++++---------- .../Ais.Net.Models/Ais.Net.Models.csproj | 2 +- 4 files changed, 161 insertions(+), 162 deletions(-) diff --git a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj index 1eea670..5e1b7c0 100644 --- a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj +++ b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj @@ -20,14 +20,14 @@ - + contentfiles; analyzers - - - - + + + + diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs index aab3790..74a8519 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs @@ -10,17 +10,16 @@ // ------------------------------------------------------------------------------ #region Designer generated code #pragma warning disable +using Reqnroll; namespace Ais.Net.Models.Specs.Features { - using Reqnroll; - using System; - using System.Linq; - [System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] [NUnit.Framework.DescriptionAttribute("AisMessageType18")] + [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] public partial class AisMessageType18Feature { @@ -28,34 +27,63 @@ public partial class AisMessageType18Feature private static string[] featureTags = ((string[])(null)); + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType18", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + #line 1 "AisMessageType18.feature" #line hidden [NUnit.Framework.OneTimeSetUpAttribute()] - public virtual async System.Threading.Tasks.Task FeatureSetupAsync() + public static async global::System.Threading.Tasks.Task FeatureSetupAsync() { - testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(); - global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType18", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); - await testRunner.OnFeatureStartAsync(featureInfo); } [NUnit.Framework.OneTimeTearDownAttribute()] - public virtual async System.Threading.Tasks.Task FeatureTearDownAsync() + public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() { - await testRunner.OnFeatureEndAsync(); - global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); - testRunner = null; } [NUnit.Framework.SetUpAttribute()] - public async System.Threading.Tasks.Task TestInitializeAsync() + public async global::System.Threading.Tasks.Task TestInitializeAsync() { + testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); + try + { + if (((testRunner.FeatureContext != null) + && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) + { + await testRunner.OnFeatureEndAsync(); + } + } + finally + { + if (((testRunner.FeatureContext != null) + && testRunner.FeatureContext.BeforeFeatureHookFailed)) + { + throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); + } + if ((testRunner.FeatureContext == null)) + { + await testRunner.OnFeatureStartAsync(featureInfo); + } + } } [NUnit.Framework.TearDownAttribute()] - public async System.Threading.Tasks.Task TestTearDownAsync() + public async global::System.Threading.Tasks.Task TestTearDownAsync() { - await testRunner.OnScenarioEndAsync(); + if ((testRunner == null)) + { + return; + } + try + { + await testRunner.OnScenarioEndAsync(); + } + finally + { + global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); + testRunner = null; + } } public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) @@ -64,22 +92,22 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); } - public async System.Threading.Tasks.Task ScenarioStartAsync() + public async global::System.Threading.Tasks.Task ScenarioStartAsync() { await testRunner.OnScenarioStartAsync(); } - public async System.Threading.Tasks.Task ScenarioCleanupAsync() + public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() { await testRunner.CollectScenarioErrorsAsync(); } [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Create AisMessageType18 record")] - public async System.Threading.Tasks.Task CreateAisMessageType18Record() + public async global::System.Threading.Tasks.Task CreateAisMessageType18Record() { string[] tagsOfScenario = ((string[])(null)); - System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary(); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType18 record", null, tagsOfScenario, argumentsOfScenario, featureTags); #line 3 this.ScenarioInitialize(scenarioInfo); diff --git a/Solutions/Ais.Net.Models.Specs/packages.lock.json b/Solutions/Ais.Net.Models.Specs/packages.lock.json index e2b9594..bb1041e 100644 --- a/Solutions/Ais.Net.Models.Specs/packages.lock.json +++ b/Solutions/Ais.Net.Models.Specs/packages.lock.json @@ -4,42 +4,46 @@ "net8.0": { "coverlet.msbuild": { "type": "Direct", - "requested": "[6.0.2, )", - "resolved": "6.0.2", - "contentHash": "8b4jBNH7mcQy1otyQErjjIUuGD74XxKZ1wvDufbY7jhWwckl7wIa+icjwdPYeI0aYMS4Tp63LIZvyMFjWwOMDw==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "Qa7Hg+wrOMDKpXVn2dw4Wlun490bIWsFW0fdNJQFJLZnbU27MCP0HJ2mPgS+3EQBQUb0zKlkwiQzP+j38Hc3Iw==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.11.1, )", - "resolved": "17.11.1", - "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", "dependencies": { - "Microsoft.CodeCoverage": "17.11.1", - "Microsoft.TestPlatform.TestHost": "17.11.1" + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" } }, "NUnit3TestAdapter": { "type": "Direct", - "requested": "[4.6.0, )", - "resolved": "4.6.0", - "contentHash": "R7e1+a4vuV/YS+ItfL7f//rG+JBvVeVLX4mHzFEZo4W1qEKl8Zz27AqvQSAqo+BtIzUCo4aAJMYa56VXS4hudw==" + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "sy4cLoUAdE6TDM4wNX5gmNCyhMev5wUz4cA6ZRf/aON9vf9t4xTVGLj/4huhDKcS4dFfmVVcgcP70yC7WC9kKg==", + "dependencies": { + "Microsoft.Testing.Extensions.VSTestBridge": "1.5.3", + "Microsoft.Testing.Platform.MSBuild": "1.5.3" + } }, "Reqnroll.NUnit": { "type": "Direct", - "requested": "[2.1.1, )", - "resolved": "2.1.1", - "contentHash": "VcacexjyMd32HmbiYX/QD5vynid+xLjXym835KR68q0vVAA2PWCCRY+bZkLoGb0k/wsddsTQ+w2lCRq1+U2Z7A==", + "requested": "[2.4.1, )", + "resolved": "2.4.1", + "contentHash": "IbP1uVirMVpopeRwjvtz2b0PO9oQtHcDnAQigTpZY9lxSRYBycQSYQaZ6fbkkg/mG/azkPPGV3YIjNILQ8jWrw==", "dependencies": { "NUnit": "3.13.1", - "Reqnroll": "[2.1.1]", - "Reqnroll.Tools.MsBuild.Generation": "[2.1.1]" + "Reqnroll": "[2.4.1]", + "Reqnroll.Tools.MsBuild.Generation": "[2.4.1]" } }, "Shouldly": { "type": "Direct", - "requested": "[4.2.1, )", - "resolved": "4.2.1", - "contentHash": "dKAKiSuhLKqD2TXwLKtqNg1nwzJcIKOOMncZjk9LYe4W+h+SCftpWdxwR79YZUIHMH+3Vu9s0s0UHNrgICLwRQ==", + "requested": "[4.3.0, )", + "resolved": "4.3.0", + "contentHash": "sDetrWXrl6YXZ4HeLsdBoNk3uIa7K+V4uvIJ+cqdRa5DrFxeTED7VkjoxCuU1kJWpUuBDZz2QXFzSxBtVXLwRQ==", "dependencies": { "DiffEngine": "11.3.0", "EmptyFiles": "4.4.0" @@ -74,48 +78,89 @@ }, "Gherkin": { "type": "Transitive", - "resolved": "29.0.0", - "contentHash": "az9zmD5V72UZUzYcDyHoZHFRT5eSLOg1Co2r3AtlshPe41Am7iMwOJG+xHxZ1Pfn6agGoWw1dhm6Pn8Dg6QBUg==" + "resolved": "30.0.0", + "contentHash": "jZM9zm9eg62vRSAOtYvcmQh4w0oYqjP6myKxMaJAlQq9VCKm4wnTfHhS5QRnLhx/sH3R5jTaoQAKLs5hhjuq7g==" + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.22.0", + "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.11.1", - "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA==" + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "5Ou6varcxLBzQ+Agfm0k0pnH7vrEITYlXMDuE6s7ZHlZHz6/G8XJ3iISZDr5rfwfge6RnXJ1+Wc479mMn52vjA==", - "dependencies": { - "System.Text.Encodings.Web": "8.0.0", - "System.Text.Json": "8.0.4" - } + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" }, - "Microsoft.NETCore.Targets": { + "Microsoft.Testing.Extensions.Telemetry": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "1.5.3", + "contentHash": "U9pGd5DQuX1PfkrdFI+xH34JGgQ2nes5QAwIITTk+MQfLvRITqsZjJeHTjpGWh33D/0q1l7aA8/LQHR7UuCgLQ==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.22.0", + "Microsoft.Testing.Platform": "1.5.3" + } + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "h34zKNpGyni66VH738mRHeXSnf3klSShUdavUWNhSfWICUUi5aXeI0LBvoX/ad93N0+9xBDU3Fyi6WfxrwKQGw==", + "dependencies": { + "Microsoft.Testing.Platform": "1.5.3" + } + }, + "Microsoft.Testing.Extensions.VSTestBridge": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "cJD67YfDT98wEWyazKVD/yPVW6+H1usXeuselCnRes7JZBTIYWtrCchcOzOahnmajT79eDKqt9sta7DXwTDU4Q==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.22.0", + "Microsoft.TestPlatform.ObjectModel": "17.12.0", + "Microsoft.Testing.Extensions.Telemetry": "1.5.3", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.5.3", + "Microsoft.Testing.Platform": "1.5.3" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "WqJydnJ99dEKtquR9HwINz104ehWJKTXbQQrydGatlLRw14bmsx0pa8+E6KUXMYXZAimN0swWlDmcJGjjW4TIg==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "bOtpRMSPeT5YLQo+NNY8EtdNTphAUcmALjW4ABU7P0rb6yR2XAZau3TzNieLmR3lRuwudguWzzBhgcLRXwZh0A==", + "dependencies": { + "Microsoft.Testing.Platform": "1.5.3" + } }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.11.1", - "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", "dependencies": { - "System.Reflection.Metadata": "1.6.0" + "System.Reflection.Metadata": "8.0.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.11.1", - "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.11.1", - "Newtonsoft.Json": "13.0.1" + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" } }, "NETStandard.Library": { @@ -128,8 +173,8 @@ }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, "NUnit": { "type": "Transitive", @@ -141,45 +186,37 @@ }, "Reqnroll": { "type": "Transitive", - "resolved": "2.1.1", - "contentHash": "RhV5B0lxVXzP+0U9OUkWf3nMGfSlWnxQdUp0mcatvuQISxB8ZPjdhJB3T4Vjo3bFMiQb/0LUXUmNZ0IqBPZldw==", + "resolved": "2.4.1", + "contentHash": "Ui229q21Kmkrv3L89THgsxGMXooJGK5OrFPLvYDXfE2nO/u4tqYsbmMRddyEE3fsR40IDP++HsRrDxlkR6reVw==", "dependencies": { "Cucumber.CucumberExpressions": "17.1.0", - "Gherkin": "29.0.0", - "Microsoft.Extensions.DependencyModel": "8.0.1", - "SpecFlow.Internal.Json": "1.0.8", - "System.Runtime.Loader": "4.3.0" + "Gherkin": "30.0.0", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "System.Text.Json": "8.0.5" } }, "Reqnroll.Tools.MsBuild.Generation": { "type": "Transitive", - "resolved": "2.1.1", - "contentHash": "xjv2ihoRHpAAjhZX9d964/0R8mhgHiquPBjnJmhc2sYQnemDCkHIsb4d2VAMKS/HeiXLi6NAxujjgAYFBcxDKg==", + "resolved": "2.4.1", + "contentHash": "+Up0eUWnKRKPdRIttE52DYzwayUd4bGMyRytYeeCwtqwZA4tLhK2NspOZJ8axPcmATOM9QSTCUHUlyp9pgcfQg==", "dependencies": { - "Reqnroll": "[2.1.1]" + "Reqnroll": "[2.4.1]" } }, - "SpecFlow.Internal.Json": { - "type": "Transitive", - "resolved": "1.0.8", - "contentHash": "lVCC/Rie7N5rFoc7YxPS0nneLfsWSTIMMlkndwxhaD8MxBp3Bsv1HeiVjVwXCjWaQeoqZcvIy52fF5Xit00ZLw==" - }, "System.CodeDom": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" }, - "System.IO": { + "System.Collections.Immutable": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" }, "System.IO.Pipelines": { "type": "Transitive", @@ -194,84 +231,18 @@ "System.CodeDom": "6.0.0" } }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.Loader": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHMaRn8D8YCK2GG2pw+UzNxn/OHVfaWx7OTLBD/hPegHZZgcZh3H6seWegrC4BYwsfuGrywIuT+MQs+rPqRLTQ==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Text.Encodings.Web": { "type": "Transitive", "resolved": "8.0.0", - "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "8.0.4", - "contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", "dependencies": { - "System.Text.Encodings.Web": "8.0.0" + "System.Collections.Immutable": "8.0.0" } }, - "System.Threading.Tasks": { + "System.Text.Json": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "ais.net.models": { "type": "Project", diff --git a/Solutions/Ais.Net.Models/Ais.Net.Models.csproj b/Solutions/Ais.Net.Models/Ais.Net.Models.csproj index debd8fd..3d1147f 100644 --- a/Solutions/Ais.Net.Models/Ais.Net.Models.csproj +++ b/Solutions/Ais.Net.Models/Ais.Net.Models.csproj @@ -22,7 +22,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 4d638c8646389febea4c45166afd66d7d8890290 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Tue, 17 Jun 2025 11:58:54 +0100 Subject: [PATCH 02/17] Migrate to ZF --- .devops/config.ps1 | 42 ----------- .github/workflows/build.yml | 147 ++++++++++++++++++++++-------------- .gitignore | 1 + .zf/config.ps1 | 68 +++++++++++++++++ build.ps1 | 59 +++++++-------- 5 files changed, 185 insertions(+), 132 deletions(-) delete mode 100644 .devops/config.ps1 create mode 100644 .zf/config.ps1 diff --git a/.devops/config.ps1 b/.devops/config.ps1 deleted file mode 100644 index 34e1621..0000000 --- a/.devops/config.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -$devopsExtensions = @( - @{ - Name = "Endjin.RecommendedPractices.Build" - Version = "[1.5.10,2.0)" - Process = "tasks/build.process.ps1" - } -) - -# Load the tasks and process -. endjin-devops.tasks - -# -# Build process configuration -# -$SolutionToBuild = (Resolve-Path (Join-Path $here "./Solutions/Ais.Net.Models.sln")).Path -$SkipBuildModuleVersionCheck = $true # currently doesn't work properly with endjin-devops - -# Set default build task -task . FullBuild - -# -# Build Process Extensibility Points - uncomment and implement as required -# - -# task RunFirst {} -# task PreInit {} -# task PostInit {} -# task PreVersion {} -# task PostVersion {} -# task PreBuild {} -# task PostBuild {} -# task PreTest {} -# task PostTest {} -# task PreTestReport {} -# task PostTestReport {} -# task PreAnalysis {} -# task PostAnalysis {} -# task PrePackage {} -# task PostPackage {} -# task PrePublish {} -# task PostPublish {} -# task RunLast {} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1354ad0..af5460f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,58 +1,89 @@ -name: build -on: - push: - branches: - - main - tags: - - '*' - pull_request: - branches: - - main - workflow_dispatch: - inputs: - forcePublish: - description: When true the Publish stage will always be run, otherwise it only runs for tagged versions. - required: false - default: false - type: boolean - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - checks: write # enable test result annotations - contents: write # enable creating releases - issues: read - packages: write # enable publishing packages - pull-requests: write # enable test result annotations - -jobs: - prepareConfig: - name: Prepare Configuration - runs-on: ubuntu-latest - outputs: - RESOLVED_ENV_VARS: ${{ steps.prepareEnvVarsAndSecrets.outputs.environmentVariablesYamlBase64 }} - RESOLVED_SECRETS: ${{ steps.prepareEnvVarsAndSecrets.outputs.secretsYamlBase64 }} - steps: - # Declare any environment variables and/or secrets that need to be available inside the build process - - uses: endjin/Endjin.RecommendedPractices.GitHubActions/actions/prepare-env-vars-and-secrets@main - id: prepareEnvVarsAndSecrets - with: - environmentVariablesYaml: | - BUILDVAR_NuGetPublishSource: "${{ startsWith(github.ref, 'refs/tags/') && 'https://api.nuget.org/v3/index.json' || 'https://nuget.pkg.github.com/ais-dotnet/index.json' }}" - BUILDVAR_Configuration: "Release" - secretsYaml: | - NUGET_API_KEY: "${{ startsWith(github.ref, 'refs/tags/') && secrets.NUGET_APIKEY || secrets.BUILD_PUBLISHER_PAT }}" - - build: - needs: prepareConfig - uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-single-job-pipeline.yml@main - with: - netSdkVersion: '8.0.x' - # workflow_dispatch inputs are always strings, the type property is just for the UI - forcePublish: ${{ github.event.inputs.forcePublish == 'true' }} - buildEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }} - secrets: - buildAzureCredentials: ${{ secrets.AZURE_READER_CREDENTIALS }} - buildSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }} +name: build +on: + push: + branches: + - main + tags: + - '*' + pull_request: + branches: + - main + workflow_dispatch: + inputs: + forcePublish: + description: When true the Publish stage will always be run, otherwise it only runs for tagged versions. + required: false + default: false + type: boolean + forcePublicNugetDestination: + description: When true the NuGet Publish destination will always be set to nuget.org. (Normally, force publishing publishes to the private feed on GitHub.) + required: false + default: false + type: boolean + skipCleanup: + description: When true the pipeline clean-up stage will not be run. For example, the cache used between pipeline stages will be retained. + required: false + default: false + type: boolean + +concurrency: + group: ${{ github.workflow }}-${{ github.sha }} + cancel-in-progress: true + +permissions: + actions: write # enable cache clean-up + checks: write # enable test result annotations + contents: write # enable creating releases + issues: read + packages: write # enable publishing packages + pull-requests: write # enable test result annotations + +jobs: + prepareConfig: + name: Prepare Configuration + runs-on: ubuntu-latest + outputs: + RESOLVED_ENV_VARS: ${{ steps.prepareEnvVarsAndSecrets.outputs.environmentVariablesYamlBase64 }} + RESOLVED_SECRETS: ${{ steps.prepareEnvVarsAndSecrets.outputs.secretsYamlBase64 }} + steps: + # Declare any environment variables and/or secrets that need to be available inside the build process + - uses: endjin/Endjin.RecommendedPractices.GitHubActions/actions/prepare-env-vars-and-secrets@main + id: prepareEnvVarsAndSecrets + with: + environmentVariablesYaml: | + ZF_NUGET_PUBLISH_SOURCE: ${{ (startsWith(github.ref, 'refs/tags/') || github.event.inputs.forcePublicNugetDestination == 'true') && 'https://api.nuget.org/v3/index.json' || format('https://nuget.pkg.github.com/{0}/index.json', github.repository_owner) }} + secretsYaml: | + NUGET_API_KEY: "${{ startsWith(github.ref, 'refs/tags/') && secrets.NUGET_APIKEY || secrets.BUILD_PUBLISHER_PAT }}" + secretsEncryptionKey: ${{ secrets.SHARED_WORKFLOW_KEY }} + + build: + needs: prepareConfig + uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-matrix-pipeline.yml@main + with: + netSdkVersion: '8.x' + # additionalNetSdkVersion: '9.x' + # workflow_dispatch inputs are always strings, the type property is just for the UI + forcePublish: ${{ github.event.inputs.forcePublish == 'true' }} + skipCleanup: ${{ github.event.inputs.skipCleanup == 'true' }} + enableCrossOsCaching: true + testPhaseMatrixJson: | + { + "os": ["windows-latest", "ubuntu-latest"], + "dotnetFramework": ["net8.0"] + } + # testArtifactName: '' + # testArtifactPath: '' + compilePhaseEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }} + testPhaseEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }} + packagePhaseEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }} + publishPhaseEnv: ${{ needs.prepareConfig.outputs.RESOLVED_ENV_VARS }} + secrets: + compilePhaseAzureCredentials: ${{ secrets.AZURE_READER_CREDENTIALS }} + # testPhaseAzureCredentials: ${{ secrets.TESTS_KV_READER_CREDENTIALS }} + # packagePhaseAzureCredentials: ${{ secrets.AZURE_PUBLISH_CREDENTIALS }} + # publishPhaseAzureCredentials: ${{ secrets.AZURE_PUBLISH_CREDENTIALS }} + # compilePhaseSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }} + # testPhaseSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }} + # packagePhaseSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }} + publishPhaseSecrets: ${{ needs.prepareConfig.outputs.RESOLVED_SECRETS }} + secretsEncryptionKey: ${{ secrets.SHARED_WORKFLOW_KEY }} diff --git a/.gitignore b/.gitignore index 8ee7fb9..52b09d1 100644 --- a/.gitignore +++ b/.gitignore @@ -401,4 +401,5 @@ FodyWeavers.xsd # Build outputs _codeCoverage/ _packages/ +.zf/extensions/ *.sbom.* \ No newline at end of file diff --git a/.zf/config.ps1 b/.zf/config.ps1 new file mode 100644 index 0000000..5b4eaf1 --- /dev/null +++ b/.zf/config.ps1 @@ -0,0 +1,68 @@ +<# +This example demonstrates a software build process using the 'ZeroFailed.Build.DotNet' extension +to provide the features needed when building a .NET solutions. +#> + +$zerofailedExtensions = @( + @{ + # References the extension from its GitHub repository. If not already installed, use latest version from 'main' will be downloaded. + Name = "ZeroFailed.Build.DotNet" + GitRepository = "https://github.com/zerofailed/ZeroFailed.Build.DotNet" + GitRef = "main" + } +) + +# Load the tasks and process +. ZeroFailed.tasks -ZfPath $here/.zf + + +# +# Build process control options +# +$SkipInit = $false +$SkipVersion = $false +$SkipBuild = $false +$CleanBuild = $Clean +$SkipTest = $false +$SkipTestReport = $false +$SkipAnalysis = $false +$SkipPackage = $false + +# +# Build process configuration +# +$SolutionToBuild = (Resolve-Path (Join-Path $here "./Solutions/Ais.Net.Models.sln")).Path +$ProjectsToPublish = @() +$NuSpecFilesToPackage = @() +$NugetPublishSource = property ZF_NUGET_PUBLISH_SOURCE "$here/_local-nuget-feed" +$IncludeAssembliesInCodeCoverage = "Ais.Net.Models" +$ExcludeAssembliesInCodeCoverage = "" + + +# Customise the build process + +task . FullBuild + + +# +# Build Process Extensibility Points - uncomment and implement as required +# + +# task RunFirst {} +# task PreInit {} +# task PostInit {} +# task PreVersion {} +# task PostVersion {} +# task PreBuild {} +# task PostBuild {} +# task PreTest {} +# task PostTest {} +# task PreTestReport {} +# task PostTestReport {} +# task PreAnalysis {} +# task PostAnalysis {} +# task PrePackage {} +# task PostPackage {} +# task PrePublish {} +# task PostPublish {} +# task RunLast {} diff --git a/build.ps1 b/build.ps1 index aa78da4..bb95a74 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,11 +3,11 @@ .SYNOPSIS Runs a .NET flavoured build process. .DESCRIPTION - This script was scaffolded using a template from the Endjin.RecommendedPractices.Build PowerShell module. - It uses the InvokeBuild module to orchestrate an opinonated software build process for .NET solutions. + This script was scaffolded using a template from the ZeroFailed project. + It uses the InvokeBuild module to orchestrate an opinionated software build process for .NET solutions. .EXAMPLE PS C:\> ./build.ps1 - Downloads any missing module dependencies (Endjin.RecommendedPractices.Build & InvokeBuild) and executes + Downloads any missing module dependencies (ZeroFailed & InvokeBuild) and executes the build process. .PARAMETER Tasks Optionally override the default task executed as the entry-point of the build. @@ -27,14 +27,12 @@ The logging verbosity. .PARAMETER Clean When true, the .NET solution will be cleaned and all output/intermediate folders deleted. -.PARAMETER BuildModulePath - The path to import the Endjin.RecommendedPractices.Build module from. This is useful when - testing pre-release versions of the Endjin.RecommendedPractices.Build that are not yet - available in the PowerShell Gallery. -.PARAMETER BuildModuleVersion - The version of the Endjin.RecommendedPractices.Build module to import. This is useful when - testing pre-release versions of the Endjin.RecommendedPractices.Build that are not yet - available in the PowerShell Gallery. +.PARAMETER ZfModulePath + The path to import the ZeroFailed module from. This is useful when testing pre-release + versions of ZeroFailed that are not yet available in the PowerShell Gallery. +.PARAMETER ZfModuleVersion + The version of the ZeroFailed module to import. This is useful when testing pre-release + versions of ZeroFailed that are not yet available in the PowerShell Gallery. .PARAMETER InvokeBuildModuleVersion The version of the InvokeBuild module to be used. #> @@ -69,13 +67,13 @@ param ( [switch] $Clean, [Parameter()] - [string] $BuildModulePath, + [string] $ZfModulePath, [Parameter()] - [string] $BuildModuleVersion = "1.0.0-preview0020", + [string] $ZfModuleVersion = "1.0.5", [Parameter()] - [version] $InvokeBuildModuleVersion = "5.11.3" + [version] $InvokeBuildModuleVersion = "5.12.1" ) $ErrorActionPreference = 'Stop' $here = Split-Path -Parent $PSCommandPath @@ -89,14 +87,6 @@ if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { Invoke-Build $Tasks $MyInvocation.MyCommand.Path @PSBoundParameters } catch { - if ($env:GITHUB_ACTIONS) { - Write-Host ("::error file={0},line={1},col={2}::{3}" -f ` - $_.InvocationInfo.ScriptName, - $_.InvocationInfo.ScriptLineNumber, - $_.InvocationInfo.OffsetInLine, - $_.Exception.Message - ) - } Write-Host -f Yellow "`n`n***`n*** Build Failure Summary - check previous logs for more details`n***" Write-Host -f Yellow $_.Exception.Message Write-Host -f Yellow $_.ScriptStackTrace @@ -106,21 +96,26 @@ if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { } #endregion -#region Import shared tasks and initialise build framework +#region Initialise build framework $splat = @{ Force = $true; Verbose = $false} -if (!($BuildModulePath)) { - Install-PSResource endjin-devops -Version $BuildModuleVersion -Scope CurrentUser -TrustRepository -Reinstall:$($BuildModuleVersion -match '-') | Out-Null - $BuildModulePath = "endjin-devops" - $splat.Add("RequiredVersion", ($BuildModuleVersion -split '-')[0]) +Import-Module Microsoft.PowerShell.PSResourceGet +if (!($ZfModulePath)) { + Install-PSResource ZeroFailed -Version $ZfModuleVersion -Scope CurrentUser -TrustRepository | Out-Null + $ZfModulePath = "ZeroFailed" + $splat.Add("RequiredVersion", ($ZfModuleVersion -split '-')[0]) } else { - Write-Host "BuildModulePath: $BuildModulePath" + Write-Host "ZfModulePath: $ZfModulePath" } -$splat.Add("Name", $BuildModulePath) +$splat.Add("Name", $ZfModulePath) +# Ensure only 1 version of the module is loaded +Get-Module ZeroFailed | Remove-Module Import-Module @splat -$ver = "{0} {1}" -f (Get-Module endjin-devops).Version, (Get-Module endjin-devops).PrivateData.PsData.PreRelease -Write-Host "Using Build module version: $ver" +$ver = "{0} {1}" -f (Get-Module ZeroFailed).Version, (Get-Module ZeroFailed).PrivateData.PsData.PreRelease +Write-Host "Using ZeroFailed module version: $ver" #endregion +$PSModuleAutoloadingPreference = 'none' + # Load the build configuration -. $here/.devops/config.ps1 +. $here/.zf/config.ps1 From 91eca261ba26a54f9dc8ff70f0bd3747ca603994 Mon Sep 17 00:00:00 2001 From: Howard van Rooijen Date: Wed, 18 Jun 2025 09:06:16 +0100 Subject: [PATCH 03/17] Add new step definitions and update project file Updated `Ais.Net.Models.Specs.csproj` to include new compile and feature item groups for `.feature.cs` and `.feature` files. Introduced nullable types and null checks in `AisMessageType18StepDefinitions.cs` for better data integrity. Added new step definition classes for `AisMessageType19`, `AisMessageType1Through3`, `AisMessageType24Part0`, `AisMessageType24Part1`, `AisMessageType27`, and `AisMessageType5`, each with methods for creating records and validating properties. Corresponding feature files were auto-generated to define scenarios for testing the new message types. --- .../Ais.Net.Models.Specs.csproj | 9 + .../AisMessageType18StepDefinitions.cs | 21 +- .../AisMessageType19StepDefinitions.cs | 118 +++++++++++ .../AisMessageType1Through3StepDefinitions.cs | 106 ++++++++++ .../AisMessageType24Part0StepDefinitions.cs | 52 +++++ .../AisMessageType24Part1StepDefinitions.cs | 86 ++++++++ .../AisMessageType27StepDefinitions.cs | 82 ++++++++ .../AisMessageType5StepDefinitions.cs | 102 ++++++++++ .../Features/AisMessageType19.feature | 8 + .../Features/AisMessageType19.feature.cs | 183 ++++++++++++++++++ .../Features/AisMessageType1Through3.feature | 8 + .../AisMessageType1Through3.feature.cs | 175 +++++++++++++++++ .../Features/AisMessageType24Part0.feature | 8 + .../Features/AisMessageType24Part0.feature.cs | 147 ++++++++++++++ .../Features/AisMessageType24Part1.feature | 8 + .../Features/AisMessageType24Part1.feature.cs | 169 ++++++++++++++++ .../Features/AisMessageType27.feature | 8 + .../Features/AisMessageType27.feature.cs | 159 +++++++++++++++ .../Features/AisMessageType5.feature | 8 + .../Features/AisMessageType5.feature.cs | 179 +++++++++++++++++ 20 files changed, 1628 insertions(+), 8 deletions(-) create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature create mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs diff --git a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj index 5e1b7c0..4e626e1 100644 --- a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj +++ b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj @@ -42,4 +42,13 @@ PreserveNewest + + + %(Filename) + + + + + + diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs index aa37e8a..9d3245d 100644 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs @@ -9,8 +9,8 @@ namespace Ais.Net.Models.Specs; [Binding] public class AisMessageType18StepDefinitions { - private AisMessageType18 sut; - private AisMessageType18Data data; + private AisMessageType18? sut; + private AisMessageType18Data? data; [Given("a new AisMessageType18 record with the following properties:")] public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) @@ -28,18 +28,23 @@ public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable ta [When("the AisMessageType is created")] public void WhenTheAisMessageTypeIsCreated() { + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + this.sut = new AisMessageType18( CanAcceptMessage22ChannelAssignment: this.data.CanAcceptMessage22ChannelAssignment, CanSwitchBands: this.data.CanSwitchBands, CourseOverGround: this.data.CourseOverGroundDegrees, - CsUnit: Enum.Parse(this.data.CsUnit), + CsUnit: Enum.Parse(this.data.CsUnit ?? throw new InvalidOperationException()), HasDisplay: this.data.HasDisplay, IsAssigned: this.data.IsAssigned, IsDscAttached: this.data.IsDscAttached, Mmsi: this.data.Mmsi, - Position: new Position(this.data.Position.Latitude, this.data.Position.Longitude), + Position: new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0), PositionAccuracy: this.data.PositionAccuracy, - RadioStatusType: Enum.Parse(this.data.RadioStatusType), + RadioStatusType: Enum.Parse(this.data.RadioStatusType ?? throw new InvalidOperationException()), RaimFlag: this.data.RaimFlag, RegionalReserved139: this.data.RegionalReserved139, RegionalReserved38: this.data.RegionalReserved38, @@ -79,14 +84,14 @@ private class AisMessageType18Data public bool CanAcceptMessage22ChannelAssignment { get; set; } public bool CanSwitchBands { get; set; } public float? CourseOverGroundDegrees { get; set; } - public string CsUnit { get; set; } + public string? CsUnit { get; set; } public bool HasDisplay { get; set; } public bool IsAssigned { get; set; } public bool IsDscAttached { get; set; } public uint Mmsi { get; set; } - public PositionData Position { get; set; } + public PositionData? Position { get; set; } public bool PositionAccuracy { get; set; } - public string RadioStatusType { get; set; } + public string? RadioStatusType { get; set; } public bool RaimFlag { get; set; } public int RegionalReserved139 { get; set; } public int RegionalReserved38 { get; set; } diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs new file mode 100644 index 0000000..2931898 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs @@ -0,0 +1,118 @@ +namespace Ais.Net.Models.Specs; + +using Abstractions; +using System; + +using Reqnroll; +using Shouldly; + +[Binding] +public class AisMessageType19StepDefinitions +{ + private AisMessageType19? sut; + private AisMessageType19Data? data; + + [Given("a new AisMessageType19 record with the following properties:")] + public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) + { + PositionData position = new() + { + Latitude = Convert.ToInt32(table.Rows[0]["Position_Latitude"]), + Longitude = Convert.ToInt32(table.Rows[0]["Position_Longitude"]) + }; + + this.data = table.CreateInstance(); + this.data.Position = position; + } + + [When("the AisMessageType19 is created")] + public void WhenTheAisMessageTypeIsCreated() + { + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + this.sut = new AisMessageType19( + CourseOverGround: this.data.CourseOverGround, + DimensionToBow: this.data.DimensionToBow, + DimensionToPort: this.data.DimensionToPort, + DimensionToStarboard: this.data.DimensionToStarboard, + DimensionToStern: this.data.DimensionToStern, + IsAssigned: this.data.IsAssigned, + IsDteNotReady: this.data.IsDteNotReady, + Mmsi: this.data.Mmsi, + Position: new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0), + PositionAccuracy: this.data.PositionAccuracy, + PositionFixType: Enum.Parse(this.data.PositionFixType ?? throw new InvalidOperationException()), + RaimFlag: this.data.RaimFlag, + RegionalReserved139: this.data.RegionalReserved139, + RegionalReserved38: this.data.RegionalReserved38, + RepeatIndicator: this.data.RepeatIndicator, + ShipName: this.data.ShipName ?? throw new InvalidOperationException(), + ShipType: (ShipType)this.data.ShipType, + Spare308: this.data.Spare308, + SpeedOverGround: this.data.SpeedOverGround, + TimeStampSecond: this.data.TimeStampSecond, + TrueHeadingDegrees: this.data.TrueHeadingDegrees + ); + } + + [Then("the AisMessageType19 properties should be set correctly")] + public void ThenThePropertiesShouldBeSetCorrectly() + { + this.sut.ShouldNotBeNull(); + this.sut.CourseOverGround.ShouldBe(123.45f); + this.sut.DimensionToBow.ShouldBe(1u); + this.sut.DimensionToPort.ShouldBe(2u); + this.sut.DimensionToStarboard.ShouldBe(3u); + this.sut.DimensionToStern.ShouldBe(4u); + this.sut.IsAssigned.ShouldBeTrue(); + this.sut.IsDteNotReady.ShouldBeTrue(); + this.sut.Mmsi.ShouldBe(12345u); + this.sut.Position.ShouldBe(new Position(1.0, 2.0)); + this.sut.PositionAccuracy.ShouldBeTrue(); + this.sut.PositionFixType.ShouldBe(EpfdFixType.Gps); + this.sut.RaimFlag.ShouldBeTrue(); + this.sut.RegionalReserved139.ShouldBe(1); + this.sut.RegionalReserved38.ShouldBe(2); + this.sut.RepeatIndicator.ShouldBe(3u); + this.sut.ShipName.ShouldBe("SHIP"); + ((int)this.sut.ShipType).ShouldBe(60); + this.sut.Spare308.ShouldBe(1u); + this.sut.SpeedOverGround.ShouldBe(12.34f); + this.sut.TimeStampSecond.ShouldBe(56u); + this.sut.TrueHeadingDegrees.ShouldBe(78u); + } + + private class AisMessageType19Data + { + public float? CourseOverGround { get; set; } + public uint DimensionToBow { get; set; } + public uint DimensionToPort { get; set; } + public uint DimensionToStarboard { get; set; } + public uint DimensionToStern { get; set; } + public bool IsAssigned { get; set; } + public bool IsDteNotReady { get; set; } + public uint Mmsi { get; set; } + public PositionData? Position { get; set; } + public bool PositionAccuracy { get; set; } + public string? PositionFixType { get; set; } + public bool RaimFlag { get; set; } + public int RegionalReserved139 { get; set; } + public int RegionalReserved38 { get; set; } + public uint RepeatIndicator { get; set; } + public string? ShipName { get; set; } + public int ShipType { get; set; } + public uint Spare308 { get; set; } + public float? SpeedOverGround { get; set; } + public uint TimeStampSecond { get; set; } + public uint TrueHeadingDegrees { get; set; } + } + + private class PositionData + { + public int Latitude { get; set; } + public int Longitude { get; set; } + } +} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs new file mode 100644 index 0000000..0a8f2aa --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs @@ -0,0 +1,106 @@ +namespace Ais.Net.Models.Specs; + +using Abstractions; +using System; + +using Reqnroll; +using Shouldly; + +[Binding] +public class AisMessageType1Through3StepDefinitions +{ + private AisMessageType1Through3? sut; + private AisMessageType1Through3Data? data; + + [Given("a new AisMessageType1Through3 record with the following properties:")] + public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) + { + PositionData position = new() + { + Latitude = Convert.ToInt32(table.Rows[0]["Position_Latitude"]), + Longitude = Convert.ToInt32(table.Rows[0]["Position_Longitude"]) + }; + + this.data = table.CreateInstance(); + this.data.Position = position; + } + + [When("the AisMessageType1Through3 is created")] + public void WhenTheAisMessageTypeIsCreated() + { + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + this.sut = new AisMessageType1Through3( + CourseOverGround: this.data.CourseOverGround, + ManoeuvreIndicator: Enum.Parse(this.data.ManoeuvreIndicator ?? throw new InvalidOperationException()), + MessageType: this.data.MessageType, + Mmsi: this.data.Mmsi, + NavigationStatus: Enum.Parse(this.data.NavigationStatus ?? throw new InvalidOperationException()), + Position: new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0), + PositionAccuracy: this.data.PositionAccuracy, + RadioSlotTimeout: this.data.RadioSlotTimeout, + RadioSubMessage: this.data.RadioSubMessage, + RadioSyncState: Enum.Parse(this.data.RadioSyncState ?? throw new InvalidOperationException()), + RateOfTurn: this.data.RateOfTurn, + RaimFlag: this.data.RaimFlag, + RepeatIndicator: this.data.RepeatIndicator, + SpareBits145: this.data.SpareBits145, + SpeedOverGround: this.data.SpeedOverGround, + TimeStampSecond: this.data.TimeStampSecond, + TrueHeadingDegrees: this.data.TrueHeadingDegrees + ); + } + + [Then("the AisMessageType1Through3 properties should be set correctly")] + public void ThenThePropertiesShouldBeSetCorrectly() + { + this.sut.ShouldNotBeNull(); + this.sut.CourseOverGround.ShouldBe(123.45f); + this.sut.ManoeuvreIndicator.ShouldBe(ManoeuvreIndicator.NotAvailable); + this.sut.MessageType.ShouldBe(1); + this.sut.Mmsi.ShouldBe(12345u); + this.sut.NavigationStatus.ShouldBe(NavigationStatus.UnderwayUsingEngine); + this.sut.Position.ShouldBe(new Position(1.0, 2.0)); + this.sut.PositionAccuracy.ShouldBeTrue(); + this.sut.RadioSlotTimeout.ShouldBe(1u); + this.sut.RadioSubMessage.ShouldBe(2u); + this.sut.RadioSyncState.ShouldBe(RadioSyncState.UtcDirect); + this.sut.RateOfTurn.ShouldBe(1); + this.sut.RaimFlag.ShouldBeTrue(); + this.sut.RepeatIndicator.ShouldBe(3u); + this.sut.SpareBits145.ShouldBe(4u); + this.sut.SpeedOverGround.ShouldBe(12.34f); + this.sut.TimeStampSecond.ShouldBe(56u); + this.sut.TrueHeadingDegrees.ShouldBe(78u); + } + + private class AisMessageType1Through3Data + { + public float? CourseOverGround { get; set; } + public string? ManoeuvreIndicator { get; set; } + public int MessageType { get; set; } + public uint Mmsi { get; set; } + public string? NavigationStatus { get; set; } + public PositionData? Position { get; set; } + public bool PositionAccuracy { get; set; } + public uint RadioSlotTimeout { get; set; } + public uint RadioSubMessage { get; set; } + public string? RadioSyncState { get; set; } + public int RateOfTurn { get; set; } + public bool RaimFlag { get; set; } + public uint RepeatIndicator { get; set; } + public uint SpareBits145 { get; set; } + public float? SpeedOverGround { get; set; } + public uint TimeStampSecond { get; set; } + public uint TrueHeadingDegrees { get; set; } + } + + private class PositionData + { + public int Latitude { get; set; } + public int Longitude { get; set; } + } +} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs new file mode 100644 index 0000000..a87c466 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs @@ -0,0 +1,52 @@ +namespace Ais.Net.Models.Specs; + +using System; +using Reqnroll; +using Shouldly; + +[Binding] +public class AisMessageType24Part0StepDefinitions +{ + private AisMessageType24Part0? sut; + private AisMessageType24Part0Data? data; + + [Given("a new AisMessageType24Part0 record with the following properties:")] + public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) + { + this.data = table.CreateInstance(); + } + + [When("the AisMessageType24Part0 is created")] + public void WhenTheAisMessageTypeIsCreated() + { + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + this.sut = new AisMessageType24Part0( + Mmsi: this.data.Mmsi, + PartNumber: this.data.PartNumber, + RepeatIndicator: this.data.RepeatIndicator, + Spare160: this.data.Spare160 + ); + } + + [Then("the AisMessageType24Part0 properties should be set correctly")] + public void ThenThePropertiesShouldBeSetCorrectly() + { + this.sut.ShouldNotBeNull(); + this.sut.Mmsi.ShouldBe(12345u); + this.sut.PartNumber.ShouldBe(0u); + this.sut.RepeatIndicator.ShouldBe(3u); + this.sut.Spare160.ShouldBe(1u); + } + + private class AisMessageType24Part0Data + { + public uint Mmsi { get; set; } + public uint PartNumber { get; set; } + public uint RepeatIndicator { get; set; } + public uint Spare160 { get; set; } + } +} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs new file mode 100644 index 0000000..5671333 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs @@ -0,0 +1,86 @@ +namespace Ais.Net.Models.Specs; + +using Abstractions; +using System; +using Reqnroll; +using Shouldly; + +[Binding] +public class AisMessageType24Part1StepDefinitions +{ + private AisMessageType24Part1? sut; + private AisMessageType24Part1Data? data; + + [Given("a new AisMessageType24Part1 record with the following properties:")] + public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) + { + this.data = table.CreateInstance(); + } + + [When("the AisMessageType24Part1 is created")] + public void WhenTheAisMessageTypeIsCreated() + { + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + this.sut = new AisMessageType24Part1( + CallSign: this.data.CallSign ?? throw new InvalidOperationException(), + DimensionToBow: this.data.DimensionToBow, + DimensionToPort: this.data.DimensionToPort, + DimensionToStarboard: this.data.DimensionToStarboard, + DimensionToStern: this.data.DimensionToStern, + Mmsi: this.data.Mmsi, + MothershipMmsi: this.data.MothershipMmsi, + PartNumber: this.data.PartNumber, + RepeatIndicator: this.data.RepeatIndicator, + SerialNumber: this.data.SerialNumber, + Spare162: this.data.Spare162, + ShipType: (ShipType)this.data.ShipType, + UnitModelCode: this.data.UnitModelCode, + VendorIdRev3: this.data.VendorIdRev3 ?? throw new InvalidOperationException(), + VendorIdRev4: this.data.VendorIdRev4 ?? throw new InvalidOperationException() + ); + } + + [Then("the AisMessageType24Part1 properties should be set correctly")] + public void ThenThePropertiesShouldBeSetCorrectly() + { + this.sut.ShouldNotBeNull(); + this.sut.CallSign.ShouldBe("CALL"); + this.sut.DimensionToBow.ShouldBe(1u); + this.sut.DimensionToPort.ShouldBe(2u); + this.sut.DimensionToStarboard.ShouldBe(3u); + this.sut.DimensionToStern.ShouldBe(4u); + this.sut.Mmsi.ShouldBe(12345u); + this.sut.MothershipMmsi.ShouldBe(54321u); + this.sut.PartNumber.ShouldBe(1u); + this.sut.RepeatIndicator.ShouldBe(3u); + this.sut.SerialNumber.ShouldBe(123u); + this.sut.Spare162.ShouldBe(1u); + ((int)this.sut.ShipType).ShouldBe(60); + this.sut.UnitModelCode.ShouldBe(1u); + this.sut.VendorIdRev3.ShouldBe("VEN"); + this.sut.VendorIdRev4.ShouldBe("DOR"); + } + + private class AisMessageType24Part1Data + { + public string? CallSign { get; set; } + public uint DimensionToBow { get; set; } + public uint DimensionToPort { get; set; } + public uint DimensionToStarboard { get; set; } + public uint DimensionToStern { get; set; } + public uint Mmsi { get; set; } + public uint MothershipMmsi { get; set; } + public uint PartNumber { get; set; } + public uint RepeatIndicator { get; set; } + public uint SerialNumber { get; set; } + public uint Spare162 { get; set; } + public int ShipType { get; set; } + public uint UnitModelCode { get; set; } + public string? VendorIdRev3 { get; set; } + public string? VendorIdRev4 { get; set; } + } +} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs new file mode 100644 index 0000000..77e202e --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs @@ -0,0 +1,82 @@ +namespace Ais.Net.Models.Specs; + +using Abstractions; +using System; + +using Reqnroll; +using Shouldly; + +[Binding] +public class AisMessageType27StepDefinitions +{ + private AisMessageType27? sut; + private AisMessageType27Data? data; + + [Given("a new AisMessageType27 record with the following properties:")] + public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) + { + PositionData position = new() + { + Latitude = Convert.ToInt32(table.Rows[0]["Position_Latitude"]), + Longitude = Convert.ToInt32(table.Rows[0]["Position_Longitude"]) + }; + + this.data = table.CreateInstance(); + this.data.Position = position; + } + + [When("the AisMessageType27 is created")] + public void WhenTheAisMessageTypeIsCreated() + { + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + this.sut = new AisMessageType27( + CourseOverGround: this.data.CourseOverGround, + GnssPositionStatus: this.data.GnssPositionStatus, + Mmsi: this.data.Mmsi, + NavigationStatus: Enum.Parse(this.data.NavigationStatus ?? throw new InvalidOperationException()), + Position: new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0), + PositionAccuracy: this.data.PositionAccuracy, + RaimFlag: this.data.RaimFlag, + RepeatIndicator: this.data.RepeatIndicator, + SpeedOverGround: this.data.SpeedOverGround + ); + } + + [Then("the AisMessageType27 properties should be set correctly")] + public void ThenThePropertiesShouldBeSetCorrectly() + { + this.sut.ShouldNotBeNull(); + this.sut.CourseOverGround.ShouldBe(123.45f); + this.sut.GnssPositionStatus.ShouldBeTrue(); + this.sut.Mmsi.ShouldBe(12345u); + this.sut.NavigationStatus.ShouldBe(NavigationStatus.UnderwayUsingEngine); + this.sut.Position.ShouldBe(new Position(1.0, 2.0)); + this.sut.PositionAccuracy.ShouldBeTrue(); + this.sut.RaimFlag.ShouldBeTrue(); + this.sut.RepeatIndicator.ShouldBe(3u); + this.sut.SpeedOverGround.ShouldBe(12.34f); + } + + private class AisMessageType27Data + { + public float? CourseOverGround { get; set; } + public bool GnssPositionStatus { get; set; } + public uint Mmsi { get; set; } + public string? NavigationStatus { get; set; } + public PositionData? Position { get; set; } + public bool PositionAccuracy { get; set; } + public bool RaimFlag { get; set; } + public uint RepeatIndicator { get; set; } + public float? SpeedOverGround { get; set; } + } + + private class PositionData + { + public int Latitude { get; set; } + public int Longitude { get; set; } + } +} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs new file mode 100644 index 0000000..ffd48f5 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs @@ -0,0 +1,102 @@ +namespace Ais.Net.Models.Specs; + +using Abstractions; +using System; + +using Reqnroll; +using Shouldly; + +[Binding] +public class AisMessageType5StepDefinitions +{ + private AisMessageType5? sut; + private AisMessageType5Data? data; + + [Given("a new AisMessageType5 record with the following properties:")] + public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) + { + this.data = table.CreateInstance(); + } + + [When("the AisMessageType5 is created")] + public void WhenTheAisMessageTypeIsCreated() + { + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + this.sut = new AisMessageType5( + AisVersion: this.data.AisVersion, + CallSign: this.data.CallSign ?? throw new InvalidOperationException(), + Destination: this.data.Destination ?? throw new InvalidOperationException(), + DimensionToBow: this.data.DimensionToBow, + DimensionToPort: this.data.DimensionToPort, + DimensionToStarboard: this.data.DimensionToStarboard, + DimensionToStern: this.data.DimensionToStern, + Draught10thMetres: this.data.Draught10thMetres, + EtaDay: this.data.EtaDay, + EtaHour: this.data.EtaHour, + EtaMinute: this.data.EtaMinute, + EtaMonth: this.data.EtaMonth, + IsDteNotReady: this.data.IsDteNotReady, + ImoNumber: this.data.ImoNumber, + Mmsi: this.data.Mmsi, + PositionFixType: Enum.Parse(this.data.PositionFixType ?? throw new InvalidOperationException()), + RepeatIndicator: this.data.RepeatIndicator, + ShipType: (ShipType)this.data.ShipType, + Spare423: this.data.Spare423, + VesselName: this.data.VesselName ?? throw new InvalidOperationException() + ); + } + + [Then("the AisMessageType5 properties should be set correctly")] + public void ThenThePropertiesShouldBeSetCorrectly() + { + this.sut.ShouldNotBeNull(); + this.sut.AisVersion.ShouldBe(1u); + this.sut.CallSign.ShouldBe("CALL"); + this.sut.Destination.ShouldBe("DEST"); + this.sut.DimensionToBow.ShouldBe(1u); + this.sut.DimensionToPort.ShouldBe(2u); + this.sut.DimensionToStarboard.ShouldBe(3u); + this.sut.DimensionToStern.ShouldBe(4u); + this.sut.Draught10thMetres.ShouldBe(5u); + this.sut.EtaDay.ShouldBe(6u); + this.sut.EtaHour.ShouldBe(7u); + this.sut.EtaMinute.ShouldBe(8u); + this.sut.EtaMonth.ShouldBe(9u); + this.sut.IsDteNotReady.ShouldBeTrue(); + this.sut.ImoNumber.ShouldBe(123u); + this.sut.Mmsi.ShouldBe(12345u); + this.sut.PositionFixType.ShouldBe(EpfdFixType.Gps); + this.sut.RepeatIndicator.ShouldBe(3u); + ((int)this.sut.ShipType).ShouldBe(60); + this.sut.Spare423.ShouldBe(1u); + this.sut.VesselName.ShouldBe("VESSEL"); + } + + private class AisMessageType5Data + { + public uint AisVersion { get; set; } + public string? CallSign { get; set; } + public string? Destination { get; set; } + public uint DimensionToBow { get; set; } + public uint DimensionToPort { get; set; } + public uint DimensionToStarboard { get; set; } + public uint DimensionToStern { get; set; } + public uint Draught10thMetres { get; set; } + public uint EtaDay { get; set; } + public uint EtaHour { get; set; } + public uint EtaMinute { get; set; } + public uint EtaMonth { get; set; } + public bool IsDteNotReady { get; set; } + public uint ImoNumber { get; set; } + public uint Mmsi { get; set; } + public string? PositionFixType { get; set; } + public uint RepeatIndicator { get; set; } + public int ShipType { get; set; } + public uint Spare423 { get; set; } + public string? VesselName { get; set; } + } +} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature new file mode 100644 index 0000000..3cde8ac --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature @@ -0,0 +1,8 @@ +Feature: AisMessageType19 + +Scenario: Create AisMessageType19 record + Given a new AisMessageType19 record with the following properties: + | CourseOverGround | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | IsAssigned | IsDteNotReady | Mmsi | Position_Latitude | Position_Longitude | PositionAccuracy | PositionFixType | RaimFlag | RegionalReserved139 | RegionalReserved38 | RepeatIndicator | ShipName | ShipType | Spare308 | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | + | 123.45 | 1 | 2 | 3 | 4 | true | true | 12345 | 1 | 2 | true | Gps | true | 1 | 2 | 3 | SHIP | 60 | 1 | 12.34 | 56 | 78 | + When the AisMessageType19 is created + Then the AisMessageType19 properties should be set correctly \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs new file mode 100644 index 0000000..e5600cd --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs @@ -0,0 +1,183 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by Reqnroll (https://www.reqnroll.net/). +// Reqnroll Version:2.0.0.0 +// Reqnroll Generator Version:2.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +using Reqnroll; +namespace Ais.Net.Models.Specs.Features +{ + + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("AisMessageType19")] + [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] + public partial class AisMessageType19Feature + { + + private global::Reqnroll.ITestRunner testRunner; + + private static string[] featureTags = ((string[])(null)); + + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType19", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + +#line 1 "AisMessageType19.feature" +#line hidden + + [NUnit.Framework.OneTimeSetUpAttribute()] + public static async global::System.Threading.Tasks.Task FeatureSetupAsync() + { + } + + [NUnit.Framework.OneTimeTearDownAttribute()] + public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() + { + } + + [NUnit.Framework.SetUpAttribute()] + public async global::System.Threading.Tasks.Task TestInitializeAsync() + { + testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); + try + { + if (((testRunner.FeatureContext != null) + && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) + { + await testRunner.OnFeatureEndAsync(); + } + } + finally + { + if (((testRunner.FeatureContext != null) + && testRunner.FeatureContext.BeforeFeatureHookFailed)) + { + throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); + } + if ((testRunner.FeatureContext == null)) + { + await testRunner.OnFeatureStartAsync(featureInfo); + } + } + } + + [NUnit.Framework.TearDownAttribute()] + public async global::System.Threading.Tasks.Task TestTearDownAsync() + { + if ((testRunner == null)) + { + return; + } + try + { + await testRunner.OnScenarioEndAsync(); + } + finally + { + global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); + testRunner = null; + } + } + + public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); + } + + public async global::System.Threading.Tasks.Task ScenarioStartAsync() + { + await testRunner.OnScenarioStartAsync(); + } + + public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() + { + await testRunner.CollectScenarioErrorsAsync(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Create AisMessageType19 record")] + public async global::System.Threading.Tasks.Task CreateAisMessageType19Record() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType19 record", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 3 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + await this.ScenarioStartAsync(); + global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { + "CourseOverGround", + "DimensionToBow", + "DimensionToPort", + "DimensionToStarboard", + "DimensionToStern", + "IsAssigned", + "IsDteNotReady", + "Mmsi", + "Position_Latitude", + "Position_Longitude", + "PositionAccuracy", + "PositionFixType", + "RaimFlag", + "RegionalReserved139", + "RegionalReserved38", + "RepeatIndicator", + "ShipName", + "ShipType", + "Spare308", + "SpeedOverGround", + "TimeStampSecond", + "TrueHeadingDegrees"}); + table2.AddRow(new string[] { + "123.45", + "1", + "2", + "3", + "4", + "true", + "true", + "12345", + "1", + "2", + "true", + "Gps", + "true", + "1", + "2", + "3", + "SHIP", + "60", + "1", + "12.34", + "56", + "78"}); +#line 4 + await testRunner.GivenAsync("a new AisMessageType19 record with the following properties:", ((string)(null)), table2, "Given "); +#line hidden +#line 7 + await testRunner.WhenAsync("the AisMessageType19 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); +#line hidden +#line 8 + await testRunner.ThenAsync("the AisMessageType19 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); +#line hidden + } + await this.ScenarioCleanupAsync(); + } + } +} +#pragma warning restore +#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature new file mode 100644 index 0000000..4d33927 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature @@ -0,0 +1,8 @@ +Feature: AisMessageType1Through3 + +Scenario: Create AisMessageType1Through3 record + Given a new AisMessageType1Through3 record with the following properties: + | CourseOverGround | ManoeuvreIndicator | MessageType | Mmsi | NavigationStatus | Position_Latitude | Position_Longitude | PositionAccuracy | RadioSlotTimeout | RadioSubMessage | RadioSyncState | RateOfTurn | RaimFlag | RepeatIndicator | SpareBits145 | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | + | 123.45 | NotAvailable | 1 | 12345 | UnderwayUsingEngine | 1 | 2 | true | 1 | 2 | UtcDirect | 1 | true | 3 | 4 | 12.34 | 56 | 78 | + When the AisMessageType1Through3 is created + Then the AisMessageType1Through3 properties should be set correctly \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs new file mode 100644 index 0000000..bc1090f --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs @@ -0,0 +1,175 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by Reqnroll (https://www.reqnroll.net/). +// Reqnroll Version:2.0.0.0 +// Reqnroll Generator Version:2.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +using Reqnroll; +namespace Ais.Net.Models.Specs.Features +{ + + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("AisMessageType1Through3")] + [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] + public partial class AisMessageType1Through3Feature + { + + private global::Reqnroll.ITestRunner testRunner; + + private static string[] featureTags = ((string[])(null)); + + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType1Through3", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + +#line 1 "AisMessageType1Through3.feature" +#line hidden + + [NUnit.Framework.OneTimeSetUpAttribute()] + public static async global::System.Threading.Tasks.Task FeatureSetupAsync() + { + } + + [NUnit.Framework.OneTimeTearDownAttribute()] + public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() + { + } + + [NUnit.Framework.SetUpAttribute()] + public async global::System.Threading.Tasks.Task TestInitializeAsync() + { + testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); + try + { + if (((testRunner.FeatureContext != null) + && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) + { + await testRunner.OnFeatureEndAsync(); + } + } + finally + { + if (((testRunner.FeatureContext != null) + && testRunner.FeatureContext.BeforeFeatureHookFailed)) + { + throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); + } + if ((testRunner.FeatureContext == null)) + { + await testRunner.OnFeatureStartAsync(featureInfo); + } + } + } + + [NUnit.Framework.TearDownAttribute()] + public async global::System.Threading.Tasks.Task TestTearDownAsync() + { + if ((testRunner == null)) + { + return; + } + try + { + await testRunner.OnScenarioEndAsync(); + } + finally + { + global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); + testRunner = null; + } + } + + public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); + } + + public async global::System.Threading.Tasks.Task ScenarioStartAsync() + { + await testRunner.OnScenarioStartAsync(); + } + + public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() + { + await testRunner.CollectScenarioErrorsAsync(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Create AisMessageType1Through3 record")] + public async global::System.Threading.Tasks.Task CreateAisMessageType1Through3Record() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType1Through3 record", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 3 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + await this.ScenarioStartAsync(); + global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { + "CourseOverGround", + "ManoeuvreIndicator", + "MessageType", + "Mmsi", + "NavigationStatus", + "Position_Latitude", + "Position_Longitude", + "PositionAccuracy", + "RadioSlotTimeout", + "RadioSubMessage", + "RadioSyncState", + "RateOfTurn", + "RaimFlag", + "RepeatIndicator", + "SpareBits145", + "SpeedOverGround", + "TimeStampSecond", + "TrueHeadingDegrees"}); + table3.AddRow(new string[] { + "123.45", + "NotAvailable", + "1", + "12345", + "UnderwayUsingEngine", + "1", + "2", + "true", + "1", + "2", + "UtcDirect", + "1", + "true", + "3", + "4", + "12.34", + "56", + "78"}); +#line 4 + await testRunner.GivenAsync("a new AisMessageType1Through3 record with the following properties:", ((string)(null)), table3, "Given "); +#line hidden +#line 7 + await testRunner.WhenAsync("the AisMessageType1Through3 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); +#line hidden +#line 8 + await testRunner.ThenAsync("the AisMessageType1Through3 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); +#line hidden + } + await this.ScenarioCleanupAsync(); + } + } +} +#pragma warning restore +#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature new file mode 100644 index 0000000..e4acf4c --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature @@ -0,0 +1,8 @@ +Feature: AisMessageType24Part0 + +Scenario: Create AisMessageType24Part0 record + Given a new AisMessageType24Part0 record with the following properties: + | Mmsi | PartNumber | RepeatIndicator | Spare160 | + | 12345 | 0 | 3 | 1 | + When the AisMessageType24Part0 is created + Then the AisMessageType24Part0 properties should be set correctly \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs new file mode 100644 index 0000000..25abb75 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs @@ -0,0 +1,147 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by Reqnroll (https://www.reqnroll.net/). +// Reqnroll Version:2.0.0.0 +// Reqnroll Generator Version:2.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +using Reqnroll; +namespace Ais.Net.Models.Specs.Features +{ + + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("AisMessageType24Part0")] + [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] + public partial class AisMessageType24Part0Feature + { + + private global::Reqnroll.ITestRunner testRunner; + + private static string[] featureTags = ((string[])(null)); + + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType24Part0", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + +#line 1 "AisMessageType24Part0.feature" +#line hidden + + [NUnit.Framework.OneTimeSetUpAttribute()] + public static async global::System.Threading.Tasks.Task FeatureSetupAsync() + { + } + + [NUnit.Framework.OneTimeTearDownAttribute()] + public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() + { + } + + [NUnit.Framework.SetUpAttribute()] + public async global::System.Threading.Tasks.Task TestInitializeAsync() + { + testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); + try + { + if (((testRunner.FeatureContext != null) + && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) + { + await testRunner.OnFeatureEndAsync(); + } + } + finally + { + if (((testRunner.FeatureContext != null) + && testRunner.FeatureContext.BeforeFeatureHookFailed)) + { + throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); + } + if ((testRunner.FeatureContext == null)) + { + await testRunner.OnFeatureStartAsync(featureInfo); + } + } + } + + [NUnit.Framework.TearDownAttribute()] + public async global::System.Threading.Tasks.Task TestTearDownAsync() + { + if ((testRunner == null)) + { + return; + } + try + { + await testRunner.OnScenarioEndAsync(); + } + finally + { + global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); + testRunner = null; + } + } + + public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); + } + + public async global::System.Threading.Tasks.Task ScenarioStartAsync() + { + await testRunner.OnScenarioStartAsync(); + } + + public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() + { + await testRunner.CollectScenarioErrorsAsync(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Create AisMessageType24Part0 record")] + public async global::System.Threading.Tasks.Task CreateAisMessageType24Part0Record() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType24Part0 record", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 3 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + await this.ScenarioStartAsync(); + global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { + "Mmsi", + "PartNumber", + "RepeatIndicator", + "Spare160"}); + table4.AddRow(new string[] { + "12345", + "0", + "3", + "1"}); +#line 4 + await testRunner.GivenAsync("a new AisMessageType24Part0 record with the following properties:", ((string)(null)), table4, "Given "); +#line hidden +#line 7 + await testRunner.WhenAsync("the AisMessageType24Part0 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); +#line hidden +#line 8 + await testRunner.ThenAsync("the AisMessageType24Part0 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); +#line hidden + } + await this.ScenarioCleanupAsync(); + } + } +} +#pragma warning restore +#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature new file mode 100644 index 0000000..0827626 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature @@ -0,0 +1,8 @@ +Feature: AisMessageType24Part1 + +Scenario: Create AisMessageType24Part1 record + Given a new AisMessageType24Part1 record with the following properties: + | CallSign | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | Mmsi | MothershipMmsi | PartNumber | RepeatIndicator | SerialNumber | Spare162 | ShipType | UnitModelCode | VendorIdRev3 | VendorIdRev4 | + | CALL | 1 | 2 | 3 | 4 | 12345 | 54321 | 1 | 3 | 123 | 1 | 60 | 1 | VEN | DOR | + When the AisMessageType24Part1 is created + Then the AisMessageType24Part1 properties should be set correctly \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs new file mode 100644 index 0000000..e78f712 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs @@ -0,0 +1,169 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by Reqnroll (https://www.reqnroll.net/). +// Reqnroll Version:2.0.0.0 +// Reqnroll Generator Version:2.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +using Reqnroll; +namespace Ais.Net.Models.Specs.Features +{ + + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("AisMessageType24Part1")] + [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] + public partial class AisMessageType24Part1Feature + { + + private global::Reqnroll.ITestRunner testRunner; + + private static string[] featureTags = ((string[])(null)); + + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType24Part1", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + +#line 1 "AisMessageType24Part1.feature" +#line hidden + + [NUnit.Framework.OneTimeSetUpAttribute()] + public static async global::System.Threading.Tasks.Task FeatureSetupAsync() + { + } + + [NUnit.Framework.OneTimeTearDownAttribute()] + public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() + { + } + + [NUnit.Framework.SetUpAttribute()] + public async global::System.Threading.Tasks.Task TestInitializeAsync() + { + testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); + try + { + if (((testRunner.FeatureContext != null) + && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) + { + await testRunner.OnFeatureEndAsync(); + } + } + finally + { + if (((testRunner.FeatureContext != null) + && testRunner.FeatureContext.BeforeFeatureHookFailed)) + { + throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); + } + if ((testRunner.FeatureContext == null)) + { + await testRunner.OnFeatureStartAsync(featureInfo); + } + } + } + + [NUnit.Framework.TearDownAttribute()] + public async global::System.Threading.Tasks.Task TestTearDownAsync() + { + if ((testRunner == null)) + { + return; + } + try + { + await testRunner.OnScenarioEndAsync(); + } + finally + { + global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); + testRunner = null; + } + } + + public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); + } + + public async global::System.Threading.Tasks.Task ScenarioStartAsync() + { + await testRunner.OnScenarioStartAsync(); + } + + public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() + { + await testRunner.CollectScenarioErrorsAsync(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Create AisMessageType24Part1 record")] + public async global::System.Threading.Tasks.Task CreateAisMessageType24Part1Record() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType24Part1 record", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 3 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + await this.ScenarioStartAsync(); + global::Reqnroll.Table table5 = new global::Reqnroll.Table(new string[] { + "CallSign", + "DimensionToBow", + "DimensionToPort", + "DimensionToStarboard", + "DimensionToStern", + "Mmsi", + "MothershipMmsi", + "PartNumber", + "RepeatIndicator", + "SerialNumber", + "Spare162", + "ShipType", + "UnitModelCode", + "VendorIdRev3", + "VendorIdRev4"}); + table5.AddRow(new string[] { + "CALL", + "1", + "2", + "3", + "4", + "12345", + "54321", + "1", + "3", + "123", + "1", + "60", + "1", + "VEN", + "DOR"}); +#line 4 + await testRunner.GivenAsync("a new AisMessageType24Part1 record with the following properties:", ((string)(null)), table5, "Given "); +#line hidden +#line 7 + await testRunner.WhenAsync("the AisMessageType24Part1 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); +#line hidden +#line 8 + await testRunner.ThenAsync("the AisMessageType24Part1 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); +#line hidden + } + await this.ScenarioCleanupAsync(); + } + } +} +#pragma warning restore +#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature new file mode 100644 index 0000000..bab76c0 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature @@ -0,0 +1,8 @@ +Feature: AisMessageType27 + +Scenario: Create AisMessageType27 record + Given a new AisMessageType27 record with the following properties: + | CourseOverGround | GnssPositionStatus | Mmsi | NavigationStatus | Position_Latitude | Position_Longitude | PositionAccuracy | RaimFlag | RepeatIndicator | SpeedOverGround | + | 123.45 | true | 12345 | UnderwayUsingEngine | 1 | 2 | true | true | 3 | 12.34 | + When the AisMessageType27 is created + Then the AisMessageType27 properties should be set correctly \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs new file mode 100644 index 0000000..67c6cc2 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs @@ -0,0 +1,159 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by Reqnroll (https://www.reqnroll.net/). +// Reqnroll Version:2.0.0.0 +// Reqnroll Generator Version:2.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +using Reqnroll; +namespace Ais.Net.Models.Specs.Features +{ + + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("AisMessageType27")] + [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] + public partial class AisMessageType27Feature + { + + private global::Reqnroll.ITestRunner testRunner; + + private static string[] featureTags = ((string[])(null)); + + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType27", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + +#line 1 "AisMessageType27.feature" +#line hidden + + [NUnit.Framework.OneTimeSetUpAttribute()] + public static async global::System.Threading.Tasks.Task FeatureSetupAsync() + { + } + + [NUnit.Framework.OneTimeTearDownAttribute()] + public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() + { + } + + [NUnit.Framework.SetUpAttribute()] + public async global::System.Threading.Tasks.Task TestInitializeAsync() + { + testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); + try + { + if (((testRunner.FeatureContext != null) + && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) + { + await testRunner.OnFeatureEndAsync(); + } + } + finally + { + if (((testRunner.FeatureContext != null) + && testRunner.FeatureContext.BeforeFeatureHookFailed)) + { + throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); + } + if ((testRunner.FeatureContext == null)) + { + await testRunner.OnFeatureStartAsync(featureInfo); + } + } + } + + [NUnit.Framework.TearDownAttribute()] + public async global::System.Threading.Tasks.Task TestTearDownAsync() + { + if ((testRunner == null)) + { + return; + } + try + { + await testRunner.OnScenarioEndAsync(); + } + finally + { + global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); + testRunner = null; + } + } + + public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); + } + + public async global::System.Threading.Tasks.Task ScenarioStartAsync() + { + await testRunner.OnScenarioStartAsync(); + } + + public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() + { + await testRunner.CollectScenarioErrorsAsync(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Create AisMessageType27 record")] + public async global::System.Threading.Tasks.Task CreateAisMessageType27Record() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType27 record", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 3 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + await this.ScenarioStartAsync(); + global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { + "CourseOverGround", + "GnssPositionStatus", + "Mmsi", + "NavigationStatus", + "Position_Latitude", + "Position_Longitude", + "PositionAccuracy", + "RaimFlag", + "RepeatIndicator", + "SpeedOverGround"}); + table6.AddRow(new string[] { + "123.45", + "true", + "12345", + "UnderwayUsingEngine", + "1", + "2", + "true", + "true", + "3", + "12.34"}); +#line 4 + await testRunner.GivenAsync("a new AisMessageType27 record with the following properties:", ((string)(null)), table6, "Given "); +#line hidden +#line 7 + await testRunner.WhenAsync("the AisMessageType27 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); +#line hidden +#line 8 + await testRunner.ThenAsync("the AisMessageType27 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); +#line hidden + } + await this.ScenarioCleanupAsync(); + } + } +} +#pragma warning restore +#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature new file mode 100644 index 0000000..f090714 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature @@ -0,0 +1,8 @@ +Feature: AisMessageType5 + +Scenario: Create AisMessageType5 record + Given a new AisMessageType5 record with the following properties: + | AisVersion | CallSign | Destination | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | Draught10thMetres | EtaDay | EtaHour | EtaMinute | EtaMonth | IsDteNotReady | ImoNumber | Mmsi | PositionFixType | RepeatIndicator | ShipType | Spare423 | VesselName | + | 1 | CALL | DEST | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | true | 123 | 12345 | Gps | 3 | 60 | 1 | VESSEL | + When the AisMessageType5 is created + Then the AisMessageType5 properties should be set correctly \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs new file mode 100644 index 0000000..9dc2d55 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs @@ -0,0 +1,179 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by Reqnroll (https://www.reqnroll.net/). +// Reqnroll Version:2.0.0.0 +// Reqnroll Generator Version:2.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +#pragma warning disable +using Reqnroll; +namespace Ais.Net.Models.Specs.Features +{ + + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("AisMessageType5")] + [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] + public partial class AisMessageType5Feature + { + + private global::Reqnroll.ITestRunner testRunner; + + private static string[] featureTags = ((string[])(null)); + + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType5", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + +#line 1 "AisMessageType5.feature" +#line hidden + + [NUnit.Framework.OneTimeSetUpAttribute()] + public static async global::System.Threading.Tasks.Task FeatureSetupAsync() + { + } + + [NUnit.Framework.OneTimeTearDownAttribute()] + public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() + { + } + + [NUnit.Framework.SetUpAttribute()] + public async global::System.Threading.Tasks.Task TestInitializeAsync() + { + testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); + try + { + if (((testRunner.FeatureContext != null) + && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) + { + await testRunner.OnFeatureEndAsync(); + } + } + finally + { + if (((testRunner.FeatureContext != null) + && testRunner.FeatureContext.BeforeFeatureHookFailed)) + { + throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); + } + if ((testRunner.FeatureContext == null)) + { + await testRunner.OnFeatureStartAsync(featureInfo); + } + } + } + + [NUnit.Framework.TearDownAttribute()] + public async global::System.Threading.Tasks.Task TestTearDownAsync() + { + if ((testRunner == null)) + { + return; + } + try + { + await testRunner.OnScenarioEndAsync(); + } + finally + { + global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); + testRunner = null; + } + } + + public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioInitialize(scenarioInfo); + testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); + } + + public async global::System.Threading.Tasks.Task ScenarioStartAsync() + { + await testRunner.OnScenarioStartAsync(); + } + + public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() + { + await testRunner.CollectScenarioErrorsAsync(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Create AisMessageType5 record")] + public async global::System.Threading.Tasks.Task CreateAisMessageType5Record() + { + string[] tagsOfScenario = ((string[])(null)); + global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType5 record", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 3 +this.ScenarioInitialize(scenarioInfo); +#line hidden + if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) + { + testRunner.SkipScenario(); + } + else + { + await this.ScenarioStartAsync(); + global::Reqnroll.Table table7 = new global::Reqnroll.Table(new string[] { + "AisVersion", + "CallSign", + "Destination", + "DimensionToBow", + "DimensionToPort", + "DimensionToStarboard", + "DimensionToStern", + "Draught10thMetres", + "EtaDay", + "EtaHour", + "EtaMinute", + "EtaMonth", + "IsDteNotReady", + "ImoNumber", + "Mmsi", + "PositionFixType", + "RepeatIndicator", + "ShipType", + "Spare423", + "VesselName"}); + table7.AddRow(new string[] { + "1", + "CALL", + "DEST", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "true", + "123", + "12345", + "Gps", + "3", + "60", + "1", + "VESSEL"}); +#line 4 + await testRunner.GivenAsync("a new AisMessageType5 record with the following properties:", ((string)(null)), table7, "Given "); +#line hidden +#line 7 + await testRunner.WhenAsync("the AisMessageType5 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); +#line hidden +#line 8 + await testRunner.ThenAsync("the AisMessageType5 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); +#line hidden + } + await this.ScenarioCleanupAsync(); + } + } +} +#pragma warning restore +#endregion From 0bb850983e87ab350270925049103cc2bcdac8a9 Mon Sep 17 00:00:00 2001 From: Howard van Rooijen Date: Wed, 18 Jun 2025 10:39:21 +0100 Subject: [PATCH 04/17] Refactor AIS step definitions and improve structure This commit refactors several AIS message type step definition classes to inherit from a new base class `StepDefinitionBase`, introducing a common `Message` property for better management of created AIS messages. The `PositionData` class is updated to use `double` for `Latitude` and `Longitude` for improved precision. A new `CommonStepDefinitions` class is added for shared background steps, and feature files are renamed for clarity, utilizing parameterized examples. Additionally, a `Hooks` class is introduced to handle pre- and post-scenario logic, enhancing the overall structure and maintainability of the tests. --- .../AisMessageType18StepDefinitions.cs | 60 +++++---- .../AisMessageType19StepDefinitions.cs | 66 +++++----- .../AisMessageType1Through3StepDefinitions.cs | 58 ++++---- .../AisMessageType24Part0StepDefinitions.cs | 24 ++-- .../AisMessageType24Part1StepDefinitions.cs | 47 ++++--- .../AisMessageType27StepDefinitions.cs | 44 ++++--- .../AisMessageType5StepDefinitions.cs | 56 ++++---- .../CommonStepDefinitions.cs | 15 +++ .../Features/AisMessageType18.feature | 15 ++- .../Features/AisMessageType18.feature.cs | 112 +++++++++++----- .../Features/AisMessageType19.feature | 15 ++- .../Features/AisMessageType19.feature.cs | 124 +++++++++++++----- .../Features/AisMessageType1Through3.feature | 18 ++- .../AisMessageType1Through3.feature.cs | 60 ++++++--- .../Features/AisMessageType24Part0.feature | 15 ++- .../Features/AisMessageType24Part0.feature.cs | 47 ++++--- .../Features/AisMessageType24Part1.feature | 15 ++- .../Features/AisMessageType24Part1.feature.cs | 96 ++++++++++---- .../Features/AisMessageType27.feature | 15 ++- .../Features/AisMessageType27.feature.cs | 65 ++++++--- .../Features/AisMessageType5.feature | 15 ++- .../Features/AisMessageType5.feature.cs | 116 +++++++++++----- Solutions/Ais.Net.Models.Specs/Hooks.cs | 20 +++ .../StepDefinitionBase.cs | 9 ++ 24 files changed, 769 insertions(+), 358 deletions(-) create mode 100644 Solutions/Ais.Net.Models.Specs/CommonStepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/Hooks.cs create mode 100644 Solutions/Ais.Net.Models.Specs/StepDefinitionBase.cs diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs index 9d3245d..97a007a 100644 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs @@ -1,15 +1,16 @@ namespace Ais.Net.Models.Specs; -using Abstractions; using System; +using Abstractions; + using Reqnroll; + using Shouldly; [Binding] -public class AisMessageType18StepDefinitions +public class AisMessageType18StepDefinitions : StepDefinitionBase { - private AisMessageType18? sut; private AisMessageType18Data? data; [Given("a new AisMessageType18 record with the following properties:")] @@ -17,8 +18,8 @@ public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable ta { PositionData position = new() { - Latitude = Convert.ToInt32(table.Rows[0]["Position_Latitude"]), - Longitude = Convert.ToInt32(table.Rows[0]["Position_Longitude"]) + Latitude = Convert.ToDouble(table.Rows[0]["Position_Latitude"]), + Longitude = Convert.ToDouble(table.Rows[0]["Position_Longitude"]) }; this.data = table.CreateInstance(); @@ -33,7 +34,7 @@ public void WhenTheAisMessageTypeIsCreated() throw new InvalidOperationException("Data is not set"); } - this.sut = new AisMessageType18( + this.Message = new AisMessageType18( CanAcceptMessage22ChannelAssignment: this.data.CanAcceptMessage22ChannelAssignment, CanSwitchBands: this.data.CanSwitchBands, CourseOverGround: this.data.CourseOverGroundDegrees, @@ -58,25 +59,30 @@ public void WhenTheAisMessageTypeIsCreated() [Then("the properties should be set correctly")] public void ThenThePropertiesShouldBeSetCorrectly() { - this.sut.ShouldNotBeNull(); - this.sut.CanAcceptMessage22ChannelAssignment.ShouldBeTrue(); - this.sut.CanSwitchBands.ShouldBeTrue(); - this.sut.CourseOverGround.ShouldBe(123.45f); - this.sut.CsUnit.ShouldBe(ClassBUnit.Cstdma); - this.sut.HasDisplay.ShouldBeTrue(); - this.sut.IsAssigned.ShouldBeTrue(); - this.sut.IsDscAttached.ShouldBeTrue(); - this.sut.Mmsi.ShouldBe(12345u); - this.sut.Position.ShouldBe(new Position(1.0, 2.0)); - this.sut.PositionAccuracy.ShouldBeTrue(); - this.sut.RadioStatusType.ShouldBe(ClassBRadioStatusType.Itdma); - this.sut.RaimFlag.ShouldBeTrue(); - this.sut.RegionalReserved139.ShouldBe(1); - this.sut.RegionalReserved38.ShouldBe(2); - this.sut.RepeatIndicator.ShouldBe(3u); - this.sut.SpeedOverGround.ShouldBe(12.34f); - this.sut.TimeStampSecond.ShouldBe(56u); - this.sut.TrueHeadingDegrees.ShouldBe(78u); + var sut = (AisMessageType18?)this.Message; + sut.ShouldNotBeNull(); + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + sut.CanAcceptMessage22ChannelAssignment.ShouldBe(this.data.CanAcceptMessage22ChannelAssignment); + sut.CanSwitchBands.ShouldBe(this.data.CanSwitchBands); + sut.CourseOverGround.ShouldBe(this.data.CourseOverGroundDegrees); + sut.CsUnit.ShouldBe(Enum.Parse(this.data.CsUnit ?? throw new InvalidOperationException())); + sut.HasDisplay.ShouldBe(this.data.HasDisplay); + sut.IsAssigned.ShouldBe(this.data.IsAssigned); + sut.IsDscAttached.ShouldBe(this.data.IsDscAttached); + sut.Mmsi.ShouldBe(this.data.Mmsi); + sut.Position.ShouldBe(new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0)); + sut.PositionAccuracy.ShouldBe(this.data.PositionAccuracy); + sut.RadioStatusType.ShouldBe(Enum.Parse(this.data.RadioStatusType ?? throw new InvalidOperationException())); + sut.RaimFlag.ShouldBe(this.data.RaimFlag); + sut.RegionalReserved139.ShouldBe(this.data.RegionalReserved139); + sut.RegionalReserved38.ShouldBe(this.data.RegionalReserved38); + sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); + sut.SpeedOverGround.ShouldBe(this.data.SpeedOverGround); + sut.TimeStampSecond.ShouldBe(this.data.TimeStampSecond); + sut.TrueHeadingDegrees.ShouldBe(this.data.TrueHeadingDegrees); } private class AisMessageType18Data @@ -103,7 +109,7 @@ private class AisMessageType18Data private class PositionData { - public int Latitude { get; set; } - public int Longitude { get; set; } + public double Latitude { get; set; } + public double Longitude { get; set; } } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs index 2931898..ab5298a 100644 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs @@ -1,15 +1,16 @@ namespace Ais.Net.Models.Specs; -using Abstractions; using System; +using Abstractions; + using Reqnroll; + using Shouldly; [Binding] -public class AisMessageType19StepDefinitions +public class AisMessageType19StepDefinitions : StepDefinitionBase { - private AisMessageType19? sut; private AisMessageType19Data? data; [Given("a new AisMessageType19 record with the following properties:")] @@ -17,8 +18,8 @@ public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable ta { PositionData position = new() { - Latitude = Convert.ToInt32(table.Rows[0]["Position_Latitude"]), - Longitude = Convert.ToInt32(table.Rows[0]["Position_Longitude"]) + Latitude = Convert.ToDouble(table.Rows[0]["Position_Latitude"]), + Longitude = Convert.ToDouble(table.Rows[0]["Position_Longitude"]) }; this.data = table.CreateInstance(); @@ -33,7 +34,7 @@ public void WhenTheAisMessageTypeIsCreated() throw new InvalidOperationException("Data is not set"); } - this.sut = new AisMessageType19( + this.Message = new AisMessageType19( CourseOverGround: this.data.CourseOverGround, DimensionToBow: this.data.DimensionToBow, DimensionToPort: this.data.DimensionToPort, @@ -61,28 +62,33 @@ public void WhenTheAisMessageTypeIsCreated() [Then("the AisMessageType19 properties should be set correctly")] public void ThenThePropertiesShouldBeSetCorrectly() { - this.sut.ShouldNotBeNull(); - this.sut.CourseOverGround.ShouldBe(123.45f); - this.sut.DimensionToBow.ShouldBe(1u); - this.sut.DimensionToPort.ShouldBe(2u); - this.sut.DimensionToStarboard.ShouldBe(3u); - this.sut.DimensionToStern.ShouldBe(4u); - this.sut.IsAssigned.ShouldBeTrue(); - this.sut.IsDteNotReady.ShouldBeTrue(); - this.sut.Mmsi.ShouldBe(12345u); - this.sut.Position.ShouldBe(new Position(1.0, 2.0)); - this.sut.PositionAccuracy.ShouldBeTrue(); - this.sut.PositionFixType.ShouldBe(EpfdFixType.Gps); - this.sut.RaimFlag.ShouldBeTrue(); - this.sut.RegionalReserved139.ShouldBe(1); - this.sut.RegionalReserved38.ShouldBe(2); - this.sut.RepeatIndicator.ShouldBe(3u); - this.sut.ShipName.ShouldBe("SHIP"); - ((int)this.sut.ShipType).ShouldBe(60); - this.sut.Spare308.ShouldBe(1u); - this.sut.SpeedOverGround.ShouldBe(12.34f); - this.sut.TimeStampSecond.ShouldBe(56u); - this.sut.TrueHeadingDegrees.ShouldBe(78u); + var sut = (AisMessageType19?)this.Message; + sut.ShouldNotBeNull(); + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + sut.CourseOverGround.ShouldBe(this.data.CourseOverGround); + sut.DimensionToBow.ShouldBe(this.data.DimensionToBow); + sut.DimensionToPort.ShouldBe(this.data.DimensionToPort); + sut.DimensionToStarboard.ShouldBe(this.data.DimensionToStarboard); + sut.DimensionToStern.ShouldBe(this.data.DimensionToStern); + sut.IsAssigned.ShouldBe(this.data.IsAssigned); + sut.IsDteNotReady.ShouldBe(this.data.IsDteNotReady); + sut.Mmsi.ShouldBe(this.data.Mmsi); + sut.Position.ShouldBe(new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0)); + sut.PositionAccuracy.ShouldBe(this.data.PositionAccuracy); + sut.PositionFixType.ShouldBe(Enum.Parse(this.data.PositionFixType ?? throw new InvalidOperationException())); + sut.RaimFlag.ShouldBe(this.data.RaimFlag); + sut.RegionalReserved139.ShouldBe(this.data.RegionalReserved139); + sut.RegionalReserved38.ShouldBe(this.data.RegionalReserved38); + sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); + sut.ShipName.ShouldBe(this.data.ShipName); + ((int)sut.ShipType).ShouldBe(this.data.ShipType); + sut.Spare308.ShouldBe(this.data.Spare308); + sut.SpeedOverGround.ShouldBe(this.data.SpeedOverGround); + sut.TimeStampSecond.ShouldBe(this.data.TimeStampSecond); + sut.TrueHeadingDegrees.ShouldBe(this.data.TrueHeadingDegrees); } private class AisMessageType19Data @@ -112,7 +118,7 @@ private class AisMessageType19Data private class PositionData { - public int Latitude { get; set; } - public int Longitude { get; set; } + public double Latitude { get; set; } + public double Longitude { get; set; } } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs index 0a8f2aa..fb2e9b4 100644 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs @@ -1,15 +1,16 @@ namespace Ais.Net.Models.Specs; -using Abstractions; using System; +using Abstractions; + using Reqnroll; + using Shouldly; [Binding] -public class AisMessageType1Through3StepDefinitions +public class AisMessageType1Through3StepDefinitions : StepDefinitionBase { - private AisMessageType1Through3? sut; private AisMessageType1Through3Data? data; [Given("a new AisMessageType1Through3 record with the following properties:")] @@ -17,8 +18,8 @@ public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable ta { PositionData position = new() { - Latitude = Convert.ToInt32(table.Rows[0]["Position_Latitude"]), - Longitude = Convert.ToInt32(table.Rows[0]["Position_Longitude"]) + Latitude = Convert.ToDouble(table.Rows[0]["Position_Latitude"]), + Longitude = Convert.ToDouble(table.Rows[0]["Position_Longitude"]) }; this.data = table.CreateInstance(); @@ -33,7 +34,7 @@ public void WhenTheAisMessageTypeIsCreated() throw new InvalidOperationException("Data is not set"); } - this.sut = new AisMessageType1Through3( + this.Message = new AisMessageType1Through3( CourseOverGround: this.data.CourseOverGround, ManoeuvreIndicator: Enum.Parse(this.data.ManoeuvreIndicator ?? throw new InvalidOperationException()), MessageType: this.data.MessageType, @@ -57,24 +58,29 @@ public void WhenTheAisMessageTypeIsCreated() [Then("the AisMessageType1Through3 properties should be set correctly")] public void ThenThePropertiesShouldBeSetCorrectly() { - this.sut.ShouldNotBeNull(); - this.sut.CourseOverGround.ShouldBe(123.45f); - this.sut.ManoeuvreIndicator.ShouldBe(ManoeuvreIndicator.NotAvailable); - this.sut.MessageType.ShouldBe(1); - this.sut.Mmsi.ShouldBe(12345u); - this.sut.NavigationStatus.ShouldBe(NavigationStatus.UnderwayUsingEngine); - this.sut.Position.ShouldBe(new Position(1.0, 2.0)); - this.sut.PositionAccuracy.ShouldBeTrue(); - this.sut.RadioSlotTimeout.ShouldBe(1u); - this.sut.RadioSubMessage.ShouldBe(2u); - this.sut.RadioSyncState.ShouldBe(RadioSyncState.UtcDirect); - this.sut.RateOfTurn.ShouldBe(1); - this.sut.RaimFlag.ShouldBeTrue(); - this.sut.RepeatIndicator.ShouldBe(3u); - this.sut.SpareBits145.ShouldBe(4u); - this.sut.SpeedOverGround.ShouldBe(12.34f); - this.sut.TimeStampSecond.ShouldBe(56u); - this.sut.TrueHeadingDegrees.ShouldBe(78u); + var sut = (AisMessageType1Through3?)this.Message; + sut.ShouldNotBeNull(); + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + sut.CourseOverGround.ShouldBe(this.data.CourseOverGround); + sut.ManoeuvreIndicator.ShouldBe(Enum.Parse(this.data.ManoeuvreIndicator ?? throw new InvalidOperationException())); + sut.MessageType.ShouldBe(this.data.MessageType); + sut.Mmsi.ShouldBe(this.data.Mmsi); + sut.NavigationStatus.ShouldBe(Enum.Parse(this.data.NavigationStatus ?? throw new InvalidOperationException())); + sut.Position.ShouldBe(new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0)); + sut.PositionAccuracy.ShouldBe(this.data.PositionAccuracy); + sut.RadioSlotTimeout.ShouldBe(this.data.RadioSlotTimeout); + sut.RadioSubMessage.ShouldBe(this.data.RadioSubMessage); + sut.RadioSyncState.ShouldBe(Enum.Parse(this.data.RadioSyncState ?? throw new InvalidOperationException())); + sut.RateOfTurn.ShouldBe(this.data.RateOfTurn); + sut.RaimFlag.ShouldBe(this.data.RaimFlag); + sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); + sut.SpareBits145.ShouldBe(this.data.SpareBits145); + sut.SpeedOverGround.ShouldBe(this.data.SpeedOverGround); + sut.TimeStampSecond.ShouldBe(this.data.TimeStampSecond); + sut.TrueHeadingDegrees.ShouldBe(this.data.TrueHeadingDegrees); } private class AisMessageType1Through3Data @@ -100,7 +106,7 @@ private class AisMessageType1Through3Data private class PositionData { - public int Latitude { get; set; } - public int Longitude { get; set; } + public double Latitude { get; set; } + public double Longitude { get; set; } } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs index a87c466..dc72039 100644 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs @@ -1,13 +1,14 @@ namespace Ais.Net.Models.Specs; using System; + using Reqnroll; + using Shouldly; [Binding] -public class AisMessageType24Part0StepDefinitions +public class AisMessageType24Part0StepDefinitions : StepDefinitionBase { - private AisMessageType24Part0? sut; private AisMessageType24Part0Data? data; [Given("a new AisMessageType24Part0 record with the following properties:")] @@ -24,7 +25,7 @@ public void WhenTheAisMessageTypeIsCreated() throw new InvalidOperationException("Data is not set"); } - this.sut = new AisMessageType24Part0( + this.Message = new AisMessageType24Part0( Mmsi: this.data.Mmsi, PartNumber: this.data.PartNumber, RepeatIndicator: this.data.RepeatIndicator, @@ -35,11 +36,18 @@ public void WhenTheAisMessageTypeIsCreated() [Then("the AisMessageType24Part0 properties should be set correctly")] public void ThenThePropertiesShouldBeSetCorrectly() { - this.sut.ShouldNotBeNull(); - this.sut.Mmsi.ShouldBe(12345u); - this.sut.PartNumber.ShouldBe(0u); - this.sut.RepeatIndicator.ShouldBe(3u); - this.sut.Spare160.ShouldBe(1u); + AisMessageType24Part0? sut = this.Message as AisMessageType24Part0; + + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + sut.ShouldNotBeNull(); + sut.Mmsi.ShouldBe(this.data.Mmsi); + sut.PartNumber.ShouldBe(this.data.PartNumber); + sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); + sut.Spare160.ShouldBe(this.data.Spare160); } private class AisMessageType24Part0Data diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs index 5671333..96a2443 100644 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs @@ -1,14 +1,14 @@ namespace Ais.Net.Models.Specs; -using Abstractions; using System; + using Reqnroll; + using Shouldly; [Binding] -public class AisMessageType24Part1StepDefinitions +public class AisMessageType24Part1StepDefinitions : StepDefinitionBase { - private AisMessageType24Part1? sut; private AisMessageType24Part1Data? data; [Given("a new AisMessageType24Part1 record with the following properties:")] @@ -25,7 +25,7 @@ public void WhenTheAisMessageTypeIsCreated() throw new InvalidOperationException("Data is not set"); } - this.sut = new AisMessageType24Part1( + this.Message = new AisMessageType24Part1( CallSign: this.data.CallSign ?? throw new InvalidOperationException(), DimensionToBow: this.data.DimensionToBow, DimensionToPort: this.data.DimensionToPort, @@ -47,22 +47,29 @@ public void WhenTheAisMessageTypeIsCreated() [Then("the AisMessageType24Part1 properties should be set correctly")] public void ThenThePropertiesShouldBeSetCorrectly() { - this.sut.ShouldNotBeNull(); - this.sut.CallSign.ShouldBe("CALL"); - this.sut.DimensionToBow.ShouldBe(1u); - this.sut.DimensionToPort.ShouldBe(2u); - this.sut.DimensionToStarboard.ShouldBe(3u); - this.sut.DimensionToStern.ShouldBe(4u); - this.sut.Mmsi.ShouldBe(12345u); - this.sut.MothershipMmsi.ShouldBe(54321u); - this.sut.PartNumber.ShouldBe(1u); - this.sut.RepeatIndicator.ShouldBe(3u); - this.sut.SerialNumber.ShouldBe(123u); - this.sut.Spare162.ShouldBe(1u); - ((int)this.sut.ShipType).ShouldBe(60); - this.sut.UnitModelCode.ShouldBe(1u); - this.sut.VendorIdRev3.ShouldBe("VEN"); - this.sut.VendorIdRev4.ShouldBe("DOR"); + AisMessageType24Part1? sut = this.Message as AisMessageType24Part1; + + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + sut.ShouldNotBeNull(); + sut.CallSign.ShouldBe(this.data.CallSign); + sut.DimensionToBow.ShouldBe(this.data.DimensionToBow); + sut.DimensionToPort.ShouldBe(this.data.DimensionToPort); + sut.DimensionToStarboard.ShouldBe(this.data.DimensionToStarboard); + sut.DimensionToStern.ShouldBe(this.data.DimensionToStern); + sut.Mmsi.ShouldBe(this.data.Mmsi); + sut.MothershipMmsi.ShouldBe(this.data.MothershipMmsi); + sut.PartNumber.ShouldBe(this.data.PartNumber); + sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); + sut.SerialNumber.ShouldBe(this.data.SerialNumber); + sut.Spare162.ShouldBe(this.data.Spare162); + ((int)sut.ShipType).ShouldBe(this.data.ShipType); + sut.UnitModelCode.ShouldBe(this.data.UnitModelCode); + sut.VendorIdRev3.ShouldBe(this.data.VendorIdRev3); + sut.VendorIdRev4.ShouldBe(this.data.VendorIdRev4); } private class AisMessageType24Part1Data diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs index 77e202e..0f640c6 100644 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs @@ -1,15 +1,16 @@ namespace Ais.Net.Models.Specs; -using Abstractions; using System; +using Abstractions; + using Reqnroll; + using Shouldly; [Binding] -public class AisMessageType27StepDefinitions +public class AisMessageType27StepDefinitions : StepDefinitionBase { - private AisMessageType27? sut; private AisMessageType27Data? data; [Given("a new AisMessageType27 record with the following properties:")] @@ -17,8 +18,8 @@ public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable ta { PositionData position = new() { - Latitude = Convert.ToInt32(table.Rows[0]["Position_Latitude"]), - Longitude = Convert.ToInt32(table.Rows[0]["Position_Longitude"]) + Latitude = Convert.ToDouble(table.Rows[0]["Position_Latitude"]), + Longitude = Convert.ToDouble(table.Rows[0]["Position_Longitude"]) }; this.data = table.CreateInstance(); @@ -33,7 +34,7 @@ public void WhenTheAisMessageTypeIsCreated() throw new InvalidOperationException("Data is not set"); } - this.sut = new AisMessageType27( + this.Message = new AisMessageType27( CourseOverGround: this.data.CourseOverGround, GnssPositionStatus: this.data.GnssPositionStatus, Mmsi: this.data.Mmsi, @@ -49,16 +50,23 @@ public void WhenTheAisMessageTypeIsCreated() [Then("the AisMessageType27 properties should be set correctly")] public void ThenThePropertiesShouldBeSetCorrectly() { - this.sut.ShouldNotBeNull(); - this.sut.CourseOverGround.ShouldBe(123.45f); - this.sut.GnssPositionStatus.ShouldBeTrue(); - this.sut.Mmsi.ShouldBe(12345u); - this.sut.NavigationStatus.ShouldBe(NavigationStatus.UnderwayUsingEngine); - this.sut.Position.ShouldBe(new Position(1.0, 2.0)); - this.sut.PositionAccuracy.ShouldBeTrue(); - this.sut.RaimFlag.ShouldBeTrue(); - this.sut.RepeatIndicator.ShouldBe(3u); - this.sut.SpeedOverGround.ShouldBe(12.34f); + AisMessageType27? sut = this.Message as AisMessageType27; + + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + sut.ShouldNotBeNull(); + sut.CourseOverGround.ShouldBe(this.data.CourseOverGround); + sut.GnssPositionStatus.ShouldBe(this.data.GnssPositionStatus); + sut.Mmsi.ShouldBe(this.data.Mmsi); + sut.NavigationStatus.ShouldBe(Enum.Parse(this.data.NavigationStatus ?? throw new InvalidOperationException())); + sut.Position.ShouldBe(new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0)); + sut.PositionAccuracy.ShouldBe(this.data.PositionAccuracy); + sut.RaimFlag.ShouldBe(this.data.RaimFlag); + sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); + sut.SpeedOverGround.ShouldBe(this.data.SpeedOverGround); } private class AisMessageType27Data @@ -76,7 +84,7 @@ private class AisMessageType27Data private class PositionData { - public int Latitude { get; set; } - public int Longitude { get; set; } + public double Latitude { get; set; } + public double Longitude { get; set; } } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs index ffd48f5..12f7d7c 100644 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs @@ -1,15 +1,14 @@ namespace Ais.Net.Models.Specs; -using Abstractions; using System; using Reqnroll; + using Shouldly; [Binding] -public class AisMessageType5StepDefinitions +public class AisMessageType5StepDefinitions : StepDefinitionBase { - private AisMessageType5? sut; private AisMessageType5Data? data; [Given("a new AisMessageType5 record with the following properties:")] @@ -26,7 +25,7 @@ public void WhenTheAisMessageTypeIsCreated() throw new InvalidOperationException("Data is not set"); } - this.sut = new AisMessageType5( + this.Message = new AisMessageType5( AisVersion: this.data.AisVersion, CallSign: this.data.CallSign ?? throw new InvalidOperationException(), Destination: this.data.Destination ?? throw new InvalidOperationException(), @@ -53,27 +52,34 @@ public void WhenTheAisMessageTypeIsCreated() [Then("the AisMessageType5 properties should be set correctly")] public void ThenThePropertiesShouldBeSetCorrectly() { - this.sut.ShouldNotBeNull(); - this.sut.AisVersion.ShouldBe(1u); - this.sut.CallSign.ShouldBe("CALL"); - this.sut.Destination.ShouldBe("DEST"); - this.sut.DimensionToBow.ShouldBe(1u); - this.sut.DimensionToPort.ShouldBe(2u); - this.sut.DimensionToStarboard.ShouldBe(3u); - this.sut.DimensionToStern.ShouldBe(4u); - this.sut.Draught10thMetres.ShouldBe(5u); - this.sut.EtaDay.ShouldBe(6u); - this.sut.EtaHour.ShouldBe(7u); - this.sut.EtaMinute.ShouldBe(8u); - this.sut.EtaMonth.ShouldBe(9u); - this.sut.IsDteNotReady.ShouldBeTrue(); - this.sut.ImoNumber.ShouldBe(123u); - this.sut.Mmsi.ShouldBe(12345u); - this.sut.PositionFixType.ShouldBe(EpfdFixType.Gps); - this.sut.RepeatIndicator.ShouldBe(3u); - ((int)this.sut.ShipType).ShouldBe(60); - this.sut.Spare423.ShouldBe(1u); - this.sut.VesselName.ShouldBe("VESSEL"); + AisMessageType5? sut = this.Message as AisMessageType5; + + if (this.data is null) + { + throw new InvalidOperationException("Data is not set"); + } + + sut.ShouldNotBeNull(); + sut.AisVersion.ShouldBe(this.data.AisVersion); + sut.CallSign.ShouldBe(this.data.CallSign); + sut.Destination.ShouldBe(this.data.Destination); + sut.DimensionToBow.ShouldBe(this.data.DimensionToBow); + sut.DimensionToPort.ShouldBe(this.data.DimensionToPort); + sut.DimensionToStarboard.ShouldBe(this.data.DimensionToStarboard); + sut.DimensionToStern.ShouldBe(this.data.DimensionToStern); + sut.Draught10thMetres.ShouldBe(this.data.Draught10thMetres); + sut.EtaDay.ShouldBe(this.data.EtaDay); + sut.EtaHour.ShouldBe(this.data.EtaHour); + sut.EtaMinute.ShouldBe(this.data.EtaMinute); + sut.EtaMonth.ShouldBe(this.data.EtaMonth); + sut.IsDteNotReady.ShouldBe(this.data.IsDteNotReady); + sut.ImoNumber.ShouldBe(this.data.ImoNumber); + sut.Mmsi.ShouldBe(this.data.Mmsi); + sut.PositionFixType.ShouldBe(Enum.Parse(this.data.PositionFixType ?? throw new InvalidOperationException())); + sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); + ((int)sut.ShipType).ShouldBe(this.data.ShipType); + sut.Spare423.ShouldBe(this.data.Spare423); + sut.VesselName.ShouldBe(this.data.VesselName); } private class AisMessageType5Data diff --git a/Solutions/Ais.Net.Models.Specs/CommonStepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/CommonStepDefinitions.cs new file mode 100644 index 0000000..81d1b04 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/CommonStepDefinitions.cs @@ -0,0 +1,15 @@ +using Reqnroll; + +namespace Ais.Net.Models.Specs +{ + [Binding] + public class CommonStepDefinitions + { + [Given(@"I have an AIS message")] + public void GivenIHaveAnAisMessage() + { + // This is a common background step that does nothing + // The actual message creation happens in the specific "Given" steps + } + } +} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature index f01dd46..3bbf1d5 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature @@ -1,8 +1,15 @@ -Feature: AisMessageType18 +Feature: Class B "CS" Position Report -Scenario: Create AisMessageType18 record +Background: + Given I have an AIS message + +Scenario Outline: Decoding a Class B "CS" position report Given a new AisMessageType18 record with the following properties: | CanAcceptMessage22ChannelAssignment | CanSwitchBands | CourseOverGroundDegrees | CsUnit | HasDisplay | IsAssigned | IsDscAttached | Mmsi | Position_Latitude | Position_Longitude | PositionAccuracy | RadioStatusType | RaimFlag | RegionalReserved139 | RegionalReserved38 | RepeatIndicator | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | - | true | true | 123.45 | Cstdma | true | true | true | 12345 | 1 | 2 | true | Itdma | true | 1 | 2 | 3 | 12.34 | 56 | 78 | + | | | | | | | | | | | | | | | | | | | | When the AisMessageType is created - Then the properties should be set correctly \ No newline at end of file + Then the properties should be set correctly + + Examples: + | can_accept_msg22 | can_switch_bands | course | cs_unit | has_display | is_assigned | is_dsc_attached | mmsi | lat | long | pos_accuracy | radio_status | raim | reg_res_139 | reg_res_38 | repeat | speed | timestamp | true_heading | + | true | true | 123.45 | Cstdma | true | true | true | 12345 | 1.0 | 2.0 | true | Itdma | true | 1 | 2 | 3 | 12.34 | 56 | 78 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs index 74a8519..e3988d0 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs @@ -18,16 +18,16 @@ namespace Ais.Net.Models.Specs.Features [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("AisMessageType18")] + [NUnit.Framework.DescriptionAttribute("Class B \"CS\" Position Report")] [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class AisMessageType18Feature + public partial class ClassBCSPositionReportFeature { private global::Reqnroll.ITestRunner testRunner; private static string[] featureTags = ((string[])(null)); - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType18", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Class B \"CS\" Position Report", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); #line 1 "AisMessageType18.feature" #line hidden @@ -102,14 +102,63 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) await testRunner.CollectScenarioErrorsAsync(); } + public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() + { +#line 3 +#line hidden +#line 4 + await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); +#line hidden + } + [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Create AisMessageType18 record")] - public async global::System.Threading.Tasks.Task CreateAisMessageType18Record() + [NUnit.Framework.DescriptionAttribute("Decoding a Class B \"CS\" position report")] + [NUnit.Framework.TestCaseAttribute("true", "true", "123.45", "Cstdma", "true", "true", "true", "12345", "1.0", "2.0", "true", "Itdma", "true", "1", "2", "3", "12.34", "56", "78", null)] + public async global::System.Threading.Tasks.Task DecodingAClassBCSPositionReport( + string can_Accept_Msg22, + string can_Switch_Bands, + string course, + string cs_Unit, + string has_Display, + string is_Assigned, + string is_Dsc_Attached, + string mmsi, + string lat, + string @long, + string pos_Accuracy, + string radio_Status, + string raim, + string reg_Res_139, + string reg_Res_38, + string repeat, + string speed, + string timestamp, + string true_Heading, + string[] exampleTags) { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = exampleTags; global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType18 record", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 3 + argumentsOfScenario.Add("can_accept_msg22", can_Accept_Msg22); + argumentsOfScenario.Add("can_switch_bands", can_Switch_Bands); + argumentsOfScenario.Add("course", course); + argumentsOfScenario.Add("cs_unit", cs_Unit); + argumentsOfScenario.Add("has_display", has_Display); + argumentsOfScenario.Add("is_assigned", is_Assigned); + argumentsOfScenario.Add("is_dsc_attached", is_Dsc_Attached); + argumentsOfScenario.Add("mmsi", mmsi); + argumentsOfScenario.Add("lat", lat); + argumentsOfScenario.Add("long", @long); + argumentsOfScenario.Add("pos_accuracy", pos_Accuracy); + argumentsOfScenario.Add("radio_status", radio_Status); + argumentsOfScenario.Add("raim", raim); + argumentsOfScenario.Add("reg_res_139", reg_Res_139); + argumentsOfScenario.Add("reg_res_38", reg_Res_38); + argumentsOfScenario.Add("repeat", repeat); + argumentsOfScenario.Add("speed", speed); + argumentsOfScenario.Add("timestamp", timestamp); + argumentsOfScenario.Add("true_heading", true_Heading); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Class B \"CS\" position report", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) @@ -119,6 +168,9 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) else { await this.ScenarioStartAsync(); +#line 3 +await this.FeatureBackgroundAsync(); +#line hidden global::Reqnroll.Table table1 = new global::Reqnroll.Table(new string[] { "CanAcceptMessage22ChannelAssignment", "CanSwitchBands", @@ -140,32 +192,32 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) "TimeStampSecond", "TrueHeadingDegrees"}); table1.AddRow(new string[] { - "true", - "true", - "123.45", - "Cstdma", - "true", - "true", - "true", - "12345", - "1", - "2", - "true", - "Itdma", - "true", - "1", - "2", - "3", - "12.34", - "56", - "78"}); -#line 4 + string.Format("{0}", can_Accept_Msg22), + string.Format("{0}", can_Switch_Bands), + string.Format("{0}", course), + string.Format("{0}", cs_Unit), + string.Format("{0}", has_Display), + string.Format("{0}", is_Assigned), + string.Format("{0}", is_Dsc_Attached), + string.Format("{0}", mmsi), + string.Format("{0}", lat), + string.Format("{0}", @long), + string.Format("{0}", pos_Accuracy), + string.Format("{0}", radio_Status), + string.Format("{0}", raim), + string.Format("{0}", reg_Res_139), + string.Format("{0}", reg_Res_38), + string.Format("{0}", repeat), + string.Format("{0}", speed), + string.Format("{0}", timestamp), + string.Format("{0}", true_Heading)}); +#line 7 await testRunner.GivenAsync("a new AisMessageType18 record with the following properties:", ((string)(null)), table1, "Given "); #line hidden -#line 7 +#line 10 await testRunner.WhenAsync("the AisMessageType is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden -#line 8 +#line 11 await testRunner.ThenAsync("the properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); #line hidden } diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature index 3cde8ac..4cd3dea 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature @@ -1,8 +1,15 @@ -Feature: AisMessageType19 +Feature: Class B "SO" Position Report -Scenario: Create AisMessageType19 record +Background: + Given I have an AIS message + +Scenario Outline: Decoding a Class B "SO" position report Given a new AisMessageType19 record with the following properties: | CourseOverGround | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | IsAssigned | IsDteNotReady | Mmsi | Position_Latitude | Position_Longitude | PositionAccuracy | PositionFixType | RaimFlag | RegionalReserved139 | RegionalReserved38 | RepeatIndicator | ShipName | ShipType | Spare308 | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | - | 123.45 | 1 | 2 | 3 | 4 | true | true | 12345 | 1 | 2 | true | Gps | true | 1 | 2 | 3 | SHIP | 60 | 1 | 12.34 | 56 | 78 | + | | | | | | | | | | | | | | | | | | | | | | | When the AisMessageType19 is created - Then the AisMessageType19 properties should be set correctly \ No newline at end of file + Then the AisMessageType19 properties should be set correctly + + Examples: + | course | dim_bow | dim_port | dim_starboard | dim_stern | is_assigned | dte_not_ready | mmsi | lat | long | pos_accuracy | pos_fix | raim | reg_res_139 | reg_res_38 | repeat | ship_name | ship_type | spare | speed | timestamp | true_heading | + | 123.45 | 1 | 2 | 3 | 4 | true | true | 12345 | 1.0 | 2.0 | true | Gps | true | 1 | 2 | 3 | SHIP | 60 | 1 | 12.34 | 56 | 78 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs index e5600cd..1ec9637 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs @@ -18,16 +18,16 @@ namespace Ais.Net.Models.Specs.Features [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("AisMessageType19")] + [NUnit.Framework.DescriptionAttribute("Class B \"SO\" Position Report")] [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class AisMessageType19Feature + public partial class ClassBSOPositionReportFeature { private global::Reqnroll.ITestRunner testRunner; private static string[] featureTags = ((string[])(null)); - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType19", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Class B \"SO\" Position Report", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); #line 1 "AisMessageType19.feature" #line hidden @@ -102,14 +102,69 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) await testRunner.CollectScenarioErrorsAsync(); } + public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() + { +#line 3 +#line hidden +#line 4 + await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); +#line hidden + } + [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Create AisMessageType19 record")] - public async global::System.Threading.Tasks.Task CreateAisMessageType19Record() + [NUnit.Framework.DescriptionAttribute("Decoding a Class B \"SO\" position report")] + [NUnit.Framework.TestCaseAttribute("123.45", "1", "2", "3", "4", "true", "true", "12345", "1.0", "2.0", "true", "Gps", "true", "1", "2", "3", "SHIP", "60", "1", "12.34", "56", "78", null)] + public async global::System.Threading.Tasks.Task DecodingAClassBSOPositionReport( + string course, + string dim_Bow, + string dim_Port, + string dim_Starboard, + string dim_Stern, + string is_Assigned, + string dte_Not_Ready, + string mmsi, + string lat, + string @long, + string pos_Accuracy, + string pos_Fix, + string raim, + string reg_Res_139, + string reg_Res_38, + string repeat, + string ship_Name, + string ship_Type, + string spare, + string speed, + string timestamp, + string true_Heading, + string[] exampleTags) { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = exampleTags; global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType19 record", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 3 + argumentsOfScenario.Add("course", course); + argumentsOfScenario.Add("dim_bow", dim_Bow); + argumentsOfScenario.Add("dim_port", dim_Port); + argumentsOfScenario.Add("dim_starboard", dim_Starboard); + argumentsOfScenario.Add("dim_stern", dim_Stern); + argumentsOfScenario.Add("is_assigned", is_Assigned); + argumentsOfScenario.Add("dte_not_ready", dte_Not_Ready); + argumentsOfScenario.Add("mmsi", mmsi); + argumentsOfScenario.Add("lat", lat); + argumentsOfScenario.Add("long", @long); + argumentsOfScenario.Add("pos_accuracy", pos_Accuracy); + argumentsOfScenario.Add("pos_fix", pos_Fix); + argumentsOfScenario.Add("raim", raim); + argumentsOfScenario.Add("reg_res_139", reg_Res_139); + argumentsOfScenario.Add("reg_res_38", reg_Res_38); + argumentsOfScenario.Add("repeat", repeat); + argumentsOfScenario.Add("ship_name", ship_Name); + argumentsOfScenario.Add("ship_type", ship_Type); + argumentsOfScenario.Add("spare", spare); + argumentsOfScenario.Add("speed", speed); + argumentsOfScenario.Add("timestamp", timestamp); + argumentsOfScenario.Add("true_heading", true_Heading); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Class B \"SO\" position report", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) @@ -119,6 +174,9 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) else { await this.ScenarioStartAsync(); +#line 3 +await this.FeatureBackgroundAsync(); +#line hidden global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { "CourseOverGround", "DimensionToBow", @@ -143,35 +201,35 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) "TimeStampSecond", "TrueHeadingDegrees"}); table2.AddRow(new string[] { - "123.45", - "1", - "2", - "3", - "4", - "true", - "true", - "12345", - "1", - "2", - "true", - "Gps", - "true", - "1", - "2", - "3", - "SHIP", - "60", - "1", - "12.34", - "56", - "78"}); -#line 4 + string.Format("{0}", course), + string.Format("{0}", dim_Bow), + string.Format("{0}", dim_Port), + string.Format("{0}", dim_Starboard), + string.Format("{0}", dim_Stern), + string.Format("{0}", is_Assigned), + string.Format("{0}", dte_Not_Ready), + string.Format("{0}", mmsi), + string.Format("{0}", lat), + string.Format("{0}", @long), + string.Format("{0}", pos_Accuracy), + string.Format("{0}", pos_Fix), + string.Format("{0}", raim), + string.Format("{0}", reg_Res_139), + string.Format("{0}", reg_Res_38), + string.Format("{0}", repeat), + string.Format("{0}", ship_Name), + string.Format("{0}", ship_Type), + string.Format("{0}", spare), + string.Format("{0}", speed), + string.Format("{0}", timestamp), + string.Format("{0}", true_Heading)}); +#line 7 await testRunner.GivenAsync("a new AisMessageType19 record with the following properties:", ((string)(null)), table2, "Given "); #line hidden -#line 7 +#line 10 await testRunner.WhenAsync("the AisMessageType19 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden -#line 8 +#line 11 await testRunner.ThenAsync("the AisMessageType19 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); #line hidden } diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature index 4d33927..cc49f35 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature @@ -1,8 +1,16 @@ -Feature: AisMessageType1Through3 +Feature: Class A Vessel Position Report -Scenario: Create AisMessageType1Through3 record +Background: + Given I have an AIS message + +Scenario Outline: Decoding a Class A vessel position report Given a new AisMessageType1Through3 record with the following properties: - | CourseOverGround | ManoeuvreIndicator | MessageType | Mmsi | NavigationStatus | Position_Latitude | Position_Longitude | PositionAccuracy | RadioSlotTimeout | RadioSubMessage | RadioSyncState | RateOfTurn | RaimFlag | RepeatIndicator | SpareBits145 | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | - | 123.45 | NotAvailable | 1 | 12345 | UnderwayUsingEngine | 1 | 2 | true | 1 | 2 | UtcDirect | 1 | true | 3 | 4 | 12.34 | 56 | 78 | + | CourseOverGround | ManoeuvreIndicator | MessageType | Mmsi | NavigationStatus | Position_Latitude | Position_Longitude | PositionAccuracy | RadioSlotTimeout | RadioSubMessage | RadioSyncState | RateOfTurn | RaimFlag | RepeatIndicator | SpareBits145 | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | + | | | 1 | | | | | | 1 | 2 | UtcDirect | 1 | true | 3 | 4 | | 56 | 78 | When the AisMessageType1Through3 is created - Then the AisMessageType1Through3 properties should be set correctly \ No newline at end of file + Then the AisMessageType1Through3 properties should be set correctly + + Examples: + | course | manoeuvre | mmsi | nav_status | lat | long | pos_accuracy | speed | + | 123.45 | NotAvailable | 12345 | UnderwayUsingEngine | 1.0 | 2.0 | true | 12.34 | + | 234.56 | NotAvailable | 54321 | AtAnchor | 3.0 | 4.0 | true | 0.0 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs index bc1090f..5043597 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs @@ -18,16 +18,16 @@ namespace Ais.Net.Models.Specs.Features [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("AisMessageType1Through3")] + [NUnit.Framework.DescriptionAttribute("Class A Vessel Position Report")] [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class AisMessageType1Through3Feature + public partial class ClassAVesselPositionReportFeature { private global::Reqnroll.ITestRunner testRunner; private static string[] featureTags = ((string[])(null)); - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType1Through3", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Class A Vessel Position Report", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); #line 1 "AisMessageType1Through3.feature" #line hidden @@ -102,14 +102,33 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) await testRunner.CollectScenarioErrorsAsync(); } + public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() + { +#line 3 +#line hidden +#line 4 + await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); +#line hidden + } + [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Create AisMessageType1Through3 record")] - public async global::System.Threading.Tasks.Task CreateAisMessageType1Through3Record() + [NUnit.Framework.DescriptionAttribute("Decoding a Class A vessel position report")] + [NUnit.Framework.TestCaseAttribute("123.45", "NotAvailable", "12345", "UnderwayUsingEngine", "1.0", "2.0", "true", "12.34", null)] + [NUnit.Framework.TestCaseAttribute("234.56", "NotAvailable", "54321", "AtAnchor", "3.0", "4.0", "true", "0.0", null)] + public async global::System.Threading.Tasks.Task DecodingAClassAVesselPositionReport(string course, string manoeuvre, string mmsi, string nav_Status, string lat, string @long, string pos_Accuracy, string speed, string[] exampleTags) { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = exampleTags; global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType1Through3 record", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 3 + argumentsOfScenario.Add("course", course); + argumentsOfScenario.Add("manoeuvre", manoeuvre); + argumentsOfScenario.Add("mmsi", mmsi); + argumentsOfScenario.Add("nav_status", nav_Status); + argumentsOfScenario.Add("lat", lat); + argumentsOfScenario.Add("long", @long); + argumentsOfScenario.Add("pos_accuracy", pos_Accuracy); + argumentsOfScenario.Add("speed", speed); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Class A vessel position report", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) @@ -119,6 +138,9 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) else { await this.ScenarioStartAsync(); +#line 3 +await this.FeatureBackgroundAsync(); +#line hidden global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { "CourseOverGround", "ManoeuvreIndicator", @@ -139,14 +161,14 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) "TimeStampSecond", "TrueHeadingDegrees"}); table3.AddRow(new string[] { - "123.45", - "NotAvailable", + string.Format("{0}", course), + string.Format("{0}", manoeuvre), "1", - "12345", - "UnderwayUsingEngine", - "1", - "2", - "true", + string.Format("{0}", mmsi), + string.Format("{0}", nav_Status), + string.Format("{0}", lat), + string.Format("{0}", @long), + string.Format("{0}", pos_Accuracy), "1", "2", "UtcDirect", @@ -154,16 +176,16 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) "true", "3", "4", - "12.34", + string.Format("{0}", speed), "56", "78"}); -#line 4 +#line 7 await testRunner.GivenAsync("a new AisMessageType1Through3 record with the following properties:", ((string)(null)), table3, "Given "); #line hidden -#line 7 +#line 10 await testRunner.WhenAsync("the AisMessageType1Through3 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden -#line 8 +#line 11 await testRunner.ThenAsync("the AisMessageType1Through3 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); #line hidden } diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature index e4acf4c..3b65ae4 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature @@ -1,8 +1,15 @@ -Feature: AisMessageType24Part0 +Feature: Static Data Report Part A -Scenario: Create AisMessageType24Part0 record +Background: + Given I have an AIS message + +Scenario Outline: Decoding a Static Data Report Part A Given a new AisMessageType24Part0 record with the following properties: | Mmsi | PartNumber | RepeatIndicator | Spare160 | - | 12345 | 0 | 3 | 1 | + | | | | | When the AisMessageType24Part0 is created - Then the AisMessageType24Part0 properties should be set correctly \ No newline at end of file + Then the AisMessageType24Part0 properties should be set correctly + + Examples: + | mmsi | part_no | repeat | spare | + | 12345 | 0 | 3 | 1 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs index 25abb75..0c170d2 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs @@ -18,16 +18,16 @@ namespace Ais.Net.Models.Specs.Features [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("AisMessageType24Part0")] + [NUnit.Framework.DescriptionAttribute("Static Data Report Part A")] [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class AisMessageType24Part0Feature + public partial class StaticDataReportPartAFeature { private global::Reqnroll.ITestRunner testRunner; private static string[] featureTags = ((string[])(null)); - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType24Part0", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Static Data Report Part A", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); #line 1 "AisMessageType24Part0.feature" #line hidden @@ -102,14 +102,28 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) await testRunner.CollectScenarioErrorsAsync(); } + public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() + { +#line 3 +#line hidden +#line 4 + await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); +#line hidden + } + [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Create AisMessageType24Part0 record")] - public async global::System.Threading.Tasks.Task CreateAisMessageType24Part0Record() + [NUnit.Framework.DescriptionAttribute("Decoding a Static Data Report Part A")] + [NUnit.Framework.TestCaseAttribute("12345", "0", "3", "1", null)] + public async global::System.Threading.Tasks.Task DecodingAStaticDataReportPartA(string mmsi, string part_No, string repeat, string spare, string[] exampleTags) { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = exampleTags; global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType24Part0 record", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 3 + argumentsOfScenario.Add("mmsi", mmsi); + argumentsOfScenario.Add("part_no", part_No); + argumentsOfScenario.Add("repeat", repeat); + argumentsOfScenario.Add("spare", spare); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Static Data Report Part A", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) @@ -119,23 +133,26 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) else { await this.ScenarioStartAsync(); +#line 3 +await this.FeatureBackgroundAsync(); +#line hidden global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { "Mmsi", "PartNumber", "RepeatIndicator", "Spare160"}); table4.AddRow(new string[] { - "12345", - "0", - "3", - "1"}); -#line 4 + string.Format("{0}", mmsi), + string.Format("{0}", part_No), + string.Format("{0}", repeat), + string.Format("{0}", spare)}); +#line 7 await testRunner.GivenAsync("a new AisMessageType24Part0 record with the following properties:", ((string)(null)), table4, "Given "); #line hidden -#line 7 +#line 10 await testRunner.WhenAsync("the AisMessageType24Part0 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden -#line 8 +#line 11 await testRunner.ThenAsync("the AisMessageType24Part0 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); #line hidden } diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature index 0827626..1760325 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature @@ -1,8 +1,15 @@ -Feature: AisMessageType24Part1 +Feature: Static Data Report Part B -Scenario: Create AisMessageType24Part1 record +Background: + Given I have an AIS message + +Scenario Outline: Decoding a Static Data Report Part B Given a new AisMessageType24Part1 record with the following properties: | CallSign | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | Mmsi | MothershipMmsi | PartNumber | RepeatIndicator | SerialNumber | Spare162 | ShipType | UnitModelCode | VendorIdRev3 | VendorIdRev4 | - | CALL | 1 | 2 | 3 | 4 | 12345 | 54321 | 1 | 3 | 123 | 1 | 60 | 1 | VEN | DOR | + | | | | | | | | | | | | | | | | When the AisMessageType24Part1 is created - Then the AisMessageType24Part1 properties should be set correctly \ No newline at end of file + Then the AisMessageType24Part1 properties should be set correctly + + Examples: + | call_sign | dim_bow | dim_port | dim_starboard | dim_stern | mmsi | mothership_mmsi | part_no | repeat | serial_no | spare | ship_type | unit_model | vendor_rev3 | vendor_rev4 | + | CALL | 1 | 2 | 3 | 4 | 12345 | 54321 | 1 | 3 | 123 | 1 | 60 | 1 | VEN | DOR | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs index e78f712..69a6230 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs @@ -18,16 +18,16 @@ namespace Ais.Net.Models.Specs.Features [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("AisMessageType24Part1")] + [NUnit.Framework.DescriptionAttribute("Static Data Report Part B")] [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class AisMessageType24Part1Feature + public partial class StaticDataReportPartBFeature { private global::Reqnroll.ITestRunner testRunner; private static string[] featureTags = ((string[])(null)); - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType24Part1", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Static Data Report Part B", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); #line 1 "AisMessageType24Part1.feature" #line hidden @@ -102,14 +102,55 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) await testRunner.CollectScenarioErrorsAsync(); } + public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() + { +#line 3 +#line hidden +#line 4 + await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); +#line hidden + } + [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Create AisMessageType24Part1 record")] - public async global::System.Threading.Tasks.Task CreateAisMessageType24Part1Record() + [NUnit.Framework.DescriptionAttribute("Decoding a Static Data Report Part B")] + [NUnit.Framework.TestCaseAttribute("CALL", "1", "2", "3", "4", "12345", "54321", "1", "3", "123", "1", "60", "1", "VEN", "DOR", null)] + public async global::System.Threading.Tasks.Task DecodingAStaticDataReportPartB( + string call_Sign, + string dim_Bow, + string dim_Port, + string dim_Starboard, + string dim_Stern, + string mmsi, + string mothership_Mmsi, + string part_No, + string repeat, + string serial_No, + string spare, + string ship_Type, + string unit_Model, + string vendor_Rev3, + string vendor_Rev4, + string[] exampleTags) { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = exampleTags; global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType24Part1 record", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 3 + argumentsOfScenario.Add("call_sign", call_Sign); + argumentsOfScenario.Add("dim_bow", dim_Bow); + argumentsOfScenario.Add("dim_port", dim_Port); + argumentsOfScenario.Add("dim_starboard", dim_Starboard); + argumentsOfScenario.Add("dim_stern", dim_Stern); + argumentsOfScenario.Add("mmsi", mmsi); + argumentsOfScenario.Add("mothership_mmsi", mothership_Mmsi); + argumentsOfScenario.Add("part_no", part_No); + argumentsOfScenario.Add("repeat", repeat); + argumentsOfScenario.Add("serial_no", serial_No); + argumentsOfScenario.Add("spare", spare); + argumentsOfScenario.Add("ship_type", ship_Type); + argumentsOfScenario.Add("unit_model", unit_Model); + argumentsOfScenario.Add("vendor_rev3", vendor_Rev3); + argumentsOfScenario.Add("vendor_rev4", vendor_Rev4); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Static Data Report Part B", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) @@ -119,6 +160,9 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) else { await this.ScenarioStartAsync(); +#line 3 +await this.FeatureBackgroundAsync(); +#line hidden global::Reqnroll.Table table5 = new global::Reqnroll.Table(new string[] { "CallSign", "DimensionToBow", @@ -136,28 +180,28 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) "VendorIdRev3", "VendorIdRev4"}); table5.AddRow(new string[] { - "CALL", - "1", - "2", - "3", - "4", - "12345", - "54321", - "1", - "3", - "123", - "1", - "60", - "1", - "VEN", - "DOR"}); -#line 4 + string.Format("{0}", call_Sign), + string.Format("{0}", dim_Bow), + string.Format("{0}", dim_Port), + string.Format("{0}", dim_Starboard), + string.Format("{0}", dim_Stern), + string.Format("{0}", mmsi), + string.Format("{0}", mothership_Mmsi), + string.Format("{0}", part_No), + string.Format("{0}", repeat), + string.Format("{0}", serial_No), + string.Format("{0}", spare), + string.Format("{0}", ship_Type), + string.Format("{0}", unit_Model), + string.Format("{0}", vendor_Rev3), + string.Format("{0}", vendor_Rev4)}); +#line 7 await testRunner.GivenAsync("a new AisMessageType24Part1 record with the following properties:", ((string)(null)), table5, "Given "); #line hidden -#line 7 +#line 10 await testRunner.WhenAsync("the AisMessageType24Part1 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden -#line 8 +#line 11 await testRunner.ThenAsync("the AisMessageType24Part1 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); #line hidden } diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature index bab76c0..dc862a1 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature @@ -1,8 +1,15 @@ -Feature: AisMessageType27 +Feature: Long-Range AIS Broadcast message -Scenario: Create AisMessageType27 record +Background: + Given I have an AIS message + +Scenario Outline: Decoding a Long-Range AIS Broadcast message Given a new AisMessageType27 record with the following properties: | CourseOverGround | GnssPositionStatus | Mmsi | NavigationStatus | Position_Latitude | Position_Longitude | PositionAccuracy | RaimFlag | RepeatIndicator | SpeedOverGround | - | 123.45 | true | 12345 | UnderwayUsingEngine | 1 | 2 | true | true | 3 | 12.34 | + | | | | | | | | | | | When the AisMessageType27 is created - Then the AisMessageType27 properties should be set correctly \ No newline at end of file + Then the AisMessageType27 properties should be set correctly + + Examples: + | course | gnss_status | mmsi | nav_status | lat | long | pos_accuracy | raim | repeat | speed | + | 123.45 | true | 12345 | UnderwayUsingEngine | 1.0 | 2.0 | true | true | 3 | 12.34 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs index 67c6cc2..6fe11cf 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs @@ -18,16 +18,16 @@ namespace Ais.Net.Models.Specs.Features [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("AisMessageType27")] + [NUnit.Framework.DescriptionAttribute("Long-Range AIS Broadcast message")] [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class AisMessageType27Feature + public partial class Long_RangeAISBroadcastMessageFeature { private global::Reqnroll.ITestRunner testRunner; private static string[] featureTags = ((string[])(null)); - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType27", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Long-Range AIS Broadcast message", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); #line 1 "AisMessageType27.feature" #line hidden @@ -102,14 +102,34 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) await testRunner.CollectScenarioErrorsAsync(); } + public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() + { +#line 3 +#line hidden +#line 4 + await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); +#line hidden + } + [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Create AisMessageType27 record")] - public async global::System.Threading.Tasks.Task CreateAisMessageType27Record() + [NUnit.Framework.DescriptionAttribute("Decoding a Long-Range AIS Broadcast message")] + [NUnit.Framework.TestCaseAttribute("123.45", "true", "12345", "UnderwayUsingEngine", "1.0", "2.0", "true", "true", "3", "12.34", null)] + public async global::System.Threading.Tasks.Task DecodingALong_RangeAISBroadcastMessage(string course, string gnss_Status, string mmsi, string nav_Status, string lat, string @long, string pos_Accuracy, string raim, string repeat, string speed, string[] exampleTags) { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = exampleTags; global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType27 record", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 3 + argumentsOfScenario.Add("course", course); + argumentsOfScenario.Add("gnss_status", gnss_Status); + argumentsOfScenario.Add("mmsi", mmsi); + argumentsOfScenario.Add("nav_status", nav_Status); + argumentsOfScenario.Add("lat", lat); + argumentsOfScenario.Add("long", @long); + argumentsOfScenario.Add("pos_accuracy", pos_Accuracy); + argumentsOfScenario.Add("raim", raim); + argumentsOfScenario.Add("repeat", repeat); + argumentsOfScenario.Add("speed", speed); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Long-Range AIS Broadcast message", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) @@ -119,6 +139,9 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) else { await this.ScenarioStartAsync(); +#line 3 +await this.FeatureBackgroundAsync(); +#line hidden global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { "CourseOverGround", "GnssPositionStatus", @@ -131,23 +154,23 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) "RepeatIndicator", "SpeedOverGround"}); table6.AddRow(new string[] { - "123.45", - "true", - "12345", - "UnderwayUsingEngine", - "1", - "2", - "true", - "true", - "3", - "12.34"}); -#line 4 + string.Format("{0}", course), + string.Format("{0}", gnss_Status), + string.Format("{0}", mmsi), + string.Format("{0}", nav_Status), + string.Format("{0}", lat), + string.Format("{0}", @long), + string.Format("{0}", pos_Accuracy), + string.Format("{0}", raim), + string.Format("{0}", repeat), + string.Format("{0}", speed)}); +#line 7 await testRunner.GivenAsync("a new AisMessageType27 record with the following properties:", ((string)(null)), table6, "Given "); #line hidden -#line 7 +#line 10 await testRunner.WhenAsync("the AisMessageType27 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden -#line 8 +#line 11 await testRunner.ThenAsync("the AisMessageType27 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); #line hidden } diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature index f090714..0a1f11b 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature @@ -1,8 +1,15 @@ -Feature: AisMessageType5 +Feature: Static and Voyage Related Data -Scenario: Create AisMessageType5 record +Background: + Given I have an AIS message + +Scenario Outline: Decoding static and voyage related data Given a new AisMessageType5 record with the following properties: | AisVersion | CallSign | Destination | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | Draught10thMetres | EtaDay | EtaHour | EtaMinute | EtaMonth | IsDteNotReady | ImoNumber | Mmsi | PositionFixType | RepeatIndicator | ShipType | Spare423 | VesselName | - | 1 | CALL | DEST | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | true | 123 | 12345 | Gps | 3 | 60 | 1 | VESSEL | + | | | | | | | | | | | | | | | | | | | | | When the AisMessageType5 is created - Then the AisMessageType5 properties should be set correctly \ No newline at end of file + Then the AisMessageType5 properties should be set correctly + + Examples: + | ais_version | call_sign | destination | dim_bow | dim_port | dim_starboard | dim_stern | draught | eta_day | eta_hour | eta_min | eta_month | dte_not_ready | imo | mmsi | pos_fix | repeat | ship_type | spare | vessel_name | + | 1 | CALL | DEST | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | true | 123 | 12345 | Gps | 3 | 60 | 1 | VESSEL | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs index 9dc2d55..9fbe344 100644 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs +++ b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs @@ -18,16 +18,16 @@ namespace Ais.Net.Models.Specs.Features [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("AisMessageType5")] + [NUnit.Framework.DescriptionAttribute("Static and Voyage Related Data")] [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class AisMessageType5Feature + public partial class StaticAndVoyageRelatedDataFeature { private global::Reqnroll.ITestRunner testRunner; private static string[] featureTags = ((string[])(null)); - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "AisMessageType5", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); + private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Static and Voyage Related Data", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); #line 1 "AisMessageType5.feature" #line hidden @@ -102,14 +102,65 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) await testRunner.CollectScenarioErrorsAsync(); } + public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() + { +#line 3 +#line hidden +#line 4 + await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); +#line hidden + } + [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Create AisMessageType5 record")] - public async global::System.Threading.Tasks.Task CreateAisMessageType5Record() + [NUnit.Framework.DescriptionAttribute("Decoding static and voyage related data")] + [NUnit.Framework.TestCaseAttribute("1", "CALL", "DEST", "1", "2", "3", "4", "5", "6", "7", "8", "9", "true", "123", "12345", "Gps", "3", "60", "1", "VESSEL", null)] + public async global::System.Threading.Tasks.Task DecodingStaticAndVoyageRelatedData( + string ais_Version, + string call_Sign, + string destination, + string dim_Bow, + string dim_Port, + string dim_Starboard, + string dim_Stern, + string draught, + string eta_Day, + string eta_Hour, + string eta_Min, + string eta_Month, + string dte_Not_Ready, + string imo, + string mmsi, + string pos_Fix, + string repeat, + string ship_Type, + string spare, + string vessel_Name, + string[] exampleTags) { - string[] tagsOfScenario = ((string[])(null)); + string[] tagsOfScenario = exampleTags; global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Create AisMessageType5 record", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 3 + argumentsOfScenario.Add("ais_version", ais_Version); + argumentsOfScenario.Add("call_sign", call_Sign); + argumentsOfScenario.Add("destination", destination); + argumentsOfScenario.Add("dim_bow", dim_Bow); + argumentsOfScenario.Add("dim_port", dim_Port); + argumentsOfScenario.Add("dim_starboard", dim_Starboard); + argumentsOfScenario.Add("dim_stern", dim_Stern); + argumentsOfScenario.Add("draught", draught); + argumentsOfScenario.Add("eta_day", eta_Day); + argumentsOfScenario.Add("eta_hour", eta_Hour); + argumentsOfScenario.Add("eta_min", eta_Min); + argumentsOfScenario.Add("eta_month", eta_Month); + argumentsOfScenario.Add("dte_not_ready", dte_Not_Ready); + argumentsOfScenario.Add("imo", imo); + argumentsOfScenario.Add("mmsi", mmsi); + argumentsOfScenario.Add("pos_fix", pos_Fix); + argumentsOfScenario.Add("repeat", repeat); + argumentsOfScenario.Add("ship_type", ship_Type); + argumentsOfScenario.Add("spare", spare); + argumentsOfScenario.Add("vessel_name", vessel_Name); + global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding static and voyage related data", null, tagsOfScenario, argumentsOfScenario, featureTags); +#line 6 this.ScenarioInitialize(scenarioInfo); #line hidden if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) @@ -119,6 +170,9 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) else { await this.ScenarioStartAsync(); +#line 3 +await this.FeatureBackgroundAsync(); +#line hidden global::Reqnroll.Table table7 = new global::Reqnroll.Table(new string[] { "AisVersion", "CallSign", @@ -141,33 +195,33 @@ public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) "Spare423", "VesselName"}); table7.AddRow(new string[] { - "1", - "CALL", - "DEST", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "true", - "123", - "12345", - "Gps", - "3", - "60", - "1", - "VESSEL"}); -#line 4 + string.Format("{0}", ais_Version), + string.Format("{0}", call_Sign), + string.Format("{0}", destination), + string.Format("{0}", dim_Bow), + string.Format("{0}", dim_Port), + string.Format("{0}", dim_Starboard), + string.Format("{0}", dim_Stern), + string.Format("{0}", draught), + string.Format("{0}", eta_Day), + string.Format("{0}", eta_Hour), + string.Format("{0}", eta_Min), + string.Format("{0}", eta_Month), + string.Format("{0}", dte_Not_Ready), + string.Format("{0}", imo), + string.Format("{0}", mmsi), + string.Format("{0}", pos_Fix), + string.Format("{0}", repeat), + string.Format("{0}", ship_Type), + string.Format("{0}", spare), + string.Format("{0}", vessel_Name)}); +#line 7 await testRunner.GivenAsync("a new AisMessageType5 record with the following properties:", ((string)(null)), table7, "Given "); #line hidden -#line 7 +#line 10 await testRunner.WhenAsync("the AisMessageType5 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); #line hidden -#line 8 +#line 11 await testRunner.ThenAsync("the AisMessageType5 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); #line hidden } diff --git a/Solutions/Ais.Net.Models.Specs/Hooks.cs b/Solutions/Ais.Net.Models.Specs/Hooks.cs new file mode 100644 index 0000000..87ebbe9 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/Hooks.cs @@ -0,0 +1,20 @@ +using Reqnroll; + +namespace Ais.Net.Models.Specs +{ + [Binding] + public class Hooks + { + [BeforeScenario] + public void BeforeScenario() + { + //TODO: implement logic that has to run before executing each scenario + } + + [AfterScenario] + public void AfterScenario() + { + //TODO: implement logic that has to run after executing each scenario + } + } +} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/StepDefinitionBase.cs b/Solutions/Ais.Net.Models.Specs/StepDefinitionBase.cs new file mode 100644 index 0000000..d5795bc --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/StepDefinitionBase.cs @@ -0,0 +1,9 @@ +using Ais.Net.Models.Abstractions; + +namespace Ais.Net.Models.Specs +{ + public class StepDefinitionBase + { + protected IAisMessage? Message { get; set; } + } +} \ No newline at end of file From 29429e40a1d67c70322150787be79bb56733e837 Mon Sep 17 00:00:00 2001 From: Howard van Rooijen Date: Wed, 18 Jun 2025 10:42:50 +0100 Subject: [PATCH 05/17] Refactor project file by removing unnecessary elements Removed `` elements for settings files and eliminated `` and `` elements related to feature files. This simplifies the project structure and may streamline the build process. --- .../Ais.Net.Models.Specs.csproj | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj index 4e626e1..616ce97 100644 --- a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj +++ b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj @@ -32,23 +32,6 @@ - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - %(Filename) - - - - From 8a74a352e18ba92733ccfd128a9a559857ca5a3c Mon Sep 17 00:00:00 2001 From: James Dawson Date: Wed, 18 Jun 2025 11:27:17 +0100 Subject: [PATCH 06/17] Test linux-only build --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af5460f..c8dea3e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,9 +66,10 @@ jobs: forcePublish: ${{ github.event.inputs.forcePublish == 'true' }} skipCleanup: ${{ github.event.inputs.skipCleanup == 'true' }} enableCrossOsCaching: true + compilePhaseRunnerOs: ubuntu-latest testPhaseMatrixJson: | { - "os": ["windows-latest", "ubuntu-latest"], + "os": ["ubuntu-latest"], "dotnetFramework": ["net8.0"] } # testArtifactName: '' From 19624244fea84b3240e3d61b98be4daa2432cbd9 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Wed, 18 Jun 2025 11:33:49 +0100 Subject: [PATCH 07/17] Re-enable windows tests --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c8dea3e..ad8491c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,7 +69,7 @@ jobs: compilePhaseRunnerOs: ubuntu-latest testPhaseMatrixJson: | { - "os": ["ubuntu-latest"], + "os": ["windows-latest", "ubuntu-latest"], "dotnetFramework": ["net8.0"] } # testArtifactName: '' From 95cd268367796dec7f021883ed481e3b5085acd6 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Wed, 18 Jun 2025 13:41:32 +0100 Subject: [PATCH 08/17] Ensure packaging phase runs on same OS compilation --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad8491c..2a35188 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,6 +67,7 @@ jobs: skipCleanup: ${{ github.event.inputs.skipCleanup == 'true' }} enableCrossOsCaching: true compilePhaseRunnerOs: ubuntu-latest + packagePhaseRunnerOs: ubuntu-latest testPhaseMatrixJson: | { "os": ["windows-latest", "ubuntu-latest"], From 38f5ed487ae50ee8441ff118e715ba8e140e3170 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Wed, 18 Jun 2025 13:44:30 +0100 Subject: [PATCH 09/17] Debug files available from cache --- .zf/config.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.zf/config.ps1 b/.zf/config.ps1 index 5b4eaf1..d515202 100644 --- a/.zf/config.ps1 +++ b/.zf/config.ps1 @@ -55,7 +55,12 @@ task . FullBuild # task PostVersion {} # task PreBuild {} # task PostBuild {} -# task PreTest {} +task PreTest { + Write-Build Green "Repo Root:" + gci $here | Out-String | Write-Host + Write-Build Green "Available specs" + gci -Recurse $here/*.Specs.dll | Out-String | Write-Host +} # task PostTest {} # task PreTestReport {} # task PostTestReport {} From 0a7ea27d038882e4ec5d08f5e14a125bd15d9f50 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Wed, 18 Jun 2025 14:21:54 +0100 Subject: [PATCH 10/17] Test experimental workflow changes re: caching --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a35188..2a9f4e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: build: needs: prepareConfig - uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-matrix-pipeline.yml@main + uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-matrix-pipeline.yml@feature/update-ci-builds with: netSdkVersion: '8.x' # additionalNetSdkVersion: '9.x' From 297ea90706b6ee5618a07ad17fa691921070e804 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Wed, 18 Jun 2025 14:29:36 +0100 Subject: [PATCH 11/17] Force build From e7f380b1aeeeca13b23a851a2795a250b4a0e462 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Wed, 18 Jun 2025 14:36:11 +0100 Subject: [PATCH 12/17] Force build From 1b84cfe549cb52a22be9095659e3f5f0e8f82251 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Wed, 18 Jun 2025 15:38:44 +0100 Subject: [PATCH 13/17] Add 'IsTestProject' property --- Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj index 616ce97..1b41614 100644 --- a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj +++ b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj @@ -12,6 +12,7 @@ --> RCS1029;SA0001;SA1204;SA1600;SA1602;CS1591 enable + true From a3c3925e07ab613c0ca4c4b83dfe7f5ddc66fbd4 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Thu, 19 Jun 2025 17:13:22 +0100 Subject: [PATCH 14/17] Force build From 0270c1b9eec567162ea4cfca663c2eb1dae935b1 Mon Sep 17 00:00:00 2001 From: James Dawson Date: Thu, 19 Jun 2025 17:38:16 +0100 Subject: [PATCH 15/17] Revert debugging changes --- .github/workflows/build.yml | 2 +- .zf/config.ps1 | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a9f4e0..2a35188 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: build: needs: prepareConfig - uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-matrix-pipeline.yml@feature/update-ci-builds + uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-matrix-pipeline.yml@main with: netSdkVersion: '8.x' # additionalNetSdkVersion: '9.x' diff --git a/.zf/config.ps1 b/.zf/config.ps1 index d515202..5b4eaf1 100644 --- a/.zf/config.ps1 +++ b/.zf/config.ps1 @@ -55,12 +55,7 @@ task . FullBuild # task PostVersion {} # task PreBuild {} # task PostBuild {} -task PreTest { - Write-Build Green "Repo Root:" - gci $here | Out-String | Write-Host - Write-Build Green "Available specs" - gci -Recurse $here/*.Specs.dll | Out-String | Write-Host -} +# task PreTest {} # task PostTest {} # task PreTestReport {} # task PostTestReport {} From c0c4d09dd7e0c4b935a95d725751096439361b85 Mon Sep 17 00:00:00 2001 From: Howard van Rooijen Date: Thu, 23 Jul 2026 11:11:47 +0100 Subject: [PATCH 16/17] Modernize solution: slnx, CPM, MTP tests, multi-targeting, and API docs Solution/build: - Convert the solution to .slnx and adopt Central Package Management (Directory.Packages.props); add global.json pinning the .NET 10 SDK and selecting the Microsoft.Testing.Platform test runner. - Multi-target the library and tests to net8.0;net10.0 (non-breaking minor release; net10.0-only planned for the next release). Update CI to the 10.x SDK and test both frameworks. Tests: - Migrate the Reqnroll BDD specs to Microsoft.Testing.Platform unit tests (MSTest.Sdk + Shouldly, NSubstitute in the stack); remove coverlet, which is incompatible with MTP. - Expand coverage to 100% line and branch (245 tests per target framework). Correctness fixes in AisMessageExtensions: - ToShipTypeGroup(49) now maps to HighSpeedCraftNoAdditionalInformation (previously mis-classified as Reserved, disagreeing with the description). - CleanVesselName strips '@'/space padding order-independently and fully collapses runs of interior spaces. - Unify the ship-type group and description into a single lookup table so the two can no longer drift. Performance/allocation: - Span-based CleanVesselName, ReadOnlySpan GetString, and guarded stackallocs in the text helpers. - Add the Ais.Net.Models.Benchmarks project (BenchmarkDotNet, MemoryDiagnoser). Documentation: - Add XML documentation across the public API, generate the docs file so it ships in the package, and drop the doc-warning suppressions. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Q3BkmohCNRYGY1RBKbNaCx --- .gitattributes | 50 ++ .github/workflows/build.yml | 4 +- .gitignore | 4 +- .zf/config.ps1 | 136 ++-- .../Ais.Net.Models.Benchmarks.csproj | 25 + .../ConversionBenchmarks.cs | 42 ++ .../MessageConstructionBenchmarks.cs | 42 ++ .../PositionBenchmarks.cs | 26 + .../Ais.Net.Models.Benchmarks/Program.cs | 9 + .../VesselNameBenchmarks.cs | 33 + .../Ais.Net.Models.Specs.csproj | 27 +- .../AisMessageExtensionsTests.cs | 107 +++ .../AisMessageType18StepDefinitions.cs | 115 ---- .../AisMessageType18Tests.cs | 56 ++ .../AisMessageType19StepDefinitions.cs | 124 ---- .../AisMessageType19Tests.cs | 62 ++ .../AisMessageType1Through3StepDefinitions.cs | 112 --- .../AisMessageType1Through3Tests.cs | 61 ++ .../AisMessageType24Part0StepDefinitions.cs | 60 -- .../AisMessageType24Part0Tests.cs | 27 + .../AisMessageType24Part1StepDefinitions.cs | 93 --- .../AisMessageType24Part1Tests.cs | 50 ++ .../AisMessageType27StepDefinitions.cs | 90 --- .../AisMessageType27Tests.cs | 38 + .../AisMessageType5StepDefinitions.cs | 108 --- .../AisMessageType5Tests.cs | 60 ++ .../Ais.Net.Models.Specs/AssemblyInfo.cs | 5 + .../CommonStepDefinitions.cs | 15 - .../Features/AisMessageType18.feature | 15 - .../Features/AisMessageType18.feature.cs | 229 ------- .../Features/AisMessageType19.feature | 15 - .../Features/AisMessageType19.feature.cs | 241 ------- .../Features/AisMessageType1Through3.feature | 16 - .../AisMessageType1Through3.feature.cs | 197 ------ .../Features/AisMessageType24Part0.feature | 15 - .../Features/AisMessageType24Part0.feature.cs | 164 ----- .../Features/AisMessageType24Part1.feature | 15 - .../Features/AisMessageType24Part1.feature.cs | 213 ------ .../Features/AisMessageType27.feature | 15 - .../Features/AisMessageType27.feature.cs | 182 ----- .../Features/AisMessageType5.feature | 15 - .../Features/AisMessageType5.feature.cs | 233 ------- Solutions/Ais.Net.Models.Specs/Hooks.cs | 20 - .../Ais.Net.Models.Specs/PositionTests.cs | 71 ++ .../ShipTypeClassificationTests.cs | 242 +++++++ .../StepDefinitionBase.cs | 9 - .../Ais.Net.Models.Specs/packages.lock.json | 647 +++++++++++------- Solutions/Ais.Net.Models.sln | 38 - Solutions/Ais.Net.Models.slnx | 10 + .../Ais.Net.Models/Ais.Net.Models.csproj | 10 +- .../Net/Models/Abstractions/IAisIsAssigned.cs | 6 + .../Models/Abstractions/IAisIsDteNotReady.cs | 6 + .../Net/Models/Abstractions/IAisMessage.cs | 3 + .../Models/Abstractions/IAisMessageType.cs | 6 + .../Models/Abstractions/IAisMessageType18.cs | 27 + .../Models/Abstractions/IAisMessageType19.cs | 15 + .../Abstractions/IAisMessageType1to3.cs | 21 + .../Abstractions/IAisMessageType24Part0.cs | 6 + .../Abstractions/IAisMessageType24Part1.cs | 21 + .../Models/Abstractions/IAisMessageType27.cs | 31 +- .../Models/Abstractions/IAisMessageType5.cs | 30 + .../Abstractions/IAisMultipartMessage.cs | 6 + .../Abstractions/IAisPositionFixType.cs | 6 + .../Ais/Net/Models/Abstractions/ICallSign.cs | 6 + .../Ais/Net/Models/Abstractions/IRaimFlag.cs | 6 + .../Models/Abstractions/IRepeatIndicator.cs | 6 + .../Ais/Net/Models/Abstractions/IShipType.cs | 6 + .../Abstractions/IVesselCourseOverGround.cs | 24 +- .../Models/Abstractions/IVesselDimensions.cs | 15 + .../Models/Abstractions/IVesselIdentity.cs | 6 + .../Net/Models/Abstractions/IVesselName.cs | 6 + .../Models/Abstractions/IVesselNavigation.cs | 12 + .../Abstractions/IVesselNavigationStatus.cs | 24 +- .../Abstractions/IVesselPositionAccuracy.cs | 24 +- .../Abstractions/IVesselSpeedOverGround.cs | 24 +- .../Ais/Net/Models/Abstractions/Position.cs | 47 +- .../Models/Abstractions/ShipTypeCategory.cs | 42 ++ .../Net/Models/Abstractions/ShipTypeGroup.cs | 163 +++++ .../Ais/Net/Models/AisMessageBase.cs | 5 + .../Ais/Net/Models/AisMessageExtensions.cs | 346 ++++++---- .../Ais/Net/Models/AisMessageType18.cs | 21 + .../Ais/Net/Models/AisMessageType19.cs | 24 + .../Ais/Net/Models/AisMessageType1Through3.cs | 20 + .../Ais/Net/Models/AisMessageType24Part0.cs | 7 + .../Ais/Net/Models/AisMessageType24Part1.cs | 18 + .../Ais/Net/Models/AisMessageType27.cs | 60 +- .../Ais/Net/Models/AisMessageType5.cs | 23 + Solutions/Directory.Packages.props | 21 + global.json | 12 + 89 files changed, 2426 insertions(+), 2919 deletions(-) create mode 100644 .gitattributes create mode 100644 Solutions/Ais.Net.Models.Benchmarks/Ais.Net.Models.Benchmarks.csproj create mode 100644 Solutions/Ais.Net.Models.Benchmarks/ConversionBenchmarks.cs create mode 100644 Solutions/Ais.Net.Models.Benchmarks/MessageConstructionBenchmarks.cs create mode 100644 Solutions/Ais.Net.Models.Benchmarks/PositionBenchmarks.cs create mode 100644 Solutions/Ais.Net.Models.Benchmarks/Program.cs create mode 100644 Solutions/Ais.Net.Models.Benchmarks/VesselNameBenchmarks.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageExtensionsTests.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType18Tests.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType19Tests.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType1Through3Tests.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType24Part0Tests.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType24Part1Tests.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType27Tests.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AisMessageType5Tests.cs create mode 100644 Solutions/Ais.Net.Models.Specs/AssemblyInfo.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/CommonStepDefinitions.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature delete mode 100644 Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/Hooks.cs create mode 100644 Solutions/Ais.Net.Models.Specs/PositionTests.cs create mode 100644 Solutions/Ais.Net.Models.Specs/ShipTypeClassificationTests.cs delete mode 100644 Solutions/Ais.Net.Models.Specs/StepDefinitionBase.cs delete mode 100644 Solutions/Ais.Net.Models.sln create mode 100644 Solutions/Ais.Net.Models.slnx create mode 100644 Solutions/Directory.Packages.props create mode 100644 global.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..98909cf --- /dev/null +++ b/.gitattributes @@ -0,0 +1,50 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.cs text +*.csx text +*.csproj text +*.sln text +*.xaml text +*.config text +*.xml text +*.json text +*.md text +*.txt text +*.yml text +*.yaml text +*.sh text eol=lf +*.ps1 text eol=crlf +*.cmd text eol=crlf +*.bat text eol=crlf + +# Declare files that will always have LF line endings on checkout. +.gitattributes text eol=lf +.gitignore text eol=lf +*.sh text eol=lf + +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf +*.slnx text eol=crlf +*.cmd text eol=crlf +*.bat text eol=crlf +*.ps1 text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.dll binary +*.exe binary +*.pdb binary +*.zip binary +*.gz binary +*.tar binary +*.7z binary +*.nupkg binary +*.snk binary +*.pfx binary \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a35188..b162112 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: needs: prepareConfig uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-matrix-pipeline.yml@main with: - netSdkVersion: '8.x' + netSdkVersion: '10.x' # additionalNetSdkVersion: '9.x' # workflow_dispatch inputs are always strings, the type property is just for the UI forcePublish: ${{ github.event.inputs.forcePublish == 'true' }} @@ -71,7 +71,7 @@ jobs: testPhaseMatrixJson: | { "os": ["windows-latest", "ubuntu-latest"], - "dotnetFramework": ["net8.0"] + "dotnetFramework": ["net8.0", "net10.0"] } # testArtifactName: '' # testArtifactPath: '' diff --git a/.gitignore b/.gitignore index 52b09d1..89be2bb 100644 --- a/.gitignore +++ b/.gitignore @@ -402,4 +402,6 @@ FodyWeavers.xsd _codeCoverage/ _packages/ .zf/extensions/ -*.sbom.* \ No newline at end of file +*.sbom.* +.endjin/ +BenchmarkDotNet.Artifacts/ \ No newline at end of file diff --git a/.zf/config.ps1 b/.zf/config.ps1 index 5b4eaf1..620121d 100644 --- a/.zf/config.ps1 +++ b/.zf/config.ps1 @@ -1,68 +1,68 @@ -<# -This example demonstrates a software build process using the 'ZeroFailed.Build.DotNet' extension -to provide the features needed when building a .NET solutions. -#> - -$zerofailedExtensions = @( - @{ - # References the extension from its GitHub repository. If not already installed, use latest version from 'main' will be downloaded. - Name = "ZeroFailed.Build.DotNet" - GitRepository = "https://github.com/zerofailed/ZeroFailed.Build.DotNet" - GitRef = "main" - } -) - -# Load the tasks and process -. ZeroFailed.tasks -ZfPath $here/.zf - - -# -# Build process control options -# -$SkipInit = $false -$SkipVersion = $false -$SkipBuild = $false -$CleanBuild = $Clean -$SkipTest = $false -$SkipTestReport = $false -$SkipAnalysis = $false -$SkipPackage = $false - -# -# Build process configuration -# -$SolutionToBuild = (Resolve-Path (Join-Path $here "./Solutions/Ais.Net.Models.sln")).Path -$ProjectsToPublish = @() -$NuSpecFilesToPackage = @() -$NugetPublishSource = property ZF_NUGET_PUBLISH_SOURCE "$here/_local-nuget-feed" -$IncludeAssembliesInCodeCoverage = "Ais.Net.Models" -$ExcludeAssembliesInCodeCoverage = "" - - -# Customise the build process - -task . FullBuild - - -# -# Build Process Extensibility Points - uncomment and implement as required -# - -# task RunFirst {} -# task PreInit {} -# task PostInit {} -# task PreVersion {} -# task PostVersion {} -# task PreBuild {} -# task PostBuild {} -# task PreTest {} -# task PostTest {} -# task PreTestReport {} -# task PostTestReport {} -# task PreAnalysis {} -# task PostAnalysis {} -# task PrePackage {} -# task PostPackage {} -# task PrePublish {} -# task PostPublish {} -# task RunLast {} +<# +This example demonstrates a software build process using the 'ZeroFailed.Build.DotNet' extension +to provide the features needed when building a .NET solutions. +#> + +$zerofailedExtensions = @( + @{ + # References the extension from its GitHub repository. If not already installed, use latest version from 'main' will be downloaded. + Name = "ZeroFailed.Build.DotNet" + GitRepository = "https://github.com/zerofailed/ZeroFailed.Build.DotNet" + GitRef = "main" + } +) + +# Load the tasks and process +. ZeroFailed.tasks -ZfPath $here/.zf + + +# +# Build process control options +# +$SkipInit = $false +$SkipVersion = $false +$SkipBuild = $false +$CleanBuild = $Clean +$SkipTest = $false +$SkipTestReport = $false +$SkipAnalysis = $false +$SkipPackage = $false + +# +# Build process configuration +# +$SolutionToBuild = (Resolve-Path (Join-Path $here "./Solutions/Ais.Net.Models.slnx")).Path +$ProjectsToPublish = @() +$NuSpecFilesToPackage = @() +$NugetPublishSource = property ZF_NUGET_PUBLISH_SOURCE "$here/_local-nuget-feed" +$IncludeAssembliesInCodeCoverage = "Ais.Net.Models" +$ExcludeAssembliesInCodeCoverage = "" + + +# Customise the build process + +task . FullBuild + + +# +# Build Process Extensibility Points - uncomment and implement as required +# + +# task RunFirst {} +# task PreInit {} +# task PostInit {} +# task PreVersion {} +# task PostVersion {} +# task PreBuild {} +# task PostBuild {} +# task PreTest {} +# task PostTest {} +# task PreTestReport {} +# task PostTestReport {} +# task PreAnalysis {} +# task PostAnalysis {} +# task PrePackage {} +# task PostPackage {} +# task PrePublish {} +# task PostPublish {} +# task RunLast {} diff --git a/Solutions/Ais.Net.Models.Benchmarks/Ais.Net.Models.Benchmarks.csproj b/Solutions/Ais.Net.Models.Benchmarks/Ais.Net.Models.Benchmarks.csproj new file mode 100644 index 0000000..9289de2 --- /dev/null +++ b/Solutions/Ais.Net.Models.Benchmarks/Ais.Net.Models.Benchmarks.csproj @@ -0,0 +1,25 @@ + + + + + net10.0 + latest + enable + Exe + false + + true + false + + CS1591 + + + + + + + + + + + diff --git a/Solutions/Ais.Net.Models.Benchmarks/ConversionBenchmarks.cs b/Solutions/Ais.Net.Models.Benchmarks/ConversionBenchmarks.cs new file mode 100644 index 0000000..0b09c8a --- /dev/null +++ b/Solutions/Ais.Net.Models.Benchmarks/ConversionBenchmarks.cs @@ -0,0 +1,42 @@ +namespace Ais.Net.Models.Benchmarks; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using BenchmarkDotNet.Attributes; + +/// +/// Measures the pure numeric/enum helpers on the decode path. These are expected to be +/// branch-free-ish, sub-nanosecond, and allocation-free; the benchmark exists to prove that +/// and to catch regressions. +/// +[MemoryDiagnoser] +public class ConversionBenchmarks +{ + private const int LatitudeIn10000thMins = 30_000_000; // 50 degrees + private const int LatitudeIn10thMins = 30_000; // 50 degrees + private const uint SpeedRaw = 123; // 12.3 knots + private const uint CourseRaw = 1234; // 123.4 degrees + private const int RawShipType = 60; // Passenger, all ships + + [Benchmark] + public double From10000thMinsToDegrees() => LatitudeIn10000thMins.From10000thMinsToDegrees(); + + [Benchmark] + public double From10thMinsToDegrees() => LatitudeIn10thMins.From10thMinsToDegrees(); + + [Benchmark] + public float? FromTenths() => SpeedRaw.FromTenths(); + + [Benchmark] + public float? FromTenthsToDegrees() => CourseRaw.FromTenthsToDegrees(); + + [Benchmark] + public ShipTypeGroup ToShipTypeGroup() => RawShipType.ToShipTypeGroup(); + + [Benchmark] + public ShipTypeCategory ToShipTypeCategory() => ((ShipType)RawShipType).ToShipTypeCategory(); + + [Benchmark] + public string ToShipTypeDescription() => RawShipType.ToShipTypeDescription(); +} diff --git a/Solutions/Ais.Net.Models.Benchmarks/MessageConstructionBenchmarks.cs b/Solutions/Ais.Net.Models.Benchmarks/MessageConstructionBenchmarks.cs new file mode 100644 index 0000000..1fc1fc2 --- /dev/null +++ b/Solutions/Ais.Net.Models.Benchmarks/MessageConstructionBenchmarks.cs @@ -0,0 +1,42 @@ +namespace Ais.Net.Models.Benchmarks; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using BenchmarkDotNet.Attributes; + +/// +/// Measures the cost of materialising a model record for a decoded message. Records are +/// reference types, so each message is at least one heap allocation (plus a nested +/// allocation for positional message types). +/// +[MemoryDiagnoser] +public class MessageConstructionBenchmarks +{ + [Benchmark] + public AisMessageType1Through3 ClassAPositionReport() => new( + CourseOverGround: 123.4f, + ManoeuvreIndicator: ManoeuvreIndicator.NotAvailable, + MessageType: 1, + Mmsi: 235009802, + NavigationStatus: NavigationStatus.UnderwayUsingEngine, + Position: new Position(50.12345, -1.98765), + PositionAccuracy: true, + RadioSlotTimeout: 1, + RadioSubMessage: 2, + RadioSyncState: RadioSyncState.UtcDirect, + RateOfTurn: 0, + RaimFlag: false, + RepeatIndicator: 0, + SpareBits145: 0, + SpeedOverGround: 12.3f, + TimeStampSecond: 42, + TrueHeadingDegrees: 181); + + [Benchmark] + public AisMessageType24Part0 StaticDataReportPartA() => new( + Mmsi: 235009802, + PartNumber: 0, + RepeatIndicator: 0, + Spare160: 0); +} diff --git a/Solutions/Ais.Net.Models.Benchmarks/PositionBenchmarks.cs b/Solutions/Ais.Net.Models.Benchmarks/PositionBenchmarks.cs new file mode 100644 index 0000000..e8ffaa1 --- /dev/null +++ b/Solutions/Ais.Net.Models.Benchmarks/PositionBenchmarks.cs @@ -0,0 +1,26 @@ +namespace Ais.Net.Models.Benchmarks; + +using Ais.Net.Models.Abstractions; + +using BenchmarkDotNet.Attributes; + +/// +/// Quantifies the cost of being a record (reference type). Every +/// decoded position is a heap allocation. The struct variant models the proposed +/// readonly record struct change to show the allocation saving on the decode path. +/// +[MemoryDiagnoser] +public class PositionBenchmarks +{ + private const double Latitude = 50.12345; + private const double Longitude = -1.98765; + + [Benchmark(Baseline = true)] + public Position RecordClass() => new(Latitude, Longitude); + + [Benchmark] + public PositionValue RecordStruct() => new(Latitude, Longitude); + + /// Models reworked as a value type. + public readonly record struct PositionValue(double Latitude, double Longitude); +} diff --git a/Solutions/Ais.Net.Models.Benchmarks/Program.cs b/Solutions/Ais.Net.Models.Benchmarks/Program.cs new file mode 100644 index 0000000..0d8d0a4 --- /dev/null +++ b/Solutions/Ais.Net.Models.Benchmarks/Program.cs @@ -0,0 +1,9 @@ +using BenchmarkDotNet.Running; + +// Use the switcher so individual benchmark classes/methods can be selected via --filter, +// e.g. dotnet run -c Release -- --filter *VesselName* +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + +public partial class Program +{ +} diff --git a/Solutions/Ais.Net.Models.Benchmarks/VesselNameBenchmarks.cs b/Solutions/Ais.Net.Models.Benchmarks/VesselNameBenchmarks.cs new file mode 100644 index 0000000..ee804f7 --- /dev/null +++ b/Solutions/Ais.Net.Models.Benchmarks/VesselNameBenchmarks.cs @@ -0,0 +1,33 @@ +namespace Ais.Net.Models.Benchmarks; + +using System; + +using Ais.Net.Models; + +using BenchmarkDotNet.Attributes; + +/// +/// Compares vessel-name cleaning strategies. The production method currently uses +/// Trim(...).Split(...).Join(...) which allocates a temporary trimmed string, an array, +/// and one substring per word. The span-based variant produces exactly one string (the result) +/// using a stack buffer, and is the proposed replacement. +/// +[MemoryDiagnoser] +public class VesselNameBenchmarks +{ + // A raw 20-character AIS name field: text plus '@' padding (the common case). + [Params("EVER GIVEN@@@@@@@@@@", "QUEEN MARY 2@@@@@", "@@@@@@@@@@@@@@@@@@@@")] + public string RawName { get; set; } = string.Empty; + + // The previous implementation, kept as the baseline to show the improvement. + [Benchmark(Baseline = true)] + public string Old_SplitJoin() + { + string trimmed = this.RawName.Trim('@', ' '); + return string.Join(' ', trimmed.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + } + + // The shipped implementation. + [Benchmark] + public string Current_Span() => this.RawName.CleanVesselName(); +} diff --git a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj index 1b41614..8c17bd9 100644 --- a/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj +++ b/Solutions/Ais.Net.Models.Specs/Ais.Net.Models.Specs.csproj @@ -1,18 +1,13 @@ - + + - net8.0 + net8.0;net10.0 latest - false - - RCS1029;SA0001;SA1204;SA1600;SA1602;CS1591 enable - true + false + + CS1591 @@ -21,14 +16,8 @@ - - - contentfiles; analyzers - - - - - + + diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageExtensionsTests.cs b/Solutions/Ais.Net.Models.Specs/AisMessageExtensionsTests.cs new file mode 100644 index 0000000..0488954 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageExtensionsTests.cs @@ -0,0 +1,107 @@ +namespace Ais.Net.Models.Specs; + +using System; + +using Ais.Net; +using Ais.Net.Models; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class AisMessageExtensionsTests +{ + [TestMethod] + [DataRow(0u, 0.0f)] + [DataRow(123u, 12.3f)] + [DataRow(1022u, 102.2f)] // one below the sentinel is still a real value + public void FromTenthsScalesByTenth(uint raw, float expected) + { + raw.FromTenths().ShouldBe(expected); + } + + [TestMethod] + public void FromTenthsTreats1023AsNotAvailable() + { + // 1023 is the AIS sentinel meaning "speed over ground not available". + 1023u.FromTenths().ShouldBeNull(); + } + + [TestMethod] + [DataRow(0u, 0.0f)] + [DataRow(1234u, 123.4f)] + [DataRow(3599u, 359.9f)] // one below the sentinel is still a real value + public void FromTenthsToDegreesScalesByTenth(uint raw, float expected) + { + raw.FromTenthsToDegrees().ShouldBe(expected); + } + + [TestMethod] + public void FromTenthsToDegreesTreats3600AsNotAvailable() + { + // 3600 (== 360.0 degrees) is the AIS sentinel meaning "course over ground not available". + 3600u.FromTenthsToDegrees().ShouldBeNull(); + } + + [TestMethod] + [DataRow("VESSEL", "VESSEL")] + [DataRow("VESSEL@@@@@", "VESSEL")] // trailing '@' padding is removed + [DataRow("@@@@@VESSEL", "VESSEL")] // leading '@' padding is removed + [DataRow("VESSEL NAME ", "VESSEL NAME")] // trailing whitespace is trimmed, interior single space kept + [DataRow("QUEEN MARY 2", "QUEEN MARY 2")] // legitimate interior single spaces are preserved + [DataRow("TWO WORDS", "TWO WORDS")] // a run of interior spaces collapses to one + [DataRow("A B", "A B")] // three-plus interior spaces collapse fully (not just paired) + [DataRow("QUEEN MARY 2", "QUEEN MARY 2")] // multiple interior runs each collapse + [DataRow("NAME@ ", "NAME")] // '@' followed by a space is still stripped (padding order-independent) + [DataRow(" @@ VESSEL @@ ", "VESSEL")] // mixed leading/trailing '@' and spaces are all stripped + [DataRow("AB@CD", "AB@CD")] // an interior '@' is left untouched (only end padding is stripped) + [DataRow("@@@@@", "")] // an all-padding name becomes empty + [DataRow("", "")] // an empty name stays empty + public void CleanVesselNameStripsPaddingAndCollapsesInteriorSpaceRuns(string raw, string expected) + { + raw.CleanVesselName().ShouldBe(expected); + } + + [TestMethod] + public void GetStringDecodesAsciiBytes() + { + ReadOnlySpan ascii = "Hello"u8; + + ascii.GetString().ShouldBe("Hello"); + } + + [TestMethod] + public void TextFieldToStringDecodesSixBitAsciiField() + { + // AIS packs text as 6-bit characters where 'H' == 8 and 'I' == 9. Those values are + // carried in the ASCII-armoured payload as (value + 48), i.e. '8' and '9'. + ReadOnlySpan armoured = "89"u8; + NmeaAisBitVectorParser bits = new(armoured, padding: 0); + NmeaAisTextFieldParser field = new(bits, bitLength: 12, bitOffset: 0); + + field.TextFieldToString().ShouldBe("HI"); + } + + [TestMethod] + public void CleanVesselNameHandlesNamesLongerThanTheStackBuffer() + { + // 260 characters (> 256) forces the heap-buffer fallback path. + string longName = new('A', 260); + + longName.CleanVesselName().ShouldBe(longName); + } + + [TestMethod] + public void TextFieldToStringHandlesFieldsLongerThanTheStackBuffer() + { + // 300 characters (> 256) forces the heap-buffer fallback. Each armoured '1' is 6-bit + // value 1, which decodes to 'A'. + byte[] armoured = new byte[300]; + armoured.AsSpan().Fill((byte)'1'); + NmeaAisBitVectorParser bits = new(armoured, padding: 0); + NmeaAisTextFieldParser field = new(bits, bitLength: (uint)(armoured.Length * 6), bitOffset: 0); + + field.TextFieldToString().ShouldBe(new string('A', 300)); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs deleted file mode 100644 index 97a007a..0000000 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType18StepDefinitions.cs +++ /dev/null @@ -1,115 +0,0 @@ -namespace Ais.Net.Models.Specs; - -using System; - -using Abstractions; - -using Reqnroll; - -using Shouldly; - -[Binding] -public class AisMessageType18StepDefinitions : StepDefinitionBase -{ - private AisMessageType18Data? data; - - [Given("a new AisMessageType18 record with the following properties:")] - public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) - { - PositionData position = new() - { - Latitude = Convert.ToDouble(table.Rows[0]["Position_Latitude"]), - Longitude = Convert.ToDouble(table.Rows[0]["Position_Longitude"]) - }; - - this.data = table.CreateInstance(); - this.data.Position = position; - } - - [When("the AisMessageType is created")] - public void WhenTheAisMessageTypeIsCreated() - { - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - this.Message = new AisMessageType18( - CanAcceptMessage22ChannelAssignment: this.data.CanAcceptMessage22ChannelAssignment, - CanSwitchBands: this.data.CanSwitchBands, - CourseOverGround: this.data.CourseOverGroundDegrees, - CsUnit: Enum.Parse(this.data.CsUnit ?? throw new InvalidOperationException()), - HasDisplay: this.data.HasDisplay, - IsAssigned: this.data.IsAssigned, - IsDscAttached: this.data.IsDscAttached, - Mmsi: this.data.Mmsi, - Position: new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0), - PositionAccuracy: this.data.PositionAccuracy, - RadioStatusType: Enum.Parse(this.data.RadioStatusType ?? throw new InvalidOperationException()), - RaimFlag: this.data.RaimFlag, - RegionalReserved139: this.data.RegionalReserved139, - RegionalReserved38: this.data.RegionalReserved38, - RepeatIndicator: this.data.RepeatIndicator, - SpeedOverGround: this.data.SpeedOverGround, - TimeStampSecond: this.data.TimeStampSecond, - TrueHeadingDegrees: this.data.TrueHeadingDegrees - ); - } - - [Then("the properties should be set correctly")] - public void ThenThePropertiesShouldBeSetCorrectly() - { - var sut = (AisMessageType18?)this.Message; - sut.ShouldNotBeNull(); - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - sut.CanAcceptMessage22ChannelAssignment.ShouldBe(this.data.CanAcceptMessage22ChannelAssignment); - sut.CanSwitchBands.ShouldBe(this.data.CanSwitchBands); - sut.CourseOverGround.ShouldBe(this.data.CourseOverGroundDegrees); - sut.CsUnit.ShouldBe(Enum.Parse(this.data.CsUnit ?? throw new InvalidOperationException())); - sut.HasDisplay.ShouldBe(this.data.HasDisplay); - sut.IsAssigned.ShouldBe(this.data.IsAssigned); - sut.IsDscAttached.ShouldBe(this.data.IsDscAttached); - sut.Mmsi.ShouldBe(this.data.Mmsi); - sut.Position.ShouldBe(new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0)); - sut.PositionAccuracy.ShouldBe(this.data.PositionAccuracy); - sut.RadioStatusType.ShouldBe(Enum.Parse(this.data.RadioStatusType ?? throw new InvalidOperationException())); - sut.RaimFlag.ShouldBe(this.data.RaimFlag); - sut.RegionalReserved139.ShouldBe(this.data.RegionalReserved139); - sut.RegionalReserved38.ShouldBe(this.data.RegionalReserved38); - sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); - sut.SpeedOverGround.ShouldBe(this.data.SpeedOverGround); - sut.TimeStampSecond.ShouldBe(this.data.TimeStampSecond); - sut.TrueHeadingDegrees.ShouldBe(this.data.TrueHeadingDegrees); - } - - private class AisMessageType18Data - { - public bool CanAcceptMessage22ChannelAssignment { get; set; } - public bool CanSwitchBands { get; set; } - public float? CourseOverGroundDegrees { get; set; } - public string? CsUnit { get; set; } - public bool HasDisplay { get; set; } - public bool IsAssigned { get; set; } - public bool IsDscAttached { get; set; } - public uint Mmsi { get; set; } - public PositionData? Position { get; set; } - public bool PositionAccuracy { get; set; } - public string? RadioStatusType { get; set; } - public bool RaimFlag { get; set; } - public int RegionalReserved139 { get; set; } - public int RegionalReserved38 { get; set; } - public uint RepeatIndicator { get; set; } - public float? SpeedOverGround { get; set; } - public uint TimeStampSecond { get; set; } - public uint TrueHeadingDegrees { get; set; } - } - - private class PositionData - { - public double Latitude { get; set; } - public double Longitude { get; set; } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType18Tests.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType18Tests.cs new file mode 100644 index 0000000..dd956a3 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType18Tests.cs @@ -0,0 +1,56 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class AisMessageType18Tests +{ + [TestMethod] + public void ClassBCsPositionReportExposesSuppliedValues() + { + AisMessageType18 message = new( + CanAcceptMessage22ChannelAssignment: true, + CanSwitchBands: true, + CourseOverGround: 123.45f, + CsUnit: ClassBUnit.Cstdma, + HasDisplay: true, + IsAssigned: true, + IsDscAttached: true, + Mmsi: 12345, + Position: new Position(1.0, 2.0), + PositionAccuracy: true, + RadioStatusType: ClassBRadioStatusType.Itdma, + RaimFlag: true, + RegionalReserved139: 1, + RegionalReserved38: 2, + RepeatIndicator: 3, + SpeedOverGround: 12.34f, + TimeStampSecond: 56, + TrueHeadingDegrees: 78); + + message.MessageType.ShouldBe(18); + message.CanAcceptMessage22ChannelAssignment.ShouldBe(true); + message.CanSwitchBands.ShouldBe(true); + message.CourseOverGround.ShouldBe(123.45f); + message.CsUnit.ShouldBe(ClassBUnit.Cstdma); + message.HasDisplay.ShouldBe(true); + message.IsAssigned.ShouldBe(true); + message.IsDscAttached.ShouldBe(true); + message.Mmsi.ShouldBe(12345u); + message.Position.ShouldBe(new Position(1.0, 2.0)); + message.PositionAccuracy.ShouldBe(true); + message.RadioStatusType.ShouldBe(ClassBRadioStatusType.Itdma); + message.RaimFlag.ShouldBe(true); + message.RegionalReserved139.ShouldBe(1); + message.RegionalReserved38.ShouldBe(2); + message.RepeatIndicator.ShouldBe(3u); + message.SpeedOverGround.ShouldBe(12.34f); + message.TimeStampSecond.ShouldBe(56u); + message.TrueHeadingDegrees.ShouldBe(78u); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs deleted file mode 100644 index ab5298a..0000000 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType19StepDefinitions.cs +++ /dev/null @@ -1,124 +0,0 @@ -namespace Ais.Net.Models.Specs; - -using System; - -using Abstractions; - -using Reqnroll; - -using Shouldly; - -[Binding] -public class AisMessageType19StepDefinitions : StepDefinitionBase -{ - private AisMessageType19Data? data; - - [Given("a new AisMessageType19 record with the following properties:")] - public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) - { - PositionData position = new() - { - Latitude = Convert.ToDouble(table.Rows[0]["Position_Latitude"]), - Longitude = Convert.ToDouble(table.Rows[0]["Position_Longitude"]) - }; - - this.data = table.CreateInstance(); - this.data.Position = position; - } - - [When("the AisMessageType19 is created")] - public void WhenTheAisMessageTypeIsCreated() - { - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - this.Message = new AisMessageType19( - CourseOverGround: this.data.CourseOverGround, - DimensionToBow: this.data.DimensionToBow, - DimensionToPort: this.data.DimensionToPort, - DimensionToStarboard: this.data.DimensionToStarboard, - DimensionToStern: this.data.DimensionToStern, - IsAssigned: this.data.IsAssigned, - IsDteNotReady: this.data.IsDteNotReady, - Mmsi: this.data.Mmsi, - Position: new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0), - PositionAccuracy: this.data.PositionAccuracy, - PositionFixType: Enum.Parse(this.data.PositionFixType ?? throw new InvalidOperationException()), - RaimFlag: this.data.RaimFlag, - RegionalReserved139: this.data.RegionalReserved139, - RegionalReserved38: this.data.RegionalReserved38, - RepeatIndicator: this.data.RepeatIndicator, - ShipName: this.data.ShipName ?? throw new InvalidOperationException(), - ShipType: (ShipType)this.data.ShipType, - Spare308: this.data.Spare308, - SpeedOverGround: this.data.SpeedOverGround, - TimeStampSecond: this.data.TimeStampSecond, - TrueHeadingDegrees: this.data.TrueHeadingDegrees - ); - } - - [Then("the AisMessageType19 properties should be set correctly")] - public void ThenThePropertiesShouldBeSetCorrectly() - { - var sut = (AisMessageType19?)this.Message; - sut.ShouldNotBeNull(); - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - sut.CourseOverGround.ShouldBe(this.data.CourseOverGround); - sut.DimensionToBow.ShouldBe(this.data.DimensionToBow); - sut.DimensionToPort.ShouldBe(this.data.DimensionToPort); - sut.DimensionToStarboard.ShouldBe(this.data.DimensionToStarboard); - sut.DimensionToStern.ShouldBe(this.data.DimensionToStern); - sut.IsAssigned.ShouldBe(this.data.IsAssigned); - sut.IsDteNotReady.ShouldBe(this.data.IsDteNotReady); - sut.Mmsi.ShouldBe(this.data.Mmsi); - sut.Position.ShouldBe(new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0)); - sut.PositionAccuracy.ShouldBe(this.data.PositionAccuracy); - sut.PositionFixType.ShouldBe(Enum.Parse(this.data.PositionFixType ?? throw new InvalidOperationException())); - sut.RaimFlag.ShouldBe(this.data.RaimFlag); - sut.RegionalReserved139.ShouldBe(this.data.RegionalReserved139); - sut.RegionalReserved38.ShouldBe(this.data.RegionalReserved38); - sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); - sut.ShipName.ShouldBe(this.data.ShipName); - ((int)sut.ShipType).ShouldBe(this.data.ShipType); - sut.Spare308.ShouldBe(this.data.Spare308); - sut.SpeedOverGround.ShouldBe(this.data.SpeedOverGround); - sut.TimeStampSecond.ShouldBe(this.data.TimeStampSecond); - sut.TrueHeadingDegrees.ShouldBe(this.data.TrueHeadingDegrees); - } - - private class AisMessageType19Data - { - public float? CourseOverGround { get; set; } - public uint DimensionToBow { get; set; } - public uint DimensionToPort { get; set; } - public uint DimensionToStarboard { get; set; } - public uint DimensionToStern { get; set; } - public bool IsAssigned { get; set; } - public bool IsDteNotReady { get; set; } - public uint Mmsi { get; set; } - public PositionData? Position { get; set; } - public bool PositionAccuracy { get; set; } - public string? PositionFixType { get; set; } - public bool RaimFlag { get; set; } - public int RegionalReserved139 { get; set; } - public int RegionalReserved38 { get; set; } - public uint RepeatIndicator { get; set; } - public string? ShipName { get; set; } - public int ShipType { get; set; } - public uint Spare308 { get; set; } - public float? SpeedOverGround { get; set; } - public uint TimeStampSecond { get; set; } - public uint TrueHeadingDegrees { get; set; } - } - - private class PositionData - { - public double Latitude { get; set; } - public double Longitude { get; set; } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType19Tests.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType19Tests.cs new file mode 100644 index 0000000..13b971e --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType19Tests.cs @@ -0,0 +1,62 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class AisMessageType19Tests +{ + [TestMethod] + public void ClassBSoPositionReportExposesSuppliedValues() + { + AisMessageType19 message = new( + CourseOverGround: 123.45f, + DimensionToBow: 1, + DimensionToPort: 2, + DimensionToStarboard: 3, + DimensionToStern: 4, + IsAssigned: true, + IsDteNotReady: true, + Mmsi: 12345, + Position: new Position(1.0, 2.0), + PositionAccuracy: true, + PositionFixType: EpfdFixType.Gps, + RaimFlag: true, + RegionalReserved139: 1, + RegionalReserved38: 2, + RepeatIndicator: 3, + ShipName: "SHIP", + ShipType: (ShipType)60, + Spare308: 1, + SpeedOverGround: 12.34f, + TimeStampSecond: 56, + TrueHeadingDegrees: 78); + + message.MessageType.ShouldBe(19); + message.CourseOverGround.ShouldBe(123.45f); + message.DimensionToBow.ShouldBe(1u); + message.DimensionToPort.ShouldBe(2u); + message.DimensionToStarboard.ShouldBe(3u); + message.DimensionToStern.ShouldBe(4u); + message.IsAssigned.ShouldBe(true); + message.IsDteNotReady.ShouldBe(true); + message.Mmsi.ShouldBe(12345u); + message.Position.ShouldBe(new Position(1.0, 2.0)); + message.PositionAccuracy.ShouldBe(true); + message.PositionFixType.ShouldBe(EpfdFixType.Gps); + message.RaimFlag.ShouldBe(true); + message.RegionalReserved139.ShouldBe(1); + message.RegionalReserved38.ShouldBe(2); + message.RepeatIndicator.ShouldBe(3u); + message.ShipName.ShouldBe("SHIP"); + message.ShipType.ShouldBe((ShipType)60); + message.Spare308.ShouldBe(1u); + message.SpeedOverGround.ShouldBe(12.34f); + message.TimeStampSecond.ShouldBe(56u); + message.TrueHeadingDegrees.ShouldBe(78u); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs deleted file mode 100644 index fb2e9b4..0000000 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3StepDefinitions.cs +++ /dev/null @@ -1,112 +0,0 @@ -namespace Ais.Net.Models.Specs; - -using System; - -using Abstractions; - -using Reqnroll; - -using Shouldly; - -[Binding] -public class AisMessageType1Through3StepDefinitions : StepDefinitionBase -{ - private AisMessageType1Through3Data? data; - - [Given("a new AisMessageType1Through3 record with the following properties:")] - public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) - { - PositionData position = new() - { - Latitude = Convert.ToDouble(table.Rows[0]["Position_Latitude"]), - Longitude = Convert.ToDouble(table.Rows[0]["Position_Longitude"]) - }; - - this.data = table.CreateInstance(); - this.data.Position = position; - } - - [When("the AisMessageType1Through3 is created")] - public void WhenTheAisMessageTypeIsCreated() - { - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - this.Message = new AisMessageType1Through3( - CourseOverGround: this.data.CourseOverGround, - ManoeuvreIndicator: Enum.Parse(this.data.ManoeuvreIndicator ?? throw new InvalidOperationException()), - MessageType: this.data.MessageType, - Mmsi: this.data.Mmsi, - NavigationStatus: Enum.Parse(this.data.NavigationStatus ?? throw new InvalidOperationException()), - Position: new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0), - PositionAccuracy: this.data.PositionAccuracy, - RadioSlotTimeout: this.data.RadioSlotTimeout, - RadioSubMessage: this.data.RadioSubMessage, - RadioSyncState: Enum.Parse(this.data.RadioSyncState ?? throw new InvalidOperationException()), - RateOfTurn: this.data.RateOfTurn, - RaimFlag: this.data.RaimFlag, - RepeatIndicator: this.data.RepeatIndicator, - SpareBits145: this.data.SpareBits145, - SpeedOverGround: this.data.SpeedOverGround, - TimeStampSecond: this.data.TimeStampSecond, - TrueHeadingDegrees: this.data.TrueHeadingDegrees - ); - } - - [Then("the AisMessageType1Through3 properties should be set correctly")] - public void ThenThePropertiesShouldBeSetCorrectly() - { - var sut = (AisMessageType1Through3?)this.Message; - sut.ShouldNotBeNull(); - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - sut.CourseOverGround.ShouldBe(this.data.CourseOverGround); - sut.ManoeuvreIndicator.ShouldBe(Enum.Parse(this.data.ManoeuvreIndicator ?? throw new InvalidOperationException())); - sut.MessageType.ShouldBe(this.data.MessageType); - sut.Mmsi.ShouldBe(this.data.Mmsi); - sut.NavigationStatus.ShouldBe(Enum.Parse(this.data.NavigationStatus ?? throw new InvalidOperationException())); - sut.Position.ShouldBe(new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0)); - sut.PositionAccuracy.ShouldBe(this.data.PositionAccuracy); - sut.RadioSlotTimeout.ShouldBe(this.data.RadioSlotTimeout); - sut.RadioSubMessage.ShouldBe(this.data.RadioSubMessage); - sut.RadioSyncState.ShouldBe(Enum.Parse(this.data.RadioSyncState ?? throw new InvalidOperationException())); - sut.RateOfTurn.ShouldBe(this.data.RateOfTurn); - sut.RaimFlag.ShouldBe(this.data.RaimFlag); - sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); - sut.SpareBits145.ShouldBe(this.data.SpareBits145); - sut.SpeedOverGround.ShouldBe(this.data.SpeedOverGround); - sut.TimeStampSecond.ShouldBe(this.data.TimeStampSecond); - sut.TrueHeadingDegrees.ShouldBe(this.data.TrueHeadingDegrees); - } - - private class AisMessageType1Through3Data - { - public float? CourseOverGround { get; set; } - public string? ManoeuvreIndicator { get; set; } - public int MessageType { get; set; } - public uint Mmsi { get; set; } - public string? NavigationStatus { get; set; } - public PositionData? Position { get; set; } - public bool PositionAccuracy { get; set; } - public uint RadioSlotTimeout { get; set; } - public uint RadioSubMessage { get; set; } - public string? RadioSyncState { get; set; } - public int RateOfTurn { get; set; } - public bool RaimFlag { get; set; } - public uint RepeatIndicator { get; set; } - public uint SpareBits145 { get; set; } - public float? SpeedOverGround { get; set; } - public uint TimeStampSecond { get; set; } - public uint TrueHeadingDegrees { get; set; } - } - - private class PositionData - { - public double Latitude { get; set; } - public double Longitude { get; set; } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3Tests.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3Tests.cs new file mode 100644 index 0000000..a4a7503 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType1Through3Tests.cs @@ -0,0 +1,61 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class AisMessageType1Through3Tests +{ + [TestMethod] + [DataRow(123.45f, NavigationStatus.UnderwayUsingEngine, 12345u, 1.0, 2.0, 12.34f)] + [DataRow(234.56f, NavigationStatus.AtAnchor, 54321u, 3.0, 4.0, 0.0f)] + public void ClassAVesselPositionReportExposesSuppliedValues( + float courseOverGround, + NavigationStatus navigationStatus, + uint mmsi, + double latitude, + double longitude, + float speedOverGround) + { + AisMessageType1Through3 message = new( + CourseOverGround: courseOverGround, + ManoeuvreIndicator: ManoeuvreIndicator.NotAvailable, + MessageType: 1, + Mmsi: mmsi, + NavigationStatus: navigationStatus, + Position: new Position(latitude, longitude), + PositionAccuracy: true, + RadioSlotTimeout: 1, + RadioSubMessage: 2, + RadioSyncState: RadioSyncState.UtcDirect, + RateOfTurn: 1, + RaimFlag: true, + RepeatIndicator: 3, + SpareBits145: 4, + SpeedOverGround: speedOverGround, + TimeStampSecond: 56, + TrueHeadingDegrees: 78); + + message.CourseOverGround.ShouldBe(courseOverGround); + message.ManoeuvreIndicator.ShouldBe(ManoeuvreIndicator.NotAvailable); + message.MessageType.ShouldBe(1); + message.Mmsi.ShouldBe(mmsi); + message.NavigationStatus.ShouldBe(navigationStatus); + message.Position.ShouldBe(new Position(latitude, longitude)); + message.PositionAccuracy.ShouldBe(true); + message.RadioSlotTimeout.ShouldBe(1u); + message.RadioSubMessage.ShouldBe(2u); + message.RadioSyncState.ShouldBe(RadioSyncState.UtcDirect); + message.RateOfTurn.ShouldBe(1); + message.RaimFlag.ShouldBe(true); + message.RepeatIndicator.ShouldBe(3u); + message.SpareBits145.ShouldBe(4u); + message.SpeedOverGround.ShouldBe(speedOverGround); + message.TimeStampSecond.ShouldBe(56u); + message.TrueHeadingDegrees.ShouldBe(78u); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs deleted file mode 100644 index dc72039..0000000 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0StepDefinitions.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace Ais.Net.Models.Specs; - -using System; - -using Reqnroll; - -using Shouldly; - -[Binding] -public class AisMessageType24Part0StepDefinitions : StepDefinitionBase -{ - private AisMessageType24Part0Data? data; - - [Given("a new AisMessageType24Part0 record with the following properties:")] - public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) - { - this.data = table.CreateInstance(); - } - - [When("the AisMessageType24Part0 is created")] - public void WhenTheAisMessageTypeIsCreated() - { - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - this.Message = new AisMessageType24Part0( - Mmsi: this.data.Mmsi, - PartNumber: this.data.PartNumber, - RepeatIndicator: this.data.RepeatIndicator, - Spare160: this.data.Spare160 - ); - } - - [Then("the AisMessageType24Part0 properties should be set correctly")] - public void ThenThePropertiesShouldBeSetCorrectly() - { - AisMessageType24Part0? sut = this.Message as AisMessageType24Part0; - - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - sut.ShouldNotBeNull(); - sut.Mmsi.ShouldBe(this.data.Mmsi); - sut.PartNumber.ShouldBe(this.data.PartNumber); - sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); - sut.Spare160.ShouldBe(this.data.Spare160); - } - - private class AisMessageType24Part0Data - { - public uint Mmsi { get; set; } - public uint PartNumber { get; set; } - public uint RepeatIndicator { get; set; } - public uint Spare160 { get; set; } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0Tests.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0Tests.cs new file mode 100644 index 0000000..a696c11 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part0Tests.cs @@ -0,0 +1,27 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class AisMessageType24Part0Tests +{ + [TestMethod] + public void StaticDataReportPartAExposesSuppliedValues() + { + AisMessageType24Part0 message = new( + Mmsi: 12345, + PartNumber: 0, + RepeatIndicator: 3, + Spare160: 1); + + message.MessageType.ShouldBe(24); // both parts of a type 24 report share message type 24 + message.Mmsi.ShouldBe(12345u); + message.PartNumber.ShouldBe(0u); + message.RepeatIndicator.ShouldBe(3u); + message.Spare160.ShouldBe(1u); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs deleted file mode 100644 index 96a2443..0000000 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1StepDefinitions.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace Ais.Net.Models.Specs; - -using System; - -using Reqnroll; - -using Shouldly; - -[Binding] -public class AisMessageType24Part1StepDefinitions : StepDefinitionBase -{ - private AisMessageType24Part1Data? data; - - [Given("a new AisMessageType24Part1 record with the following properties:")] - public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) - { - this.data = table.CreateInstance(); - } - - [When("the AisMessageType24Part1 is created")] - public void WhenTheAisMessageTypeIsCreated() - { - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - this.Message = new AisMessageType24Part1( - CallSign: this.data.CallSign ?? throw new InvalidOperationException(), - DimensionToBow: this.data.DimensionToBow, - DimensionToPort: this.data.DimensionToPort, - DimensionToStarboard: this.data.DimensionToStarboard, - DimensionToStern: this.data.DimensionToStern, - Mmsi: this.data.Mmsi, - MothershipMmsi: this.data.MothershipMmsi, - PartNumber: this.data.PartNumber, - RepeatIndicator: this.data.RepeatIndicator, - SerialNumber: this.data.SerialNumber, - Spare162: this.data.Spare162, - ShipType: (ShipType)this.data.ShipType, - UnitModelCode: this.data.UnitModelCode, - VendorIdRev3: this.data.VendorIdRev3 ?? throw new InvalidOperationException(), - VendorIdRev4: this.data.VendorIdRev4 ?? throw new InvalidOperationException() - ); - } - - [Then("the AisMessageType24Part1 properties should be set correctly")] - public void ThenThePropertiesShouldBeSetCorrectly() - { - AisMessageType24Part1? sut = this.Message as AisMessageType24Part1; - - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - sut.ShouldNotBeNull(); - sut.CallSign.ShouldBe(this.data.CallSign); - sut.DimensionToBow.ShouldBe(this.data.DimensionToBow); - sut.DimensionToPort.ShouldBe(this.data.DimensionToPort); - sut.DimensionToStarboard.ShouldBe(this.data.DimensionToStarboard); - sut.DimensionToStern.ShouldBe(this.data.DimensionToStern); - sut.Mmsi.ShouldBe(this.data.Mmsi); - sut.MothershipMmsi.ShouldBe(this.data.MothershipMmsi); - sut.PartNumber.ShouldBe(this.data.PartNumber); - sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); - sut.SerialNumber.ShouldBe(this.data.SerialNumber); - sut.Spare162.ShouldBe(this.data.Spare162); - ((int)sut.ShipType).ShouldBe(this.data.ShipType); - sut.UnitModelCode.ShouldBe(this.data.UnitModelCode); - sut.VendorIdRev3.ShouldBe(this.data.VendorIdRev3); - sut.VendorIdRev4.ShouldBe(this.data.VendorIdRev4); - } - - private class AisMessageType24Part1Data - { - public string? CallSign { get; set; } - public uint DimensionToBow { get; set; } - public uint DimensionToPort { get; set; } - public uint DimensionToStarboard { get; set; } - public uint DimensionToStern { get; set; } - public uint Mmsi { get; set; } - public uint MothershipMmsi { get; set; } - public uint PartNumber { get; set; } - public uint RepeatIndicator { get; set; } - public uint SerialNumber { get; set; } - public uint Spare162 { get; set; } - public int ShipType { get; set; } - public uint UnitModelCode { get; set; } - public string? VendorIdRev3 { get; set; } - public string? VendorIdRev4 { get; set; } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1Tests.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1Tests.cs new file mode 100644 index 0000000..4153435 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType24Part1Tests.cs @@ -0,0 +1,50 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class AisMessageType24Part1Tests +{ + [TestMethod] + public void StaticDataReportPartBExposesSuppliedValues() + { + AisMessageType24Part1 message = new( + CallSign: "CALL", + DimensionToBow: 1, + DimensionToPort: 2, + DimensionToStarboard: 3, + DimensionToStern: 4, + Mmsi: 12345, + MothershipMmsi: 54321, + PartNumber: 1, + RepeatIndicator: 3, + SerialNumber: 123, + Spare162: 1, + ShipType: (ShipType)60, + UnitModelCode: 1, + VendorIdRev3: "VEN", + VendorIdRev4: "DOR"); + + message.MessageType.ShouldBe(24); // both parts of a type 24 report share message type 24 + message.CallSign.ShouldBe("CALL"); + message.DimensionToBow.ShouldBe(1u); + message.DimensionToPort.ShouldBe(2u); + message.DimensionToStarboard.ShouldBe(3u); + message.DimensionToStern.ShouldBe(4u); + message.Mmsi.ShouldBe(12345u); + message.MothershipMmsi.ShouldBe(54321u); + message.PartNumber.ShouldBe(1u); + message.RepeatIndicator.ShouldBe(3u); + message.SerialNumber.ShouldBe(123u); + message.Spare162.ShouldBe(1u); + message.ShipType.ShouldBe((ShipType)60); + message.UnitModelCode.ShouldBe(1u); + message.VendorIdRev3.ShouldBe("VEN"); + message.VendorIdRev4.ShouldBe("DOR"); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs deleted file mode 100644 index 0f640c6..0000000 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType27StepDefinitions.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace Ais.Net.Models.Specs; - -using System; - -using Abstractions; - -using Reqnroll; - -using Shouldly; - -[Binding] -public class AisMessageType27StepDefinitions : StepDefinitionBase -{ - private AisMessageType27Data? data; - - [Given("a new AisMessageType27 record with the following properties:")] - public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) - { - PositionData position = new() - { - Latitude = Convert.ToDouble(table.Rows[0]["Position_Latitude"]), - Longitude = Convert.ToDouble(table.Rows[0]["Position_Longitude"]) - }; - - this.data = table.CreateInstance(); - this.data.Position = position; - } - - [When("the AisMessageType27 is created")] - public void WhenTheAisMessageTypeIsCreated() - { - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - this.Message = new AisMessageType27( - CourseOverGround: this.data.CourseOverGround, - GnssPositionStatus: this.data.GnssPositionStatus, - Mmsi: this.data.Mmsi, - NavigationStatus: Enum.Parse(this.data.NavigationStatus ?? throw new InvalidOperationException()), - Position: new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0), - PositionAccuracy: this.data.PositionAccuracy, - RaimFlag: this.data.RaimFlag, - RepeatIndicator: this.data.RepeatIndicator, - SpeedOverGround: this.data.SpeedOverGround - ); - } - - [Then("the AisMessageType27 properties should be set correctly")] - public void ThenThePropertiesShouldBeSetCorrectly() - { - AisMessageType27? sut = this.Message as AisMessageType27; - - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - sut.ShouldNotBeNull(); - sut.CourseOverGround.ShouldBe(this.data.CourseOverGround); - sut.GnssPositionStatus.ShouldBe(this.data.GnssPositionStatus); - sut.Mmsi.ShouldBe(this.data.Mmsi); - sut.NavigationStatus.ShouldBe(Enum.Parse(this.data.NavigationStatus ?? throw new InvalidOperationException())); - sut.Position.ShouldBe(new Position(this.data.Position?.Latitude ?? 0, this.data.Position?.Longitude ?? 0)); - sut.PositionAccuracy.ShouldBe(this.data.PositionAccuracy); - sut.RaimFlag.ShouldBe(this.data.RaimFlag); - sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); - sut.SpeedOverGround.ShouldBe(this.data.SpeedOverGround); - } - - private class AisMessageType27Data - { - public float? CourseOverGround { get; set; } - public bool GnssPositionStatus { get; set; } - public uint Mmsi { get; set; } - public string? NavigationStatus { get; set; } - public PositionData? Position { get; set; } - public bool PositionAccuracy { get; set; } - public bool RaimFlag { get; set; } - public uint RepeatIndicator { get; set; } - public float? SpeedOverGround { get; set; } - } - - private class PositionData - { - public double Latitude { get; set; } - public double Longitude { get; set; } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType27Tests.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType27Tests.cs new file mode 100644 index 0000000..14ae355 --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType27Tests.cs @@ -0,0 +1,38 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class AisMessageType27Tests +{ + [TestMethod] + public void LongRangeAisBroadcastMessageExposesSuppliedValues() + { + AisMessageType27 message = new( + CourseOverGround: 123.45f, + GnssPositionStatus: true, + Mmsi: 12345, + NavigationStatus: NavigationStatus.UnderwayUsingEngine, + Position: new Position(1.0, 2.0), + PositionAccuracy: true, + RaimFlag: true, + RepeatIndicator: 3, + SpeedOverGround: 12.34f); + + message.MessageType.ShouldBe(27); + message.CourseOverGround.ShouldBe(123.45f); + message.GnssPositionStatus.ShouldBe(true); + message.Mmsi.ShouldBe(12345u); + message.NavigationStatus.ShouldBe(NavigationStatus.UnderwayUsingEngine); + message.Position.ShouldBe(new Position(1.0, 2.0)); + message.PositionAccuracy.ShouldBe(true); + message.RaimFlag.ShouldBe(true); + message.RepeatIndicator.ShouldBe(3u); + message.SpeedOverGround.ShouldBe(12.34f); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs deleted file mode 100644 index 12f7d7c..0000000 --- a/Solutions/Ais.Net.Models.Specs/AisMessageType5StepDefinitions.cs +++ /dev/null @@ -1,108 +0,0 @@ -namespace Ais.Net.Models.Specs; - -using System; - -using Reqnroll; - -using Shouldly; - -[Binding] -public class AisMessageType5StepDefinitions : StepDefinitionBase -{ - private AisMessageType5Data? data; - - [Given("a new AisMessageType5 record with the following properties:")] - public void GivenANewAisMessageTypeRecordWithTheFollowingProperties(DataTable table) - { - this.data = table.CreateInstance(); - } - - [When("the AisMessageType5 is created")] - public void WhenTheAisMessageTypeIsCreated() - { - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - this.Message = new AisMessageType5( - AisVersion: this.data.AisVersion, - CallSign: this.data.CallSign ?? throw new InvalidOperationException(), - Destination: this.data.Destination ?? throw new InvalidOperationException(), - DimensionToBow: this.data.DimensionToBow, - DimensionToPort: this.data.DimensionToPort, - DimensionToStarboard: this.data.DimensionToStarboard, - DimensionToStern: this.data.DimensionToStern, - Draught10thMetres: this.data.Draught10thMetres, - EtaDay: this.data.EtaDay, - EtaHour: this.data.EtaHour, - EtaMinute: this.data.EtaMinute, - EtaMonth: this.data.EtaMonth, - IsDteNotReady: this.data.IsDteNotReady, - ImoNumber: this.data.ImoNumber, - Mmsi: this.data.Mmsi, - PositionFixType: Enum.Parse(this.data.PositionFixType ?? throw new InvalidOperationException()), - RepeatIndicator: this.data.RepeatIndicator, - ShipType: (ShipType)this.data.ShipType, - Spare423: this.data.Spare423, - VesselName: this.data.VesselName ?? throw new InvalidOperationException() - ); - } - - [Then("the AisMessageType5 properties should be set correctly")] - public void ThenThePropertiesShouldBeSetCorrectly() - { - AisMessageType5? sut = this.Message as AisMessageType5; - - if (this.data is null) - { - throw new InvalidOperationException("Data is not set"); - } - - sut.ShouldNotBeNull(); - sut.AisVersion.ShouldBe(this.data.AisVersion); - sut.CallSign.ShouldBe(this.data.CallSign); - sut.Destination.ShouldBe(this.data.Destination); - sut.DimensionToBow.ShouldBe(this.data.DimensionToBow); - sut.DimensionToPort.ShouldBe(this.data.DimensionToPort); - sut.DimensionToStarboard.ShouldBe(this.data.DimensionToStarboard); - sut.DimensionToStern.ShouldBe(this.data.DimensionToStern); - sut.Draught10thMetres.ShouldBe(this.data.Draught10thMetres); - sut.EtaDay.ShouldBe(this.data.EtaDay); - sut.EtaHour.ShouldBe(this.data.EtaHour); - sut.EtaMinute.ShouldBe(this.data.EtaMinute); - sut.EtaMonth.ShouldBe(this.data.EtaMonth); - sut.IsDteNotReady.ShouldBe(this.data.IsDteNotReady); - sut.ImoNumber.ShouldBe(this.data.ImoNumber); - sut.Mmsi.ShouldBe(this.data.Mmsi); - sut.PositionFixType.ShouldBe(Enum.Parse(this.data.PositionFixType ?? throw new InvalidOperationException())); - sut.RepeatIndicator.ShouldBe(this.data.RepeatIndicator); - ((int)sut.ShipType).ShouldBe(this.data.ShipType); - sut.Spare423.ShouldBe(this.data.Spare423); - sut.VesselName.ShouldBe(this.data.VesselName); - } - - private class AisMessageType5Data - { - public uint AisVersion { get; set; } - public string? CallSign { get; set; } - public string? Destination { get; set; } - public uint DimensionToBow { get; set; } - public uint DimensionToPort { get; set; } - public uint DimensionToStarboard { get; set; } - public uint DimensionToStern { get; set; } - public uint Draught10thMetres { get; set; } - public uint EtaDay { get; set; } - public uint EtaHour { get; set; } - public uint EtaMinute { get; set; } - public uint EtaMonth { get; set; } - public bool IsDteNotReady { get; set; } - public uint ImoNumber { get; set; } - public uint Mmsi { get; set; } - public string? PositionFixType { get; set; } - public uint RepeatIndicator { get; set; } - public int ShipType { get; set; } - public uint Spare423 { get; set; } - public string? VesselName { get; set; } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/AisMessageType5Tests.cs b/Solutions/Ais.Net.Models.Specs/AisMessageType5Tests.cs new file mode 100644 index 0000000..998bf3e --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AisMessageType5Tests.cs @@ -0,0 +1,60 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class AisMessageType5Tests +{ + [TestMethod] + public void StaticAndVoyageRelatedDataExposesSuppliedValues() + { + AisMessageType5 message = new( + AisVersion: 1, + CallSign: "CALL", + Destination: "DEST", + DimensionToBow: 1, + DimensionToPort: 2, + DimensionToStarboard: 3, + DimensionToStern: 4, + Draught10thMetres: 5, + EtaDay: 6, + EtaHour: 7, + EtaMinute: 8, + EtaMonth: 9, + IsDteNotReady: true, + ImoNumber: 123, + Mmsi: 12345, + PositionFixType: EpfdFixType.Gps, + RepeatIndicator: 3, + ShipType: (ShipType)60, + Spare423: 1, + VesselName: "VESSEL"); + + message.MessageType.ShouldBe(5); + message.AisVersion.ShouldBe(1u); + message.CallSign.ShouldBe("CALL"); + message.Destination.ShouldBe("DEST"); + message.DimensionToBow.ShouldBe(1u); + message.DimensionToPort.ShouldBe(2u); + message.DimensionToStarboard.ShouldBe(3u); + message.DimensionToStern.ShouldBe(4u); + message.Draught10thMetres.ShouldBe(5u); + message.EtaDay.ShouldBe(6u); + message.EtaHour.ShouldBe(7u); + message.EtaMinute.ShouldBe(8u); + message.EtaMonth.ShouldBe(9u); + message.IsDteNotReady.ShouldBe(true); + message.ImoNumber.ShouldBe(123u); + message.Mmsi.ShouldBe(12345u); + message.PositionFixType.ShouldBe(EpfdFixType.Gps); + message.RepeatIndicator.ShouldBe(3u); + message.ShipType.ShouldBe((ShipType)60); + message.Spare423.ShouldBe(1u); + message.VesselName.ShouldBe("VESSEL"); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/AssemblyInfo.cs b/Solutions/Ais.Net.Models.Specs/AssemblyInfo.cs new file mode 100644 index 0000000..15e991d --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +// MSTest 4.0 requires explicit parallelization configuration (MSTEST0001). +// Workers = 0 uses all available cores; MethodLevel allows tests to run concurrently. +[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.MethodLevel)] diff --git a/Solutions/Ais.Net.Models.Specs/CommonStepDefinitions.cs b/Solutions/Ais.Net.Models.Specs/CommonStepDefinitions.cs deleted file mode 100644 index 81d1b04..0000000 --- a/Solutions/Ais.Net.Models.Specs/CommonStepDefinitions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Reqnroll; - -namespace Ais.Net.Models.Specs -{ - [Binding] - public class CommonStepDefinitions - { - [Given(@"I have an AIS message")] - public void GivenIHaveAnAisMessage() - { - // This is a common background step that does nothing - // The actual message creation happens in the specific "Given" steps - } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature deleted file mode 100644 index 3bbf1d5..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature +++ /dev/null @@ -1,15 +0,0 @@ -Feature: Class B "CS" Position Report - -Background: - Given I have an AIS message - -Scenario Outline: Decoding a Class B "CS" position report - Given a new AisMessageType18 record with the following properties: - | CanAcceptMessage22ChannelAssignment | CanSwitchBands | CourseOverGroundDegrees | CsUnit | HasDisplay | IsAssigned | IsDscAttached | Mmsi | Position_Latitude | Position_Longitude | PositionAccuracy | RadioStatusType | RaimFlag | RegionalReserved139 | RegionalReserved38 | RepeatIndicator | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | - | | | | | | | | | | | | | | | | | | | | - When the AisMessageType is created - Then the properties should be set correctly - - Examples: - | can_accept_msg22 | can_switch_bands | course | cs_unit | has_display | is_assigned | is_dsc_attached | mmsi | lat | long | pos_accuracy | radio_status | raim | reg_res_139 | reg_res_38 | repeat | speed | timestamp | true_heading | - | true | true | 123.45 | Cstdma | true | true | true | 12345 | 1.0 | 2.0 | true | Itdma | true | 1 | 2 | 3 | 12.34 | 56 | 78 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs deleted file mode 100644 index e3988d0..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType18.feature.cs +++ /dev/null @@ -1,229 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by Reqnroll (https://www.reqnroll.net/). -// Reqnroll Version:2.0.0.0 -// Reqnroll Generator Version:2.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -using Reqnroll; -namespace Ais.Net.Models.Specs.Features -{ - - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("Class B \"CS\" Position Report")] - [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class ClassBCSPositionReportFeature - { - - private global::Reqnroll.ITestRunner testRunner; - - private static string[] featureTags = ((string[])(null)); - - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Class B \"CS\" Position Report", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); - -#line 1 "AisMessageType18.feature" -#line hidden - - [NUnit.Framework.OneTimeSetUpAttribute()] - public static async global::System.Threading.Tasks.Task FeatureSetupAsync() - { - } - - [NUnit.Framework.OneTimeTearDownAttribute()] - public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() - { - } - - [NUnit.Framework.SetUpAttribute()] - public async global::System.Threading.Tasks.Task TestInitializeAsync() - { - testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); - try - { - if (((testRunner.FeatureContext != null) - && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) - { - await testRunner.OnFeatureEndAsync(); - } - } - finally - { - if (((testRunner.FeatureContext != null) - && testRunner.FeatureContext.BeforeFeatureHookFailed)) - { - throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); - } - if ((testRunner.FeatureContext == null)) - { - await testRunner.OnFeatureStartAsync(featureInfo); - } - } - } - - [NUnit.Framework.TearDownAttribute()] - public async global::System.Threading.Tasks.Task TestTearDownAsync() - { - if ((testRunner == null)) - { - return; - } - try - { - await testRunner.OnScenarioEndAsync(); - } - finally - { - global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); - testRunner = null; - } - } - - public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioInitialize(scenarioInfo); - testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); - } - - public async global::System.Threading.Tasks.Task ScenarioStartAsync() - { - await testRunner.OnScenarioStartAsync(); - } - - public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() - { - await testRunner.CollectScenarioErrorsAsync(); - } - - public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() - { -#line 3 -#line hidden -#line 4 - await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); -#line hidden - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Decoding a Class B \"CS\" position report")] - [NUnit.Framework.TestCaseAttribute("true", "true", "123.45", "Cstdma", "true", "true", "true", "12345", "1.0", "2.0", "true", "Itdma", "true", "1", "2", "3", "12.34", "56", "78", null)] - public async global::System.Threading.Tasks.Task DecodingAClassBCSPositionReport( - string can_Accept_Msg22, - string can_Switch_Bands, - string course, - string cs_Unit, - string has_Display, - string is_Assigned, - string is_Dsc_Attached, - string mmsi, - string lat, - string @long, - string pos_Accuracy, - string radio_Status, - string raim, - string reg_Res_139, - string reg_Res_38, - string repeat, - string speed, - string timestamp, - string true_Heading, - string[] exampleTags) - { - string[] tagsOfScenario = exampleTags; - global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - argumentsOfScenario.Add("can_accept_msg22", can_Accept_Msg22); - argumentsOfScenario.Add("can_switch_bands", can_Switch_Bands); - argumentsOfScenario.Add("course", course); - argumentsOfScenario.Add("cs_unit", cs_Unit); - argumentsOfScenario.Add("has_display", has_Display); - argumentsOfScenario.Add("is_assigned", is_Assigned); - argumentsOfScenario.Add("is_dsc_attached", is_Dsc_Attached); - argumentsOfScenario.Add("mmsi", mmsi); - argumentsOfScenario.Add("lat", lat); - argumentsOfScenario.Add("long", @long); - argumentsOfScenario.Add("pos_accuracy", pos_Accuracy); - argumentsOfScenario.Add("radio_status", radio_Status); - argumentsOfScenario.Add("raim", raim); - argumentsOfScenario.Add("reg_res_139", reg_Res_139); - argumentsOfScenario.Add("reg_res_38", reg_Res_38); - argumentsOfScenario.Add("repeat", repeat); - argumentsOfScenario.Add("speed", speed); - argumentsOfScenario.Add("timestamp", timestamp); - argumentsOfScenario.Add("true_heading", true_Heading); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Class B \"CS\" position report", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 6 -this.ScenarioInitialize(scenarioInfo); -#line hidden - if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) - { - testRunner.SkipScenario(); - } - else - { - await this.ScenarioStartAsync(); -#line 3 -await this.FeatureBackgroundAsync(); -#line hidden - global::Reqnroll.Table table1 = new global::Reqnroll.Table(new string[] { - "CanAcceptMessage22ChannelAssignment", - "CanSwitchBands", - "CourseOverGroundDegrees", - "CsUnit", - "HasDisplay", - "IsAssigned", - "IsDscAttached", - "Mmsi", - "Position_Latitude", - "Position_Longitude", - "PositionAccuracy", - "RadioStatusType", - "RaimFlag", - "RegionalReserved139", - "RegionalReserved38", - "RepeatIndicator", - "SpeedOverGround", - "TimeStampSecond", - "TrueHeadingDegrees"}); - table1.AddRow(new string[] { - string.Format("{0}", can_Accept_Msg22), - string.Format("{0}", can_Switch_Bands), - string.Format("{0}", course), - string.Format("{0}", cs_Unit), - string.Format("{0}", has_Display), - string.Format("{0}", is_Assigned), - string.Format("{0}", is_Dsc_Attached), - string.Format("{0}", mmsi), - string.Format("{0}", lat), - string.Format("{0}", @long), - string.Format("{0}", pos_Accuracy), - string.Format("{0}", radio_Status), - string.Format("{0}", raim), - string.Format("{0}", reg_Res_139), - string.Format("{0}", reg_Res_38), - string.Format("{0}", repeat), - string.Format("{0}", speed), - string.Format("{0}", timestamp), - string.Format("{0}", true_Heading)}); -#line 7 - await testRunner.GivenAsync("a new AisMessageType18 record with the following properties:", ((string)(null)), table1, "Given "); -#line hidden -#line 10 - await testRunner.WhenAsync("the AisMessageType is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); -#line hidden -#line 11 - await testRunner.ThenAsync("the properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); -#line hidden - } - await this.ScenarioCleanupAsync(); - } - } -} -#pragma warning restore -#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature deleted file mode 100644 index 4cd3dea..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature +++ /dev/null @@ -1,15 +0,0 @@ -Feature: Class B "SO" Position Report - -Background: - Given I have an AIS message - -Scenario Outline: Decoding a Class B "SO" position report - Given a new AisMessageType19 record with the following properties: - | CourseOverGround | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | IsAssigned | IsDteNotReady | Mmsi | Position_Latitude | Position_Longitude | PositionAccuracy | PositionFixType | RaimFlag | RegionalReserved139 | RegionalReserved38 | RepeatIndicator | ShipName | ShipType | Spare308 | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | - | | | | | | | | | | | | | | | | | | | | | | | - When the AisMessageType19 is created - Then the AisMessageType19 properties should be set correctly - - Examples: - | course | dim_bow | dim_port | dim_starboard | dim_stern | is_assigned | dte_not_ready | mmsi | lat | long | pos_accuracy | pos_fix | raim | reg_res_139 | reg_res_38 | repeat | ship_name | ship_type | spare | speed | timestamp | true_heading | - | 123.45 | 1 | 2 | 3 | 4 | true | true | 12345 | 1.0 | 2.0 | true | Gps | true | 1 | 2 | 3 | SHIP | 60 | 1 | 12.34 | 56 | 78 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs deleted file mode 100644 index 1ec9637..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType19.feature.cs +++ /dev/null @@ -1,241 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by Reqnroll (https://www.reqnroll.net/). -// Reqnroll Version:2.0.0.0 -// Reqnroll Generator Version:2.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -using Reqnroll; -namespace Ais.Net.Models.Specs.Features -{ - - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("Class B \"SO\" Position Report")] - [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class ClassBSOPositionReportFeature - { - - private global::Reqnroll.ITestRunner testRunner; - - private static string[] featureTags = ((string[])(null)); - - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Class B \"SO\" Position Report", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); - -#line 1 "AisMessageType19.feature" -#line hidden - - [NUnit.Framework.OneTimeSetUpAttribute()] - public static async global::System.Threading.Tasks.Task FeatureSetupAsync() - { - } - - [NUnit.Framework.OneTimeTearDownAttribute()] - public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() - { - } - - [NUnit.Framework.SetUpAttribute()] - public async global::System.Threading.Tasks.Task TestInitializeAsync() - { - testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); - try - { - if (((testRunner.FeatureContext != null) - && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) - { - await testRunner.OnFeatureEndAsync(); - } - } - finally - { - if (((testRunner.FeatureContext != null) - && testRunner.FeatureContext.BeforeFeatureHookFailed)) - { - throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); - } - if ((testRunner.FeatureContext == null)) - { - await testRunner.OnFeatureStartAsync(featureInfo); - } - } - } - - [NUnit.Framework.TearDownAttribute()] - public async global::System.Threading.Tasks.Task TestTearDownAsync() - { - if ((testRunner == null)) - { - return; - } - try - { - await testRunner.OnScenarioEndAsync(); - } - finally - { - global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); - testRunner = null; - } - } - - public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioInitialize(scenarioInfo); - testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); - } - - public async global::System.Threading.Tasks.Task ScenarioStartAsync() - { - await testRunner.OnScenarioStartAsync(); - } - - public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() - { - await testRunner.CollectScenarioErrorsAsync(); - } - - public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() - { -#line 3 -#line hidden -#line 4 - await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); -#line hidden - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Decoding a Class B \"SO\" position report")] - [NUnit.Framework.TestCaseAttribute("123.45", "1", "2", "3", "4", "true", "true", "12345", "1.0", "2.0", "true", "Gps", "true", "1", "2", "3", "SHIP", "60", "1", "12.34", "56", "78", null)] - public async global::System.Threading.Tasks.Task DecodingAClassBSOPositionReport( - string course, - string dim_Bow, - string dim_Port, - string dim_Starboard, - string dim_Stern, - string is_Assigned, - string dte_Not_Ready, - string mmsi, - string lat, - string @long, - string pos_Accuracy, - string pos_Fix, - string raim, - string reg_Res_139, - string reg_Res_38, - string repeat, - string ship_Name, - string ship_Type, - string spare, - string speed, - string timestamp, - string true_Heading, - string[] exampleTags) - { - string[] tagsOfScenario = exampleTags; - global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - argumentsOfScenario.Add("course", course); - argumentsOfScenario.Add("dim_bow", dim_Bow); - argumentsOfScenario.Add("dim_port", dim_Port); - argumentsOfScenario.Add("dim_starboard", dim_Starboard); - argumentsOfScenario.Add("dim_stern", dim_Stern); - argumentsOfScenario.Add("is_assigned", is_Assigned); - argumentsOfScenario.Add("dte_not_ready", dte_Not_Ready); - argumentsOfScenario.Add("mmsi", mmsi); - argumentsOfScenario.Add("lat", lat); - argumentsOfScenario.Add("long", @long); - argumentsOfScenario.Add("pos_accuracy", pos_Accuracy); - argumentsOfScenario.Add("pos_fix", pos_Fix); - argumentsOfScenario.Add("raim", raim); - argumentsOfScenario.Add("reg_res_139", reg_Res_139); - argumentsOfScenario.Add("reg_res_38", reg_Res_38); - argumentsOfScenario.Add("repeat", repeat); - argumentsOfScenario.Add("ship_name", ship_Name); - argumentsOfScenario.Add("ship_type", ship_Type); - argumentsOfScenario.Add("spare", spare); - argumentsOfScenario.Add("speed", speed); - argumentsOfScenario.Add("timestamp", timestamp); - argumentsOfScenario.Add("true_heading", true_Heading); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Class B \"SO\" position report", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 6 -this.ScenarioInitialize(scenarioInfo); -#line hidden - if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) - { - testRunner.SkipScenario(); - } - else - { - await this.ScenarioStartAsync(); -#line 3 -await this.FeatureBackgroundAsync(); -#line hidden - global::Reqnroll.Table table2 = new global::Reqnroll.Table(new string[] { - "CourseOverGround", - "DimensionToBow", - "DimensionToPort", - "DimensionToStarboard", - "DimensionToStern", - "IsAssigned", - "IsDteNotReady", - "Mmsi", - "Position_Latitude", - "Position_Longitude", - "PositionAccuracy", - "PositionFixType", - "RaimFlag", - "RegionalReserved139", - "RegionalReserved38", - "RepeatIndicator", - "ShipName", - "ShipType", - "Spare308", - "SpeedOverGround", - "TimeStampSecond", - "TrueHeadingDegrees"}); - table2.AddRow(new string[] { - string.Format("{0}", course), - string.Format("{0}", dim_Bow), - string.Format("{0}", dim_Port), - string.Format("{0}", dim_Starboard), - string.Format("{0}", dim_Stern), - string.Format("{0}", is_Assigned), - string.Format("{0}", dte_Not_Ready), - string.Format("{0}", mmsi), - string.Format("{0}", lat), - string.Format("{0}", @long), - string.Format("{0}", pos_Accuracy), - string.Format("{0}", pos_Fix), - string.Format("{0}", raim), - string.Format("{0}", reg_Res_139), - string.Format("{0}", reg_Res_38), - string.Format("{0}", repeat), - string.Format("{0}", ship_Name), - string.Format("{0}", ship_Type), - string.Format("{0}", spare), - string.Format("{0}", speed), - string.Format("{0}", timestamp), - string.Format("{0}", true_Heading)}); -#line 7 - await testRunner.GivenAsync("a new AisMessageType19 record with the following properties:", ((string)(null)), table2, "Given "); -#line hidden -#line 10 - await testRunner.WhenAsync("the AisMessageType19 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); -#line hidden -#line 11 - await testRunner.ThenAsync("the AisMessageType19 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); -#line hidden - } - await this.ScenarioCleanupAsync(); - } - } -} -#pragma warning restore -#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature deleted file mode 100644 index cc49f35..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature +++ /dev/null @@ -1,16 +0,0 @@ -Feature: Class A Vessel Position Report - -Background: - Given I have an AIS message - -Scenario Outline: Decoding a Class A vessel position report - Given a new AisMessageType1Through3 record with the following properties: - | CourseOverGround | ManoeuvreIndicator | MessageType | Mmsi | NavigationStatus | Position_Latitude | Position_Longitude | PositionAccuracy | RadioSlotTimeout | RadioSubMessage | RadioSyncState | RateOfTurn | RaimFlag | RepeatIndicator | SpareBits145 | SpeedOverGround | TimeStampSecond | TrueHeadingDegrees | - | | | 1 | | | | | | 1 | 2 | UtcDirect | 1 | true | 3 | 4 | | 56 | 78 | - When the AisMessageType1Through3 is created - Then the AisMessageType1Through3 properties should be set correctly - - Examples: - | course | manoeuvre | mmsi | nav_status | lat | long | pos_accuracy | speed | - | 123.45 | NotAvailable | 12345 | UnderwayUsingEngine | 1.0 | 2.0 | true | 12.34 | - | 234.56 | NotAvailable | 54321 | AtAnchor | 3.0 | 4.0 | true | 0.0 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs deleted file mode 100644 index 5043597..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType1Through3.feature.cs +++ /dev/null @@ -1,197 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by Reqnroll (https://www.reqnroll.net/). -// Reqnroll Version:2.0.0.0 -// Reqnroll Generator Version:2.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -using Reqnroll; -namespace Ais.Net.Models.Specs.Features -{ - - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("Class A Vessel Position Report")] - [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class ClassAVesselPositionReportFeature - { - - private global::Reqnroll.ITestRunner testRunner; - - private static string[] featureTags = ((string[])(null)); - - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Class A Vessel Position Report", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); - -#line 1 "AisMessageType1Through3.feature" -#line hidden - - [NUnit.Framework.OneTimeSetUpAttribute()] - public static async global::System.Threading.Tasks.Task FeatureSetupAsync() - { - } - - [NUnit.Framework.OneTimeTearDownAttribute()] - public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() - { - } - - [NUnit.Framework.SetUpAttribute()] - public async global::System.Threading.Tasks.Task TestInitializeAsync() - { - testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); - try - { - if (((testRunner.FeatureContext != null) - && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) - { - await testRunner.OnFeatureEndAsync(); - } - } - finally - { - if (((testRunner.FeatureContext != null) - && testRunner.FeatureContext.BeforeFeatureHookFailed)) - { - throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); - } - if ((testRunner.FeatureContext == null)) - { - await testRunner.OnFeatureStartAsync(featureInfo); - } - } - } - - [NUnit.Framework.TearDownAttribute()] - public async global::System.Threading.Tasks.Task TestTearDownAsync() - { - if ((testRunner == null)) - { - return; - } - try - { - await testRunner.OnScenarioEndAsync(); - } - finally - { - global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); - testRunner = null; - } - } - - public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioInitialize(scenarioInfo); - testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); - } - - public async global::System.Threading.Tasks.Task ScenarioStartAsync() - { - await testRunner.OnScenarioStartAsync(); - } - - public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() - { - await testRunner.CollectScenarioErrorsAsync(); - } - - public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() - { -#line 3 -#line hidden -#line 4 - await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); -#line hidden - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Decoding a Class A vessel position report")] - [NUnit.Framework.TestCaseAttribute("123.45", "NotAvailable", "12345", "UnderwayUsingEngine", "1.0", "2.0", "true", "12.34", null)] - [NUnit.Framework.TestCaseAttribute("234.56", "NotAvailable", "54321", "AtAnchor", "3.0", "4.0", "true", "0.0", null)] - public async global::System.Threading.Tasks.Task DecodingAClassAVesselPositionReport(string course, string manoeuvre, string mmsi, string nav_Status, string lat, string @long, string pos_Accuracy, string speed, string[] exampleTags) - { - string[] tagsOfScenario = exampleTags; - global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - argumentsOfScenario.Add("course", course); - argumentsOfScenario.Add("manoeuvre", manoeuvre); - argumentsOfScenario.Add("mmsi", mmsi); - argumentsOfScenario.Add("nav_status", nav_Status); - argumentsOfScenario.Add("lat", lat); - argumentsOfScenario.Add("long", @long); - argumentsOfScenario.Add("pos_accuracy", pos_Accuracy); - argumentsOfScenario.Add("speed", speed); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Class A vessel position report", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 6 -this.ScenarioInitialize(scenarioInfo); -#line hidden - if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) - { - testRunner.SkipScenario(); - } - else - { - await this.ScenarioStartAsync(); -#line 3 -await this.FeatureBackgroundAsync(); -#line hidden - global::Reqnroll.Table table3 = new global::Reqnroll.Table(new string[] { - "CourseOverGround", - "ManoeuvreIndicator", - "MessageType", - "Mmsi", - "NavigationStatus", - "Position_Latitude", - "Position_Longitude", - "PositionAccuracy", - "RadioSlotTimeout", - "RadioSubMessage", - "RadioSyncState", - "RateOfTurn", - "RaimFlag", - "RepeatIndicator", - "SpareBits145", - "SpeedOverGround", - "TimeStampSecond", - "TrueHeadingDegrees"}); - table3.AddRow(new string[] { - string.Format("{0}", course), - string.Format("{0}", manoeuvre), - "1", - string.Format("{0}", mmsi), - string.Format("{0}", nav_Status), - string.Format("{0}", lat), - string.Format("{0}", @long), - string.Format("{0}", pos_Accuracy), - "1", - "2", - "UtcDirect", - "1", - "true", - "3", - "4", - string.Format("{0}", speed), - "56", - "78"}); -#line 7 - await testRunner.GivenAsync("a new AisMessageType1Through3 record with the following properties:", ((string)(null)), table3, "Given "); -#line hidden -#line 10 - await testRunner.WhenAsync("the AisMessageType1Through3 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); -#line hidden -#line 11 - await testRunner.ThenAsync("the AisMessageType1Through3 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); -#line hidden - } - await this.ScenarioCleanupAsync(); - } - } -} -#pragma warning restore -#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature deleted file mode 100644 index 3b65ae4..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature +++ /dev/null @@ -1,15 +0,0 @@ -Feature: Static Data Report Part A - -Background: - Given I have an AIS message - -Scenario Outline: Decoding a Static Data Report Part A - Given a new AisMessageType24Part0 record with the following properties: - | Mmsi | PartNumber | RepeatIndicator | Spare160 | - | | | | | - When the AisMessageType24Part0 is created - Then the AisMessageType24Part0 properties should be set correctly - - Examples: - | mmsi | part_no | repeat | spare | - | 12345 | 0 | 3 | 1 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs deleted file mode 100644 index 0c170d2..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part0.feature.cs +++ /dev/null @@ -1,164 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by Reqnroll (https://www.reqnroll.net/). -// Reqnroll Version:2.0.0.0 -// Reqnroll Generator Version:2.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -using Reqnroll; -namespace Ais.Net.Models.Specs.Features -{ - - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("Static Data Report Part A")] - [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class StaticDataReportPartAFeature - { - - private global::Reqnroll.ITestRunner testRunner; - - private static string[] featureTags = ((string[])(null)); - - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Static Data Report Part A", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); - -#line 1 "AisMessageType24Part0.feature" -#line hidden - - [NUnit.Framework.OneTimeSetUpAttribute()] - public static async global::System.Threading.Tasks.Task FeatureSetupAsync() - { - } - - [NUnit.Framework.OneTimeTearDownAttribute()] - public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() - { - } - - [NUnit.Framework.SetUpAttribute()] - public async global::System.Threading.Tasks.Task TestInitializeAsync() - { - testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); - try - { - if (((testRunner.FeatureContext != null) - && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) - { - await testRunner.OnFeatureEndAsync(); - } - } - finally - { - if (((testRunner.FeatureContext != null) - && testRunner.FeatureContext.BeforeFeatureHookFailed)) - { - throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); - } - if ((testRunner.FeatureContext == null)) - { - await testRunner.OnFeatureStartAsync(featureInfo); - } - } - } - - [NUnit.Framework.TearDownAttribute()] - public async global::System.Threading.Tasks.Task TestTearDownAsync() - { - if ((testRunner == null)) - { - return; - } - try - { - await testRunner.OnScenarioEndAsync(); - } - finally - { - global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); - testRunner = null; - } - } - - public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioInitialize(scenarioInfo); - testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); - } - - public async global::System.Threading.Tasks.Task ScenarioStartAsync() - { - await testRunner.OnScenarioStartAsync(); - } - - public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() - { - await testRunner.CollectScenarioErrorsAsync(); - } - - public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() - { -#line 3 -#line hidden -#line 4 - await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); -#line hidden - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Decoding a Static Data Report Part A")] - [NUnit.Framework.TestCaseAttribute("12345", "0", "3", "1", null)] - public async global::System.Threading.Tasks.Task DecodingAStaticDataReportPartA(string mmsi, string part_No, string repeat, string spare, string[] exampleTags) - { - string[] tagsOfScenario = exampleTags; - global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - argumentsOfScenario.Add("mmsi", mmsi); - argumentsOfScenario.Add("part_no", part_No); - argumentsOfScenario.Add("repeat", repeat); - argumentsOfScenario.Add("spare", spare); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Static Data Report Part A", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 6 -this.ScenarioInitialize(scenarioInfo); -#line hidden - if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) - { - testRunner.SkipScenario(); - } - else - { - await this.ScenarioStartAsync(); -#line 3 -await this.FeatureBackgroundAsync(); -#line hidden - global::Reqnroll.Table table4 = new global::Reqnroll.Table(new string[] { - "Mmsi", - "PartNumber", - "RepeatIndicator", - "Spare160"}); - table4.AddRow(new string[] { - string.Format("{0}", mmsi), - string.Format("{0}", part_No), - string.Format("{0}", repeat), - string.Format("{0}", spare)}); -#line 7 - await testRunner.GivenAsync("a new AisMessageType24Part0 record with the following properties:", ((string)(null)), table4, "Given "); -#line hidden -#line 10 - await testRunner.WhenAsync("the AisMessageType24Part0 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); -#line hidden -#line 11 - await testRunner.ThenAsync("the AisMessageType24Part0 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); -#line hidden - } - await this.ScenarioCleanupAsync(); - } - } -} -#pragma warning restore -#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature deleted file mode 100644 index 1760325..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature +++ /dev/null @@ -1,15 +0,0 @@ -Feature: Static Data Report Part B - -Background: - Given I have an AIS message - -Scenario Outline: Decoding a Static Data Report Part B - Given a new AisMessageType24Part1 record with the following properties: - | CallSign | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | Mmsi | MothershipMmsi | PartNumber | RepeatIndicator | SerialNumber | Spare162 | ShipType | UnitModelCode | VendorIdRev3 | VendorIdRev4 | - | | | | | | | | | | | | | | | | - When the AisMessageType24Part1 is created - Then the AisMessageType24Part1 properties should be set correctly - - Examples: - | call_sign | dim_bow | dim_port | dim_starboard | dim_stern | mmsi | mothership_mmsi | part_no | repeat | serial_no | spare | ship_type | unit_model | vendor_rev3 | vendor_rev4 | - | CALL | 1 | 2 | 3 | 4 | 12345 | 54321 | 1 | 3 | 123 | 1 | 60 | 1 | VEN | DOR | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs deleted file mode 100644 index 69a6230..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType24Part1.feature.cs +++ /dev/null @@ -1,213 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by Reqnroll (https://www.reqnroll.net/). -// Reqnroll Version:2.0.0.0 -// Reqnroll Generator Version:2.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -using Reqnroll; -namespace Ais.Net.Models.Specs.Features -{ - - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("Static Data Report Part B")] - [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class StaticDataReportPartBFeature - { - - private global::Reqnroll.ITestRunner testRunner; - - private static string[] featureTags = ((string[])(null)); - - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Static Data Report Part B", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); - -#line 1 "AisMessageType24Part1.feature" -#line hidden - - [NUnit.Framework.OneTimeSetUpAttribute()] - public static async global::System.Threading.Tasks.Task FeatureSetupAsync() - { - } - - [NUnit.Framework.OneTimeTearDownAttribute()] - public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() - { - } - - [NUnit.Framework.SetUpAttribute()] - public async global::System.Threading.Tasks.Task TestInitializeAsync() - { - testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); - try - { - if (((testRunner.FeatureContext != null) - && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) - { - await testRunner.OnFeatureEndAsync(); - } - } - finally - { - if (((testRunner.FeatureContext != null) - && testRunner.FeatureContext.BeforeFeatureHookFailed)) - { - throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); - } - if ((testRunner.FeatureContext == null)) - { - await testRunner.OnFeatureStartAsync(featureInfo); - } - } - } - - [NUnit.Framework.TearDownAttribute()] - public async global::System.Threading.Tasks.Task TestTearDownAsync() - { - if ((testRunner == null)) - { - return; - } - try - { - await testRunner.OnScenarioEndAsync(); - } - finally - { - global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); - testRunner = null; - } - } - - public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioInitialize(scenarioInfo); - testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); - } - - public async global::System.Threading.Tasks.Task ScenarioStartAsync() - { - await testRunner.OnScenarioStartAsync(); - } - - public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() - { - await testRunner.CollectScenarioErrorsAsync(); - } - - public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() - { -#line 3 -#line hidden -#line 4 - await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); -#line hidden - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Decoding a Static Data Report Part B")] - [NUnit.Framework.TestCaseAttribute("CALL", "1", "2", "3", "4", "12345", "54321", "1", "3", "123", "1", "60", "1", "VEN", "DOR", null)] - public async global::System.Threading.Tasks.Task DecodingAStaticDataReportPartB( - string call_Sign, - string dim_Bow, - string dim_Port, - string dim_Starboard, - string dim_Stern, - string mmsi, - string mothership_Mmsi, - string part_No, - string repeat, - string serial_No, - string spare, - string ship_Type, - string unit_Model, - string vendor_Rev3, - string vendor_Rev4, - string[] exampleTags) - { - string[] tagsOfScenario = exampleTags; - global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - argumentsOfScenario.Add("call_sign", call_Sign); - argumentsOfScenario.Add("dim_bow", dim_Bow); - argumentsOfScenario.Add("dim_port", dim_Port); - argumentsOfScenario.Add("dim_starboard", dim_Starboard); - argumentsOfScenario.Add("dim_stern", dim_Stern); - argumentsOfScenario.Add("mmsi", mmsi); - argumentsOfScenario.Add("mothership_mmsi", mothership_Mmsi); - argumentsOfScenario.Add("part_no", part_No); - argumentsOfScenario.Add("repeat", repeat); - argumentsOfScenario.Add("serial_no", serial_No); - argumentsOfScenario.Add("spare", spare); - argumentsOfScenario.Add("ship_type", ship_Type); - argumentsOfScenario.Add("unit_model", unit_Model); - argumentsOfScenario.Add("vendor_rev3", vendor_Rev3); - argumentsOfScenario.Add("vendor_rev4", vendor_Rev4); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Static Data Report Part B", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 6 -this.ScenarioInitialize(scenarioInfo); -#line hidden - if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) - { - testRunner.SkipScenario(); - } - else - { - await this.ScenarioStartAsync(); -#line 3 -await this.FeatureBackgroundAsync(); -#line hidden - global::Reqnroll.Table table5 = new global::Reqnroll.Table(new string[] { - "CallSign", - "DimensionToBow", - "DimensionToPort", - "DimensionToStarboard", - "DimensionToStern", - "Mmsi", - "MothershipMmsi", - "PartNumber", - "RepeatIndicator", - "SerialNumber", - "Spare162", - "ShipType", - "UnitModelCode", - "VendorIdRev3", - "VendorIdRev4"}); - table5.AddRow(new string[] { - string.Format("{0}", call_Sign), - string.Format("{0}", dim_Bow), - string.Format("{0}", dim_Port), - string.Format("{0}", dim_Starboard), - string.Format("{0}", dim_Stern), - string.Format("{0}", mmsi), - string.Format("{0}", mothership_Mmsi), - string.Format("{0}", part_No), - string.Format("{0}", repeat), - string.Format("{0}", serial_No), - string.Format("{0}", spare), - string.Format("{0}", ship_Type), - string.Format("{0}", unit_Model), - string.Format("{0}", vendor_Rev3), - string.Format("{0}", vendor_Rev4)}); -#line 7 - await testRunner.GivenAsync("a new AisMessageType24Part1 record with the following properties:", ((string)(null)), table5, "Given "); -#line hidden -#line 10 - await testRunner.WhenAsync("the AisMessageType24Part1 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); -#line hidden -#line 11 - await testRunner.ThenAsync("the AisMessageType24Part1 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); -#line hidden - } - await this.ScenarioCleanupAsync(); - } - } -} -#pragma warning restore -#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature deleted file mode 100644 index dc862a1..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature +++ /dev/null @@ -1,15 +0,0 @@ -Feature: Long-Range AIS Broadcast message - -Background: - Given I have an AIS message - -Scenario Outline: Decoding a Long-Range AIS Broadcast message - Given a new AisMessageType27 record with the following properties: - | CourseOverGround | GnssPositionStatus | Mmsi | NavigationStatus | Position_Latitude | Position_Longitude | PositionAccuracy | RaimFlag | RepeatIndicator | SpeedOverGround | - | | | | | | | | | | | - When the AisMessageType27 is created - Then the AisMessageType27 properties should be set correctly - - Examples: - | course | gnss_status | mmsi | nav_status | lat | long | pos_accuracy | raim | repeat | speed | - | 123.45 | true | 12345 | UnderwayUsingEngine | 1.0 | 2.0 | true | true | 3 | 12.34 | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs deleted file mode 100644 index 6fe11cf..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType27.feature.cs +++ /dev/null @@ -1,182 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by Reqnroll (https://www.reqnroll.net/). -// Reqnroll Version:2.0.0.0 -// Reqnroll Generator Version:2.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -using Reqnroll; -namespace Ais.Net.Models.Specs.Features -{ - - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("Long-Range AIS Broadcast message")] - [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class Long_RangeAISBroadcastMessageFeature - { - - private global::Reqnroll.ITestRunner testRunner; - - private static string[] featureTags = ((string[])(null)); - - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Long-Range AIS Broadcast message", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); - -#line 1 "AisMessageType27.feature" -#line hidden - - [NUnit.Framework.OneTimeSetUpAttribute()] - public static async global::System.Threading.Tasks.Task FeatureSetupAsync() - { - } - - [NUnit.Framework.OneTimeTearDownAttribute()] - public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() - { - } - - [NUnit.Framework.SetUpAttribute()] - public async global::System.Threading.Tasks.Task TestInitializeAsync() - { - testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); - try - { - if (((testRunner.FeatureContext != null) - && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) - { - await testRunner.OnFeatureEndAsync(); - } - } - finally - { - if (((testRunner.FeatureContext != null) - && testRunner.FeatureContext.BeforeFeatureHookFailed)) - { - throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); - } - if ((testRunner.FeatureContext == null)) - { - await testRunner.OnFeatureStartAsync(featureInfo); - } - } - } - - [NUnit.Framework.TearDownAttribute()] - public async global::System.Threading.Tasks.Task TestTearDownAsync() - { - if ((testRunner == null)) - { - return; - } - try - { - await testRunner.OnScenarioEndAsync(); - } - finally - { - global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); - testRunner = null; - } - } - - public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioInitialize(scenarioInfo); - testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); - } - - public async global::System.Threading.Tasks.Task ScenarioStartAsync() - { - await testRunner.OnScenarioStartAsync(); - } - - public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() - { - await testRunner.CollectScenarioErrorsAsync(); - } - - public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() - { -#line 3 -#line hidden -#line 4 - await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); -#line hidden - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Decoding a Long-Range AIS Broadcast message")] - [NUnit.Framework.TestCaseAttribute("123.45", "true", "12345", "UnderwayUsingEngine", "1.0", "2.0", "true", "true", "3", "12.34", null)] - public async global::System.Threading.Tasks.Task DecodingALong_RangeAISBroadcastMessage(string course, string gnss_Status, string mmsi, string nav_Status, string lat, string @long, string pos_Accuracy, string raim, string repeat, string speed, string[] exampleTags) - { - string[] tagsOfScenario = exampleTags; - global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - argumentsOfScenario.Add("course", course); - argumentsOfScenario.Add("gnss_status", gnss_Status); - argumentsOfScenario.Add("mmsi", mmsi); - argumentsOfScenario.Add("nav_status", nav_Status); - argumentsOfScenario.Add("lat", lat); - argumentsOfScenario.Add("long", @long); - argumentsOfScenario.Add("pos_accuracy", pos_Accuracy); - argumentsOfScenario.Add("raim", raim); - argumentsOfScenario.Add("repeat", repeat); - argumentsOfScenario.Add("speed", speed); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding a Long-Range AIS Broadcast message", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 6 -this.ScenarioInitialize(scenarioInfo); -#line hidden - if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) - { - testRunner.SkipScenario(); - } - else - { - await this.ScenarioStartAsync(); -#line 3 -await this.FeatureBackgroundAsync(); -#line hidden - global::Reqnroll.Table table6 = new global::Reqnroll.Table(new string[] { - "CourseOverGround", - "GnssPositionStatus", - "Mmsi", - "NavigationStatus", - "Position_Latitude", - "Position_Longitude", - "PositionAccuracy", - "RaimFlag", - "RepeatIndicator", - "SpeedOverGround"}); - table6.AddRow(new string[] { - string.Format("{0}", course), - string.Format("{0}", gnss_Status), - string.Format("{0}", mmsi), - string.Format("{0}", nav_Status), - string.Format("{0}", lat), - string.Format("{0}", @long), - string.Format("{0}", pos_Accuracy), - string.Format("{0}", raim), - string.Format("{0}", repeat), - string.Format("{0}", speed)}); -#line 7 - await testRunner.GivenAsync("a new AisMessageType27 record with the following properties:", ((string)(null)), table6, "Given "); -#line hidden -#line 10 - await testRunner.WhenAsync("the AisMessageType27 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); -#line hidden -#line 11 - await testRunner.ThenAsync("the AisMessageType27 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); -#line hidden - } - await this.ScenarioCleanupAsync(); - } - } -} -#pragma warning restore -#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature deleted file mode 100644 index 0a1f11b..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature +++ /dev/null @@ -1,15 +0,0 @@ -Feature: Static and Voyage Related Data - -Background: - Given I have an AIS message - -Scenario Outline: Decoding static and voyage related data - Given a new AisMessageType5 record with the following properties: - | AisVersion | CallSign | Destination | DimensionToBow | DimensionToPort | DimensionToStarboard | DimensionToStern | Draught10thMetres | EtaDay | EtaHour | EtaMinute | EtaMonth | IsDteNotReady | ImoNumber | Mmsi | PositionFixType | RepeatIndicator | ShipType | Spare423 | VesselName | - | | | | | | | | | | | | | | | | | | | | | - When the AisMessageType5 is created - Then the AisMessageType5 properties should be set correctly - - Examples: - | ais_version | call_sign | destination | dim_bow | dim_port | dim_starboard | dim_stern | draught | eta_day | eta_hour | eta_min | eta_month | dte_not_ready | imo | mmsi | pos_fix | repeat | ship_type | spare | vessel_name | - | 1 | CALL | DEST | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | true | 123 | 12345 | Gps | 3 | 60 | 1 | VESSEL | \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs b/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs deleted file mode 100644 index 9fbe344..0000000 --- a/Solutions/Ais.Net.Models.Specs/Features/AisMessageType5.feature.cs +++ /dev/null @@ -1,233 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by Reqnroll (https://www.reqnroll.net/). -// Reqnroll Version:2.0.0.0 -// Reqnroll Generator Version:2.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -using Reqnroll; -namespace Ais.Net.Models.Specs.Features -{ - - - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Reqnroll", "2.0.0.0")] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("Static and Voyage Related Data")] - [NUnit.Framework.FixtureLifeCycleAttribute(NUnit.Framework.LifeCycle.InstancePerTestCase)] - public partial class StaticAndVoyageRelatedDataFeature - { - - private global::Reqnroll.ITestRunner testRunner; - - private static string[] featureTags = ((string[])(null)); - - private static global::Reqnroll.FeatureInfo featureInfo = new global::Reqnroll.FeatureInfo(new global::System.Globalization.CultureInfo("en-US"), "Features", "Static and Voyage Related Data", null, global::Reqnroll.ProgrammingLanguage.CSharp, featureTags); - -#line 1 "AisMessageType5.feature" -#line hidden - - [NUnit.Framework.OneTimeSetUpAttribute()] - public static async global::System.Threading.Tasks.Task FeatureSetupAsync() - { - } - - [NUnit.Framework.OneTimeTearDownAttribute()] - public static async global::System.Threading.Tasks.Task FeatureTearDownAsync() - { - } - - [NUnit.Framework.SetUpAttribute()] - public async global::System.Threading.Tasks.Task TestInitializeAsync() - { - testRunner = global::Reqnroll.TestRunnerManager.GetTestRunnerForAssembly(featureHint: featureInfo); - try - { - if (((testRunner.FeatureContext != null) - && (testRunner.FeatureContext.FeatureInfo.Equals(featureInfo) == false))) - { - await testRunner.OnFeatureEndAsync(); - } - } - finally - { - if (((testRunner.FeatureContext != null) - && testRunner.FeatureContext.BeforeFeatureHookFailed)) - { - throw new global::Reqnroll.ReqnrollException("Scenario skipped because of previous before feature hook error"); - } - if ((testRunner.FeatureContext == null)) - { - await testRunner.OnFeatureStartAsync(featureInfo); - } - } - } - - [NUnit.Framework.TearDownAttribute()] - public async global::System.Threading.Tasks.Task TestTearDownAsync() - { - if ((testRunner == null)) - { - return; - } - try - { - await testRunner.OnScenarioEndAsync(); - } - finally - { - global::Reqnroll.TestRunnerManager.ReleaseTestRunner(testRunner); - testRunner = null; - } - } - - public void ScenarioInitialize(global::Reqnroll.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioInitialize(scenarioInfo); - testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext); - } - - public async global::System.Threading.Tasks.Task ScenarioStartAsync() - { - await testRunner.OnScenarioStartAsync(); - } - - public async global::System.Threading.Tasks.Task ScenarioCleanupAsync() - { - await testRunner.CollectScenarioErrorsAsync(); - } - - public virtual async global::System.Threading.Tasks.Task FeatureBackgroundAsync() - { -#line 3 -#line hidden -#line 4 - await testRunner.GivenAsync("I have an AIS message", ((string)(null)), ((global::Reqnroll.Table)(null)), "Given "); -#line hidden - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Decoding static and voyage related data")] - [NUnit.Framework.TestCaseAttribute("1", "CALL", "DEST", "1", "2", "3", "4", "5", "6", "7", "8", "9", "true", "123", "12345", "Gps", "3", "60", "1", "VESSEL", null)] - public async global::System.Threading.Tasks.Task DecodingStaticAndVoyageRelatedData( - string ais_Version, - string call_Sign, - string destination, - string dim_Bow, - string dim_Port, - string dim_Starboard, - string dim_Stern, - string draught, - string eta_Day, - string eta_Hour, - string eta_Min, - string eta_Month, - string dte_Not_Ready, - string imo, - string mmsi, - string pos_Fix, - string repeat, - string ship_Type, - string spare, - string vessel_Name, - string[] exampleTags) - { - string[] tagsOfScenario = exampleTags; - global::System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new global::System.Collections.Specialized.OrderedDictionary(); - argumentsOfScenario.Add("ais_version", ais_Version); - argumentsOfScenario.Add("call_sign", call_Sign); - argumentsOfScenario.Add("destination", destination); - argumentsOfScenario.Add("dim_bow", dim_Bow); - argumentsOfScenario.Add("dim_port", dim_Port); - argumentsOfScenario.Add("dim_starboard", dim_Starboard); - argumentsOfScenario.Add("dim_stern", dim_Stern); - argumentsOfScenario.Add("draught", draught); - argumentsOfScenario.Add("eta_day", eta_Day); - argumentsOfScenario.Add("eta_hour", eta_Hour); - argumentsOfScenario.Add("eta_min", eta_Min); - argumentsOfScenario.Add("eta_month", eta_Month); - argumentsOfScenario.Add("dte_not_ready", dte_Not_Ready); - argumentsOfScenario.Add("imo", imo); - argumentsOfScenario.Add("mmsi", mmsi); - argumentsOfScenario.Add("pos_fix", pos_Fix); - argumentsOfScenario.Add("repeat", repeat); - argumentsOfScenario.Add("ship_type", ship_Type); - argumentsOfScenario.Add("spare", spare); - argumentsOfScenario.Add("vessel_name", vessel_Name); - global::Reqnroll.ScenarioInfo scenarioInfo = new global::Reqnroll.ScenarioInfo("Decoding static and voyage related data", null, tagsOfScenario, argumentsOfScenario, featureTags); -#line 6 -this.ScenarioInitialize(scenarioInfo); -#line hidden - if ((global::Reqnroll.TagHelper.ContainsIgnoreTag(scenarioInfo.CombinedTags) || global::Reqnroll.TagHelper.ContainsIgnoreTag(featureTags))) - { - testRunner.SkipScenario(); - } - else - { - await this.ScenarioStartAsync(); -#line 3 -await this.FeatureBackgroundAsync(); -#line hidden - global::Reqnroll.Table table7 = new global::Reqnroll.Table(new string[] { - "AisVersion", - "CallSign", - "Destination", - "DimensionToBow", - "DimensionToPort", - "DimensionToStarboard", - "DimensionToStern", - "Draught10thMetres", - "EtaDay", - "EtaHour", - "EtaMinute", - "EtaMonth", - "IsDteNotReady", - "ImoNumber", - "Mmsi", - "PositionFixType", - "RepeatIndicator", - "ShipType", - "Spare423", - "VesselName"}); - table7.AddRow(new string[] { - string.Format("{0}", ais_Version), - string.Format("{0}", call_Sign), - string.Format("{0}", destination), - string.Format("{0}", dim_Bow), - string.Format("{0}", dim_Port), - string.Format("{0}", dim_Starboard), - string.Format("{0}", dim_Stern), - string.Format("{0}", draught), - string.Format("{0}", eta_Day), - string.Format("{0}", eta_Hour), - string.Format("{0}", eta_Min), - string.Format("{0}", eta_Month), - string.Format("{0}", dte_Not_Ready), - string.Format("{0}", imo), - string.Format("{0}", mmsi), - string.Format("{0}", pos_Fix), - string.Format("{0}", repeat), - string.Format("{0}", ship_Type), - string.Format("{0}", spare), - string.Format("{0}", vessel_Name)}); -#line 7 - await testRunner.GivenAsync("a new AisMessageType5 record with the following properties:", ((string)(null)), table7, "Given "); -#line hidden -#line 10 - await testRunner.WhenAsync("the AisMessageType5 is created", ((string)(null)), ((global::Reqnroll.Table)(null)), "When "); -#line hidden -#line 11 - await testRunner.ThenAsync("the AisMessageType5 properties should be set correctly", ((string)(null)), ((global::Reqnroll.Table)(null)), "Then "); -#line hidden - } - await this.ScenarioCleanupAsync(); - } - } -} -#pragma warning restore -#endregion diff --git a/Solutions/Ais.Net.Models.Specs/Hooks.cs b/Solutions/Ais.Net.Models.Specs/Hooks.cs deleted file mode 100644 index 87ebbe9..0000000 --- a/Solutions/Ais.Net.Models.Specs/Hooks.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Reqnroll; - -namespace Ais.Net.Models.Specs -{ - [Binding] - public class Hooks - { - [BeforeScenario] - public void BeforeScenario() - { - //TODO: implement logic that has to run before executing each scenario - } - - [AfterScenario] - public void AfterScenario() - { - //TODO: implement logic that has to run after executing each scenario - } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/PositionTests.cs b/Solutions/Ais.Net.Models.Specs/PositionTests.cs new file mode 100644 index 0000000..d8b884a --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/PositionTests.cs @@ -0,0 +1,71 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class PositionTests +{ + [TestMethod] + [DataRow(600000, 1.0)] // 1 degree == 60 minutes == 600,000 ten-thousandths of a minute + [DataRow(-600000, -1.0)] + [DataRow(300000, 0.5)] + [DataRow(0, 0.0)] + public void From10000thMinsToDegreesScalesBySixHundredThousand(int tenThousandthsOfMinutes, double expectedDegrees) + { + tenThousandthsOfMinutes.From10000thMinsToDegrees().ShouldBe(expectedDegrees); + } + + [TestMethod] + [DataRow(600, 1.0)] // 1 degree == 60 minutes == 600 tenths of a minute + [DataRow(-600, -1.0)] + [DataRow(300, 0.5)] + [DataRow(0, 0.0)] + public void From10thMinsToDegreesScalesBySixHundred(int tenthsOfMinutes, double expectedDegrees) + { + tenthsOfMinutes.From10thMinsToDegrees().ShouldBe(expectedDegrees); + } + + [TestMethod] + public void From10000thMinsBuildsPositionFromScaledLatitudeAndLongitude() + { + Position position = Position.From10000thMins(600000, 1200000); + + position.ShouldBe(new Position(1.0, 2.0)); + } + + [TestMethod] + public void From10thMinsBuildsPositionFromScaledLatitudeAndLongitude() + { + Position position = Position.From10thMins(600, 1200); + + position.ShouldBe(new Position(1.0, 2.0)); + } + + [TestMethod] + [DataRow(54600000, 91.0)] // AIS "latitude not available" sentinel (91 degrees) + [DataRow(108600000, 181.0)] // AIS "longitude not available" sentinel (181 degrees) + public void From10000thMinsToDegreesDoesNotSpecialCaseNotAvailableSentinels(int tenThousandthsOfMinutes, double expectedDegrees) + { + // Unlike speed/course, the coordinate helpers do not null out the "not available" + // sentinels; the raw out-of-range degrees pass straight through (any such filtering + // is the caller's / decoder's responsibility). + tenThousandthsOfMinutes.From10000thMinsToDegrees().ShouldBe(expectedDegrees); + } + + [TestMethod] + public void PositionsWithTheSameCoordinatesAreEqual() + { + new Position(51.5, -0.12).ShouldBe(new Position(51.5, -0.12)); + } + + [TestMethod] + public void PositionsWithDifferentCoordinatesAreNotEqual() + { + new Position(51.5, -0.12).ShouldNotBe(new Position(51.5, 0.12)); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/ShipTypeClassificationTests.cs b/Solutions/Ais.Net.Models.Specs/ShipTypeClassificationTests.cs new file mode 100644 index 0000000..67450ad --- /dev/null +++ b/Solutions/Ais.Net.Models.Specs/ShipTypeClassificationTests.cs @@ -0,0 +1,242 @@ +namespace Ais.Net.Models.Specs; + +using Ais.Net.Models; +using Ais.Net.Models.Abstractions; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Shouldly; + +[TestClass] +public class ShipTypeClassificationTests +{ + [TestMethod] + [DataRow(0, ShipTypeGroup.NotAvailable)] + [DataRow(1, ShipTypeGroup.Reserved)] + [DataRow(19, ShipTypeGroup.Reserved)] + [DataRow(20, ShipTypeGroup.WingInGroundAll)] + [DataRow(21, ShipTypeGroup.WingInGroundHazardous)] + [DataRow(24, ShipTypeGroup.WingInGroundHazardous)] + [DataRow(25, ShipTypeGroup.WingInGroundReserved)] + [DataRow(29, ShipTypeGroup.WingInGroundReserved)] + [DataRow(30, ShipTypeGroup.Fishing)] + [DataRow(31, ShipTypeGroup.Towing)] + [DataRow(32, ShipTypeGroup.TowingLengthOver200MOrBreadthOver25M)] + [DataRow(33, ShipTypeGroup.DredgingOrUnderwaterOps)] + [DataRow(34, ShipTypeGroup.DivingOps)] + [DataRow(35, ShipTypeGroup.MilitaryOps)] + [DataRow(36, ShipTypeGroup.Sailing)] + [DataRow(37, ShipTypeGroup.PleasureCraft)] + [DataRow(38, ShipTypeGroup.Reserved)] + [DataRow(39, ShipTypeGroup.Reserved)] + [DataRow(40, ShipTypeGroup.HighSpeedCraftAll)] + [DataRow(41, ShipTypeGroup.HighSpeedCraftHazardous)] + [DataRow(44, ShipTypeGroup.HighSpeedCraftHazardous)] + [DataRow(45, ShipTypeGroup.HighSpeedCraftReserved)] + [DataRow(48, ShipTypeGroup.HighSpeedCraftReserved)] + [DataRow(49, ShipTypeGroup.HighSpeedCraftNoAdditionalInformation)] // '9' == "no additional information", matching 69/79/89/99 + [DataRow(50, ShipTypeGroup.PilotVessel)] + [DataRow(51, ShipTypeGroup.SearchAndRescueVessel)] + [DataRow(52, ShipTypeGroup.Tug)] + [DataRow(53, ShipTypeGroup.PortTender)] + [DataRow(54, ShipTypeGroup.AntiPollutionEquipment)] + [DataRow(55, ShipTypeGroup.LawEnforcement)] + [DataRow(56, ShipTypeGroup.SpareLocalVessel)] + [DataRow(57, ShipTypeGroup.SpareLocalVessel)] + [DataRow(58, ShipTypeGroup.MedicalTransport)] + [DataRow(59, ShipTypeGroup.NoncombatantShip)] + [DataRow(60, ShipTypeGroup.PassengerAll)] + [DataRow(61, ShipTypeGroup.PassengerHazardous)] + [DataRow(64, ShipTypeGroup.PassengerHazardous)] + [DataRow(65, ShipTypeGroup.PassengerReserved)] + [DataRow(68, ShipTypeGroup.PassengerReserved)] + [DataRow(69, ShipTypeGroup.PassengerNoAdditionalInformation)] + [DataRow(70, ShipTypeGroup.CargoAll)] + [DataRow(71, ShipTypeGroup.CargoHazardous)] + [DataRow(74, ShipTypeGroup.CargoHazardous)] + [DataRow(75, ShipTypeGroup.CargoReserved)] + [DataRow(78, ShipTypeGroup.CargoReserved)] + [DataRow(79, ShipTypeGroup.CargoNoAdditionalInformation)] + [DataRow(80, ShipTypeGroup.TankerAll)] + [DataRow(81, ShipTypeGroup.TankerHazardous)] + [DataRow(84, ShipTypeGroup.TankerHazardous)] + [DataRow(85, ShipTypeGroup.TankerReserved)] + [DataRow(88, ShipTypeGroup.TankerReserved)] + [DataRow(89, ShipTypeGroup.TankerNoAdditionalInformation)] + [DataRow(90, ShipTypeGroup.OtherAll)] + [DataRow(91, ShipTypeGroup.OtherHazardous)] + [DataRow(94, ShipTypeGroup.OtherHazardous)] + [DataRow(95, ShipTypeGroup.OtherReserved)] + [DataRow(98, ShipTypeGroup.OtherReserved)] + [DataRow(99, ShipTypeGroup.OtherNoAdditionalInformation)] + [DataRow(100, ShipTypeGroup.NotAvailable)] + [DataRow(-1, ShipTypeGroup.NotAvailable)] + public void ToShipTypeGroupClassifiesRawShipTypeCode(int shipType, ShipTypeGroup expected) + { + shipType.ToShipTypeGroup().ShouldBe(expected); + } + + [TestMethod] + [DataRow(ShipTypeGroup.NotAvailable, ShipTypeCategory.NotAvailable)] + [DataRow(ShipTypeGroup.Reserved, ShipTypeCategory.Reserved)] + [DataRow(ShipTypeGroup.WingInGroundAll, ShipTypeCategory.WingInGround)] + [DataRow(ShipTypeGroup.WingInGroundHazardous, ShipTypeCategory.WingInGround)] + [DataRow(ShipTypeGroup.WingInGroundReserved, ShipTypeCategory.WingInGround)] + [DataRow(ShipTypeGroup.Fishing, ShipTypeCategory.SpecialCategory3)] + [DataRow(ShipTypeGroup.Towing, ShipTypeCategory.SpecialCategory3)] + [DataRow(ShipTypeGroup.TowingLengthOver200MOrBreadthOver25M, ShipTypeCategory.SpecialCategory3)] + [DataRow(ShipTypeGroup.DredgingOrUnderwaterOps, ShipTypeCategory.SpecialCategory3)] + [DataRow(ShipTypeGroup.DivingOps, ShipTypeCategory.SpecialCategory3)] + [DataRow(ShipTypeGroup.MilitaryOps, ShipTypeCategory.SpecialCategory3)] + [DataRow(ShipTypeGroup.Sailing, ShipTypeCategory.SpecialCategory3)] + [DataRow(ShipTypeGroup.PleasureCraft, ShipTypeCategory.SpecialCategory3)] + [DataRow(ShipTypeGroup.HighSpeedCraftAll, ShipTypeCategory.HighSpeedCraft)] + [DataRow(ShipTypeGroup.HighSpeedCraftHazardous, ShipTypeCategory.HighSpeedCraft)] + [DataRow(ShipTypeGroup.HighSpeedCraftReserved, ShipTypeCategory.HighSpeedCraft)] + [DataRow(ShipTypeGroup.HighSpeedCraftNoAdditionalInformation, ShipTypeCategory.HighSpeedCraft)] + [DataRow(ShipTypeGroup.PilotVessel, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.SearchAndRescueVessel, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.Tug, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.PortTender, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.AntiPollutionEquipment, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.LawEnforcement, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.SpareLocalVessel, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.MedicalTransport, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.NoncombatantShip, ShipTypeCategory.SpecialCategory5)] + [DataRow(ShipTypeGroup.PassengerAll, ShipTypeCategory.Passenger)] + [DataRow(ShipTypeGroup.PassengerHazardous, ShipTypeCategory.Passenger)] + [DataRow(ShipTypeGroup.PassengerReserved, ShipTypeCategory.Passenger)] + [DataRow(ShipTypeGroup.PassengerNoAdditionalInformation, ShipTypeCategory.Passenger)] + [DataRow(ShipTypeGroup.CargoAll, ShipTypeCategory.Cargo)] + [DataRow(ShipTypeGroup.CargoHazardous, ShipTypeCategory.Cargo)] + [DataRow(ShipTypeGroup.CargoReserved, ShipTypeCategory.Cargo)] + [DataRow(ShipTypeGroup.CargoNoAdditionalInformation, ShipTypeCategory.Cargo)] + [DataRow(ShipTypeGroup.TankerAll, ShipTypeCategory.Tanker)] + [DataRow(ShipTypeGroup.TankerHazardous, ShipTypeCategory.Tanker)] + [DataRow(ShipTypeGroup.TankerReserved, ShipTypeCategory.Tanker)] + [DataRow(ShipTypeGroup.TankerNoAdditionalInformation, ShipTypeCategory.Tanker)] + [DataRow(ShipTypeGroup.OtherAll, ShipTypeCategory.Other)] + [DataRow(ShipTypeGroup.OtherHazardous, ShipTypeCategory.Other)] + [DataRow(ShipTypeGroup.OtherReserved, ShipTypeCategory.Other)] + [DataRow(ShipTypeGroup.OtherNoAdditionalInformation, ShipTypeCategory.Other)] + [DataRow((ShipTypeGroup)999, ShipTypeCategory.NotAvailable)] // an unrecognised group falls back to NotAvailable + public void ToShipTypeCategoryMapsGroupToCategory(ShipTypeGroup group, ShipTypeCategory expected) + { + group.ToShipTypeCategory().ShouldBe(expected); + } + + [TestMethod] + [DataRow(0, ShipTypeCategory.NotAvailable)] + [DataRow(30, ShipTypeCategory.SpecialCategory3)] + [DataRow(40, ShipTypeCategory.HighSpeedCraft)] + [DataRow(50, ShipTypeCategory.SpecialCategory5)] + [DataRow(60, ShipTypeCategory.Passenger)] + [DataRow(70, ShipTypeCategory.Cargo)] + [DataRow(80, ShipTypeCategory.Tanker)] + [DataRow(90, ShipTypeCategory.Other)] + public void ToShipTypeCategoryClassifiesShipTypeEnumViaItsGroup(int shipType, ShipTypeCategory expected) + { + ((ShipType)shipType).ToShipTypeCategory().ShouldBe(expected); + } + + [TestMethod] + [DataRow(-5, "Not available")] // below the valid range + [DataRow(0, "Not available")] + [DataRow(1, "Reserved for future use")] + [DataRow(10, "Reserved for future use")] + [DataRow(19, "Reserved for future use")] + [DataRow(20, "Wing in ground(WIG), all ships of this type")] + [DataRow(21, "Wing in ground(WIG), Hazardous category A")] + [DataRow(22, "Wing in ground(WIG), Hazardous category B")] + [DataRow(23, "Wing in ground(WIG), Hazardous category C")] + [DataRow(24, "Wing in ground(WIG), Hazardous category D")] + [DataRow(25, "Wing in ground(WIG), Reserved for future use")] + [DataRow(29, "Wing in ground(WIG), Reserved for future use")] + [DataRow(30, "Fishing")] + [DataRow(31, "Towing")] + [DataRow(32, "Towing: length exceeds 200m or breadth exceeds 25m")] + [DataRow(33, "Dredging or underwater ops")] + [DataRow(34, "Diving ops")] + [DataRow(35, "Military ops")] + [DataRow(36, "Sailing")] + [DataRow(37, "Pleasure Craft")] + [DataRow(38, "Reserved")] + [DataRow(39, "Reserved")] + [DataRow(40, "High speed craft(HSC), all ships of this type")] + [DataRow(41, "High speed craft(HSC), Hazardous category A")] + [DataRow(42, "High speed craft(HSC), Hazardous category B")] + [DataRow(43, "High speed craft(HSC), Hazardous category C")] + [DataRow(44, "High speed craft(HSC), Hazardous category D")] + [DataRow(45, "High speed craft(HSC), Reserved for future use")] + [DataRow(48, "High speed craft(HSC), Reserved for future use")] + [DataRow(49, "High speed craft(HSC), No additional information")] + [DataRow(50, "Pilot Vessel")] + [DataRow(51, "Search and Rescue vessel")] + [DataRow(52, "Tug")] + [DataRow(53, "Port Tender")] + [DataRow(54, "Anti - pollution equipment")] + [DataRow(55, "Law Enforcement")] + [DataRow(56, "Spare - Local Vessel")] + [DataRow(57, "Spare - Local Vessel")] + [DataRow(58, "Medical Transport")] + [DataRow(59, "Noncombatant ship according to RR Resolution No. 18")] + [DataRow(60, "Passenger, all ships of this type")] + [DataRow(61, "Passenger, Hazardous category A")] + [DataRow(62, "Passenger, Hazardous category B")] + [DataRow(63, "Passenger, Hazardous category C")] + [DataRow(64, "Passenger, Hazardous category D")] + [DataRow(65, "Passenger, Reserved for future use")] + [DataRow(68, "Passenger, Reserved for future use")] + [DataRow(69, "Passenger, No additional information")] + [DataRow(70, "Cargo, all ships of this type")] + [DataRow(71, "Cargo, Hazardous category A")] + [DataRow(72, "Cargo, Hazardous category B")] + [DataRow(73, "Cargo, Hazardous category C")] + [DataRow(74, "Cargo, Hazardous category D")] + [DataRow(75, "Cargo, Reserved for future use")] + [DataRow(78, "Cargo, Reserved for future use")] + [DataRow(79, "Cargo, No additional information")] + [DataRow(80, "Tanker, all ships of this type")] + [DataRow(81, "Tanker, Hazardous category A")] + [DataRow(82, "Tanker, Hazardous category B")] + [DataRow(83, "Tanker, Hazardous category C")] + [DataRow(84, "Tanker, Hazardous category D")] + [DataRow(85, "Tanker, Reserved for future use")] + [DataRow(88, "Tanker, Reserved for future use")] + [DataRow(89, "Tanker, No additional information")] + [DataRow(90, "Other Type, all ships of this type")] + [DataRow(91, "Other Type, Hazardous category A")] + [DataRow(92, "Other Type, Hazardous category B")] + [DataRow(93, "Other Type, Hazardous category C")] + [DataRow(94, "Other Type, Hazardous category D")] + [DataRow(95, "Other Type, Reserved for future use")] + [DataRow(98, "Other Type, Reserved for future use")] + [DataRow(99, "Other Type, no additional information")] + [DataRow(100, "Not available")] // above the valid range + + // Remaining interior values of each "Reserved for future use" band — every code has its + // own arm in the source, so these are included to exercise all of them. + [DataRow(26, "Wing in ground(WIG), Reserved for future use")] + [DataRow(27, "Wing in ground(WIG), Reserved for future use")] + [DataRow(28, "Wing in ground(WIG), Reserved for future use")] + [DataRow(46, "High speed craft(HSC), Reserved for future use")] + [DataRow(47, "High speed craft(HSC), Reserved for future use")] + [DataRow(66, "Passenger, Reserved for future use")] + [DataRow(67, "Passenger, Reserved for future use")] + [DataRow(76, "Cargo, Reserved for future use")] + [DataRow(77, "Cargo, Reserved for future use")] + [DataRow(86, "Tanker, Reserved for future use")] + [DataRow(87, "Tanker, Reserved for future use")] + [DataRow(96, "Other Type, Reserved for future use")] + [DataRow(97, "Other Type, Reserved for future use")] + public void ToShipTypeDescriptionReturnsHumanReadableText(int shipType, string expected) + { + shipType.ToShipTypeDescription().ShouldBe(expected); + } + + [TestMethod] + public void ToShipTypeGroupAcceptsTheShipTypeEnum() + { + ((ShipType)60).ToShipTypeGroup().ShouldBe(ShipTypeGroup.PassengerAll); + } +} diff --git a/Solutions/Ais.Net.Models.Specs/StepDefinitionBase.cs b/Solutions/Ais.Net.Models.Specs/StepDefinitionBase.cs deleted file mode 100644 index d5795bc..0000000 --- a/Solutions/Ais.Net.Models.Specs/StepDefinitionBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Ais.Net.Models.Abstractions; - -namespace Ais.Net.Models.Specs -{ - public class StepDefinitionBase - { - protected IAisMessage? Message { get; set; } - } -} \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.Specs/packages.lock.json b/Solutions/Ais.Net.Models.Specs/packages.lock.json index bb1041e..ba3248a 100644 --- a/Solutions/Ais.Net.Models.Specs/packages.lock.json +++ b/Solutions/Ais.Net.Models.Specs/packages.lock.json @@ -1,255 +1,394 @@ -{ - "version": 1, - "dependencies": { - "net8.0": { - "coverlet.msbuild": { - "type": "Direct", - "requested": "[6.0.4, )", - "resolved": "6.0.4", - "contentHash": "Qa7Hg+wrOMDKpXVn2dw4Wlun490bIWsFW0fdNJQFJLZnbU27MCP0HJ2mPgS+3EQBQUb0zKlkwiQzP+j38Hc3Iw==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.14.1, )", - "resolved": "17.14.1", - "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", - "dependencies": { - "Microsoft.CodeCoverage": "17.14.1", - "Microsoft.TestPlatform.TestHost": "17.14.1" - } - }, - "NUnit3TestAdapter": { - "type": "Direct", - "requested": "[5.0.0, )", - "resolved": "5.0.0", - "contentHash": "sy4cLoUAdE6TDM4wNX5gmNCyhMev5wUz4cA6ZRf/aON9vf9t4xTVGLj/4huhDKcS4dFfmVVcgcP70yC7WC9kKg==", - "dependencies": { - "Microsoft.Testing.Extensions.VSTestBridge": "1.5.3", - "Microsoft.Testing.Platform.MSBuild": "1.5.3" - } - }, - "Reqnroll.NUnit": { - "type": "Direct", - "requested": "[2.4.1, )", - "resolved": "2.4.1", - "contentHash": "IbP1uVirMVpopeRwjvtz2b0PO9oQtHcDnAQigTpZY9lxSRYBycQSYQaZ6fbkkg/mG/azkPPGV3YIjNILQ8jWrw==", - "dependencies": { - "NUnit": "3.13.1", - "Reqnroll": "[2.4.1]", - "Reqnroll.Tools.MsBuild.Generation": "[2.4.1]" - } - }, - "Shouldly": { - "type": "Direct", - "requested": "[4.3.0, )", - "resolved": "4.3.0", - "contentHash": "sDetrWXrl6YXZ4HeLsdBoNk3uIa7K+V4uvIJ+cqdRa5DrFxeTED7VkjoxCuU1kJWpUuBDZz2QXFzSxBtVXLwRQ==", - "dependencies": { - "DiffEngine": "11.3.0", - "EmptyFiles": "4.4.0" - } - }, - "Ais.Net": { - "type": "Transitive", - "resolved": "0.4.2", - "contentHash": "+wtx8g0YwxEaDbilf4MiED9S4Yxr9J7RHAAMEN48+yB0it10KYdhmtB6dxt7qdCOvdoDnBOumggcGYefWMz+WA==", - "dependencies": { - "System.IO.Pipelines": "4.7.4" - } - }, - "Cucumber.CucumberExpressions": { - "type": "Transitive", - "resolved": "17.1.0", - "contentHash": "IZFDLLwrUCKuGDXQzmAWZa+kQFpVmVwWD8wEl0UWGOR83UQfzqg8hZeSSNUOXlW0Kh3vMO6kEm3FpNC4amKMaw==" - }, - "DiffEngine": { - "type": "Transitive", - "resolved": "11.3.0", - "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", - "dependencies": { - "EmptyFiles": "4.4.0", - "System.Management": "6.0.1" - } - }, - "EmptyFiles": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" - }, - "Gherkin": { - "type": "Transitive", - "resolved": "30.0.0", - "contentHash": "jZM9zm9eg62vRSAOtYvcmQh4w0oYqjP6myKxMaJAlQq9VCKm4wnTfHhS5QRnLhx/sH3R5jTaoQAKLs5hhjuq7g==" - }, - "Microsoft.ApplicationInsights": { - "type": "Transitive", - "resolved": "2.22.0", - "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "5.0.0" - } - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" - }, - "Microsoft.Extensions.DependencyModel": { - "type": "Transitive", - "resolved": "8.0.2", - "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.Testing.Extensions.Telemetry": { - "type": "Transitive", - "resolved": "1.5.3", - "contentHash": "U9pGd5DQuX1PfkrdFI+xH34JGgQ2nes5QAwIITTk+MQfLvRITqsZjJeHTjpGWh33D/0q1l7aA8/LQHR7UuCgLQ==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.Testing.Platform": "1.5.3" - } - }, - "Microsoft.Testing.Extensions.TrxReport.Abstractions": { - "type": "Transitive", - "resolved": "1.5.3", - "contentHash": "h34zKNpGyni66VH738mRHeXSnf3klSShUdavUWNhSfWICUUi5aXeI0LBvoX/ad93N0+9xBDU3Fyi6WfxrwKQGw==", - "dependencies": { - "Microsoft.Testing.Platform": "1.5.3" - } - }, - "Microsoft.Testing.Extensions.VSTestBridge": { - "type": "Transitive", - "resolved": "1.5.3", - "contentHash": "cJD67YfDT98wEWyazKVD/yPVW6+H1usXeuselCnRes7JZBTIYWtrCchcOzOahnmajT79eDKqt9sta7DXwTDU4Q==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.TestPlatform.ObjectModel": "17.12.0", - "Microsoft.Testing.Extensions.Telemetry": "1.5.3", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.5.3", - "Microsoft.Testing.Platform": "1.5.3" - } - }, - "Microsoft.Testing.Platform": { - "type": "Transitive", - "resolved": "1.5.3", - "contentHash": "WqJydnJ99dEKtquR9HwINz104ehWJKTXbQQrydGatlLRw14bmsx0pa8+E6KUXMYXZAimN0swWlDmcJGjjW4TIg==" - }, - "Microsoft.Testing.Platform.MSBuild": { - "type": "Transitive", - "resolved": "1.5.3", - "contentHash": "bOtpRMSPeT5YLQo+NNY8EtdNTphAUcmALjW4ABU7P0rb6yR2XAZau3TzNieLmR3lRuwudguWzzBhgcLRXwZh0A==", - "dependencies": { - "Microsoft.Testing.Platform": "1.5.3" - } - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==", - "dependencies": { - "System.Reflection.Metadata": "8.0.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.14.1", - "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.14.1", - "Newtonsoft.Json": "13.0.3" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "NUnit": { - "type": "Transitive", - "resolved": "3.13.1", - "contentHash": "vWBvrSelmTYwqHWvO3dA63z7cOpaFR/3nJ9MMVLBkWeaWa7oiglPPm5g1h96B4i2XXqjFexxhR5MyMjmIJYPfg==", - "dependencies": { - "NETStandard.Library": "2.0.0" - } - }, - "Reqnroll": { - "type": "Transitive", - "resolved": "2.4.1", - "contentHash": "Ui229q21Kmkrv3L89THgsxGMXooJGK5OrFPLvYDXfE2nO/u4tqYsbmMRddyEE3fsR40IDP++HsRrDxlkR6reVw==", - "dependencies": { - "Cucumber.CucumberExpressions": "17.1.0", - "Gherkin": "30.0.0", - "Microsoft.Extensions.DependencyModel": "8.0.2", - "System.Text.Json": "8.0.5" - } - }, - "Reqnroll.Tools.MsBuild.Generation": { - "type": "Transitive", - "resolved": "2.4.1", - "contentHash": "+Up0eUWnKRKPdRIttE52DYzwayUd4bGMyRytYeeCwtqwZA4tLhK2NspOZJ8axPcmATOM9QSTCUHUlyp9pgcfQg==", - "dependencies": { - "Reqnroll": "[2.4.1]" - } - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "4.7.4", - "contentHash": "TQqNjDcTVu6eTDB2S0vjSUhn0udEugL93D9zgsOsSiJO1kv1UWDM2o0+79H95I5X+S0VPEzm4ihj6KPwhNEb7g==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", - "dependencies": { - "System.CodeDom": "6.0.0" - } - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", - "dependencies": { - "System.Collections.Immutable": "8.0.0" - } - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "8.0.5", - "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" - }, - "ais.net.models": { - "type": "Project", - "dependencies": { - "Ais.Net": "[0.4.2, )" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.9.0, )", + "resolved": "18.9.0", + "contentHash": "Odp8djWEUFTJoRV02y0zJaAwAHF31CtNaDgnKKM+8dBHbbaH7Ee0oo9gweLpbzjI7UrtMUzH6xLJSrICZw+Nwg==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.9", + "Microsoft.Extensions.DependencyModel": "10.0.8", + "Microsoft.Testing.Platform": "2.3.0" + } + }, + "Microsoft.Testing.Extensions.TrxReport": { + "type": "Direct", + "requested": "[2.3.2, )", + "resolved": "2.3.2", + "contentHash": "JzCtiOAmIfkPP2PpAKZeiH1hVYIVC9jMhsPWrd6PnH/Wtuct1UMuxpKi2F1cmXgfokc/HOadC6HOO1qzI23tnA==", + "dependencies": { + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.3.2", + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "MSTest.TestAdapter": { + "type": "Direct", + "requested": "[4.3.2, )", + "resolved": "4.3.2", + "contentHash": "uqpjJqQ3ws5z7Pq6BfMFpuqdrN2YIfe0Gfww91LRVXwxBdLqUaJd4JbHUImOSQvmK5m3PqW285kPjKbb69QFoA==", + "dependencies": { + "MSTest.TestFramework": "4.3.2", + "Microsoft.Testing.Extensions.VSTestBridge": "2.3.2", + "Microsoft.Testing.Platform.MSBuild": "2.3.2" + } + }, + "MSTest.TestFramework": { + "type": "Direct", + "requested": "[4.3.2, )", + "resolved": "4.3.2", + "contentHash": "Vlj3THt/BcOV/93k0Cdq29RNEsrY3TvUV6mF13rOuY8Gx7Ltd+LrOYId8AYu0cH9ioCYY/Sx1hppfI9p/vP+aw==", + "dependencies": { + "MSTest.Analyzers": "4.3.2" + } + }, + "NSubstitute": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "0gvKMbiJ+/WrfbcfBfqRZZrvfLJcd3rqkqVMjjlY5dtmLRVzMY+o/K/rJUStofQ2haSr9Vd04YDfvZtVVGS3/A==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "Shouldly": { + "type": "Direct", + "requested": "[4.3.0, )", + "resolved": "4.3.0", + "contentHash": "sDetrWXrl6YXZ4HeLsdBoNk3uIa7K+V4uvIJ+cqdRa5DrFxeTED7VkjoxCuU1kJWpUuBDZz2QXFzSxBtVXLwRQ==", + "dependencies": { + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" + } + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "DiffEngine": { + "type": "Transitive", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "dependencies": { + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" + } + }, + "EmptyFiles": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.23.0", + "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==" + }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.2.9", + "contentHash": "WLbDc/W9XkHpGVtd1L6Vml8W6+PaaYZZTJASHjHF7W2WX/hld6bfNK102Ui+I3a6zIdx4+oKRJtMgHz44JmBfw==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "vLyZVpxmduO2jx+76ggqnsA3m81kwMY3NkWciNTj5E+Nvqb0VihqCvQP89QsGONWp0AJwMZG+u9GzaCjDdFGNw==" + }, + "Microsoft.Testing.Extensions.Telemetry": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "Q1iapTk1DwFFT9aPtv4wxJHe22++NLxsg7IekEujac9RDAugxGQdCN0SQ1b9ryF78cdidhSBB17pHdCANurOyQ==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.23.0", + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "frw+Qa6gUxOZSw2Mw6SPtJT9gPzcP4nS9TfQhS0cHXKL6Rcp/CqLG+o4Psc1bHAhxW04rpzTBhjpNMnw2L7BuQ==", + "dependencies": { + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "Microsoft.Testing.Extensions.VSTestBridge": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "DXJIaLlwt+6GnPurlf7eVMI0wAZV5KCZrgGPibhx9i9LNxAkwEOK2jVzdVjLvvZ/SX9MUUaGJB9yXuF/VoT4/w==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "18.4.0", + "Microsoft.Testing.Extensions.Telemetry": "2.3.2", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.3.2", + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "FuJgIEfNU+rFHJYGhuqA8uup+3cryJ1N67Tijqo01Jd5xAr28ptz0aXkFfSqK3MhAUmGsrPpgQ92+3+0oXv8AA==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "guFvvaE8FGUlB6h24jN+P+NBz/P5x4On9sk4n5UapUAPq4fr0y1AgUe4sXmItp7r2NMe3rhZYLE1teP9T9sVbg==", + "dependencies": { + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "18.4.0", + "contentHash": "4L6m2kS2pY5uJ9cpeRxzW22opr6ttScIRqsOpMDQpgENp/ZwxkkQCcmc6LRSURo2dFaaSW5KVflQZvroiJ7Wzg==" + }, + "MSTest.Analyzers": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "GNvrVI6/Y5pNoKnK1/VELBls3XiHNZaj+zh14LtwlCqFI8zXTW3vuBe0Jv2qEr9wxHlOAQQBTJPRoNRjneGrKQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "ais.net.models": { + "type": "Project", + "dependencies": { + "Ais.Net": "[0.4.2, )" + } + }, + "Ais.Net": { + "type": "CentralTransitive", + "requested": "[0.4.2, )", + "resolved": "0.4.2", + "contentHash": "+wtx8g0YwxEaDbilf4MiED9S4Yxr9J7RHAAMEN48+yB0it10KYdhmtB6dxt7qdCOvdoDnBOumggcGYefWMz+WA==" + } + }, + "net8.0": { + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[18.9.0, )", + "resolved": "18.9.0", + "contentHash": "Odp8djWEUFTJoRV02y0zJaAwAHF31CtNaDgnKKM+8dBHbbaH7Ee0oo9gweLpbzjI7UrtMUzH6xLJSrICZw+Nwg==", + "dependencies": { + "Microsoft.DiaSymReader": "2.2.9", + "Microsoft.Extensions.DependencyModel": "10.0.8", + "Microsoft.Testing.Platform": "2.3.0", + "System.IO.Pipelines": "10.0.8", + "System.Text.Encodings.Web": "10.0.8", + "System.Text.Json": "10.0.8" + } + }, + "Microsoft.Testing.Extensions.TrxReport": { + "type": "Direct", + "requested": "[2.3.2, )", + "resolved": "2.3.2", + "contentHash": "JzCtiOAmIfkPP2PpAKZeiH1hVYIVC9jMhsPWrd6PnH/Wtuct1UMuxpKi2F1cmXgfokc/HOadC6HOO1qzI23tnA==", + "dependencies": { + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.3.2", + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "MSTest.TestAdapter": { + "type": "Direct", + "requested": "[4.3.2, )", + "resolved": "4.3.2", + "contentHash": "uqpjJqQ3ws5z7Pq6BfMFpuqdrN2YIfe0Gfww91LRVXwxBdLqUaJd4JbHUImOSQvmK5m3PqW285kPjKbb69QFoA==", + "dependencies": { + "MSTest.TestFramework": "4.3.2", + "Microsoft.Testing.Extensions.VSTestBridge": "2.3.2", + "Microsoft.Testing.Platform.MSBuild": "2.3.2" + } + }, + "MSTest.TestFramework": { + "type": "Direct", + "requested": "[4.3.2, )", + "resolved": "4.3.2", + "contentHash": "Vlj3THt/BcOV/93k0Cdq29RNEsrY3TvUV6mF13rOuY8Gx7Ltd+LrOYId8AYu0cH9ioCYY/Sx1hppfI9p/vP+aw==", + "dependencies": { + "MSTest.Analyzers": "4.3.2" + } + }, + "NSubstitute": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "0gvKMbiJ+/WrfbcfBfqRZZrvfLJcd3rqkqVMjjlY5dtmLRVzMY+o/K/rJUStofQ2haSr9Vd04YDfvZtVVGS3/A==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "Shouldly": { + "type": "Direct", + "requested": "[4.3.0, )", + "resolved": "4.3.0", + "contentHash": "sDetrWXrl6YXZ4HeLsdBoNk3uIa7K+V4uvIJ+cqdRa5DrFxeTED7VkjoxCuU1kJWpUuBDZz2QXFzSxBtVXLwRQ==", + "dependencies": { + "DiffEngine": "11.3.0", + "EmptyFiles": "4.4.0" + } + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "DiffEngine": { + "type": "Transitive", + "resolved": "11.3.0", + "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==", + "dependencies": { + "EmptyFiles": "4.4.0", + "System.Management": "6.0.1" + } + }, + "EmptyFiles": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw==" + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.23.0", + "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==" + }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.2.9", + "contentHash": "WLbDc/W9XkHpGVtd1L6Vml8W6+PaaYZZTJASHjHF7W2WX/hld6bfNK102Ui+I3a6zIdx4+oKRJtMgHz44JmBfw==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "vLyZVpxmduO2jx+76ggqnsA3m81kwMY3NkWciNTj5E+Nvqb0VihqCvQP89QsGONWp0AJwMZG+u9GzaCjDdFGNw==", + "dependencies": { + "System.Text.Encodings.Web": "10.0.8", + "System.Text.Json": "10.0.8" + } + }, + "Microsoft.Testing.Extensions.Telemetry": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "Q1iapTk1DwFFT9aPtv4wxJHe22++NLxsg7IekEujac9RDAugxGQdCN0SQ1b9ryF78cdidhSBB17pHdCANurOyQ==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.23.0", + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "frw+Qa6gUxOZSw2Mw6SPtJT9gPzcP4nS9TfQhS0cHXKL6Rcp/CqLG+o4Psc1bHAhxW04rpzTBhjpNMnw2L7BuQ==", + "dependencies": { + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "Microsoft.Testing.Extensions.VSTestBridge": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "DXJIaLlwt+6GnPurlf7eVMI0wAZV5KCZrgGPibhx9i9LNxAkwEOK2jVzdVjLvvZ/SX9MUUaGJB9yXuF/VoT4/w==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "18.4.0", + "Microsoft.Testing.Extensions.Telemetry": "2.3.2", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.3.2", + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "FuJgIEfNU+rFHJYGhuqA8uup+3cryJ1N67Tijqo01Jd5xAr28ptz0aXkFfSqK3MhAUmGsrPpgQ92+3+0oXv8AA==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "2.3.2", + "contentHash": "guFvvaE8FGUlB6h24jN+P+NBz/P5x4On9sk4n5UapUAPq4fr0y1AgUe4sXmItp7r2NMe3rhZYLE1teP9T9sVbg==", + "dependencies": { + "Microsoft.Testing.Platform": "2.3.2" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "18.4.0", + "contentHash": "4L6m2kS2pY5uJ9cpeRxzW22opr6ttScIRqsOpMDQpgENp/ZwxkkQCcmc6LRSURo2dFaaSW5KVflQZvroiJ7Wzg==" + }, + "MSTest.Analyzers": { + "type": "Transitive", + "resolved": "4.3.2", + "contentHash": "GNvrVI6/Y5pNoKnK1/VELBls3XiHNZaj+zh14LtwlCqFI8zXTW3vuBe0Jv2qEr9wxHlOAQQBTJPRoNRjneGrKQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "STVNTIVd+UrYvSo31D2tOOTs19IThdjGDN14FS3/NZb4PWsVakAg/VT4sq+JYWGP/GtazzvXwebWL6LPBAwnFQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "HvFIJXM/CMTRu6PBQmjukQZ/O32Vx5fOEYZs0kq0OD5s9vJQ19KHLWCVmnfh3gNC7pbYomm06tzOTgJBflr/nQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "10.0.8", + "contentHash": "/fysUDkD7oFGaRPoA7IaFs0wRoO3GlwlCNq2P+xWZqxLy1R4cktRSKfMjJDy9ymS4grL7IDVdt8de8L9a0z55Q==", + "dependencies": { + "System.IO.Pipelines": "10.0.8", + "System.Text.Encodings.Web": "10.0.8" + } + }, + "ais.net.models": { + "type": "Project", + "dependencies": { + "Ais.Net": "[0.4.2, )" + } + }, + "Ais.Net": { + "type": "CentralTransitive", + "requested": "[0.4.2, )", + "resolved": "0.4.2", + "contentHash": "+wtx8g0YwxEaDbilf4MiED9S4Yxr9J7RHAAMEN48+yB0it10KYdhmtB6dxt7qdCOvdoDnBOumggcGYefWMz+WA==", + "dependencies": { + "System.IO.Pipelines": "4.7.4" + } + } + } + } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models.sln b/Solutions/Ais.Net.Models.sln deleted file mode 100644 index 476f7e4..0000000 --- a/Solutions/Ais.Net.Models.sln +++ /dev/null @@ -1,38 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.32126.317 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6C0840FD-F495-41BA-9D68-2B691313D2DC}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - ..\GitVersion.yml = ..\GitVersion.yml - ..\README.md = ..\README.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ais.Net.Models", "Ais.Net.Models\Ais.Net.Models.csproj", "{5B090F5E-28EA-4394-83DB-8C2764970FDC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ais.Net.Models.Specs", "Ais.Net.Models.Specs\Ais.Net.Models.Specs.csproj", "{AA6F29A1-3F00-4796-A7C7-9EDFD6C677F8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5B090F5E-28EA-4394-83DB-8C2764970FDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B090F5E-28EA-4394-83DB-8C2764970FDC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B090F5E-28EA-4394-83DB-8C2764970FDC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B090F5E-28EA-4394-83DB-8C2764970FDC}.Release|Any CPU.Build.0 = Release|Any CPU - {AA6F29A1-3F00-4796-A7C7-9EDFD6C677F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA6F29A1-3F00-4796-A7C7-9EDFD6C677F8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA6F29A1-3F00-4796-A7C7-9EDFD6C677F8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA6F29A1-3F00-4796-A7C7-9EDFD6C677F8}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B807F890-1DEB-4C00-A4FC-67EC55DE21F5} - EndGlobalSection -EndGlobal diff --git a/Solutions/Ais.Net.Models.slnx b/Solutions/Ais.Net.Models.slnx new file mode 100644 index 0000000..830c3a3 --- /dev/null +++ b/Solutions/Ais.Net.Models.slnx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Solutions/Ais.Net.Models/Ais.Net.Models.csproj b/Solutions/Ais.Net.Models/Ais.Net.Models.csproj index 3d1147f..a0e40c7 100644 --- a/Solutions/Ais.Net.Models/Ais.Net.Models.csproj +++ b/Solutions/Ais.Net.Models/Ais.Net.Models.csproj @@ -3,10 +3,12 @@ - net8.0 + net8.0;net10.0 latest enable True + + true @@ -17,12 +19,12 @@ - RCS1029;SA1313;SA1009;SA1600;SA1591;CS1591; + RCS1029;SA1313;SA1009; - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisIsAssigned.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisIsAssigned.cs index 3eb08c4..8e1c6e0 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisIsAssigned.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisIsAssigned.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the AIS assigned-mode status flag. +/// public interface IAisIsAssigned { + /// + /// Gets a value indicating whether the station is operating in assigned mode. + /// bool IsAssigned { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisIsDteNotReady.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisIsDteNotReady.cs index 05f42ce..01bb786 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisIsDteNotReady.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisIsDteNotReady.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the AIS data terminal equipment (DTE) readiness status flag. +/// public interface IAisIsDteNotReady { + /// + /// Gets a value indicating whether the data terminal equipment (DTE) is not ready. + /// bool IsDteNotReady { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessage.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessage.cs index 345d16b..3b87794 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessage.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessage.cs @@ -4,6 +4,9 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Common abstraction for an AIS message, combining vessel identity with the message type identifier. +/// public interface IAisMessage : IVesselIdentity, IAisMessageType { } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType.cs index 8681284..f70c7c7 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the numeric AIS message type identifier. +/// public interface IAisMessageType { + /// + /// Gets the numeric AIS message type identifier. + /// int MessageType { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType18.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType18.cs index bbb01d1..ca3c33a 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType18.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType18.cs @@ -4,21 +4,48 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Describes the properties specific to an AIS message type 18 (Class B "CS" position report). +/// public interface IAisMessageType18 { + /// + /// Gets a value indicating whether the unit can accept a channel assignment via a message type 22. + /// bool CanAcceptMessage22ChannelAssignment { get; } + /// + /// Gets a value indicating whether the unit can switch frequency bands. + /// bool CanSwitchBands { get; } + /// + /// Gets the Class B carrier sense (CS) unit type. + /// ClassBUnit CsUnit { get; } + /// + /// Gets a value indicating whether the unit has a display. + /// bool HasDisplay { get; } + /// + /// Gets a value indicating whether a digital selective calling (DSC) transceiver is attached. + /// bool IsDscAttached { get; } + /// + /// Gets the Class B radio status type indicating how the radio status field is encoded. + /// ClassBRadioStatusType RadioStatusType { get; } + /// + /// Gets the regional reserved value held in the bits at offset 139. + /// int RegionalReserved139 { get; } + /// + /// Gets the regional reserved value held in the bits at offset 38. + /// int RegionalReserved38 { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType19.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType19.cs index 1693bbf..38df203 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType19.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType19.cs @@ -4,13 +4,28 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Describes the properties specific to an AIS message type 19 (Class B "SO" extended position report). +/// public interface IAisMessageType19 { + /// + /// Gets the regional reserved value held in the bits at offset 139. + /// int RegionalReserved139 { get; } + /// + /// Gets the regional reserved value held in the bits at offset 38. + /// int RegionalReserved38 { get; } + /// + /// Gets the name of the ship. + /// string ShipName { get; } + /// + /// Gets the spare value held in the bits at offset 308. + /// uint Spare308 { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType1to3.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType1to3.cs index 7c98287..6c65c84 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType1to3.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType1to3.cs @@ -4,17 +4,38 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Describes the properties specific to an AIS message type 1, 2 or 3 (Class A position report). +/// public interface IAisMessageType1to3 { + /// + /// Gets the special manoeuvre indicator for the vessel. + /// ManoeuvreIndicator ManoeuvreIndicator { get; } + /// + /// Gets the number of frames remaining until the transmission slot times out. + /// uint RadioSlotTimeout { get; } + /// + /// Gets the radio sub-message, whose meaning depends on the slot timeout value. + /// uint RadioSubMessage { get; } + /// + /// Gets the radio synchronisation state. + /// RadioSyncState RadioSyncState { get; } + /// + /// Gets the rate of turn of the vessel. + /// int RateOfTurn { get; } + /// + /// Gets the spare value held in the bits at offset 145. + /// uint SpareBits145 { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType24Part0.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType24Part0.cs index eb3d8cc..040599e 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType24Part0.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType24Part0.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Describes the properties specific to an AIS message type 24 Part A (static data report carrying the vessel name). +/// public interface IAisMessageType24Part0 { + /// + /// Gets the spare value held in the bits at offset 160. + /// uint Spare160 { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType24Part1.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType24Part1.cs index 0d08ae9..4fd1247 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType24Part1.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType24Part1.cs @@ -4,17 +4,38 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Describes the properties specific to an AIS message type 24 Part B (static data report carrying vessel dimensions and equipment details). +/// public interface IAisMessageType24Part1 { + /// + /// Gets the MMSI of the mothership associated with this unit. + /// uint MothershipMmsi { get; } + /// + /// Gets the manufacturer-assigned serial number of the unit. + /// uint SerialNumber { get; } + /// + /// Gets the spare value held in the bits at offset 162. + /// uint Spare162 { get; } + /// + /// Gets the manufacturer-assigned unit model code. + /// uint UnitModelCode { get; } + /// + /// Gets the vendor identifier as interpreted under ITU-R M.1371 revision 3. + /// string VendorIdRev3 { get; } + /// + /// Gets the vendor identifier as interpreted under ITU-R M.1371 revision 4. + /// string VendorIdRev4 { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType27.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType27.cs index efc086b..8836ba2 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType27.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType27.cs @@ -1,12 +1,21 @@ -// -// Copyright (c) Endjin Limited. All rights reserved. -// - -namespace Ais.Net.Models.Abstractions; - -public interface IAisMessageType27 -{ - bool GnssPositionStatus { get; } - - Position? Position { get; } +// +// Copyright (c) Endjin Limited. All rights reserved. +// + +namespace Ais.Net.Models.Abstractions; + +/// +/// Describes the properties specific to an AIS message type 27 (long-range broadcast position report). +/// +public interface IAisMessageType27 +{ + /// + /// Gets a value indicating whether the GNSS position is being reported by the position system. + /// + bool GnssPositionStatus { get; } + + /// + /// Gets the reported position of the vessel, or if no position is available. + /// + Position? Position { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType5.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType5.cs index 4683a7c..c7c7a7e 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType5.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMessageType5.cs @@ -4,23 +4,53 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Describes the properties specific to an AIS message type 5 (static and voyage related data). +/// public interface IAisMessageType5 { + /// + /// Gets the AIS version indicator supported by the station. + /// uint AisVersion { get; } + /// + /// Gets the destination of the voyage. + /// string Destination { get; } + /// + /// Gets the maximum present static draught, expressed in tenths of a metre. + /// uint Draught10thMetres { get; } + /// + /// Gets the month component of the estimated time of arrival (ETA). + /// uint EtaMonth { get; } + /// + /// Gets the day component of the estimated time of arrival (ETA). + /// uint EtaDay { get; } + /// + /// Gets the hour component of the estimated time of arrival (ETA). + /// uint EtaHour { get; } + /// + /// Gets the minute component of the estimated time of arrival (ETA). + /// uint EtaMinute { get; } + /// + /// Gets the IMO number identifying the vessel. + /// uint ImoNumber { get; } + /// + /// Gets the spare value held in the bits at offset 423. + /// uint Spare423 { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMultipartMessage.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMultipartMessage.cs index b116ed4..88466ce 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMultipartMessage.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisMultipartMessage.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Marks an AIS message that is split across multiple parts. +/// public interface IAisMultipartMessage { + /// + /// Gets the zero-based part number identifying this part of the multipart message. + /// uint PartNumber { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisPositionFixType.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisPositionFixType.cs index b6d3b52..93ba542 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisPositionFixType.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IAisPositionFixType.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the electronic position fixing device (EPFD) type used by the vessel. +/// public interface IAisPositionFixType { + /// + /// Gets the type of electronic position fixing device (EPFD) used to obtain the position fix. + /// EpfdFixType PositionFixType { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ICallSign.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ICallSign.cs index 44fc845..7938a6e 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ICallSign.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ICallSign.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the vessel's radio call sign. +/// public interface ICallSign { + /// + /// Gets the vessel's radio call sign. + /// string CallSign { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IRaimFlag.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IRaimFlag.cs index 1c1a6fb..e05f44f 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IRaimFlag.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IRaimFlag.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes whether Receiver Autonomous Integrity Monitoring (RAIM) is in use. +/// public interface IRaimFlag { + /// + /// Gets a value indicating whether Receiver Autonomous Integrity Monitoring (RAIM) is in use by the position fixing device. + /// bool RaimFlag { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IRepeatIndicator.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IRepeatIndicator.cs index a0a50b1..b1286f2 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IRepeatIndicator.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IRepeatIndicator.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes how many times the message has been repeated by a repeater. +/// public interface IRepeatIndicator { + /// + /// Gets the number of times the message has been repeated by a repeater. + /// uint RepeatIndicator { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IShipType.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IShipType.cs index eb1b85a..fb5013a 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IShipType.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IShipType.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the ship-and-cargo type. +/// public interface IShipType { + /// + /// Gets the ship-and-cargo type of the vessel. + /// ShipType ShipType { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselCourseOverGround.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselCourseOverGround.cs index f3e5bf2..6b30b55 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselCourseOverGround.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselCourseOverGround.cs @@ -1,10 +1,16 @@ -// -// Copyright (c) Endjin Limited. All rights reserved. -// - -namespace Ais.Net.Models.Abstractions; - -public interface IVesselCourseOverGround -{ - float? CourseOverGround { get; } +// +// Copyright (c) Endjin Limited. All rights reserved. +// + +namespace Ais.Net.Models.Abstractions; + +/// +/// Exposes the vessel's course over ground. +/// +public interface IVesselCourseOverGround +{ + /// + /// Gets the course over ground in degrees, or null when not available. + /// + float? CourseOverGround { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselDimensions.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselDimensions.cs index 8e4388f..a7f8f35 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselDimensions.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselDimensions.cs @@ -4,13 +4,28 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the vessel's dimensions, measured in metres from the position reference point. +/// public interface IVesselDimensions { + /// + /// Gets the distance in metres from the position reference point to the bow of the vessel. + /// uint DimensionToBow { get; } + /// + /// Gets the distance in metres from the position reference point to the port side of the vessel. + /// uint DimensionToPort { get; } + /// + /// Gets the distance in metres from the position reference point to the starboard side of the vessel. + /// uint DimensionToStarboard { get; } + /// + /// Gets the distance in metres from the position reference point to the stern of the vessel. + /// uint DimensionToStern { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselIdentity.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselIdentity.cs index b2bed8d..baa02c2 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselIdentity.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselIdentity.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the vessel's identity. +/// public interface IVesselIdentity { + /// + /// Gets the vessel's Maritime Mobile Service Identity (MMSI). + /// uint Mmsi { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselName.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselName.cs index 3aa233f..a66bfa7 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselName.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselName.cs @@ -4,7 +4,13 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the vessel's name. +/// public interface IVesselName { + /// + /// Gets the vessel's name. + /// string VesselName { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselNavigation.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselNavigation.cs index 4acc947..f186ee4 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselNavigation.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselNavigation.cs @@ -4,11 +4,23 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Exposes the vessel's navigation data, including position, course, speed and heading. +/// public interface IVesselNavigation: IVesselPositionAccuracy, IVesselSpeedOverGround, IVesselCourseOverGround { + /// + /// Gets the vessel's position, or null when not available. + /// Position? Position { get; } + /// + /// Gets the UTC second at which the position was reported. + /// uint TimeStampSecond { get; } + /// + /// Gets the vessel's true heading in degrees. + /// uint TrueHeadingDegrees { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselNavigationStatus.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselNavigationStatus.cs index 58558e7..7fb86a6 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselNavigationStatus.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselNavigationStatus.cs @@ -1,10 +1,16 @@ -// -// Copyright (c) Endjin Limited. All rights reserved. -// - -namespace Ais.Net.Models.Abstractions; - -public interface IVesselNavigationStatus -{ - NavigationStatus NavigationStatus { get; } +// +// Copyright (c) Endjin Limited. All rights reserved. +// + +namespace Ais.Net.Models.Abstractions; + +/// +/// Exposes the vessel's navigation status. +/// +public interface IVesselNavigationStatus +{ + /// + /// Gets the vessel's navigation status. + /// + NavigationStatus NavigationStatus { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselPositionAccuracy.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselPositionAccuracy.cs index a47eb94..8b83811 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselPositionAccuracy.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselPositionAccuracy.cs @@ -1,10 +1,16 @@ -// -// Copyright (c) Endjin Limited. All rights reserved. -// - -namespace Ais.Net.Models.Abstractions; - -public interface IVesselPositionAccuracy -{ - bool PositionAccuracy { get; } +// +// Copyright (c) Endjin Limited. All rights reserved. +// + +namespace Ais.Net.Models.Abstractions; + +/// +/// Exposes whether a high-accuracy (DGPS) position fix is indicated. +/// +public interface IVesselPositionAccuracy +{ + /// + /// Gets a value indicating whether a high-accuracy (DGPS) position fix is indicated. + /// + bool PositionAccuracy { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselSpeedOverGround.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselSpeedOverGround.cs index 66a4243..ded9689 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselSpeedOverGround.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/IVesselSpeedOverGround.cs @@ -1,10 +1,16 @@ -// -// Copyright (c) Endjin Limited. All rights reserved. -// - -namespace Ais.Net.Models.Abstractions; - -public interface IVesselSpeedOverGround -{ - float? SpeedOverGround { get; } +// +// Copyright (c) Endjin Limited. All rights reserved. +// + +namespace Ais.Net.Models.Abstractions; + +/// +/// Exposes the vessel's speed over ground. +/// +public interface IVesselSpeedOverGround +{ + /// + /// Gets the speed over ground in knots, or null when not available. + /// + float? SpeedOverGround { get; } } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/Position.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/Position.cs index d692a41..edb6cf6 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/Position.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/Position.cs @@ -1,14 +1,33 @@ -// -// Copyright (c) Endjin Limited. All rights reserved. -// - -namespace Ais.Net.Models.Abstractions; - -public record Position(double Latitude, double Longitude) -{ - public static Position From10000thMins(int latitude, int longitude) => - new (latitude.From10000thMinsToDegrees(), longitude.From10000thMinsToDegrees()); - - public static Position From10thMins(int latitude, int longitude) => - new(latitude.From10thMinsToDegrees(), longitude.From10thMinsToDegrees()); -} \ No newline at end of file +// +// Copyright (c) Endjin Limited. All rights reserved. +// + +namespace Ais.Net.Models.Abstractions; + +/// +/// A geographic position expressed as latitude and longitude in degrees. +/// +/// The latitude in degrees (positive north of the equator). +/// The longitude in degrees (positive east of the prime meridian). +public record Position(double Latitude, double Longitude) +{ + /// + /// Creates a from coordinates expressed in ten-thousandths of a minute + /// (the encoding used by most AIS position reports). + /// + /// The latitude in units of 1/10000 minute. + /// The longitude in units of 1/10000 minute. + /// The position in degrees. + public static Position From10000thMins(int latitude, int longitude) => + new (latitude.From10000thMinsToDegrees(), longitude.From10000thMinsToDegrees()); + + /// + /// Creates a from coordinates expressed in tenths of a minute (the + /// lower-precision encoding used by the long-range message type 27). + /// + /// The latitude in units of 1/10 minute. + /// The longitude in units of 1/10 minute. + /// The position in degrees. + public static Position From10thMins(int latitude, int longitude) => + new(latitude.From10thMinsToDegrees(), longitude.From10thMinsToDegrees()); +} diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ShipTypeCategory.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ShipTypeCategory.cs index 97545ac..9cc3e81 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ShipTypeCategory.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ShipTypeCategory.cs @@ -4,16 +4,58 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Coarse classification of a vessel derived from the AIS ship type code. +/// public enum ShipTypeCategory { + /// + /// No ship type category is available. + /// NotAvailable = 0, + + /// + /// Reserved for future use. + /// Reserved = 1, + + /// + /// Wing in ground (WIG) craft. + /// WingInGround = 2, + + /// + /// Special category reserved for the ship type code range beginning with 3. + /// SpecialCategory3 = 3, + + /// + /// High speed craft (HSC). + /// HighSpeedCraft = 4, + + /// + /// Special category reserved for the ship type code range beginning with 5. + /// SpecialCategory5 = 5, + + /// + /// Passenger vessel. + /// Passenger = 6, + + /// + /// Cargo vessel. + /// Cargo = 7, + + /// + /// Tanker. + /// Tanker = 8, + + /// + /// Other type of vessel. + /// Other = 9 } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ShipTypeGroup.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ShipTypeGroup.cs index 1062b7b..1968510 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ShipTypeGroup.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/Abstractions/ShipTypeGroup.cs @@ -4,55 +4,218 @@ namespace Ais.Net.Models.Abstractions; +/// +/// Fine-grained grouping of a vessel derived from the AIS ship type code. +/// public enum ShipTypeGroup { + /// + /// No ship type group is available. + /// NotAvailable, + + /// + /// Reserved for future use. + /// Reserved, + + /// + /// Wing in ground craft, all ships of this type. + /// WingInGroundAll, + + /// + /// Wing in ground craft carrying hazardous or polluting cargo. + /// WingInGroundHazardous, + + /// + /// Wing in ground craft, reserved for future use. + /// WingInGroundReserved, + /// + /// Fishing vessel. + /// Fishing, + + /// + /// Vessel engaged in towing. + /// Towing, + + /// + /// Vessel engaged in towing where the tow exceeds 200 metres in length or 25 metres in breadth. + /// TowingLengthOver200MOrBreadthOver25M, + + /// + /// Vessel engaged in dredging or underwater operations. + /// DredgingOrUnderwaterOps, + + /// + /// Vessel engaged in diving operations. + /// DivingOps, + + /// + /// Vessel engaged in military operations. + /// MilitaryOps, + + /// + /// Sailing vessel. + /// Sailing, + + /// + /// Pleasure craft. + /// PleasureCraft, + /// + /// High speed craft, all ships of this type. + /// HighSpeedCraftAll, + + /// + /// High speed craft carrying hazardous or polluting cargo. + /// HighSpeedCraftHazardous, + + /// + /// High speed craft, reserved for future use. + /// HighSpeedCraftReserved, + + /// + /// High speed craft with no additional information. + /// HighSpeedCraftNoAdditionalInformation, + /// + /// Pilot vessel. + /// PilotVessel, + + /// + /// Search and rescue vessel. + /// SearchAndRescueVessel, + + /// + /// Tug. + /// Tug, + + /// + /// Port tender. + /// PortTender, + + /// + /// Vessel carrying anti-pollution equipment. + /// AntiPollutionEquipment, + + /// + /// Law enforcement vessel. + /// LawEnforcement, + + /// + /// Spare, reserved for local or regional vessel assignments. + /// SpareLocalVessel, + + /// + /// Medical transport. + /// MedicalTransport, + + /// + /// Ship or aircraft not party to an armed conflict (noncombatant). + /// NoncombatantShip, + /// + /// Passenger vessel, all ships of this type. + /// PassengerAll, + + /// + /// Passenger vessel carrying hazardous or polluting cargo. + /// PassengerHazardous, + + /// + /// Passenger vessel, reserved for future use. + /// PassengerReserved, + + /// + /// Passenger vessel with no additional information. + /// PassengerNoAdditionalInformation, + /// + /// Cargo vessel, all ships of this type. + /// CargoAll, + + /// + /// Cargo vessel carrying hazardous or polluting cargo. + /// CargoHazardous, + + /// + /// Cargo vessel, reserved for future use. + /// CargoReserved, + + /// + /// Cargo vessel with no additional information. + /// CargoNoAdditionalInformation, + /// + /// Tanker, all ships of this type. + /// TankerAll, + + /// + /// Tanker carrying hazardous or polluting cargo. + /// TankerHazardous, + + /// + /// Tanker, reserved for future use. + /// TankerReserved, + + /// + /// Tanker with no additional information. + /// TankerNoAdditionalInformation, + /// + /// Other type of vessel, all ships of this type. + /// OtherAll, + + /// + /// Other type of vessel carrying hazardous or polluting cargo. + /// OtherHazardous, + + /// + /// Other type of vessel, reserved for future use. + /// OtherReserved, + + /// + /// Other type of vessel with no additional information. + /// OtherNoAdditionalInformation, } \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageBase.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageBase.cs index 900152c..836e0c7 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageBase.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageBase.cs @@ -6,4 +6,9 @@ namespace Ais.Net.Models; +/// +/// The abstract base for all AIS messages, carrying the fields common to every message type. +/// +/// The AIS message type identifier. +/// The Maritime Mobile Service Identity of the transmitting station. public record AisMessageBase(int MessageType, uint Mmsi) : IAisMessage; \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageExtensions.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageExtensions.cs index 212173f..0d1f5a8 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageExtensions.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageExtensions.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) Endjin Limited. All rights reserved. // @@ -8,51 +8,138 @@ namespace Ais.Net.Models; +/// +/// Helper methods for converting raw AIS field values into the units and types exposed by the +/// model records, and for classifying ship-type codes. +/// public static class AisMessageExtensions { + /// + /// Converts a coordinate expressed in ten-thousandths of a minute (the encoding used by most + /// AIS position reports) to degrees. + /// + /// The coordinate in units of 1/10000 minute. + /// The coordinate in degrees. public static double From10000thMinsToDegrees(this int value) { return value / 600000.0; } + /// + /// Converts a coordinate expressed in tenths of a minute (the lower-precision encoding used by + /// the long-range message type 27) to degrees. + /// + /// The coordinate in units of 1/10 minute. + /// The coordinate in degrees. public static double From10thMinsToDegrees(this int value) { return value / 600.0; } + /// + /// Converts a value expressed in tenths (for example speed over ground in 1/10 knot) to its + /// real value, treating the sentinel 1023 as "not available". + /// + /// The raw value in tenths. + /// The scaled value, or when the value is the 1023 "not available" sentinel. public static float? FromTenths(this uint value) { return value == 1023 ? null : (value / 10.0f); } + /// + /// Converts a value expressed in tenths of a degree (for example course over ground) to + /// degrees, treating the sentinel 3600 as "not available". + /// + /// The raw value in tenths of a degree. + /// The value in degrees, or when the value is the 3600 "not available" sentinel. public static float? FromTenthsToDegrees(this uint value) { return value == 3600 ? null : (value / 10.0f); } + /// + /// Removes the '@' and space padding that AIS applies to text name fields and collapses any + /// run of interior spaces to a single space, preserving significant single spaces (such as in + /// "QUEEN MARY 2"). + /// + /// The raw, padded vessel name. + /// The cleaned vessel name, or an empty string when the input is entirely padding. public static string CleanVesselName(this string value) { - return value.Trim('@').Trim().Replace(" ", " "); + // AIS pads names with '@' (6-bit value 0) and/or spaces. Strip that padding from both + // ends (order-independently) and collapse any run of interior spaces to a single space + // (a single interior space is significant, e.g. "QUEEN MARY 2"). The result is built + // through a stack buffer so that only the returned string is allocated. + ReadOnlySpan trimmed = value.AsSpan().Trim("@ "); + if (trimmed.IsEmpty) + { + return string.Empty; + } + + // AIS text fields are short (<= 20 chars); the heap fallback guards against a pathological + // caller passing an over-long string to what is otherwise a stackalloc. + Span buffer = trimmed.Length <= 256 ? stackalloc char[256] : new char[trimmed.Length]; + int length = 0; + bool previousWasSpace = false; + foreach (char c in trimmed) + { + bool isSpace = c == ' '; + if (isSpace && previousWasSpace) + { + continue; + } + + buffer[length++] = c; + previousWasSpace = isSpace; + } + + return new string(buffer[..length]); } - public static string GetString(this Span value) + /// + /// Decodes a span of ASCII bytes to a string. + /// + /// The ASCII bytes to decode. + /// The decoded string. + public static string GetString(this ReadOnlySpan value) { return Encoding.ASCII.GetString(value); } + /// + /// Decodes an AIS 6-bit ASCII text field to a string. + /// + /// The parser positioned over the text field. + /// The decoded text. public static string TextFieldToString(this NmeaAisTextFieldParser field) { - Span ascii = stackalloc byte[(int)field.CharacterCount]; - field.WriteAsAscii(ascii); + int characterCount = (int)field.CharacterCount; - return Encoding.ASCII.GetString(ascii); + // AIS text fields are short; the heap fallback guards against a field that reports a + // pathologically large character count from overflowing the stack. + Span ascii = characterCount <= 256 ? stackalloc byte[256] : new byte[characterCount]; + Span field6BitAscii = ascii[..characterCount]; + field.WriteAsAscii(field6BitAscii); + + return Encoding.ASCII.GetString(field6BitAscii); } + /// + /// Maps a ship-and-cargo type to its coarse . + /// + /// The ship type. + /// The category the ship type belongs to. public static ShipTypeCategory ToShipTypeCategory(this ShipType shipType) { return shipType.ToShipTypeGroup().ToShipTypeCategory(); } + /// + /// Maps a to its coarse . + /// + /// The ship-type group. + /// The category the group belongs to. public static ShipTypeCategory ToShipTypeCategory(this ShipTypeGroup shipTypeGroup) { return shipTypeGroup switch @@ -103,148 +190,133 @@ public static ShipTypeCategory ToShipTypeCategory(this ShipTypeGroup shipTypeGro }; } + /// + /// Returns the human-readable description for a raw ship-and-cargo type code. + /// + /// The raw ship-type code (0-99). + /// The description, or "Not available" when the code is out of range. public static string ToShipTypeDescription(this int shipType) { - switch (shipType) - { - case 0: goto default; - case >= 1 and <= 19: return "Reserved for future use"; - case 20: return "Wing in ground(WIG), all ships of this type"; - case 21: return "Wing in ground(WIG), Hazardous category A"; - case 22: return "Wing in ground(WIG), Hazardous category B"; - case 23: return "Wing in ground(WIG), Hazardous category C"; - case 24: return "Wing in ground(WIG), Hazardous category D"; - case 25: return "Wing in ground(WIG), Reserved for future use"; - case 26: return "Wing in ground(WIG), Reserved for future use"; - case 27: return "Wing in ground(WIG), Reserved for future use"; - case 28: return "Wing in ground(WIG), Reserved for future use"; - case 29: return "Wing in ground(WIG), Reserved for future use"; - case 30: return "Fishing"; - case 31: return "Towing"; - case 32: return "Towing: length exceeds 200m or breadth exceeds 25m"; - case 33: return "Dredging or underwater ops"; - case 34: return "Diving ops"; - case 35: return "Military ops"; - case 36: return "Sailing"; - case 37: return "Pleasure Craft"; - case 38: return "Reserved"; - case 39: return "Reserved"; - case 40: return "High speed craft(HSC), all ships of this type"; - case 41: return "High speed craft(HSC), Hazardous category A"; - case 42: return "High speed craft(HSC), Hazardous category B"; - case 43: return "High speed craft(HSC), Hazardous category C"; - case 44: return "High speed craft(HSC), Hazardous category D"; - case 45: return "High speed craft(HSC), Reserved for future use"; - case 46: return "High speed craft(HSC), Reserved for future use"; - case 47: return "High speed craft(HSC), Reserved for future use"; - case 48: return "High speed craft(HSC), Reserved for future use"; - case 49: return "High speed craft(HSC), No additional information"; - case 50: return "Pilot Vessel"; - case 51: return "Search and Rescue vessel"; - case 52: return "Tug"; - case 53: return "Port Tender"; - case 54: return "Anti - pollution equipment"; - case 55: return "Law Enforcement"; - case 56: return "Spare - Local Vessel"; - case 57: return "Spare - Local Vessel"; - case 58: return "Medical Transport"; - case 59: return "Noncombatant ship according to RR Resolution No. 18"; - case 60: return "Passenger, all ships of this type"; - case 61: return "Passenger, Hazardous category A"; - case 62: return "Passenger, Hazardous category B"; - case 63: return "Passenger, Hazardous category C"; - case 64: return "Passenger, Hazardous category D"; - case 65: return "Passenger, Reserved for future use"; - case 66: return "Passenger, Reserved for future use"; - case 67: return "Passenger, Reserved for future use"; - case 68: return "Passenger, Reserved for future use"; - case 69: return "Passenger, No additional information"; - case 70: return "Cargo, all ships of this type"; - case 71: return "Cargo, Hazardous category A"; - case 72: return "Cargo, Hazardous category B"; - case 73: return "Cargo, Hazardous category C"; - case 74: return "Cargo, Hazardous category D"; - case 75: return "Cargo, Reserved for future use"; - case 76: return "Cargo, Reserved for future use"; - case 77: return "Cargo, Reserved for future use"; - case 78: return "Cargo, Reserved for future use"; - case 79: return "Cargo, No additional information"; - case 80: return "Tanker, all ships of this type"; - case 81: return "Tanker, Hazardous category A"; - case 82: return "Tanker, Hazardous category B"; - case 83: return "Tanker, Hazardous category C"; - case 84: return "Tanker, Hazardous category D"; - case 85: return "Tanker, Reserved for future use"; - case 86: return "Tanker, Reserved for future use"; - case 87: return "Tanker, Reserved for future use"; - case 88: return "Tanker, Reserved for future use"; - case 89: return "Tanker, No additional information"; - case 90: return "Other Type, all ships of this type"; - case 91: return "Other Type, Hazardous category A"; - case 92: return "Other Type, Hazardous category B"; - case 93: return "Other Type, Hazardous category C"; - case 94: return "Other Type, Hazardous category D"; - case 95: return "Other Type, Reserved for future use"; - case 96: return "Other Type, Reserved for future use"; - case 97: return "Other Type, Reserved for future use"; - case 98: return "Other Type, Reserved for future use"; - case 99: return "Other Type, no additional information"; - default: return "Not available"; - } + return (uint)shipType < (uint)ShipTypeTable.Length + ? ShipTypeTable[shipType].Description + : NotAvailableDescription; } + /// + /// Maps a ship-and-cargo type to its . + /// + /// The ship type. + /// The group the ship type belongs to. public static ShipTypeGroup ToShipTypeGroup(this ShipType shipType) { return ToShipTypeGroup((int)shipType); } + /// + /// Maps a raw ship-and-cargo type code to its . + /// + /// The raw ship-type code (0-99). + /// The group the code belongs to, or when the code is out of range. public static ShipTypeGroup ToShipTypeGroup(this int shipType) { - switch (shipType) + return (uint)shipType < (uint)ShipTypeTable.Length + ? ShipTypeTable[shipType].Group + : ShipTypeGroup.NotAvailable; + } + + private const string NotAvailableDescription = "Not available"; + + // Single source of truth for ship-type classification: each raw code (0-99) is mapped to its + // group AND its human-readable description in exactly one place, so they can never disagree. + // (A previous bug had code 49 classified as "reserved" by the group switch while the separate + // description switch said "no additional information".) Category is derived from the group, so + // all three facets flow from this one table. + private static readonly ShipTypeInfo[] ShipTypeTable = BuildShipTypeTable(); + + private static ShipTypeInfo[] BuildShipTypeTable() + { + ShipTypeInfo[] table = new ShipTypeInfo[100]; + for (int i = 0; i < table.Length; i++) + { + table[i] = new ShipTypeInfo(ShipTypeGroup.NotAvailable, NotAvailableDescription); + } + + SetRange(table, 1, 19, ShipTypeGroup.Reserved, "Reserved for future use"); + + // Wing in ground (20-29): note this band has no "no additional information" code. + table[20] = new ShipTypeInfo(ShipTypeGroup.WingInGroundAll, "Wing in ground(WIG), all ships of this type"); + SetHazardousCategories(table, 21, ShipTypeGroup.WingInGroundHazardous, "Wing in ground(WIG)"); + SetRange(table, 25, 29, ShipTypeGroup.WingInGroundReserved, "Wing in ground(WIG), Reserved for future use"); + + table[30] = new ShipTypeInfo(ShipTypeGroup.Fishing, "Fishing"); + table[31] = new ShipTypeInfo(ShipTypeGroup.Towing, "Towing"); + table[32] = new ShipTypeInfo(ShipTypeGroup.TowingLengthOver200MOrBreadthOver25M, "Towing: length exceeds 200m or breadth exceeds 25m"); + table[33] = new ShipTypeInfo(ShipTypeGroup.DredgingOrUnderwaterOps, "Dredging or underwater ops"); + table[34] = new ShipTypeInfo(ShipTypeGroup.DivingOps, "Diving ops"); + table[35] = new ShipTypeInfo(ShipTypeGroup.MilitaryOps, "Military ops"); + table[36] = new ShipTypeInfo(ShipTypeGroup.Sailing, "Sailing"); + table[37] = new ShipTypeInfo(ShipTypeGroup.PleasureCraft, "Pleasure Craft"); + SetRange(table, 38, 39, ShipTypeGroup.Reserved, "Reserved"); + + // High speed craft (40-49): code 49 is "no additional information", not reserved. + table[40] = new ShipTypeInfo(ShipTypeGroup.HighSpeedCraftAll, "High speed craft(HSC), all ships of this type"); + SetHazardousCategories(table, 41, ShipTypeGroup.HighSpeedCraftHazardous, "High speed craft(HSC)"); + SetRange(table, 45, 48, ShipTypeGroup.HighSpeedCraftReserved, "High speed craft(HSC), Reserved for future use"); + table[49] = new ShipTypeInfo(ShipTypeGroup.HighSpeedCraftNoAdditionalInformation, "High speed craft(HSC), No additional information"); + + table[50] = new ShipTypeInfo(ShipTypeGroup.PilotVessel, "Pilot Vessel"); + table[51] = new ShipTypeInfo(ShipTypeGroup.SearchAndRescueVessel, "Search and Rescue vessel"); + table[52] = new ShipTypeInfo(ShipTypeGroup.Tug, "Tug"); + table[53] = new ShipTypeInfo(ShipTypeGroup.PortTender, "Port Tender"); + table[54] = new ShipTypeInfo(ShipTypeGroup.AntiPollutionEquipment, "Anti - pollution equipment"); + table[55] = new ShipTypeInfo(ShipTypeGroup.LawEnforcement, "Law Enforcement"); + SetRange(table, 56, 57, ShipTypeGroup.SpareLocalVessel, "Spare - Local Vessel"); + table[58] = new ShipTypeInfo(ShipTypeGroup.MedicalTransport, "Medical Transport"); + table[59] = new ShipTypeInfo(ShipTypeGroup.NoncombatantShip, "Noncombatant ship according to RR Resolution No. 18"); + + // Passenger (60-69). + table[60] = new ShipTypeInfo(ShipTypeGroup.PassengerAll, "Passenger, all ships of this type"); + SetHazardousCategories(table, 61, ShipTypeGroup.PassengerHazardous, "Passenger"); + SetRange(table, 65, 68, ShipTypeGroup.PassengerReserved, "Passenger, Reserved for future use"); + table[69] = new ShipTypeInfo(ShipTypeGroup.PassengerNoAdditionalInformation, "Passenger, No additional information"); + + // Cargo (70-79). + table[70] = new ShipTypeInfo(ShipTypeGroup.CargoAll, "Cargo, all ships of this type"); + SetHazardousCategories(table, 71, ShipTypeGroup.CargoHazardous, "Cargo"); + SetRange(table, 75, 78, ShipTypeGroup.CargoReserved, "Cargo, Reserved for future use"); + table[79] = new ShipTypeInfo(ShipTypeGroup.CargoNoAdditionalInformation, "Cargo, No additional information"); + + // Tanker (80-89). + table[80] = new ShipTypeInfo(ShipTypeGroup.TankerAll, "Tanker, all ships of this type"); + SetHazardousCategories(table, 81, ShipTypeGroup.TankerHazardous, "Tanker"); + SetRange(table, 85, 88, ShipTypeGroup.TankerReserved, "Tanker, Reserved for future use"); + table[89] = new ShipTypeInfo(ShipTypeGroup.TankerNoAdditionalInformation, "Tanker, No additional information"); + + // Other (90-99): code 99 uses lowercase "no additional information" in the source data. + table[90] = new ShipTypeInfo(ShipTypeGroup.OtherAll, "Other Type, all ships of this type"); + SetHazardousCategories(table, 91, ShipTypeGroup.OtherHazardous, "Other Type"); + SetRange(table, 95, 98, ShipTypeGroup.OtherReserved, "Other Type, Reserved for future use"); + table[99] = new ShipTypeInfo(ShipTypeGroup.OtherNoAdditionalInformation, "Other Type, no additional information"); + + return table; + } + + private static void SetHazardousCategories(ShipTypeInfo[] table, int firstCode, ShipTypeGroup group, string descriptionPrefix) + { + // The four consecutive codes map to "Hazardous category A" through "Hazardous category D". + for (int i = 0; i < 4; i++) { - case 0: goto default; - case >= 1 and <= 19: return ShipTypeGroup.Reserved; - case 20: return ShipTypeGroup.WingInGroundAll; - case >= 21 and <= 24: return ShipTypeGroup.WingInGroundHazardous; - case >= 25 and <= 29: return ShipTypeGroup.WingInGroundReserved; - case 30: return ShipTypeGroup.Fishing; - case 31: return ShipTypeGroup.Towing; - case 32: return ShipTypeGroup.TowingLengthOver200MOrBreadthOver25M; - case 33: return ShipTypeGroup.DredgingOrUnderwaterOps; - case 34: return ShipTypeGroup.DivingOps; - case 35: return ShipTypeGroup.MilitaryOps; - case 36: return ShipTypeGroup.Sailing; - case 37: return ShipTypeGroup.PleasureCraft; - case >= 38 and <= 39: return ShipTypeGroup.Reserved; - case 40: return ShipTypeGroup.HighSpeedCraftAll; - case >= 41 and <= 44: return ShipTypeGroup.HighSpeedCraftHazardous; - case >= 45 and <= 49: return ShipTypeGroup.HighSpeedCraftReserved; - case 50: return ShipTypeGroup.PilotVessel; - case 51: return ShipTypeGroup.SearchAndRescueVessel; - case 52: return ShipTypeGroup.Tug; - case 53: return ShipTypeGroup.PortTender; - case 54: return ShipTypeGroup.AntiPollutionEquipment; - case 55: return ShipTypeGroup.LawEnforcement; - case >= 56 and <= 57: return ShipTypeGroup.SpareLocalVessel; - case 58: return ShipTypeGroup.MedicalTransport; - case 59: return ShipTypeGroup.NoncombatantShip; - case 60: return ShipTypeGroup.PassengerAll; - case >= 61 and <= 64: return ShipTypeGroup.PassengerHazardous; - case >= 65 and <= 68: return ShipTypeGroup.PassengerReserved; - case 69: return ShipTypeGroup.PassengerNoAdditionalInformation; - case 70: return ShipTypeGroup.CargoAll; - case >= 71 and <= 74: return ShipTypeGroup.CargoHazardous; - case >= 75 and <= 78: return ShipTypeGroup.CargoReserved; - case 79: return ShipTypeGroup.CargoNoAdditionalInformation; - case 80: return ShipTypeGroup.TankerAll; - case >= 81 and <= 84: return ShipTypeGroup.TankerHazardous; - case >= 85 and <= 88: return ShipTypeGroup.TankerReserved; - case 89: return ShipTypeGroup.TankerNoAdditionalInformation; - case 90: return ShipTypeGroup.OtherAll; - case >= 91 and <= 94: return ShipTypeGroup.OtherHazardous; - case >= 95 and <= 98: return ShipTypeGroup.OtherReserved; - case 99: return ShipTypeGroup.OtherNoAdditionalInformation; - default: return ShipTypeGroup.NotAvailable; + table[firstCode + i] = new ShipTypeInfo(group, $"{descriptionPrefix}, Hazardous category {(char)('A' + i)}"); } } -} \ No newline at end of file + + private static void SetRange(ShipTypeInfo[] table, int firstCode, int lastCode, ShipTypeGroup group, string description) + { + for (int i = firstCode; i <= lastCode; i++) + { + table[i] = new ShipTypeInfo(group, description); + } + } + + private readonly record struct ShipTypeInfo(ShipTypeGroup Group, string Description); +} diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType18.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType18.cs index ccc70c7..9d32279 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType18.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType18.cs @@ -6,6 +6,27 @@ namespace Ais.Net.Models; +/// +/// A Class B "CS" (carrier-sense) position report (AIS message type 18). +/// +/// Whether the unit can accept a channel assignment via message type 22. +/// Whether the unit can switch frequency bands. +/// The course over ground in degrees, or null when not available. +/// The Class B unit type (carrier-sense or SOTDMA). +/// Whether the unit is equipped with a display. +/// Whether the station is operating in assigned mode. +/// Whether the unit is attached to a Digital Selective Calling (DSC) transceiver. +/// The Maritime Mobile Service Identity of the transmitting station. +/// The reported latitude/longitude, or null when not available. +/// Whether a high-accuracy (DGPS) fix is indicated. +/// The type of radio status information carried by the message. +/// Whether Receiver Autonomous Integrity Monitoring is in use. +/// The value of the regional reserved bits at bit offset 139. +/// The value of the regional reserved bits at bit offset 38. +/// The number of times the message has been repeated. +/// The speed over ground in knots, or null when not available. +/// The UTC second when the report was generated. +/// The true heading in degrees. public record AisMessageType18( bool CanAcceptMessage22ChannelAssignment, bool CanSwitchBands, diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType19.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType19.cs index 12884b7..587acf4 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType19.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType19.cs @@ -6,6 +6,30 @@ namespace Ais.Net.Models; +/// +/// A Class B "SO" extended position report (AIS message type 19). +/// +/// The course over ground in degrees, or null when not available. +/// The distance in metres from the position reference point to the bow. +/// The distance in metres from the position reference point to the port side. +/// The distance in metres from the position reference point to the starboard side. +/// The distance in metres from the position reference point to the stern. +/// Whether the station is operating in assigned mode. +/// Whether the Data Terminal Equipment is not ready. +/// The Maritime Mobile Service Identity of the transmitting station. +/// The reported latitude/longitude, or null when not available. +/// Whether a high-accuracy (DGPS) fix is indicated. +/// The electronic position fixing device type. +/// Whether Receiver Autonomous Integrity Monitoring is in use. +/// The value of the regional reserved bits at bit offset 139. +/// The value of the regional reserved bits at bit offset 38. +/// The number of times the message has been repeated. +/// The name of the vessel. +/// The ship-and-cargo type code. +/// The value of the spare bits at bit offset 308. +/// The speed over ground in knots, or null when not available. +/// The UTC second when the report was generated. +/// The true heading in degrees. public record AisMessageType19( float? CourseOverGround, uint DimensionToBow, diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType1Through3.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType1Through3.cs index c945de3..f7bcb82 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType1Through3.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType1Through3.cs @@ -6,6 +6,26 @@ namespace Ais.Net.Models; +/// +/// A Class A vessel position report (AIS message types 1, 2 and 3). +/// +/// The course over ground in degrees, or null when not available. +/// The special manoeuvre indicator for the vessel. +/// The AIS message type identifier. +/// The Maritime Mobile Service Identity of the transmitting station. +/// The reported navigation status of the vessel. +/// The reported latitude/longitude, or null when not available. +/// Whether a high-accuracy (DGPS) fix is indicated. +/// The number of remaining transmission slots before the radio slot timeout. +/// The radio sub-message value, whose meaning depends on the radio slot timeout. +/// The synchronisation state of the radio. +/// The rate of turn of the vessel. +/// Whether Receiver Autonomous Integrity Monitoring is in use. +/// The number of times the message has been repeated. +/// The value of the spare bits at bit offset 145. +/// The speed over ground in knots, or null when not available. +/// The UTC second when the report was generated. +/// The true heading in degrees. public record AisMessageType1Through3( float? CourseOverGround, ManoeuvreIndicator ManoeuvreIndicator, diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType24Part0.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType24Part0.cs index 716bec1..622f239 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType24Part0.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType24Part0.cs @@ -6,6 +6,13 @@ namespace Ais.Net.Models; +/// +/// A static data report, Part A - the vessel name (AIS message type 24, part 0). +/// +/// The Maritime Mobile Service Identity of the transmitting station. +/// The part number identifying this as Part A of the static data report. +/// The number of times the message has been repeated. +/// The value of the spare bits at bit offset 160. public record AisMessageType24Part0( uint Mmsi, uint PartNumber, diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType24Part1.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType24Part1.cs index aa445b9..ce2d5ac 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType24Part1.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType24Part1.cs @@ -6,6 +6,24 @@ namespace Ais.Net.Models; +/// +/// A static data report, Part B - type, dimensions and identifiers (AIS message type 24, part 1). +/// +/// The call sign of the vessel. +/// The distance in metres from the position reference point to the bow. +/// The distance in metres from the position reference point to the port side. +/// The distance in metres from the position reference point to the starboard side. +/// The distance in metres from the position reference point to the stern. +/// The Maritime Mobile Service Identity of the transmitting station. +/// The MMSI of the mothership for an auxiliary craft. +/// The part number identifying this as Part B of the static data report. +/// The number of times the message has been repeated. +/// The equipment serial number of the Class B unit. +/// The value of the spare bits at bit offset 162. +/// The ship-and-cargo type code. +/// The unit model code of the Class B equipment. +/// The vendor identifier as encoded under revision 3 of the specification. +/// The vendor identifier as encoded under revision 4 of the specification. public record AisMessageType24Part1( string CallSign, uint DimensionToBow, diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType27.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType27.cs index 15076ff..0cfe8e0 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType27.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType27.cs @@ -1,25 +1,37 @@ -// -// Copyright (c) Endjin Limited. All rights reserved. -// - -using Ais.Net.Models.Abstractions; - -namespace Ais.Net.Models; - -public record AisMessageType27( - float? CourseOverGround, - bool GnssPositionStatus, - uint Mmsi, - NavigationStatus NavigationStatus, - Position? Position, - bool PositionAccuracy, - bool RaimFlag, - uint RepeatIndicator, - float? SpeedOverGround) : - AisMessageBase(MessageType: 27, Mmsi), - IAisMessageType27, - IRaimFlag, - IRepeatIndicator, - IVesselCourseOverGround, - IVesselNavigationStatus, +// +// Copyright (c) Endjin Limited. All rights reserved. +// + +using Ais.Net.Models.Abstractions; + +namespace Ais.Net.Models; + +/// +/// A long-range AIS broadcast position message (AIS message type 27). +/// +/// The course over ground in degrees, or null when not available. +/// Whether the position was obtained from the on-board GNSS receiver. +/// The Maritime Mobile Service Identity of the transmitting station. +/// The reported navigation status of the vessel. +/// The reported latitude/longitude, or null when not available. +/// Whether a high-accuracy (DGPS) fix is indicated. +/// Whether Receiver Autonomous Integrity Monitoring is in use. +/// The number of times the message has been repeated. +/// The speed over ground in knots, or null when not available. +public record AisMessageType27( + float? CourseOverGround, + bool GnssPositionStatus, + uint Mmsi, + NavigationStatus NavigationStatus, + Position? Position, + bool PositionAccuracy, + bool RaimFlag, + uint RepeatIndicator, + float? SpeedOverGround) : + AisMessageBase(MessageType: 27, Mmsi), + IAisMessageType27, + IRaimFlag, + IRepeatIndicator, + IVesselCourseOverGround, + IVesselNavigationStatus, IVesselPositionAccuracy; \ No newline at end of file diff --git a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType5.cs b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType5.cs index a5439ca..eb53894 100644 --- a/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType5.cs +++ b/Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType5.cs @@ -6,6 +6,29 @@ namespace Ais.Net.Models; +/// +/// Static and voyage related data (AIS message type 5). +/// +/// The AIS station compliance version. +/// The call sign of the vessel. +/// The destination of the current voyage. +/// The distance in metres from the position reference point to the bow. +/// The distance in metres from the position reference point to the port side. +/// The distance in metres from the position reference point to the starboard side. +/// The distance in metres from the position reference point to the stern. +/// The maximum present static draught in 1/10 metre. +/// The day component of the estimated time of arrival. +/// The hour component of the estimated time of arrival. +/// The minute component of the estimated time of arrival. +/// The month component of the estimated time of arrival. +/// Whether the Data Terminal Equipment is not ready. +/// The IMO ship identification number. +/// The Maritime Mobile Service Identity of the transmitting station. +/// The electronic position fixing device type. +/// The number of times the message has been repeated. +/// The ship-and-cargo type code. +/// The value of the spare bits at bit offset 423. +/// The name of the vessel. public record AisMessageType5( uint AisVersion, string CallSign, diff --git a/Solutions/Directory.Packages.props b/Solutions/Directory.Packages.props new file mode 100644 index 0000000..f446a11 --- /dev/null +++ b/Solutions/Directory.Packages.props @@ -0,0 +1,21 @@ + + + + true + + + + + + + + + + + + + + + + + diff --git a/global.json b/global.json new file mode 100644 index 0000000..cbef943 --- /dev/null +++ b/global.json @@ -0,0 +1,12 @@ +{ + "sdk": { + "version": "10.0.100", + "rollForward": "latestMinor" + }, + "test": { + "runner": "Microsoft.Testing.Platform" + }, + "msbuild-sdks": { + "MSTest.Sdk": "4.3.2" + } +} From a57d163e754334edc8342325442ddb3d5f34406e Mon Sep 17 00:00:00 2001 From: Howard van Rooijen Date: Thu, 23 Jul 2026 11:33:03 +0100 Subject: [PATCH 17/17] Fix CI for MTP + multi-targeting - Package failed with NETSDK1127 (the net8.0 targeting pack was missing): the runner only had the .NET 10 SDK. Install the .NET 8 SDK too via additionalNetSdkVersion so net8.0 can be built, tested and packed. - Windows test jobs failed because Microsoft.Testing.Platform test projects build an OS-specific executable, and the pipeline compiles once on ubuntu and shares the binaries cross-OS, so the Windows job cannot launch the Linux test host. Restrict the test matrix to ubuntu-latest (the compile OS); the library is pure managed code, so single-OS testing is sufficient. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Q3BkmohCNRYGY1RBKbNaCx --- .github/workflows/build.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b162112..85444a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,16 +61,23 @@ jobs: uses: endjin/Endjin.RecommendedPractices.GitHubActions/.github/workflows/scripted-build-matrix-pipeline.yml@main with: netSdkVersion: '10.x' - # additionalNetSdkVersion: '9.x' + # Install the .NET 8 SDK too so the net8.0 targeting pack/runtime is available for + # building, testing and packing the multi-targeted (net8.0;net10.0) projects. + additionalNetSdkVersion: '8.x' # workflow_dispatch inputs are always strings, the type property is just for the UI forcePublish: ${{ github.event.inputs.forcePublish == 'true' }} skipCleanup: ${{ github.event.inputs.skipCleanup == 'true' }} enableCrossOsCaching: true compilePhaseRunnerOs: ubuntu-latest packagePhaseRunnerOs: ubuntu-latest + # Tests run only on ubuntu-latest. Microsoft.Testing.Platform test projects build an + # OS-specific executable, and this pipeline compiles once (compilePhaseRunnerOs) then shares + # the binaries cross-OS, so a Windows test job cannot launch the Linux-built test host. The + # library is pure managed code with no OS-specific behaviour, so testing on the compile OS is + # sufficient. (Restoring Windows test coverage would require compiling per-OS.) testPhaseMatrixJson: | { - "os": ["windows-latest", "ubuntu-latest"], + "os": ["ubuntu-latest"], "dotnetFramework": ["net8.0", "net10.0"] } # testArtifactName: ''