OPC与三菱PLC通信
一、OPC软件配置
安装好KEPServerEX 6 软件,网上资源很多,自己下载。
打开软件:
我电脑上已经配置好了两个连接三菱Q03UDVPLC的通道,两个都可以同时连接,只需要设置不同的端口号就可以了。
再新建一个通道:
选择三菱网络通信
选择自己电脑的网口作为网络适配器
通道创建完成
点击添加设备,选择Q系列PLC:
ID设置,前面位网址,后面按照这样写就可以了,详细信息可以点击帮助了解。
选择TCP/IP,再设置端口号,PLC配置时需要添加此端口
设备创建完成
添加标记:
标记创建完成
二、PLC网络配置
设置IP地址
选择TCP,MC协议,设置端口号:
将参数下载到PLC,重启PLC。
三、通信测试
打开Quick Client
通信正常,同时另外两个通道也通信正常。
四、通过C#程序监控数据
需要一个OPCAutomation.dll文件,引入这个文件后就可以进行OPC通信编程。
代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OPCAutomation;//引入OPC命名空间
using System.Net;
using thinger.com.Models;
namespace OPC通信
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
#region 创建对象
private OPCServer KepServer;//OPC服务器对象
private OPCBrowser KepBrower;//OPC服务器中的变量集
private OPCGroups KepGroups;//OPC服务器中的组集合
private OPCGroup KepGroup;//OPC服务器中组集合内的组
private Array ServerHandles;//句柄
private Array ServerErrors;//错误信息
private int TransactionID = 0;
private int CancelID = 0;
#endregion
#region 刷新节点列表
/// <summary>
/// 刷新节点列表
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnRefresh_Click(object sender, EventArgs e)
{
this.cbServerNode.Items.Clear();
IPHostEntry iphost = Dns.GetHostEntry(Environment.MachineName);//获取计算机名
if(iphost.AddressList.Length>0)
{
for(int i=0;i<iphost.AddressList.Length;i++)
{
string host = Dns.GetHostEntry(iphost.AddressList[i]).HostName;//得到计算机名
if(!this.cbServerNode.Items.Contains(host))
{
this.cbServerNode.Items.Add(host);
}
}
}
if(this.cbServerNode.Items.Count>0)
{
this.cbServerNode.SelectedIndex = 0;
}
}
#endregion
#region 获取服务列表
private void cbServerNode_SelectedIndexChanged(object sender, EventArgs e)
{
KepServer = new OPCServer();
object serverList = KepServer.GetOPCServers(this.cbServerNode.Text);//根据计算机名获取OPC服务器
foreach (var item in (Array)serverList)
{
if (!this.cbServerName.Items.Contains(item))
{
this.cbServerName.Items.Add(item);
}
}
if (this.cbServerName.Items.Count > 0)
{
this.cbServerName.SelectedIndex = 0;
}
}
#endregion
#region 连接与断开
/// <summary>
/// 连接与断开
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnConnect_Click(object sender, EventArgs e)
{
if(this.btnConnect.Text=="Connect")
{
if(KepServer==null)
{
KepServer = new OPCServer();
}
try
{
KepServer.Connect(this.cbServerName.Text, this.cbServerNode.Text);//连接OPC
}
catch(Exception ex)
{
MessageBox.Show("连接失败" + ex.Message);
return;
}
//获取所有的变量
KepBrower = this.KepServer.CreateBrowser();
KepBrower.ShowBranches();
KepBrower.ShowLeafs(true);
this.lvName.Items.Clear();
//往lvName中添加变量名
foreach(var item in KepBrower)
{
this.lvName.Items.Add(item.ToString());
}
//通信组集合初始化
KepGroups = KepServer.OPCGroups;
KepGroups.DefaultGroupDeadband = 0;
KepGroups.DefaultGroupIsActive = true;
//通信组初始化
KepGroup = KepGroups.Add("测试组");
KepGroup.IsActive = true;//激活
KepGroup.IsSubscribed = true;//订阅
KepGroup.UpdateRate = 250;
KepGroup.IsSubscribed = true;
KepGroup.AsyncReadComplete += KepGroup_AsyncReadComplete;
this.btnConnect.Text = "Disconnect";
//开始读取
this.timer1.Enabled = true;
}
else
{
KepServer.Disconnect();
this.btnConnect.Text = "Connect";
//停止读取
this.timer1.Enabled = false;
}
}
#endregion
#region 双击创建变量
//创建变量集合
private List<OPCVariable> OPCVarList = new List<OPCVariable>();
/// <summary>
/// lvName双击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lvName_DoubleClick(object sender, EventArgs e)
{
if(this.lvName.SelectedItems!=null)
{
//添加一个变量到集合里
OPCVarList.Add(new OPCVariable()
{
OPCItemID = this.lvName.SelectedItems[0].Text
});
//OPCVarList>>ServerHander
//OPCVarList>>NumItems ItemIDS ClientHandles
//Array和集合的区别 前者索引从1开始 后者索引从0开始
List<string> ItemIDS = new List<string>();
List<int> ClientHandles = new List<int>();
//初始化
ItemIDS.Add("0");
ClientHandles.Add(0);
//变量个数
int Count = OPCVarList.Count;
//循环添加
for(int i=0;i<Count;i++)
{
ItemIDS.Add(OPCVarList[i].OPCItemID);
ClientHandles.Add(i + 1);
}
Array ItemID = ItemIDS.ToArray();
Array ClientHandle = ClientHandles.ToArray();
KepGroup.OPCItems.AddItems(Count, ref ItemID, ref ClientHandle, out ServerHandles, out ServerErrors);
}
}
#endregion
#region 定时读取
/// <summary>
/// 定时读取事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
//读取变量 服务器 OPCServer->组集合 OPCGroups->组 OPCGroup
//OPC读取三种方式:同步 异步 订阅
//KepGroup.SyncRead(); 同步
//KepGroup.AsyncRead(); 异步
//KepGroup.DataChange(); 订阅
if (KepServer!=null&& this.OPCVarList.Count > 0)
{
//同步读取
//KepGroup.SyncRead();
//异步读取
KepGroup.AsyncRead(this.OPCVarList.Count, ref ServerHandles, out ServerErrors, TransactionID, out CancelID);
}
}
/// <summary>
/// 异步读取完成事件
/// </summary>
/// <param name="TransactionID"></param>
/// <param name="NumItems"></param>
/// <param name="ClientHandles"></param>
/// <param name="ItemValues"></param>
/// <param name="Qualities"></param>
/// <param name="TimeStamps"></param>
/// <param name="Errors"></param>
private void KepGroup_AsyncReadComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps, ref Array Errors)
{
//数据解析
for(int i=1;i<=NumItems;i++)
{
object value = ItemValues.GetValue(i);
int clientHandle = Convert.ToInt32(ClientHandles.GetValue(i));
for(int j=0;j<this.OPCVarList.Count;j++)
{
if(j+1==clientHandle)
{
this.OPCVarList[j].OPCValue = value.ToString();
this.OPCVarList[j].Quality = Qualities.GetValue(i).ToString();
this.OPCVarList[j].UpdateTime = TimeStamps.GetValue(i).ToString();
}
}
}
this.dgvData.DataSource = null;
this.dgvData.DataSource = this.OPCVarList;
}
#endregion
}
}
由于DLL文件是32位的,所以只能编译为32位的程序,点击运行:
三个通道访问同一个数据都正常。
五、采用UDP通信
设置一个UDP通道,设置好端口号。
在PLC参数设置中添加一个UDP通道:
运行Quick Client,通信正常
C#监控程序正常工作。