本章介绍如何使用Go语言设计基于HLA/RTI标准的聊天仿真程序,详细讲解了程序设计思路、代码实现及运行测试,展示了KY-RTI在Go语言仿真领域的应用,并演示了Go与C++、Java、Python、C#、Rust语言的互操作。
1 4.1. 概述
本章以控制台聊天程序为例,说明Go仿真成员如何调用KY-RTI提供的Go接口完成联邦创建、加入、交互类的公布与订购、发送与接收交互,以及程序退出。联邦对象模型仍使用chat.fed。与Java接口类似,Go接口中各类句柄一般用整型(int32)表示。
1 4.2. 聊天程序
14.2.1 需求分析
本项目需要实现一个类似微信群或 QQ 群的聊天功能:每个参与者发送的消息,都应能被同群中的其他人看到。程序以控制台方式运行,用户输入昵称后反复输入语句;输入exit结束。
14.2.2 项目设计
每条聊天信息应包含两项内容:聊天者昵称、聊天的一句话,这样接收者就知道是谁在发言。"聊天者昵称"用name表示,"聊天的一句话"用sentence表示,二者均为字符串。因为HLA是面向对象的,发送的数据要么采用对象类,要么采用交互类。本项目采用交互类,将name和sentence封装到名叫chat的交互类中,伪代码如下。
class chat { // 交互类
string name; // 参数
string sentence; // 参数
}
FED 中交互类片段如下。
(interactions
(class InteractionRoot reliable receive
(class chat reliable receive
(parameter name)
(parameter sentence)
)
)
)
本项目对时间没有特别要求,不需要采用HLA时间管理机制。
14.2.3 代码设计
Go聊天示例由单个源文件main.go实现,如表14.1所是。
需要特别说明:在C++中的回调服务receiveInteraction在Go中成为ReceiveInteraction;C++中的CreateFederationExecution在Go中成为CreateFederationExecution。因为Go语言要求函数名以大写字母开头。函数名首字母大写相当于"公开"(public),可以被其他包访问。首字母小写相当于"私有"(private),只能在当前包内部使用。这个大小写规则是Go语言强制规定的访问控制方式。
表14.1 Go聊天示例:main.go
Go
1.package main
2.
3.import (
4. "bufio"
5. "errors"
6. "fmt"
7. "os"
8. "strings"
9. "kyrti"
10.)
11.
12.type chatFed struct {
13. kyrti.NullFederateAmbassador
14. hName int32
15. hSentence int32
16.}
17.
18.func (f *chatFed) ReceiveInteraction(_ int32, params []kyrti.HandleValuePair, _ bool, _ float64, _ string, _ kyrti.RetractionHandle) {
19. var name, sentence string
20. for _, p := range params {
21. switch p.Handle {
22. case f.hName:
23. name = p.Value
24. case f.hSentence:
25. sentence = p.Value
26. }
27. }
28. fmt.Printf("\n%s: %s\n", name, sentence)
29.}
30.
31.func main() {
32. federationName := "chat"
33. fedFile := "chat.fed"
34.
35. reader := bufio.NewReader(os.Stdin)
36. fmt.Print("Please input your name: ")
37. federateName, _ := reader.ReadString('\n')
38. federateName = strings.TrimSpace(federateName)
39. if federateName == "" {
40. federateName = "user"
41. }
42.
43. rti, err := kyrti.NewAmbassador()
44. if err != nil {
45. fmt.Fprintln(os.Stderr, err)
46. os.Exit(1)
47. }
48. defer rti.Close()
49.
50. err = rti.CreateFederationExecution(federationName, fedFile)
51. if err != nil {
52. var already *kyrti.AlreadyExistsError
53. if !errors.As(err, &already) {
54. fmt.Fprintln(os.Stderr, "create federation:", err)
55. os.Exit(1)
56. }
57. }
58.
59. fed := &chatFed{}
60. if _, err := rti.JoinFederationExecution(federateName, federationName, fed); err != nil {
61. fmt.Fprintln(os.Stderr, "join:", err)
62. os.Exit(1)
63. }
64.
65. hClass, err := rti.GetInteractionClassHandle("chat")
66. if err != nil {
67. fmt.Fprintln(os.Stderr, err)
68. os.Exit(1)
69. }
70. hName, err := rti.GetParameterHandle("name", hClass)
71. if err != nil {
72. fmt.Fprintln(os.Stderr, err)
73. os.Exit(1)
74. }
75. hSentence, err := rti.GetParameterHandle("sentence", hClass)
76. if err != nil {
77. fmt.Fprintln(os.Stderr, err)
78. os.Exit(1)
79. }
80. fed.hName = hName
81. fed.hSentence = hSentence
82.
83. if err := rti.PublishInteractionClass(hClass); err != nil {
84. fmt.Fprintln(os.Stderr, err)
85. os.Exit(1)
86. }
87. if err := rti.SubscribeInteractionClass(hClass); err != nil {
88. fmt.Fprintln(os.Stderr, err)
89. os.Exit(1)
90. }
91.
92. for {
93. fmt.Print("Please input a sentence: ")
94. line, err := reader.ReadString('\n')
95. if err != nil {
96. break
97. }
98. sz := strings.TrimRight(line, "\r\n")
99. if sz == "exit" {
100. break
101. }
102. params := []kyrti.HandleValuePair{
103. {Handle: hName, Value: federateName},
104. {Handle: hSentence, Value: sz},
105. }
106. if _, err := rti.SendInteraction(hClass, params, ""); err != nil {
107. fmt.Fprintln(os.Stderr, "send:", err)
108. }
109. }
110.
111. _ = rti.ResignFederationExecution(kyrti.DeleteObjectsAndReleaseAttributes)
112. err = rti.DestroyFederationExecution(federationName)
113. if err != nil {
114. var joined *kyrti.FederatesJoinedError
115. var missing *kyrti.DoesNotExistError
116. if !errors.As(err, &joined) && !errors.As(err, &missing) {
117. fmt.Fprintln(os.Stderr, "destroy:", err)
118. }
119. }
120.}
main.go代码说明:
12-16行:定义仿真成员大使结构体chatFed,嵌入NullFederateAmbassador,并保存交互参数name、sentence的句柄;
18-29行:实现ReceiveInteraction回调。当收到其它仿真成员发送的chat交互时,按参数句柄解析昵称与语句并输出到终端;
32-33行:设置联邦执行名称与FED文件名;
35-41行:从控制台读取仿真成员名(昵称);若输入为空则使用默认名 user;
43-48行:创建RTI对象;若失败则退出;defer保证程序结束时关闭RTI;
50-57行:创建联邦执行;按HLA标准,通常只有第一个仿真成员能创建成功,若联邦已存在则忽略该错误并继续;
59-63行:加入联邦执行,并将仿真成员大使注册给RTI;
65-81行:获取交互类chat及其参数name、sentence的句柄,并保存到仿真成员大使中,供回调时识别参数;
83-86行:公布交互类,只有公布之后才能够向RTI发送交互;
87-90行:订购交互类,只有订购之后才能够从RTI收到其他人的聊天内容;
92-109行:循环操作,每次输入一句话,组装参数表并调用SendInteraction服务发送给RTI;当用户输入"exit"时则退出循环;
111行:退出联邦执行;
112-119行:尝试销毁联邦执行;若仍有其它仿真成员加入或联邦已不存在,则忽略相应错误。
14.2.4 编译运行
14. 2.4.1 编译
编译Go程序前需已安装 Go(建议1.22及以上),并正确设置KY-RTI环境,使Go能够解析kyrti包。在示例目录下执行:
Linux
bash
source $RTI_HOME/env.sh
cd $RTI_HOME/apps-go/chat
go build -o chat .
Windows
Go
call %RTI_HOME%\env.bat
cd /d %RTI_HOME%\apps-go\chat
go build -o chat.exe .
Windows 上使用Gobuild 时,通常还需准备64位MinGW的gcc供Go工具链使用;具体说明见apps-go/chat/README.txt。另外,为方便用户编译程序,在Linux下还提供了Makefile。
14. 2.4.2 测试运行
测试项目:在同一台机器上运行1个Go仿真成员、1个C++仿真成员、1个Java仿真成员、1个Python仿真成员、1个C#仿真成员、1个Rust仿真成员,验证KY-RTI聊天通信和不同语言间的互操作功能。
测试步骤:
第1步:确认RTI.rid中关闭tick开关。因为本程序没有使用tick服务,所以需要关闭tick。查看程序目录下RTI.rid,将";; UsingTickSwitch On"改为";; UsingTickSwitch Off"。
第2步:启动KY-RTI控制台RTIManager。注意bin目录中configure.rti的IP地址和端口号,要与仿真程序目录中的RTI.rid一致。6个仿真成员加入后,控制台如下图所示,与之前相比重新设计了界面并添加了更多信息。
cd $RTI_HOME/bin
./RTIManager.sh # Linux
或 RTIManager.bat # Windows

第3步:开启6个终端,依次进入apps-go/chat、apps-cpp/chat、apps-java/chat、apps-python/chat、apps-csharp/chat、apps-rust/chat,分别运行1个Go仿真成员、1个C++仿真成员、1个Java仿真成员、1个Python仿真成员、1个C#仿真成员、1个Rust仿真成员,name分别输入Go、C++、Java、Python、C#、Rust。下面各个图首先用ls命令列出该目录下的文件,可以看到为各个语言都提供了Makefile用来编译代码,也可以手工执行编译命令;之后执行聊天程序,不同语言的执行方式略有不同。






第4步:在6个终端上各输入一句话:"你好,我是XXX成员"。譬如,在第1个终端上输入"你好,我是Go成员"。

第5步:依次查看6个终端,每个仿真成员都收到了来自其他语言的仿真成员的聊天信息。下面是第1个终端的聊天信息。

测试结果表明:6种语言仿真成员可以实现跨语言通信。
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数据分发管理的应用)