Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/amazon-s3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在C中等待来自串行端口的数据#_C#_Optimization_Serial Port - Fatal编程技术网

C# 在C中等待来自串行端口的数据#

C# 在C中等待来自串行端口的数据#,c#,optimization,serial-port,C#,Optimization,Serial Port,我有一个应用程序,它使用RS-232从无线电台接收数据。这些无线电使用API与多个客户端通信。为了使用这些无线电,我创建了一个与它们通信的库,其他软件可以利用它,只需对普通串行端口连接进行最小的更改。该库从SerialPort对象读取数据,并根据从中接收的无线电将传入数据插入不同的缓冲区。接收到的每个数据包都包含一个指示其长度、来源等的报头 我首先从端口读取固定长度的报头并对其进行解析。在报头中,数据的长度是在数据有效负载本身之前定义的,因此,一旦我知道数据的长度,我就会等待那么多数据可用,然后

我有一个应用程序,它使用RS-232从无线电台接收数据。这些无线电使用API与多个客户端通信。为了使用这些无线电,我创建了一个与它们通信的库,其他软件可以利用它,只需对普通串行端口连接进行最小的更改。该库从SerialPort对象读取数据,并根据从中接收的无线电将传入数据插入不同的缓冲区。接收到的每个数据包都包含一个指示其长度、来源等的报头

我首先从端口读取固定长度的报头并对其进行解析。在报头中,数据的长度是在数据有效负载本身之前定义的,因此,一旦我知道数据的长度,我就会等待那么多数据可用,然后读入那么多字节

示例(省略标题中的其他元素):

//读取头
字节[]头=新字节[RCV_头长度];
this.Port.Read(标头,0,RCV_标头长度);
//获取数据包中数据的长度
短数据长度=头[1];
字节[]有效载荷=新字节[dataLength];
//确保此数据包的所有有效负载都已准备好读取
而(this.Port.BytesToRead
很明显,while端口是空的。如果由于某种原因数据从未到达,线程将锁定。我还没有遇到这个问题,但我正在寻找一种优雅的方法来做到这一点。我的第一个想法是添加一个在while循环之前启动的短计时器,并在它结束时设置一个abortRead标志,该标志将中断while循环,如下所示:

// Make sure all the payload of this packet is ready to read
abortRead = false;
readTimer.Start();
while (this.Port.BytesToRead < dataLength && !abortRead) {}
//确保此数据包的所有有效负载都已准备好读取
abortrade=false;
readTimer.Start();
而(this.Port.BytesToRead

这段代码需要尽可能快地处理源源不断的传入数据,因此将开销保持在最低是一个问题,我想知道我是否正确地做到了这一点。

如果你想真正解决这个问题,你需要在后台运行这段代码。有不同的选择可以做到这一点;您可以启动线程、启动
任务
或使用
异步等待

要完全涵盖所有选项,答案将是无止境的。如果使用默认调度程序的线程或任务,并且等待时间预计很短,则可以使用
SpinWait.spinUtil
而不是while循环。这将比您的解决方案性能更好:

SpinWait.SpinUntil(() => this.Port.BytesToRead >= dataLength);
如果您可以自由使用
asyncwait
,我建议您使用此解决方案,因为您只需要对代码进行一些更改。您可以使用
Task.Delay
,在最佳情况下,您可以通过
CancellationToken
来取消操作:

try {
    while (this.Port.BytesToRead < dataLength) {
        await Task.Delay(100, cancellationToken);
    }
}
catch(OperationCancelledException) {
    //Cancellation logic
}
试试看{
while(this.Port.BytesToRead
您不必运行此while循环,方法
Read
将为您填充缓冲区,或者如果未在
SerialPort.ReadTimeout
time(您可以根据需要调整)中填充缓冲区,则会抛出
TimeoutException


但是一般来说,while循环会导致CPU的大量工作,在数据到达的几毫秒内,while循环迭代将有数千次结束,你应该在里面添加一些
Thread.Sleep

我想我应该与
SerialPort
DataReceived
事件异步执行

// Class fields
private const int RCV_HEADER_LENGTH = 8;
private const int MAX_DATA_LENGTH = 255;

private SerialPort Port;

private byte[] PacketBuffer = new byte[RCV_HEADER_LENGTH + MAX_DATA_LENGTH];
private int Readi = 0;
private int DataLength = 0;


// In your constructor
this.Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);


private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
    if (e.EventType != SerialData.Chars)
    {
        return;
    }

    // Read all available bytes.
    int len = Port.BytesToRead;
    byte[] data = new byte[len];
    Port.Read(data, 0, len);

    // Go through each byte.
    for (int i = 0; i < len; i++)
    {
        // Add the next byte to the packet buffer.
        PacketBuffer[Readi++] = data[i];

        // Check if we've received the complete header.
        if (Readi == RCV_HEADER_LENGTH)
        {
            DataLength = PacketBuffer[1];
        }

        // Check if we've received the complete data.
        if (Readi == RCV_HEADER_LENGTH + DataLength)
        {
            // The packet is complete add it to the appropriate buffer.

            Readi = 0;
        }
    }
}
//类字段
专用常量int RCV_头长度=8;
私有常量int MAX_DATA_LENGTH=255;
专用串行端口;
专用字节[]PacketBuffer=新字节[RCV_头长度+最大数据长度];
私有int Readi=0;
私有int数据长度=0;
//在构造函数中
this.Port.DataReceived+=新的SerialDataReceivedEventHandler(DataReceivedHandler);
私有void DataReceivedHandler(对象发送方,SerialDataReceivedEventArgs e)
{
if(e.EventType!=SerialData.Chars)
{
返回;
}
//读取所有可用字节。
int len=Port.BytesToRead;
字节[]数据=新字节[len];
端口读取(数据,0,len);
//检查每个字节。
对于(int i=0;i
我不知道/使用C#,但“将开销保持在最低限度”的一般规则是尽可能少地进行系统调用。在每次读取请求中获取尽可能多的数据。不要尝试将单个读取请求用作解析算法的一部分(例如,先读取标头,然后读取变量数据)。只需将所有内容读入缓冲区,然后从uart解析初始数据可能无效,因此通常必须在开始时清除错误数据。我怀疑你的情况就是这样。您在第一条消息中获得的字节计数错误,并锁定了软件。我知道我可能想得太多了。请使用SpinWait,而不是Thread.Sleep。SpinWait是专门为这些情况而设计的,操作系统对多CPU友好,并且会根据需要向另一个线程屈服(与睡眠不同,睡眠总是会屈服)。@Sefe请纠正我的错误,但我认为SpinWait是为等待CPU操作而设计的(它基本上是while循环,除了它会产生而不是继续循环更长的时间。应该补充的是,我的代码在一个函数中,当DataReceived事件触发时会被调用。触发事件的接收数据阈值设置为l。)
// Class fields
private const int RCV_HEADER_LENGTH = 8;
private const int MAX_DATA_LENGTH = 255;

private SerialPort Port;

private byte[] PacketBuffer = new byte[RCV_HEADER_LENGTH + MAX_DATA_LENGTH];
private int Readi = 0;
private int DataLength = 0;


// In your constructor
this.Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);


private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
    if (e.EventType != SerialData.Chars)
    {
        return;
    }

    // Read all available bytes.
    int len = Port.BytesToRead;
    byte[] data = new byte[len];
    Port.Read(data, 0, len);

    // Go through each byte.
    for (int i = 0; i < len; i++)
    {
        // Add the next byte to the packet buffer.
        PacketBuffer[Readi++] = data[i];

        // Check if we've received the complete header.
        if (Readi == RCV_HEADER_LENGTH)
        {
            DataLength = PacketBuffer[1];
        }

        // Check if we've received the complete data.
        if (Readi == RCV_HEADER_LENGTH + DataLength)
        {
            // The packet is complete add it to the appropriate buffer.

            Readi = 0;
        }
    }
}