KY-RTI分布仿真技术:第十五章 Rust程序设计

        本章通过两个示例展示了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-RTI分布仿真技术:第一章 简介

KY-RTI分布仿真技术:第二章 系统安装

KY-RTI分布仿真技术:第三章 KY-OMT对象模型模板工具

KY-RTI分布仿真技术:第四章 C++程序设计

KY-RTI分布仿真技术:第五章 Qt程序设计

KY-RTI分布仿真技术:第六章 Java程序设计

KY-RTI分布仿真技术:第七章 Visual C++程序设计

KY-RTI分布仿真技术:第八章 Visual C#程序设计

KY-RTI分布仿真技术:第九章 综合演示

KY-RTI分布仿真技术:第十章 Python程序设计

KY-RTI分布仿真技术:第十一章 人工智能大模型仿真--低空无人机巡逻实战示例

KY-RTI分布仿真技术:第十二章 HLA联邦保存与恢复--仿真中的热恢复与冷恢复

KY-RTI分布仿真技术:第十三章 基于Unity的多语言三维态势仿真--以Island Assault(夺岛突击)想定为例

KY-RTI分布仿真技术:第十四章 Go程序设计

KY-RTI分布仿真技术:第十五章 Rust程序设计

KY-RTI分布仿真技术:附录1 分组聊天(HLA数据分发管理的应用

KY-RTI分布仿真技术:附录2 大联邦(构建1000个成员的HLA/RTI仿真系统)

KY-RTI分布仿真技术:附录3 国产化(操作系统+CPUs)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值