关于C#如何使用Prodave实战DllImport.docx
《关于C#如何使用Prodave实战DllImport.docx》由会员分享,可在线阅读,更多相关《关于C#如何使用Prodave实战DllImport.docx(16页珍藏版)》请在冰点文库上搜索。
关于C#如何使用Prodave实战DllImport
严格意义上讲,此文不算OPC的范畴。
起因是,另一个项目的PLC强人说,OPC慢,用prodave吧,好,用就用吧,装好Prodave看是看其英文资料,虽然英文不好,但好在这里英文
很简单。
好了,上网查了点资料,这里还要感谢几个朋友的帮忙,让我对于C#与C++的数据结构有了更进一步的认识,也学会了使用DllImport
在本文的开头,我要说明下,Prodave是西门子的通信方式,即使我提供了Prodave6.dll,您不注册也是没用用的,所以请使用西门子的安装程序,哪里下载?
自己XX一下。
不
要来问我哪里下载Prodave6.dll,也不要问我为什么程序会报错说没有注册dll
下面开始进入正文。
(1)上来第一个函数,就是连接PLC的LoadConnection_ex6,在说明书里描述如下:
LoadConnection_ex6
ThebasicLoadConnection_ex6functioninitializestheadapter,checksifthe
driverisloaded,initializestheaddressesthathavebeenassignedparametersand
activatestheselectedinterface.
LoadConnection_ex6isusedtosetupatransportconnectionviaMPI/PB-orIP
addresses(TCP/IPprotocol)
intLoadConnection_ex6(intConNr,char*pAccessPoint,intConTableLen,
CON_TABLE_TYPE*pConTable);
Parameters
ConNr
[in]Numberoftheconnection(max.64connections).
pAccessPoint
[in]accesspoint(zero-terminated)ofthedriverused,e.g."S7ONLINE"fortheMPI
driveror0(default).
ConTableLen
[in]lengthofthetableofconnectionsprovidedbytheuserinbytes
pConTable
[in]pointertoaddresslistofconnectedusers;‘Adr==0’istakenastheendmarkof
thelist.
#pragmapack
(1)
typedefunion{
unsignedcharMpi;//MPI/PBstationaddress
(2)
unsignedcharIp[4];//IPaddress(192.168.0.1)
unsignedcharMac[6];//MACaddress(08-00-06-01-AA-BB)
}CON_ADR_TYPE;
typedefstruct{
CON_ADR_TYPEAdr;//connectionaddress
unsignedcharAdrType;//Typeofaddress:
MPI/PB
(1),IP
(2),MAC(3)
unsignedcharSlotNr;//Slotnumber
unsignedcharRackNr;//Racknumber
}CON_TABLE_TYPE;
#pragmapack
(1)
好吧,起先其他的转换网上都有,不难,但是出现了union共用体,恩C#没有这个概念。
怎么办?
起先,参考网上的资料,采用
[StructLayout(LayoutKind.Explicit)]
structS1
{
[FieldOffset(0)]
inta;
[FieldOffset(0)]
intb;
}
类似这样的布局,再加上类似[MarshalAs(UnmanagedType.ByValArray,SizeConst=6)]的声明。
但是,最终单纯的使用2个数组作为“共用体”字段的话是可以通过编译的,但是再加上unsignedcharMpi的话,OK你是过不了编译的,就算过了运行也出错。
反复尝试还是失败
,最后我联想到在内存中实际上这个共用体用的是一个以最大字段为空间大小的内存,于是乎尝试了,直接定义[MarshalAs(UnmanagedType.ByValArray,SizeConst=6)]byte
Mac[6],果然解决了,共用体的问题,其实C++传进去的参数也其实是6字节的数组而已,进而想既然一个数组搞定,那么还用共用体干嘛,不要了,于是出现了如下函数转换的正
解:
publicstructCON_TABLE_TYPE//待连接plc地址属性表
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst=6)]
//publicCON_ADR_TYPEAdr;//connectionaddress
publicbyte[]Adr;//connectionaddress
//MPI/PBstationaddress
(2)
//IPaddress(192.168.0.1)
//MACaddress(08-00-06-01-AA-BB)
publicbyteAdrType;//Typeofaddress:
MPI/PB
(1),IP
(2),MAC(3)
publicbyteSlotNr;//Slotnumber
publicbyteRackNr;//Racknumber
}
[DllImport("Prodave6.dll")]//连接PLC操作
//参数:
连接号(0-63)、常值"S7ONLINE"、待连接plc地址属性表长度(字节为单位,常值9)、待连接plc地址属性表
publicexternstaticintLoadConnection_ex6(intConNr,stringpAccessPoint,intConTableLen,refCON_TABLE_TYPEpConTable);
(2)关于unsignedchar*pBuffer,这个unsignedchar*其实有2个转换可选,有时可以使用byte[],有时则是StringBuilder,这就要集体问题具体分析了。
例如:
intGetErrorMessage_ex6(intErrorNr,unsignedlongBufLen,unsignedchar*pBuffer);
voidcopy_buffer_ex6(unsignedchar*pTargetBuffer,unsignedchar*pSourceBuffer,unsignedlongAmount);
前者就转换成StringBuilder后者是byte[]。
(3)有些变量虽是整型但是可以用枚举,而且用枚举感觉更合适
例如intdb_read_ex6(unsignedshortBlkNr,unsignedcharDatType,unsigned
shortStartNr,unsignedlong*pAmount,unsignedlongBufLen,unsigned
char*pReadBuffer,unsignedlong*pDatLen)中,unsignedcharDatType其实指的是“0x02=BYTE,0x04=WORD,0x06=DWORDdefault:
DatType=0x02”等数据类型
,因此可以翻译成
publicenumDatType:
byte//PLC数据类型
{
BYTE=0x02,
WORD=0x04,
DWORD=0x06,
}
(4)对了,如果是对象型的引用,比如unsignedchar*转成byte[],是不需要加ref,但如果是c++int转c#int则要加ref关键字。
要说明的就是这些,下面请各位看官看看我的转换代码吧,还请见教:
publicclassProdave6
{
#region常值定义(用于极限值)
publicconstintMAX_CONNECTIONS=64;//64isdefaultinPRODAVE
publicconstintMAX_DEVNAME_LEN=128;//e.g."S7ONLINE"
publicconstintMAX_BUFFERS=64;//64forblk_read()andblk_write()
publicconstintMAX_BUFFER=65536;//Transferbufferforerrortext)
#endregion
#region结构体定义
publicstructCON_TABLE_TYPE//待连接plc地址属性表
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst=6)]
//publicCON_ADR_TYPEAdr;//connectionaddress
publicbyte[]Adr;//connectionaddress
//MPI/PBstationaddress
(2)
//IPaddress(192.168.0.1)
//MACaddress(08-00-06-01-AA-BB)
publicbyteAdrType;//Typeofaddress:
MPI/PB
(1),IP
(2),MAC(3)
publicbyteSlotNr;//Slotnumber
publicbyteRackNr;//Racknumber
}
publicenumDatType:
byte//PLC数据类型
{
BYTE=0x02,
WORD=0x04,
DWORD=0x06,
}
publicenumFieldType:
byte//PLC区域类型
{
//ValuetypesasASCIIcharacters区域类型对应的ASCII字符
//databyte(d/D)
d=100,
D=68,
//inputbyte(e/E)
e=101,
E=69,
//outputbyte(a/A)
a=97,
A=65,
//memorybyte(m/M)
m=109,
M=77,
//timerword(t/T),
t=116,
T=84,
}
#endregion
#regionPLC基本函数
[DllImport("Prodave6.dll")]//连接PLC操作
//参数:
连接号(0-63)、常值"S7ONLINE"、待连接plc地址属性表长度(字节为单位,常值9)、待连接plc地址属性表
publicexternstaticintLoadConnection_ex6(intConNr,stringpAccessPoint,intConTableLen,refCON_TABLE_TYPEpConTable);
[DllImport("Prodave6.dll")]//断开PLC操作
//参数:
连接号(0-63)
publicexternstaticintUnloadConnection_ex6(UInt16ConNr);
[DllImport("Prodave6.dll")]//激活PLC连接操作
//参数:
连接号(0-63)
publicexternstaticintSetActiveConnection_ex6(UInt16ConNr);
[DllImport("Prodave6.dll")]//PLCdb区读取操作
//参数:
datablock号、要读取的数据类型、起始地址号、需要读取类型的数量、缓冲区长度(字节为单位)、缓冲区、缓冲区数据交互的长度
publicexternstaticintdb_read_ex6(UInt16BlkNr,DatTypeDType,UInt16StartNr,refUInt32pAmount,UInt32BufLen,
byte[]pBuffer,refUInt32pDatLen);
[DllImport("Prodave6.dll")]//PLCdb区写入操作
//参数:
datablock号、要写入的数据类型、起始地址号、需要写入类型的数量、缓冲区长度(字节为单位)、缓冲区
publicexternstaticintdb_write_ex6(UInt16BlkNr,DatTypeType,UInt16StartNr,refUInt32pAmount,UInt32BufLen,
byte[]pBuffer);
[DllImport("Prodave6.dll")]//PLC任意区读取操作
//参数:
要读取的区类型、datablock号(DB区特有,默认为0)、起始地址号、需要读取类型的数量、
//缓冲区长度(字节为单位)、缓冲区、缓冲区数据交互的长度
publicexternstaticintfield_read_ex6(FieldTypeFType,UInt16BlkNr,UInt16StartNr,UInt32pAmount,UInt32BufLen,
byte[]pBuffer,refUInt32pDatLen);
[DllImport("Prodave6.dll")]//PLC任意区写入操作
//参数:
要写入的区类型、datablock号(DB区特有,默认为0)、起始地址号、需要写入类型的数量、
//缓冲区长度(字节为单位)、缓冲区
publicexternstaticintfield_write_ex6(FieldTypeFType,UInt16BlkNr,UInt16StartNr,UInt32pAmount,UInt32BufLen,
byte[]pBuffer);
[DllImport("Prodave6.dll")]//PLCM区某字节的某位读取操作
//参数:
M区字节号、位号、当前的值(0/1)
publicexternstaticintmb_bittest_ex6(UInt16MbNr,UInt16BitNr,refintpValue);
[DllImport("Prodave6.dll")]//PLCM区某字节的某位写入操作
//参数:
M区字节号、位号、要写入的值(0/1)
publicexternstaticintmb_setbit_ex6(UInt16MbNr,UInt16BitNr,byteValue);
#endregion
#regionPLC200用数据传输函数
[DllImport("Prodave6.dll")]//200系列PLC任意区读取操作
//参数:
要读取的区类型、datablock号(DB区特有,默认为0)、起始地址号、需要读取类型的数量、
//缓冲区长度(字节为单位)、缓冲区、缓冲区数据交互的长度
publicexternstaticintas200_field_read_ex6(FieldTypeFType,UInt16BlkNr,UInt16StartNr,UInt32pAmount,UInt32BufLen,
byte[]pBuffer,refUInt32pDatLen);
[DllImport("Prodave6.dll")]//200系列PLC任意区写入操作
//参数:
要写入的区类型、datablock号(DB区特有,默认为0)、起始地址号、需要写入类型的数量、
//缓冲区长度(字节为单位)、缓冲区
publicexternstaticintas200_field_write_ex6(FieldTypeFType,UInt16BlkNr,UInt16StartNr,UInt32pAmount,UInt32BufLen,
byte[]pBuffer);
[DllImport("Prodave6.dll")]//200系列PLCM区某字节的某位读取操作
//参数:
M区字节号、位号、当前的值(0/1)
publicexternstaticintas200_mb_bittest_ex6(UInt16MbNr,UInt16BitNr,refintpValue);
[DllImport("Prodave6.dll")]//200系列PLCM区某字节的某位写入操作
//参数:
M区字节号、位号、要写入的值(0/1)
publicexternstaticintas200_mb_setbit_ex6(UInt16MbNr,UInt16BitNr,byteValue);
#endregion
#regionPLC数据转换函数
[DllImport("Prodave6.dll")]//诊断错误信息操作
//参数:
错误代号、缓冲区大小(字节为单位)、缓冲区
publicexternstaticintGetErrorMessage_ex6(intErrorNr,UInt32BufLen,[MarshalAs(UnmanagedType.LPStr)]StringBuilderpBuffer);
[DllImport("Prodave6.dll")]//S7浮点数转换成PC浮点数
//参数:
S7浮点数、PC浮点数
publicexternstaticintgp_2_float_ex6(UInt32gp,reffloatpieee);
[DllImport("Prodave6.dll")]//PC浮点数转换成S7浮点数
//参数:
PC浮点数、S7浮点数
publicexternstaticintfloat_2_gp_ex6(floatieee,refUInt32pgp);
[DllImport("Prodave6.dll")]//检测某字节的某位的值是0或1
//参数:
字节值、位号
publicexternstaticinttestbit_ex6(byteValue,intBitNr);
[DllImport("Prodave6.dll")]//检测某字节的byte值转换成int数组
//参数:
byte值、int数组(长度为8)
publicexternstaticvoidbyte_2_bool_ex6(byteValue,int[]pBuffer);
[DllImport("Prodave6.dll")]//检测某字节的int数组转换成byte值
//参数:
int数组(长度为8)
publicexternstaticbytebool_2_byte_ex6(int[]pBuffer);
[DllImport("Prodave6.dll")]//交换数据的高低字节——16位数据
//参数:
待交换的数据
publicexternstaticUInt16kf_2_integer_ex6(UInt16wValue);//16位数据——WORD
[DllImport("Prodave6.dll")]//交换数据的高低字节——32位数据
//参数:
待交换的数据
publicexternstaticUInt32kf_2_long_ex6(UInt32dwValue);//32位数据——DWORD
[DllImport("Prodave6.dll")]//交换数据缓冲区的的高低字节区,例如pBuffer[0]与pBuffer[1],pBuffer[2]与pBuffer[3]交换
//参数:
待交换的数据缓冲区,要交换的字节数,如Amount=pBuffer.Length,则交换全部缓冲
publicexternstaticvoidswab_buffer_ex6(byte[]pBuffer,UInt32Amount);
[DllImport("Prodave6.dll")]//复制数据缓冲区
//参数:
目的数据缓冲区,源数据缓冲区,要复制的数量(字节为单位)
publicexternstaticvoidcopy_buffer_ex6(byte[]pTargetBuffer,byte[]pSourceBuffer,UInt32Amount);
[DllImport("Prodave6.dll")]//把二进制数组传换成BCD码的数组——16位数据
//参数:
要处理的数组,要处理的字节数,转换前是否先交换高低字节,转换后是否要交换高低字节
//InBytechange为1则转换BCD码之前,先交换高低字节
//OutBytechange为1则转换BCD码之后,再交换高低字节
//如果InBytechange和OutBytechange都没有置1,则不发生高低位的交换
//16位数据BCD码值的许可范围是:
+999——-999
publicexternstaticvoidushort_2_bcd_ex6(UInt16[]pwValues,UInt32Amount,intInBytechange,intOutBytechange);//16位数据——WORD
[DllImport("Prodave6.dll")]//把二进制数组传换成BCD码的数组——32位数据
//参数:
要处理的数组,要处理的字节数,转换前是否先交换高低字节,转换后是否要交换高低字节
//InBytechange为1则转换BCD码之前,先交换高低字节
//OutBytechange为1则转换BCD码之后,再交换高低字节
//如果InBytechange和OutBytechange都没有置1,则不发生高低位的交换
//32位数据BCD码值的许可范围是:
+9999999——-9999999