自动化项目上用的是PLC控制器,西门子的1200系列,本身支持OPC通讯。如果用上位机与PLC进行OPC通讯,有两种方式,一种是把PLC作为服务器,自己写OPC客户端,直接读取。
另一种是通过中间软件,比如kepware这种,kepware连接plc,上位机读取kepware。
两种方式都可以,不同的是kepware是收费软件,如果不差钱的,建议使用kepware,省事,好用。
如果要节省成本,又愿意自己折腾的,那么可以用上位机自己来写。
本文主要是说一下第二种方式,纯粹是自己的个人经验,如有不足之处,烦请告知。
工具:win10系统
visual studio2019(VB.net)
西门子1200系列plc(支持opc功能,固件版本应该是在V4.4及以上才可以)
本文主要用VB.net来做上位机编码,对VB.net没有兴趣的可以不用继续往下看了。
准备工作就是把用到的软件都下载好,包括上位机软件visual studio2019(其他版本没用过),西门子编程软件TIA PORTAL V16(可以在西门子官网下载试用版)。
visual studio2019链接:
https://visualstudio.microsoft.com/zh-hans/vs/
博图V16链接:https://support.industry.siemens.com/cs/document/109772803/simatic-step-7-incl-safety-and-wincc-v16-trial-download?dti=0&lc=en-KW
西门子软件比较大,下载和安装都要花一点时间,自己看着来。
(PS:博图最新的版本已经到V17了,但V16还没有用明白呢)
本文主要说上位机的程序,PLC端的简单说一下:
博图V16新建项目,添加一个1200的PLC,如1214C DC/DC/DC,固件V4.4及以上。
然后在的PLC的设备视图里设置一下opc参数,例如激活opc ua 服务器,设置端口、超时时间、采样率等,安全设置里添加证书和许可证,然后在左侧的项目导航器里新添一个opc服务器接口,将想要读取的PLC变量添加进入,可以设置一下变量格式,以便上位机读取。
PLC端设置好后,编译下载即可。
上位机这边,电脑上打开visual studio2019,新建一个VB.net窗体项目,这里有个注意的地方,是需要添加一下opc的引用。
这个引用需要先在注册表注册,是一个dll文件:OPCDAAuto.dll。
注册的时候如果有问题,参考下面的解决办法:
感谢这位的提供:@tayloramanda
引用添加好后,在程序里就可以使用opcautomation这个库了。
其实关于VB.net中的opc编程,我也是看了好多网上的文章,还有opc的官方文档:
opc官网链接:
https://www.opcfoundation.cn/
不过opc不是免费的,官网里的一些文档是需要会员才能下载的,而会员必须得氪金。
opc通讯分为几步,即扫描opc服务器、连接、数据读写或者订阅事件、断开服务器。
我们一步步来,首先是连接opc服务器。作为客户端程序,要从服务器获取数据,首先得和服务器建立连接,这是最基本的要求,而要连接服务器,首先要知道,要连接的服务器的信息。
opcAutomation库提供了“GetOPCServers”模块,用于获取当前处于活动状态的opc服务器列表。
Private Sub GetOpcServer(lb As ListBox) '获取OPC服务器列表
Dim getserver As OPCAutomation.OPCServer
Dim infosvr As Object
Dim i As Integer
getserver = New OPCAutomation.OPCServer
infosvr = getserver.GetOPCServers
lb.Items.Clear()
For i = LBound(infosvr) To UBound(infosvr)
lb.Items.Add(infosvr(i))
Next i
End Sub
获取opc服务器列表后,选择自己要连接的服务器的名称,开始连接。OPCAutomation库提供“Connect”模块,用于与选定的opc服务器进行连接。
Private Sub OpcServerConnect(daautosvr As OPCAutomation.OPCServer, lb As ListBox, lbl As Label)
Try
daautosvr.Connect(lb.SelectedItem, "")
If daautosvr.ServerState = OPCAutomation.OPCServerState.OPCRunning Then
lbl.Text = "已连接到:" + daautosvr.ServerName
Else
lbl.Text = "状态" + daautosvr.ServerState.ToString
End If
Catch
MessageBox.Show("")
End Try
End Sub
和opc服务器连接成功后,就可以想办法读取服务器的数据了。因为opc的数据是item,即一个item代表一个数据,可以每次单独添加,也可以群体添加。
要读取服务器的item,需要在客户端建立和服务器中名称相同的item,然后使用read读取。
新建item
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click '新建item
Dim itm1 As ListViewItem
itemID(newitemcount) = TextBox2.Text
item(newitemcount) = daitems.AddItem(itemID(newitemcount), newitemcount)
Label2.Text = newitemcount
itm1 = ListView1.Items.Add(itemID(newitemcount))
itm1.SubItems.AddRange({"", "", ""})
newitemcount = newitemcount + 1
itemcount = itemcount + 1
End Sub
read数据
Private Sub OpcItemRead(item As OPCAutomation.OPCItem, tb As TextBox)
Dim s, v, q, t As Object
item.Read(1, v, q, t)
tb.Text = v
End Sub
除了使用read、write进行读写item或items之外,opc提供一种基于数据变化的订阅事件,这个事件在groups中。groups是opc数据中的组,即item是包含中组里面的,所以,在客户端建立一个groups,里面包含多个group,而group又包含多个item或items。所以,对于整体的groups,对其数据进行统一监视,只要里面有数据在实时改变,就可以通过订阅事件来通知客户端,客户端就可以知道当前数据的实时值。
Private Sub dagroups_GlobalDataChange(TransactionID As Integer, GroupHandle As Integer, NumItems As Integer, ByRef ClientHandles As Array, ByRef ItemValues As Array, ByRef Qualities As Array, ByRef TimeStamps As Array) Handles dagroups.GlobalDataChange
For i = 1 To NumItems
itemvalue(i) = ItemValues(i)
quality(i) = Qualities(i)
tt(i) = TimeStamps(i)
Next i
For i = 1 To NumItems
ListView1.Items.Item(i - 1).SubItems(1).Text = itemvalue(NumItems + 1 - i)
ListView1.Items.Item(i - 1).SubItems(2).Text = quality(NumItems + 1 - i)
Next i
End Sub
当然,如果不读写数据的时候,记得关闭opc服务器并释放资源。
Private Sub OpcServerDisconnect(daautosvr1 As OPCAutomation.OPCServer, dagroups1 As OPCAutomation.OPCGroups,
dagroup1 As OPCAutomation.OPCGroup, daitems1 As OPCAutomation.OPCItems,
daitem1 As OPCAutomation.OPCItem)
dagroup1.IsActive = False
dagroups1.Remove(dagroup.ServerHandle)
daautosvr1.Disconnect() '断开opc服务器连接
daitem1 = Nothing
daitems1 = Nothing
dagroup1 = Nothing
dagroups1 = Nothing
daautosvr1 = Nothing
End Sub
下面是完整程序:
Option Explicit On
Imports System.Text
Public Class Form1
Dim WithEvents daautosvr As OPCAutomation.OPCServer
Dim WithEvents dagroups As OPCAutomation.OPCGroups
Dim WithEvents dagroup As OPCAutomation.OPCGroup
Dim daitems As OPCAutomation.OPCItems
Dim daitem As OPCAutomation.OPCItem
Dim item(100) As OPCAutomation.OPCItem
Dim newitemcount As Integer
Dim itemcount As Integer
Public dabrowser As OPCAutomation.OPCBrowser
Dim itemID(100) As String
Public Shared itemvalue(100) As String
Dim quality(100) As String
Dim timestamps(100) As String
Dim tt(100) As String
Dim clihand() As Integer
Dim svrhand() As Integer
Dim err() As Integer
Dim numitems As Integer = 3
Dim r1, ap As Object
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
GetOpcServer(ListBox1)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
With ListView1
.Clear()
.View = View.Details
.FullRowSelect = False '表示当单击此行选项时,是否选中整行
.Columns.Add("itemID", 80)
.Columns.Add("值", 80)
.Columns.Add("质量", 80)
.Columns.Add("时间", 80)
End With
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
daautosvr = New OPCAutomation.OPCServer
OpcServerConnect(daautosvr, ListBox1, Label1)
dabrowser = daautosvr.CreateBrowser
dagroups = daautosvr.OPCGroups
dagroup = dagroups.Add("group1")
dagroup.DeadBand = 0 '设置opc服务器参数
dagroup.UpdateRate = 100
dagroup.IsActive = True
dagroup.IsSubscribed = True
daitems = dagroup.OPCItems
dabrowser.ShowBranches()
Timer1.Interval = 1000
Timer1.Start()
End Sub
Private Sub GetOpcServer(lb As ListBox) '获取OPC服务器列表
Dim getserver As OPCAutomation.OPCServer
Dim infosvr As Object
Dim i As Integer
getserver = New OPCAutomation.OPCServer
infosvr = getserver.GetOPCServers
lb.Items.Clear()
For i = LBound(infosvr) To UBound(infosvr)
lb.Items.Add(infosvr(i))
Next i
End Sub
Private Sub OpcServerConnect(daautosvr As OPCAutomation.OPCServer, lb As ListBox, lbl As Label)
Try
daautosvr.Connect(lb.SelectedItem, "")
If daautosvr.ServerState = OPCAutomation.OPCServerState.OPCRunning Then
lbl.Text = "已连接到:" + daautosvr.ServerName
Else
lbl.Text = "状态" + daautosvr.ServerState.ToString
End If
Catch
MessageBox.Show("")
End Try
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim svrname As String
svrname = daautosvr.ServerName
OpcServerDisconnect(daautosvr, dagroups, dagroup, daitems, daitem)
If daautosvr.ServerState = OPCAutomation.OPCServerState.OPCDisconnected Then
Label1.Text = "已断开连接:" + svrname
End If
End Sub
Private Sub OpcServerDisconnect(daautosvr1 As OPCAutomation.OPCServer, dagroups1 As OPCAutomation.OPCGroups,
dagroup1 As OPCAutomation.OPCGroup, daitems1 As OPCAutomation.OPCItems,
daitem1 As OPCAutomation.OPCItem)
dagroup1.IsActive = False
dagroups1.Remove(dagroup.ServerHandle)
daautosvr1.Disconnect() '断开opc服务器连接
daitem1 = Nothing
daitems1 = Nothing
dagroup1 = Nothing
dagroups1 = Nothing
daautosvr1 = Nothing
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
OpcItemRead(item(TextBox3.Text), TextBox1)
End Sub
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click '新建item
Dim itm1 As ListViewItem
itemID(newitemcount) = TextBox2.Text
item(newitemcount) = daitems.AddItem(itemID(newitemcount), newitemcount)
Label2.Text = newitemcount
itm1 = ListView1.Items.Add(itemID(newitemcount))
itm1.SubItems.AddRange({"", "", ""})
newitemcount = newitemcount + 1
itemcount = itemcount + 1
End Sub
Private Sub OpcItemRead(item As OPCAutomation.OPCItem, tb As TextBox)
Dim s, v, q, t As Object
item.Read(1, v, q, t)
tb.Text = v
End Sub
Private Sub dagroups_GlobalDataChange(TransactionID As Integer, GroupHandle As Integer, NumItems As Integer, ByRef ClientHandles As Array, ByRef ItemValues As Array, ByRef Qualities As Array, ByRef TimeStamps As Array) Handles dagroups.GlobalDataChange
For i = 1 To NumItems
itemvalue(i) = ItemValues(i)
quality(i) = Qualities(i)
tt(i) = TimeStamps(i)
Next i
For i = 1 To NumItems
ListView1.Items.Item(i - 1).SubItems(1).Text = itemvalue(NumItems + 1 - i)
ListView1.Items.Item(i - 1).SubItems(2).Text = quality(NumItems + 1 - i)
Next i
End Sub
Private Sub 添加itemToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles 添加itemToolStripMenuItem.Click
Form2.Show()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim s, v, q, t As Object
If daautosvr.ServerState = OPCAutomation.OPCServerState.OPCRunning Then
If itemcount > 0 Then
For i = 0 To newitemcount - 1
item(i).Read(1, v, q, t)
If i >= 1 Then
ListView1.Items.Item(i).SubItems(1).Text = v
ListView1.Items.Item(i).SubItems(2).Text = q
ListView1.Items.Item(i).SubItems(3).Text = t
End If
Next i
End If
End If
End Sub
End Class