您的位置:首页 > 其它

CHATCC流程

2015-11-20 12:13 239 查看
根据<HLA仿真程序设计》第六章逐步解析。

顺序:

一,创建数据:

1,创建RTI大使对象

 args.push_back( wstring( L"crcHost = " ).append( host ) );

  args.push_back( wstring( L"crcPost = " ).append( port ) );

  RTIambassadorFactory * rtiAmbassadorFactory = new RTIambassadorFactory();

  mpRtiAmb  = rtiAmbassadorFactory->createRTIambassador( args );

host是服务器地址,port为端口号。不同的端口号,可以创建不同的联邦执行。即同一个服务器可以创建不同的联邦执行。

2,创建联邦大使

这个在run()函数里没有,但是

class ChatCCFederate : public BaseFederateAmbassador

class  BaseFederateAmbassador : public FederateAmbassador

可知,chatCCFederate是继承自联邦大使类,调用是在main()里面

ChatCCFederate* chatCCFederate = new ChatCCFederate();

联邦成员包含联邦大使和RTI大使。为什么需要2个大使呢?RTI大使是联邦成员放在RTI的大使,联邦大使是RTI放在联邦里的大使,RTI大使要求RTI提供服务,主动动作。联邦大使要求联邦成员响应。,联邦大使写回调函数,被动动作。类似于信号与槽的关系,联邦成员通过RTI大使发信号给RTI,RTI通过联邦大使要求联邦成员调用回调函数响应。联邦成员要求RTI提供服务,RTI需要联邦成员响应。RTI和联邦成员互派大使。

RTI大使调用服务时,所引起的回调函数是固定的,至于哪个联邦成员响应,则根据订购和发布情况决定,谁发布了谁相应。

3,初始化成员的仿真对象,

不知所云,是说的chat.xml么?

二,创建联邦执行

mpRtiAmb->createFederationExecution( fedExec, fullPathFDD );

如果两个RTI大使对象同时创建联邦执行,则第一个有效,其他的创建会catch到异常。可以加入该联邦执行。就好比都在教室A开会B,那么先创建的就召开会议,其他人参加就行。



1,加入联邦执行

this->mhFederateID = this->mpRtiAmb->joinFederationExecution( fedName, fedExec,

   * this,

   _mTimeFactory,

   _mIntervalFactory );

从此开始,就建立了个传话的联邦大使this,通过回调函数传话。_mTimeFactory是时刻。_mIntervalFactory是间隔。

2,若加入异常则退出程序

其实,每一步都有异常处理,异常的catch,看看RTI相应的父类有哪些即可。

四,声明公布/订购关系

公布/订购关系是只发布和订购感兴趣的交互类和对象类,有利于减少网络流量。

两种过滤:基于类的过滤和基于实例的过滤,而声明管理是基于类的过滤。

1,书上是先对象类,再交互类,但是例子中相反,这个无所谓,因为都是XML文件中,先读取哪个后读取哪个不是问题,反正还没处理。

(1)交互类相关

《1》获取交互类句柄值,就是说联邦成员之间要干啥。这里应该是发消息。

 _messageId = mpRtiAmb->getInteractionClassHandle(L"Communication");

这个"Communication"在chat.xml中,出处

<interactionClass name="Communication" sharing="Publish" dimensions="NA" transportation="HLAreliable" order="Receive" semantics="Communication">

<parameter name="Message" dataType="HLAunicodeString" semantics="Contents of message"/>

<parameter name="Sender" dataType="HLAunicodeString" semantics="The name of the person that sent the message."/>

</interactionClass>

《2》获取交互参数句柄值

  _parameterIdText = mpRtiAmb->getParameterHandle(_messageId, L"Message");

  _parameterIdSender = mpRtiAmb->getParameterHandle(_messageId, L"Sender");

在《1》中已经看到了chat.xml中有两个交互参数名称:"Message"和"Sender"

这两个值是RTI::ParameterHandle类型,即交互参数句柄类。

《3》公布交互类

 mpRtiAmb->publishInteractionClass(_messageId);

《4》订购交互类

  mpRtiAmb->subscribeInteractionClass(_messageId);

(2)对象类相关

《1》为每一对象类创建句柄集

 AttributeHandleSet _theAttributes;

《2》获取对象类句柄值

 _participantId = mpRtiAmb->getObjectClassHandle(L"Participant");

仍然看下chat.xml,找到对象类

<objectClass name="Participant" sharing="Neither">

<attribute name="Name" dataType="HLAunicodeString" dimensions="NA" transportation="HLAreliable" order="Receive"/>

</objectClass>

《3》获取对象类属性句柄值

 _attributeIdName = mpRtiAmb->getAttributeHandle(_participantId, L"Name");

在《2》中看到,有个叫“Name"的属性名

《4》公布对象类

_theAttributes.insert(_attributeIdName);

  mpRtiAmb->publishObjectClassAttributes(_participantId, _theAttributes);

《5》订购对象类

  mpRtiAmb->subscribeObjectClassAttributes(_participantId, _theAttributes);

这里注意的是,类具有继承关系

比如

<class A.........>

<class B...>

</classB>

</classA>

则B类实际上是A.B类

那么属性值和参数值有什么区别呢?

属性值是长久保存的,交互参数值是一次性的。

比如,对于炮弹爆炸,有两个参数:位置和爆炸当量,

爆炸这个动作是个交互的,发出一次即可。

如果认为参数值也可以,大不了加个BOOL量,是否爆炸,3个属性值也未尝不可。

但是,如果100个炮弹呢?参数表将会很长。

五,确定联邦成员的时间推进策略,

这里是默认的,

六,注册对象实例

这里通过输入值,保存成功后注册,除去异常情况,如下

  do

{

  cin.getline(tmpUsername, 128, '\n');

   string2wstring(_username,tmpUsername);

    mpRtiAmb->reserveObjectInstanceName(_username);

  } while (!_reservationSucceeded);

七,请求时间推进。

这个例子没有这项,因为只有发送消息,

八,仿真推进

1,运行联邦成员的仿真模型

不知所云,可能是那个while语句发送信息吧

2,更新对象实例属性值

这里再复习下,对象实例的属性句柄在哪里。

  _participantId = mpRtiAmb->getObjectClassHandle(L"Participant");

  _attributeIdName = mpRtiAmb->getAttributeHandle(_participantId, L"Name")

这里只有一个属性句柄"Name“,也就是名字

是的,就是_attributeIdName

接着往下看,又设定了个属性句柄集,加入该属性句柄,说不准有N多属性句柄

  AttributeHandleSet _theAttributes;

  _theAttributes.insert(_attributeIdName);

然后再订购和发布对象类句柄及其属性句柄集就是大的类和大的属性句柄集合。

  mpRtiAmb->subscribeObjectClassAttributes(_participantId, _theAttributes);

  mpRtiAmb->publishObjectClassAttributes(_participantId, _theAttributes);

那怎么找单个对象实例呢?再往前看,原来早就注册了。

 _userId = mpRtiAmb->registerObjectInstance(_participantId, _username);

简言之,就是对象实例句柄_userId,就是对象类句柄_participantId指定了名称_username构成。

void ChatCCFederate::

provideAttributeValueUpdate(

       ObjectInstanceHandle const & theObject,

       std::auto_ptr< AttributeHandleSet > theAttributes,

       UserSuppliedTag const & theUserSuppliedTag)

       throw (

       ObjectInstanceNotKnown,

       AttributeNotRecognized,

       AttributeNotOwned,

       FederateInternalError)

{

 AttributeHandleSet *attributeHandleSet = (AttributeHandleSet*)theAttributes.get();

 set<AttributeHandle>::iterator iter;

 iter = attributeHandleSet->find(_attributeIdName);

 if ((theObject == _userId) && (attributeHandleSet->count(_attributeIdName)!=0))

 {

  pthread_t thread;

  pthread_create( &thread, NULL, updateMyName, this);

 }

}

意思是当为该对象实例时,且该对象实例属性句柄值在属性句柄集中存在时,则更新属性。

这里对属性句柄集合的每个成员逐一调用了updateMyName()函数,

void* ChatCCFederate::updateMyName(void * args)

{

  ChatCCFederate* fed = (ChatCCFederate*)args;

  unsigned int usernamelength;

  char* hlausername = string2hlaStringBytes(fed->_username, usernamelength);

  AttributeHandleValueMap attributes;

  attributes[fed->_attributeIdName] = AttributeValue(hlausername, usernamelength);

  fed->mpRtiAmb->updateAttributeValues(fed->_userId, attributes, UserSuppliedTag(0, 0));

 }

 也就是说通过provideAttributeValueUpdate()来更新的属性实例集中的每个对象类实例属性值

简而言之,

HLA字符串  = string2hlaStringBytes(发送内容字符串,HLA字符串长度);

再加上个AttributeHandleValueMap类型的attributes

attributes[属性句柄】 = AttributeValue( hla返回字符,HLA字符串字节数】,

最后更新下该对象实例的attributes,贴上标签UserSuppliedTag(0, 0).

看似比较复杂。实际上细想起来简单。

你要更新这个对象实例的某个属性句柄,你就得先找到这个对象实例(( _userId) ,并且在属性句柄集中找到这个属性句柄吧set<AttributeHandle>::iterator iter;

 iter = attributeHandleSet->find(_attributeIdName);。

如果找到了,再往下走,if ((theObject == _userId) && (attributeHandleSet->count(_attributeIdName)!=0))

更新把,实际上就是个将属性值从明文(发送端)到HLA密文(供RTI传输)的过程,HLA密文有两个值,(长度和字符串地址),知道字符串从哪里开始了,多少长度了,自然就能提取出来了。

如果明文传递,则用MAP的话,(属性句柄,属性值);现在用HLA密文了,所以first仍然是属性句柄,second就是AttributeValue( hla返回字符串地址,HLA字符串长度】,

明文到HLA密文的传递过程是用参数HLA字符串  = string2hlaStringBytes(发送内容字符串,HLA字符串长度);

最后更新时,肯定要根据这个对象实例句柄去更新这个HLA密文属性MAP了,再贴上标签用以辨识,OK了。

其实这么一想,too easy,

3,发送交互实例,

类似地,对于

  ParameterHandleValueMap parameters;

   unsigned int messagelength;

   char* hlaMessage = string2hlaStringBytes(_message, messagelength);

   unsigned int namelength;

   char* hlaName = string2hlaStringBytes(_username, namelength);

   parameters[_parameterIdText] = ParameterValue(hlaMessage, messagelength);

   parameters[_parameterIdSender] = ParameterValue(hlaName, namelength);

 mpRtiAmb->sendInteraction(_messageId, parameters, UserSuppliedTag(0, 0));

与2类似,只是参数句柄代替了属性句柄,给了交互句柄和参数句柄值MAP的更新,更新方法不是provideupdate()而是sendInteraction(),不再赘述。

4,根据需要创建/删除对象实例

5,根据需要转移/接收实例属性所有权

6,根据需要公布/取消公布、订购/取消订购对象类/交互类

7,根据需要改变联邦成员的时间推进策略,

4-7在本例中没有涉及到,毕竟是个小例子

九,退出联邦执行

this->mpRtiAmb->resignFederationExecution( RTI::CANCEL_THEN_DELETE_THEN_DIVEST );

十,撤销联邦执行

this->mpRtiAmb->destroyFederationExecution( this->GetFedExecName() );

以上就是全过程,大致流程。

然而,还有几个回调函数还在遗留。

1,关于对象实例更新:provideAttributeValueUpdate()和reflectAttributeValues()

(1)provideAttributeValueUpdate():当其他联邦成员针对本地联邦成员已经公布的对象类或对象实例明确调用了requestClassAttributeValueUpdate()或requestObjectAttributeValueUpdate()服务时,比如,迟到的联邦成员加入联邦执行后想得到某个对象实例或者某个对象类的所有实例时。

(2)reflectAttributeValues():

过程

<1>本地联邦成员注册对象实例

<2>RTI发送回调函数turnUpdatesOnForObjectInstance(),

<3>联邦成员更新指定的对象实例

,<4>RTI发送回调函数reflectAttributeValues()给该对象实例的对象类的有效订购者

在代码中如此改

 

 std::auto_ptr< AttributeHandleSet > test( new AttributeHandleSet );

 test->insert(_attributeIdName);

mpRtiAmb->subscribeObjectClassAttributes(_participantId, *test);

mpRtiAmb->publishObjectClassAttributes(_participantId, *test);

 _userId = mpRtiAmb->registerObjectInstance(_participantId, _username);

  this->turnUpdatesOnForObjectInstance( _userId,  test );

但是再开一个的时候,依然无法看到加入信息,即reflectAttributeValues()没起效果。

为什么呢?因为、注册实例对应的是发现实例,注册实例即被加入的联邦大使发现

registerObjectInstance()->discoverObjectInstance()

发布订阅属性是回调函数reflectAttributeValues的前提,就是先自己订阅了才能反射回来。自己发布了,才能反射到其他订阅了该属性的联邦。这只是声明而已,并没有参加实际动作,只是喊了一声而已。

但是,

(1)若A想要更新B类的属性值,必须要求具有B类属性值的联邦成员更新属性值,即

A联邦成员requestAttributeValueUpdate()->B属性provideAttributeValueUpdate()(这个回调函数用 mpRtiAmb->updateAttributeValues()更新了属性值----->A联邦成员reflectAttributeValues(),

或者

(2)若A想更新自身类的属性值时,则需要打包给rti后updateAttributeValues()->reflectAttributeValues(),

unsigned int usernamelength;

  char* hlausername = string2hlaStringBytes(_username, usernamelength);

  AttributeHandleValueMap attributes;

  attributes[_attributeIdName] = AttributeValue(hlausername, usernamelength);

  mpRtiAmb->updateAttributeValues(_userId, attributes, UserSuppliedTag(0, 0));

当然,订购者要解包

void ChatCCFederate::

reflectAttributeValues(

        ObjectInstanceHandle const & theObject,

        std::auto_ptr< AttributeHandleValueMap > theAttributeValues,

        UserSuppliedTag const & theUserSuppliedTag,

        OrderType const & sentOrder,

        TransportationType const & theType)

        throw (

        ObjectInstanceNotKnown,

        AttributeNotRecognized,

        AttributeNotSubscribed,

        FederateInternalError)

{

 cout <<"reflectattribute"<<endl;

 if (_knownObjects.count(theObject)==0)

 {

  map<ObjectInstanceHandle,Participant>::iterator iter;

  for (AttributeHandleValueMap::const_iterator i = theAttributeValues->begin();i!= theAttributeValues->end(); ++i) {

   AttributeHandle const & handle = i->first;

   AttributeValue const & value = i->second;

   if (handle == _attributeIdName) {

    Participant member = Participant(hlaStringBytes2string((const char*)value.data()));

    //wcout << L"[ " << member.toString() << L" has joined the chat ]" << endl;

    //cout << "> ";

    //cout <<"wo jiu caole"<<endl;

    _knownObjects[theObject] = member;

   }

  }

 }

}

这里有个_knownObjects,看看它的定义

map<ObjectInstanceHandle, Participant> _knownObjects;

可知,在每个联邦大使类里都要保存着一份对应着对象实例的MAP,为啥呢?比如,一个坦克出现了,这个事件也要被其他联邦成员感知,也就是说其他人也要看的见这辆坦克,才能做下一步动作是击毁呢?还是增援?要不没得玩了。成单机了。

订阅发布对象类属性值后就进行注册实例,并调用发现实例回调函数

registerObjectInstance()<----------->discoverObjectInstance()

类的属性没有变化(仍是Name),自然也不会引起属性值反射。

(3)注册后发现,然后更新属性值。

2,

receiveInteraction(),接收交互实例

是在发送交互实例mpRtiAmb->sendInteraction(_messageId, parameters, UserSuppliedTag(0, 0));后接收的,成对出现。

当然,发送时要先打成包用以HLA传输。

然而,这里并没有这么简单,因为发送交互实例是有两个函数的

  virtual std::auto_ptr< MessageRetractionHandle >  sendInteraction

    (InteractionClassHandle  const & theInteraction,

     ParameterHandleValueMap const & theParameterValues,

     UserSuppliedTag const & theUserSuppliedTag,

     LogicalTime const & theTime);

   virtual void sendInteraction

    (InteractionClassHandle  const & theInteraction,

     ParameterHandleValueMap const & theParameterValues,

     UserSuppliedTag const & theUserSuppliedTag)

注意,第一个函数,是有时间戳的。

3,deleteObjectInstance( )和removeObjectInstance()

(1),deleteObjectInstance()是有权删除该对象实例的联邦成员发出的主动动作

(2)removeObjectInstance()是RTI通知订购该对象实例的联邦成员的回调函数,

4,objectInstanceNameReservationSucceeded()和objectInstanceNameReservationFailed()

这两个回调函数是mpRtiAmb->reserveObjectInstanceName(_username);的回调函数,属于对象管理中的保留对象实例名,1.3中似乎没有

5, virtual

  void

  removeObjectInstance(ObjectInstanceHandle const & theObject,

  UserSuppliedTag const & theUserSuppliedTag,

  OrderType const & sentOrder)

  throw (ObjectInstanceNotKnown,

  FederateInternalError);

之所以没起作用,是因为它只是一个回调函数,必须有删除动作,即

mpRtiAmb->deleteObjectInstance( _userId,  UserSuppliedTag(0, 0));

deleteObjectInstance()->removeObjectInstance()

5,关于chat.xml

创建联邦执行的需要FOM,加入联邦的是需要SOM,SOM是订购与发布的对象类和交互类的集合。怎么写的格式呢?就是OMT。当然,SOM是FOM的子集。











内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: