紫外工控论坛

 找回密码
 立即注册

扫一扫,访问微社区

QQ登录

只需一步,快速开始

搜索
查看: 3838|回复: 0

[OPC技术] 运用VC#编程通过OPC方式实现PC机与西门子PLC通讯--同步篇(2)

[复制链接]
冰糖 发表于 2010-12-26 14:52:14 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
3、 OPC编程

<1>、西门子的变量结构如下:

----------------------服务器------------------------------

/ OPC.SimaticNet OPCServer.Wincc .... (一系列类型的服务器)

/ Group1 Group2 Group3 ...(把更新时间一致的变量统一为一个组)

/ Item1 Item2 ... (变量:I、Q、M、DB等,指向网络中某个PC站OPC Server服务的某个连接)

-----------------------------------------------------------------------------------------------------------------

  第一层是不同种类的服务器,如:OPC.SimaticNET类型,OPC.SimaticNET.DP类型,OPCServer.WinCC等一系列类型,这里选择OPC.SimaticNET类型。

第二层是Group,一个服务器下可以有多个组,可以把组理解为扫描周期相同的一系列变量的集合。在开发组态界面时,可以把一个界面中的所有变量统一到一个组中。

第三层是Item,项是指向网络中某个PC站OPC Server服务的某个连接的一系列变量,如:I、Q、M、DB等

<2>、项的命名

  项即Item,在S7连接中针对的直接是PLC中的变量,因此它的命名很重要:

  格式: :[]

其中的protocolID表示连接类型,在上面的组态PC站时可以选择,这里应该与它一致,类型有9种,最常用的为S7,即S7连接,其他类型请参看文档。

  Connectionname:顾名思义,即在上面的组态PC站时产生的连接名,如果使用仿真功能,连接名为DEMO Variablename:变量名有一系列规则,这里举例说明,读者也可以使用OPC Scout创建变量,学习程序是如何生成变量名的。

S7:[DEMO]MB1 :表示连接类型为S7,连接名为DEMO(这里为仿真),变量为MB1

S7:[DEMO]QB0,3: 表示为从QB0开始的三个连续变量。

S7:[DEMO]DB10,X4.6 :表示DB10的DBX4.6。

<3>、添加引用

  在VC#开发环境中添加对OpcRcw.Da库的引用引用,该库属于.NET库,不属于COM库,西门子虽然编写了类库,以提供对.NET平台的支持,但这些类库仍然难于编程,

  里面包含了大量的在托管和非托管区传输数据,因此我们需要在它的基础上再开发一个类库,以简化以后的编程,首先在类的开头使用命名空间:

using System.Runtime.InteropServices;

using OpcRcw.Da;

using System.Collections;

<4>、编程

1、 在类的开头部分生名变量

private string serverType="";

private IOPCServer pIOPCServer; // OPC server接口

private Object pobjGroup1; // Pointer to group object

private int nSvrGroupID; // server group handle for the added group

private System.Collections.Hashtable groupsID=new Hashtable(11); //用于记录组名和组ID号

private System.Collections.Hashtable hitemsID=new Hashtable(17); //用于记录项名和项ID号

private Guid iidRequiredInterface;

private int hClientGroup = 0; //客户组号

private int hClientItem=0; //Item号

2、 创建服务器,编写Open()方法

/// 创建一个OPC Server接口

///

/// 返回错误信息

/// 若为true,创建成功,否则创建失败

public bool Open(out string error)

{

error="";bool success=true;

Type svrComponenttyp ;

//获取 OPC Server COM 接口

iidRequiredInterface = typeof(IOPCItemMgt).GUID;

svrComponenttyp = System.Type.GetTypeFromProgID(serverType);

try

{

//创建接口

pIOPCServer =(IOPCServer)System.Activator.CreateInstance(svrComponenttyp);

error="";

}

catch (System.Exception err) //捕捉失败信息

{

error="错误信息:"+err.Message;success=false;

}

Return true;

}

3、 在服务器上添加用于添加Group的函数

/// 添加组

/// 组名

/// /创建时,组是否被激活

/// //组的刷新频率,以ms为单位

/// 返回错误信息

/// 若为true,添加成功,否则添加失败

public bool AddGroup(string groupName,int bActive,int updateRate,out string error)

{

error="";

int dwLCID = 0x407; //本地语言为英语

int pRevUpdateRate;

float deadband = 0;

// 处理非托管COM内存

GCHandle hDeadband;

IntPtr pTimeBias = IntPtr.Zero;

hDeadband = GCHandle.Alloc(deadband,GCHandleType.Pinned);

try

{

pIOPCServer.AddGroup(groupName, //组名

bActive, //创建时,组是否被激活

updateRate, //组的刷新频率,以ms为单位

hClientGroup, //客户号

pTimeBias, //这里不使用

(IntPtr)hDeadband,

dwLCID, //本地语言

out nSvrGroupID, //移去组时,用到的组ID号

out pRevUpdateRate, //返回组中的变量改变时的最短通知时间间隔

ref iidRequiredInterface,

out pobjGroup1); //指向要求的接口

hClientGroup=hClientGroup+1;

int groupID=nSvrGroupID;

groupsID.Add(groupName,groupID);

}

catch (System.Exception err) //捕捉失败信息

{

error="错误信息:"+err.Message;

}

finally

{

if (hDeadband.IsAllocated) hDeadband.Free();

}

if(error=="")

return true;

else

return false;

}

4、 向指定的组中添加变量的函数

/// 添加多个项到组

///

/// 指定组名

/// 指定项名

/// 由函数返回的服务器确定的项ID号

/// 无错误,返回true,否则返回false

public bool AddItems(string groupName,string[] itemsName,int[] itemsID)

{

bool success=true;

OPCITEMDEF[] ItemDefArray=new OPCITEMDEF[itemsName.Length];

for(int i=0;i<ITEMSNAME.LENGTH;I++)

{

hClientItem=hClientItem+1;

ItemDefArray[i].szAccessPath = ""; // 可选的通道路径,对于Simatiic Net不需要。

ItemDefArray[i].szItemID = itemsName[i]; // ItemID, see above

ItemDefArray[i].bActive = 1; // item is active

ItemDefArray[i].hClient = hClientItem; // client handle

ItemDefArray[i].dwBlobSize = 0; // blob size

ItemDefArray[i].pBlob = IntPtr.Zero; // pointer to blob

ItemDefArray[i].vtRequestedDataType = 2; //Word数据类型

}

//初始化输出参数

IntPtr pResults = IntPtr.Zero;

IntPtr pErrors = IntPtr.Zero;

try

{

// 添加项到组

((IOPCItemMgt)GetGroupByName(groupName)).AddItems(itemsName.Length,ItemDefArray,out pResults,out pErrors);

// Unmarshal to get the server handles out fom the m_pItemResult

// after checking the errors

int[] errors = new int[itemsName.Length];

Marshal.Copy(pErrors, errors, 0,itemsName.Length);

IntPtr pos = pResults;

for(int i=0;i

{

if (errors[i] == 0)

{

OPCITEMRESULT result = (OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OPCITEMRESULT));

itemsID[i] = result.hServer;

this.hitemsID.Add(itemsName[i],result.hServer);

pos = new IntPtr(pos.ToInt32() + Marshal.SizeOf(typeof(OPCITEMRESULT)));

}

else

{

success=false;

break;

}

}

}

catch (System.Exception err) // catch for error in adding items.

{

success=false;

}

finally

{

// 释放非托管内存

if(pResults != IntPtr.Zero)

{

Marshal.FreeCoTaskMem(pResults);

pResults = IntPtr.Zero;

}

if(pErrors != IntPtr.Zero)

{

Marshal.FreeCoTaskMem(pErrors);

pErrors = IntPtr.Zero;

}

}

return success;

}

  说明:使用该函数时,在类的开头,应该先声明整数数据,以用于保存由本函数返回的服务器对每一项分配的Item ID号:

5、 向指定组中指定的一系列项变量写入数据的公开方法

///

/// 一次性写入多个值

///

/// 指定组名

/// 由服务器给每个项分配的标志号

/// 一系列值

/// 无错误,返回true,否则返回false

public bool Write(string groupName,int[] itemID,object[] values)

{

bool success=true;

IntPtr pErrors = IntPtr.Zero;

if(GetGroupByName(groupName) != null)

{

try

{ //同步写入

((IOPCSyncIO)GetGroupByName(groupName)).Write(itemID.Length,itemID,values,out pErrors);

int[] errors = new int[itemID.Length];

Marshal.Copy(pErrors, errors, 0,itemID.Length);

for(int i=0;i

{

if (errors[i] != 0)

{

pErrors = IntPtr.Zero;

success=false;

}

}

}

catch(System.Exception error)

{

success=false;

}

}

return success;

}

注:参数int[] itemID应该是与AddItems函数中的int[] itemsID参数相对应。

6、 编写获取变量值的函数

/// 一次性读取多个数据

/// 指定组名

/// >由服务器给每个项分配的标志号

/// 返回的值

/// 无错误,返回true,否则返回false

public bool Read(string groupName,int[] itemID,object[] result)

{

bool success=true;

//指向非托管内存

//指向非托管内存

IntPtr pItemValues = IntPtr.Zero;

IntPtr pErrors = IntPtr.Zero;

if(GetGroupByName(groupName)!=null)

{

try

{ //同步读取

((IOPCSyncIO)GetGroupByName(groupName)).Read(OPCDATASOURCE.OPC_DS_DEVICE,itemID.Length,itemID,out pItemValues,out pErrors);

int[] errors = new int[itemID.Length];

Marshal.Copy(pErrors, errors, 0,itemID.Length);

OPCITEMSTATE[] pItemState=new OPCITEMSTATE[itemID.Length];

IntPtr pos = pItemValues;

for(int i=0;i

{

if (errors[i] == 0)

{

//从非托管区封送数据到托管区

pItemState[i] = (OPCITEMSTATE)Marshal.PtrToStructure(pos,typeof(OPCITEMSTATE));

pos = new IntPtr(pos.ToInt32() + Marshal.SizeOf(typeof(OPCITEMSTATE)));

result[i]=pItemState[i].vDataValue;

}

}

}

catch(System.Exception error)

{

return false;

}

}

return success;

}

  注:同Write()函数一样,参数int[] itemID应该是与AddItems函数中的int[] itemsID参数相对应。

  通过给类编写上面的几个最重要的函数,我们已经可以读写PLC数据了,下面给出例子。

  创建一个C#工程,添加对上面开发的类库的引用,并在窗体类的开头,声名:

int[] nt=new int[2];int[] nt1=new int[2];

S7Connection.SynServer server;

其中的SynServer即为上面开发的类。

<1>、创建服务器接口

在程序初始化处,添加:

server =new S7Connection.SynServer(S7Connection.ServerType.OPC_SimaticNET);

<2>、打开连接

string err;

server.Open(out err);

<3>、添加组

server.AddGroup("maiker",1,350,out err);

server.AddGroup("maiker1",1,350,out err);

<4>、添加项(即变量),同样在程序的初始化中,将一系列项添加到他们各自得组。

string[] m1={"S7:[DEMO]MB1","S7:[DEMO]MW3"};

string[] m2={"S7:[DEMO]MB6","S7:[DEMO]MW8"};

server.AddItems("maiker",m1,nt);

server.AddItems("maiker1",m2,nt1);

<5>、读写数据,这里以写数据为例:

obj[0]=this.textBox2.Text;

obj[1]=this.textBox3.Text;

if(radioButton1.Checked)

{

server.Write("maiker",nt,obj);

}

else if(radioButton2.Checked)

{

server.Write("maiker1",nt1,obj);

}
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


--------------------------------------------------------------------------------------------------------------------
本站是工控技术交流站点,论坛内容均为网络收集或会员所发表,并不代表本站立场,会员拥有该内容的所有权力及责任!
本站内容如有侵犯您的版权,请按下面方式联系本站管理员,我们将及时删除处理
管理员:冰糖 QQ:5483695(请直击主题), Mail:admin#ziwai.net(#改成@) 其它非本人.
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论!

QQ|Archiver|手机版|小黑屋|紫外工控论坛. ( 苏ICP备11032118号-1 )

GMT+8, 2024-5-4 12:03 , Processed in 0.406250 second(s), 17 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表