最近,我们Ndolls工作室承接了山大某个自动化控制项目,主要做了一套工控信息化系统,其中有一个功能模块是将系统管理的一部分数据参数发送至OPC服务器,由OPC服务器接收数据后执行相应工控操作。第一次接触OPC的项目,有点头大,与大家分享一点经验,希望对大家有所帮助。

一、开发调试环境

1、系统环境:win7 64位

2、开发工具:Microsoft Visual Studio 2010(.Net4.0)

3、OPC组件:Interop.OPCAutomation.dll

下载地址:http://www.ndolls.net/doc/OPCAutomation.zip

4、OPC Server仿真软件:ICONICS Simulator OPC Server 3.12

下载地址(需要登陆后下载):http://www.ndolls.net/doc/ICONICSSimulatorOPCServer3.12.zip

二、注意事项

1、Interop.OPCAutomation.dll需要在系统进行注册,可以直接运行如下命令:

regsvr32 Interop.OPCAutomation.dll存放路径

如因为win7系统权限问题无法注册成功,请使用如下命令:

runas /user:administrator regsvr32 Interop.OPCAutomation.dll存放路径

2、请将项目设置成X86模式,不然Interop.OPCAutomation.dll会调用失败。

3、使用仿真软件ICONICS Simulator OPC Server需要开启Distributed Transaction Coordinator服务。

4、在调用Interop.OPCAutomation.dll过程中,如遇到返回错误码,可参考以下地址进行分析解决:

https://www.cnblogs.com/heroius/p/7401026.html

5、Interop.OPCAutomation.dll中文文档可从以下地址下载:

http://www.ndolls.net/doc/OPC_API.pdf

三、Demo界面以及代码分析(下载地址:http://www.ndolls.net/doc/OPCManage.zip)

Demo主要实现了向OPC服务器指定的分组和标签定向发送数据。

下图为demo的winform界面

下图为仿真软件界面

下面贴上代码,请注意首先需要添加引用using OPCAutomation

        #region 变量

        //OPCServer的IP
private String strHostIP;
//OPCServer的主机名
private String strHostName;
//是否和OPCServer建立连接
private Boolean opc_connected;
//建立的OPCServer对象
private OPCServer myServer;
//OPCServer节点浏览器
private OPCBrowser myOPCBrowser;
//分组集合
private OPCGroups myGroups;
//分组实例
private OPCGroup myGroup;
//分组的TAG节点
private OPCItems myItems;
//服务端句柄
int itmHandleServer = 0;
//要写入的叶子节点
private OPCItem[] myItemArray; #endregion #region 触发事件 /// <summary>
/// 每当项数据有变化时执行的事件
/// </summary>
/// <param name="TransactionID">处理ID</param>
/// <param name="NumItems">项个数</param>
/// <param name="ClientHandles">项客户端句柄</param>
/// <param name="ItemValues">TAG值</param>
/// <param name="Qualities">品质</param>
/// <param name="TimeStamps">时间戳</param>
void myGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
for (int i = 1; i <= NumItems; i++)
{
txtValue.Text = ItemValues.GetValue(i).ToString();
} //为方便测试,显示到状态栏输出
lblState.Text = "TransactionID:" + TransactionID.ToString() + "--" + "NumItems:" + NumItems.ToString();
} /// <summary>
/// 写入TAG值时执行的事件
/// </summary>
/// <param name="TransactionID">处理ID</param>
/// <param name="NumItems">项个数</param>
/// <param name="ClientHandles">项客户端句柄</param>
/// <param name="Errors">服务器返回的错误信息</param>
void myGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
{
//为方便测试,显示到状态栏输出
for (int i = 1; i <= NumItems; i++)
{
lblState.Text = "TransactionID:" + TransactionID.ToString() + "--" + "ClientHandle:" + ClientHandles.GetValue(i).ToString() + "--" + "ErrorValue: " + Errors.GetValue(i).ToString();
}
} #endregion #region 按钮事件 /// <summary>
/// 连接服务器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnConIP_Click(object sender, EventArgs e)
{
//根据输入获取OPC服务器IP地址
strHostIP = txtIP.Text; //通过IP来获取OPC服务器主机名
IPHostEntry ipHostEntry = Dns.GetHostEntry(strHostIP);
strHostName = ipHostEntry.HostName.ToString(); try
{
//实例化OPC服务
myServer = new OPCServer(); //获取OPCServer列表
object serverList = myServer.GetOPCServers(strHostName); //将OPCServer展示到ComboBox
foreach (string turn in (Array)serverList)
{
cmbServer.Items.Add(turn);
}
cmbServer.SelectedIndex = 0; //开启OPC连接按钮
btnOPC.Enabled = true;
}
catch (Exception err)
{
MessageBox.Show("枚举OPC服务出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
} /// <summary>
/// 连接OPC服务器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnOPC_Click(object sender, EventArgs e)
{
try
{
//根据选择的OPCServer进行连接
myServer.Connect(cmbServer.Text, strHostIP); //根据连接成功与否输出状态信息
if (myServer.ServerState == (int)OPCServerState.OPCRunning)
{
lblState.Text = "已连接到:" + myServer.ServerName;
//显示服务器信息
lblState.Text += "----开始时间:" + myServer.StartTime.ToString();
lblState.Text += "----版本:" + myServer.MajorVersion.ToString() + "." + myServer.MinorVersion.ToString() + "." + myServer.BuildNumber.ToString();
}
else
{
lblState.Text = "状态:" + myServer.ServerState.ToString();
} //已连接标记
opc_connected = true; //开启获取标签按钮
btnGetGrps.Enabled = true; MessageBox.Show("链接OPC成功");
}
catch (Exception err)
{
MessageBox.Show("初始化出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
} /// <summary>
/// 获取OPC服务器所有分组和标签
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnGetGrps_Click(object sender, EventArgs e)
{
//实例化Tag浏览器
myOPCBrowser = myServer.CreateBrowser(); //展开分组
myOPCBrowser.ShowBranches(); //展开标签
myOPCBrowser.ShowLeafs(true); //将所有分支和叶子节点显示到ListBox
lstItems.Items.Clear();
foreach (object turn in myOPCBrowser)
{
lstItems.Items.Add(turn.ToString());
} //开启定位标签按钮
btnSetItem.Enabled = true;
} /// <summary>
/// 根据列表中选中的当前标签,定位到需要发送数据的标签
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSetItem_Click(object sender, EventArgs e)
{
try
{
/**
* 需要注意,不同的OPC服务器的标签格式也是不同的
* 测试时,使用的是ICONICS Simulator OPC Server,标签格式如:Textual.Memory
* 生产环境时,使用的是SimaticNet_V13Sp1,标签格式如:S7:[S7_Connection_1]MReal120
* **/ //根据ListBox选中的标签,处理得到分组名称
string groupName = lstItems.Text; //实例化组
myGroups = myServer.OPCGroups;
myGroup = myGroups.Add(groupName); //设置缺省的组属性
myServer.OPCGroups.DefaultGroupIsActive = true;
myServer.OPCGroups.DefaultGroupDeadband = 0;
myGroup.UpdateRate = 250;
myGroup.IsActive = true;
myGroup.IsSubscribed = true; //定位需要发送数据的目标项
myItems = myGroup.OPCItems; //实例化组内标签
myItemArray = new OPCItem[1]; //填充项目组
myItemArray[0] = myItems.AddItem(lstItems.Text, 1); //获取服务端句柄
itmHandleServer = myItemArray[0].ServerHandle; //监听组内数据变化
myGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(myGroup_DataChange);
myGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(myGroup_AsyncWriteComplete); //开启发送参数按钮
btnWrite.Enabled = true;
}
catch (Exception err)
{
MessageBox.Show("创建组出现错误:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
} /// <summary>
/// 写入值
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnWrite_Click(object sender, EventArgs e)
{
//获取之前定位的标签
OPCItem bItem = myItems.GetOPCItem(itmHandleServer); //根据用户界面输入生成数据对象
int[] temp = new int[2] { 0, bItem.ServerHandle };
Array serverHandles = (Array)temp;
object[] valueTemp = new object[2] { "", txtMyValue.Text };
Array values = (Array)valueTemp;
Array Errors;
int cancelID; //异步写入到OPC服务器
myGroup.AsyncWrite(1, ref serverHandles, ref values, out Errors, 2009, out cancelID); //回收资源
GC.Collect();
} /// <summary>
/// 关闭窗体时,关闭链接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OPCManage_FormClosing(object sender, FormClosingEventArgs e)
{
if (!opc_connected)
{
return;
} if (myGroup != null)
{
myGroup.DataChange -= new DIOPCGroupEvent_DataChangeEventHandler(myGroup_DataChange);
} if (myServer != null)
{
myServer.Disconnect();
} opc_connected = false;
} #endregion

四、生产环境

真实的生产环境中,工控终端采用西门子数控系统(SINUMERIK 808),OPC服务器采用SimaticNet_V13Sp1。由工控人员预先在OPC服务的S7:[S7_Connection_1]M分组下,创建MReal120—MReal140共6个寄存节点,用于接收软件写入的工艺参数。本系统用户根据需要从我们的软件中检索工艺参数,并定向写入到OPC服务器的S7:[S7_Connection_1]M节点下,由数控人员控制数控系统从OPC服务器获取到最新参数进行生产。

C#.Net平台与OPC服务器通讯的更多相关文章

  1. OPC协议解析-OPC客户端与服务器通讯解析

    1      OPC服务器 OPC服务器, 是指按照OPC基金组织规定的OPC规范群开发的软件驱动.OPC服务器作为中间媒介负责从数据源读取数据再跟另外一端的客户端通信.在 OPC客户端/服务器 的结 ...

  2. 基于第三方开源库的OPC服务器开发指南(2)——LightOPC的编译及部署

    前文已经说过,OPC基于微软的DCOM技术,所以开发OPC服务器我们要做的事情就是开发一个基于DCOM的EXE文件.一个代理/存根文件,然后就是写一个OPC客户端测试一下我们的服务器了.对于第一项工作 ...

  3. Linux 下 简单客户端服务器通讯模型(TCP)

    原文:Linux 下 简单客户端服务器通讯模型(TCP) 服务器端:server.c #include<stdio.h> #include<stdlib.h> #include ...

  4. 学习STM32F769DK-OTA例程之百度云平台建立MQTT服务器

    @2019-04-17 [小记] 百度云平台建立MQTT服务器时需要设置权限组,否则连接失败

  5. 无法连接到 OPC服务器[无此类接口支持(异常来自HRESULT:0x80004002(E_NOINTERFACE))]

    来源:https://stackoverrun.com/cn/q/10400663 无法连接到ABB Freelance OPC服务器[无此类接口支持(异常来自HRESULT:0x80004002(E ...

  6. OPC服务器开发浅谈 — 服务器模型(转)

    这里主要讨论的是OPC Data Access 2.0服务器的开发,在掌握了这个最常用的OPC服务器开发之后,对其它类型的OPC服务器,如A&E.HDA等就可以触类旁通了. 一个OPC服务器的 ...

  7. Android 通过Socket 和服务器通讯

    Extends:(http://www.cnblogs.com/likwo/p/3641135.html) Android 通过Socket 和服务器通讯,是一种比较常用的通讯方式,时间比较紧,说下大 ...

  8. 微信公众平台—— 获取微信服务器IP地址

    微信公众平台—— 获取微信服务器IP地址 const ServerIpUrl = 'https://api.weixin.qq.com/cgi-bin/getcallbackip?&acces ...

  9. Rabbitmq消息服务器通讯异常: name must not be blank

    前人挖坑,后人填! 倒霉的遇到一个破项目,该 项目使用了 RabbitMQ 消息队列向服务器发送消息, 但在发送中老是报 RabbitMQ 服务器异常! 呃,查看了服务器,服务器好好的,日志中却是这样 ...

随机推荐

  1. leetcode1032

    class StreamChecker: def __init__(self, words: 'List[str]'): self.maxLen = 0 self.List = set(words) ...

  2. GIL计算python 2 和 python 3 计算密集型

    首先我画了一张图来表示GIL运行的方式: Python 3执行如下计算代码:#-*-conding:utf-8-*-import threading import timedef add(): n = ...

  3. 【浅说】堆(heap)和栈(stack)区别

    在了解堆与栈之前,我们想来了解下程序的内存分配 一个编译的程序占用的内存分为以下几个部分  :  1.栈区(stack)—   由编译器自动分配释放   ,存放函数的参数值,局部变量的值等.其操作方式 ...

  4. ODPS SQL <for 数据操作语言DML>

    基本操作: 查询: SELECT [ALL | DISTINCT] select_expr, select_expr, ... FROM table_reference [WHERE where_co ...

  5. mysql创建索引以及对索引的理解

    创建表的时候创建索引   创建索引是指在某个表的一列或多列上建立一个索引,以便提高对表的访问速度.创建索引有3种方式,这3种方式分别是创建表的时候创建索引.在已经存在的表上创建索引和使用ALTER T ...

  6. linux之重定向命令

    1.shell重定向概念:shell重定向包含输出重定向和输入重定向 何为输入输出方向?何为标准输入输出方向? 标准输入方向:从键盘读取用户输入的数据,然后再把数据拿到程序(C语言程序.Shell 脚 ...

  7. 三、putty工具常见设置

    转载自:https://www.cnblogs.com/hdk1993/p/4769072.html Putty是一个免费小巧的Win32平台下的telnet,rlogin和ssh客户端. 它的主程序 ...

  8. VMWare 14.1 15 Pro 安装 macOS Mojave 10.14.1系统 遇到的问题解决方案

    安装环境 WIN10VMware Workstation Pro 15.0.0 Build 10134415工具准备1.VMware Workstation Pro 15.0.0 Build 1013 ...

  9. springmvc mybatis shiro构建cms系统

    开发语言: java.ios.android 部署平台: linux.window jdk版本:JDK1.7以上版本 开发工具: eclipse.idea等 服务器中间件:Tomcat 6.7.Jbo ...

  10. zeromq学习记录(一)最初的简单示例使用ZMQ_REQ ZMQ_REP

    阅读zeromq guide的一些学习记录 zeromq官方例子 在VC下运行会有些跨平台的错误 我这里有做修改 稍后会发布出来 相关的代码与库  http://download.zeromq.org ...