问题描述
我正在创建一个程序,该程序与不断发送数据的串行设备通信。我每100毫秒(使用计时器)从设备读取一次数据。我使用port.ReadExisting()
从设备接收所有当前可用的数据,然后尝试将其分成几行,因为我需要检查一些接收到的数据,而最好的方法是检查行。设备发送不以"\r\n"
或'\n'
结尾的数据时,会发生问题。
在理想情况下,port.ReadExisting()
返回:"sampletext\r\nsomesampletext\nsampletext\r\n
但是,如果末尾没有CR或LF字符,则会出现问题:
第一次port.ReadExisting()
返回此内容:"text\nsamp"
第二次port.ReadExisting()
返回此:letext\r\ntext\r\n"
最终结果应如下所示:
text
sampletext
text
但是我得到的是这样的:
text
samp
letext
text
我的代码:
这是timer
,每100毫秒运行一次:
private void CommandTimer_Tick(object sender,EventArgs e)
{
BackgroundWorker seriaDataWorker = new BackgroundWorker();
seriaDataWorker.DoWork += (obj,p) => PrintSerialData();
seriaDataWorker.RunWorkerAsync();
}
BackgroundWorker
由计时器调用:
private void PrintSerialData()
{
try
{
if (Randomreboot)
{
RebootWatch.Start();
}
if (COMport.IsOpen)
{
if (COMport.BytesToRead != 0)
{
SerialPrint(COMport.ReadExisting());
}
}
}
catch (System.IO.IOException SerialException)
{
return;
}
}
将接收到的数据解析为行的函数:
private void SerialPrint(string data)
{
using (var buffer = new StringReader(data))
{
string line = "";
while((line = buffer.ReadLine()) != null)
{
if (CheckForAnsw)
{
ReceivedCommandData = line;
if (ReceivedCommandData.Contains(AnswExpected))
{
ReceivedAnsw = true;
ReceivedLine = ReceivedCommandData;
ReceivedCommandData = "";
}
}
this.Invoke(new MethodInvoker(delegate
{
AppendText(TextBox_System_Log,Color.Black,line + "\r\n");
}
));
}
}
}
我知道问题是buffer.ReadLine()
会将不以CR或LF字符结尾的字符串的其余部分作为单独的行来对待,但是我不知道如何解决。
我过去曾尝试使用port.ReadLine()
,但是它速度较慢,当断开串口等连接时会给我带来麻烦。
解决方法
我认为StringReader
并没有一种简单的方法来处理此问题。相反,您可以自己分割字符串:
private static string _buffer = string.Empty;
private static void SerialPrint(string data)
{
// Append the new data to the leftover of the previous operation
data = _buffer + data;
int index = data.IndexOf('\n');
int start = 0;
while (index != -1)
{
var command = data.Substring(start,index - start);
ProcessCommand(command.TrimEnd('\r'));
start = index + 1;
index = data.IndexOf('\n',start);
}
// Store the leftover in the buffer
if (!data.EndsWith("\n"))
{
_buffer = data.Substring(start);
}
else
{
_buffer = string.Empty;
}
}
private static void ProcessCommand(string command)
{
Console.WriteLine(command);
}
,
您可以使用AnonymousPipes传输和缓冲传入的数据,并将其读取为行以将其输出到某个地方。
这是一个小示例,该示例创建一个服务器和客户端管道流,然后在一个任务中将数据写入服务器(数据中包含一些换行符),并在每行中读取另一个任务中的数据,并将它们输出到控制台
public class Program
{
public static async Task Main()
{
(var writer,var reader) = CreatePipe();
using (writer)
using (reader)
{
var writerTask = Task.Run(async () =>
{
writer.AutoFlush = true;
writer.Write("?");
for (int i = 0; i < 100; i++)
{
if (i % 10 == 9)
{
await writer.WriteAsync("!");
await writer.WriteAsync(Environment.NewLine);
await writer.WriteAsync("?");
}
else
{
await writer.WriteAsync((i % 10).ToString());
}
await Task.Delay(100);
}
writer.Close();
});
var readerTask = Task.Run(async () =>
{
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
Console.WriteLine(line);
}
});
await Task.WhenAll(writerTask,readerTask);
}
}
public static (StreamWriter,StreamReader) CreatePipe()
{
var server = new AnonymousPipeServerStream(PipeDirection.Out);
var client = new AnonymousPipeClientStream(server.GetClientHandleAsString());
return
(
new StreamWriter(server,Encoding.UTF8),new StreamReader(client,Encoding.UTF8)
);
}
}
请尝试使此代码适合您的用例,并在遇到困难时发表评论。
,使用\r\n
可以解决\n
和Environment.NewLine
的问题。我不确定AppendText
的作用,但是如果您使用它来存储值,那么您会做得过多。您需要先将所有数据存储在StringBuilder
中,然后处理它们,或处理每个数据并将它们存储在托管类型(例如Array)中,以分别定义每行。仅在表示层中使用string
(如果您有一些GUI,希望用户看到结果)。
因此,我建议将行存储在StringBuilder
中,如下所示:
private readonly StringBuilder _strDataBuilder = new StringBuilder();
private void PrintSerialData()
{
try
{
if (RandomReboot)
{
RebootWatch.Start();
}
if(COMport.IsOpen && COMport.BytesToRead != 0)
{
var data = COMport.ReadExisting();
if(!string.IsNullOrEmpty(data)) {
_strDataBuilder.Append(data);
}
}
}
catch (System.IO.IOException SerialException)
{
return;
}
}
private void SerialPrint()
{
var data = _strDataBuilder.ToString();
if(string.IsNullOrEmpty(data)) { return; }
var lines = data.Split(Environment.NewLine);
if(lines.Length == 0) { return; }
for(int x = 0; x < lines.Length; x++)
{
var line = lines[x];
if (CheckForAnsw)
{
ReceivedCommandData = line;
if (ReceivedCommandData.Contains(AnswExpected))
{
ReceivedAnsw = true;
ReceivedLine = ReceivedCommandData;
ReceivedCommandData = "";
}
}
this.Invoke(new MethodInvoker(delegate
{
AppendText(TextBox_System_Log,Color.Black,line + Environment.NewLine);
}
));
}
}
如果要添加更多处理步骤或重复使用结果,则首先存储它们将使事情变得更具可维护性和可修复性。
如果仅重新打印SerialPrint()
中的数据,尽管GUI
是不必要的。由于数据已经分隔成几行。所以,如果你这样做
TextBox_System_Log.Text = _strDataBuilder.ToString();
直接将它们以默认颜色列出在行中。但是,如果您打算将它们拆分以分别处理每行(例如,以进行验证),那么就可以了。