http://blog.csdn.net/ohiolee/article/details/17878567在柔性制造FMS系统中,本次项目以西门子PLC300,PLC200和倍加福RFID构建自动化输送和立体仓库物流方案。
硬件:
1、一个西门子PLC300为主站,四个PLC200为从站
2、倍加福RFID IC-KP-B17-AIDA1 IDENTControl interface with Ethernet interface的识别系统
系统:
因为西门子PLC,选用WinCC做组态画面,VBS实现MES/WMS的功能。
课题:
因为首次选用带TCP/IP的倍加福RFID,需要解决其与WinCC的通讯问题。
倍加福RFID有两种方式可以进行通讯,一、DLL,通过加载和调用所提供的DLL进行通讯;二、TCP的Socket进行通讯。
首先尝试了DLL,在WinCC中是可以调用DLL的,尝试了一个简单的DLL进行测试。
在WinCC图形编辑器中添加一个按钮,2个输入输出框。在按钮点击事件中创建以下代码:
- #include "apdefap.h"
- void OnLButtonDown(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName, UINT nFlags, int x, int y)
- {
-
- #pragma code ("c:/DLL/T1_C.dll") //指定绝对路径,网上有说放在WinCC/bin目录的,好像不靠谱还是这样好。
- int aufruf1(int a); //声明函数
- #pragma code ()
-
- int value;
- value = GetTagDouble("var1"); //Return-Type: double
- SetTagDouble("result",aufruf1(value)); //Return-Type: BOOL
-
- }
其中T1_C.dll里有一个返回参数+100的aufruf1函数。详见
http://www.cnblogs.com/ohiolee/articles/3498381.html
但是倍加福提供的RFID_TCP_DLL及示例代码是C++的,没怎么弄明白要怎么声明里面的函数,还有类的问题。代码如下:
- /* in your c++ header file */
-
- #include "RFIDTCP.h"
- #pragma comment(lib, "..\\impDLL\\PF_RFID_LIB.lib")
-
- /* in your c++ CPP file */
-
- void RFID_test()
- {
- int ret;
- RFIDTCP *pRFIDTCP;
- pRFIDTCP=new RFIDTCP;
- rfidMSG *pRfidMSG;
- pRfidMSG= new rfidMSG;
- pRFIDTCP->setupDevice("192.168.40.119",2);
-
- int startAddr=0;
- int wordNum=0;
- int channel;
- int ms_BlocTimeout=1000;
- int retStatus;
-
- channel=1;
-
- pRFIDTCP->connectDevice(ms_BlocTimeout);
-
- /* CT Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidCT(channel,pRfidMSG->pMsgBuff,"21",&retStatus);
- TRACE("CT [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
-
- startAddr=0;
- wordNum=26/4+1;
-
- Sleep(500);
- /* SW Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidSW(channel,startAddr,wordNum,pRfidMSG->pMsgBuff,"zyxvwutsrqponmlkjihgfedcba",&retStatus);
- TRACE("SW [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
- /* SR Test */
-
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidSR(channel,startAddr,wordNum,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("SR [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
-
- /* QU Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidQU(channel,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("QU [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
- /* SF Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidSF(channel,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("SF [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
-
-
-
- /* EW Test */
- /*wordNum MUST <= 15 () */
- pRfidMSG->reset();
- char wContent[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ@ABCDEFGHIJKLMNOPQRSTUVWXYZ@@ABCDEFGHIJKLMNOPQRSTUVWXYZ@";
- //wordNum=(strlen(wContent)/4+1);
- wordNum=15;
- ret=pRFIDTCP->rfidEW(channel,startAddr,wordNum,pRfidMSG->pMsgBuff,wContent,&retStatus);
- pRfidMSG->reset();
- TRACE("EW [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
- for(int i=0;i<5;i++)
- {
- pRfidMSG->reset();
- ret=pRFIDTCP->enhancedMSGRec(pRfidMSG);
- TRACE("EW [ret=%d][i=%d][status=%d][%s]\n",ret,i,pRfidMSG->status,pRfidMSG->pString);
- }
-
- /* QU Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidQU(channel,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("QU [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
- /* ER Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidER(channel,startAddr,wordNum,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("ER [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
- pRfidMSG->reset();
- for(int i=0;i<5;i++)
- {
- pRfidMSG->reset();
- ret=pRFIDTCP->enhancedMSGRec(pRfidMSG);
- TRACE("ER [ret=%d][i=%d][status=%d][%s]\n",ret,i,pRfidMSG->status,pRfidMSG->pString);
- }
-
- /* QU Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidQU(channel,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("QU [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
-
- /* EF Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidEF(channel,startAddr,wordNum,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("EF [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
- pRfidMSG->reset();
- for(int i=0;i<5;i++)
- {
- pRfidMSG->reset();
- ret=pRFIDTCP->enhancedMSGRec(pRfidMSG);
- TRACE("EF [ret=%d][i=%d][status=%d][%s]\n",ret,i,pRfidMSG->status,pRfidMSG->pString);
- if(ret>6)
- {
- TRACE("[");
- for(int j=0;j<(ret-6); j++)
- TRACE("0x%02x ",(unsigned char )*(pRfidMSG->pString+j));
- TRACE("]\n");
- }
- }
-
- /* QU Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidQU(channel,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("QU [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
-
-
- /* RS Test */
- pRfidMSG->reset();
- ret=pRFIDTCP->rfidRS(channel,pRfidMSG->pMsgBuff,&retStatus);
- TRACE("RS [ret=%d][%s][status=%d]\n",ret,pRfidMSG->pString,retStatus);
-
-
-
- pRFIDTCP->disconnectDevice();
- delete pRFIDTCP;
- delete pRfidMSG;
-
- }
最终没能实现。要求厂家提供VB示例代码也没能给过来。
转而研究了其Socket的通讯方式。其报文格式如下:
- 报文格式请参看手册26页,27页。报文格式是这样的
-
- *****************************************************
- CT格式
-
- 00:06:04:02:30:33 (6个字节的字符)
- 00:06 报文总长度
- 就是CT的命令格式,0x 10是读 就是 SR命令, 000610220000
- 通道1,0000 0010 红色标记的就是通道的编号
- 30:33 Tag type (IPC03)
- 发生命令以后,控制器会回复一个6个字节的确认短报文,一个6个或者6个字节以上的回复报文。
-
- 确认短报文:
- 00:06:04:02:FF:01
- 00:06报文总长度
- 重复一下命令(CT)
- 重复受到的命令
- FF Status FFh (说明正在执行)
- 回复的计数器 到0xff以后会重新开始计数
-
- 回复报文
-
- 00:06:04:02:00:02
- 00:06报文总长度
- 04重复一下命令(CT)
- 02重复受到的命令
- Status 0 (0表示命令成功完成,如果不是0,可以参考10 Troubleshooting 的10.1 Fault/Status messages P114. 有返回错误码的解释)
- 02回复的计数器
-
- *****************************************************
- 单次写SW 命令:
- 0A 40 12 00 01 39 38 37 36
- 0A报文总长度
- 40命令(SW)
- (0001 0010)红色表示写一个双字,也就是4个字节
- 01 表示起始地址是1.也就从地址0 偏移一个双字(4个字节)
- 38 37 36 表示写入的数值,这里就是ASCII :9876
- SW的确认短报文
- 06 40 12 ff 67
- SW的回复报文
- 06 40 02 00 68
-
- **************************************************
-
- 单次读SR 命令:
- 00:06:10:22:00:00
- 00:06 报文总长度
- 10命令(SR)
- (0010 0010)Word number (2) / Channel (1), toggle bit (0)
- 00:00 读起始地址(0000)
-
- 确认短报文
- 00:06:10:22:FF:01
- 00:06报文总长度
- 10重复一下命令(SR)
- (0010 0010)Word number (2) / Channel (1), toggle bit (0)
- FF Status FFh (processing command)
- 01回复的计数器
-
- 回复报文
-
- 00:0E:10:22:00:02:31:32:33:34:35:36:37:38
- 00:0E报文总长度(14 bytes)
- 10重复一下命令(SR)
- (0010 0010)Word number (2) / Channel (1), toggle bit (0)
- Status 0 (成功运行)
- 回复的计数
- 31:32:33:34:35:36:37:38 读出数据
-
- 注意,:冒号表示一个字节间的分割,但是TCP传输报文中,没有冒号的。
拿到这些资料,首先面对的问题是WinCC中没有WinSock控件,网上找到了一篇《关于在WINCC中使用WinSock控件进行TCP_IP通讯的例程》的文档,介绍的比较详细。
一、在WINCC中使用WinSock控件进行TCP_IP通讯的例程
1、WinSock控件注册
在WinCC中使用WinSock控件前,需要先进行注册。下载地址:http://down5.cr173.com//soft1/MSWINSCK.rar
注册方法如下:
使用记事本新建一个后缀名为bat的文件,编辑文件,加入以下文本:
Copy /y MSWINSCK.OCX %windir%\system32\
regsvr32/s %windir%\system32\MSWINSCK.OCX
保存文件退出。双击执行即可。(下载地址中提供了安装.bat文件,双击即可)
2、在WinCC中添加WinSock控件
在WinCC图形编辑器中打开需要显示服务器数据的画面,选择“对象选项板”的“控件”选项卡,在选项卡中选择“添加/删除”,在“选择OCX控件”对话框中选择“Microsoft WinSock Control, version 6.0”进行注册。
在对象选项板中的WinSock控件拖入画面中。
在实施到服务器上时,碰到了以下问题。
“未找到许可证信息(winsock)”
网上找到了一个神解决方案:
打开注册表,找到HKEY_CLASSES_ROOT下的Licenses,在他下面添加一个项,并且命名为2c49f800-c2dd-11cf-9ad6-0080c7e7b78d,然后修改这个项的默认值为:mlrljgrlhltlngjlthrligklpkrhllglqlrk,关闭注册表编辑器,再次启动WINCC即可。
这不是WinCC的许可问题,而是WinSock的注册问题。
二、因为需要做数据转换,需导入ads.dll,下载 ArrayConvert.exe 文件。
以下文件是可从 Microsoft 下载中心下载:
收起这个图片展开这个图片Download the arrayconvert.exe package now. (http://download.microsoft.com/download/exch55/sample/55/win98/en-us/arrayconvert.exe)
打开您下载 ArrayConvert.exe 文件文件夹,然后双击 ArrayConvert.exe 文件。
在 解压缩到文件夹 框中键入 C:\ArrayConvert,然后单击 解压缩。
单击 确定,然后单击 关闭。
单击 开始,单击 运行,键入 regsvr32 C:\ArrayConvert\ads.dll,然后按 ENTER 键。
单击 确定。
- '-------------------------------------------------------------------------------
- ' 连接倍加福RFID
- '-------------------------------------------------------------------------------
- Function RFID_Connect
- Dim Obj_SockClient
- Dim cnvt
- Dim Tag_intTXStatus
- Dim i,j
- Dim Tag_f,Tag_b
-
- ScreenItems("msg").Text = "Obj_SockClient init" & Chr(13) & Chr(10) & ScreenItems("msg").Text
- '获得当前窗口中的WinSock对象
- Set Obj_SockClient = ScreenItems("objSock")
-
- '这是一个转换DLL,后面还要讲到
- Set cnvt = CreateObject("ADS.ArrayConvert")
-
- '以下是建立连接前的参数初始化工作,远程地址和端口号根据实际情况修改,此处设置是为了方便测试工作,可使用TCP&UDP测试工具进行测试。
- 'Obj_SockClient.Protocol = "sckTCPProtocol"
- 'Obj_SockClient.RemoteHost = "192.168.1.222"
- 'Obj_SockClient.RemotePort = 10000
- ScreenItems("msg").Text = "Obj_SockClient.Protocol=" & Obj_SockClient.Protocol & Chr(13) & Chr(10) & ScreenItems("msg").Text
- ScreenItems("msg").Text = "Obj_SockClient.RemoteHost=" & Obj_SockClient.RemoteHost & Chr(13) & Chr(10) & ScreenItems("msg").Text
- ScreenItems("msg").Text = "Obj_SockClient.RemotePort=" & Obj_SockClient.RemotePort & Chr(13) & Chr(10) & ScreenItems("msg").Text
- ScreenItems("msg").Text = "Obj_SockClient.State=" & Obj_SockClient.State & Chr(13) & Chr(10) & ScreenItems("msg").Text
-
- '连接没有建立、连接错误、同级人员正在关闭连接,这三种情况下,先关闭连接,再尝试建立连接。
- If (Obj_SockClient.State = 0) Or (Obj_SockClient.State = 9) Or (Obj_SockClient.State = 8) Then
- Obj_SockClient.Close
- Obj_SockClient.Connect
- End If
- ScreenItems("msg").Text = "Obj_SockClient.Connect=" & Obj_SockClient.State & Chr(13) & Chr(10) & ScreenItems("msg").Text
-
-
- End Function
- Sub DataArrival(ByVal Item, ByVal bytesTotal)
- On Error Resume Next
-
- Dim cnvt
- Dim obj_OutText
- Dim strReceive
- Dim strFromRec
- Dim strTest
-
- Set cnvt = CreateObject("ADS.ArrayConvert")
- Set obj_OutText = ScreenItems("objOutText")
-
- ' MsgBox "DataArrival:bytesTotal=" & bytesTotal
- strFromRec = ""
- Set strReceive = Null
- '从接收缓冲区取得数据,并清空缓冲区
- Item.GetData strReceive
-
- strFromRec = cnvt.CvOctetStr2vHexStr(strReceive)
- 'obj_OutText.Text = "strFromRec=" & strTest & Chr(13) & Chr(10) & obj_OutText.Text
- If Len(strFromRec) > 0 Then
- getDataArrival(strFromRec)
- End If
-
- End Sub
|