# VS2017

# 生成辅助文件

1、进入 uDDSGen 文件,创建一个 idl 文件并在 idl 文件里面添加需要使用的数据类型,数据类型的定义形式为 IDL 文件,用户可以创建一个文本文件,然后将扩展名修改为“.idl”,并使用文本编辑器进行数据类型定义,如图所示 。

图 1 用户定义数据类型

2、 使用控制台进入 uDDSGen 目录并执行 uDDSGen.exe 程序,对用户定义的 idl 文件进行编译。例如编译器名称为 uDDSGen.exe,用户定义的 idl 文件名称为 UserDataType.idl,执行代码如下

uDDSGen.exe UserDataType.idl
1

如图所示。

图 2 编译 idl 文件

3、在当前目录下会生成一个以 IDL 名字+当前本地年月日时分组合的名字文件,在这个文件里面有 7 个辅助文件,如图所示。

图 3 辅助文件

这七个文件的主要内容如下:

  • IDL_DataReader.h:UserDataType 数据类型订阅接口定义
  • IDL_DataReader.cpp:UserDataType 数据类型订阅接口实现
  • IDL_DataWriter.h:UserDataType 数据类型发布接口定义
  • IDL_DataWriter.cpp:UserDataType 数据类型发布接口实现
  • IDL_TypeSupport.h:UserDataType 数据类型实现需要头文件及类
  • IDL_UserDataType.h:UserDataType 数据类型的定义
  • IDL_UserDataType.cpp:UserDataType 数据类型的函数实现

# 创建发布端/订阅端

使用生成的辅助代码进行用户程序开发,新建工程将上面生成的七个文件添加进来,并分别添加发布端/订阅端示例程序。

1、创建解决方案 uDDS_demo,在解决方案下创建项目 publisher 和 subscriber。打开 visual studio 2017,按如下步骤进行操作:“文件”(界面左上角)-> “新建”-> “项目”【或者使用快捷键 Ctrl+shift+N】。选择“Visual C++”里的“空项目”,并对解决方案和项目进行命名。【注:一个解决方案中可以有多个项目,每个项目都是一个单独的程序】在此处先创建解决方案 uDDS_demo 和项目 publisher,如图所示。

图 4 创建解决方案

2、返回主界面,打开解决方案资源管理器,再创建第二个项目 subscriber。右击“解决方案资源管理器”中的解决方案“uDDS_demo” ->“添加” -> “新建项目”。此时界面中只有设置项目名的输入框,输入项目名“subscriber”。点击确定,创建项目 subscriber。再次观察解决方案管理器,解决方案 uDDS_demo 下会有两个项目,如图所示。

图 5 publisher 和 subscribe 项目

3、将 IDL 编译器 uDDSGen.exe 生成的七个文件各复制一份到两个项目对应的本地目录中(~\uDDS_demo\publisher 目录和~\uDDS_demo\subscriber 目录,本地目录添加完成后,还需要在“解决方案资源管理器”的项目里手动导入, “右击头文件”->“添加现有项”->“选择所有.h 文件”,“右击源文件”->“添加现有项”->“选择所有.cpp 文件”,将每个项目对应目录里的文件导入(也可以直接把头文件和源文件拖入),同时我们提供了 pub.cpp 和 sub.cpp 发布和订阅代码模板参考,在“uDDS_VS2017_Trial\示例代码\ExampleCode winodws”您可以找到他们并加入到您的项目中,如图所示。

图 6 导入辅助文件

4、在配置项目属性前,x86 和 x64 的环境配置是独立,以下所有配置在切换平台之后都要重新配置,如图所示。

图 7 注意平台对应

5、为项目添加 uDDS 的头文件路径。在解决方案资源管理器中右击项目 publisher 点击属性。选择“C/C++” ->“常规”中的“附加包含目录”,设置头文件所在路径。(请注意,具体路径以在您电脑上的真实情况为准,主要就是把 uDDS 目录中的 include 目录添加进去)。在为 publisher 项目完成如上操作后,为 subscriber 项目进行相同的头文件路径添加操作。

图 8 添加“附加包含目录”

6、添加 uDDS 动态库,debug 版本的动态库名字为 uDDSd.lib,release 版本的动态库名字为 uDDS.lib 。

首先添加 lib 文件所在路径,右击项目名 publisher 或 subscriber,点击“属性”。选择“链接器” -> “常规” -> “附加库目录”,在其中设置 lib 文件所在路径。

图 9 添加 lib 路径

添加 lib 库名字。右击项目名,点击“属性”,选择“链接器” -> “输入” -> “附加依赖项”,在其中手动输入 lib 的名字。(debug 版本的动态库名字为 uDDSd.lib,release 版本的动态库名字为 uDDS.lib 。)如图所示。

图 10 release 版本 lib
图 11 debug 版本 lib

7、为程序添加 uDDS 动态库

将 uDDS.dll 拷贝到应用软件可执行程序的同一目录下,如果您需要调试请在调试目录下添加动态库文件。

# 错误排查

下面内容有助于解决一些问题,这些问题是在您拿到压缩文件到运行演示结束,过程中可能会遇到。如果这一阶段您没有出现问题,可以忽略。

# 拷贝文件失败

如图所示。

图 12 拷贝 vs 版本失败

问题原因:idl 文件输入有误。

解决方法:确认一下 DemoTool 文件下,用户输入的 idl 文件名字是否与 UserDataType.idl 对应。

# SDK 版本错误

如图所示。

图 13 SDK 版本错误

问题原因:用户使用的 SDK 版本和预先设定的 SDK 版本不统一。

解决方法:右击项目属性->配置属性->常规->Windows SDK 版本,选择一版您那边所拥有的即可,如图所示。

图 14 解决 SDK 问题

# Write error 200

如图所示。

图 15 write error 200

问题原因:没有对应的订阅端演示程序。

解决方法:启动一个订阅端演示程序即可。

# 发布端示例程序

/**********************************************************
*****************发布端程序pub.cpp*************************
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <cstring>

/* IDL_TypeSupport.h中包含所有依赖的头文件 */
#include "IDL_TypeSupport.h"

/* 删除所有实体 */
static int publisher_shutdown(DomainParticipant *participant)
{
	ReturnCode_t retcode;
	int status = 0;

	if (participant != NULL) {
		retcode = participant->delete_contained_entities();
		if (retcode != RETCODE_OK) {
			fprintf(stderr, "delete_contained_entities error %d\n", retcode);
			status = -1;
		}

		retcode = DomainParticipantFactory::get_instance()->delete_participant(participant);
		if (retcode != RETCODE_OK) {
			fprintf(stderr, "delete_participant error %d\n", retcode);
			status = -1;
		}
	}

	return status;
}

/* 发布者函数 */
extern "C" int publisher_main(int domainId, int sample_count)
{
	DomainParticipant *participant = NULL;
	Publisher *publisher = NULL;
	Topic *topic = NULL;
	DataWriter *writer = NULL;
	UserDataTypeDataWriter * UserDataType_writer = NULL;
	UserDataType *instance = NULL;
	ReturnCode_t retcode;
	InstanceHandle_t instance_handle = HANDLE_NIL;
	const char *type_name = NULL;
	int count = 0;

	/* 1. 创建一个participant,可以在此处定制participant的QoS */
	/* 建议1:在程序启动后优先创建participant,进行资源初始化*/
	/* 建议2:相同的domainId只创建一次participant,重复创建会占用大量资源 */
	participant = DomainParticipantFactory::get_instance()->create_participant(
		domainId, PARTICIPANT_QOS_DEFAULT/* participant默认QoS */,
		NULL /* listener */, STATUS_MASK_NONE);
	if (participant == NULL) {
		fprintf(stderr, "create_participant error\n");
		publisher_shutdown(participant);
		return -1;
	}

	/* 2. 创建一个publisher,可以在创建publisher的同时定制其QoS  */
	/* 建议1:在程序启动后优先创建publisher */
	/* 建议2:一个participant下创建一个publisher即可,无需重复创建 */
	publisher = participant->create_publisher(
		PUBLISHER_QOS_DEFAULT /* 默认QoS */,
		NULL /* listener */, STATUS_MASK_NONE);
	if (publisher == NULL) {
		fprintf(stderr, "create_publisher error\n");
		publisher_shutdown(participant);
		return -1;
	}

	/* 3. 在创建主题之前注册数据类型 */
	/* 建议1:在程序启动后优先进行注册 */
	/* 建议2:一个数据类型注册一次即可 */
	type_name = UserDataTypeTypeSupport::get_type_name();
	retcode = UserDataTypeTypeSupport::register_type(
		participant, type_name);
	if (retcode != RETCODE_OK) {
		fprintf(stderr, "register_type error %d\n", retcode);
		publisher_shutdown(participant);
		return -1;
	}

	/* 4. 创建主题,并定制主题的QoS  */
	/* 建议1:在程序启动后优先创建Topic */
	/* 建议2:一种主题名创建一次即可,无需重复创建 */
	topic = participant->create_topic(
		"Example UserDataType"/* 主题名 */,
		type_name /* 类型名 */, TOPIC_QOS_DEFAULT/* 默认QoS */,
		NULL /* listener */, STATUS_MASK_NONE);
	if (topic == NULL) {
		fprintf(stderr, "create_topic error\n");
		publisher_shutdown(participant);
		return -1;
	}

	/* 5. 创建datawriter,并定制datawriter的QoS  */
	/* 建议1:在程序启动后优先创建datawriter */
	/* 建议2:创建一次即可,无需重复创建 */
	/* 建议3:在程序退出时再进行释放 */
	/* 建议4:避免打算发送数据时创建datawriter,发送数据后删除,该做法消耗资源,影响性能 */
	writer = publisher->create_datawriter(
		topic , DATAWRITER_QOS_DEFAULT/* 默认QoS */,
		NULL /* listener */, STATUS_MASK_NONE);
	if (writer == NULL) {
		fprintf(stderr, "create_datawriter error\n");
		publisher_shutdown(participant);
		return -1;
	}
	UserDataType_writer = UserDataTypeDataWriter::narrow(writer);
	if (UserDataType_writer == NULL) {
		fprintf(stderr, "DataWriter narrow error\n");
		publisher_shutdown(participant);
		return -1;
	}

	/* 6. 创建一个数据样本 */
	/* 建议:该数据为new出来的,使用后用户需要调用delete_data进行释放内存*/
	instance = UserDataTypeTypeSupport::create_data();
	if (instance == NULL) {
		fprintf(stderr, "UserDataTypeTypeSupport::create_data error\n");
		publisher_shutdown(participant);
		return -1;
	}

	//此处可以修改数据的值
	instance->x = 3;
	instance->y = 4;
	instance->color = const_cast<char *>("red");

	/* 7. 主循环 ,发送数据 */
	for (count = 0; (sample_count == 0) || (count < sample_count); ++count) {

		retcode = UserDataType_writer->write(*instance, instance_handle);
		if (retcode != RETCODE_OK) {
			fprintf(stderr, "write error %d\n", retcode);
		}
		else
			fprintf(stderr, "%d : write  successfully . . \n", count);
		#ifdef ULINX_LINUX
			sleep(1);
		#elif (defined( ULINX_WIN) ||  defined(_WIN32))
			Sleep(1000);
		#endif //沉睡一秒
	}

	/* 8. 删除数据样本 */
	retcode = UserDataTypeTypeSupport::delete_data(instance);
	if (retcode != RETCODE_OK) {
		fprintf(stderr, "UserDataTypeTypeSupport::delete_data error %d\n", retcode);
	}

	/* 9. 删除所有实例 */
	return publisher_shutdown(participant);
}
int main(int argc, char *argv[])
{
	int domain_id = 0;
	int sample_count = 0; /* 无限循环 */

	if (argc >= 2) {
		domain_id = atoi(argv[1]);  /* 发送至域domain_id */
	}
	if (argc >= 3) {
		sample_count = atoi(argv[2]); /* 发送sample_count次 */
	}
	return publisher_main(domain_id, sample_count);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

# 订阅端示例程序

/**********************************************************
*****************订阅端程序sub.cpp*************************
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <cstring>

/* IDL_TypeSupport.h中包含所有依赖的头文件 */
#include "IDL_TypeSupport.h"

/* UserDataTypeListener继承于DataReaderListener,
   需要重写其继承过来的方法on_data_available(),在其中进行数据监听读取操作 */
class UserDataTypeListener : public DataReaderListener {
public:
	virtual void on_data_available(DataReader* reader);
};

/* 重写继承过来的方法on_data_available(),在其中进行数据监听读取操作 */
void UserDataTypeListener::on_data_available(DataReader* reader)
{
	UserDataTypeDataReader *UserDataType_reader = NULL;
	UserDataTypeSeq data_seq;
	SampleInfoSeq info_seq;
	ReturnCode_t retcode;
	int i;

	/* 利用reader,创建一个读取UserDataType类型的UserDataType_reader*/
	UserDataType_reader = UserDataTypeDataReader::narrow(reader);
	if (UserDataType_reader == NULL) {
		fprintf(stderr, "DataReader narrow error\n");
		return;
	}

	/* 获取数据,存放至data_seq,data_seq是一个队列 */
	retcode = UserDataType_reader->read(
		data_seq, info_seq,  DDS_LENGTH_UNLIMITED, DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, DDS_ANY_INSTANCE_STATE);
	if (retcode == RETCODE_NO_DATA) {
		return;
	}
	else if (retcode != RETCODE_OK) {
		fprintf(stderr, "take error %d\n", retcode);
		return;
	}

	/* 打印数据 */
	/* 建议1:避免在此进行复杂数据处理 */
	/* 建议2:将数据传送到其他数据处理线程中进行处理 *
	/* 建议3:假如数据结构中有string类型,用完后需手动释放 */
	for (i = 0; i < data_seq.length(); ++i) {
		UserDataTypeTypeSupport::print_data(&data_seq[i]);
	}
}

/* 删除所有实体 */
static int subscriber_shutdown(
	DomainParticipant *participant)
{
	ReturnCode_t retcode;
	int status = 0;

	if (participant != NULL) {
		retcode = participant->delete_contained_entities();
		if (retcode != RETCODE_OK) {
			fprintf(stderr, "delete_contained_entities error %d\n", retcode);
			status = -1;
		}

		retcode = DomainParticipantFactory::get_instance()->delete_participant(participant);
		if (retcode != RETCODE_OK) {
			fprintf(stderr, "delete_participant error %d\n", retcode);
			status = -1;
		}
	}
	return status;
}

/* 订阅者函数 */
extern "C" int subscriber_main(int domainId, int sample_count)
{
	DomainParticipant *participant = NULL;
	Subscriber *subscriber = NULL;
	Topic *topic = NULL;
	UserDataTypeListener *reader_listener = NULL;
	DataReader *reader = NULL;
	ReturnCode_t retcode;
	const char *type_name = NULL;
	int count = 0;
	int status = 0;

	/* 1. 创建一个participant,可以在此处定制participant的QoS */
	/* 建议1:在程序启动后优先创建participant,进行资源初始化*/
	/* 建议2:相同的domainId只创建一次participant,重复创建会占用大量资源 */
	participant = DomainParticipantFactory::get_instance()->create_participant(
		domainId, PARTICIPANT_QOS_DEFAULT/* participant默认QoS */,
		NULL /* listener */, STATUS_MASK_NONE);
	if (participant == NULL) {
		fprintf(stderr, "create_participant error\n");
		subscriber_shutdown(participant);
		return -1;
	}

	/* 2. 创建一个subscriber,可以在创建subscriber的同时定制其QoS  */
	/* 建议1:在程序启动后优先创建subscriber*/
	/* 建议2:一个participant下创建一个subscriber即可,无需重复创建 */
	subscriber = participant->create_subscriber(
		SUBSCRIBER_QOS_DEFAULT/* 默认QoS */,
		NULL /* listener */, STATUS_MASK_NONE);
	if (subscriber == NULL) {
		fprintf(stderr, "create_subscriber error\n");
		subscriber_shutdown(participant);
		return -1;
	}

	/* 3. 在创建主题之前注册数据类型 */
	/* 建议1:在程序启动后优先进行注册 */
	/* 建议2:一个数据类型注册一次即可 */
	type_name = UserDataTypeTypeSupport::get_type_name();
	retcode = UserDataTypeTypeSupport::register_type(
		participant, type_name);
	if (retcode != RETCODE_OK) {
		fprintf(stderr, "register_type error %d\n", retcode);
		subscriber_shutdown(participant);
		return -1;
	}

	/* 4. 创建主题,并定制主题的QoS  */
	/* 建议1:在程序启动后优先创建Topic */
	/* 建议2:一种主题名创建一次即可,无需重复创建 */
	topic = participant->create_topic(
		"Example UserDataType"/* 主题名,应与发布者主题名一致 */,
		type_name, TOPIC_QOS_DEFAULT/* 默认QoS */,
		NULL /* listener */, STATUS_MASK_NONE);
	if (topic == NULL) {
		fprintf(stderr, "create_topic error\n");
		subscriber_shutdown(participant);
		return -1;
	}

	/* 5. 创建一个监听器 */
	reader_listener = new UserDataTypeListener();

	/* 6. 创建datareader,并定制datareader的QoS */
	/* 建议1:在程序启动后优先创建datareader*/
	/* 建议2:创建一次即可,无需重复创建 */
	/* 建议3:在程序退出时再进行释放 */
	/* 建议4:避免打算接收数据时创建datareader,接收数据后删除,该做法消耗资源,影响性能 */
	reader = subscriber->create_datareader(
		topic, DATAREADER_QOS_DEFAULT/* 默认QoS */,
		reader_listener/* listener */, STATUS_MASK_ALL);
	if (reader == NULL) {
		fprintf(stderr, "create_datareader error\n");
		subscriber_shutdown(participant);
		delete reader_listener;
		return -1;
	}

	/* 7. 主循环 ,监听器会默认调用on_data_available()监听数据 */
	for (count = 0; (sample_count == 0) || (count < sample_count); ++count) {
		//保持进程一直运行
		#ifdef ULINX_LINUX
			sleep(1);
		#elif (defined( ULINX_WIN) ||  defined(_WIN32))
			Sleep(1000);
		#endif //沉睡一秒
	}

	/* 8. 删除所有实体和监听器 */
	status = subscriber_shutdown(participant);
	delete reader_listener;

	return status;
}

int main(int argc, char *argv[])
{
	int domain_id = 0;
	int sample_count = 0; /* 无限循环 */

	if (argc >= 2) {
		domain_id = atoi(argv[1]);/* 发送至域domain_id */
	}
	if (argc >= 3) {
		sample_count = atoi(argv[2]);/* 发送sample_count次 */
	}
	return subscriber_main(domain_id, sample_count);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187