用C#通过modbus tcp协议写master端接收plc传来的数据

wangderful12 2015-12-14 09:51:37
用modbus tcp协议,在用户界面接收传来的数据,现在已经可以接收和显示传来的数据了,但想用户点击按钮之后界面上的数据实时变化,比如1s变化一次,该如何做?语言用的C#,下面贴出界面的代码,modbusTCP类用的就是从csdn下载的。只需要接收数据,不用写数据。

public frmStart()
{
InitializeComponent();
}

protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
static void Main()
{
Application.Run(new frmStart());
}
private void frmStart_Load(object sender, System.EventArgs e)
{
// Set standard format byte, make some textboxes
radBytes.Checked = true;
data = new byte[0];
ResizeData();
}
private void frmStart_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if(MBmaster != null)
{
MBmaster.Dispose();
MBmaster = null;
}
Application.Exit();
}
// ------------------------------------------------------------------------
// Button connect
// ------------------------------------------------------------------------
private void btnConnect_Click(object sender, System.EventArgs e)
{
try
{
// Create new modbus master and add event functions
MBmaster = new Master(txtIP.Text, 502);
MBmaster.OnResponseData += new ModbusTCP.Master.ResponseData(MBmaster_OnResponseData);
MBmaster.OnException += new ModbusTCP.Master.ExceptionData(MBmaster_OnException);
// Show additional fields, enable watchdog
grpExchange.Visible = true;
grpData.Visible = true;
}
catch(SystemException error)
{
MessageBox.Show(error.Message);
}
}

// ------------------------------------------------------------------------
// Button read coils
// ------------------------------------------------------------------------
private void btnReadCoils_Click(object sender, System.EventArgs e)
{
ushort ID = 1;
byte unit = Convert.ToByte(txtUnit.Text);
ushort StartAddress = ReadStartAdr();
byte Length = Convert.ToByte(txtSize.Text);

MBmaster.ReadCoils(ID, unit, StartAddress, Length);
}

// ------------------------------------------------------------------------
// Button read discrete inputs
// ------------------------------------------------------------------------
private void btnReadDisInp_Click(object sender, System.EventArgs e)
{
ushort ID = 2;
byte unit = Convert.ToByte(txtUnit.Text);
ushort StartAddress = ReadStartAdr();
byte Length = Convert.ToByte(txtSize.Text);

MBmaster.ReadDiscreteInputs(ID, unit, StartAddress, Length);
}

// ------------------------------------------------------------------------
// Button read holding register
// ------------------------------------------------------------------------
private void btnReadHoldReg_Click(object sender, System.EventArgs e)
{
ushort ID = 3;
byte unit = Convert.ToByte(txtUnit.Text);
ushort StartAddress = ReadStartAdr();
byte Length = Convert.ToByte(txtSize.Text);

MBmaster.ReadHoldingRegister(ID, unit, StartAddress, Length);
}

// ------------------------------------------------------------------------
// Button read holding register
// ------------------------------------------------------------------------
private void btnReadInpReg_Click(object sender, System.EventArgs e)
{
ushort ID = 4;
byte unit = Convert.ToByte(txtUnit.Text);
ushort StartAddress = ReadStartAdr();
byte Length = Convert.ToByte(txtSize.Text);

MBmaster.ReadInputRegister(ID, unit, StartAddress, Length);
}
private void MBmaster_OnResponseData(ushort ID, byte unit, byte function, byte[] values)
{
// ------------------------------------------------------------------
// Seperate calling threads
if (this.InvokeRequired)
{
this.BeginInvoke(new Master.ResponseData(MBmaster_OnResponseData), new object[] { ID, unit, function, values });
return;
}

// ------------------------------------------------------------------------
// Identify requested data
switch(ID)
{
case 1:
grpData.Text = "Read coils";
data = values;
ShowAs(null, null);
break;
case 2:
grpData.Text = "Read discrete inputs";
data = values;
ShowAs(null, null);
break;
case 3:
grpData.Text = "Read holding register";
data = values;
ShowAs(null, null);
break;
case 4:
grpData.Text = "Read input register";
data = values;
ShowAs(null, null);
break;
case 5:
grpData.Text = "Write single coil";
break;
case 6:
grpData.Text = "Write multiple coils";
break;
case 7:
grpData.Text = "Write single register";
break;
case 8:
grpData.Text = "Write multiple register";
break;
}
}

// ------------------------------------------------------------------------
// Modbus TCP slave exception
// ------------------------------------------------------------------------
private void MBmaster_OnException(ushort id, byte unit, byte function, byte exception)
{
string exc = "Modbus says error: ";
switch(exception)
{
case Master.excIllegalFunction: exc += "Illegal function!"; break;
case Master.excIllegalDataAdr: exc += "Illegal data adress!"; break;
case Master.excIllegalDataVal: exc += "Illegal data value!"; break;
case Master.excSlaveDeviceFailure: exc += "Slave device failure!"; break;
case Master.excAck: exc += "Acknoledge!"; break;
case Master.excGatePathUnavailable: exc += "Gateway path unavailbale!"; break;
case Master.excExceptionTimeout: exc += "Slave timed out!"; break;
case Master.excExceptionConnectionLost: exc += "Connection is lost!"; break;
case Master.excExceptionNotConnected: exc += "Not connected!"; break;
}

MessageBox.Show(exc, "Modbus slave exception");
}

// ------------------------------------------------------------------------
// Generate new number of text boxes
// ------------------------------------------------------------------------
private void ResizeData()
{
// Create as many textboxes as fit into window
grpData.Controls.Clear();
int x = 0;
int y = 10;
int z = 20;
while(y < grpData.Size.Width - 100)
{
labData = new Label();
grpData.Controls.Add(labData);
labData.Size = new System.Drawing.Size(30, 20);
labData.Location = new System.Drawing.Point(y, z);
labData.Text = Convert.ToString(x + 1);

txtData = new TextBox();
grpData.Controls.Add(txtData);
txtData.Size = new System.Drawing.Size(50, 20);
txtData.Location = new System.Drawing.Point(y + 30, z);
txtData.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
txtData.Tag = x;

x++;
z = z + txtData.Size.Height + 5;
if(z > grpData.Size.Height - 40)
{
y = y + 100;
z = 20;
}
}
}

// ------------------------------------------------------------------------
// Resize form elements
// ------------------------------------------------------------------------
private void frmStart_Resize(object sender, System.EventArgs e)
{
if(grpData.Visible == true) ResizeData();
}

// ------------------------------------------------------------------------
// Read start address
// ------------------------------------------------------------------------
private ushort ReadStartAdr()
{
// Convert hex numbers into decimal
if(txtStartAdress.Text.IndexOf("0x", 0, txtStartAdress.Text.Length) == 0)
{
string str = txtStartAdress.Text.Replace("0x", "");
ushort hex = Convert.ToUInt16(str, 16);
return hex;
}
else
{
return Convert.ToUInt16(txtStartAdress.Text);
}
}
// ------------------------------------------------------------------------
// Show values in selected way
// ------------------------------------------------------------------------
private void ShowAs(object sender, System.EventArgs e)
{
RadioButton rad;
if(sender is RadioButton)
{
rad = (RadioButton) sender;
if(rad.Checked == false) return;
}

bool[] bits = new bool[1];
int[] word = new int[1];

// Convert data to selected data type
if(radBits.Checked == true)
{
BitArray bitArray = new BitArray(data);
bits = new bool[bitArray.Count];
bitArray.CopyTo(bits, 0);
}
if(radWord.Checked == true)
{
if(data.Length < 2) return;
word = new int[data.Length/2];
for(int x=0;x<data.Length;x=x+2)
{
word[x/2] = data[x] * 256 + data[x+1];
}
}

// ------------------------------------------------------------------------
// Put new data into text boxes
foreach(Control ctrl in grpData.Controls)
{
if (ctrl is TextBox)
{
int x = Convert.ToInt16(ctrl.Tag);
if(radBits.Checked)
{
if(x <= bits.GetUpperBound(0))
{
ctrl.Text = Convert.ToByte(bits[x]).ToString();
ctrl.Visible = true;
}
else ctrl.Text = "";
}
if(radBytes.Checked)
{
if(x <= data.GetUpperBound(0))
{
ctrl.Text = data[x].ToString();
ctrl.Visible = true;
}
else ctrl.Text = "";
}
if(radWord.Checked)
{
if(x <= word.GetUpperBound(0))
{
ctrl.Text = word[x].ToString();
ctrl.Visible = true;
}
else ctrl.Text = "";
}
}
}
}
...全文
2590 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
jzdcf 2019-10-26
  • 打赏
  • 举报
回复
通讯部分单独一个后台线程收集数据,form里的线程只是呈现数据
DragonJesse 2017-03-13
  • 打赏
  • 举报
回复
学习了,正好准备做个监控的功能,学学
wangderful12 2015-12-17
  • 打赏
  • 举报
回复
引用 9 楼 FTD_Fred 的回复:
[quote=引用 6 楼 yerrow_dark 的回复:] [quote=引用 2 楼 FTD_Fred 的回复:] 代码太长,没看…… 关键是你要有一个timer,如果用户点击了按钮以后就设置timer.enable=true,然后实现每个时间间隔刷新一次页面
所以定义一个timer,还需要一个线程,timer是用来刷新接收数据,还是用来刷新显示数据的?然后这个timer应该是放在线程里的吧[/quote] 只要到时间就接收数据并刷新页面~因为和主界面是不同的线程所以你需要了解一下委托 至于你问的最后一个,你知道什么是线程就知道怎么回事了[/quote] 恩恩,用timer解决了,不过要重新用线程我得改大部分的逻辑
FTD_Fred 2015-12-15
  • 打赏
  • 举报
回复
引用 6 楼 yerrow_dark 的回复:
[quote=引用 2 楼 FTD_Fred 的回复:] 代码太长,没看…… 关键是你要有一个timer,如果用户点击了按钮以后就设置timer.enable=true,然后实现每个时间间隔刷新一次页面
所以定义一个timer,还需要一个线程,timer是用来刷新接收数据,还是用来刷新显示数据的?然后这个timer应该是放在线程里的吧[/quote] 只要到时间就接收数据并刷新页面~因为和主界面是不同的线程所以你需要了解一下委托 至于你问的最后一个,你知道什么是线程就知道怎么回事了
wangderful12 2015-12-14
  • 打赏
  • 举报
回复
谁能来帮下忙教一下
wangderful12 2015-12-14
  • 打赏
  • 举报
回复
有没有懂的人进来教一下呢
wangderful12 2015-12-14
  • 打赏
  • 举报
回复
引用 4 楼 Tidal_Choidi 的回复:
你都说了,已经可以正常接收数据了,我是觉得,你的接收数据和处理数据单独开一个线程来进行处理,不要在界面线程(或者说是控件线程)中去完成接收和处理数据的过程。如果这样,导致的后果就是界面卡顿或者丢失数据(显示)。合适的解决办法就是:写一个函数专门用来完成显示图形---》 ///以下是跨线程访问控件时的函数模型 public void DispDatas(DataType Datas)//参数 Datas就是你实时显示数据 { if(dispControl.跨线程访问属性==true) { DispDatas(DataType Datas); } else { //负责实时数据显示的代码 } } 在负责接收数据的线程中调用上面的函数,这样就可以解决你说的问题了。可以试试看。。
恩,我大概懂你什么意思,跨线程访问属性没懂什么意思? 不过我的代码该如何修改呢,只有显示数据,并没有对数据进行什么处理,唯一的处理就是用不同的方式show出来,有bits、bytes还有words三种方式
wangderful12 2015-12-14
  • 打赏
  • 举报
回复
引用 2 楼 FTD_Fred 的回复:
代码太长,没看…… 关键是你要有一个timer,如果用户点击了按钮以后就设置timer.enable=true,然后实现每个时间间隔刷新一次页面
所以定义一个timer,还需要一个线程,timer是用来刷新接收数据,还是用来刷新显示数据的?然后这个timer应该是放在线程里的吧
Tidal_Choidi 2015-12-14
  • 打赏
  • 举报
回复
不好意思,上面的代码有错误的地方,纠正一下: if(dispControl.跨线程访问属性==true) { DispDatas(DataType Datas); } 应该改为: if(dispControl.跨线程访问属性==true) { DispControl.BeginInvoke(new delegate(DispDatas),New object{Datas}) //跨线程,委托 }
Tidal_Choidi 2015-12-14
  • 打赏
  • 举报
回复
你都说了,已经可以正常接收数据了,我是觉得,你的接收数据和处理数据单独开一个线程来进行处理,不要在界面线程(或者说是控件线程)中去完成接收和处理数据的过程。如果这样,导致的后果就是界面卡顿或者丢失数据(显示)。合适的解决办法就是:写一个函数专门用来完成显示图形---》 ///以下是跨线程访问控件时的函数模型 public void DispDatas(DataType Datas)//参数 Datas就是你实时显示数据 { if(dispControl.跨线程访问属性==true) { DispDatas(DataType Datas); } else { //负责实时数据显示的代码 } } 在负责接收数据的线程中调用上面的函数,这样就可以解决你说的问题了。可以试试看。。
FTD_Fred 2015-12-14
  • 打赏
  • 举报
回复
另外,在新的线程中做这个工作,不要影响主界面的操作……
FTD_Fred 2015-12-14
  • 打赏
  • 举报
回复
代码太长,没看…… 关键是你要有一个timer,如果用户点击了按钮以后就设置timer.enable=true,然后实现每个时间间隔刷新一次页面

110,536

社区成员

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

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

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