C#上位机与PLC通信,测试程序,偶尔会出现界面卡死,不能进行数据采集。

奔跑的啸呼 2018-12-21 01:49:48
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using Modbus.Device;
using Modbus.IO;
using Modbus;
using System.Text;
using Modbus.Message;
using Modbus.Data;
namespace MES
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}


private void label1_Click(object sender, EventArgs e)
{

}

static SerialPort serialPort1 = new SerialPort("COM7");//声明串口

IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(serialPort1);
byte slaveId = 2;


//Read holdregisters
ushort startAddress = 50;
ushort numRegisters = 10;

//Read input
ushort startAddress_c1 = 0;
ushort numRegisters_c1 = 8;

//Read output
ushort startAddress_c2 = 0;
ushort numRegisters_c2 = 8;

//读取现场状态
public void ReadState()
{
//寄存器数值
ushort[] registers = master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);


float f_done = ushort2float(registers[0], registers[1]);
float f_un = (Convert.ToSingle(Txtb_Set.Text))-f_done;

Txtb_Done.Text = f_done.ToString();
Txtb_Un.Text = f_un.ToString();
//进度条显示


//布尔量输入
bool []intput_1 = master.ReadInputs (slaveId, startAddress_c1, numRegisters_c1);
if (intput_1[0])
Lab_Xianti.ForeColor = Color.Green;
else
Lab_Xianti.ForeColor = Color.Gray;

//布尔量输出
bool[] state = master.ReadCoils(slaveId, startAddress_c2, numRegisters_c2);
if (state[1])
Lab_RaoXian.ForeColor = Color.Green;
else
Lab_RaoXian.ForeColor = Color.Gray;

}
//窗口加载时打开串口,设置参数
private void Form2_Load(object sender, EventArgs e)
{

serialPort1.BaudRate = 9600;
serialPort1.DataBits = 8;
serialPort1.Parity = Parity.None;
serialPort1.StopBits = StopBits.One;
serialPort1.ReadTimeout = 1000;
serialPort1.WriteTimeout = 1000;

serialPort1.Open();//只能执行一次,不能在定时器中持续调用
////serialPort1.ReceivedBytesThreshold = 1;
//ReadState();

}
// public Timer timer1 =new Timer();




//实时时间显示
private void timer1_Tick(object sender, EventArgs e)
{
DateTime dt = DateTime.Now;
label4_Time.Text = dt.ToString();
}
//定时采集数据
private void timer2_Tick(object sender, EventArgs e)
{
ReadState();
progressBar1.Value = Convert.ToInt32((Convert.ToSingle(Txtb_Done.Text) / (Convert.ToSingle(Txtb_Set.Text)) * 100));
}
//ushort2float
public float ushort2float(ushort a, ushort b)
{
string aa = Convert.ToString((a), 16);
string bb = Convert.ToString((b), 16);
bb = aa + bb;
//string to float
uint num = uint.Parse(bb, System.Globalization.NumberStyles.AllowHexSpecifier);
byte[] floatVals = BitConverter.GetBytes(num);
float f = BitConverter.ToSingle(floatVals, 0);
return f;
}

private void button1_Click(object sender, EventArgs e)
{
progressBar1.Value = Convert.ToInt32((Convert.ToSingle(Txtb_Done.Text) / (Convert.ToSingle(Txtb_Set.Text)) * 100));
}

private void Form2_FormClosed(object sender, FormClosedEventArgs e)
{
master.Dispose();
master = null;

// Destroy serial port
serialPort1.Close();
serialPort1.Dispose();
serialPort1 = null;
System.Environment.Exit(0);
}


}



}
...全文
2461 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
埃和智能 2019-05-28
  • 打赏
  • 举报
回复
用以太网,建立多个连接用多啊线程,几百台PLC都没有问题的;
这里有范例:
http://blog.sina.com.cn/s/blog_16d7d3ecb0102x1z5.html
qq_26485823 2021-12-06
  • 举报
回复
@埃和智能 U
gd6321374 2018-12-29
  • 打赏
  • 举报
回复


我使用winform 做串口助手,都是开工作线程接收数据(接收方法都是阻塞)并处理数据,然后通过委托发送消息到UI线程中显示数据。

龙宜坡 2018-12-27
  • 打赏
  • 举报
回复
搞WinForm,必用多线程,一个线程搞定UI,不要在UI线程中搞一些需要挂起等待的事情;需要挂起的动作放到别的线程中去。 再如P哥所说,不要胡乱轮询。
  • 打赏
  • 举报
回复
记住一点基本道理,要事件驱动设计,不要胡乱轮询。
  • 打赏
  • 举报
回复
是的!当状态改变时才应该去读取,而那种疯狂地反复读取状态、不管需不需要都轮询的方式,而且读取了之后不管值有没有改变都卡死到 UI 主线程去刷新的方式,肯定要把好好的程序迅速搞死。而且这类思路会指数级地叠加,就好像编程上的传让性脓疮一样。
  • 打赏
  • 举报
回复
你的进程中假设使用了1万个控件实例、每个控件都有那么几十种事件可以捕获,那么你监听其状态值改变时自然都知道怎么写代码。 为什么自己设计程序的时候就只知道只能搞 几十万个 timer 轮询、或者搞几十万个死循环的”线程“去轮询呢?(记住,每一个线程都要占用至少1K栈空间、要占用操作系统上下文切换调度时间) 这是一个设计问题。当你设计一个稍微实用的软件,只有设计思路堵死了,或者没有学过设计模式,才会仅仅用刚学编程时的方式。
  • 打赏
  • 举报
回复
比如说 Data_Received 之后有可能会通知我们来更新界面,这是多么基本的设计概念啊?!!就好像你用了 Text 控件知道捕获 TextChanged 事件,用其它控件则知道如何捕获处理控件的事件,这也是基本的设计概念。如果你遇到一个人说每一个 TextBox 控件都要对应一个“线程”来死循环轮询,遇到其它每一个控件也对应一个‘线程“,遇到其它一个对象属性监听也要对应一个”线程“.......无数线程来轮询,那么此时虽然用了线程来缓解界面卡死现象了,这就好像是血管被10000处、每一处血栓都堵死了90%,这个软件快被这类代码弄垮掉了。
  • 打赏
  • 举报
回复
“线程”这个概念并不能培养起最基本地“事件驱动设计”基本素质,反而会助长滥用线程。此时“线程”只会让脓疮掩盖得更久一点,并不会很好地解决设计问题。
threenewbee 2018-12-21
  • 打赏
  • 举报
回复
把更新界面的放在UI线程,别的放在后台线程。
zxy2847225301 2018-12-21
  • 打赏
  • 举报
回复
是你这段代码阻塞了主线程
//定时采集数据
private void timer2_Tick(object sender, EventArgs e)
{
ReadState();
progressBar1.Value = Convert.ToInt32((Convert.ToSingle(Txtb_Done.Text) / (Convert.ToSingle(Txtb_Set.Text)) * 100));
}

就算是另外开线程也不好开吧,因为你靠定时器2每隔一段时间操作一次耗时的操作,估计你的程序需要重新设计了,要不你把定时器2定时的时间长一点,但是定时的时间需要是串口读写时间的倍数
平底锅锅锅 2018-12-21
  • 打赏
  • 举报
回复
刷新用线程。serialPort1.WriteTimeout = 1000;注释掉试试。
xian_wwq 2018-12-21
  • 打赏
  • 举报
回复
界面卡死是主线程被阻塞了 耗时的操作都需要放到工作线程中去,使用线程,UI交互就需要使用委托 使用timer也可以,但是timer有短板,无法解决重入 也就是如果某一轮由于异常没有执行完超时, 发生重入后,就会发生问题。 如果对线程不熟悉,可以换成backgroundworker控件

110,571

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧