C# 应用程序因串行连接阻塞而卡住,原因是 Arduino 板在建立 COM 端口连接后出现内部异常

问题描述 投票:0回答:1

我有一个问题,对于互联网上的任何人来说似乎都是一个很大的未知,当串行连接被板的内部异常中断时,我的 Windows 应用程序会卡住。

这是该问题的最简单示例:

最简单的C#代码,打开COM端口连接。

internal static class Program
{
    private static SerialPortStream _serialPort;

    static void Main()
    {
        _serialPort = new SerialPortStream("COM6", 9600, 8, Parity.None, StopBits.One);
        _serialPort.Open();
    }
}

Arduino 板代码,它将在短暂延迟后模拟内部异常。

void loop()
{
  delay(500); //We wait 0.5 seconds to allow the COM port connection to "Successfully" be established, and so the .Open() call in C# won't throw an exception.
  
  //We create an internal exception, in this instance we try to set an array size to a negative value
  int index = -4; 
  byte* serialData = new byte[index];
}

这是重现此问题的非常简单的方法。

一些值得注意的信息:

  • 我正在使用 Raspberry Pi Pico 模块 (RP2040 Zero)
  • RP2040采用USB-C连接,其串口通过USB运行。
  • 我在 C# 端使用“RJCP.IO.Ports”进行 COM 端口通信。我也不认为这是库的问题,因为我注意到 Visual Studio Code 的终端在尝试建立串行读取通信时也会卡住。
  • 我尝试在不同的任务和BackgroundWorker中调用“Open()”方法,在这两种情况下,即使应用程序没有冻结,我也无法关闭应用程序,直到我通过板载开关重置板(实际上,我我意识到程序将在我尝试关闭大约 1 分钟后关闭)。
  • 我知道我可以在 Arduino 代码中“修复”这个问题,但这个问题的重点是能够在 C# 应用程序中处理这种情况(例如,你不会总是你拥有的任何模块的创建者)。我认为简单的主板不应该停止主机内的任何控制。

感谢所有提供帮助的人!

c# c++ arduino usbserial com-port
1个回答
0
投票

您可以在 C# 程序中使用一些策略来处理这种情况:

读/写超时:

使用串行端口的读写超时是防止程序挂起的一种技术。如果 Arduino 在预定时间内没有做出反应,这允许您的应用程序捕获超时并处理问题。

_serialPort.ReadTimeout = 500; 
_serialPort.WriteTimeout = 500;

异步通信:

避免阻塞主线程的一种方法是使用异步技术进行读写。您可以使用 Task, for Task.Run 来执行串行读数。

async Task ReadFromSerialPort()
{
    try
    {
        while (true)
        {
            string data = await Task.Run(() => _serialPort.ReadLine());
        }
    }
    catch (TimeoutException)
    {
       
    }
    catch (Exception ex)
    {
      
    }
}

例外:

要处理异常,请将 Open 方法和所有随后的读取或写入包含在 try-catch 块中。通过这种方式,您可以记录错误,并可能在出现问题时尝试重新打开连接。

try
{
    _serialPort.Open();
}
catch (UnauthorizedAccessException ex)
{
   
}
catch (IOException ex)
{
    
}

串口状态:

您可以密切关注串口的状况,如果它停止响应,可以将其关闭。为了确定端口是否仍然响应,必须执行不同的线程或作业。

取消标记:

如果您实现任何长时间运行的操作(例如从串行端口读取),请使用取消令牌。这使您能够在发生错误或活动时间比预期长时停止活动。 例如:

private static SerialPortStream _serialPort;

static async Task Main()
{
    _serialPort = new SerialPortStream("COM6", 9600, 8, Parity.None, StopBits.One)
    {
        ReadTimeout = 500,
        WriteTimeout = 500
    };

    try
    {
        _serialPort.Open();
        await ReadFromSerialPort();
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error: {ex.Message}");
    }
}

static async Task ReadFromSerialPort()
{
    try
    {
        while (true)
        {
            string data = await Task.Run(() => _serialPort.ReadLine());
        }
    }
    catch (TimeoutException)
    {
        
    }
    catch (IOException ex)
    {
       
    }
    catch (Exception ex)
    {
        
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.