/// <summary> /// Creates a <see cref="NmeaLineToAisStreamAdapter"/>. /// </summary> /// <param name="messageProcessor"> /// The message process to which complete AIS messages are to be passed. /// </param> /// <param name="options">Configures parser behaviour.</param> public NmeaLineToAisStreamAdapter( INmeaAisMessageStreamProcessor messageProcessor, NmeaParserOptions options) { this.messageProcessor = messageProcessor; this.options = options; }
/// <summary> /// Process the contents of a file one AIS message at a time. /// </summary> /// <param name="path">Path of the file to process.</param> /// <param name="processor">Handler for the AIS messages.</param> /// <param name="options">Configures parser behaviour.</param> /// <returns>A task that completes when the stream has been processed.</returns> /// <remarks> /// This reassembles AIS messages that have been split over multiple NMEA lines. /// </remarks> public static async Task ParseFileAsync( string path, INmeaAisMessageStreamProcessor processor, NmeaParserOptions options) { using var adapter = new NmeaLineToAisStreamAdapter(processor, options); await ParseFileAsync(path, adapter, options).ConfigureAwait(false); }
/// <summary> /// Process the contents of a file. /// </summary> /// <param name="path">Path of the file to process.</param> /// <param name="processor">Handler for the parsed lines.</param> /// <param name="options">Configures parser behaviour.</param> /// <returns>A task that completes when the stream has been processed.</returns> public static async Task ParseFileAsync( string path, INmeaLineStreamProcessor processor, NmeaParserOptions options) { // This turns off internal file stream buffering using var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize: 1, useAsync: true); await ParseStreamAsync(file, processor, options).ConfigureAwait(false); }
/// <summary> /// Process the contents of a stream. /// </summary> /// <param name="stream">The stream to process.</param> /// <param name="processor">Handler for the parsed lines.</param> /// <param name="options">Configures parser behaviour.</param> /// <returns>A task that completes when the stream has been processed.</returns> public static async Task ParseStreamAsync( Stream stream, INmeaLineStreamProcessor processor, NmeaParserOptions options) { int lines = 0; int ticksAtStart = Environment.TickCount; int ticksAtLastLineCount = ticksAtStart; try { PipeReader reader = CreateFileReader(stream); byte[] splitLineBuffer = new byte[1000]; while (true) { ReadResult result = await reader.ReadAsync().ConfigureAwait(false); ReadOnlySequence <byte> remainingSequence = ProcessBuffer(result); reader.AdvanceTo(remainingSequence.Start, remainingSequence.End); if (result.IsCompleted) { break; } } const int LineCountInterval = 100000; int finalTicks = Environment.TickCount; int totalTicks = finalTicks - ticksAtStart; processor.Progress( true, lines, totalTicks, lines % LineCountInterval, finalTicks - ticksAtLastLineCount); ReadOnlySequence <byte> ProcessBuffer( in ReadResult result) { ReadOnlySpan <byte> lineSpan; SequencePosition? position = null; ReadOnlySequence <byte> remainingSequence = result.Buffer; while ((position = remainingSequence.PositionOf((byte)'\n') ?? (remainingSequence.IsEmpty || !result.IsCompleted ? default(SequencePosition?) : remainingSequence.End)) != null) { ReadOnlySequence <byte> line = remainingSequence.Slice(remainingSequence.Start, position.Value); if (line.IsSingleSegment) { lineSpan = line.First.Span; } else { Span <byte> reassemblySpan = splitLineBuffer; line.CopyTo(reassemblySpan); lineSpan = reassemblySpan.Slice(0, (int)line.Length); } if (lineSpan.Length > 0 && lineSpan[lineSpan.Length - 1] == (byte)'\r') { lineSpan = lineSpan.Slice(0, lineSpan.Length - 1); } if (lineSpan.Length > 0) { try { var parsedLine = new NmeaLineParser(lineSpan, options.ThrowWhenTagBlockContainsUnknownFields); processor.OnNext(parsedLine, lines + 1); } catch (Exception x) { processor.OnError(lineSpan, x, lines + 1); } } remainingSequence = position.Value.Equals(remainingSequence.End) ? remainingSequence.Slice(remainingSequence.End) : remainingSequence.Slice(remainingSequence.GetPosition(1, position.Value)); if (++lines % LineCountInterval == 0) { int currentTicks = Environment.TickCount; int ticksSinceLastLineCount = currentTicks - ticksAtLastLineCount; processor.Progress( false, lines, currentTicks - ticksAtStart, LineCountInterval, ticksSinceLastLineCount); ticksAtLastLineCount = currentTicks; } } return(remainingSequence); } } finally { processor.OnCompleted(); } }