在 C# 中以编程方式更改串口配置

Moe*_*eez 5 c# serial-port modbus serial-communication

2的设备类型具有不同的协议并与单个串行端口连接。通过协议,我的意思是串口配置不同。

我有一个协议 ID p_id,我可以通过它检查当前正在读取哪个设备。下面是我的代码

下面是我的主函数,它调用一个名为的类 CombinedEngine

 static class Program
 {
   private static CombinedEngine _eng;
   static async Task Main(string[] args)
    {
      try
      {
         _eng = new CombinedEngine();
      }
      catch (Exception ex)
      {
            Debug.WriteLine(ex.Message.ToString());
                //_log.Error(ex, ex.Message);
      }
    }
     while(true);
 }
Run Code Online (Sandbox Code Playgroud)

组合发动机类

class CombinedEngine
{
   SerialPort port = new SerialPort();
   public CombinedEngine()
    {          

        try
        {
            
            var p = mdc.mdc_protocol.ToList();
            
            if(p.Count > 0)
            {
                foreach(var pr in p)
                {
                    var p_id = pr.protocol_id;

                    if(p_id=="01")//modbus
                    {
                        if (port.IsOpen)
                            port.Close();

                        port = new SerialPort("COM8", 9600, Parity.Even, 8, StopBits.One);
                        port.ReadTimeout = 500;
                        port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);                          
                        port.Open();

                        Console.WriteLine("Port opened successfully for modbus...");
                        Console.WriteLine("I am Recieving for modbus...");


                        var result = mdc.mdc_meter_config.Where(m => m.config_flag == 0)
                            .Where(m=>m.p_id == p_id).ToList();

                        if (result.Count > 0)
                        {
                            foreach (var item in result)
                            {
                                var iteration = new Iterations()
                                {
                                    hex = (string)item.m_hex,
                                    row_id = (string)item.row_id,
                                    device_id = (int)item.meter_id,
                                    protocol_id = (string)item.p_id,
                                    command_id = (string)item.command_id,
                                    config_flag = (int)item.config_flag,
                                    msn = (string)item.msn,
                                    time = (string)item.time
                                };
                                confList.Add(iteration);
                                time = Convert.ToDouble(item.time);
                            }

                            var modbus = confList.Where(x => x.protocol_id == "01").ToList();
                            
                            aTimer = new System.Timers.Timer();


                            // Create a timer...
                            aTimer = new System.Timers.Timer();
                            // Hook up the Elapsed event for the timer. 
                            aTimer.Interval = time * 1000.0;
                            aTimer.Elapsed += (sender, e) => MyModbusMethod(sender, e, modbus, aTimer);            
                            aTimer.AutoReset = true;
                            aTimer.Enabled = true;

                        }
                        else
                        {

                            Console.WriteLine("No Data available");
                        }
                    }
                    else if(p_id=="02")//ytl_bus
                    {
                        if (port.IsOpen)
                            port.Close();

                        port = new SerialPort("COM8", 38400, Parity.None, 8, StopBits.One);
                        port.ReadTimeout = 500;
                        port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                        port.Open();

                        Console.WriteLine("Port opened successfully for ytlbus...");
                        Console.WriteLine("I am Recieving for ytlbus...");


                        var result = mdc.mdc_meter_config.Where(m => m.config_flag == 0)
                            .Where(m => m.p_id == p_id).ToList();

                        if (result.Count > 0)
                        {
                            foreach (var item in result)
                            {
                                var iteration = new Iterations()
                                {
                                    hex = (string)item.m_hex,
                                    row_id = (string)item.row_id,
                                    device_id = (int)item.meter_id,
                                    protocol_id = (string)item.p_id,
                                    command_id = (string)item.command_id,
                                    config_flag = (int)item.config_flag,
                                    msn = (string)item.msn,
                                    time = (string)item.time
                                };
                                confList.Add(iteration);
                                time = Convert.ToDouble(item.time);
                            }

                            
                            var ytlbus = confList.Where(x => x.protocol_id == "02").ToList();

                            

                            aTimer = new System.Timers.Timer();


                            // Create a timer...
                            aTimer = new System.Timers.Timer();
                            // Hook up the Elapsed event for the timer. 
                            aTimer.Interval = time * 1000.0;
                            aTimer.Elapsed += (sender, e) => MyElapsedMethod(sender, e,ytlbus , aTimer);            
                            aTimer.AutoReset = true;
                            aTimer.Enabled = true;

                        }
                        else
                        {

                            Console.WriteLine("No Data available");
                        }

                    }

                   
                    
                   
                }
               
            }
           

        }
        catch (Exception ex)
        {
            Console.WriteLine("Error at Line " + LineNumber(), ex.Message.ToString());
            throw ex;
        }
       
       
    }


}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我检查了如果p_id等于01那么modbus应该完成串口配置。但是如果p_id是,02那么ytlbus应该会遇到串口配置。两种设备都有不同的波特率和奇偶校验位。所以我试图设置它们

另外,我有一个计时器,它是60秒。所以每过60几秒,下一个定时器就会被初始化。

例如。如果p_id01代码将波特率设置为9600,奇偶校验设置为Even。然后SerialDataRecievedEventHandler被调用,它将检查来自设备的任何传入数据,并将管理转储到DB.

然后代码会从表中检查设备详细信息mdc_meter_config并从中取出相关信息。将所有详细信息一一添加到所有设备的列表中。此外,时间将进行。在这种情况下,所有设备的时间都是相同的,即 60 秒。

然后将该列表传递给一个变量,该变量然后传递给一个ElapsedEventHandler函数。该frame由它来处理发送。

p_idequals02执行相同的操作,唯一的区别是它将波特率设置为38400并将奇偶校验设置为None

我面临什么问题?

上面的代码运行,我面临的问题是两个条件同时工作。即 for 01,它将起作用,然后同时跳转到02条件。下面是图片

在此处输入图片说明

它应该为任何p_id价值完成工作,然后为其他p_id价值完成工作。

更新 1

我已经更新了我的代码。添加了一个新async功能,仅添加了一个计时器。并添加了一个串行端口扩展类

    public static class SerialPortExtensions
{
    public async static Task ReadAsync(this SerialPort serialPort, byte[] buffer, int offset, int count)
    {
        var bytesToRead = count;
        var temp = new byte[count];

        while (bytesToRead > 0)
        {
            var readBytes = await serialPort.BaseStream.ReadAsync(temp, 0, bytesToRead);
            Array.Copy(temp, 0, buffer, offset + count - bytesToRead, readBytes);
            bytesToRead -= readBytes;
        }
    }

    public async static Task<byte[]> ReadAsync(this SerialPort serialPort, int count)
    {
        var buffer = new byte[count];
        await serialPort.ReadAsync(buffer, 0, count);
        return buffer;
    }
}

public CombinedEngine()
    {
        try
        {
            var p = mdc.mdc_protocol.ToList();

            if (p.Count > 0)
            {
                foreach (var pr in p)
                {
                    var p_id = pr.protocol_id;

                    if (p_id == "01")//modbus
                    {
                        if (port.IsOpen)
                            port.Close();
                        comm = true;
                        port = new SerialPort("COM8", 9600, Parity.Even, 8, StopBits.One);
                        port.ReadTimeout = 500;
                        //port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                        port.Open();
                        Work();

                        Console.WriteLine("Port opened successfully for modbus...");
                        Console.WriteLine("I am Recieving for modbus...");
                        
                    }
                    else if (p_id == "02")//ytl_bus
                    {
                        if (port.IsOpen)
                            port.Close();
                        comm = true;
                        port = new SerialPort("COM8", 38400, Parity.None, 8, StopBits.One);
                        port.ReadTimeout = 500;
                        //port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                        port.Open();
                        Work();

                        Console.WriteLine("Port opened successfully for ytlbus...");
                        Console.WriteLine("I am Recieving for ytlbus...");                          

                    }
                    var result = mdc.mdc_meter_config.Where(m => m.config_flag == 0).ToList();
                    if (result.Count > 0)
                    {
                        foreach (var item in result)
                        {
                            var iteration = new Iterations()
                            {
                                hex = (string)item.m_hex,
                                row_id = (string)item.row_id,
                                device_id = (int)item.meter_id,
                                protocol_id = (string)item.p_id,
                                command_id = (string)item.command_id,
                                config_flag = (int)item.config_flag,
                                msn = (string)item.msn,
                                time = (string)item.time
                            };
                            confList.Add(iteration);
                            time = Convert.ToDouble(item.time);
                        }

                        var modbus = confList.Where(x => x.protocol_id == "01").ToList();
                        var ytlbus = confList.Where(x => x.protocol_id == "02").ToList();

                        //ModbusMethod(modbus);

                        aTimer = new System.Timers.Timer();
                        // Create a timer...
                        aTimer = new System.Timers.Timer();
                        // Hook up the Elapsed event for the timer. 
                        aTimer.Interval = time * 1000.0;
                        aTimer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, ytlbus, modbus, aTimer);
                        //aTimer.Elapsed += OnTimedEvent(iterations, dataItems);            
                        aTimer.AutoReset = true;
                        aTimer.Enabled = true;

                    }
                    else
                    {

                        //Console.WriteLine("No Data available");
                    }

                }

            }


        }
        catch (Exception ex)
        {
            Console.WriteLine("Error at Line " + LineNumber(), ex.Message.ToString());
            throw ex;
        }
        finally
        {
        }


    }

public async void Work()
    {
        try
        {
            var data = await port.ReadAsync(4096);
            Console.WriteLine("Data at Line " + LineNumber(), data.ToString());
            //DoStuff(data);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error at Line " + LineNumber(), ex.Message.ToString());
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在我得到的错误是 The I/O operation has been aborted because of either a thread exit or an application request.

在 System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str) 在 System.IO.Ports.SerialStream.EndRead(IAsyncResult asyncResult) 在 System.IO.Stream.<>c.b__43_1(Stream stream, IAsyncResult asyncResult)在 System.Threading.Tasks.TaskFactory 1.FromAsyncTrimPromise1.Complete(TInstance thisRef, Func3 endMethod, IAsyncResult asyncResult, Boolean requiresSynchronization) at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at CommunicationProfile.SerialPortExtensions.d__0.MoveNext() in F:\MDC Development\Scheduler\CommunicationProfile\CombinedEngine.cs:line 1198 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime .CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at CommunicationProfile.SerialPortExtensions.d__1.MoveNext() in F:\MDC Development\Scheduler\CommunicationProfile\CombinedEngine.cs:line 1207 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at CommunicationProfile.CombinedEngine.d__27.MoveNext( ) 在 F:\MDC Development\Scheduler\CommunicationProfile\CombinedEngine.cs:line 368

错误发生在以下几行

var readBytes = await serialPort.BaseStream.ReadAsync(temp, 0, bytesToRead);//1198 line
await serialPort.ReadAsync(buffer, 0, count);//1207 line 
var data = await port.ReadAsync(4096); // 368 line
Run Code Online (Sandbox Code Playgroud)

注意:上述方法应在设备通电时连续运行,并且每隔60几秒钟就会发送数据。

任何帮助将不胜感激。

Gro*_*roo 2

代码的最后一个版本的主要问题是您调用Work()without await,因此该调用只是创建一个异步后台任务,而不等待其完成。此外,此功能不应存在于构造函数内部,而应存在于单独的async方法中。

第二个建议是从循环中删除if/语句,并将区分这些协议所需的数据放在单独的类中。switch您可以将每个协议所需的任何其他属性放置在此类中:

// contains specific settings for each ProtocolId
class ProtocolCfg
{
    public string ProtocolId { get; set; }
    public string PortName { get; set; }
    public int BaudRate { get; set; }
    public Parity Parity { get; set; }
    public int DataBits { get; set; }
    public StopBits StopBits { get; set; }

    public ProtocolCfg(string id, string port, int baud, Parity parity, int bits, StopBits stop)
    {
        ProtocolId = id; PortName = port; BaudRate = baud; Parity = parity;
        DataBits = bits; StopBits = stop;
    }
}
Run Code Online (Sandbox Code Playgroud)

有了这个,您的for循环就不需要区分这些协议:

class CombinedEngine
{
    readonly ProtocolCfg[] _portConfigs;

    public CombinedEngine(ProtocolCfg[] portConfigs)
    {
        // just assign the field and do nothing else
        _portConfigs = portConfigs;
    }

    public async Task Run(CancellationToken cancelToken)
    {
        // repeat indefinitely
        while (!cancelToken.IsCancellationRequested)
        {
            // run all protocols
            foreach (var portcfg in _portConfigs)
            {
                SerialPort serialPort = null;

                try
                {
                    // init using current config
                    serialPort = new SerialPort(
                         portcfg.PortName, portcfg.BaudRate, portcfg.Parity,
                         portcfg.DataBits, portcfg.StopBits);

                    serialPort.ReadTimeout = 500;

                    // await data
                    var data = await serialPort.ReadAsync(4096);

                    // do something with this data
                    Console.WriteLine($"P{portcfg.ProtocolId}: {data.Length}B received");

                    // do other stuff here

                    // wait between protocol changes if needed?
                    await Task.Delay(500, cancelToken);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
                finally
                {
                    serialPort?.Close();
                    serialPort?.Dispose();
                }
            }

            // wait between iterations?
            await Task.Delay(500, cancelToken);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

调用该Run函数时,请记住它是异步的,因此您需要调用await. 但是,您可能还想等待控制台内的按键,因此在这种情况下,您可以将返回的值存储Task在变量中,并在需要时取消它:

class Program
{
    static void Main(string[] args)
    {
        // define all possible protocols
        var protocols = new[]
        {
            new ProtocolCfg("01", "COM8",  9600, Parity.Even, 8, StopBits.One),
            new ProtocolCfg("02", "COM8", 38400, Parity.None, 8, StopBits.One)
        };

        // we will need this to tell the async task to end
        var tokenSource = new CancellationTokenSource();
        var token = tokenSource.Token; 

        // note that this constructor does not do anything of importance
        var engine = new CombinedEngine(protocols);

        // this is where all the work is done, pass the cancellation token 
        var task = engine.Run(token);

        // wait until Q is pressed
        Console.WriteLine("Running, press Q to quit... ");
        ConsoleKey k;
        do { k = Console.ReadKey().Key; }
        while (k != ConsoleKey.Q);

        // shutdown
        tokenSource.Cancel();
        task.Wait();            
        Console.WriteLine("Done.");
    }        
}
Run Code Online (Sandbox Code Playgroud)