我正在研究一种完全用c#编码的防盗报警解决方案。该程序与基于USB串口的IO板进行通信,该板具有硬连线的报警传感器。我有一个问题,即DataReceived事件无法更新主UI,除非我从DataReceived事件中调用一个名为TextLog的子程序(请参阅end)。奇怪的是,来自查询区域1的DataReceived事件能够更新主UI但不能更新区域2或3.此外,如果我在执行串行端口写入的行插入断点,它将按预期工作。
值得一提的是这些全局变量:
string ioCardRxString = "";
bool[] arrGlobalZoneStatus = new bool[4];
通过读取设置文件中的设置打开串口(一切正常)。
private void OpenIOComPort()
{
bool error = false;
else
{
// Set the port's settings
spIOCard.PortName = Settings1.Default.ioComPort;
spIOCard.BaudRate = int.Parse(Settings1.Default.ioBaudRate);
spIOCard.DataBits = int.Parse(Settings1.Default.ioDataBits);
spIOCard.StopBits = (System.IO.Ports.StopBits)Enum.Parse(typeof(System.IO.Ports.StopBits), Settings1.Default.ioStopBits);
spIOCard.Parity = (System.IO.Ports.Parity)Enum.Parse(typeof(System.IO.Ports.Parity), Settings1.Default.ioParity);
spIOCard.Handshake = (System.IO.Ports.Handshake)Enum.Parse(typeof(System.IO.Ports.Handshake), Settings1.Default.ioHandshake);
try
{
// Open the port
spIOCard.Open();
}
catch (UnauthorizedAccessException) { error = true; }
catch (System.IO.IOException) { error = true; }
catch (ArgumentException) { error = true; }
//On error, advise the user
if (error)
{
MessageBox.Show("Could not open the I/O Board COM port.");
globalIOCardError = true;
}
if (!error)
{
globalIOCardError = false;
// Do Nothing
}
}
}
定时器每500ms运行一次,它将3个命令写入串口,每个命令相隔25ms(硬件限制)。这些命令用于查询IO板,以确定每个报警传感器的状态。
private void tmrAuditSensors_Tick(object sender, EventArgs e)
{
try
{
if (globalIOCardError == false && Settings1.Default.disableSensorAudit == false)
{
if (Settings1.Default.zone1Armed == true)
{
spIOCard.Write("~in01~");
System.Threading.Thread.Sleep(25);;
}
if (Settings1.Default.zone2Armed == true)
{
spIOCard.Write("~in02~");
System.Threading.Thread.Sleep(25);;
}
if (Settings1.Default.zone3Armed == true)
{
spIOCard.Write("~in03~");
System.Threading.Thread.Sleep(25);;
}
//Applicable results will appear on serial data received event
}
}
catch
{
textLog("There was a problem writing to the serial port, check and restart app");
emergencyHalt();
}
}
串行端口DataReceived事件读取返回的字符串并写入全局布尔数组。如果该区域打开(= 1)则为True,如果该区域关闭(= 0)则为False。注意:已加星标的行。
private void spIO_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
ioCardRxString = spIOCard.ReadExisting();
textLog(ioCardRxString); //*Cannot make it work without this
if (ioCardRxString.Contains("in05=1") == true)
{
arrGlobalZoneStatus[1] = true;
}
if (ioCardRxString.Contains("in05=0") == true)
{
arrGlobalZoneStatus[1] = false;
}
if (ioCardRxString.Contains("in01=1") == true)
{
arrGlobalZoneStatus[2] = true;
}
if (ioCardRxString.Contains("in01=0") == true)
{
arrGlobalZoneStatus[2] = false;
}
if (ioCardRxString.Contains("in17=1") == true)
{
arrGlobalZoneStatus[3] = true;
}
if (ioCardRxString.Contains("in17=0") == true)
{
arrGlobalZoneStatus[3] = false;
}
}
另外,另一个计时器定期(每250ms)检查每个数组成员的内容,然后通过一些颜色更改和文本更新来更新主UI。
private void tmrCheckZoneStatus_Tick(object sender, EventArgs e)
{
if (arrGlobalZoneStatus[1] == true)
{
button10.BackColor = System.Drawing.Color.Red;
textLog(button10.Text + " was activated");
if (globalFullAlarmSet || globalNightAlarmSet || globalDoorsAlarmSet)
{
this.BeginInvoke(new EventHandler(delegate { checkAndActivateRelays(1); }));
}
}
if (arrGlobalZoneStatus[1] == false)
{
button10.BackColor = System.Drawing.Color.Gray;
}
if (arrGlobalZoneStatus[2] == true)
{
button11.BackColor = System.Drawing.Color.Red;
textLog(button11.Text + " was activated"); ;
if (globalFullAlarmSet || globalNightAlarmSet || globalDoorsAlarmSet)
{
this.BeginInvoke(new EventHandler(delegate { checkAndActivateRelays(2); }));
}
}
if (arrGlobalZoneStatus[2] == false)
{
button11.BackColor = System.Drawing.Color.Gray;
}
if (arrGlobalZoneStatus[3] == true)
{
button12.BackColor = System.Drawing.Color.Red;
textLog(button12.Text + " was activated");
if (globalFullAlarmSet || globalNightAlarmSet || globalDoorsAlarmSet)
{
this.BeginInvoke(new EventHandler(delegate { checkAndActivateRelays(3); }));
}
}
if (arrGlobalZoneStatus[3] == false)
{
button12.BackColor = System.Drawing.Color.Gray;
}
}
textLog sub:
public void textLog(string logEntry)
{
textLines++;
try
{
if (this.txtLog.InvokeRequired)
{
ChangeTextCallback MethodCallback = new ChangeTextCallback(textLog);
this.Invoke(MethodCallback, new object[] { logEntry });
}
else
{
if (!logEntry.Contains("?"))
{
txtLog.Text = txtLog.Text + DateTime.Now + " >: " + logEntry + "\r\n";
txtLog.SelectionStart = txtLog.Text.Length;
txtLog.ScrollToCaret();
if (textLines > 3000)
{
txtLog.Clear();
textLines = 0;
textLog("Text log cleared");
}
System.IO.StreamWriter sw = new System.IO.StreamWriter(logFile, true);
try
{
sw.WriteLine(DateTime.Now + " >: " + logEntry);
}
catch (Exception ex)
{
//
}
sw.Close();
}
}
}
catch
{
//
}
}
我想我需要在某个地方加入invoke / delegate,但作为一个菜鸟我有点挠头。感谢您的帮助。
由于
答案 0 :(得分:2)
我没有读过所有内容,但这应该让你走上正确的道路:
SerialPort.DataReceived
事件是在一个单独的线程上引发的。如果您需要异步处理数据的接收,并对其执行GUI操作,您可以执行以下操作:
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
var port = (SerialPort)sender;
string data = port.ReadExisting();
UpdateGui(data);
}
private void UpdateGui(string data) {
if (this.InvokeRequired) {
this.Invoke(new Action( d => UpdateGui(d) ));
return;
}
this.txtBox1.Text = data;
}
现在,那说.... 你真的想要使用DataReceived
吗?听起来你(主机)正在启动与外部主板的所有通信。如果是这种情况,那么我建议您改为使用同步(阻塞)读取:
1. Write the request out the port
2. Call read() with the expected number of bytes
3. Process the reply.