【OPCAutomation】 使用OPCAutomation实现对OPC数据的访问
折腾了一段时间研究OPC,理清了下位机、OPCServer 和OPCClient的关系和通信模型,终于能够来写一篇相关的博客了。
我们使用西门子的 S7 200 SMART作为下位机端,通过3G路由器从vpn与公司服务器通信,服务器运行配置好的PC Access SMART 作为OPC Server, 完成对下位机内存地址的定义后,使用自动化接口开发中间件负责将OPC Server得到的PLC数据存放至SQL Server
中间件和数据库的设计思路是:
数据库按真实设备类型分别建表用作存储,数据库建有数据字典表用作配置功能,包括配置内存地址和opc服务等。
中间件作用是调用OPCAutomation 类访问OPCServer端,并进行可控制读取间隔的OPC数据读取、存储工作。
由于第一次接触这类开发,在设计功能时花费了很多精力,从完全不懂到基本理解这种通信模式和原理,也绕了不少远路。
本文主要介绍OPCAutomation类的使用。
简单说下流程就是:
1. 创建OPC Server的连接
2. 创建OPC组对象并初始化设置
3. 获取组的OPCItems对象,为读取数据作准备
4. opcItem的操作。
5. 退出程序的资源释放
创建连接很简单,需要指定OPCServer所在的服务器(内网可以指定ip或者计算机名),指定OPC服务的名称(同一服务器可能运行多个OPC服务以适配同的下位机)
private bool ConnectRemoteServer(string remoteServerIP, string remoteServerName)
{
try
{
this.opcServer.Connect(remoteServerName, remoteServerIP);
string status;
if (opcServer.ServerState == (int)OPCServerState.OPCRunning)
{
status = "已连接到-" + opcServer.ServerName + " ";
}
else
{
//这里你可以根据返回的状态来自定义显示信息,请查看自动化接口API文档
status = "状态:" + opcServer.ServerState.ToString() + " ";
}
Console.WriteLine(status);
}
catch (Exception err)
{
Console.WriteLine("连接远程服务器出现错误:" + err.Message, "提示信息");
return false;
}
return true;
}
其中 this.opcServer.Connect(remoteServerName, remoteServerIP);即OPCAutomation类提供的连接方法。
需要注意的是,在实际配置时,需要完全对OPC 服务端所在服务器上配置防火墙出入站规则后,OPC服务才能够被其他服务器上的中间件访问到。故我们选择最简单的方式,在本机运行中间件。
创建组相当于读取到OPC上特定的项目,而具体的数据值是在每个项目下根据开发人员的定义而确定。
opcGroups = opcServer.OPCGroups;
opcGroup = opcGroups.Add("OPCDOTNETGROUP");
SetGroupProperty();
opcGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(opcGroup_DataChange); //opcGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(KepGroup_AsyncWriteComplete); opcItems = opcGroup.OPCItems;
注释的代码是绑定写操作的事件。
这段代码是为OPCGroup对象进行初始化。 比较关键的是两句绑定事件的,第一个绑定的是每当OPC数据有变化时触发的事件。
数据变化的时间是由以下代码中UpdateRate控制。
private void SetGroupProperty()
{
opcServer.OPCGroups.DefaultGroupIsActive = true;
opcServer.OPCGroups.DefaultGroupDeadband = ;
opcGroup.UpdateRate = this.updateRate;
opcGroup.IsActive = true;
opcGroup.IsSubscribed = true;
}
关于逐项配置的具体说明,建议参考OPCAutomation的api,如果有看到翻译比较合适的后续会补充上来。
之后是为全局变量里的OPCGroup对象添加Items,在OPC中,每个opcItem会被分配一个客户端句柄值,同时会被配置上服务端句柄。几乎所有有关获取指定OpcItem对象的方法均要求指定客户端句柄值。
void AddAllOpcItem()
{
opcBrowser.ShowBranches();
opcBrowser.ShowLeafs(true);
int count = this.opcBrowser.Count;
if (this.opcItemsArray == null)
{
opcItemsArray = new List<string>();
}
foreach (var item in opcBrowser)
{
opcItemsArray.Add(item.ToString());
}
AddOpcItems();
}
//逐项绑定句柄值
void AddOpcItems()
{
foreach (var item in this.opcItemsArray)
{
itmHandleClient = ;
opcItem = opcItems.AddItem(item, itmHandleClient);
itmHandleServer = opcItem.ServerHandle;
}
}
这段代码是为了把获取到的全部OPCItem添加到所创建的OPCGroup对象的opcItem集合中,以便我们所绑定的DataChange事件能够被触发并且正确对应到每个地址值上。
基本上到此,只要在所绑定的DataChange的实现代码中完成具体的数据读取业务,中间件的核心部分已经完成。
稍微提一下,OPC的数据项主要包含:名称、条目id、地址、数据类型、数据值、工程单位上下限、时间戳和质量几项,在实际业务中我们关注条目ID、数据类型、值、时间戳和质量,对于读取的opc数据值而言,需要注意的是得到值是Dynamic类型的,需要正确进行判断和转换。质量返回值为0时基本可以认为下位机到服务端的数据链断了,或者下位机对应项目没有收到数据,据此可进行日志和提示的业务操作。
在OPC的数据类型中,REAL对应float, WORD对应16位整型,BOOL可以用bit或者Bool进行转换,用bit和sql server的数据项可以比较方便对接。
第一次做这类开发,谬误难免,希望多多指点。
感谢阅读。
【OPCAutomation】 使用OPCAutomation实现对OPC数据的访问的更多相关文章
- C# - VS2019 通过DataGridView实现对Oracle数据表的增删改查
前言 通过VS2019建立WinFrm应用程序,搭建桌面程序后,通过封装数据库操作OracleHelper类和业务逻辑操作OracleSQL类,进而通过DataGridView实现对Oracle数据表 ...
- 使用JWT来实现对API的授权访问
目录 什么是JWT JWT的结构 Header Payload Signature 解码后的JWT JWT是怎样工作的 在JAVA里使用JWT 引入依赖 JWT Service 生成JWT 解码JWT ...
- SILVERLIGHT实现对HTML DOM的访问
实现对HTML DOM的访问.Silverlight 2在命名空间System.Windows.Browser下内置了很多对于HTML DOM访问和操作的支持,我们最常用的一个对象是HtmlEleme ...
- js实现对json数据的序列化(兼容ie6以上浏览器)
/** * 增加对JSON数据的序列化方法, * 主要用于IE6.7不支持JSON对象的浏览器 */ var xue = xue || {};xue.json = xue.json || {}; xu ...
- python实现对excel数据进行修改/添加
import osimport xlrdfrom xlutils.copy import copydef base_dir(filename=None): return os.path.join(os ...
- C++实现对Json数据的友好处理
背景 C/C++客户端需要接收和发送JSON格式的数据到后端以实现通讯和数据交互.C++没有现成的处理JSON格式数据的接口,直接引用第三方库还是避免不了拆解拼接.考虑到此项目将会有大量JSON数据需 ...
- SQLite帮助类SQlitehelper 实现对SQLite数据的增删改查
public class SQLiteHelper { public const string sConn = "Data Source=" + @"path" ...
- Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问
本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...
- 写出java8实现对List<User>中的username字段过滤出不等于张三的数据
写出java8实现对List<User>中的username字段过滤出不等于张三的数据... 对...这个是一道面试题.当时没有看过java8的新特性...所以有点懵. 看完之后感觉 真. ...
随机推荐
- levelDB SSTable-1
创建sstable文件 了解了sstable文件的存储格式,以及Data Block的组织,下面就可以分析如何创建sstable文件了.相关代码在table_builder.h/.cc以及block_ ...
- [Python3 填坑] 001 格式化符号 & 格式化操作符的辅助指令
目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 Python 格式化符号表 举例说明 (1) %c (2) %s 与 %d (3) %o (4) %x (5) %f (6) %e (7 ...
- [常用类]String 类
String 字符串是常量,一旦被赋值,就不能被更改. String str = “abc”: // "abc" 可以堪称是一个字符串对象 str = “def“: // 当把 & ...
- csrf的中间件
csrf的中间件 源码简略分析: def process_request(self, request): # 从cookies中获取csrf_token csrf_token = self._get_ ...
- Gradle打包问题Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0
前言 使用gradle打包react native的时候,出现了如下报错,下面和大家说一下解决的具体办法 Deprecated Gradle features were used in this bu ...
- HDU_2007
/** *注意:输入的两个数字的大小并不确定 */ #include <iostream> #include <stdio.h> #include <string.h&g ...
- Cannot change column 'id': used in a foreign key constraint
原因:为表添加自增长,但由于该表有外键而报错 发现是因为外键的影响,不能随便的更改表结构. 要想更改表结构,首先要把基层的表修改了. A表 作为B表的外键,A表不能随便修改. B表 有A表的外键,必须 ...
- vue.js(19)--vue中子组件调用父组件的方法
子组件是不能直接使用父组件中的方法的,需要进行事件绑定(v-on:自定义方法名="父组件方法名"),然后在子组件中再定义一个方法,使用this.$emit('自定义方法名')语句完 ...
- tf.clip_by_global_norm
首先明白这个事干嘛的,在我们做求导的时候,会遇到一种情况,求导函数突然变得特别陡峭,是不是意味着下一步的进行会远远高于正常值,这个函数的意义在于,在突然变得陡峭的求导函数中,加上一些判定,如果过于陡峭 ...
- Nginx优化_自定义报错页面
自定义返回给客户端的404错误页面 1. 优化前,客户端使用浏览器访问不存在的页面,会提示404文件未找到 client]# firefox http://192.168.4.5/xxxxx ...