简体   繁体   English

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

[英]Changing serial port configurations programmatically in c#

I have 2 types of devices that have different protocols and are connected with a single serial port.我有2类型的设备,它们具有不同的协议并与单个串行端口连接。 By protocol, I mean that serial port configurations are different.通过协议,我的意思是串口配置不同。

I have a protocol id p_id by which I can check which device is being currently read.我有一个协议 ID p_id通过它我可以检查当前正在读取哪个设备。 Below is my code下面是我的代码

Below is my main function which calls a class named CombinedEngine下面是我的主函数,它调用一个名为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);
 }

Combined Engine Class组合发动机类

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;
        }
       
       
    }


}

In the above code, I have checked that if p_id is equal to 01 then modbus serial port configurations should be done.在上面的代码中,我检查了如果p_id等于01则应该完成modbus串口配置。 But if p_id is 02 then ytlbus serial port configurations should be encountered.但是如果p_id02那么应该会遇到ytlbus串口配置。 Both devices have a different baud rate and a parity bit.两种设备都有不同的波特率和奇偶校验位。 So I have tried to set them所以我试图设置它们

Also, I have a timer which is 60 seconds.另外,我有一个60秒的计时器。 So after every 60 seconds, the next timer will be initialized.所以每60秒后,下一个计时器将被初始化。

For example.例如。 If p_id is 01 the code sets the baud rate to 9600 and parity to Even .如果p_id01代码将波特率设置为9600并将奇偶校验设置为Even Then SerialDataRecievedEventHandler is called which will check for any incoming data from the devices and it will manage the data dumping into the DB .然后SerialDataRecievedEventHandler被调用,它将检查来自设备的任何传入数据,并将管理转储到DB

Then the code will check the device details from a table mdc_meter_config and take out relevant information from it.然后代码将从表mdc_meter_config检查设备详细信息并从中取出相关信息。 All the details are added to the list one by one for all the devices.将所有详细信息一一添加到所有设备的列表中。 Also, the time would be carried out.此外,时间将进行。 In this case, all device's time is the same ie 60 seconds.在这种情况下,所有设备的时间都是相同的,即 60 秒。

The list is then passed to a variable which is then passed to an ElapsedEventHandler function.然后将该列表传递给一个变量,该变量随后传递给一个ElapsedEventHandler函数。 The frame sending is handled by it. frame发送由它处理。

The same will be done for p_id equals 02 the only difference is that it will set the baud rate to 38400 and parity to None .p_id等于02 p_id ,唯一的区别是它将波特率设置为38400并将奇偶校验设置为None

What problem I am facing?我面临什么问题?

The above code runs, the issue that I am facing is that both conditions worked at the same time.上面的代码运行,我面临的问题是两个条件同时工作。 ie for 01 , it will work and then simultaneously it will jump to the 02 condition.即对于01 ,它将工作,然后同时跳转到02条件。 Below is the image下面是图片

在此处输入图片说明

It should complete the work for any p_id value, then complete the work for other p_id value.它应该完成任何p_id值的工作,然后完成其他p_id值的工作。

Update 1更新 1

I have updated my code.我已经更新了我的代码。 Added a new async function, added only a single timer.添加了新的async功能,仅添加了一个计时器。 and added a class for serial port extentions并添加了一个串行端口扩展类

    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());
        }
    }

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

at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str) at System.IO.Ports.SerialStream.EndRead(IAsyncResult asyncResult) at System.IO.Stream.<>c.b__43_1(Stream stream, IAsyncResult asyncResult) at System.Threading.Tasks.TaskFactory 1.FromAsyncTrimPromise 1.Complete(TInstance thisRef, Func 3 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.TaskAwaiter 1.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 C在 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.FromAsyncTrimPromise (TInstance thisRef, Func 3 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.TaskAwaiter 1.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 C ommunicationProfile.SerialPortExtensions.d__1.MoveNext() in F:\\MDC Development\\Scheduler\\CommunicationProfile\\CombinedEngine.cs:line 1207 at 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() in F:\\MDC Development\\Scheduler\\CommunicationProfile\\CombinedEngine.cs:line 368 ommunicationProfile.SerialPortExtensions.d__1.MoveNext() 在 F:\\MDC Development\\Scheduler\\CommunicationProfile\\CombinedEngine.cs:line 1207 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 在 System.Runtime.CompilerServices.TaskAwaiterAndDebuggerNotHandleN (任务任务)在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at CommunicationProfile.CombinedEngine.d__27.MoveNext() in F:\\MDC Development\\Scheduler\\CommunicationProfile\\CombinedEngine.cs:line 368

The error is occurring at below lines错误发生在以下几行

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

Note: The above method should be running continuously as the devices are powered on and will send their data after every 60 seconds.注意:上述方法应在设备通电时连续运行,并在每60秒后发送数据。

Any help would be highly appreciated.任何帮助将不胜感激。

The main problem with the last revision of your code is that you are calling Work() without await , so the call just creates an asynchronous background task and doesn't wait for its completion.上次修订代码的主要问题是您调用Work()没有使用await ,因此该调用只会创建一个异步后台任务,而不会等待其完成。 Also, this functionality should not exist inside the constructor, but in a separate async method.此外,此功能不应存在于构造函数中,而应存在于单独的async方法中。

Second suggestion is to remove the if / switch statements from the loop, and place the data needed to differentiate these protocols in a separate class.第二个建议是从循环中删除if / switch语句,并将区分这些协议所需的数据放在一个单独的类中。 You could place any additional properties needed for each protocol inside this class:您可以在此类中放置每个协议所需的任何其他属性:

// 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;
    }
}

With this in place, your for loop doesn't need to differentiate between these protocols:有了这个,你的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);
        }
    }
}

When calling the Run function, remember that it's async, so you need to call await .调用Run函数时,请记住它是异步的,因此您需要调用await However, you also may want to wait for a keypress inside the console, so in that case you would store the returned Task in a variable, and cancel it when needed:但是,您可能还需要等待控制台内的按键操作,因此在这种情况下,您可以将返回的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.");
    }        
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM