折腾了一段时间研究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数据的访问的更多相关文章

  1. C# - VS2019 通过DataGridView实现对Oracle数据表的增删改查

    前言 通过VS2019建立WinFrm应用程序,搭建桌面程序后,通过封装数据库操作OracleHelper类和业务逻辑操作OracleSQL类,进而通过DataGridView实现对Oracle数据表 ...

  2. 使用JWT来实现对API的授权访问

    目录 什么是JWT JWT的结构 Header Payload Signature 解码后的JWT JWT是怎样工作的 在JAVA里使用JWT 引入依赖 JWT Service 生成JWT 解码JWT ...

  3. SILVERLIGHT实现对HTML DOM的访问

    实现对HTML DOM的访问.Silverlight 2在命名空间System.Windows.Browser下内置了很多对于HTML DOM访问和操作的支持,我们最常用的一个对象是HtmlEleme ...

  4. js实现对json数据的序列化(兼容ie6以上浏览器)

    /** * 增加对JSON数据的序列化方法, * 主要用于IE6.7不支持JSON对象的浏览器 */ var xue = xue || {};xue.json = xue.json || {}; xu ...

  5. python实现对excel数据进行修改/添加

    import osimport xlrdfrom xlutils.copy import copydef base_dir(filename=None): return os.path.join(os ...

  6. C++实现对Json数据的友好处理

    背景 C/C++客户端需要接收和发送JSON格式的数据到后端以实现通讯和数据交互.C++没有现成的处理JSON格式数据的接口,直接引用第三方库还是避免不了拆解拼接.考虑到此项目将会有大量JSON数据需 ...

  7. SQLite帮助类SQlitehelper 实现对SQLite数据的增删改查

    public class SQLiteHelper { public const string sConn = "Data Source=" + @"path" ...

  8. Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问

    本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...

  9. 写出java8实现对List<User>中的username字段过滤出不等于张三的数据

    写出java8实现对List<User>中的username字段过滤出不等于张三的数据... 对...这个是一道面试题.当时没有看过java8的新特性...所以有点懵. 看完之后感觉 真. ...

随机推荐

  1. levelDB SSTable-1

    创建sstable文件 了解了sstable文件的存储格式,以及Data Block的组织,下面就可以分析如何创建sstable文件了.相关代码在table_builder.h/.cc以及block_ ...

  2. [Python3 填坑] 001 格式化符号 & 格式化操作符的辅助指令

    目录 1. print( 坑的信息 ) 2. 开始填坑 2.1 Python 格式化符号表 举例说明 (1) %c (2) %s 与 %d (3) %o (4) %x (5) %f (6) %e (7 ...

  3. [常用类]String 类

    String 字符串是常量,一旦被赋值,就不能被更改. String str = “abc”: // "abc" 可以堪称是一个字符串对象 str = “def“: // 当把 & ...

  4. csrf的中间件

    csrf的中间件 源码简略分析: def process_request(self, request): # 从cookies中获取csrf_token csrf_token = self._get_ ...

  5. 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 ...

  6. HDU_2007

    /** *注意:输入的两个数字的大小并不确定 */ #include <iostream> #include <stdio.h> #include <string.h&g ...

  7. Cannot change column 'id': used in a foreign key constraint

    原因:为表添加自增长,但由于该表有外键而报错 发现是因为外键的影响,不能随便的更改表结构. 要想更改表结构,首先要把基层的表修改了. A表 作为B表的外键,A表不能随便修改. B表 有A表的外键,必须 ...

  8. vue.js(19)--vue中子组件调用父组件的方法

    子组件是不能直接使用父组件中的方法的,需要进行事件绑定(v-on:自定义方法名="父组件方法名"),然后在子组件中再定义一个方法,使用this.$emit('自定义方法名')语句完 ...

  9. tf.clip_by_global_norm

    首先明白这个事干嘛的,在我们做求导的时候,会遇到一种情况,求导函数突然变得特别陡峭,是不是意味着下一步的进行会远远高于正常值,这个函数的意义在于,在突然变得陡峭的求导函数中,加上一些判定,如果过于陡峭 ...

  10. Nginx优化_自定义报错页面

    自定义返回给客户端的404错误页面 1. 优化前,客户端使用浏览器访问不存在的页面,会提示404文件未找到 client]# firefox http://192.168.4.5/xxxxx      ...