C#编程之实现与西门子SIMATIC NET OPC DA通讯
白羽 2018-07-06 来源 :网络 阅读 2781 评论 0

摘要:本文将带你了解C#编程之实现与西门子SIMATIC NET OPC DA通讯,希望本文对大家学C#/.Net有所帮助。


OPC是Object Linking and Embedding(OLE)forProcess Control的缩写,它是微软公司的对象链接和嵌入技术在过程控制方面的应用。OPC以OLE/COM/DCOM技术为基础,采用客户/服务器模式,为工业自动化软件面向对象的开发提供了统一的标准,这个标准定义了应用Microsoft操作系统在基于PC的客户机之间交换自动化实时数据的方法,采用这项标准后,硬件开发商将取代软件开发商为自己的硬件产品开发统一的OPC接口程序,而软件开发者可免除开发驱动程序的工作,充分发挥自己的特长,把更多的精力投入到其核心产品的开发上。

SimaticNet是西门子全集成自动化系统中的一个重要组成部分,它为完善的工业自动化控制系统的通讯提供部件和网络,同时提供多个OPCServer,为数据的外部访问提供接口,本文主要以OPC.SimaticNET为例说明。

90年代OPC基金会开发了一系列的通讯接口比如 Data Access (DA), Alarm & Events (A&E), Historical Data Access (HDA) and Data eXchange (DX),统称传统OPC。今天主要使用的OPC DA通讯方式,这个在1995年左右还是很流行的方法,最近几年OPC Foundation又开发了新的 OPC Unified Architecture (UA) 标准,更好的适应了工业4.0。关于传统OPC和OPC UA的区别,后面会单独来说。

许多OPC服务器,包括OPC.SimaticNet,是在COM平台开发的,从而对于基于.NET框架下的C#语言,作为客户端程序语言访问OPCServer,需要解决两个平台间无缝迁移的问题。OPC基金会对会员提供了OpcRcw动态链接库,OPC NET COM 包装器和OPC NET API,将OPC复杂的规范封状成简单易用的C#类 ,可以比较容易地实现数据访问。

OPC主要包含两种接口:CUSTOM标准接口和OLE自动化标准接口,自定义接口是服务商必须提供的,而自动化接口则是可选的。
自定义接口是一组COM接口,主要用于采用C++语言的应用程序开发;
自动化接口是一组OLE接口,主要用于采用VB,DELPHI,Excel等基于脚本编程语言的应用程序开发。本文是使用C#通过自动化接口来实现的,也是最简单的方式。

首先必须了解的是OPC服务器的对象模型:

 

 

程序中涉及到的重要方法和属性比较多,解释下几个容易搞混的:

OPCItem 对象的属性ServerHandle,只读属性,服务器提供给Item的句柄,通过此句柄,Client可以定位到此Item,来对此Item进行后续的操作,比如移动删除;

OPCItem 对象的属性ClientHandle,可读可写属性,客户端分配给Item的句柄,这个句柄可以手动设置,也可由.NET随机选取的,不需要我们来设置,并且每次运行时,这
个句柄都不同,类似于TCP scoket通讯中的Client端分配的端口号。Server端必须指定端口号,Client端随机生成,每次都不一样。

OPCGroup 对象的属性的IsSubscribed,可读可写属性,Group的IsSubscribed为True,此Group才能开始接受服务器的数据属性,此Group才能被订阅。

OPCGroup 对象的事件DataChange (TransactionID As Long, NumItems As Long, ClientHandles() As Long,ItemValues() As Variant, Qualities() As Long, TimeStamps() As Date)需要注意的是NumItems参数是每次事件触发时Group中实际发生数据变化的Item的数量,而不是整个Group里的Items.

OPCGroup 对象的属性UpdateRate,可读可写属性,规定了数据刷新的周期,单位milliseconds.注意的是,不是设定多少ms,实际就是多少,比如给定53ms,OPC server会就近选择50ms.有区间划分的。

源程序如下:

 

[csharp] view plain copy

 

1. using System;  

2. using System.Collections.Generic;  

3. using System.ComponentModel;  

4. using System.Data;  

5. using System.Drawing;  

6. using System.Text;  

7. using System.Windows.Forms;  

8.   

9. using System.Net;  

10. using OPCAutomation;  

11.   

12.   

13. namespace OpcClient  

14. {  

15.     public partial class OpcClient : Form  

16.     {  

17.         public OpcClient()  

18.         {  

19.             InitializeComponent();  

20.         }  

21.  

22.         #region 私有变量  

23.         private String strHostIP;  

24.         private String strHostName;  

25.         private Boolean opc_connected;  

26.   

27.         private OPCServer LocalServer;  

28.         private OPCGroups myGroups;  

29.         private OPCGroup myGroup;  

30.         private OPCGroup myGroup1;  

31.         private OPCItems myItems;  

32.         private OPCItems myItems1;  

33.         private OPCItem myItem;  

34.         int itmHandleClient = 0;        /// 客户端句柄  

35.         int itmHandleServer = 0;        /// 服务端句柄  

36.         //**wfx  

37.         private OPCItem [] myItemArray;  

38.         private OPCItem[] myItemArray1;  

39.  

40.         #endregion  

41.  

42.         #region 私有方法  

43.         /// <summary>  

44.         /// 连接OPC服务器  

45.         /// </summary>  

46.         /// <param name="remoteServerIP">OPCServerIP</param>  

47.         /// <param name="remoteServerName">OPCServer名称</param>  

48.         private bool ConnectRemoteServer(string remoteServerIP, string remoteServerName)  

49.         {  

50.             try  

51.             {  

52.                 LocalServer.Connect(remoteServerName, remoteServerIP);  

53.   

54.                 if (LocalServer.ServerState == (int)OPCServerState.OPCRunning)  

55.                 {  

56.                     lblState.Text = "已连接到:" + "\r\n" + LocalServer.ServerName + "\r\n";  

57.                     //显示服务器信息  

58.                     lblState.Text = lblState.Text + "开始时间:" + "\r\n" + LocalServer.StartTime.ToString() + "\r\n";  

59.                     lblState.Text = lblState.Text + "版本:" + LocalServer.MajorVersion.ToString() + "." + LocalServer.MinorVersion.ToString() + "." + LocalServer.BuildNumber.ToString();  

60.                 }  

61.                 else  

62.                 {  

63.                     //这里你可以根据返回的状态来自定义显示信息,请查看自动化接口API文档  

64.                     lblState.Text = "状态:" + LocalServer.ServerState.ToString() + "\r\n";  

65.   

66.                 }  

67.             }  

68.             catch (Exception err)  

69.             {  

70.                 MessageBox.Show("连接远程服务器出现错误:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);  

71.                 return false;  

72.             }  

73.             return true;  

74.         }  

75.   

76.         /// <summary>  

77.         /// 每当项数据有变化时执行的事件  

78.         /// </summary>  

79.         /// <param name="TransactionID">处理ID</param>  

80.         /// <param name="NumItems">项个数</param>  

81.         /// <param name="ClientHandles">项客户端句柄</param>  

82.         /// <param name="ItemValues">TAG值</param>  

83.         /// <param name="Qualities">品质</param>  

84.         /// <param name="TimeStamps">时间戳</param>  

85.         void myGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)  

86.         {  

87.             //为了测试,所以加了控制台的输出,来查看事物ID号  

88.             Console.WriteLine("********"+TransactionID.ToString()+NumItems.ToString()+"*********");//第二次进来后为啥变成1了,之前都是4个  

89.             //**wfx  

90.             /*  

91.             for (int i = 1; i <= NumItems; i++)  

92.             {  

93.                 this.txtValue.Text = ItemValues.GetValue(i).ToString();  

94.                 this.txtQuality.Text = Qualities.GetValue(i).ToString();  

95.                 this.txtTime.Text = TimeStamps.GetValue(i).ToString();  

96.             }  

97.             */  

98.             //**wfx  

99.             TextBox[] tb = new TextBox[4];  

100.             tb[0] = textBox1;  

101.             tb[1] = textBox2;  

102.             tb[2] = textBox3;  

103.             tb[3] = textBox4;  

104.             //ClientHandles.GetValue(i);  

105.             for (int i = 1; i <= NumItems;i++ )  

106.             {  

107.                 tb[(int)ClientHandles.GetValue(i)-1].Text = ((float)ItemValues.GetValue(i)).ToString("0.00");  

108.             }  

109.         }  

110.   

111.         /// <summary>  

112.         /// 写入TAG值时执行的事件  

113.         /// </summary>  

114.         /// <param name="TransactionID"></param>  

115.         /// <param name="NumItems"></param>  

116.         /// <param name="ClientHandles"></param>  

117.         /// <param name="Errors"></param>  

118.         void myGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)  

119.         {  

120.             lblState.Text = "";  

121.             for (int i = 1; i <= NumItems; i++)  

122.             {  

123.                 lblState.Text += "TransactionID:" + TransactionID.ToString() + "\r\n" + "ClientHandle:" + ClientHandles.GetValue(i).ToString() + "\r\n" + "ErrorValue: " + Errors.GetValue(i).ToString() + "\r\n";  

124.             }  

125.         }  

126.   

127.         /// <summary>  

128.         /// 创建组  

129.         /// </summary>  

130.         private bool CreateGroup()  

131.         {  

132.             try  

133.             {  

134.                 myGroups = LocalServer.OPCGroups;  

135.                 myGroup = myGroups.Add("OpcDotNetGroup");  

136.                 myGroup1 = myGroups.Add("OpcDotNetGroup1");  

137.                 SetDefaultGroupProperty();  

138.                 myItems = myGroup.OPCItems;  

139.                 myItems1 = myGroup1.OPCItems;  

140.                 //**wfx  

141.                 myItemArray =new OPCItem [16];  

142.                 myItemArray1 = new OPCItem[16];  

143.                 //**add items  

144.                 //DA格式 S7:[S7 connection_1]MX3.1  

145.                 //S7:[S7 connection_1]DB1,X3.0  

146.                 //OPC UA格式 S7:S7 connection_1.db5.68,r  

147.                 //S7:S7 connection_1.db1.20,x7  

148.                 myItemArray[0] = myItems.AddItem("S7:[S7 connection_1]db5,real68", 1);//Z轴  

149.                 myItemArray[1] = myItems.AddItem("S7:[S7 connection_1]db1,real50", 2);//刮刀  

150.                 myItemArray[2] = myItems.AddItem("S7:[S7 connection_1]db1,real96", 3);//送粉  

151.                 myItemArray[3] = myItems.AddItem("S7:[S7 connection_1]db1,real72", 4);//温度  

152.                 //**wfx  

153.                 /*  

154.                 myItemArray1[4] = myItems1.AddItem("S7:[S7 connection_1]MX3.1", 4);//Z 正  

155.                 myItemArray1[5] = myItems1.AddItem("S7:[S7 connection_1]MX3.6", 5);//Z 负  

156.                 */  

157.                 myGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(myGroup_DataChange);  

158.                 myGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(myGroup_AsyncWriteComplete);  

159.                   

160.             }  

161.             catch (Exception err)  

162.             {  

163.                 MessageBox.Show("创建组出现错误:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);  

164.                 return false;  

165.             }  

166.             return true;  

167.         }  

168.   

169.         /// <summary>  

170.         /// 设置缺省的组属性  

171.         /// </summary>  

172.         private void SetDefaultGroupProperty()  

173.         {  

174.             LocalServer.OPCGroups.DefaultGroupIsActive = true ;  

175.             LocalServer.OPCGroups.DefaultGroupDeadband = 0 ;  

176.             myGroup.UpdateRate = 250 ;  

177.             myGroup.IsActive = true ;  

178.             myGroup.IsSubscribed = true ;  

179.             myGroup1.UpdateRate = 250;  

180.             myGroup1.IsActive = true;  

181.             myGroup1.IsSubscribed = true;  

182.         }  

183.  

184.  

185.         #endregion  

186.  

187.         #region 窗体事件  

188.   

189.         //窗体载入时,查询本机安装的OPC Server 列表  

190.         private void OpcClient_Load(object sender, EventArgs e)  

191.         {  

192.   

193.   

194.             //获取本地计算机IP,计算机名称  

195.             IPHostEntry IPHost = Dns.GetHostEntry(Environment.MachineName);  

196.             IPHost.HostName.ToString();  

197.             if (IPHost.AddressList.Length > 0)  

198.             {  

199.                 strHostIP = IPHost.AddressList[0].ToString();  

200.             }  

201.             else  

202.             {  

203.                 return;  

204.             }  

205.             //通过IP来获取计算机名称,可用在局域网内  

206.             IPHostEntry ipHostEntry = Dns.GetHostEntry(strHostIP);  

207.             strHostName = ipHostEntry.HostName.ToString();  

208.   

209.             //获取本地计算机上的OPCServerName  

210.             try  

211.             {  

212.                 LocalServer = new OPCServer();  

213.                 object serverList = LocalServer.GetOPCServers(strHostName);  

214.   

215.                 foreach (string turn in (Array)serverList)  

216.                 {  

217.                     cmbServer.Items.Add(turn);  

218.                 }  

219.   

220.                 cmbServer.SelectedIndex = 0;  

221.                 btnConnect.Enabled = true;  

222.             }  

223.             catch (Exception err)  

224.             {  

225.                 MessageBox.Show("枚举本地OPC服务器出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);  

226.             }  

227.   

228.         }  

229.   

230.   

231.         //点击"连接"按钮, 动作  

232.         private void btnConnect_Click(object sender, EventArgs e)  

233.         {  

234.             //连接服务器  

235.             try  

236.             {  

237.                 if (!ConnectRemoteServer("127.0.0.1", cmbServer.Text))  

238.                 {  

239.                     return;  

240.                 }  

241.   

242.                 opc_connected = true;   //已连接标记  

243.   

244.                 OPCBrowser oPCBrowser = LocalServer.CreateBrowser();  

245.                 //展开分支  

246.                 oPCBrowser.ShowBranches();  

247.                 //展开叶子  

248.                 oPCBrowser.ShowLeafs(true);  

249.                 lstItems.Items.Clear();  //清空列表  

250.                 foreach (object turn in oPCBrowser)  

251.                 {  

252.                     lstItems.Items.Add(turn.ToString());  

253.                 }  

254.   

255.                 if (!CreateGroup())  

256.                 {  

257.                     return;  

258.                 }  

259.             }  

260.             catch (Exception err)  

261.             {  

262.                 MessageBox.Show("初始化出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);  

263.             }  

264.          

265.         }  

266.   

267.   

268.   

269.   

270.         /// 选择列表中的某个Item时处理的事情  

271.         private void lstItems_SelectedIndexChanged(object sender, EventArgs e)  

272.         {  

273.             try  

274.             {  

275.                 this.txtName.Text = lstItems.SelectedItem.ToString();  

276.                 if (itmHandleClient != 0)  

277.                 {   

278.                     this.txtValue.Text = "";  

279.                     this.txtQuality.Text = "";  

280.                     this.txtTime.Text = "";  

281.   

282.                     Array Errors;  

283.                     OPCItem bItem = myItems.GetOPCItem(itmHandleServer);  

284.                     //注:OPC中以1为数组的基数  

285.                     int[] temp = new int[2] { 0, bItem.ServerHandle };  

286.                     Array serverHandle = (Array)temp;  

287.                     //移除上一次选择的项  

288.                     myItems.Remove(myItems.Count, ref serverHandle, out Errors);  

289.                 }  

290.                 itmHandleClient = 1234;  

291.                 myItem = myItems.AddItem(lstItems.SelectedItem.ToString(), itmHandleClient);  

292.                 itmHandleServer = myItem.ServerHandle;  

293.                 Console.WriteLine("*****" + itmHandleServer+"*****");//ServerHandle是随机分配,好比Socket的客户端端口号也是随机分配一样,ClientHandle是用户指定,  

294.             }  

295.             catch (Exception err)  

296.             {  

297.                 //没有任何权限的项,都是OPC服务器保留的系统项,此处可不做处理。  

298.                 itmHandleClient = 0;  

299.                 txtValue.Text = "Error ox";  

300.                 txtQuality.Text = "Error ox";  

301.                 txtTime.Text = "Error ox";  

302.                 MessageBox.Show("此项为系统保留项:" + err.Message, "提示信息");  

303.             }  

304.         }  

305.   

306.   

307.         //点击"写入"按钮  

308.         private void cmdWrite_Click(object sender, EventArgs e)  

309.         {  

310.             OPCItem bItem = myItems.GetOPCItem(itmHandleServer);  

311.             int[] temp = new int[2] { 0, bItem.ServerHandle };  

312.             Array serverHandles = (Array)temp;  

313.             object[] valueTemp = new object[2] { "", txtNewValue.Text };  

314.             Array values = (Array)valueTemp;  

315.             Array Errors;  

316.             int cancelID;  

317.             myGroup.AsyncWrite(1, ref serverHandles, ref values, out Errors, 2009, out cancelID);  

318.             //myItem.Write(txtNewValue.Text);//这句也可以写入,但并不触发写入事件  

319.             GC.Collect();  

320.         }  

321.   

322.   

323.         //点击"断开" 按钮  

324.         private void btnDisConn_Click(object sender, EventArgs e)  

325.         {  

326.             if (!opc_connected)  

327.             {  

328.                 return;  

329.             }  

330.   

331.             if (myGroup != null)  

332.             {  

333.                 myGroup.DataChange -= new DIOPCGroupEvent_DataChangeEventHandler(myGroup_DataChange);  

334.             }  

335.   

336.             if (LocalServer != null)  

337.             {  

338.                 LocalServer.Disconnect();  

339.                 //LocalServer = null;  

340.   

341.             }  

342.   

343.             opc_connected = false;  

344.             lstItems.Items.Clear();  

345.   

346.             //显示信息  

347.             lblState.Text = "已经从OPC服务器断开." + "\r\n" ;  

348.             //显示服务器信息  

349.             lblState.Text = lblState.Text + "断开时间:" + "\r\n" + System.DateTime.Now.ToString() + "\r\n";  

350.   

351.         }  

352.   

353.   

354.   

355.   

356.         //关闭窗体时候,清理工作  

357.         private void OpcClient_FormClosing(object sender, FormClosingEventArgs e)  

358.         {  

359.             btnDisConn_Click(new Object(),new EventArgs());  

360.         }  

361.  

362.         #endregion  

363.   

364.         private void button1_Click(object sender, EventArgs e)  

365.         {  

366.             myItemArray1[6] = myItems1.AddItem("S7:[S7 connection_1]db1,x0.3", 6);//刮刀正  

367.             myItemArray1[10] = myItems1.AddItem("S7:[S7 connection_1]db1,real41", 10);//刮刀速度  

368.             myItemArray1[11] = myItems1.AddItem("S7:[S7 connection_1]db1,real33", 11);//刮刀位置  

369.                   

370.             int[] temp = new int[4] { 0, myItemArray1[6].ServerHandle, myItemArray1[10].ServerHandle, myItemArray1[11].ServerHandle };  

371.             Array serverHandles = (Array)temp;  

372.             object[] valueTemp = new object[4] { "", true, textBox6.Text, textBox7.Text };  

373.             Array values = (Array)valueTemp;  

374.             Array Errors;  

375.             int cancelID;  

376.             myGroup1.AsyncWrite(3, ref serverHandles, ref values, out Errors, 2009, out cancelID);  

377.             //myItem.Write(txtNewValue.Text);//这句也可以写入,但并不触发写入事件  

378.   

379.         }  

380.   

381.         private void button2_Click(object sender, EventArgs e)  

382.         {  

383.             myItemArray1[7] = myItems1.AddItem("S7:[S7 connection_1]db1,x0.7", 7);//刮刀负  

384.             myItemArray1[10] = myItems1.AddItem("S7:[S7 connection_1]db1,real41", 10);//刮刀速度  

385.             myItemArray1[11] = myItems1.AddItem("S7:[S7 connection_1]db1,real33", 11);//刮刀位置  

386.               

387.             int[] temp = new int[4] { 0, myItemArray1[7].ServerHandle, myItemArray1[10].ServerHandle, myItemArray1[11].ServerHandle };  

388.             Array serverHandles = (Array)temp;  

389.             object[] valueTemp = new object[4] { "", true, textBox6.Text, textBox7.Text };  

390.             Array values = (Array)valueTemp;  

391.             Array Errors;  

392.             int cancelID;  

393.             myGroup1.AsyncWrite(3, ref serverHandles, ref values, out Errors, 2009, out cancelID);  

394.             //myItem.Write(txtNewValue.Text);//这句也可以写入,但并不触发写入事件  

395.         }  

396.   

397.         private void button3_Click(object sender, EventArgs e)  

398.         {  

399.             int[] temp = new int[4] { 0, myItemArray[8].ServerHandle, myItemArray[12].ServerHandle, myItemArray[13].ServerHandle };  

400.             Array serverHandles = (Array)temp;  

401.             object[] valueTemp = new object[4] { "", true, textBox8.Text, textBox9.Text };  

402.             Array values = (Array)valueTemp;  

403.             Array Errors;  

404.             int cancelID;  

405.             myGroup.AsyncWrite(3, ref serverHandles, ref values, out Errors, 2009, out cancelID);  

406.             //myItem.Write(txtNewValue.Text);//这句也可以写入,但并不触发写入事件  

407.         }  

408.   

409.         private void button4_Click(object sender, EventArgs e)  

410.         {  

411.             int[] temp = new int[4] { 0, myItemArray[9].ServerHandle, myItemArray[12].ServerHandle, myItemArray[13].ServerHandle };  

412.             Array serverHandles = (Array)temp;  

413.             object[] valueTemp = new object[4] { "", true, textBox8.Text, textBox9.Text };  

414.             Array values = (Array)valueTemp;  

415.             Array Errors;  

416.             int cancelID;  

417.             myGroup.AsyncWrite(3, ref serverHandles, ref values, out Errors, 2009, out cancelID);  

418.             //myItem.Write(txtNewValue.Text);//这句也可以写入,但并不触发写入事件  

419.         }  

420.   

421.     }  

422. }  


 

 

最后,从整体上说下OPC DA的协议规范,OPC DA是在WINDOWS的COM/DOM技术上定义的接口定义,在TCP IP七层模型的最高层应用层,决定了它必须运行在WINDOWS平台,不能够跨平台,灵活性和安全性不如OPC UA,因为OPC DA的会话层和表示层用户是有权利来使用的。对比OPC DA 和OPC UA的协议规范如下:更多不同点,后面会单独再说。

 

 
以上就介绍了C#.NET的相关知识,希望对C#.NET有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言C#.NET频道!

本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 9 不喜欢 | 1
看完这篇文章有何感觉?已经有10人表态,90%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程