运用VC#编程通过OPC方式实现PC机与西门子PLC通讯Word格式.docx
- 文档编号:16601635
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:9
- 大小:20.92KB
运用VC#编程通过OPC方式实现PC机与西门子PLC通讯Word格式.docx
《运用VC#编程通过OPC方式实现PC机与西门子PLC通讯Word格式.docx》由会员分享,可在线阅读,更多相关《运用VC#编程通过OPC方式实现PC机与西门子PLC通讯Word格式.docx(9页珍藏版)》请在冰豆网上搜索。
本文以PrfibusDP网络为例,介绍PC站的配置。
其内容主要来自西门子文档。
需要的软件:
Step7V5.3
SimaticNetPC-SoftwareCD2005
需要的硬件:
至少为CP5611或以上级别,笔记本可以为CP5511,带DP口的S7-300PLC(若使用SimaticNET的仿真功能可以不需要这些硬件,后面会介绍到)
<
、组态一个S7站,配置ProfibusDP网络,其DP地址设为3,并下载到PLC,然后把网线由MPI口转到DP口。
S7站的配置这里就不介绍了。
、在Step7V5.3中建立一个新工程,插入一个PC站,并把该PC站的名字改成与你的计算机名字相同。
打开该PC站的硬件组态界面。
插入OPC服务器和连接卡CP5611(或者CP5511),他们在PC槽中处的位置可以任意,
注:
在插入CP5611时,应该选择与组态S7站一样的Profibus网络,并将网络地址设为2,一定不要与PLC的地址冲突。
然后点击下面工具条标为红色的按钮:
选中”OPCServer”,然后插入一个新的连接,
在弹出的对话框中选择连接类型为S7Connection,
在OK后,然后在新对话框的红色标志位置输入3,表示PLC的地址,
并选择AddressDetails…,设置CPU的槽号为2,
OK后,然后编译并保存。
、然后建立OPC服务器,有两种方式,本文介绍较简单的一种。
打开,SimaticNet中的StationConfigurator,一般安装后,他会自动启动,并点击ImportStation…按钮,找到你刚才在Step7中建立PC站时创建的
XDBs文件夹下的XDB文件,然后导入成功。
、可以使用SimaticNet中的OPCScout,并选择SimaticNET服务,然后在它下面创建组,然后在组下创建变量,这样可以监控PLC数据,VC#编程不需要使用该程序,但熟悉使用OPCScout有利于了解SimaticNet中的编程结构。
说明:
打开SimaticNet中的ConfigurationConsole,选中S7进行如下的配置后,可以不需要PLC、CP5611等并可以模拟,
上面的所有步骤,均可在ConfigurationConsole下,PCStation的根树下,选择相应的帮助文档得到。
3、OPC编程
、西门子的变量结构如下:
----------------------服务器------------------------------
/OPC.SimaticNetOPCServer.Wincc....(一系列类型的服务器)
/Group1Group2Group3...(把更新时间一致的变量统一为一个组)
/Item1Item2...(变量:
I、Q、M、DB等,指向网络中某个PC站OPCServer服务的某个连接)
-----------------------------------------------------------------------------------------------------------------
第一层是不同种类的服务器,如:
OPC.SimaticNET类型,OPC.SimaticNET.DP类型,OPCServer.WinCC等一系列类型,这里选择OPC.SimaticNET类型。
第二层是Group,一个服务器下可以有多个组,可以把组理解为扫描周期相同的一系列变量的集合。
在开发组态界面时,可以把一个界面中的所有变量统一到一个组中。
第三层是Item,项是指向网络中某个PC站OPCServer服务的某个连接的一系列变量,如:
I、Q、M、DB等
、项的命名
项即Item,在S7连接中针对的直接是PLC中的变量,因此它的命名很重要:
格式:
:
[]
其中的protocolID表示连接类型,在上面的组态PC站时可以选择,这里应该与它一致,类型有9种,最常用的为S7,即S7连接,其他类型请参看文档。
Connectionname:
顾名思义,即在上面的组态PC站时产生的连接名,如果使用仿真功能,连接名为DEMOVariablename:
变量名有一系列规则,这里举例说明,读者也可以使用OPCScout创建变量,学习程序是如何生成变量名的。
S7:
[DEMO]MB1:
表示连接类型为S7,连接名为DEMO(这里为仿真),变量为MB1
[DEMO]QB0,3:
表示为从QB0开始的三个连续变量。
[DEMO]DB10,X4.6:
表示DB10的DBX4.6。
、添加引用
在VC#开发环境中添加对OpcRcw.Da库的引用引用,该库属于.NET库,不属于COM库,西门子虽然编写了类库,以提供对.NET平台的支持,但这些类库仍然难于编程,
里面包含了大量的在托管和非托管区传输数据,因此我们需要在它的基础上再开发一个类库,以简化以后的编程,首先在类的开头使用命名空间:
usingSystem.Runtime.InteropServices;
usingOpcRcw.Da;
usingSystem.Collections;
、编程
1、在类的开头部分生名变量
privatestringserverType="
"
;
privateIOPCServerpIOPCServer;
//OPCserver接口
privateObjectpobjGroup1;
//Pointertogroupobject
privateintnSvrGroupID;
//servergrouphandlefortheaddedgroup
privateSystem.Collections.HashtablegroupsID=newHashtable(11);
//用于记录组名和组ID号
privateSystem.Collections.HashtablehitemsID=newHashtable(17);
//用于记录项名和项ID号
privateGuidiidRequiredInterface;
privateinthClientGroup=0;
//客户组号
privateinthClientItem=0;
//Item号
2、创建服务器,编写Open()方法
///创建一个OPCServer接口
///
///返回错误信息
///若为true,创建成功,否则创建失败
publicboolOpen(outstringerror)
{
error="
boolsuccess=true;
TypesvrComponenttyp;
//获取OPCServerCOM接口
iidRequiredInterface=typeof(IOPCItemMgt).GUID;
svrComponenttyp=System.Type.GetTypeFromProgID(serverType);
try
//创建接口
pIOPCServer=(IOPCServer)System.Activator.CreateInstance(svrComponenttyp);
}
catch(System.Exceptionerr)//捕捉失败信息
错误信息:
+err.Message;
success=false;
Returntrue;
3、在服务器上添加用于添加Group的函数
///添加组
///组名
////创建时,组是否被激活
/////组的刷新频率,以ms为单位
///若为true,添加成功,否则添加失败
publicboolAddGroup(stringgroupName,intbActive,intupdateRate,outstringerror)
{
intdwLCID=0x407;
//本地语言为英语
intpRevUpdateRate;
floatdeadband=0;
//处理非托管COM内存
GCHandlehDeadband;
IntPtrpTimeBias=IntPtr.Zero;
hDeadband=GCHandle.Alloc(deadband,GCHandleType.Pinned);
pIOPCServer.AddGroup(groupName,//组名
bActive,//创建时,组是否被激活
updateRate,//组的刷新频率,以ms为单位
hClientGroup,//客户号
pTimeBias,//这里不使用
(IntPtr)hDeadband,
dwLCID,//本地语言
outnSvrGroupID,//移去组时,用到的组ID号
outpRevUpdateRate,//返回组中的变量改变时的最短通知时间间隔
refiidRequiredInterface,
outpobjGroup1);
//指向要求的接口
hClientGroup=hClientGroup+1;
intgroupID=nSvrGroupID;
groupsID.Add(groupName,groupID);
finally
if(hDeadband.IsAllocated)hDeadband.Free();
if(error=="
)
returntrue;
else
returnfalse;
4、向指定的组中添加变量的函数
///添加多个项到组
///指定组名
///指定项名
///由函数返回的服务器确定的项ID号
///无错误,返回true,否则返回false
publicboolAddItems(stringgroupName,string[]itemsName,int[]itemsID)
OPCITEMDEF[]ItemDefArray=newOPCITEMDEF[itemsName.Length];
for(inti=0;
i<
ITEMSNAME.LENGTH;
I++)
hClientItem=hClientItem+1;
ItemDefArray[i].szAccessPath="
//可选的通道路径,对于SimatiicNet不需要。
ItemDefArray[i].szItemID=itemsName[i];
//ItemID,seeabove
ItemDefArray[i].bActive=1;
//itemisactive
ItemDefArray[i].hClient=hClientItem;
//clienthandle
ItemDefArray[i].dwBlobSize=0;
//blobsize
ItemDefArray[i].pBlob=IntPtr.Zero;
//pointertoblob
ItemDefArray[i].vtRequestedDataType=2;
//Word数据类型
//初始化输出参数
IntPtrpResults=IntPtr.Zero;
IntPtrpErrors=IntPtr.Zero;
try
//添加项到组
((IOPCItemMgt)GetGroupByName(groupName)).AddItems(itemsName.Length,ItemDefArray,outpResults,outpErrors);
//Unmarshaltogettheserverhandlesoutfomthem_pItemResult
//aftercheckingtheerrors
int[]errors=newint[itemsName.Length];
Marshal.Copy(pErrors,errors,0,itemsName.Length);
IntPtrpos=pResults;
i
if(errors[i]==0)
OPCITEMRESULTresult=(OPCITEMRESULT)Marshal.PtrToStructure(pos,typeof(OPCITEMRESULT));
itemsID[i]=result.hServer;
this.hitemsID.Add(itemsName[i],result.hServer);
pos=newIntPtr(pos.ToInt32()+Marshal.SizeOf(typeof(OPCITEMRESULT)));
break;
catch(System.Exceptionerr)//catchforerrorinaddingitems.
//释放非托管内存
if(pResults!
=IntPtr.Zero)
Marshal.FreeCoTaskMem(pResults);
pResults=IntPtr.Zero;
if(pErrors!
Marshal.FreeCoTaskMem(pErrors);
pErrors=IntPtr.Zero;
returnsuccess;
使用该函数时,在类的开头,应该先声明整数数据,以用于保存由本函数返回的服务器对每一项分配的ItemID号:
5、向指定组中指定的一系列项变量写入数据的公开方法
///一次性写入多个值
///由服务器给每个项分配的标志号
///一系列值
publicboolWrite(stringgroupName,int[]itemID,object[]values)
if(GetGroupByName(groupName)!
=null)
{//同步写入
((IOPCSyncIO)GetGroupByName(groupName)).Write(itemID.Length,itemID,values,outpErrors);
int[]errors=newint[itemID.Length];
Marshal.Copy(pErrors,errors,0,itemID.Length);
if(errors[i]!
=0)
catch(System.Exceptionerror)
}
注:
参数int[]itemID应该是与AddItems函数中的int[]itemsID参数相对应。
6、编写获取变量值的函数
///一次性读取多个数据
///>
由服务器给每个项分配的标志号
///返回的值
publicboolRead(stringgroupName,int[]itemID,object[]result)
//指向非托管内存
IntPtrpItemValues=IntPtr.Zero;
if(GetGroupByName(groupName)!
=null)
{//同步读取
((IOPCSyncIO)GetGroupByName(groupName)).Read(OPCDATASOURCE.OPC_DS_DEVICE,itemID.Length,itemID,outpItemValues,outpErrors);
OPCITEMSTATE[]pItemState=newOPCITEMSTATE[itemID.Length];
IntPtrpos=pItemValues;
//从非托管区封送数据到托管区
pItemState[i]=(OPCITEMSTATE)Marshal.PtrToStructure(pos,typeof(OPCITEMSTATE));
pos=newIntPtr(pos.ToInt32()+Marshal.SizeOf(typeof(OPCITEMSTATE)));
result[i]=pItemState[i].vDataValue;
同Write()函数一样,参数int[]itemID应该是与AddItems函数中的int[]itemsID参数相对应。
通过给类编写上面的几个最重要的函数,我们已经可以读写PLC数据了,下面给出例子。
创建一个C#工程,添加对上面开发的类库的引用,并在窗体类的开头,声名:
int[]nt=newint[2];
int[]nt1=newint[2];
S7Connection.SynServerserver;
其中的SynServer即为上面开发的类。
、创建服务器接口
在程序初始化处,添加:
server=newS7Connection.SynServer(S7Connection.ServerType.OPC_SimaticNET);
、打开连接
stringerr;
server.Open(outerr);
、添加组
server.AddGroup("
maiker"
1,350,outerr);
maiker1"
、添加项(即变量),同样在程序的初始化中,将一系列项添加到他们各自得组。
string[]m1={"
[DEMO]MB1"
"
[DEMO]MW3"
};
string[]m2={"
[DEMO]MB6"
[DEMO]MW8"
server.AddItems("
m1,nt);
m2,nt1);
、读写数据,这里以写数据为例:
obj[0]=this.textBox2.Text;
obj[1]=this.textBox3.Text;
if(radioButton1.Checked)
server.Write("
nt,obj);
elseif(radioButton2.Checked)
nt1,obj);
至此并完成了数据的通讯,如何,只要你把类库开发完善,在它的基础上再开发,会异常简单,本人已开发了完善的类库,上面的类库只是把最重要的部分讲解出来,我曾经在网上求助过很多次这方面的知识,无人应答。
唉!
太不容易了,等待SimaticNET软件花费了我一个月的时间,然后读几百页的英文文档,到开发程序,并测试花费了我一个星期的空闲时间,写这篇文章,又花费了我一个晚上的时间,不过我还是愿意把这些摸索出来的东西发给大家。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 运用 VC 编程 通过 OPC 方式 实现 PC 西门子 PLC 通讯