1. 新建ild文件

新建一个txt文档,命名为:MessageTopic.idl
使用文本工具打开这个文件,输入以下内容后保存

module MessageTopic{
	#pragma DCPS_DATA_TYPE "MessageTopic::Msg"
	#pragma DCPS_DATA_KEY "MessageTopic::Msg uuid"

	struct Msg{
		string uuid;
		string msgInfo;
	};
};

备注:
MessageTopic::Msg:必须和模块名字和结构体的名称一致;
MessageTopic::Msg uuid:uuid必须是结构体中的成员,名字保持一致。
DCPS_DATA_TYPE:idl要求定义的数据类型,而且必须是一个结构体,结构体内部的数据类型可以根据需要自定义。在编译的时候,会自动根据这个宏编译。
DCPS_DATA_KEY:表明了DCPS_DATA_TYPE数据类型的一个键值。键值用来区分同一个主题内的不同实体。在上面的idl定义中:uuid是数据类型MessageTopic::Msg唯一的建,每次发布时都必须是唯一的uuid值,这样可以区分不同的实例。

2. 编译ild文件

通过开始菜单,打开【VS2015开发人员命令提示】

ODL restconf开发 opendds开发实例_ci


进入到MessageTopic.idl所在的目录。

输入命令:

tao_idl MessageTopic.idl

如果不能运行,则要查看ACE_ROOT、TAO_ROOT、DDS_ROOT的环境变量是否添加上,并且把ACE和DDS的bin和lib目录添加到Path中。

ODL restconf开发 opendds开发实例_ODL restconf开发_02


编译完成之后,在对应的目录下面会对应的出现几个文件

ODL restconf开发 opendds开发实例_#include_03


然后在使用openDDS编译idl,同样通过输入命令编译:

opendds_idl MessageTopic.idl

ODL restconf开发 opendds开发实例_数据_04


编译完成之后,会多生成3个文件:

ODL restconf开发 opendds开发实例_ODL restconf开发_05


其中MessageTopicTypeSupport.idl包含了MsgTypeSupport、MsgDataWriter、MsgDataReader的定义。这个定义的接口数据类型会在稍后注册数据类型、发布数据样本和接收数据样本中使用

3. 编辑mpc文件

新建MessageTopic.mpc文件,和idl文件在同一个目录下,把以下内容拷到文件中:

project(*idl): dcps{
    TypeSupport_Files{    
        MessageTopic.idl
    }
    custom_only = 1
}

在输入命令:

mwc.pl -type vc14

ODL restconf开发 opendds开发实例_数据_06


在目录下面就会生成sln文件,使用VS2015打开.sln文件,编译通过!

4. 编写发布端

在原来的mpc文件中添加如下内容,并且在目录下面新建一个Publisher.cpp的空文件。

project(*Publisher) : dcpsexe_with_tcp {
 exename   = publisher
 after   += *idl
 TypeSupport_Files {
   MessageTopic.idl
 }

 Source_Files {
   Publisher.cpp
 }
}

添加之后的mpc的内容为:

ODL restconf开发 opendds开发实例_#include_07


然后在运行命令行:

mwc.pl -type vc14

重新生成sln文件。打开sln文件编写Publisher.cpp文件的内容;

ODL restconf开发 opendds开发实例_#include_08


Publisher.cpp文件中的内容:

#include "MessageTopicTypeSupportImpl.h"
#include <dds/DCPS/Service_Participant.h>
#include <dds/DCPS/Marked_Default_Qos.h>
#include <dds/DCPS/PublisherImpl.h>
#include <dds/DCPS/transport/tcp/TcpInst.h>

#include "dds/DCPS/StaticIncludes.h"

#include <ace/streams.h>
#include "ace/Get_Opt.h"
#include "ace/OS_NS_unistd.h"

int main(int argc, char *argv[]) {
	//1.为当前进程初始化一个OpenDDS参与者
	/*
	调用宏TheParticipantFactoryWithArgs使用命令行初始化参与者工厂,
	这个命令行参数用来初始化ORB服务
	*/
	DDS::DomainParticipantFactory_var dpf = TheParticipantFactoryWithArgs(argc, argv);

	/*
	在域参与者工厂中注册一个域参与者,使用默认的QoS策略,
	指定域ID为42(域ID可以范围0x0-0x7FFFFFFF之间的任意值)
	使用DDS默认的状态掩码,确保所有在中间件中的相关通信状态改变都能传递到应用程序中
	返回值:域参与对象的引用,用来注册待公布的数据类型
	*/
	DDS::DomainParticipant_var participant = dpf->create_participant(42
		, PARTICIPANT_QOS_DEFAULT
		, 0
		, OpenDDS::DCPS::DEFAULT_STATUS_MASK);

	//2.注册数据类型并创建主题
	MessageTopic::MsgTypeSupport_var mts = new MessageTopic::MsgTypeSupportImpl();
	
	/*
	调用register_type注册一个带有类型名称的类型
	下面代码中:使用空类型名称,DDS缺省会把MsgTypeSupport接口标识符作为该类型的名称,
	当然也可以输入“msg”这样的特定类型的名称
	*/
	if (DDS::RETCODE_OK != mts->register_type(participant, "")) {
		return -1;
	}

	/*
	从类型支持对象中获得注册类型名称,调用create_topic来创建主题.
	下面创建一个名称为“MsgInfo”的主题,默认主题类型和默认的QoS策略

	*/
	CORBA::String_var typeName = mts->get_type_name();
	DDS::Topic_var topic = participant->create_topic("MsgInfo"
		, typeName,
		TOPIC_QOS_DEFAULT,
		0,
		OpenDDS::DCPS::DEFAULT_STATUS_MASK);
	if (!topic){
		return -2;
	}

	//3.创建公布者
	/*
	创建一个默认公布者QoS策略的公布者
	*/
	DDS::Publisher_var pub = participant->create_publisher(PUBLISHER_QOS_DEFAULT
		,0
		,OpenDDS::DCPS::DEFAULT_STATUS_MASK	);
	if (!pub){
		return -3;
	}

	//4.创建数据写入者
	/*
	有了公布者,在调用create_datawriter()创建一个数据写入者
	使用已经创建好的主题,默认的QoS策略和空的监听者
	*/
	DDS::DataWriter_var writer = pub->create_datawriter(topic
		,DATAWRITER_QOS_DEFAULT
		,0
		, OpenDDS::DCPS::DEFAULT_STATUS_MASK);
	if (!writer){
		return -4;
	}
	/*
	将数据写入者引用转换为MsgDataWriter对象引用,方便使用户数写入者类已经定义好的接口
	*/
	MessageTopic::MsgDataWriter_var msgWriter = MessageTopic::MsgDataWriter::_narrow(writer);

	//5.发布数据
	/*
	对于每个循环,调用write将数据发送给所有注册过改主题的订阅者
	*/
	MessageTopic::Msg msg;
	msg.uuid = "1";
	msg.msgInfo = "qqqqwqefgdafd";
	DDS::ReturnCode_t error = msgWriter->write(msg, DDS::HANDLE_NIL);

	if (error != DDS::RETCODE_OK){
		return -5;
	}
	//6.等待接收
	/*
	由于DDS中的数据公布和数据订阅是解耦的,数据发布出去之后,接受者不一定能接收到。
	如果发布者要求所有的发布的数据全部被交付,需要在发布端调用调用数据写入者的wait_for_acknowledgements()操作,
	来使发布端的应用程序一直等待,处于阻塞状态,直到订阅端接收到所有发布的数据。
	要使wait_for_acknowledgements()操作有效,数据读者必须使用QoS策略为RELIABILITY。
	QoS策略缺省值为RELIABLE.

	数据写入者调用这个函数,并且绑定一个timeout值作为等待的超时时间。
	*/

	DDS::Duration_t shutdownDelay = { 15,0 };
	DDS::ReturnCode_t result = writer->wait_for_acknowledgments(shutdownDelay);
	if (result != DDS::RETCODE_OK){
		return -6;
	}

	//7.实体清理
	/*
	在公布完数据以后,需要清理与OpenDDS相关的资源
	*/

	participant->delete_contained_entities();
	dpf->delete_participant(participant);
	TheServiceParticipant->shutdown();

	return 0;
}

6. 编写订阅端

同样和发布端一样,在mpc文件中,添加如下内容:

project(*Subscriber) : dcpsexe_with_tcp {
 exename   = subscriber
 after   += *idl
 TypeSupport_Files {
   MessageTopic.idl
 }
 Source_Files {
   Subscriber.cpp
   DataReaderListenerImpl.cpp
 }
}

添加之后的mpc文件:

ODL restconf开发 opendds开发实例_ci_09


和编译发布端一样,同样使用【VS2015开发者工具】命令窗口,输入以下命令:

mwc.pl -type vc14

并且在目录下面新建三个空文件:Subscriber.cpp、DataReaderListenerImpl.h、DataReaderListenerImpl.cpp,分别用来编写订阅端的逻辑代码,并且把这三个文件添加到项目中。

ODL restconf开发 opendds开发实例_数据_10


Subscriber.cpp的源码

#include "MessageTopicTypeSupportImpl.h"
#include <dds/DCPS/Service_Participant.h>
#include <dds/DCPS/Marked_Default_Qos.h>
#include <dds/DCPS/PublisherImpl.h>
#include <dds/DCPS/transport/tcp/TcpInst.h>

#include "dds/DCPS/StaticIncludes.h"

#include <ace/streams.h>
#include "ace/Get_Opt.h"
#include "ace/OS_NS_unistd.h"
#include "DataReaderListenerImpl.h"

using namespace MessageTopic;

int main(int argc, char *argv[]) {
	//1.初始化参与者
	DDS::DomainParticipantFactory_var dpf = TheParticipantFactoryWithArgs(argc, argv);
	DDS::DomainParticipant_var participant = dpf->create_participant(42//域ID
		,PARTICIPANT_QOS_DEFAULT
		,0
		,OpenDDS::DCPS::DEFAULT_STATUS_MASK);

	if (!participant){
		return -1;
	}

	//2.注册数据类型并创建主题
	MessageTopic::MsgTypeSupport_var mts = new MessageTopic::MsgTypeSupportImpl();
	if (DDS::RETCODE_OK != mts->register_type(participant, "")){
		return -20;
	}

	CORBA::String_var typeName = mts->get_type_name();

	DDS::Topic_var topic = participant->create_topic("MsgInfo"
		,typeName
		,TOPIC_QOS_DEFAULT
		,0
		,OpenDDS::DCPS::DEFAULT_STATUS_MASK
		);
	if (!topic){
		return -21;
	}

	//3.创建订阅者
	/*
	调用create_subscriber()操作创建一个带有默认QoS策略的订阅者
	*/
	DDS::Subscriber_var sub = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT
		,0
		,OpenDDS::DCPS::DEFAULT_STATUS_MASK);

	if (!sub){
		return -3;
	}

	//4.创建数据读者以及监听者
	/*
	订阅端需要给数据读者关联一个监听者,用来接收数据的到达
	*/
	DDS::DataReaderListener_var listener(new DataReaderListenerImpl);

	/*
	采用默认的QoS策略创建读者,并将它与主题、刚刚创建的监听者对象相关联起来
	创建完成之后,主线程就可以去处理其他工作了。
	当有数据到达时,OpenDDS会调用监听者对象的回调接口通知,在DataReaderListenerImpl类的回调函数中接收需要的数据就可多了
	*/
	DDS::DataReader_var dr = sub->create_datareader(topic
		,DATAREADER_QOS_DEFAULT
		,listener
		,OpenDDS::DCPS::DEFAULT_STATUS_MASK);
	if (!dr){
		return -4;
	}

	while (1) {
		ACE_OS::sleep(1);
	}

	//5.清理实体
	/*
	在订阅完数据以后,需要清理与OpenDDS相关联的资源
	delete_contained_entities删除所有该参与者创建的主题、订阅者
	*/
	participant->delete_contained_entities();
	dpf->delete_participant(participant);
	TheServiceParticipant->shutdown();

	return 0;
}

DataReaderListenerImpl.h

// -*- C++ -*-
//
#ifndef DATAREADER_LISTENER_IMPL
#define DATAREADER_LISTENER_IMPL

#include <dds/DdsDcpsSubscriptionExtC.h>
#include <dds/DCPS/LocalObject.h>

#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */

class DataReaderListenerImpl
	: public virtual OpenDDS::DCPS::LocalObject<OpenDDS::DCPS::DataReaderListener> {
public:
	DataReaderListenerImpl();

	virtual ~DataReaderListenerImpl(void);

	virtual void on_requested_deadline_missed(
		DDS::DataReader_ptr reader,
		const DDS::RequestedDeadlineMissedStatus & status);

	virtual void on_requested_incompatible_qos(
		DDS::DataReader_ptr reader,
		const DDS::RequestedIncompatibleQosStatus & status);

	virtual void on_liveliness_changed(
		DDS::DataReader_ptr reader,
		const DDS::LivelinessChangedStatus & status);

	virtual void on_subscription_matched(
		DDS::DataReader_ptr reader,
		const DDS::SubscriptionMatchedStatus & status);

	virtual void on_sample_rejected(
		DDS::DataReader_ptr reader,
		const DDS::SampleRejectedStatus& status);

	virtual void on_data_available(
		DDS::DataReader_ptr reader);

	virtual void on_sample_lost(
		DDS::DataReader_ptr reader,
		const DDS::SampleLostStatus& status);

	virtual void on_subscription_disconnected(
		DDS::DataReader_ptr reader,
		const ::OpenDDS::DCPS::SubscriptionDisconnectedStatus & status);

	virtual void on_subscription_reconnected(
		DDS::DataReader_ptr reader,
		const ::OpenDDS::DCPS::SubscriptionReconnectedStatus & status);

	virtual void on_subscription_lost(
		DDS::DataReader_ptr reader,
		const ::OpenDDS::DCPS::SubscriptionLostStatus & status);

	virtual void on_connection_deleted(
		::DDS::DataReader_ptr reader);

	virtual void on_budget_exceeded(
		DDS::DataReader_ptr reader,
		const ::OpenDDS::DCPS::BudgetExceededStatus& status);

	long num_reads() const {
		return num_reads_;
	}

private:

	DDS::DataReader_var reader_;
	long                  num_reads_;
};

#endif /* DATAREADER_LISTENER_IMPL  */

DataReaderListenerImpl.cpp

// -*- C++ -*-
//
#include "DataReaderListenerImpl.h"
#include "MessageTopicTypeSupportC.h"
#include "MessageTopicTypeSupportImpl.h"
#include <dds/DCPS/Service_Participant.h>
#include <ace/streams.h>

using namespace MessageTopic;

DataReaderListenerImpl::DataReaderListenerImpl()
	: num_reads_(0) {
}

DataReaderListenerImpl::~DataReaderListenerImpl() {
}

void DataReaderListenerImpl::on_data_available(DDS::DataReader_ptr reader) {
	++num_reads_;

	try {
		MessageTopic::MsgDataReader_var message_dr = MessageTopic::MsgDataReader::_narrow(reader);
		if (CORBA::is_nil(message_dr.in())) {
			cerr << "read: _narrow failed." << endl;
			exit(1);
		}

		MessageTopic::Msg message;
		DDS::SampleInfo si;
		DDS::ReturnCode_t status = message_dr->take_next_sample(message, si);

		if (status == DDS::RETCODE_OK) {
			cout << "Message: is    = " << message.uuid << endl
				<< "         msgInfo = " << message.msgInfo << endl;

			cout << "SampleInfo.sample_rank = " << si.sample_rank << endl;
		} else if (status == DDS::RETCODE_NO_DATA) {
			cerr << "ERROR: reader received DDS::RETCODE_NO_DATA!" << endl;
		} else {
			cerr << "ERROR: read Message: Error: " << status << endl;
		}
	} catch (CORBA::Exception& e) {
		cerr << "Exception caught in read:" << endl << e << endl;
		exit(1);
	}
}

void DataReaderListenerImpl::on_requested_deadline_missed(
	DDS::DataReader_ptr,
	const DDS::RequestedDeadlineMissedStatus &) {
	cerr << "DataReaderListenerImpl::on_requested_deadline_missed" << endl;
}

void DataReaderListenerImpl::on_requested_incompatible_qos(
	DDS::DataReader_ptr,
	const DDS::RequestedIncompatibleQosStatus &) {
	cerr << "DataReaderListenerImpl::on_requested_incompatible_qos" << endl;
}

void DataReaderListenerImpl::on_liveliness_changed(
	DDS::DataReader_ptr,
	const DDS::LivelinessChangedStatus &) {
	cerr << "DataReaderListenerImpl::on_liveliness_changed" << endl;
}

void DataReaderListenerImpl::on_subscription_matched(
	DDS::DataReader_ptr,
	const DDS::SubscriptionMatchedStatus &) {
	cerr << "DataReaderListenerImpl::on_subscription_matched" << endl;
}

void DataReaderListenerImpl::on_sample_rejected(
	DDS::DataReader_ptr,
	const DDS::SampleRejectedStatus&) {
	cerr << "DataReaderListenerImpl::on_sample_rejected" << endl;
}

void DataReaderListenerImpl::on_sample_lost(
	DDS::DataReader_ptr,
	const DDS::SampleLostStatus&) {
	cerr << "DataReaderListenerImpl::on_sample_lost" << endl;
}

void DataReaderListenerImpl::on_subscription_disconnected(
	DDS::DataReader_ptr,
	const ::OpenDDS::DCPS::SubscriptionDisconnectedStatus &) {
	cerr << "DataReaderListenerImpl::on_subscription_disconnected" << endl;
}

void DataReaderListenerImpl::on_subscription_reconnected(
	DDS::DataReader_ptr,
	const ::OpenDDS::DCPS::SubscriptionReconnectedStatus &) {
	cerr << "DataReaderListenerImpl::on_subscription_reconnected" << endl;
}

void DataReaderListenerImpl::on_subscription_lost(
	DDS::DataReader_ptr,
	const ::OpenDDS::DCPS::SubscriptionLostStatus &) {
	cerr << "DataReaderListenerImpl::on_subscription_lost" << endl;
}

void DataReaderListenerImpl::on_budget_exceeded(
	DDS::DataReader_ptr,
	const ::OpenDDS::DCPS::BudgetExceededStatus&) {
	cerr << "DataReaderListenerImpl::on_budget_exceeded" << endl;
}

void DataReaderListenerImpl::on_connection_deleted(::DDS::DataReader_ptr reader) {

}

7. 生成exe

编译整个项目,在根目录下生成exe

ODL restconf开发 opendds开发实例_ci_11

aaa