甘南西门子PLC总代理商

供应商
浔之漫智控技术-西门子PLC代理商
认证
手机号
15221406036
经理
聂航
所在地
上海市松江区石湖荡镇塔汇路755弄29号1幢一层A区213室
更新时间
2024-05-08 07:10

详细介绍

甘南西门子plc总代理商

1、 opc服务介绍
  西门子提供的新软件:simatic net pc-software cd2005为各种组态软件的开发提供了一个统一的平台,它建立的pc站既为一些组态软件,如:wincc、protol等提供了与plc的通讯平台,也提供了一套编程接口,可使用语言编程通过simaticnet访问plc数据。本文讨论的主要就是这个编程接口,新版的simatic net支持五种编程方式:
<1>、activex控件
  提供了一系列数据访问控件,以便于向vb6这种语言使用控件的方式与plc通讯。
<2>、opc自动化
为vb6、dephi等语言运用ole 自动化的方式进行编程。
<3>、opc用户接口
  这是专门为vc++提供的一种高效编程方式,其灵活程度与执行效率比前面的两种方式均要高得多。
<4>、针对微软的.net平台的opc用户接口
  这也是一种非常灵活的编程接口,不过它针对的是.net平台,其提供了大量的.net类库,以便于像vc#、vb.net等语言编程。本文将详细的介绍该接口。
<5>、oplxml接口
  顾名思义,主要是针对xml编程的。
  对于<2>、<3>、<4>编程方式,他们各自又可以分为同步访问方式和异步访问方式。按西门子的文档解释:同步通讯指的是当一个客户在访问服务器时,其他客户的访问必须等待,直到服务器处理完该客户的请求,才能继续进行下一个服务,异步访问与之正好相反,本文主要讲的是同步编程篇,异步篇以后再提供。
2、 配置opc服务器
  要进行编程,必须先配置服务器。本文以prfibus dp网络为例,介绍pc站的配置。其内容主要来自西门子文档。
需要的软件:
step7 v5.3
simatic net pc-software cd 2005
需要的硬件:
  至少为cp5611或以上级别,笔记本可以为cp5511,带dp口的s7-300 plc(若使用simaticnet的仿真功能可以不需要这些硬件,后面会介绍到)
  <1>、组态一个s7站,配置profibusdp网络,其dp地址设为3,并下载到plc,然后把网线由mpi口转到dp口。s7站的配置这里就不介绍了。
  <2>、在 step7v5.3中建立一个新工程,插入一个pc站,并把该pc站的名字改成与你的计算机名字相同。打开该pc站的硬件组态界面。插入opc服务器和连接卡cp5611(或者cp5511),他们在pc槽中处的位置可以任意,如下图:
 
  注:在插入cp5611时,应该选择与组态s7站一样的profibus网络,并将网络地址设为2,一定不要与plc的地址冲突。
  然后点击下面工具条标为红色的按钮:
 
  选中”opc server”,然后插入一个新的连接,如下图:
  
 
  在弹出的对话框中选择连接类型为s7 connection,如下图:
  

  在ok后,然后在新对话框的红色标志位置输入3,表示plc的地址,如下图:
   

  并选择address details…,设置cpu的槽号为2,如下图:
 
 
  ok后,然后编译并保存。
  <3>、然后建立opc服务器,有两种方式,本文介绍较简单的一种。
  打开,simatic net中的station configurator,一般安装后,他会自动启动,并点击importstation…按钮,找到你刚才在step 7中建立pc站时创建的
  xdbs文件夹下的xdb文件,然后导入成功。
  <4>、可以使用simatic net中的opc scout,并选择simaticnet服务,然后在它下面创建组,然后在组下创建变量,这样可以监控plc数据,vc#编程不需要使用该程序,但熟悉使用opcscout有利于了解simatic net中的编程结构。
  说明:打开simatic net中的configurationconsole,选中s7进行如下的配置后,可以不需要plc、cp5611等并可以模拟,如下图:
 

  上面的所有步骤,均可在configuration console下,pc station的根树下,选择相应的帮助文档得到。
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站时产生的连接名,如果使用仿真功能,连接名为demovariablename:变量名有一系列规则,这里举例说明,读者也可以使用opcscout创建变量,学习程序是如何生成变量名的。
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 bbbbbb servertype="";
private iopcserver piopcserver; // opc server接口
private bbbbbb pobjgroup1; // pointer to group bbbbbb
private int nsvrgroupid; // server group handle for the addedgroup
private system.collections.bbbbtable groupsid=new bbbbtable(11);//用于记录组名和组id号
private system.collections.bbbbtable hitemsid=new bbbbtable(17);//用于记录项名和项id号
private guid iidre;
private int hclientgroup = 0; //客户组号
private int hclientitem=0; //item号
2、创建服务器,编写open()方法
///
/// 创建一个opc server接口
///
/// 返回错误信息
/// 若为true,创建成功,否则创建失败
public bool open(out bbbbbb error)
{
error="";bool success=true;
type svrcomponenttyp ;
//获取 opc server com 接口
iidre = 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(bbbbbb groupname,int bactive,intupdaterate,out bbbbbb 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 iidre,
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(bbbbbb groupname,bbbbbb[] 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 = ""; // 可选的通道路径,对于simatiicnet不需要。
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].vtre = 2; //word数据类型
}
//初始化输出参数
intptr presults = intptr.zero;
intptr perrors = intptr.zero;
try
{
// 添加项到组
((iopcitemmgt)getgroupbyname(groupname)).additems(itemsname.length,itemdefarray,outpresults,out perrors);
// unmarshal to get the server handles out fom them_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<itemsname.length;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 addingitems.
{
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(bbbbbb groupname,int[] itemid,bbbbbb[]values)
{
bool success=true;
intptr perrors = intptr.zero;
if(getgroupbyname(groupname) != null)
{
try
{ //同步写入
((iopcsyncio)getgroupbyname(groupname)).write(itemid.length,itemid,values,outperrors);
int[] errors = new int[itemid.length];
marshal.copy(perrors, errors, 0,itemid.length);
for(int i=0;i<itemid.length;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(bbbbbb groupname,int[] itemid,bbbbbb[] 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,outpitemvalues,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<itemid.length;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 =news7connection.synserver(s7connection.servertype.opc_simaticnet);
<2>、打开连接
bbbbbb err;
server.open(out err);
<3>、添加组
server.addgroup("maiker",1,350,out err);
server.addgroup("maiker1",1,350,out err);
<4>、添加项(即变量),同样在程序的初始化中,将一系列项添加到他们各自得组。
bbbbbb[] m1={"s7:[demo]mb1","s7:[demo]mw3"};
bbbbbb[] 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);
}
  至此并完成了数据的通讯,如何,只要你把类库开发完善,在它的基础上再开发,会异常简单,本人已开发了完善的类库,上面的类库只是把重要的部分讲解出来,我曾经在网上求助过很多次这方面的知识,无人应答。唉!太不容易了,等待simaticnet软件花费了我一个月的时间,然后读几百页的英文文档,到开发程序,并测试花费了我一个星期的空闲时间,写这篇文章,又花费了我一个晚上的时间,不过我还是愿意把这些摸索出来的东西发给大家。

西门子代理商,西门子模块代理商,西门子一级代理商,西门子PLC代理,西门子中国代理商

展开全文

我们其他产品
我们的新闻
咨询 在线询价 拨打电话