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

本章介绍如何使用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-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)

相关推荐
microrain10 小时前
从数据烟囱到融合中心:SagooIoT统一数据处理平台的设计实践
物联网·golang·开源·sagooiot
DLYSB_13 小时前
API 网关流量洪峰与突发 CC 攻击:我用 Go 写了个“现场物理防御哨兵”,把故障响应压缩到秒级
开发语言·后端·golang·报警灯
朋克洛德的码农16 小时前
反射是什么?
golang
北冥you鱼2 天前
Go语言大数(big.Int)比较大小:原理、方法与最佳实践
开发语言·后端·golang
xcLeigh2 天前
Go入门:零值与变量默认初始化机制
开发语言·后端·golang
DLYSB_2 天前
数据库连接池爆满导致全线宕机:我用 Go 写了个“现场声光报警器”,比钉钉快了 10 倍
数据库·golang·钉钉·报警灯
Awna3 天前
Golang 大小写可见性规范
开发语言·后端·golang
进击的程序猿~3 天前
Go 内存分配与垃圾回收源码深度学习手册
开发语言·后端·golang