问题描述
我有 2
类型的设备,它们具有不同的协议并通过单个串行端口连接.协议是指串口配置不同.
I have 2
types of devices that have different protocols and are connected with a single serial port. By protocol, I mean that serial port configurations are different.
我有一个协议 ID p_id
,我可以通过它检查当前正在读取哪个设备.下面是我的代码
I have a protocol id p_id
by which I can check which device is being currently read. Below is my code
下面是我的主要函数,它调用了一个名为 CombinedEngine
Below is my main function which calls a class named 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);
}
组合引擎类
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;
}
}
}
在上面的代码中,我检查了如果 p_id
等于 01
那么应该进行 modbus
串口配置.但是如果 p_id
是 02
那么应该会遇到 ytlbus
串口配置.两种设备都有不同的波特率和奇偶校验位.所以我尝试设置它们
In the above code, I have checked that if p_id
is equal to 01
then modbus
serial port configurations should be done. But if p_id
is 02
then ytlbus
serial port configurations should be encountered. Both devices have a different baud rate and a parity bit. So I have tried to set them
另外,我有一个 60
秒的计时器.所以每 60
秒后,下一个定时器就会被初始化.
Also, I have a timer which is 60
seconds. So after every 60
seconds, the next timer will be initialized.
例如.如果 p_id
为 01
,则代码将波特率设置为 9600
,奇偶校验设置为 Even
.然后调用 SerialDataRecievedEventHandler
,它将检查来自设备的任何传入数据,并将管理转储到 DB
中的数据.
For example. If p_id
is 01
the code sets the baud rate to 9600
and parity to 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
.
然后代码将从mdc_meter_config
表中检查设备详细信息并从中取出相关信息.所有设备的所有详细信息都将一一添加到列表中.此外,时间将被执行.在这种情况下,所有设备的时间都是相同的,即 60 秒.
Then the code will check the device details from a table mdc_meter_config
and take out relevant information from it. 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 i.e. 60 seconds.
然后将该列表传递给一个变量,然后将该变量传递给一个 ElapsedEventHandler
函数.frame
发送由它处理.
The list is then passed to a variable which is then passed to an ElapsedEventHandler
function. The frame
sending is handled by it.
p_id
等于 02
的唯一区别是将波特率设置为 38400
和奇偶校验设置为 <代码>无.
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
.
我面临什么问题?
上面的代码运行,我面临的问题是两个条件同时工作.即对于 01
,它将工作,然后同时跳转到 02
条件.下面是图片
The above code runs, the issue that I am facing is that both conditions worked at the same time. i.e. for 01
, it will work and then simultaneously it will jump to the 02
condition. Below is the image
它应该完成任何 p_id
值的工作,然后完成其他 p_id
值的工作.
It should complete the work for any p_id
value, then complete the work for other p_id
value.
更新 1
我已经更新了我的代码.添加了一个新的 async
函数,只添加了一个计时器.并为串口扩展添加了一个类
I have updated my code. Added a new async
function, added only a single timer.
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());
}
}
我现在得到的错误是 由于线程退出或应用程序请求,I/O 操作已中止.
在 System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)在 System.IO.Ports.SerialStream.EndRead(IAsyncResult asyncResult)在 System.IO.Stream.<>c.b__43_1(Stream 流,IAsyncResult asyncResult)在 System.Threading.Tasks.TaskFactory1.FromAsyncTrimPromise
1.Complete(TInstance thisRef, Func3 endMethod, IAsyncResult asyncResult, Boolean requiresSynchronization) 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task任务)在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter
1.GetResult()在 F:MDC DevelopmentSchedulerCommunicationProfileCombinedEngine.cs:line 1198 中的 CommunicationProfile.SerialPortExtensions.d__0.MoveNext()在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.GetResult()在 F:MDC DevelopmentSchedulerCommunicationProfileCombinedEngine.cs:line 1207 中的 CommunicationProfile.SerialPortExtensions.d__1.MoveNext()在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()在 F:MDC DevelopmentSchedulerCommunicationProfileCombinedEngine.cs:line 368 中的 CommunicationProfile.CombinedEngine.d__27.MoveNext() 处
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, 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 DevelopmentSchedulerCommunicationProfileCombinedEngine.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 DevelopmentSchedulerCommunicationProfileCombinedEngine.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 DevelopmentSchedulerCommunicationProfileCombinedEngine.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
注意:上述方法应在设备开机时连续运行,并在每60
秒后发送数据.
Note: The above method should be running continuously as the devices are powered on and will send their data after every 60
seconds.
任何帮助将不胜感激.
推荐答案
你的代码最后一个版本的主要问题是你在没有 await
Work()>,所以调用只是创建一个异步后台任务,而不是等待它的完成.此外,此功能不应存在于构造函数中,而应存在于单独的 async
方法中.
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. Also, this functionality should not exist inside the constructor, but in a separate async
method.
第二个建议是从循环中删除 if
/switch
语句,并将区分这些协议所需的数据放在单独的类中.您可以在此类中放置每个协议所需的任何其他属性:
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. 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;
}
}
有了这个,你的 for
循环就不需要区分这些协议了:
With this in place, your for
loop doesn't need to differentiate between these protocols:
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
函数时,记住它是异步的,所以需要调用await
.但是,您可能还想等待控制台内的按键,因此在这种情况下,您可以将返回的 Task
存储在一个变量中,并在需要时取消它:
When calling the Run
function, remember that it's async, so you need to call 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:
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.");
}
}
这篇关于在 C# 中以编程方式更改串行端口配置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!