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_id
是01
代码将波特率设置为9600
,奇偶校验设置为Even
。然后SerialDataRecievedEventHandler
被调用,它将检查来自设备的任何传入数据,并将管理转储到DB
.
然后代码会从表中检查设备详细信息mdc_meter_config
并从中取出相关信息。将所有详细信息一一添加到所有设备的列表中。此外,时间将进行。在这种情况下,所有设备的时间都是相同的,即 60 秒。
然后将该列表传递给一个变量,该变量然后传递给一个ElapsedEventHandler
函数。该frame
由它来处理发送。
对p_id
equals02
执行相同的操作,唯一的区别是它将波特率设置为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.FromAsyncTrimPromise
1.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.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 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
几秒钟就会发送数据。
任何帮助将不胜感激。
代码的最后一个版本的主要问题是您调用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)