# MakeFile

# 生成辅助文件

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

图 1 用户定义数据类型

2、 使用控制台进入进阶开发资源/uDDSGen 目录并执行 uDDSGen 可执行程序,对用户定义的 idl 文件进行编译。例如编译器名称为 uDDSGen,用户定义的 idl 文件名称为 UserDataType.idl,执行如下命令:

 ./uDDSGen UserDataType.idl
1

uDDSGen:idl 文件编译程序; UserDataType.idl:要用户自定义 idl 文件的文件名

如图所示。

图 2 编译 idl 文件

3、如果输出结果为用户编译的 idl 文件名称,如上图所示,则说明编译成功,编译器将在 IDL 目录下产生七个辅助文件放入“UserDataType_当前时间”文件夹下,如图所示。

图 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、新建两个目录并分别命令为“Pub”作为发布端工作目录、“Sub”作为订阅端工作目录(如下图所示): 注:目录名用户可自定义;

图 4 生成 Pub 和 Sub 文件夹

2、将生成的七个辅助文件复制到 Pub 文件夹及 Sub 文件夹下: 注:辅助文件不可修改。

图 5 复制辅助文件

3、用户可根据测试用例代码及 idl 文件编写自己的代码调用辅助文件中的 uDDS API 接口并复制到 Pub 或 Sub 目录下,本文以测试用例 pub.cpp 及 sub.cpp 为例。将“进阶开发资源/测试用例代码”中 pub.cpp 复制到 Pub 目录下,将 sub.cpp 复制到 Sub 目录下。

图 6 复制测试用例代码

4、将“进阶开发资源/MakeFile”目录下 Makefile 文件复制到 Pub 和 Sub 文件夹下:

图 7 复制 Makefile 文件

5、分别在 Pub 文件夹和 Sub 文夹下打开终端并执行 make 命令:

图 8 执行 make 语句

执行命令后得到发布端可执行程序“TestPub”和订阅端可执行程序“TestSub”:

图 9 生成可执行程序在当前目录

# 常见错误排查

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

# 找不到指定文件

如图所示。

图 10 拷贝 vs 版本失败

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

# Write error 200

如图所示。

图 11 write error 200

问题原因:没有对应的接收端演示程序。 解决方法:启动一个接收端演示程序即可。

# 发布端示例程序

/**********************************************************
*****************发布端程序pub.cpp*************************
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#if defined(_WIN32)
#else
#include <unistd.h>
#endif


/* 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);
#if defined(_WIN32)
		Sleep(1000);//沉睡1秒
#else
		sleep(1);//沉睡1秒
#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
170
171
172
173
174
175

# 订阅端示例程序

/**********************************************************
*****************订阅端程序sub.cpp*************************
***********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#if defined(_WIN32)
#else
#include <unistd.h>
#endif


/* 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) {
		//保持进程一直运行
#if defined(_WIN32)
		Sleep(1000);//沉睡1秒
#else
		sleep(1);//沉睡1秒
#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
188
189
190
191