第六章 Java程序设计
本章讲述了如何基于Java设计聊天程序和时间管理程序,两个程序都是控制台程序。聊天程序相当于4.3节的GNU C++聊天程序;时间管理程序相当于4.4节的GNU C++程序。对于不同的程序设计语言而言,基于HLA/RTI设计仿真应用的方法都差不多,其关键在于RTI软件能否支持相应程序设计语言的开发,用户只需要关心一下调用接口即可,通过调用接口可以设计形式多样的程序。与C++调用接口的一个显著区别在于句柄和时间的表示,Java中的各种句柄全部用int表示,各类时间则用double表示。
6.1 聊天程序
6.1.1需求分析
本项目需要实现一个类似微信群或者QQ群聊天功能的Java程序,每个人发送的消息都能够被群里的其他人看到。
6.1.2项目设计
每条聊天信息应包含2个内容:聊天者昵称、聊天的一句话,这样接收者就会知道是谁在发言。“聊天者昵称”用name表示,“聊天的一句话”用sentence表示,两个都是字符串类型。因为HLA是面向对象的,发送的数据要么采用对象类,要么采用交互类。本项目可采用交互类,将name和sentence封装到一个名叫“chat”的交互类中,如下列伪代码所示。
class chat { //交互类
string name; //参数
string sentence; //参数
}
下面采用KY-OMT创建fed文件,相应的chat.fed文件已经在3.3.3中创建完成,将该文件保存到KY-RTI的bin目录。
本项目对时间没有特别要求,不需要采用HLA时间管理机制。
6.1.3代码设计
该程序由3个源文件组成:GlobalVariables.java、Chat.java、HwFederateAmbassador.java。GlobalVariables.java定义了其他两个文件需要用到的公用变量;Chat.java调用RTI服务访问RTI;HwFederateAmbassador.java接收RTI回调服务。
GlobalVariables.java定义了可由Chat.java、HwFederateAmbassador.java共享使用的静态变量;因为Java不支持C++的全局变量概念,这里的静态变量就相当于C++的全局变量。
表6.1 Java聊天示例:GlobalVariables.java
1. public class GlobalVariables
2. {
3. public static int hChatClass;
4. public static int hChatName;
5. public static int hChatSentence;
6. }
Chat.java代码说明:
21-25行:创建联邦执行;
27-33行:加入联邦执行;
36-38行:获取交互类及其参数句柄;
41行:公布交互类,只有公布之后才能够向RTI发送交互;
43行:订购交互类,只有订购之后才能够从RTI收到其他人的聊天内容;
46-59行:循环操作,每次输入一句话,并调用sendInteraction服务发送给RTI;当用户输入“exit”时则退出执行;
65行:该行为注释行,表示仿真成员在结束时不需要调用'resignFederationExecution'和 'destroyFederationExecution'服务,RTI服务器会自动执行这两个服务。
表6.2 Java聊天示例:Chat.java
1. import MID.*;
2. import RTI.*;
3.
4. public class Chat
5. {
6. public static void main(String[] args){
7. String federationName = "chat";
8. String federateName = System.console().readLine("Please input your name: ");
9.
10. HwFederateAmbassador fed;
11. RTI_RTIambassador rti;
12.
13. try{
14. fed =new HwFederateAmbassador();
15. rti =new RTI_RTIambassador();
16. }catch(Exception ex){
17. ex.printStackTrace();
18. return;
19. }
20.
21. try{
22. rti.createFederationExecution(federationName, "chat.fed");
23. }catch(Exception ex){
24. // According to the HLA standard, only the first federate can call createFederationExecution successfully. Don't return.
25. }
26.
27. try{
28. int federateHandle = rti.joinFederationExecution(federateName, federationName, fed);
29. //System.out.println("my federate handle is "+federateHandle);
30. }catch(Exception ex){
31. ex.printStackTrace();
32. return;
33. }
34.
35. try{
36. GlobalVariables.hChatClass = rti.getInteractionClassHandle("chat");
37. GlobalVariables.hChatName = rti.getParameterHandle("name", GlobalVariables.hChatClass);
38. GlobalVariables.hChatSentence = rti.getParameterHandle("sentence", GlobalVariables.hChatClass);
39.
40. //如果向外发送,则需要公布
41. rti.publishInteractionClass(GlobalVariables.hChatClass);
42. //如果需要接收,则必须订购
43. rti.subscribeInteractionClass(GlobalVariables.hChatClass);
44.
45. String szSentence = "";
46. while(!szSentence.equals("exit")){
47. szSentence = System.console().readLine("Please input a sentence: ");
48.
49. HandleValuePair[] theParameters =new HandleValuePair[2];
50. theParameters[0]=new HandleValuePair();
51. theParameters[0].aHandle = GlobalVariables.hChatName;
52. theParameters[0].aValue = federateName; //int -> String
53.
54. theParameters[1]=new HandleValuePair();
55. theParameters[1].aHandle = GlobalVariables.hChatSentence;
56. theParameters[1].aValue = szSentence; //int -> String
57.
58. rti.sendInteraction(GlobalVariables.hChatClass, theParameters, "");
59. }
60. }catch(Exception ex){
61. ex.printStackTrace();
62. return;
63. }
64.
65. //After the program exits, the RTI will automatically calls 'resignFederationExecution' and 'destroyFederationExecution'.
66. }
67. }
HwFederateAmbassador.java代码说明:
5-8行:由于不处理时间参数,因此如果接收到这种类型的receiveInteraction交互,则直接调用不带时间参数的服务来统一处理;
10-28行:处理接收到的聊天信息,将其简单输出即可。
表6.3 Java聊天示例:HwFederateAmbassador.java
1. import MID.*;
2.
3. public class HwFederateAmbassador extends RTIFederateAmbassador
4. {
5. public final void receiveInteraction(int theInteraction, MID.HandleValuePair[] theParameters, double theTime, String theTag, MID.EventRetractionHandle theHandle){
6. //call the next service.
7. receiveInteraction(theInteraction, theParameters, theTag);
8. }
9.
10. public final void receiveInteraction(int theInteraction, MID.HandleValuePair[] theParameters, String theTag){
11. String name =new String(); //name of sender
12. String sentence =new String(); //sentence of sender
13.
14. for( int i = 0; i < theParameters.length; i++){
15. if(theParameters[i].aHandle == GlobalVariables.hChatName){
16. name = theParameters[i].aValue;
17.
18. }elseif(theParameters[i].aHandle == GlobalVariables.hChatSentence){
19. sentence = theParameters[i].aValue;
20.
21. }else{
22. System.out.println("Receive wrong parameter handle.");
23. }
24. }
25.
26. System.out.println();
27. System.out.println(name + ": " + sentence);
28. }
29. }
6.1.4编译运行
6.1.4.1编译
编译Java程序,只要将kyrti.jar文件加入到CLASSPATH环境变量,或者在编译时作为命令参数添加。
(1)如果在CLASSPATH环境变量中已经设置,则编译命令如下。
javac *.java
(2)在编译时作为参数添加,则编译命令如下。
javac -classpath .;C:\KY-RTI\jar\ky-rti.jar *.java #Windows
javac -classpath .:/home/lbq/RTI-1.3NGv6/Linux-x86_64-opt-mt/jar/ky-rti.jar *.java #Linux
6.1.4.2测试运行
测试项目:在银河麒麟操作系统上运行2个Java仿真成员,测试KY-RTI通信功能。
测试步骤:
第1步:修改RTI.rid,关闭tick开关。
因为本程序没有使用tick服务,所以需要关闭tick开关。
查看当前目录下是否存在RTI.rid,若没有则在运行程序之后会自动生成该文件。将RTI.rid文件中的“;; UsingTickSwitch On”改为“;; UsingTickSwitch Off”。
第2步:启动KY-RTI控制台。注意,bin目录中configure.rti的IP地址和端口号,要与仿真程序目录中的RTI.rid一致。
第3步:如图6.1和图6.2所示,开启两个终端,运行2个仿真成员,开始仿真。运行命令:
java Chat
测试结果表明:Java仿真成员的聊天功能正常,KY-RTI支持中英文传输。
图6.1 Java聊天者1
图6.2 Java聊天者2
6.2 时间管理程序
6.2.1需求分析
本仿真项目的名称为“TimeManagementExample”,名称规定了不是“TimeManagementExample”的程序不属于本项目。对HLA/RTI程序来说,联邦名称为“TimeManagementExample”,不是该名字的仿真成员不属于本项目。
每个仿真成员拥有3架飞机,但其中只有1架飞机会起飞,飞机的x、y坐标为随机数(不考虑合理性),飞机会每隔1秒发布自己的位置信息。
6.2.2项目设计
飞机每隔1秒发布自己的位置信息,意味着该仿真应采用时间管理服务,仿真步长为1。
飞机发送的是自己的x、y二维态势信息,用一个对象类plane来封装,两个属性为xPos和yPos,类型为整型;但不管什么类型,在RTI中都是作为字符串来传送。如下列代码所示。
class plane { //对象类
int xPos; //属性
int yPos; //属性
}
下面采用KY-OMT创建fed文件,相应的tracer.fed文件已经在3.3.2中创建完成,将该文件保存到KY-RTI的bin目录。
6.2.3代码设计
该程序由3个源文件组成:GlobalVariables.java、TimeManagement.java、HwFederateAmbassador.java。GlobalVariables.java定义了其他两个文件需要用到的公用变量;Chat.java调用RTI服务访问RTI;HwFederateAmbassador.java接收RTI回调服务。
GlobalVariables.java定义了可由TimeManagement.java、HwFederateAmbassador.java共享使用的静态变量;因为Java不支持C++的全局变量概念,这里的静态变量就相当于C++的全局变量。
表6.4 Java时间管理示例:GlobalVariables.java
1. public class GlobalVariables
2. {
3. public static double g_currentTime = 0.0;
4. public static double g_lookahead = 1.0;
5. public static boolean g_bConstrained =false;
6. public static boolean g_bRegulation =false;
7. public static boolean g_granted =false;
8.
9. public static int g_hxPos;
10. public static int g_hyPos;
11.
12. //3 planes
13. public static int g_hInstance1, g_hInstance2, g_hInstance3;
14. }
TimeManagement.java代码说明:
7行:联邦名称定义为“TimeManagementExample”;
21-26行:创建联邦执行;
28-34行:加入联邦执行;
37-39行:获取对象类及其属性句柄;
45行:公布对象类属性,只有公布之后才能够向RTI发送二维态势信息,即xPos和yPos;
46行:订购交互类属性,只有订购之后才能够从RTI收到二维态势信息;
48-53行:注册3架飞机;
60行:将仿真成员设置为时间管理受限的;
61-63行:等待RTI同意将该仿真成员设置为时间管理受限的;
65行:将仿真成员设置为时间管控成员;
66-68行:等待RTI同意将该仿真成员设置为时间管控成员;
70行:打开异步消息开关;
77行:仿真周期设置为1秒;这里的逻辑时间1对应物理时间的1秒(假设设置为2,则1个逻辑时间单位对应物理时间的0.5秒,2个逻辑时间单位对应仿真周期1秒);
83-167行:每隔1秒循环推进仿真,直到中断退出仿真;
100行:发送飞机在下一时刻的二维态势信息(如果采用RO消息也可以发送当前时刻的态势,当前或下一步信息依项目要求来抉择);
131行:将仿真请求推进到下一步;
132-134行:等待RTI同意该仿真成员推进到下一步;
169行:该行为注释行,表示仿真成员在结束时不需要调用'resignFederationExecution'和 'destroyFederationExecution'服务,RTI服务器会自动执行这两个服务。
表6.5 Java时间管理示例:TimeManagement.java
1. import MID.*;
2. import RTI.*;
3.
4. public class TimeManagement
5. {
6. public static void main(String[] args){
7. String federationName = "TimeManagementExample";
8. String federateName = System.console().readLine("Please input the federate name: ");
9.
10. HwFederateAmbassador fed;
11. RTI_RTIambassador rti;
12.
13. try{
14. fed =new HwFederateAmbassador();
15. rti =new RTI_RTIambassador();
16. }catch(Exception ex){
17. ex.printStackTrace();
18. return;
19. }
20.
21. try{
22. rti.createFederationExecution(federationName, "tracer.fed");
23. }catch(Exception ex){
24. // According to the HLA standard, only the first federate can call createFederationExecution successfully. Don't return.
25. ex.printStackTrace();
26. }
27.
28. try{
29. int federateHandle = rti.joinFederationExecution(federateName, federationName, fed);
30. System.out.println("my federate handle is "+federateHandle);
31. }catch(Exception ex){
32. ex.printStackTrace();
33. return;
34. }
35.
36. try{
37. int hPlaneClass = rti.getObjectClassHandle("plane");
38. GlobalVariables.g_hxPos = rti.getAttributeHandle("xPos", hPlaneClass); //different from p1516
39. GlobalVariables.g_hyPos = rti.getAttributeHandle("yPos", hPlaneClass); //different from p1516
40.
41. int[] theAttributes =new int[2];
42. theAttributes[0]= GlobalVariables.g_hxPos;
43. theAttributes[1]= GlobalVariables.g_hyPos;
44.
45. rti.publishObjectClass(hPlaneClass, theAttributes);
46. rti.subscribeObjectClassAttributes(hPlaneClass, theAttributes);
47.
48. //register one plane
49. GlobalVariables.g_hInstance1 = rti.registerObjectInstance(hPlaneClass);
50. //register 2nd plane
51. GlobalVariables.g_hInstance2 = rti.registerObjectInstance(hPlaneClass);
52. //register 3rd plane
53. GlobalVariables.g_hInstance3 = rti.registerObjectInstance(hPlaneClass);
54. }catch(Exception ex){
55. ex.printStackTrace();
56. return;
57. }
58.
59. try{
60. rti.enableTimeConstrained();
61. while(!GlobalVariables.g_bConstrained){
62. Thread.sleep(1); //1 millisecond
63. }
64.
65. rti.enableTimeRegulation(0.0, GlobalVariables.g_lookahead);
66. while(!GlobalVariables.g_bRegulation){
67. Thread.sleep(1); //1 millisecond
68. }
69.
70. rti.enableAsynchronousDelivery();
71. }catch(Exception ex){
72. ex.printStackTrace();
73. return;
74. }
75.
76. double targetTime = GlobalVariables.g_currentTime;
77. double intervalTime = 1;
78. int xPos, yPos;
79. String tag = "F15";
80. int step = 0;
81. java.util.Random r =new java.util.Random(); //get a random number
82.
83. while(true){
84. step++;
85. System.out.println("Step: "+step);
86.
87. xPos=r.nextInt();
88. yPos=r.nextInt();
89.
90. HandleValuePair[] theAttributeValues =new HandleValuePair[2];
91. theAttributeValues[0]=new HandleValuePair();
92. theAttributeValues[0].aHandle = GlobalVariables.g_hxPos;
93. theAttributeValues[0].aValue = String.valueOf(xPos); //int -> String
94.
95. theAttributeValues[1]=new HandleValuePair();
96. theAttributeValues[1].aHandle = GlobalVariables.g_hyPos;
97. theAttributeValues[1].aValue = String.valueOf(yPos); //int -> String
98.
99. try{
100. rti.updateAttributeValues(GlobalVariables.g_hInstance1, theAttributeValues, GlobalVariables.g_currentTime + GlobalVariables.g_lookahead, tag);
101. }catch(RTI.ObjectNotKnown ex){
102. ex.printStackTrace();
103. return;
104. }catch(RTI.AttributeNotDefined ex){
105. ex.printStackTrace();
106. return;
107. }catch(RTI.AttributeNotOwned ex){
108. ex.printStackTrace();
109. return;
110. }catch(RTI.InvalidFederationTime ex){
111. ex.printStackTrace();
112. return;
113. }catch(RTI.FederateNotExecutionMember ex){
114. ex.printStackTrace();
115. return;
116. }catch(RTI.SaveInProgress ex){
117. ex.printStackTrace();
118. return;
119. }catch(RTI.RestoreInProgress ex){
120. ex.printStackTrace();
121. return;
122. }catch(Exception ex){
123. ex.printStackTrace();
124. return;
125. }
126.
127. targetTime = GlobalVariables.g_currentTime + intervalTime;
128. System.out.println("This federate will advance to "+targetTime);
129.
130. try{
131. rti.timeAdvanceRequest(targetTime);
132. while(!GlobalVariables.g_granted){
133. Thread.sleep(1);//1 millisecond
134. }
135.
136. GlobalVariables.g_granted =false;
137. System.out.println("The federate has advanced to "+GlobalVariables.g_currentTime);
138. System.out.println();
139. }catch(RTI.InvalidFederationTime ex){
140. ex.printStackTrace();
141. return;
142. }catch(RTI.FederationTimeAlreadyPassed ex){
143. ex.printStackTrace();
144. return;
145. }catch(RTI.TimeAdvanceAlreadyInProgress ex){
146. ex.printStackTrace();
147. return;
148. }catch(RTI.EnableTimeRegulationPending ex){
149. ex.printStackTrace();
150. return;
151. }catch(RTI.EnableTimeConstrainedPending ex){
152. ex.printStackTrace();
153. return;
154. }catch(RTI.FederateNotExecutionMember ex){
155. ex.printStackTrace();
156. return;
157. }catch(RTI.SaveInProgress ex){
158. ex.printStackTrace();
159. return;
160. }catch(RTI.RestoreInProgress ex){
161. ex.printStackTrace();
162. return;
163. }catch(Exception ex){
164. ex.printStackTrace();
165. return;
166. }
167. }
168.
169. //After the program exits, the RTI will automatically calls 'resignFederationExecution' and 'destroyFederationExecution'.
170. }
171. }
HwFederateAmbassador.java代码说明:
5-7行:将发现的飞机输出到终端;
9-33行:将收到的飞机态势信息输出到终端;
35-39行:RTI同意将仿真成员设置为时间管控成员;
41-45行:RTI同意将仿真成员设置为时间管理受限的成员;
47-51行:RTI同意仿真成员推进到下一步。
表6.6 Java时间管理示例:HwFederateAmbassador.java
1. import MID.*;
2.
3. public class HwFederateAmbassador extends RTIFederateAmbassador
4. {
5. public final void discoverObjectInstance(int theObject, int theObjectClass, String theObjectName){
6. System.out.println("discoverObjectInstance: "+theObject+","+theObjectClass+","+theObjectName);
7. }
8.
9. public final void reflectAttributeValues(int theObject, HandleValuePair[] theAttributes, double theTime, String theTag, MID.EventRetractionHandle theHandle){
10. //call the next service.
11. reflectAttributeValues(theObject, theAttributes, theTag);
12. }
13.
14. public final void reflectAttributeValues(int theObject, MID.HandleValuePair[] theAttributes, String theTag){
15. System.out.println("reflectAttributeValues: " + theObject);
16.
17. String value =new String();
18. for(int i = 0; i < theAttributes.length;++i){
19. if(theAttributes[i].aHandle == GlobalVariables.g_hxPos){
20. value = theAttributes[i].aValue;
21.
22. }elseif(theAttributes[i].aHandle == GlobalVariables.g_hyPos){
23. value = theAttributes[i].aValue;
24.
25. }else{
26. System.out.println("Receive wrong parameter handle.");
27. }
28.
29. System.out.println(" <"+theAttributes[i].aHandle+","+value+">");
30. }
31.
32. System.out.println(" tag:" + theTag);
33. }
34.
35. public final void timeRegulationEnabled(double theFederateTime){
36. GlobalVariables.g_currentTime = theFederateTime;
37. GlobalVariables.g_bRegulation =true;
38. System.out.println("timeRegulationEnabled: " + theFederateTime);
39. }
40.
41. public final void timeConstrainedEnabled(double theFederateTime){
42. GlobalVariables.g_currentTime = theFederateTime;
43. GlobalVariables.g_bConstrained =true;
44. System.out.println("timeRegulationEnabled: " + theFederateTime);
45. }
46.
47. public final void timeAdvanceGrant(double theTime){
48. GlobalVariables.g_currentTime = theTime;
49. GlobalVariables.g_granted =true;
50. System.out.println("timeAdvanceGrant: " + theTime);
51. }
6.2.4编译运行
编译方法参照6.1.4.1执行,生成可执行程序之后就可以进行测试。
测试项目:在银河麒麟操作系统上运行2个Java仿真成员,两个仿真成员启动后尽可能快地向前推进;测试KY-RTI的HLA的基本服务功能,特别是时间管理同步功能。
测试步骤:
第1步:修改RTI.rid,关闭tick开关。
因为本程序没有使用tick服务,所以需要关闭tick开关。
查看当前目录下是否存在RTI.rid,若没有则在运行程序之后会自动生成该文件。将RTI.rid文件中的“;; UsingTickSwitch On”改为“;; UsingTickSwitch Off”。
第2步:启动KY-RTI控制台。注意,bin目录中configure.rti的IP地址和端口号,要与仿真程序目录中的RTI.rid一致。
第3步:如图6.3和图6.4所示,开启两个终端,运行2个Java仿真成员,开始仿真。运行命令:
java TimeManagement
测试结果表明:KY-RTI支持基于麒麟操作系统的Java仿真技术,时间管理同步功能强。
测试说明:
(1)图6.3是仿真成员“Air01”被Ctrl+C中断执行时的界面。此时,它能收到仿真成员“Air02”发送的二维态势信息。
(2)图6.4是仿真成员“Air02”被Ctrl+C中断执行时的界面。此时,只显示它自己的运行信息,不会有其他仿真成员的二维态势信息。
图6.3 使用时间管理服务的HLA仿真成员Air01
图6.4 使用时间管理服务的HLA仿真成员Air02