計算機仿真中的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.

type0  increase by  interval<1.5>    //這裏是產生的6個sushi實例,信息包括type和製作時間,其實意思是說6個chef每人做一個

type0  increase by  interval<1.5>

type3  increase by  interval<4.0>

type1  increase by  interval<2.3>

type3  increase by  interval<4.0>

type1  increase by  interval<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[type]);

        _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中。

 

發佈了32 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章