本节目标:
1)编写汇川PLC程序,用于和Labview进行通信;
2)使用UaExpert,读取汇川PLC中的变量与OPCUA节点名称;
3)使用C#编写用于OpcUa通信的dll;
4)使用Labview对dll库进行打包使用;
5)编写Labview程序,读写服务器中变量值,创建订阅触发事件;
本节内容执行结果如下,连接后点击写入按钮可以将设定的值写入PLC中,点击读取可以读取写入的值,点击PLCStart,PLC中的程序运行,Y1-Y4依次点亮;

1.1 编写汇川PLC程序
1) 下载安装软件

下载完成后,点击安装。本人使用的是V1.8.1.3版本。
2)创建程序
本次使用的时汇川的AM521,语言选择结构化文本,点击确定。

3)设置OpcUa
双击Device,鼠标左击OPC UA,选择模式1。

4)新建变量

保持默认名称,点击确定;

在GVL文件中添加变量;
VAR_GLOBAL
i_steps :INT;
start :INT;
value1:REAL;
OutVal :UINT;
Y0 :BOOL;
Y1 :BOOL;
Y2 :BOOL;
Y3 :BOOL;
Ton1 :TON;
END_VAR
5)添加程序
双击PLC_PRG,打开编辑框;

添加程序;
Ton1(PT := T#1000MS);
CASE i_steps OF
10:
if start=1 THEN
start:=0;
Y3:=FALSE;
Y0:=TRUE;
i_steps:=20;
END_IF
20:
Ton1.IN:=TRUE;
IF Ton1.Q THEN
Ton1.IN:=FALSE;
Y0:=FALSE;
Y1:=TRUE;
i_steps:=30;
END_IF
30:
Ton1.IN:=TRUE;
IF Ton1.Q THEN
Ton1.IN:=FALSE;
Y1:=FALSE;
Y2:=TRUE;
i_steps:=40;
END_IF
40:
Ton1.IN:=TRUE;
IF Ton1.Q THEN
Ton1.IN:=FALSE;
Y2:=FALSE;
Y3:=TRUE;
i_steps:=0;
END_IF
END_CASE
//赋值
OutVal:=0;
IF Y0 THEN
OutVal := OutVal OR SHL(UINT#1, 0);
END_IF;
IF Y1 THEN
OutVal := OutVal OR SHL(UINT#1, 1);
END_IF;
IF Y2 THEN
OutVal := OutVal OR SHL(UINT#1, 2);
END_IF;
IF Y3 THEN
OutVal := OutVal OR SHL(UINT#1, 3);
END_IF;
6)符号配置
添加这一步,OPC才可以读到标签;
添加符号配置;

默认名称,点击打开;

点击编译,等待编译完成;

编译完成后,选择需要向公网公开的变量,这里勾选整个GVL,点击导出;

7)编译完成后,下载运行
下面是运行状态下的变量状态;

1.2 UaExpert读取节点名称
打开UaExpert,此为OPC客户端,用于测试服务器
点击加号


输入URL

选择匿名登录

连接


将变量拉入到显示界面处。


可以看到变量的值,同时也可以修改值。
其中四个变量的节点名称如下:
ns=4;s=|var|Inovace-ARM-Linux.Application.GVL.i_steps
ns=4;s=|var|Inovace-ARM-Linux.Application.GVL.start
ns=4;s=|var|Inovace-ARM-Linux.Application.GVL.value1
ns=4;s=|var|Inovace-ARM-Linux.Application.GVL.OutVal
节点名称在后续的上位机编写时使用。
1.3 创建OPC UA通信dll
1)新建项目

2)现在依赖包
下载OpcUaHelper,用于OpcUa通信。

下载Costura.Fody包,可以将多个dll合并到一个dll中,让文件变得更加简洁。

3)添加程序
OpcUaConnector.cs中如下;
具体讲解可以参考博文:进阶01:Labview与OPC UA设备通信_labview opc ua-CSDN博客
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpcUaHelper;
using Opc.Ua;
using Opc.Ua.Client;
namespace OpcUaDll
{
public class OpcUaConnector
{
private string ServerUrl;
private OpcUaClient m_OpcUaClient;
public event EventHandler<TEventArgs> OnSubscription;
public struct TEventArgs
{
public string key;
public object value;
}
public OpcUaConnector(string url)
{
ServerUrl = url;
m_OpcUaClient = new OpcUaClient();
}
public bool Connect()
{
m_OpcUaClient.UserIdentity = new UserIdentity(new AnonymousIdentityToken());//匿名链接
try
{
m_OpcUaClient.ConnectServer(ServerUrl);//链接服务器
}
catch (Exception ex)
{
Console.WriteLine("Connect failed!!");
return false;
}
return true;
}
public object ReadTag(string tag)
{
object val;
try
{
val = m_OpcUaClient.ReadNode<object>(tag);
}
catch
{
return false;
}
return val;
}
public object[] ReadTags(string[] tags)
{
List<object> val1 = m_OpcUaClient.ReadNodes<object>(tags);
object[] result = new object[val1.Count];
int i = 0;
foreach(object obj in val1)
{
result[i] = obj;
i++;
}
return result;
}
public bool WriteTag(string tag, object val)
{
try
{
var var1 = m_OpcUaClient.WriteNode<object>(tag, val);
}
catch(Exception ex)
{
Console.WriteLine("Write failed!!");
return false;
}
return true;
}
public bool WriteTags(string[] tags, object[] vals)
{
try
{
var var1 = m_OpcUaClient.WriteNodes(tags, vals);
}
catch (Exception ex)
{
Console.WriteLine("Write failed!!");
return false;
}
return true;
}
public bool AddSubscription(string key,string tag)
{
if (m_OpcUaClient.Connected)
{
m_OpcUaClient.AddSubscription(key, tag, SubCallback);
return true;
}
else
return false;
}
public bool AddSubscriptions(string[] keys, string[] tags)
{
if (m_OpcUaClient.Connected)
{
for (int i = 0; i < tags.Length;i++ )
m_OpcUaClient.AddSubscription(keys[i], tags[i], SubCallback);
return true;
}
else
return false;
}
private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
{
MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
//notification.Value.WrappedValue.Value;
TEventArgs ww = new TEventArgs();
ww.key = key;
ww.value = notification.Value.WrappedValue.Value;
OnSubscription.Invoke(this, ww);
}
public bool RemoveSubscription(string key)
{
if (m_OpcUaClient.Connected)
{
m_OpcUaClient.RemoveSubscription(key);
return true;
}
else
return false;
}
public bool RemoveAllSubscription()
{
if (m_OpcUaClient.Connected)
{
try
{
m_OpcUaClient.RemoveAllSubscription();
}
catch
{
return false;
}
return true;
}
else
return false;
}
public bool Disconnect()
{
if (m_OpcUaClient.Connected)
{
m_OpcUaClient.RemoveAllSubscription();
m_OpcUaClient.Disconnect();
}
return true;
}
}
}
程序编辑完成后,编译,可在debug文件夹中生成dll文件。

1.4 Labview创建OpcUaHelper类
新建OpcUaHelper类如下,在类中新建多个子vi,该类用于集成OpcUa的通信。

将OpcUaDll.dll拷贝到类的文件夹OpcUaDll下。
用于连接OpcUa服务器;

其中,构造器中内容如下;

用于断开OpcUa通信,同时可以将订阅等关掉;

读取一个节点的变量值;

用于读取多个节点的值;

用于写入单个节点的值;

用于写入多个节点的值;

用于添加多个订阅;


8)新建OpcUaRemoveSubscription.vi
去除单个节点订阅;

1.5 Labview调用OpcUa通讯类
1)新建main.vi,前面板如下

2)程序框图
主界面的程序流程图如下所示:

3)连接事件
点击连接按钮触发该事件,如果是true,先连接OPC服务器,在注册OutVal订阅事件,如果OutVal值变化会触发注册的用户事件,将变量值传递过来;

如果是假,关闭OPC连接;

4)读取事件
使用读取vi读取节点中的值,并显示在界面上;

5)写入事件
将界面上输入的值写入到节点中;

6)停止按钮
断开OPC连接,程序停止运行;

7)用户事件
将读取到的OutVal值分解成四个bool量,显示在界面上;

8)PLC Start事件
向i_steps变量写10,向start变量写1,触发PLC开始运行程序,请结合上面的PLC程序进行学习;

1.6 运行
PLC程序下载成功后,运行Labview程序;
点击连接按钮,没有报错即连接成功;
连接后点击写入按钮可以将设定的值写入PLC中;
点击读取可以读取写入的值;
点击PLCStart,PLC中的程序运行,Y1-Y4依次点亮;

上面的内容是Labview与汇川PLC通讯的简单示例,基于该简单示例可以开发更为复杂的程序,用于自动化开发。