我有 2
具有不同协议(protocol)并与单个串行端口连接的设备类型。通过协议(protocol),我的意思是串行端口配置不同。
我有一个协议(protocol) 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);
}
组合引擎类 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;
}
}
}
在上面的代码中,我检查了 if 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
也一样。等于 02
唯一的区别是它将波特率设置为38400
和 None
的平价.我面临什么问题?
上面的代码运行,我面临的问题是两个条件同时工作。即对于
01
,它将工作,然后同时跳转到 02
健康)状况。下面是图片它应该完成任何
p_id
的工作。值,然后为其他 p_id
完成工作值(value)。更新 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());
}
}
我现在得到的错误是 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, 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 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
错误发生在以下几行
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
后发送数据。秒。任何帮助将不胜感激。
最佳答案
您的代码的最新版本的主要问题是您正在调用 Work()
没有 await
,所以调用只是创建一个异步后台任务,而不是等待它的完成。此外,此功能不应存在于构造函数中,而应存在于单独的 async
中。方法。
第二个建议是删除 if
/switch
循环中的语句,并将区分这些协议(protocol)所需的数据放在单独的类中。您可以在此类中放置每个协议(protocol)所需的任何其他属性:
// 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
loop 不需要区分这些协议(protocol):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
在变量中,并在需要时取消它: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# - 在 C# 中以编程方式更改串行端口配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64203396/