一、IEC 61499简介
IEC 61499 作为工业自动化领域分布式控制系统级建模语言的标准,其第一(体系结构)、二(软件工具要求)、四(兼容文件的规则)部分的第一版于 2005 年正式发布,并在 2012 年发布第一部分的 2.0 版本,主要解决了旧版中诸如功能块执行控制表语法语义存在的歧义等一系列问题。
图1 分布式应用的功能块连接
设备是多条资源的容器,并提供这些资源与通讯网络、传感器和执行器之间的接口。这些接口提供的服务由支持分布式应用的专用资源中的SIFB完成。通讯网络把各分散设备集成为一个完整的系统。这样,分布在不同物理设备中的功能块形成了一个真正的分布式应用。
分布式指的是上图的某个功能块可以部署到某一个硬件上,多个硬件设备共同实现一个工业流程,红线代表事件流,蓝线代表数据流,各个功能块协同工作,但对于每个功能块到底是在哪里并不关心,61499协议并不对底层的具体实现方式进行定义。
1. 4diac FORTE
Eclipse Foundation 4diac project 是一个支持IEC-61499协议的开源项目,
由IDE、FORTE、功能块库等部分组成。
图2. 4diac project
2. 4diac IDE
IDE用于编辑PLC应用逻辑,下发PLC程序到硬件设备或导出PLC为.fboot文件,供PLC运行时使用。
下图是IEC-61499的DEMO,主要功能是一个实现一个opc ua服务器,对外提供10个数据点,10个数据由随机数生成器产生,随机数生成器每50ms更新一次。 demo内容下载链接在文章最后。
图3 IDE编辑IEC61499 demo
3. FORTE编译
下载FORTE源码并编译:
unzip forte_2.0.1.zip
cd forte_2.0.1
mkdir build
cd build
cmake -DFORTE_ARCHITECTURE=Posix -DFORTE_COM_ETH=ON \
-DFORTE_COM_FBDK=ON -DFORTE_COM_LOCAL=ON \
-DFORTE_TESTS=OFF -DFORTE_MODULE_CONVERT=ON \
-DFORTE_MODULE_IEC61131=ON -DFORTE_MODULE_UTILS=ON ..make –j
运行FORTE:
./forte -c 0.0.0.0:61500 -f server.fboot
-c设置PLC程序监听的地址。
-f指定启动文件,即从IDE中导出的fboot格式PLC程序。
4. server.fboot文件内容
以类似于XML文件的格式,定义PLC程序中用到的功能块和全部的连接信息。
图4 FB_RANDOM功能块到.fboot的转换
Action定义CREATE、WRITE、START、DELETE等操作。
fastWrite;<Request ID="5" Action="CREATE"><FB Name="FB_RANDOM" Type="FB_RANDOM" /></Request>
创建名为FB_RANDOM的功能块,ID为5,Type为FB_RANDOM。
fastWrite;<Request ID="6" Action="WRITE"><Connection Source="0" Destination="FB_RANDOM.SEED" /></Request>
为一个连接写入值,Source是0,Destination是FB_RANDOM.SEED。
fastWrite;<Request ID="12" Action="CREATE"><Connection Source="E_CYCLE.EO" Destination="FB_RANDOM.REQ" /></Request>
创建从E_CYCLE.EO到FB_RANDOM.REQ的连接。
fastWrite;<Request ID="19" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_1" /></Request>
创建从FB_RANDOM.VAL到PUBLISH_10.SD_1的连接。
完整的server.fboot的内容,列在本文最后。
二、PLC功能块到汇编代码的过程
区别于iec61131,比如OpenPLC的实现方式,OpenPLC是将PLC的逻辑变成中间语言的.st文件,再生成.cpp进行编译后生成 elf的可执行程序再进行执行的。
FORTE是将PLC程序导出成类似于XML格式的.fboot文件,在设备上运行的时候,由FORTE进行动态解析建立PLC逻辑。
下面分析.fboot文件在FORTE中的解析和处理流程。此处我们以FB_RANDOM功能块为例进行分析。
1.FB_RANDOM功能块
FB_RANDOM功能块的作用是生成介于0-1之间的float随机数。
图2.3 FB_RANDOM的对外接口
FB_RANDOM功能块的对外接口如下所示:
按61499的规范,功能块的左边是输入,右边是输出。
- INIT用于接收事件进行初始化。
- INITO用于对外输出初始化完毕事件,在INIT结束后发出。
- REQ用于接收外部请求事件,请求一次生成一个随机数。
- SEED即外部给的随机数种子。
- CNF是Execution Confirmation执行确认,REQ请求事件处理完毕后输出执行完毕事件。
- VAL是功能块的输出,输出生成的随机数。.fboot文件到具体逻辑的转换
2.FORTE_FB_RANDOM 的功能实现
IDE中的每一个功能块都与对应的cpp实现相关联。
如果要在IDE中自定义新的功能块,必须导出成对应的.cpp和.h文件,并加入到FORTE的源代码的src\modules目录中,重新编译FORTE才能启用此功能块。目前FORTE的这种实现方式添加新的功能块还不是特别方便。
自定义功能块功能支持ST语法或C语法,编写的程序会导出到.cpp文件中。
IEC 61499功能块按功能分为基本功能块、复合功能块、服务接口功能块(通讯功能块和管理功能块)和适配器(插件和插座)。
src\core\funcbloc.h中定义了CFunctionBlock,CFunctionBlock是所有功能块的基类。
继承此类的有4个类:
basicfb.h定义基本功能块类CBasicFB。
cfb.h定义复合功能块类CCompositeFB。
esfb.h定义服务接口功能块类CEventSourceFB。
adapter.h定义接口类CAdapter。
FB_RANDOM功能块对应的cpp类名称为:FORTE_FB_RANDOM,继承自CBasicFB。
class FORTE_FB_RANDOM: public CBasicFB{
3.FB_RANDOM.h
FB_RANDOM类继承了 CBasicFB,第一行DECLARE_FIRMWARE_FB(FORTE_FB_RANDOM)用于定义
宏展开后如下:
定义了一个static 静态函数createFB,用于生成此类的实例。
class FORTE_FB_RANDOM: public CBasicFB{DECLARE_FIRMWARE_FB(FORTE_FB_RANDOM)private:static const CStringDictionary::TStringId scm_anDataInputNames[];static const CStringDictionary::TStringId scm_anDataInputTypeIds[];CIEC_UINT &SEED() {return *static_cast<CIEC_UINT*>(getDI(0));};static const CStringDictionary::TStringId scm_anDataOutputNames[];static const CStringDictionary::TStringId scm_anDataOutputTypeIds[];CIEC_REAL &VAL() {return *static_cast<CIEC_REAL*>(getDO(0));};static const TEventID scm_nEventINITID = 0;static const TEventID scm_nEventREQID = 1;static const TForteInt16 scm_anEIWithIndexes[];static const TDataIOID scm_anEIWith[];static const CStringDictionary::TStringId scm_anEventInputNames[];static const TEventID scm_nEventINITOID = 0;static const TEventID scm_nEventCNFID = 1;static const TForteInt16 scm_anEOWithIndexes[];static const TDataIOID scm_anEOWith[];static const CStringDictionary::TStringId scm_anEventOutputNames[];static const SFBInterfaceSpec scm_stFBInterfaceSpec;FORTE_BASIC_FB_DATA_ARRAY(2, 1, 1, 0, 0);virtual void setInitialValues();void alg_INIT(void);void alg_REQ(void);static const TForteInt16 scm_nStateSTART = 0;static const TForteInt16 scm_nStateREQ = 1;static const TForteInt16 scm_nStateState = 2;void enterStateSTART(void);void enterStateREQ(void);void enterStateState(void);virtual void executeEvent(int pa_nEIID);public:FORTE_FB_RANDOM(CStringDictionary::TStringId pa_nInstanceNameId, CResource *pa_poSrcRes) : CBasicFB(pa_poSrcRes, &scm_stFBInterfaceSpec, pa_nInstanceNameId,0, m_anFBConnData, m_anFBVarsData){};virtual ~FORTE_FB_RANDOM(){};};
4.FB_RANDOM.cpp
在FB_RANDOM.cpp中是这样实现的:
#include "FB_RANDOM.h"
#ifdef FORTE_ENABLE_GENERATED_SOURCE_CPP
#include "FB_RANDOM_gen.cpp"
#endif
#include <time.h>
#include <stdlib.h>DEFINE_FIRMWARE_FB(FORTE_FB_RANDOM, g_nStringIdFB_RANDOM)const CStringDictionary::TStringId FORTE_FB_RANDOM::scm_anDataInputNames[] = {g_nStringIdSEED};const CStringDictionary::TStringId FORTE_FB_RANDOM::scm_anDataInputTypeIds[] = {g_nStringIdUINT};const CStringDictionary::TStringId FORTE_FB_RANDOM::scm_anDataOutputNames[] = {g_nStringIdVAL};const CStringDictionary::TStringId FORTE_FB_RANDOM::scm_anDataOutputTypeIds[] = {g_nStringIdREAL};const TForteInt16 FORTE_FB_RANDOM::scm_anEIWithIndexes[] = {0, -1};
const TDataIOID FORTE_FB_RANDOM::scm_anEIWith[] = {0, 255};
const CStringDictionary::TStringId FORTE_FB_RANDOM::scm_anEventInputNames[] = {g_nStringIdINIT, g_nStringIdREQ};const TDataIOID FORTE_FB_RANDOM::scm_anEOWith[] = {0, 255};
const TForteInt16 FORTE_FB_RANDOM::scm_anEOWithIndexes[] = {-1, 0, -1};
const CStringDictionary::TStringId FORTE_FB_RANDOM::scm_anEventOutputNames[] = {g_nStringIdINITO, g_nStringIdCNF};const SFBInterfaceSpec FORTE_FB_RANDOM::scm_stFBInterfaceSpec = {2, scm_anEventInputNames, scm_anEIWith, scm_anEIWithIndexes,2, scm_anEventOutputNames, scm_anEOWith, scm_anEOWithIndexes, 1, scm_anDataInputNames, scm_anDataInputTypeIds,1, scm_anDataOutputNames, scm_anDataOutputTypeIds,0, 0
};void FORTE_FB_RANDOM::setInitialValues(){SEED().fromString("0");
}void FORTE_FB_RANDOM::alg_INIT(void){
// WARNING - Don't forget to add #include <time.h>if (SEED() == 0) {srand((unsigned int) time(NULL) );} else {srand( SEED() );}
}void FORTE_FB_RANDOM::alg_REQ(void){VAL() = static_cast<TForteFloat>(rand())/static_cast<TForteFloat>(RAND_MAX);
}void FORTE_FB_RANDOM::enterStateSTART(void){m_nECCState = scm_nStateSTART;
}void FORTE_FB_RANDOM::enterStateREQ(void){m_nECCState = scm_nStateREQ;alg_REQ();sendOutputEvent( scm_nEventCNFID);
}void FORTE_FB_RANDOM::enterStateState(void){m_nECCState = scm_nStateState;alg_INIT();sendOutputEvent( scm_nEventINITOID);
}void FORTE_FB_RANDOM::executeEvent(int pa_nEIID){bool bTransitionCleared;do{bTransitionCleared = true;switch(m_nECCState){case scm_nStateSTART:if(scm_nEventREQID == pa_nEIID)enterStateREQ();elseif(scm_nEventINITID == pa_nEIID)enterStateState();elsebTransitionCleared = false; //no transition clearedbreak;case scm_nStateREQ:if(1)enterStateSTART();elsebTransitionCleared = false; //no transition clearedbreak;case scm_nStateState:if(1)enterStateSTART();elsebTransitionCleared = false; //no transition clearedbreak;default:DEVLOG_ERROR("The state is not in the valid range! The state value is: %d. The max value can be: 2.", m_nECCState.operator TForteUInt16 ());m_nECCState = 0; //0 is always the initial statebreak;}pa_nEIID = cg_nInvalidEventID; // we have to clear the event after the first check in order to ensure correct behavior}while(bTransitionCleared);
}
三、FB_RANDOM 功能块的动态加载
1. .fboot文件解析流程
.fboot文件中定义了PLC程序的功能块及相互的连接。
stdfblib\ita\DEV_MGR.cpp实现对.fboot文件的处理。通过解析.fboot文件建立各个功能块及输入和输出端口之间的连接。
DEV_MGR::parseAndExecuteMGMCommand(char *paDest, char *paCommand)
对.fboot进行文本解析,解析后的需要进行的处理保存在mCommand变量中,再执行executeMGMCommand函数进行相关资源的创建。
forte::core::SManagementCMD mCommand;
executeMGMCommand(mCommand);
在core\resource.cpp中实现资源创建。
可以看到FORTE支持动态创建,即用LUA语言进行动态创建,但暂时在.fboot文件中还没有看到lua的脚本,并且lua脚本可能也是类似的工作机制,并不能实现自定义功能块逻辑。
EMGMResponse CResource::executeMGMCommand(forte::core::SManagementCMD &paCommand){
最终执行到FORTE_FB_RANDOM的构造函数。
FB_RANDOM功能块初始化的调用堆栈如下所示:
#0 FORTE_FB_RANDOM::FORTE_FB_RANDOM (this=0x180, pa_nInstanceNameId=0, pa_poSrcRes=0x555555695119 <operator new(unsigned long)+28>) at /home/plc/forte_2.0.1/src/modules/utils/FB_RANDOM.h:66
#1 0x0000555555722445 in FORTE_FB_RANDOM::createFB (pa_nInstanceNameId=666, pa_poSrcRes=0x7ffff0004b60) at /home/plc/forte_2.0.1/src/modules/utils/FB_RANDOM.h:21
#2 0x00005555556b82cf in CTypeLib::CFBTypeEntry::createFBInstance (this=0x5555557b7020 <FORTE_FB_RANDOM::csm_oFirmwareFBEntry_FORTE_FB_RANDOM>, pa_nInstanceNameId=666, pa_poSrcRes=0x7ffff0004b60)at /home/plc/forte_2.0.1/src/core/./datatypes/../typelib.h:155
#3 0x00005555556b7e86 in CTypeLib::createFB (pa_nInstanceNameId=666, pa_nFBTypeId=666, pa_poRes=0x7ffff0004b60) at /home/plc/forte_2.0.1/src/core/typelib.cpp:118
#4 0x00005555556b33c0 in forte::core::CFBContainer::createFB (this=0x7ffff0004bd8, paNameListIt=..., paTypeName=666, paRes=0x7ffff0004b60) at /home/plc/forte_2.0.1/src/core/fbcontainer.cpp:68
#5 0x00005555556b476e in CResource::executeMGMCommand (this=0x7ffff0004b60, paCommand=...) at /home/plc/forte_2.0.1/src/core/resource.cpp:73
#6 0x00005555556b1418 in CDevice::executeMGMCommand (this=0x5555557cd870, paCommand=...) at /home/plc/forte_2.0.1/src/core/device.cpp:30
#7 0x000055555572a323 in DEV_MGR::parseAndExecuteMGMCommand (this=0x5555557cecc0, paDest=0x7ffff0005814 "fastWrite", paCommand=0x7ffff000581e "<Request ID=\"5") at /home/plc/forte_2.0.1/src/stdfblib/ita/DEV_MGR.cpp:637
#8 0x000055555572a6a3 in DEV_MGR::executeCommand (this=0x5555557cecc0, paDest=0x7ffff0005814 "fastWrite", paCommand=0x7ffff000581e "<Request ID=\"5") at /home/plc/forte_2.0.1/src/stdfblib/ita/DEV_MGR.cpp:696
#9 0x000055555572bad3 in ForteBootFileLoader::loadBootFile (this=0x7ffff724bde0) at /home/plc/forte_2.0.1/src/stdfblib/ita/ForteBootFileLoader.cpp:90
#10 0x0000555555728771 in DEV_MGR::executeEvent (this=0x5555557cecc0, paEIID=0) at /home/plc/forte_2.0.1/src/stdfblib/ita/DEV_MGR.cpp:68
#11 0x00005555556b2881 in CFunctionBlock::receiveInputEvent (this=0x5555557cecc0, paEIID=0, paExecEnv=0x5555557cdf60) at /home/plc/forte_2.0.1/src/core/funcbloc.cpp:314
#12 0x00005555556b8510 in CEventChainExecutionThread::mainRun (this=0x5555557cdf60) at /home/plc/forte_2.0.1/src/core/ecet.cpp:49
#13 0x00005555556b8454 in CEventChainExecutionThread::run (this=0x5555557cdf60) at /home/plc/forte_2.0.1/src/core/ecet.cpp:34
#14 0x0000555555695aa7 in forte::arch::CThreadBase<unsigned long, 0ul, forte::arch::EmptyThreadDeletePolicy>::runThread (paThread=0x5555557cdf60) at /home/plc/forte_2.0.1/src/arch/posix/../threadbase.tpp:69
#15 0x0000555555695324 in CPosixThread::threadFunction (paArguments=0x5555557cdf60) at /home/plc/forte_2.0.1/src/arch/posix/forte_thread.cpp:68
#16 0x00007ffff7f9a609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#17 0x00007ffff7b90293 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
2.FB_RANDOM类是如何注册的?
从调用堆栈可以看到,.fboot文件解析后,FORTE根据.fboot文件的内容进行了动态创建功能块,但大家是不是有一个疑问,就是从Cpp语法角度,如何根据字符串创建一个类对象?
就像如下的代码,但下面的代码与FORTE的运行情况还不太一样,FORTE程序并不引用功能块的.h文件,main函数无法拿到FuncA的定义,那功能块又是如何动态创建的呢?
就是说,程序在运行时如何知道有这个类FuncA存在?
Class Base{
};Class FuncA : public Base {FuncA(){printf("FuncA");}
};Class FuncB : public Base {FuncB(){printf("FuncB");}
};int main(){Base* ptr = nullptr;string str="FuncA";if(str== "FuncA")ptr = new FuncA();else if(str== "FuncB")ptr = new FuncB();return 0;
}
在FB_RANDOM.cpp中有如下宏定义
DEFINE_FIRMWARE_FB(FORTE_FB_RANDOM, g_nStringIdFB_RANDOM)
宏展开后是这样的:
定义了一个const CTypeLib::CFBTypeEntry 类型的static变量 , static定义在FB_RANDOM.h中。
FORTE_FB_RANDOM::csm_oFirmwareFBEntry_FORTE_FB_RANDOM(( xxx), xxx,xxx);
CTypeLib::CFBTypeEntry 这个类在初始化的时候,在CFBTypeEntry 构造函数中执行 CTypeLib::addFBType函数,将自己的相关信息(包括id,接口相关函数)加入到静态变量中。
//! The base class for all function block types entries in the type lib.class CFBTypeEntry : public CSpecTypeEntry{public:CFBTypeEntry(CStringDictionary::TStringId pa_nTypeNameId, TFunctionBlockCreateFunc pa_pfuncCreateFB, const SFBInterfaceSpec* paSocketInterfaceSpec);virtual ~CFBTypeEntry(void);virtual CFunctionBlock *createFBInstance(CStringDictionary::TStringId pa_nInstanceNameId, CResource *pa_poSrcRes){return m_pfuncFBCreationFunc( pa_nInstanceNameId, pa_poSrcRes);}private:TFunctionBlockCreateFunc m_pfuncFBCreationFunc;};
CTypeLib::CFBTypeEntry::CFBTypeEntry(
CStringDictionary::TStringId pa_nTypeNameId,TFunctionBlockCreateFunc pa_pfuncCreateFB,const SFBInterfaceSpec* paSocketInterfaceSpec):CSpecTypeEntry(pa_nTypeNameId, paSocketInterfaceSpec),m_pfuncFBCreationFunc(pa_pfuncCreateFB)
{CTypeLib::addFBType(this); //这一行
}void CTypeLib::addFBType(CFBTypeEntry *pa_poFBTypeEntry)
{if (0 == findType(pa_poFBTypeEntry->getTypeNameId(), m_poFBLibStart))
{if(m_poFBLibStart == 0) {m_poFBLibStart = pa_poFBTypeEntry;} else {m_poFBLibEnd->m_poNext = pa_poFBTypeEntry;//pa_poFBTypeEntry加入静态变量m_poFBLibEnd中}m_poFBLibEnd = pa_poFBTypeEntry;}
}CTypeLib::CFBTypeEntry *CTypeLib::m_poFBLibStart = 0;
CTypeLib::CFBTypeEntry *CTypeLib::m_poFBLibEnd = 0;//.h头文件中的定义
class CTypeLib
{static CFBTypeEntry *m_poFBLibStart, *m_poFBLibEnd;//!< pointer to the begin of the firmware fb library list//!<pointer to the end of the firmware fb library list//静态变量的定义
}
在创建此类型的时候,首先执行findType()函数,进行查找,找到指定的TypeNameId,如果找不到,就报错,无法运行此程序。
void CTypeLib::addFBType(CFBTypeEntry *pa_poFBTypeEntry) {if (0 == findType(pa_poFBTypeEntry->getTypeNameId(), m_poFBLibStart)) {if(m_poFBLibStart == 0) {m_poFBLibStart = pa_poFBTypeEntry;} else {m_poFBLibEnd->m_poNext = pa_poFBTypeEntry;}m_poFBLibEnd = pa_poFBTypeEntry;}
}
c程序运行时,并不是从main开始运行,在main()运行前,会有一系列的函数进行一些初始化工作,static静态变量都是在这一时期初始化的。具体可参见下面的链接。
因此,在main函数运行前,会初始化此处的CTypeLib::CFBTypeEntry 类型的变量,将FB_RANDOM类的相关信息添加到m_poFBLibStart 开始的链表中。即可实现在解析.fboot文件时,根据ID查找对应的类。并通过调用对应的createFB()生成对应的对象。
linux编程之main()函数启动过程_gary_ygl的专栏-CSDN博客_linux启动main函数
附件
server.fboot文件内容:
;<Request ID="2" Action="CREATE"><FB Name="fastWrite" Type="EMB_RES" /></Request>
fastWrite;<Request ID="3" Action="CREATE"><FB Name="E_CYCLE" Type="E_CYCLE" /></Request>
fastWrite;<Request ID="4" Action="WRITE"><Connection Source="T#50ms" Destination="E_CYCLE.DT" /></Request>
fastWrite;<Request ID="5" Action="CREATE"><FB Name="FB_RANDOM" Type="FB_RANDOM" /></Request>
fastWrite;<Request ID="6" Action="WRITE"><Connection Source="0" Destination="FB_RANDOM.SEED" /></Request>
fastWrite;<Request ID="7" Action="CREATE"><FB Name="PUBLISH_10" Type="PUBLISH_10" /></Request>
fastWrite;<Request ID="8" Action="WRITE"><Connection Source="1" Destination="PUBLISH_10.QI" /></Request>
fastWrite;<Request ID="9" Action="WRITE"><Connection Source="opc_ua[WRITE;/Objects/folder1/var1;/Objects/folder1/var2;/Objects/folder1/var3;/Objects/folder1/var4;/Objects/folder1/var5;/Objects/folder1/var6;/Objects/folder1/var7;/Objects/folder1/var8;/Objects/folder1/var9;/Objects/folder1/var10]" Destination="PUBLISH_10.ID" /></Request>
fastWrite;<Request ID="10" Action="CREATE"><FB Name="E_DELAY_2" Type="E_DELAY" /></Request>
fastWrite;<Request ID="11" Action="WRITE"><Connection Source="T#2s" Destination="E_DELAY_2.DT" /></Request>
fastWrite;<Request ID="12" Action="CREATE"><Connection Source="E_CYCLE.EO" Destination="FB_RANDOM.REQ" /></Request>
fastWrite;<Request ID="13" Action="CREATE"><Connection Source="START.WARM" Destination="FB_RANDOM.INIT" /></Request>
fastWrite;<Request ID="14" Action="CREATE"><Connection Source="START.COLD" Destination="FB_RANDOM.INIT" /></Request>
fastWrite;<Request ID="15" Action="CREATE"><Connection Source="FB_RANDOM.INITO" Destination="PUBLISH_10.INIT" /></Request>
fastWrite;<Request ID="16" Action="CREATE"><Connection Source="FB_RANDOM.CNF" Destination="PUBLISH_10.REQ" /></Request>
fastWrite;<Request ID="17" Action="CREATE"><Connection Source="PUBLISH_10.INITO" Destination="E_DELAY_2.START" /></Request>
fastWrite;<Request ID="18" Action="CREATE"><Connection Source="E_DELAY_2.EO" Destination="E_CYCLE.START" /></Request>
fastWrite;<Request ID="19" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_1" /></Request>
fastWrite;<Request ID="20" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_2" /></Request>
fastWrite;<Request ID="21" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_3" /></Request>
fastWrite;<Request ID="22" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_4" /></Request>
fastWrite;<Request ID="23" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_5" /></Request>
fastWrite;<Request ID="24" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_6" /></Request>
fastWrite;<Request ID="25" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_7" /></Request>
fastWrite;<Request ID="26" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_8" /></Request>
fastWrite;<Request ID="27" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_9" /></Request>
fastWrite;<Request ID="28" Action="CREATE"><Connection Source="FB_RANDOM.VAL" Destination="PUBLISH_10.SD_10" /></Request>
fastWrite;<Request ID="28" Action="START"/>
所用demo下载
https://download.csdn.net/download/v6543210/80644938