C#.Net平台与OPC服务器通讯
最近,我们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服务器通讯的更多相关文章
- OPC协议解析-OPC客户端与服务器通讯解析
1 OPC服务器 OPC服务器, 是指按照OPC基金组织规定的OPC规范群开发的软件驱动.OPC服务器作为中间媒介负责从数据源读取数据再跟另外一端的客户端通信.在 OPC客户端/服务器 的结 ...
- 基于第三方开源库的OPC服务器开发指南(2)——LightOPC的编译及部署
前文已经说过,OPC基于微软的DCOM技术,所以开发OPC服务器我们要做的事情就是开发一个基于DCOM的EXE文件.一个代理/存根文件,然后就是写一个OPC客户端测试一下我们的服务器了.对于第一项工作 ...
- Linux 下 简单客户端服务器通讯模型(TCP)
原文:Linux 下 简单客户端服务器通讯模型(TCP) 服务器端:server.c #include<stdio.h> #include<stdlib.h> #include ...
- 学习STM32F769DK-OTA例程之百度云平台建立MQTT服务器
@2019-04-17 [小记] 百度云平台建立MQTT服务器时需要设置权限组,否则连接失败
- 无法连接到 OPC服务器[无此类接口支持(异常来自HRESULT:0x80004002(E_NOINTERFACE))]
来源:https://stackoverrun.com/cn/q/10400663 无法连接到ABB Freelance OPC服务器[无此类接口支持(异常来自HRESULT:0x80004002(E ...
- OPC服务器开发浅谈 — 服务器模型(转)
这里主要讨论的是OPC Data Access 2.0服务器的开发,在掌握了这个最常用的OPC服务器开发之后,对其它类型的OPC服务器,如A&E.HDA等就可以触类旁通了. 一个OPC服务器的 ...
- Android 通过Socket 和服务器通讯
Extends:(http://www.cnblogs.com/likwo/p/3641135.html) Android 通过Socket 和服务器通讯,是一种比较常用的通讯方式,时间比较紧,说下大 ...
- 微信公众平台—— 获取微信服务器IP地址
微信公众平台—— 获取微信服务器IP地址 const ServerIpUrl = 'https://api.weixin.qq.com/cgi-bin/getcallbackip?&acces ...
- Rabbitmq消息服务器通讯异常: name must not be blank
前人挖坑,后人填! 倒霉的遇到一个破项目,该 项目使用了 RabbitMQ 消息队列向服务器发送消息, 但在发送中老是报 RabbitMQ 服务器异常! 呃,查看了服务器,服务器好好的,日志中却是这样 ...
随机推荐
- mysql简单介绍及安装
MySQL是一个关系型数据库管理系统关系数据库,将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性,所使用的 SQL 语言是用于访问数据库的最常用标准化语言.My ...
- 如何在 ajax 外拿到 ajax 的数据???和ajax的参数
第一步: var 变量名 = $.ajax({ url: "发送请求的地址", dataType: 'json', type: 'post', async: false }) 第 ...
- Linux工作中常用命令
1. 搜索 在vi和vim中如果打开一个很大的文件,不容易找到对应的内容,可以使用自带的搜索关键字进行搜索定位: 在vi和vim界面中输入:"/"(反斜杠),之后会出现一个输入框让 ...
- equals的使用
源码:这里只是把Integer拿出来,String,Long 都一样 /** * Compares this object to the specified object. The result is ...
- vue项目如何通过前端实现自动识别并配置服务器环境地址
前言: 一般来说,一个web项目的生产环境和测试环境的服务器地址一旦确定下来,很少会频繁变动的.那么就可以单独写一个脚本文件,通过当前访问的域名来判断当前的访问环境,然后再通过一定的规则获取对应的服务 ...
- 无监督学习算法-Apriori进行关联分析
关联分析 是无监督讯息算法中的一种,Apriori主要用来做_关联分析_,_关联分析_可以有两种形式:频繁项集或者关联规则.举个例子:交易订单 序号 商品名称 1 书籍,电脑 2 杯子,手机,手机壳, ...
- Python基础-python数据类型(四)
python数据类型 在python中,变量就是变量,它没有类型,我们所说的类型是变量所指的内存中对象的类型. python中的数据类型: 1.数字 python中没有专门定义常量的方式,通常使用大写 ...
- XPath简介及节点
XPath是一门在XML文档中查找信息的语言. XPath可用来在XML文档中对元素和属性进行遍历. XPath使用路径表达式在XML文档中进行导航. XPath路径表达式:XPath使用路径表达式来 ...
- 651. 4 Keys Keyboard复制粘贴获得的最大长度
[抄题]: Imagine you have a special keyboard with the following keys: Key 1: (A): Print one 'A' on scre ...
- 796. Rotate String旋转字符串
[抄题]: We are given two strings, A and B. A shift on A consists of taking string A and moving the lef ...