public static NmdcUserCommandCommand Parse(string message) { if (string.IsNullOrWhiteSpace(message)) { return(null); } var match = ParseRegex.Match(message); if (match == null || !match.Success) { return(null); } var groups = match.Groups; var commandType = (NmdcUserCommandType)byte.Parse(groups[TypeGroupName].Value); var context = (NmdcUserCommandContextType)byte.Parse(groups[ContextGroupName].Value); var titleGroup = groups[TitleGroupName]; var title = titleGroup.Success ? titleGroup.Value : string.Empty; UserCommandsGenerator commandsGenerator = null; var lines = Enumerable.Empty <string>(); var commandsGroup = groups[CommandsGroupName]; if (commandsGroup.Success) { var gluedMessages = commandsGroup.Value; var lineMatch = LineVariableParseRegex.Match(gluedMessages); if (lineMatch.Success) { lines = lineMatch.Captures.Cast <Capture>().Select(capture => capture.Value); } var commandsMessages = UnescapeArgument(gluedMessages).Split(new[] { StopChar }, StringSplitOptions.RemoveEmptyEntries); commandsGenerator = (nickReplacement, myNickReplacement, lineReplacements) => { var replacements = (lineReplacements ?? new Dictionary <string, string>()) .ToDictionary(line => GetLineVariable(line.Key), line => line.Value); replacements.Add(NickVariable, nickReplacement); replacements.Add(MyNickVariable, myNickReplacement); var regex = new Regex($"({string.Join("|", replacements.Keys.Select(Regex.Escape))})"); return((commandsMessages ?? Enumerable.Empty <string>()) .Select(m => NmdcCommandParser.Parse(regex.Replace(m, mm => replacements[mm.Value])))); }; } return(new NmdcUserCommandCommand(commandType, context, title, lines, commandsGenerator)); }
/// <summary> /// Asynchronously receives a sequence of commands and monitors cancellation requests. /// </summary> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A task that represents the asynchronous receive operation. The value of /// the TResult parameter contains collection of received commands.</returns> /// <exception cref="InvalidOperationException"> /// <para>Attempt to receive data via dead connection.</para> /// <para>-or-</para> /// <para>Data cannot be received via this connection.</para> /// <para>-or-</para> /// <para>Underlying network stream is currently in use by a previous receive operation.</para> /// </exception> /// <exception cref="ObjectDisposedException">The connection has been disposed.</exception> /// <exception cref="OperationCanceledException">The token has had cancellation requested.</exception> public async Task <IEnumerable <NmdcCommand> > ReceiveAsync(CancellationToken cancellationToken) { if (_disposed) { throw new ObjectDisposedException(nameof(NmdcHubConnection), "The connection has been disposed."); } // [0] Check the connection state. if (!IsConnected) { throw new InvalidOperationException("Attempt to receive data via dead connection."); } if (_networkStream?.CanRead != true) { throw new InvalidOperationException("Data cannot be received via this connection."); } // [1] Receive bytes via connection until the command stop byte (|) is encountered. cancellationToken.ThrowIfCancellationRequested(); byte[] commandsBytes = null; using (var memoryStream = new MemoryStream()) { if (_receivedBytesRemainder != null && _receivedBytesRemainder.Length > 0) { await memoryStream.WriteAsync(_receivedBytesRemainder, 0, _receivedBytesRemainder.Length, cancellationToken); } while (true) { cancellationToken.ThrowIfCancellationRequested(); var receivedBytesCount = await _networkStream.ReadAsync(_receiveBuffer, 0, _receiveBuffer.Length, cancellationToken); if (receivedBytesCount <= 0) { continue; } await memoryStream.WriteAsync(_receiveBuffer, 0, receivedBytesCount, cancellationToken); if (Array.IndexOf(_receiveBuffer, NmdcCommand.StopByte, 0, receivedBytesCount) >= 0) { break; } } commandsBytes = memoryStream.ToArray(); } // [2] Parse commands presented in the received bytes. cancellationToken.ThrowIfCancellationRequested(); var result = new List <NmdcCommand>(); var offset = 0; while (offset < commandsBytes.Length) { cancellationToken.ThrowIfCancellationRequested(); var commandStopBytePosition = Array.IndexOf(commandsBytes, NmdcCommand.StopByte, offset); if (commandStopBytePosition < 0) { break; } var message = Encoding.GetString(commandsBytes, offset, commandStopBytePosition - offset); var command = NmdcCommandParser.Parse(message); result.Add(command); OnCommandReceived(message, command); offset = commandStopBytePosition + 1; } // [3] Save the bytes remained after parsing since they must be used as start of a command on next receive operation. cancellationToken.ThrowIfCancellationRequested(); _receivedBytesRemainder = null; var receivedDataTailLength = commandsBytes.Length - offset; if (receivedDataTailLength > 0) { _receivedBytesRemainder = new byte[receivedDataTailLength]; Buffer.BlockCopy(commandsBytes, offset, _receivedBytesRemainder, 0, receivedDataTailLength); } // return(result); }