public static async Task Main(string[] args) { IConfiguration config = new ConfigurationBuilder() .AddJsonFile("settings.json", true, true) .AddJsonFile("settings.local.json", true, true) .Build(); AisConfig aisConfig = config.GetSection("Ais").Get <AisConfig>(); StorageConfig storageConfig = config.GetSection("Storage").Get <StorageConfig>(); IStorageClient storageClient = new AzureAppendBlobStorageClient(storageConfig); INmeaReceiver receiver = new NetworkStreamNmeaReceiver( aisConfig.Host, aisConfig.Port, aisConfig.RetryPeriodicity, aisConfig.RetryAttempts); // INmeaReceiver receiver = new FileStreamNmeaReceiver(@"PATH-TO-RECORDING.nm4"); var receiverHost = new ReceiverHost(receiver); // Decode teh sentences into messages, and group by the vessel by Id IObservable <IGroupedObservable <uint, IAisMessage> > byVessel = receiverHost.Messages.GroupBy(m => m.Mmsi); // Combine the various message types required to create a stream containing name and navigation IObservable <(uint mmsi, IVesselNavigation navigation, IVesselName name)>?vesselNavigationWithNameStream = from perVesselMessages in byVessel let vesselNavigationUpdates = perVesselMessages.OfType <IVesselNavigation>() let vesselNames = perVesselMessages.OfType <IVesselName>() let vesselLocationsWithNames = vesselNavigationUpdates.CombineLatest(vesselNames, (navigation, name) => (navigation, name)) from vesselLocationAndName in vesselLocationsWithNames select(mmsi : perVesselMessages.Key, vesselLocationAndName.navigation, vesselLocationAndName.name); vesselNavigationWithNameStream.Subscribe(navigationWithName => { (uint mmsi, IVesselNavigation navigation, IVesselName name) = navigationWithName; string positionText = navigation.Position is null ? "unknown position" : $"{navigation.Position.Latitude},{navigation.Position.Longitude}"; Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"[{mmsi}: '{name.VesselName.CleanVesselName()}'] - [{positionText}] - [{navigation.CourseOverGroundDegrees ?? 0}]"); Console.ResetColor(); }); var batchBlock = new BatchBlock <string>(storageConfig.WriteBatchSize); var actionBlock = new ActionBlock <IEnumerable <string> >(storageClient.PersistAsync); batchBlock.LinkTo(actionBlock); // Write out the messages as they are received over the wire. receiverHost.Sentences.Subscribe(sentence => Console.WriteLine(sentence)); // Persist the messages as they are received over the wire. receiverHost.Sentences.Subscribe(batchBlock.AsObserver()); var cts = new CancellationTokenSource(); Task task = receiverHost.StartAsync(cts.Token); // If you wanted to cancel the long running process: // cts.Cancel(); await task; }