g87616758 发表于 2015-11-26 09:14:17

计算机仿真中的HLA技术中的餐馆例子分析 (1) Production

  从书中例子来看,这个餐馆例子是第一个真正意义上的仿真例子。因为前两个例子的共同点是:所有联邦都运行同一个程序,仅仅是通过运行设置(比如有的订阅,有的发布),才表现为不同的联邦。而餐馆联邦直接把联邦的程序分开,不同的联邦成员具有不同的程序代码。
  
  另一个方面,书上对这个例子有较详细的解释。这也为这个例子的分析带来了很多便利。
  
  书上把production联邦作为例子进行了详细分析,我们这边也先从production联邦入手。其实后面可以看到,production,transport,comsumption三个联邦代码非常类似,甚至manager和viewer的代码也类似,只是pruduction联邦,consumption联邦使用了NER的时间调度方式,后manager,viewer和transport联邦使用了TAR的时间调度方式。
  
  总体来看,每个联邦都有几个java文件组成,以production联邦为例:Production.java,ProductionFrame.java, ProductionInternalError.java, ProductionNames.java,FedAmbImpl.java(production)。其中,frame是GUI类,FedAmbImpl包括所有回调函数。所有对RTI的函数调用,都在Production.java完成,使用_rti变量。
  --------------------------------------------------------------------------------------------------------
  主函数很简单
  
  public static void main(String[] args) {
  Properties props = parseArgs(args);
  loadProperties(props);
  Production production = new Production(props);
  production.mainThread();
  }
  主要功能是从命令行或者配置文件中读取相关参数,产生production对象,运行mainThread线程
  ---------------------------------------------------------------------------------------------------------
  
  主要的功能都在mainThread函数中
  
  (1)取得相关配置信息,判断是否有federation execution实例运行,没有则创建,然后加入join
  
  (2)设置时间受限和时间调节。这里首先设置了时间受限,同时在设置时间受限和时间调节时,使用了barrier机制来协调各个线程(barrier机制在例子中多次使用,这里不讨论其细节)。
  
  (3)获取相关对象类,属性,交互类的handle,存储下来以备后续使用。--getHandles();
  
  (4)发布对象类及其相关属性,包括Serving类及其position属性和type属性;Chef对象类及其position,chefStage和servingName
  
  (5)订阅对象类及其相关属性,包括Boat对象类及其position,spaceAvailable和cargo属性,以及SimulationEnds交互类和TransferAccepted交互类。
  
  (6)处理第一个同步点ReadyToPolulate,发出同步点已经achieved请求。使用barrier机制阻塞线程,结束阻塞后输出已经同步信息。
  
  (7)根据预先给定的chef个数,逐个注册chef对象实例,并把所注册的chef实例加入本地表格_chefTable中。同时,把每个chef的信息进行更新_rti.updateAttributeValues(chefHandle, sa, null, sendTime);然后随机产生一个类型的sushi,并把该sushi加入_internalQueue--_internalQueue.enqueue(new FinishMakingSushiEvent(eventTime, serial, type));(注意这里,eventTime从0.0开始,每一次增加的时间量是做某种sushi的时间)。 注意,上述所有活动都在循环体内。也就是说,对每个chef,都要做后续的所有工作。同时还要注意,制作不同的sushi的时间不同,但是_internalQueue.enqueue方法将保证制作时间越短的sushi越排在前面。这里的物理含义是:6个chef一起(从_logicTime=0.0时刻)做sushi,则制作时间越短的sushi越早制作好。
  
  (8)处理第二个同步点ReadyToRun,发出同步点已经achieved请求。使用barrier机制阻塞线程,结束阻塞后输出已经在该同步点同步的信息。
  
  (9)调用rti函数enableAsynchronousDelivery,目的是instructs the LRC to begin delivering receive-ordered events to the federate even while no time-advancement service is in progress,其实就是production这个时间受限联邦成员要求在时间批准状态下也能接收RO事件。
  
  (10)进入时间推进的主循环。循环结束条件是该联邦收到simulationEnds交互。基本的消息循环流程:production联邦收到的所有RTI的回调都会进入callbackQueue排队。然后在消息循环里面逐一取出消息来处理。
  
  下面是主循环代码:
  timeLoop:
  while (!_simulationEndsReceived) {
  _userInterface.setTimeStateAdvancing();
  LogicalTime timeToMoveTo = _internalQueue.getTimeAtHead();
  //_userInterface.post("NER to " + timeToMoveTo);
  _rti.nextEventRequest(timeToMoveTo);
  boolean wasTimeAdvanceGrant;
  do {
  Callback callback = _callbackQueue.dequeue();
  wasTimeAdvanceGrant = callback.dispatch();
  if (_simulationEndsReceived) break timeLoop;
  } while (!wasTimeAdvanceGrant);
  updateInternalStateAtNewTime();
  }
  主循环流程解释如下:
  
  
  while(没有收到simulationEnds交互)
  {
  界面显示联邦开始时间推进;
  从_internalQueue队列中取出队头事件所包含的时间(对production联邦而言,开始运行时,internalQueue中存储的其实就是从0.0时刻开始制作完成的第一个sushi的时间);
  请求时间推进NER到上一条语句所取出的时间上,对production联邦而言,其实就是取出做完一个sushi后的logicTime时间。
  do{
  从callbackQueue中取出一个事件,返回给callback对象;
  调用callback对象的dispatch函数,返回一个布尔量wasTimeAdvanceGrant
  判断是否收到_simulationEnds交互,如果收到,退出循环;
  while (!wasTimeAdvanceGrant);
  从internalQueue中取出所有小于等于当前时间的事件处理掉,此例中就是取出最短时间做好的sushi,注册serving对象实例,并把serving对象实例加入_serving结构中;
  根据当前_chefTable中所有的chef信息发送updateAttributeValues请求;
  根据当前_serving中所有的sushi信息,发送updateAttributeValues请求;
  }      
  
  
  (11)处理第三个同步点ReadyToResign,发出同步点已经achieved请求。使用barrier机制阻塞线程,结束阻塞后输出已经在该同步点同步的信息。然后结束该联邦的运行。
  ---------------------------------------------------------------------------------------------------------------------------
  下面是运行5个联邦后,production.java输出的一些信息以及对这些输出的解释:
  
  
  RTIambassador created
  Federation execution restaurant_1 already exists.
  Joined as federate 8
  Enabling time constraint...
  ...constraint enabled at time<0.0>
  Enabling time regulation...
  ...regulation enabled at time<0.0>
  This point is after subscribe...
  Waiting for ReadyToPopulate...
  �5.10)startRegistrationForObjectClass:3
  �5.10)startRegistrationForObjectClass:6
  ...federation synchronized.
  type0increase byinterval<1.5>    //这里是产生的6个sushi实例,信息包括type和制作时间,其实意思是说6个chef每人做一个
  type0increase byinterval<1.5>
  type3increase byinterval<4.0>
  type1increase byinterval<2.3>
  type3increase byinterval<4.0>
  type1increase byinterval<2.3>
  Waiting for ReadyToRun...
  ...federation synchronized.
  NER to time<1.5>                           //进入主循环,首先根据第一个sushi制作完成的时间,发出NER to 1.5的时间推进请求
  Discovered Boat 115(B_9_0)         //此时,外部事件callback队列中首先出现的时间是tranport.java联邦注册的Boat对象,一共8个。这里callback应该是DiscoverObjectInstanceCallback
  Discovered Boat 117(B_9_1)
  Discovered Boat 118(B_9_2)
  Discovered Boat 119(B_9_3)
  Discovered Boat 120(B_9_4)
  Discovered Boat 121(B_9_5)
  Discovered Boat 122(B_9_6)
  Discovered Boat 123(B_9_7)
  ...granted to time<0.01>            //下一个外部事件队列中出现的是一个time grant
  NER to time<1.5>                         //重新回到主循环的起始,因为得到的time grant的时间小于第一个sushi制作完成的时间,内部事件队列中并不删除第一个sushi制作完成这个事件,重新发出NER to 1.5的时间推进请求
  ...granted to time<1.01>             //又得到一个外部事件队列事件,是一个time grant
  NER to time<1.5>                        //同理,又发出一个NER to 1.5的时间推进请求
  ...granted to time<1.5>            //这回,终于得到grant了
  Dequeued internal event at time<1.5>, chef: 0   //下面就是从_internalQueue中取出两个1.5时间做好的sushi,注册相应的对象实例,并从_internalQueue中删除两个1.5时间做好的sushi
  This is dispatch in FinishMakingSushiEvent
  Dequeued internal event at time<1.5>, chef: 1
  This is dispatch in FinishMakingSushiEvent
  NER to time<2.3>                      //这回,轮到第三个被做好的sushi了
  Chef 1 attempting to load boat B_9_1//下一个外部事件是reflectiveAttributeValues,是transport联邦对Boat实例对象属性的修改更新导致的事件
  AODN Serving S_8_1               //再下一个外部事件是AODN callback,即AttributeOwnershipDivestitureNotificationCallback,RTI回调这个服务通知所有权正式转移
  ...granted to time<2.01>          //下一个外部事件是一个time grant
  NER to time<2.3>                     //没有得到了2.3的grant,继续NER to time 2.3
  ...granted to time<2.3>         //终于得到2.3的grant了
  Dequeued internal event at time<2.3>, chef: 3    //下面就是从_internalQueue中取出两个2.3时间做好的sushi,注册相应的对象实例,并从_internalQueue中删除两个2.3时间做好的sushi
  This is dispatch in FinishMakingSushiEvent
  Dequeued internal event at time<2.3>, chef: 5
  This is dispatch in FinishMakingSushiEvent
  NER to time<4.0>                  //重新回到循环的起始,取出下一个做好的sushi的时间是4.0
  TransferAccepted: S_8_1 from chef serial 1 time<3.01>   //下一个事件是接收到一个交互,
  ...granted to time<3.01>
  NER to time<4.0>
  ...granted to time<4.0>
  Dequeued internal event at time<4.0>, chef: 2
  This is dispatch in FinishMakingSushiEvent
  Dequeued internal event at time<4.0>, chef: 4
  This is dispatch in FinishMakingSushiEvent
  
  ---------------------------------------------------------------------------------------------------------------------------
  
  需要注意的是,callback是一个抽象类,dispatch方法也是一个抽象方法。具体从外部事件队列中取出的事件,调用其dispatch方法时,会根据具体的对象实例所属的类(比如ExternalEvent ,AODNcallback,DiscoverObjectInstanceCallback,ProvideAttributeValueUpdateCallback,ReceiveInteractionCallback,ReflectAttributeValuesEvent,RemoveObjectInstanceEvent,RAORcallback)调用合适的方法。
  ----------------------------------------------------------------------------------------------------------------------------
  
  前面例子中,我们初始时(0.0时间起)一共生产了6个sushi,分别由6个chef生产。现在的问题是:从动态运行的输出来看,当production联邦的时间已经进展到4.0时,所有6个sushi都已经生产出来。则下一个sushi什么时候产生呢? 从源代码查看得知,应该是transport联邦发出TransferAccepted交互时产生了新的sushi。
  
  在production.java中,类public final class ReceiveInteractionEvent extends ExternalEvent是来处理TransferAccepted交互的。即production收到TransferAccepted交互时,将调用上述类中的dispatch方法,而在dispatch方法中,有如下代码:
  
  //put event on internal queue
  LogicalTime eventTime = new LogicalTimeDouble(0.0);
  eventTime.setTo(_logicalTime);
  eventTime.increaseBy(_manufactureTimes);
  _internalQueue.enqueue(new FinishMakingSushiEvent(eventTime, chefSerial, type));
  
  
  显然,上述代码就是产生新的内部事件,该事件说明生产新的sushi,该sushi的开始生产时间是_logicalTime,结束时间是某种sushi生产的时间+_logicalTime
  
  
  对评论的回复:
  
    在下载的book-update-2000-07目录下,有两个批处理,一个是rti运行的批处理,一个是对production联邦运行建立了一个批处理
  rti运行:
  java -cp bin;lib/prti.jar;lib/jgl3.1.0.jar se.pitch.prti.RTIexec
  production联邦运行:
  java -cp src;lib/prti.jar;lib/jgl3.1.0.jar;lib/swingall.jar org.mitre.hla.book.restaurant.production.Production
  之前需要设置java系统的运行目录到path环境变量中,或者jre的bin目录到path中。

  
页: [1]
查看完整版本: 计算机仿真中的HLA技术中的餐馆例子分析 (1) Production