本章通过两个示例展示了KY-RTI在Rust仿真领域的应用,示例1说明如何使用Rust语言设计基于HLA/RTI标准的乐观时间管理仿真程序,详细讲解了程序设计思路、代码实现及运行测试。示例2设计了A(Rust)、B(Rust)、C(Java)、D(Python)四个仿真成员,并将联邦保存与恢复功能融入乐观时间管理。A和B参与联邦保存,在仿真过程中A会发送1个超前消息;C和D作为替身代替A和B参与联邦恢复,D会使用FQR(flushQueueRequest)服务乐观地推进时间并获得之前在联邦保存阶段A发送的超前消息,之后C使用retract请求撤销超前消息,D则会收到requestRetraction请求撤销回调服务。
15.1. 概述
在RTI软件中,乐观时间管理和联邦保存与恢复一直是实现的两个难点。本章设计了两个示例来很好地展示了KY-RTI的实现。
示例1:15.2节FQR示例展示了KY-RTI所提供的乐观时间管理服务使用方法,该例基于Rust语言实现了A和B两个仿真成员,A发送时戳10消息并调用TARA(timeAdvanceRequestAvailable)将时间推进到5.0,B调用FQR提前接收到时戳10消息并将时间推进到5.0;之后A调用retract要求撤销时戳10消息,B会收到requestRetraction回调服务。
示例2:15.3节跨语言FQR与联邦保存/冷恢复示例基于示例1实现。示例1的时间推进、接收消息和撤销消息都在一次仿真运行过程中实现;示例2则将整个过程分成2个独立的阶段,即保存阶段和冷恢复阶段,每个阶段都是一次独立的仿真,需要重启KY-RTI,互不影响。该例设计了A、B、C、D四个仿真成员,其中A和B为Rust仿真成员,C为Java仿真成员,D为Python仿真成员。在保存阶段使用A和B;在冷恢复阶段使用C和D。在通常的仿真中,保存阶段如果使用A和B,则在恢复阶段还是A和B,然而示例2在恢复阶段用C替代A,用D替代B,不仅程序不同,而且编程语言也不同。在保存阶段,A发送时戳10消息并调用TARA将时间推进到5.0;B也调用TARA将时间推进到5.0。因为TARA是保守时间管理服务,所以B不会收到时戳10消息。之后A和B两个仿真成员开始进行联邦保存,A会保存时戳10消息句柄,KY-RTI则会保存自己管理的整个联邦信息,包括A和B的信息。在示例2中,用户能直接感受到的KY-RTI保存信息包括A和B两个仿真成员的逻辑时间、是否为时间受限仿真成员、是否为时间管控仿真成员、消息队列中的待处理消息,其他还有公布订购关系、发现的对象实例、同步点等。
本章示例的难度较高,虽然代码看上去简单。示例1展示了乐观时间推进的基本原理,而示例2跨语言示例结合联邦保存与恢复机制,在实现上有很大的难度,一般不能在国内外常见的各个RTI产品上复现。
1. 编程语言接口。主流RTI产品的官方接口以C++、Java为主,部分商业产品还提供C#/.NET绑定;Rust、Go、Python等语言支持在行业中较少见。KY-RTI支持C++、Java、Python、C#、Go、Rust等。示例1采用Rust语言实现了A和B两个仿真成员;示例2包括A、B、C、D四个仿真成员,其中A和B采用Rust语言实现,C采用Java语言实现,D采用Python语言实现。A和B两个仿真成员会在退出前进行联邦保存,之后用C代替A,用D代替B,由C和D来执行联邦恢复功能,C和D就像A和B的影子但更换了编程语言。示例2采用三种语言参与同一联邦的保存与冷恢复,这在其他RTI产品中几乎不会被复现。
2. 乐观时间管理。HLA时间管理在部分开源RTI中已有实现;但完整的乐观时间管理服务实现程度因产品而异,在开源RTI中往往不够完整。本章两个示例说明了KY-RTI对乐观时间管理服务的完整支持。
3. 冷恢复与热恢复。恢复功能包括热恢复(RTI和仿真成员不重启)与冷恢复(重启RTI和仿真成员)。部分RTI产品以热恢复为主;完整的冷恢复实现较少。本示例演示的正是冷恢复,对RTI的数据持久化、状态重建等仿真要求更高。另外,第12章示例同时提供了热恢复和冷恢复功能。
4. RTI与仿真成员各自保存内容。在保存时,KY-RTI需持久化联邦运行时状态,包括各仿真成员的逻辑时间、时间受限/时间管控标识、对象实例,以及尚未发送的时戳消息(譬如示例2中的时戳10消息)。冷恢复后,仿真成员D调用FQR(5.0)能收到上次仿真滞留在RTI中的消息,从而验证RTI消息队列被正确恢复。需要说明的是:retract所需的消息句柄在本示例中由程序A在联邦保存时写入本地文件../federate_state/savepoint_fqr/A.json,程序C冷恢复后读取该文件再调用retract来撤销消息。
15.2. FQR程序
15.2.1 需求分析
本项目需要演示HLA时间管理中的乐观推进与消息回滚:仿真成员A发出一条未来时戳的属性更新消息,并请求推进到某一逻辑时间;仿真成员B通过FQR推进并接收队列中的时戳消息;随后A对已发送消息调用retract撤销服务。程序以控制台方式运行,按提示键入字符后分步继续。
15.2.2 项目设计
消息采用对象类plane封装,属性为xPos与yPos(在RTI中按字符串传送)。伪代码如下。
class plane { //对象类
string xPos; //属性string
string yPos; //属性
}
FED文件使用tracer.fed(与C++/Go等FQR示例相同),联邦执行名称为FQRExample。
本项目采用HLA时间管理,双方均为时间受限与时间管控仿真成员;A使用TARA推进;B使用FQR推进;A在适当时机调用retract服务来回滚消息。lookahead取0.0。运行时RTI.rid中UsingTickSwitch应为On,程序通过tick等待回调。
15.2.3 代码设计
由A程序和B程序组成。
需要特别说明:Rust语言官方编码风格明确规定函数名、方法名、变量名、模块名必须采用蛇形命名法,而不是驼峰命名法。如果函数名写成 createFederationExecution,rustfmt会报警告,甚至 CI(持续集成)流程都可能不通过。因此,在C++中的回调服务discoverObjectInstance、reflectAttributeValues在Rust中分别写成discover_object_instance、reflect_attribute_values;C++中的createFederationExecution在Rust中成为create_federation_execution,而timeAdvanceRequestAvailable(TARA)和flushQueueRequest(FQR)分别写成time_advance_request_available和flush_queue_request。
15.2.3.1 A程序
A程序由单个源文件main.rs实现,如表15.1所示。
表15.1 A程序:main.rs
1.use std::io::{self, BufRead, Write};
2.use std::sync::{Arc, Mutex};
3.use std::thread;
4.use std::time::Duration;
5.
6.use kyrti::{
7. Ambassador, Error, FederateAmbassador, HandleValuePair, RetractionHandle,
8.};
9.
10.struct Flags {
11. constrained: bool,
12. regulation: bool,
13. granted: bool,
14.}
15.
16.struct Fed {
17. flags: Arc<Mutex<Flags>>,
18.}
19.
20.impl FederateAmbassador for Fed {
21. fn discover_object_instance(&mut self, _o: i32, _c: i32, _n: &str) {
22. println!("发现一个对象实例");
23. }
24. fn reflect_attribute_values(
25. &mut self,
26. _object: i32,
27. _attrs: &[HandleValuePair],
28. has_time: bool,
29. the_time: f64,
30. _tag: &str,
31. _retraction: RetractionHandle,
32. ) {
33. if has_time {
34. println!("收到一个时戳为{the_time}的消息");
35. } else {
36. println!("收到一个RO消息");
37. }
38. }
39. fn time_regulation_enabled(&mut self, _t: f64) {
40. self.flags.lock().unwrap().regulation = true;
41. }
42. fn time_constrained_enabled(&mut self, _t: f64) {
43. self.flags.lock().unwrap().constrained = true;
44. }
45. fn time_advance_grant(&mut self, t: f64) {
46. self.flags.lock().unwrap().granted = true;
47. println!("RTI同意推进到逻辑时间{t}");
48. }
49.}
50.
51.fn tick(rti: &Ambassador) -> kyrti::Result<()> {
52. rti.tick((0.001, 1.0)).map(|_| ())
53.}
54.
55.fn read_line_prompt(msg: &str) {
56. print!("{msg}");
57. let _ = io::stdout().flush();
58. let mut s = String::new();
59. let _ = io::stdin().lock().read_line(&mut s);
60.}
61.
62.fn main() -> Result<(), Box<dyn std::error::Error>> {
63. let federation = "FQRExample";
64. let fed_file = "tracer.fed";
65. let federate_name = "A";
66. let lookahead = 0.0;
67.
68. let mut rti = Ambassador::new()?;
69. match rti.create_federation_execution(federation, fed_file) {
70. Ok(()) | Err(Error::AlreadyExists(_)) => {}
71. Err(e) => return Err(e.into()),
72. }
73.
74. let flags = Arc::new(Mutex::new(Flags {
75. constrained: false,
76. regulation: false,
77. granted: false,
78. }));
79. let federate_id = rti.join_federation_execution(
80. federate_name,
81. federation,
82. Box::new(Fed {
83. flags: Arc::clone(&flags),
84. }),
85. )?;
86.
87. let h_plane = rti.get_object_class_handle("plane")?;
88. let hx = rti.get_attribute_handle("xPos", h_plane)?;
89. let hy = rti.get_attribute_handle("yPos", h_plane)?;
90. let attrs = [hx, hy];
91. rti.publish_object_class(h_plane, &attrs)?;
92. rti.subscribe_object_class_attributes(h_plane, &attrs)?;
93. let h_inst = rti.register_object_instance(h_plane)?;
94.
95. rti.enable_time_constrained()?;
96. while !flags.lock().unwrap().constrained {
97. tick(&rti)?;
98. }
99.
100. rti.enable_time_regulation(0.0, lookahead)?;
101. while !flags.lock().unwrap().regulation {
102. tick(&rti)?;
103. }
104. rti.enable_asynchronous_delivery()?;
105.
106. if federate_id == 1 {
107. read_line_prompt(
108. "本次测试有2个仿真成员,请启动另外一个仿真成员后,再输入字符c后继续...",
109. );
110. } else if federate_id != 2 {
111. eprintln!("本次测试只使用2个仿真成员,请重新测试");
112. std::process::exit(1);
113. }
114.
115. println!("当前时间: {}", rti.query_federate_time()?);
116.
117. println!("发送一个时戳为10的消息");
118. let pairs = [
119. HandleValuePair {
120. handle: hx,
121. value: "110".into(),
122. },
123. HandleValuePair {
124. handle: hy,
125. value: "220".into(),
126. },
127. ];
128. let msg_handle = rti.update_attribute_values((h_inst, &pairs, "", 10.0))?;
129.
130. println!("调用TARA(5.0)");
131. flags.lock().unwrap().granted = false;
132. rti.time_advance_request_available(5.0)?;
133. while !flags.lock().unwrap().granted {
134. tick(&rti)?;
135. }
136.
137. println!("当前时间: {}", rti.query_federate_time()?);
138. println!("请继续运行B,让其调用FQR(5.0)");
139. println!("现在B推进到5.0并接收到消息10");
140. read_line_prompt("再输入字符c后继续...");
141.
142. println!("调用retract来回滚消息");
143. if let Err(e) = rti.retract(msg_handle) {
144. eprintln!("retract: {e}");
145. eprintln!("注意,不能回滚过时消息");
146. }
147.
148. loop {
149. tick(&rti)?;
150. thread::sleep(Duration::from_millis(10));
151. }
152.}
A程序main.rs代码说明:
10-14行:Flags结构,存放时间管理回调标志。
16-18行:Fed结构,通过Arc<Mutex<Flags>>在回调与主线程间共享状态。
20-49行:FederateAmbassador回调,处理发现对象、收消息、时间管理。
51-53行:调用tick服务。
55-60行:提示并等待用户输入。
63-66行:设置联邦名FQRExample、FED文件、成员名A、lookahead。
68-72行:创建联邦执行。
74-85行:加入联邦。
87-93行:获取plane类及xPos、yPos属性,公布/订购,注册对象实例。
95-98行:设置时间受限盟员。
100-103行:设置时间管控盟员。
104行:打开异步消息开关。
106-113行:2成员握手,成员号不为1或2则退出。
115行:打印当前时间。
117-128行:发送时戳10消息,保存返回的消息句柄。
130-135行:TARA推进到逻辑时间5。
137-140行:打印当前时间,提示运行B调用FQR。
142-146行:retract回滚时戳10消息。
148-151行:循环运行,用户输入Ctrl-C结束程序。
15.2.3.2 B程序
B程序由单个源文件main.rs实现,如表15.2所示。
表15.2 B程序:main.rs
1.use std::io::{self, BufRead, Write};
2.use std::sync::{Arc, Mutex};
3.use std::thread;
4.use std::time::Duration;
5.
6.use kyrti::{
7. Ambassador, Error, FederateAmbassador, HandleValuePair, RetractionHandle,
8.};
9.
10.struct Flags {
11. constrained: bool,
12. regulation: bool,
13. granted: bool,
14.}
15.
16.struct Fed {
17. flags: Arc<Mutex<Flags>>,
18.}
19.
20.impl FederateAmbassador for Fed {
21. fn discover_object_instance(&mut self, _o: i32, _c: i32, _n: &str) {
22. println!("发现一个对象实例");
23. }
24. fn reflect_attribute_values(
25. &mut self,
26. _object: i32,
27. _attrs: &[HandleValuePair],
28. has_time: bool,
29. the_time: f64,
30. _tag: &str,
31. _retraction: RetractionHandle,
32. ) {
33. if has_time {
34. println!("收到一个时戳为{the_time}的消息");
35. } else {
36. println!("收到一个RO消息");
37. }
38. }
39. fn time_regulation_enabled(&mut self, _t: f64) {
40. self.flags.lock().unwrap().regulation = true;
41. }
42. fn time_constrained_enabled(&mut self, _t: f64) {
43. self.flags.lock().unwrap().constrained = true;
44. }
45. fn time_advance_grant(&mut self, t: f64) {
46. self.flags.lock().unwrap().granted = true;
47. println!("RTI同意推进到逻辑时间{t}");
48. }
49. fn request_retraction(&mut self, _h: RetractionHandle) {
50. println!("收到requestRetraction服务,发送方要求回滚该消息");
51. }
52.}
53.
54.fn tick(rti: &Ambassador) -> kyrti::Result<()> {
55. rti.tick((0.001, 1.0)).map(|_| ())
56.}
57.
58.fn read_line_prompt(msg: &str) {
59. print!("{msg}");
60. let _ = io::stdout().flush();
61. let mut s = String::new();
62. let _ = io::stdin().lock().read_line(&mut s);
63.}
64.
65.fn main() -> Result<(), Box<dyn std::error::Error>> {
66. let federation = "FQRExample";
67. let fed_file = "tracer.fed";
68. let federate_name = "B";
69. let lookahead = 0.0;
70.
71. let mut rti = Ambassador::new()?;
72. match rti.create_federation_execution(federation, fed_file) {
73. Ok(()) | Err(Error::AlreadyExists(_)) => {}
74. Err(e) => return Err(e.into()),
75. }
76.
77. let flags = Arc::new(Mutex::new(Flags {
78. constrained: false,
79. regulation: false,
80. granted: false,
81. }));
82. let federate_id = rti.join_federation_execution(
83. federate_name,
84. federation,
85. Box::new(Fed {
86. flags: Arc::clone(&flags),
87. }),
88. )?;
89.
90. let h_plane = rti.get_object_class_handle("plane")?;
91. let hx = rti.get_attribute_handle("xPos", h_plane)?;
92. let hy = rti.get_attribute_handle("yPos", h_plane)?;
93. let attrs = [hx, hy];
94. rti.publish_object_class(h_plane, &attrs)?;
95. rti.subscribe_object_class_attributes(h_plane, &attrs)?;
96. let _ = rti.register_object_instance(h_plane)?;
97.
98. rti.enable_time_constrained()?;
99. while !flags.lock().unwrap().constrained {
100. tick(&rti)?;
101. }
102.
103. rti.enable_time_regulation(0.0, lookahead)?;
104. while !flags.lock().unwrap().regulation {
105. tick(&rti)?;
106. }
107. rti.enable_asynchronous_delivery()?;
108.
109. if federate_id == 1 {
110. read_line_prompt(
111. "本次测试有2个仿真成员,请启动另外一个仿真成员后,再输入字符c后继续...",
112. );
113. } else if federate_id != 2 {
114. eprintln!("本次测试只使用2个仿真成员,请重新测试");
115. std::process::exit(1);
116. }
117.
118. read_line_prompt(
119. "等待另外一个仿真成员TARA(5.0)之后,再输入字符c后继续...",
120. );
121.
122. println!("请求推进到逻辑时间5");
123. flags.lock().unwrap().granted = false;
124. rti.flush_queue_request(5.0)?;
125. while !flags.lock().unwrap().granted {
126. tick(&rti)?;
127. }
128.
129. println!("当前时间: {}", rti.query_federate_time()?);
130.
131. loop {
132. tick(&rti)?;
133. thread::sleep(Duration::from_millis(10));
134. }
135.}
B程序main.rs代码说明:
10-14行:Flags结构,存放时间管理回调标志。
16-18行:Fed结构,通过Arc<Mutex<Flags>>在回调与主线程间共享状态。
20-52行:FederateAmbassador回调,处理发现对象、收消息、时间管理、请求撤销。
54-56行:调用tick服务。
58-63行:提示并等待用户输入。
66-69行:设置联邦名FQRExample、FED文件、成员名B、lookahead。
71-75行:创建联邦执行。
77-88行:加入联邦。
90-96行:获取plane类及xPos、yPos,公布/订购,注册对象实例。
98-101行:设置时间受限盟员。
103-106行:设置时间管控盟员。
107行:打开异步消息开关。
109-116行:2成员握手,成员号不为1或2则退出。
118-120行:提示等A完成TARA(5.0)。
122-127行:FQR推进到逻辑时间5。
129行:打印当前时间。
131-134行:循环运行,等待A调用retract并接收requestRetraction;用户输入Ctrl-C结束程序。
15.2.4 编译运行
15.2.4.1 编译
编译前需已安装Rust工具链(建议通过rustup安装),并正确设置KY-RTI环境,使cargo能够解析kyrti包并链接运行库。本示例位于apps-rust/FQR。
编译A程序:
source $RTI_HOME/env.sh #Windows执行: call %RTI_HOME%\env.bat
cd $RTI_HOME/apps-rust/FQR/A
cargo build --release
cp target/release/a .
编译B程序:
cd $RTI_HOME/apps-rust/FQR/B
cargo build --release
cp target/release/b .
Windows下将上述source改为call %RTI_HOME%\env.bat,并将生成的a.exe、b.exe复制到对应目录即可。
15.2.4.2 测试运行
测试项目:在同一台机器上运行A、B两个Rust仿真成员,验证乐观时间推进FQR与retract消息回滚。
测试步骤:
第1步:确认RTI.rid中打开tick开关。本程序在等待回调时主动调用tick服务,因此查看A、B程序目录下RTI.rid,将“;; UsingTickSwitch Off”改为“;; UsingTickSwitch On”。同时确认RTI.rid中的IP与端口与RTIManager一致。
第2步:启动KY-RTI控制台RTIManager。
cd $RTI_HOME/bin
./RTIManager.sh
两个仿真成员加入后,控制台如下图所示。图中显示了2个联邦,1个是聊天示例,另一个是本示例。

第3步:先启动A,再启动B(顺序很重要)。
终端1(A):
source $RTI_HOME/env.sh
cd $RTI_HOME/apps-rust/FQR/A
./a

终端2(B):该仿真成员后启动会发现一个对象示例。
source $RTI_HOME/env.sh
cd $RTI_HOME/apps-rust/FQR/B
./b

第4步:按提示操作,在仿真成员A的窗口输入字符c,然后A发送时戳为10的属性更新消息,并调用TARA推进到5.0,此时仿真成员A发现一个对象示例。

再查看B,发现终端与之前一样。

第5步:在仿真成员B窗口输入字符c,然而调用FQR(5.0),B应收到时戳为10的消息并推进到5.0。

再查看A,终端信息如下图所示。

第6步:在仿真成员A窗口输入字符c,然后仿真成员A调用retract尝试回滚该消息。

再查看B,仿真成员B收到request_retraction提示。

测试结果表明:Rust仿真成员可完成FQR乐观推进与消息回滚相关服务调用;与C++、Go等语言的FQR示例使用相同联邦名FQRExample与tracer.fed时,也可进行跨语言互操作。
15.3 跨语言FQR与联邦保存/冷恢复
本节在15.2节基础上扩展为四个程序、两阶段演示:程序A、B(Rust语言实现)完成联邦保存;重启KY-RTI后,程序C(Java语言实现)、D(Python语言实现)完成冷恢复,并再次演示FQR乐观推进与retract消息回滚。
15.3.1 四程序总体设计
四个程序与仿真成员名的对应关系如下:程序A(Rust,仿真成员名A)发送时戳10消息并TARA(5.0),联邦保存时消息句柄被写入A.json;程序B(Rust,仿真成员名B)同样TARA(5.0)但不会收到时戳10消息,随后发起requestFederationSave开始联邦保存,保存点标签定义为savepoint_fqr;程序C(Java,作为A的替身,仿真成员名仍为A)进行冷恢复,恢复后执行retract;程序D(Python,作为B的替身,仿真成员名仍为B)也进行冷恢复,恢复后调用FQR(5.0)取出时戳10消息,并等待C的retract触发requestRetraction回调服务。
与15.2节的区别。15.2节示例在一次仿真中由A发消息、B调用FQR、A再retract、B收到requestRetraction回调服务;15.3节示例则将流程拆为保存阶段(A/B)与冷恢复阶段(C/D),中间需重启KY-RTI,以验证保存点中逻辑时间、时间管理(enableTimeConstrained / enableTimeRegulation)角色(时间受限/时间管控)及未发送而滞留在消息队列中的消息均可恢复。
15.3.1.1 保存阶段(程序A、B)
启动KY-RTI进入保存阶段。
程序A:创建/加入联邦FQRExample,设置时间受限与时间管控角色,登记plane对象实例,发送时戳10的属性更新(xPos=110,yPos=220),记录返回的消息句柄;调用TARA(5.0)推进到逻辑时间5.0;等待程序B也TARA(5.0)后,等待B发起保存。A在initiateFederateSave回调触发的process_pending_save中,依次调用federateSaveBegun、将句柄写入A.json、federateSaveComplete,完成保存握手。
程序B:设置时间受限与时间管控角色,TARA(5.0)。因B的保守推进,时戳10的消息不会在此阶段发送给B,仍保留在KY-RTI的消息队列中。保存前,KY-RTI会删除之前仿真使用的$RTI_HOME/bin/save/savepoint_fqr文件,B会删除../federate_state/savepoint_fqr/下的旧数据(避免重复演示时因同名标签报错);之后B调用requestFederationSave("savepoint_fqr")发起保存。A、B在保存服务完成后退出。
15.3.1.2 冷恢复阶段(程序C、D)
重启KY-RTI进入冷恢复阶段。
冷恢复要求新仿真成员使用的仿真成员名与保存时一致(A、B),先后启动C和D,C作为A的替身加入仿真,仿真成员名仍为A;D作为B的替身加入仿真,仿真成员名仍为B。
C和D加入仿真后,故意不在恢复前调用enableTimeConstrained / enableTimeRegulation,以便在恢复完成后通过queryFederateTime查询当前逻辑时间,此时才调用enableTimeConstrained / enableTimeRegulation会收到AlreadyEnabled异常,表明该成员已经是时间受限/时间管控仿真成员。D程序调用requestFederationRestore("savepoint_fqr")发起恢复;C、D在initiateFederateRestore回调中应答federateRestoreComplete,待federationRestored后继续。D调用FQR(5.0),会收到仍保存在KY-RTI中的时戳10消息;C从A.json获取时戳10消息的句柄后调用retract;D会收到requestRetraction回调,这与15.2节行为一致,但发生在冷恢复之后。
15.3.2 代码设计
由A、B、C、D四个程序组成。
15.3.2.1 A程序
A程序由单个源文件main.rs实现,如表15.3所示。
表15.3 A程序:main.rs
1.use std::fs;
2.use std::io::{self, BufRead, Write};
3.use std::path::PathBuf;
4.use std::sync::{Arc, Mutex};
5.
6.use kyrti::{
7. Ambassador, Error, FederateAmbassador, HandleValuePair, RetractionHandle,
8.};
9.
10.const SAVE_LABEL: &str = "savepoint_fqr";
11.
12.struct Flags {
13. constrained: bool,
14. regulation: bool,
15. granted: bool,
16. pending_save: bool,
17. federation_saved: bool,
18. save_label: String,
19. msg_handle: RetractionHandle,
20.}
21.
22.struct Fed {
23. flags: Arc<Mutex<Flags>>,
24.}
25.
26.impl FederateAmbassador for Fed {
27. fn discover_object_instance(&mut self, _o: i32, _c: i32, _n: &str) {}
28. fn reflect_attribute_values(
29. &mut self,
30. _object: i32,
31. _attrs: &[HandleValuePair],
32. has_time: bool,
33. the_time: f64,
34. _tag: &str,
35. _retraction: RetractionHandle,
36. ) {
37. if has_time {
38. println!("收到时戳{the_time}的消息");
39. }
40. }
41. fn time_regulation_enabled(&mut self, _t: f64) {
42. self.flags.lock().unwrap().regulation = true;
43. }
44. fn time_constrained_enabled(&mut self, _t: f64) {
45. self.flags.lock().unwrap().constrained = true;
46. }
47. fn time_advance_grant(&mut self, t: f64) {
48. self.flags.lock().unwrap().granted = true;
49. println!("TAG {t}");
50. }
51. fn initiate_federate_save(&mut self, label: &str) {
52. let mut f = self.flags.lock().unwrap();
53. f.save_label = label.to_string();
54. f.pending_save = true;
55. }
56. fn federation_saved(&mut self) {
57. println!("联邦已保存: {SAVE_LABEL}");
58. self.flags.lock().unwrap().federation_saved = true;
59. }
60. fn federation_not_saved(&mut self) {
61. eprintln!("联邦保存失败");
62. }
63.}
64.
65.fn tick(rti: &Ambassador) -> kyrti::Result<()> {
66. rti.tick((0.001, 1.0)).map(|_| ())
67.}
68.
69.fn state_dir(label: &str) -> PathBuf {
70. PathBuf::from("..").join("federate_state").join(label)
71.}
72.
73.fn state_file(label: &str, federate: &str) -> PathBuf {
74. state_dir(label).join(format!("{federate}.json"))
75.}
76.
77.fn write_a_state(label: &str, h: RetractionHandle) -> io::Result<()> {
78. let dir = state_dir(label);
79. fs::create_dir_all(&dir)?;
80. let body = format!(
81. "{{\n \"serial\": {},\n \"sending_federate\": {}\n}}\n",
82. h.serial, h.sending_federate
83. );
84. fs::write(state_file(label, "A"), body)
85.}
86.
87.fn read_line_prompt(msg: &str) {
88. print!("{msg}");
89. let _ = io::stdout().flush();
90. let mut s = String::new();
91. let _ = io::stdin().lock().read_line(&mut s);
92.}
93.
94.fn main() -> Result<(), Box<dyn std::error::Error>> {
95. let federation = "FQRExample";
96. let fed_file = "tracer.fed";
97. let federate_name = "A";
98. let lookahead = 0.0;
99.
100. let mut rti = Ambassador::new()?;
101. match rti.create_federation_execution(federation, fed_file) {
102. Ok(()) | Err(Error::AlreadyExists(_)) => {}
103. Err(e) => return Err(e.into()),
104. }
105.
106. let flags = Arc::new(Mutex::new(Flags {
107. constrained: false,
108. regulation: false,
109. granted: false,
110. pending_save: false,
111. federation_saved: false,
112. save_label: String::new(),
113. msg_handle: RetractionHandle::default(),
114. }));
115. let federate_id = rti.join_federation_execution(
116. federate_name,
117. federation,
118. Box::new(Fed {
119. flags: Arc::clone(&flags),
120. }),
121. )?;
122.
123. let h_plane = rti.get_object_class_handle("plane")?;
124. let hx = rti.get_attribute_handle("xPos", h_plane)?;
125. let hy = rti.get_attribute_handle("yPos", h_plane)?;
126. let attrs = [hx, hy];
127. rti.publish_object_class(h_plane, &attrs)?;
128. rti.subscribe_object_class_attributes(h_plane, &attrs)?;
129. let h_inst = rti.register_object_instance(h_plane)?;
130.
131. rti.enable_time_constrained()?;
132. while !flags.lock().unwrap().constrained {
133. tick(&rti)?;
134. }
135.
136. rti.enable_time_regulation(0.0, lookahead)?;
137. while !flags.lock().unwrap().regulation {
138. tick(&rti)?;
139. }
140. rti.enable_asynchronous_delivery()?;
141.
142. if federate_id == 1 {
143. read_line_prompt("请启动另一仿真成员后输入c继续...");
144. } else if federate_id != 2 {
145. eprintln!("本测试仅2个仿真成员");
146. std::process::exit(1);
147. }
148.
149. println!("发送时戳10的消息");
150. let pairs = [
151. HandleValuePair {
152. handle: hx,
153. value: "110".into(),
154. },
155. HandleValuePair {
156. handle: hy,
157. value: "220".into(),
158. },
159. ];
160. let msg_handle = rti.update_attribute_values((h_inst, &pairs, "", 10.0))?;
161. flags.lock().unwrap().msg_handle = msg_handle;
162.
163. println!("TARA(5.0)");
164. flags.lock().unwrap().granted = false;
165. rti.time_advance_request_available(5.0)?;
166. while !flags.lock().unwrap().granted {
167. tick(&rti)?;
168. }
169. println!("当前时间: {}", rti.query_federate_time()?);
170.
171. read_line_prompt("请让B也TARA(5.0)后输入c继续...");
172.
173. println!("等待B发起保存...");
174. while !flags.lock().unwrap().pending_save {
175. tick(&rti)?;
176. }
177. let label = flags.lock().unwrap().save_label.clone();
178. let handle = flags.lock().unwrap().msg_handle;
179.
180. rti.federate_save_begun()?;
181. write_a_state(&label, handle)?;
182. rti.federate_save_complete()?;
183.
184. println!("等待联邦保存完成...");
185. while !flags.lock().unwrap().federation_saved {
186. tick(&rti)?;
187. }
188.
189. println!("完成。请重启KY-RTI后运行C、D。");
190. Ok(())
191.}
A程序main.rs代码说明:
10行:定义保存点标签常量SAVE_LABEL。
12-20行:Flags结构,存放时间管理、保存和消息句柄相关状态。
22-24行:Fed结构,通过Arc<Mutex<Flags>>在回调与主线程间共享状态。
27行:discoverObjectInstance回调,本程序用不上。
28-40行:reflectAttributeValues回调,打印收到的时戳消息。
41-43行:timeRegulationEnabled回调,置regulation标识。
44-46行:timeConstrainedEnabled回调,置constrained标识。
47-50行:timeAdvanceGrant回调,置granted标识并打印TAG。
51-55行:initiateFederateSave回调,记录保存标签并置pending_save。
56-59行:federationSaved回调,打印并置federation_saved。
60-62行:federationNotSaved回调,打印保存失败。
65-67行:调用tick服务。
69-71行:构造../federate_state/<label>目录路径。
73-75行:构造A.json文件路径。
77-85行:把serial和sending_federate写入A.json。
87-92行:提示并等待用户输入。
95-98行:设置联邦名FQRExample、FED文件、成员名A、lookahead。
100-104行:创建联邦执行。
106-114行:初始化Flags各字段默认值。
115-121行:加入联邦。
123-129行:获取plane类及xPos、yPos属性,公布/订购,注册对象实例。
131-134行:设置时间受限盟员。
136-139行:设置时间管控盟员。
140行:打开异步消息开关。
142-147行:2成员握手,成员句柄不为1或2则退出。
149-161行:发送时戳10消息,保存返回的消息句柄。
163-169行:TARA推进到逻辑时间5,打印当前时间。
171行:提示等B也TARA到5。
173-176行:等待initiateFederateSave。
177-178行:取出保存标签和消息句柄。
180-182行:federateSaveBegun、写A.json、federateSaveComplete应答RTI。
184-187行:等待联邦保存完成。
189-190行:提示重启KY-RTI后运行C、D,正常退出。
15.3.2.2 B程序
B程序由单个源文件main.rs实现,如表15.4所示。
表15.4 B程序:main.rs
1.use std::fs;
2.use std::io::{self, BufRead, Write};
3.use std::path::PathBuf;
4.use std::sync::{Arc, Mutex};
5.
6.use kyrti::{
7. Ambassador, Error, FederateAmbassador, HandleValuePair, RetractionHandle,
8.};
9.
10.const SAVE_LABEL: &str = "savepoint_fqr";
11.
12.struct Flags {
13. constrained: bool,
14. regulation: bool,
15. granted: bool,
16. pending_save: bool,
17. federation_saved: bool,
18. save_label: String,
19.}
20.
21.struct Fed {
22. flags: Arc<Mutex<Flags>>,
23.}
24.
25.impl FederateAmbassador for Fed {
26. fn discover_object_instance(&mut self, _o: i32, _c: i32, _n: &str) {}
27. fn reflect_attribute_values(
28. &mut self,
29. _object: i32,
30. _attrs: &[HandleValuePair],
31. has_time: bool,
32. the_time: f64,
33. _tag: &str,
34. _retraction: RetractionHandle,
35. ) {
36. if has_time {
37. println!("收到时戳{the_time}的消息");
38. }
39. }
40. fn time_regulation_enabled(&mut self, _t: f64) {
41. self.flags.lock().unwrap().regulation = true;
42. }
43. fn time_constrained_enabled(&mut self, _t: f64) {
44. self.flags.lock().unwrap().constrained = true;
45. }
46. fn time_advance_grant(&mut self, t: f64) {
47. self.flags.lock().unwrap().granted = true;
48. println!("TAG {t}");
49. }
50. fn initiate_federate_save(&mut self, label: &str) {
51. let mut f = self.flags.lock().unwrap();
52. f.save_label = label.to_string();
53. f.pending_save = true;
54. }
55. fn federation_saved(&mut self) {
56. println!("联邦已保存: {SAVE_LABEL}");
57. self.flags.lock().unwrap().federation_saved = true;
58. }
59. fn federation_not_saved(&mut self) {
60. eprintln!("联邦保存失败");
61. }
62.}
63.
64.fn tick(rti: &Ambassador) -> kyrti::Result<()> {
65. rti.tick((0.001, 1.0)).map(|_| ())
66.}
67.
68.fn state_dir(label: &str) -> PathBuf {
69. PathBuf::from("..").join("federate_state").join(label)
70.}
71.
72.fn write_b_state(label: &str) -> io::Result<()> {
73. let dir = state_dir(label);
74. fs::create_dir_all(&dir)?;
75. fs::write(dir.join("B.json"), "{}\n")
76.}
77.
78.fn clear_local_save_state(label: &str) {
79. let dir = state_dir(label);
80. if dir.exists() {
81. if let Err(e) = fs::remove_dir_all(&dir) {
82. eprintln!("删除旧本地状态目录失败 {}: {e}", dir.display());
83. } else {
84. println!("已删除旧本地状态: {}", dir.display());
85. }
86. }
87.}
88.
89.fn read_line_prompt(msg: &str) {
90. print!("{msg}");
91. let _ = io::stdout().flush();
92. let mut s = String::new();
93. let _ = io::stdin().lock().read_line(&mut s);
94.}
95.
96.fn main() -> Result<(), Box<dyn std::error::Error>> {
97. let federation = "FQRExample";
98. let fed_file = "tracer.fed";
99. let federate_name = "B";
100. let lookahead = 0.0;
101.
102. let mut rti = Ambassador::new()?;
103. match rti.create_federation_execution(federation, fed_file) {
104. Ok(()) | Err(Error::AlreadyExists(_)) => {}
105. Err(e) => return Err(e.into()),
106. }
107.
108. let flags = Arc::new(Mutex::new(Flags {
109. constrained: false,
110. regulation: false,
111. granted: false,
112. pending_save: false,
113. federation_saved: false,
114. save_label: String::new(),
115. }));
116. let federate_id = rti.join_federation_execution(
117. federate_name,
118. federation,
119. Box::new(Fed {
120. flags: Arc::clone(&flags),
121. }),
122. )?;
123.
124. let h_plane = rti.get_object_class_handle("plane")?;
125. let hx = rti.get_attribute_handle("xPos", h_plane)?;
126. let hy = rti.get_attribute_handle("yPos", h_plane)?;
127. let attrs = [hx, hy];
128. rti.publish_object_class(h_plane, &attrs)?;
129. rti.subscribe_object_class_attributes(h_plane, &attrs)?;
130. let _ = rti.register_object_instance(h_plane)?;
131.
132. rti.enable_time_constrained()?;
133. while !flags.lock().unwrap().constrained {
134. tick(&rti)?;
135. }
136.
137. rti.enable_time_regulation(0.0, lookahead)?;
138. while !flags.lock().unwrap().regulation {
139. tick(&rti)?;
140. }
141. rti.enable_asynchronous_delivery()?;
142.
143. if federate_id == 1 {
144. read_line_prompt("请启动另一仿真成员后输入c继续...");
145. } else if federate_id != 2 {
146. eprintln!("本测试仅2个仿真成员");
147. std::process::exit(1);
148. }
149.
150. read_line_prompt("请等A完成TARA(5.0)后输入c继续...");
151.
152. println!("TARA(5.0)");
153. flags.lock().unwrap().granted = false;
154. rti.time_advance_request_available(5.0)?;
155. while !flags.lock().unwrap().granted {
156. tick(&rti)?;
157. }
158. println!("当前时间: {}", rti.query_federate_time()?);
159.
160. read_line_prompt(
161. "请先在A终端输入c(A应显示「等待B发起保存...」),再在此处输入c发起保存...",
162. );
163.
164. clear_local_save_state(SAVE_LABEL);
165.
166. println!("requestFederationSave({SAVE_LABEL})");
167. loop {
168. match rti.request_federation_save(SAVE_LABEL) {
169. Ok(()) => break,
170. Err(Error::Other(ref s)) if s == "SaveInProgress" => {
171. eprintln!("SaveInProgress:上一保存未完成或RTI仍占用,tick后重试...");
172. tick(&rti)?;
173. }
174. Err(e) => return Err(e.into()),
175. }
176. }
177.
178. while !flags.lock().unwrap().pending_save {
179. tick(&rti)?;
180. }
181. let label = flags.lock().unwrap().save_label.clone();
182.
183. rti.federate_save_begun()?;
184. write_b_state(&label)?;
185. rti.federate_save_complete()?;
186.
187. println!("等待联邦保存完成...");
188. while !flags.lock().unwrap().federation_saved {
189. tick(&rti)?;
190. }
191.
192. println!("完成。请重启KY-RTI后运行C、D。");
193. Ok(())
194.}
B程序main.rs代码说明:
10行:保存点标签常量SAVE_LABEL。
12-19行:Flags结构,存放时间管理和保存相关状态。
21-23行:Fed结构,通过Arc<Mutex<Flags>>在回调与主线程间共享状态。
26行:discoverObjectInstance回调,本程序用不上。
27-39行:reflectAttributeValues,打印收到的时戳消息(保存阶段B用TARA不应收到时戳10)。
40-42行:timeRegulationEnabled回调,置regulation标识。
43-45行:timeConstrainedEnabled回调,置constrained标识。
46-49行:timeAdvanceGrant回调,置granted标识并打印TAG。
50-54行:initiateFederateSave回调,记录保存标签并置pending_save。
55-58行:federationSaved回调,打印并置federation_saved。
59-61行:federationNotSaved回调,打印保存失败。
64-66行:调用tick服务。
68-70行:构造../federate_state/<label>目录路径。
72-76行:写文件B.json,内容为空(B没有需要保存的内容)。
78-87行:删除本地旧状态目录。
89-94行:提示并等待用户输入。
97-100行:设置联邦名、FED文件、成员名B、lookahead。
102-106行:创建联邦执行。
108-115行:初始化Flags。
116-122行:加入联邦。
124-130行:获取plane类及xPos、yPos,公布/订购,注册对象实例。
132-135行:设置时间受限盟员。
137-140行:设置时间管控盟员。
141行:打开异步消息开关。
143-148行:2成员握手,成员句柄不为1或2则退出。
150行:提示等A完成TARA到5。
152-158行:TARA推进到逻辑时间5,打印当前时间。
160-162行:提示先在A终端确认,再在此处发起保存。
164行:删除本地旧状态。
166-176行:请求联邦保存。
178-181行:等待initiateFederateSave回调,取出保存标签。
183-185行:federateSaveBegun、写B.json、federateSaveComplete应答RTI。
187-190行:等待联邦保存完成。
192-193行:提示重启KY-RTI后运行C、D,正常退出。
15.3.2.3 C程序
C程序包括源文件C.java和HwFederateAmbassador.java,以及定义两个文件共享变量的GlobalVariables.java。
表15.5 C程序:C.java
1.import MID.*;
2.import RTI.*;
3.import java.io.*;
4.import java.nio.charset.StandardCharsets;
5.import java.nio.file.*;
6.import java.util.Scanner;
7.import java.util.regex.*;
8.
9./**
10. * 冷恢复程序C:代码目录为C,联邦仿真成员名仍为A。
11. * 等待D发起restore(savepoint_fqr);展示恢复后的逻辑时间与时间管理状态;
12. * 从 A 保存的 A.json 读取事件句柄,再执行 retract。
13. */
14.public class C
15.{
16. static final String SAVE_LABEL = "savepoint_fqr";
17.
18. static void tick(RTI_RTIambassador rti) throws Exception
19. {
20. rti.tick(0.001, 1.0);
21. }
22.
23. static void prompt(String msg)
24. {
25. System.out.print(msg);
26. System.out.flush();
27. if (System.console() != null) {
28. System.console().readLine();
29. } else {
30. new Scanner(System.in).nextLine();
31. }
32. }
33.
34. /** 从 ../federate_state/savepoint_fqr/A.json 读取 serial、sending_federate */
35. static EventRetractionHandle loadHandleFromJson() throws IOException
36. {
37. Path p = Paths.get("..", "federate_state", SAVE_LABEL, "A.json");
38. String text = new String(Files.readAllBytes(p), StandardCharsets.UTF_8);
39. long serial = Long.parseLong(matchNum(text, "serial"));
40. int sending = Integer.parseInt(matchNum(text, "sending_federate"));
41. return new EventRetractionHandle(serial, sending);
42. }
43.
44. static String matchNum(String text, String key)
45. {
46. Pattern pat = Pattern.compile("\"" + key + "\"\\s*:\\s*(-?[0-9]+)");
47. Matcher m = pat.matcher(text);
48. if (!m.find()) {
49. throw new RuntimeException("JSON缺少字段: " + key);
50. }
51. return m.group(1);
52. }
53.
54. public static void main(String[] args)
55. {
56. String federationName = "FQRExample";
57. String federateName = "A";
58. HwFederateAmbassador fed;
59. RTI_RTIambassador rti;
60.
61. try {
62. fed = new HwFederateAmbassador();
63. rti = new RTI_RTIambassador();
64. } catch (Exception ex) {
65. ex.printStackTrace();
66. return;
67. }
68.
69. try {
70. rti.createFederationExecution(federationName, "tracer.fed");
71. } catch (Exception ex) {
72. }
73.
74. int federateId;
75. try {
76. federateId = rti.joinFederationExecution(federateName, federationName, fed);
77. System.out.println("已加入联邦,仿真成员名=" + federateName + " handle=" + federateId);
78. } catch (Exception ex) {
79. ex.printStackTrace();
80. return;
81. }
82.
83. try {
84. if (federateId == 1) {
85. prompt("本次测试有2个仿真成员,请启动另外一个仿真成员后,再输入字符c后继续...");
86. } else if (federateId != 2) {
87. System.err.println("本次测试只使用2个仿真成员,请重新测试");
88. return;
89. }
90.
91. System.out.println("等待联邦恢复回调(由程序D发起requestFederationRestore)...");
92. while (!GlobalVariables.g_federationRestored) {
93. if (GlobalVariables.g_pendingRestore) {
94. rti.federateRestoreComplete();
95. GlobalVariables.g_pendingRestore = false;
96. System.out.println("federateRestoreComplete() 已发送");
97. }
98. tick(rti);
99. }
100.
101. System.out.println("--- 联邦恢复结果(仿真成员A)---");
102. double[] t = new double[1];
103. rti.queryFederateTime(t);
104. System.out.println("逻辑时间: " + t[0]);
105.
106. try {
107. rti.enableTimeConstrained();
108. System.out.println("constrained: 恢复后未置位");
109. while (!GlobalVariables.g_bConstrained) {
110. tick(rti);
111. }
112. } catch (RTI.TimeConstrainedAlreadyEnabled e) {
113. GlobalVariables.g_bConstrained = true;
114. System.out.println("constrained: 已由恢复置为ENABLED");
115. }
116.
117. try {
118. rti.enableTimeRegulation(0.0, GlobalVariables.g_lookahead);
119. System.out.println("regulating: 恢复后未置位");
120. while (!GlobalVariables.g_bRegulation) {
121. tick(rti);
122. }
123. } catch (RTI.TimeRegulationAlreadyEnabled e) {
124. GlobalVariables.g_bRegulation = true;
125. System.out.println("regulating: 已由恢复置为ENABLED");
126. }
127.
128. prompt("请输入字符c继续retract...");
129.
130. System.out.println("调用retract来回滚消息");
131. try {
132. EventRetractionHandle erh = loadHandleFromJson();
133. rti.retract(erh);
134. } catch (Exception e) {
135. System.err.println("retract: " + e);
136. System.err.println("注意,不能回滚过时消息;并确认 ../federate_state/"
137. + SAVE_LABEL + "/A.json 存在且含 serial、sending_federate");
138. e.printStackTrace();
139. }
140.
141. while (true) {
142. tick(rti);
143. Thread.sleep(10);
144. }
145. } catch (Exception ex) {
146. ex.printStackTrace();
147. }
148. }
149.}
C程序C.java代码说明:
16行:定义保存点标签常量SAVE_LABEL。
18-21行:调用tick服务。
23-32行:提示并等待用户输入。
34-42行:读取../federate_state/savepoint_fqr/A.json文件,构造EventRetractionHandle。
44-52行:解析JSON数值字段。
61-67行:创建HwFederateAmbassador和RTI_RTIambassador。
69-72行:创建联邦执行。
74-81行:加入联邦。
84-89行:2成员握手,成员句柄不为1或2则退出。
91-99行:等待initiateFederateRestore,调用federateRestoreComplete应答,再等待联邦恢复完成。
101-104行:打印恢复后的逻辑时间。
106-115行:设置时间受限盟员,但会收到异常表明恢复后已置位,未置位则等待置位。
117-126行:设置时间管控盟员,但会收到异常表明恢复后已置位,未置位则等待置位。
128行:提示用户确认后继续retract。
130-139行:从A.json获取时戳10消息句柄,调用retract回滚该消息。
141-144行:循环运行,用户输入Ctrl-C结束程序。
表15.6 C程序:HwFederateAmbassador.java
1.import MID.*;
2.
3.public class HwFederateAmbassador extends RTIFederateAmbassador
4.{
5. public final void requestFederationRestoreSucceeded(String label)
6. {
7. System.out.println("requestFederationRestoreSucceeded: " + label);
8. }
9.
10. public final void requestFederationRestoreFailed(String label, String reason)
11. {
12. System.out.println("requestFederationRestoreFailed: " + label + " reason=" + reason);
13. }
14.
15. public final void federationRestoreBegun()
16. {
17. System.out.println("federationRestoreBegun");
18. }
19.
20. public final void initiateFederateRestore(String label, int handle)
21. {
22. System.out.println("收到initiateFederateRestore: " + label + " handle=" + handle);
23. GlobalVariables.g_restoreLabel = label;
24. GlobalVariables.g_pendingRestore = true;
25. }
26.
27. public final void federationRestored()
28. {
29. System.out.println("联邦已恢复: savepoint_fqr");
30. GlobalVariables.g_federationRestored = true;
31. }
32.
33. public final void federationNotRestored()
34. {
35. System.out.println("联邦恢复失败");
36. }
37.
38. public final void discoverObjectInstance(int theObject, int theObjectClass, String theObjectName)
39. {
40. System.out.println("发现一个对象实例");
41. }
42.
43. public final void reflectAttributeValues(int theObject, HandleValuePair[] theAttributes, double theTime, String theTag, MID.EventRetractionHandle theHandle)
44. {
45. System.out.println("收到一个时戳为" + theTime + "的消息");
46. }
47.
48. public final void timeRegulationEnabled(double theFederateTime)
49. {
50. GlobalVariables.g_currentTime = theFederateTime;
51. GlobalVariables.g_bRegulation = true;
52. }
53.
54. public final void timeConstrainedEnabled(double theFederateTime)
55. {
56. GlobalVariables.g_currentTime = theFederateTime;
57. GlobalVariables.g_bConstrained = true;
58. }
59.
60. public final void timeAdvanceGrant(double theTime)
61. {
62. GlobalVariables.g_currentTime = theTime;
63. GlobalVariables.g_granted = true;
64. System.out.println("RTI同意推进到逻辑时间" + theTime);
65. }
66.
67. public final void requestRetraction(MID.EventRetractionHandle theHandle)
68. {
69. System.out.println("收到requestRetraction服务,发送方要求回滚该消息");
70. }
71.}
C程序HwFederateAmbassador.java代码说明:
5-8行:requestFederationRestoreSucceeded回调,打印恢复成功。
10-13行:requestFederationRestoreFailed回调,打印恢复失败。
15-18行:federationRestoreBegun回调,打印恢复开始。
20-25行:initiateFederateRestore回调,记录标签并置g_pendingRestore。
27-31行:federationRestored回调,置g_federationRestored。
33-36行:federationNotRestored回调,打印恢复失败。
38-41行:discoverObjectInstance回调,打印发现对象示例。
43-46行:reflectAttributeValues回调,打印收到的时戳消息。
48-52行:timeRegulationEnabled回调,更新时间和管控标识。
54-58行:timeConstrainedEnabled回调,更新时间和受限标识。
60-65行:timeAdvanceGrant回调,更新grant标识并打印。
67-70行:requestRetraction回调,本程序不会触发。
15.3.2.4 D程序
D程序由单个源文件D.py实现,如表15.7所示。需要特别说明:Python程序在最新的实现中未采用驼峰命名法。
表15.7 D程序:D.py
1.#coding:utf-8
2."""冷恢复程序D:代码目录为D,联邦仿真成员名仍为B。
3.
4.发起request_federation_restore('savepoint_fqr');
5.恢复后调用FQR(5.0)取出仍留在KY-RTI中的时戳10消息;
6.再等待程序C(仿真成员A)retract触发的requestRetraction。
7."""
8.
9.from __future__ import print_function
10.
11.import sys
12.import time
13.
14.import kyrti
15.
16.SAVE_LABEL = "savepoint_fqr"
17.FEDERATION = "FQRExample"
18.FEDERATE_NAME = "B" # 冷恢复仿真成员名须与保存时一致
19.
20.class State(object):
21. def __init__(self):
22. self.constrained = False
23. self.regulation = False
24. self.granted = False
25. self.pending_restore = False
26. self.federation_restored = False
27. self.restore_label = ""
28. self.restore_failed = False
29. self.got_tso10 = False
30. self.got_retraction = False
31.
32.state = State()
33.
34.class Fed(kyrti.NullFederateAmbassador):
35. def discover_object_instance(self, o, c, n):
36. print("发现一个对象实例")
37.
38. def reflect_attribute_values(self, object, attrs, has_time, the_time, tag, retraction):
39. if has_time:
40. print("收到一个时戳为%s的消息" % the_time)
41. if abs(the_time - 10.0) < 1e-9:
42. state.got_tso10 = True
43. else:
44. print("收到一个RO消息")
45.
46. def time_regulation_enabled(self, t):
47. state.regulation = True
48.
49. def time_constrained_enabled(self, t):
50. state.constrained = True
51.
52. def time_advance_grant(self, t):
53. state.granted = True
54. print("RTI同意推进到逻辑时间%s" % t)
55.
56. def request_retraction(self, h):
57. print("收到requestRetraction服务,发送方要求回滚该消息")
58. state.got_retraction = True
59.
60. def initiate_federate_restore(self, label, handle):
61. print("收到initiateFederateRestore: %s handle=%s" % (label, handle))
62. state.restore_label = label
63. state.pending_restore = True
64.
65. def federation_restored(self):
66. print("联邦已恢复: %s" % SAVE_LABEL)
67. state.federation_restored = True
68.
69. def federation_not_restored(self):
70. print("联邦恢复失败")
71. state.restore_failed = True
72.
73. def request_federation_restore_succeeded(self, label):
74. print("requestFederationRestoreSucceeded: %s" % label)
75.
76. def request_federation_restore_failed(self, label, reason):
77. print("requestFederationRestoreFailed: %s reason=%s" % (label, reason))
78. state.restore_failed = True
79.
80.def tick(rti):
81. rti.tick(0.001, 1.0)
82.
83.def main():
84. rti = kyrti.Ambassador()
85. try:
86. rti.create_federation_execution(FEDERATION, "tracer.fed")
87. except kyrti.AlreadyExistsError:
88. pass
89.
90. fed = Fed()
91. federate_id = rti.join_federation_execution(FEDERATE_NAME, FEDERATION, fed)
92. print("已加入联邦,仿真成员名=%s handle=%s" % (FEDERATE_NAME, federate_id))
93.
94. if federate_id == 1:
95. input("本次测试有2个仿真成员,请启动另外一个仿真成员后,再输入字符c后继续...")
96. elif federate_id != 2:
97. print("本次测试只使用2个仿真成员,请重新测试", file=sys.stderr)
98. sys.exit(1)
99.
100. print("开始恢复...")
101. print("调用request_federation_restore(%s)" % SAVE_LABEL)
102. state.federation_restored = False
103. state.restore_failed = False
104. rti.request_federation_restore(SAVE_LABEL)
105. while not (state.federation_restored or state.restore_failed):
106. if state.pending_restore:
107. rti.federate_restore_complete()
108. state.pending_restore = False
109. print("federate_restore_complete() 已发送")
110. tick(rti)
111. if state.restore_failed:
112. print("恢复失败,退出", file=sys.stderr)
113. sys.exit(1)
114.
115. print("--- 联邦恢复结果(仿真成员B)---")
116. cur = rti.query_federate_time()
117. print("逻辑时间: %s " % cur)
118.
119. try:
120. rti.enable_time_constrained()
121. print("constrained: 恢复后未置位")
122. while not state.constrained:
123. tick(rti)
124. except kyrti.KyrtiError as e:
125. state.constrained = True
126. print("constrained: 已由恢复置为ENABLED")
127.
128. try:
129. rti.enable_time_regulation(0.0, 0.0)
130. print("regulating: 恢复后未置位")
131. while not state.regulation:
132. tick(rti)
133. except kyrti.KyrtiError as e:
134. state.regulation = True
135. print("regulating: 已由恢复置为ENABLED")
136.
137. print("调用FQR(5.0),应收到仍保存在KY-RTI中的时戳10消息")
138. state.granted = False
139. state.got_tso10 = False
140. rti.flush_queue_request(5.0)
141. while not state.granted:
142. tick(rti)
143. while not state.got_tso10:
144. tick(rti)
145.
146. cur = rti.query_federate_time()
147. print("当前时间: %s" % cur)
148. print("请在程序C中继续,C会调用retract...")
149.
150. while True:
151. tick(rti)
152. time.sleep(0.01)
153.
154.if __name__ == "__main__":
155. main()
D程序D.py代码说明:
16-18行:定义保存点标签、联邦名、仿真成员名B。
20-30行:定义State类,包括保存恢复、FQR和retract相关的运行时标识。
32行:全局state实例。
34-78行:Fed继承NullFederateAmbassador,实现恢复、收时戳10消息、收撤销通知等回调。
80-81行:调用tick服务。
85-88行:创建联邦执行。
90-92行:加入联邦。
94-98行:2成员握手,成员句柄不为1或2则退出。
100-113行:请求联邦恢复,等待initiateFederateRestore并应答,再等待联邦恢复完成。
115-117行:打印恢复后的逻辑时间。
119-126行:设置时间受限盟员,但会收到异常表明恢复后已置位,未置位则等待置位。
128-135行:设置时间管控盟员,但会收到异常表明恢复后已置位,未置位则等待置位。
137-144行:FQR推进到5,等待granted和时戳10消息。
146-148行:打印当前时间,提示在C中继续retract。
150-152行:循环运行,等待C调用retract服务。
15.3.3 测试运行
测试项目:在同一台机器上运行A、B两个Rust仿真成员,验证联邦保存功能;之后重启KY-RTI控制台后,运行C、D两个Java和Python仿真成员,验证联邦恢复功能以及乐观时间推进FQR与retract消息回滚。
测试步骤:
第1步:启动KY-RTI控制台RTIManager。
cd $RTI_HOME/bin
./RTIManager.sh
A和B两个仿真成员加入后,控制台如下图所示,KY-RTI进程号为179186。

第2步:先启动A,再启动B(顺序很重要)。
终端1(A):
cd apps/FQR/A
./a

终端2(B):
cd apps/FQR/B
./b

第3步:2个成员握手。在A窗口输入c。

B窗口保持原样:

第4步:B窗口输入c后同样TARA(5.0)。

A窗口:

第5步:A窗口输入c,A应显示「等待B发起保存...」。

B窗口保持原样:

第6步:B窗口输入c,B删除旧保存点后发起联邦保存,最终联邦保存成功。

A窗口:A也完成联邦保存操作,将时戳10消息句柄记入Json文件。

第7步:重启KY-RTI控制台RTIManager。
C和D两个仿真成员加入后,控制台如下图所示,KY-RTI进程号为207180,与之前的KY-RTI不同,表明是重启了KY-RTI。

第8步:先启动C,再启动D(顺序很重要)。
终端1(C):
cd apps/FQR/C
java C

终端2(D):启动后即开始联邦恢复功能
cd apps/FQR/D
python3 D.py

第9步:在C窗口输入c,则联邦恢复完成,并且逻辑时间被恢复到5.0,enableTimeConstrained和enableTimeRegulation标识也恢复了,注意这一点非常重要。

D窗口:逻辑时间被恢复到5.0,enableTimeConstrained和enableTimeRegulation标识也恢复了;通过FQR(5.0),收到一个上次A和B仿真时滞留在RTI消息队列中的时戳10消息,注意该消息并不是C发送的,是联邦恢复时得到的,注意这一点同样非常重要。

第10步:在C窗口输入c执行retract;D应收到requestRetraction。

再查看D,仿真成员D收到requestRetraction提示。

测试结果表明:Rust仿真成员A和B在完成联邦保存后,通过替身Java仿真成员C和替身Python仿真成员D完成了冷恢复功能,完整地展示了KY-RTI在HLA乐观时间管理与联邦保存/恢复方面的能力。
KY-RTI的Linux、Windows版本和源码请联系作者:walt_lbq@163.com
KY-RTI分布仿真技术:第三章 KY-OMT对象模型模板工具
KY-RTI分布仿真技术:第七章 Visual C++程序设计
KY-RTI分布仿真技术:第八章 Visual C#程序设计
KY-RTI分布仿真技术:第十一章 人工智能大模型仿真--低空无人机巡逻实战示例
KY-RTI分布仿真技术:第十二章 HLA联邦保存与恢复--仿真中的热恢复与冷恢复
KY-RTI分布仿真技术:第十三章 基于Unity的多语言三维态势仿真--以Island Assault(夺岛突击)想定为例
KY-RTI分布仿真技术:附录1 分组聊天(HLA数据分发管理的应用)

409

被折叠的 条评论
为什么被折叠?



