OpenDDS application development steps

OpenDDS application development steps


Personal learning records, only to sort out the main process, no practical reference value.

1. Define the data type

By using IDL, the data types used by each DDS are defined. OpenDDS uses the #pragma directive to identify the data types that DDS transmits and processes. These data are processed by the TAO IDL compiler and the OpenDDS IDL compiler to generate the required code to transfer these types of data using OpenDDS. Examples are as follows:

module Supermarket {
	#pragma DCPS_DATA_TYPE "Supermarket::UpdateInf"
	#pragma DCPS_DATA_KEY "Supermarket::UpdateInf UpdateInfID"
	struct UpdateInf {
		long UpdateInfID;
		string UpdateInfTopic;
		string UpdateInfMessage;
		string PublisherName;
		string MTime;
		long ClockTime;
	};
	
	#pragma DCPS_DATA_TYPE "Supermarket::DefaultInf"
	#pragma DCPS_DATA_KEY "Supermarket::DefaultInf DefaultInfID"
	struct DefaultInf {
		long DefaultInfID;
		string DefaultInfTopic;
		string DefaultInfMessage;
		string PublisherName;
		string MTime;
		long ClockTime;
	};
 
};

DCPS_DATA_TYPEMarks a data type for use by OpenDDS; DCPS_DATA_KEYmarks the field identifying the DCPS data type, which is used as a key for that type. In the above example, the member UpdateInfID of Supermarket::UpdateInf is regarded as a key. Each sample published with a different UpdateInfID value will be defined as a different instance within the same topic.

2. Processing IDL


This step can be omitted, and then it can be automatically compiled by MPC (The Makefile, Project, And Workspace Creator).

3. Write a message publisher

3.1 Initialize participants

//初始化域参数工厂
DDS::DomainParticipantFactory_var dpf =
    TheParticipantFactoryWithArgs(argc, argv);
//创建域参数
DDS::DomainParticipant_var participant =
    dpf->create_participant(42, // domain ID
        PARTICIPANT_QOS_DEFAULT,
        0,  // No listener required
        OpenDDS::DCPS::DEFAULT_STATUS_MASK);
//域参数创建异常处理
if (!participant) {
    std::cerr << "create_participant failed." << std::endl;
    return 1;
}

3.2 Registration Data Types and Participation Subjects

//注册Supermarket::DefaultInf类型的类型支持类
Supermarket::DefaultInfTypeSupport_var ts = new Supermarket::DefaultInfTypeSupportImpl;
//注册异常处理
if (DDS::RETCODE_OK != ts->register_type(participant, "")) {
    std::cerr << "register_type failed." << std::endl;
    return 1;
}

//创建主题(DefaultInf Supermarket)
CORBA::String_var default_name = ts->get_type_name();
DDS::Topic_var default_topic =
    participant->create_topic("DefaultInf Supermarket",
        default_name,
        TOPIC_QOS_DEFAULT,
        0,   // No listener required
        OpenDDS::DCPS::DEFAULT_STATUS_MASK);
//主题创建异常处理
if (!default_topic) {
    std::cerr << "create_topic failed." << std::endl;
    return 1;
}

3.3 Create a publisher

//创建发布者
DDS::Publisher_var pub =
    participant->create_publisher(PUBLISHER_QOS_DEFAULT,
        0,    // No listener required
        OpenDDS::DCPS::DEFAULT_STATUS_MASK);
//发布者创建异常处理
if (!pub) {
    std::cerr << "create_publisher failed." << std::endl;
    return 1;
}

3.4 Create data writers and wait for subscribers

//创建数据写者
DDS::DataWriter_var DefaultWriter =
    pub->create_datawriter(default_topic,
        DATAWRITER_QOS_DEFAULT,
        0,    // No listener required
        OpenDDS::DCPS::DEFAULT_STATUS_MASK);
//数据写者创建异常
if (!DefaultWriter) {
    std::cerr << "create_dataDefaultWriter failed." << std::endl;
    return 1;
}

3.5 Release data samples

//写入数据样本
Supermarket::DefaultInf DefaultInf;
Supermarket::DefaultInf DefaultInf2;
DefaultInf.DefaultInfID= 0;
DefaultInf.DefaultInfMessage = "您有新的饿了么外卖订单,请及时处理!";
DefaultInf.DefaultInfTopic= "饿了么";
DefaultInf.PublisherName = "饿了么(我才不饿)的发布者";
DefaultInf2.DefaultInfID = 0;
DefaultInf2.DefaultInfMessage = "您有新的美团外卖订单,请及时处理!";
DefaultInf2.DefaultInfTopic = "美团";
DefaultInf2.PublisherName = "美团外卖送啥都快";
//开始发布了,发布default
//开始发布了,发布update
DDS::Duration_t timeout = { 30, 0 };
for (int i = 0; i < 1000; ++i) {
    time_t nowTime;
    time(&nowTime);
    struct timeb tb;
    ftime(&tb);	
    double rand_num = rand() / (RAND_MAX + 1.0);
    if (rand_num>0.5)
    {
        DefaultInf.ClockTime = clock();
        CORBA::String_var s_Time = CORBA::string_dup(ctime(&nowTime));
        DefaultInf.MTime = s_Time;
        DDS::ReturnCode_t error = DefaultInf_DefaultWriter->write(DefaultInf, DDS::HANDLE_NIL);
        ++DefaultInf.DefaultInfID;
        if (error != DDS::RETCODE_OK) {
            std::cerr << "default write failed." << std::endl;
            return 1;
        }	
        if (DefaultWriter->wait_for_acknowledgments(timeout) != DDS::RETCODE_OK)
        {
            std::cerr << "wait_for_acknowledgments failed." << std::endl;
            return 1;
        }
        cout << "[S01]发送了一条饿了么主题消息,发送时间是" << ctime(&nowTime) << endl;
    }
    else
    {
        DefaultInf2.ClockTime = clock();
        CORBA::String_var s_Time = CORBA::string_dup(ctime(&nowTime));
        DefaultInf2.MTime = s_Time;
        DDS::ReturnCode_t error = UpdateInf_DefaultWriter->write(DefaultInf2, DDS::HANDLE_NIL);
        ++DefaultInf2.DefaultInfID;
        if (error != DDS::RETCODE_OK) {
            std::cerr << "update write failed." << std::endl;
            return 1;
        }
        if (UpdateWriter->wait_for_acknowledgments(timeout) != DDS::RETCODE_OK)
        {
            std::cerr << "wait_for_acknowledgments failed." << std::endl;
            return 1;
        }
        cout << "[S01]发送了一条美团主题消息,发送时间是" << ctime(&nowTime) << endl;
    }
    Sleep(2500);
}

4. Implement subscribers

4.1 Initialize participants

//初始化域参数工厂
DDS::DomainParticipantFactory_var dpf =
    TheParticipantFactoryWithArgs(argc, argv);
//创建域参数
DDS::DomainParticipant_var participant =
    dpf->create_participant(42, // Domain ID
                            PARTICIPANT_QOS_DEFAULT,
                            0,  // No listener required
                            OpenDDS::DCPS::DEFAULT_STATUS_MASK);
//域参数创建异常处理
if (!participant) {
    std::cerr << "create_participant failed." << std::endl;
    return 1;
}

4.2 Register data types and create topics

//注册类型支持类
		Supermarket::DefaultInfTypeSupport_var mts = new Supermarket::DefaultInfTypeSupportImpl;
		//支持类异常处理
		if (DDS::RETCODE_OK != mts->register_type(participant, "")) {
			std::cerr << "Failed to register the DefaultInfTypeSupport." << std::endl;
			return 1;
		}
		//创建主题2(同样也是DefaultInf Supermarket)
		CORBA::String_var type_name = mts->get_type_name();
		DDS::Topic_var topic2 =
			participant->create_topic("DefaultInf Supermarket",
				type_name,
				TOPIC_QOS_DEFAULT,
				0,   // No listener required
				OpenDDS::DCPS::DEFAULT_STATUS_MASK);
		//主题异常处理
		if (!topic2) {
			std::cerr << "Failed to create_topic." << std::endl;
			return 1;
		}
		//创建主题(同样也是DefaultInf Supermarket)
		DDS::Topic_var topic =
			participant->create_topic("UpdateInf Supermarket",
				type_name,
				TOPIC_QOS_DEFAULT,
				0,   // No listener required
				OpenDDS::DCPS::DEFAULT_STATUS_MASK);
		//主题异常处理
		if (!topic) {
			std::cerr << "Failed to create_topic." << std::endl;
			return 1;
		}

4.3 Create a subscriber

//创建订阅者
		DDS::Subscriber_var sub =
			participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT,
				0,   // No listener required
				OpenDDS::DCPS::DEFAULT_STATUS_MASK);
		//异常处理
		if (!sub) {
			std::cerr << "Failed to create_subscriber." << std::endl;
			return 1;
		}

4.4 Create DataReader and Listener

Using the created Listener object, you can detect when data is available.

// 创建监听
DDS::DataReaderListener_var listener(new DataReaderListenerImpl);
// 创建数据读取者
			DDS::DataReader_var dr =
				sub->create_datareader(topic,
					DATAREADER_QOS_DEFAULT,
					listener,
					OpenDDS::DCPS::DEFAULT_STATUS_MASK);
			//读者创建异常处理
			if (!dr) {
				std::cerr << "create_datareader failed." << std::endl;
				return 1;
			}
			Supermarket::DefaultInfDataReader_var reader_i = Supermarket::DefaultInfDataReader::_narrow(dr);
			if (!reader_i)
			{
				std::cerr << "_narrow failed." << std::endl;
			}

5. DataReaderListener implementation

void DataReaderListenerImpl::on_data_available(DDS::DataReader_ptr reader)
{
	try {
		Supermarket::DefaultInfDataReader_var reader_i = Supermarket::DefaultInfDataReader::_narrow(reader);
		if (!reader_i) {
			std::cerr << "read: _narrow failed." << std::endl;
			return;
		}
		Supermarket::DefaultInf Supermarket_DefaultInf;
		DDS::SampleInfo si;
		DDS::ReturnCode_t status = reader_i->take_next_sample(Supermarket_DefaultInf, si);
		if (status == DDS::RETCODE_OK) {
			printf("\n");
			//std::cout << "【冷漠到不想吃饭小组开心提示您】请查看以下信息:" << std::endl;
			//std::cout << "SampleInfo.sample_rank = " << si.sample_rank << std::endl;
			//std::cout << "SampleInfo.instance_state = " << si.instance_state << std::endl;
			if (si.valid_data)
			{
				time_t nowTime;
				time(&nowTime);
				struct timeb tb;
				ftime(&tb);
				std::cout << "[S01]发送者消息编号=" << Supermarket_DefaultInf.DefaultInfID << std::endl
					<< "[S01]发送者毫秒级计时:" << Supermarket_DefaultInf.ClockTime<< std::endl
					<< "   消息标题: " << Supermarket_DefaultInf.DefaultInfTopic << std::endl
					<< "====================================================================="<<std::endl 
					<< Supermarket_DefaultInf.DefaultInfMessage << std::endl
					<< "=====================================================================" << std::endl 
					<< "消息来源:"<< Supermarket_DefaultInf.PublisherName<< std::endl
					<< "[S01]发送者时间:" << Supermarket_DefaultInf.MTime
					<< "[S01]本地接收时间:" << ctime(&nowTime) 
				    << "[S01]本地接收毫秒级计时:" << clock() << std::endl;
			}
			else if (si.instance_state == DDS::NOT_ALIVE_DISPOSED_INSTANCE_STATE)
			{
				std::cout << "当前实例句柄" << std::endl;
			}
			else if (si.instance_state == DDS::NOT_ALIVE_NO_WRITERS_INSTANCE_STATE)
			{
				std::cout << "instance is unregistered" << std::endl;
			}
			else
			{
				std::cerr << "ERROR: received unknown instance state "
					<< si.instance_state << std::endl;
			}
		}
		else if (status == DDS::RETCODE_NO_DATA) {
			std::cerr << "ERROR: reader received DDS::RETCODE_NO_DATA!" << std::endl;
		}
		else {
			std::cerr << "ERROR: read DefaultInf: Error: " << status << std::endl;
		}
	}
	catch (CORBA::Exception& e) {
		std::cerr << "Exception caught in main.cpp:" << std::endl
			<< e << std::endl;
		ACE_OS::exit(1);
	}

}

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

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

void DataReaderListenerImpl::on_liveliness_changed(
	DDS::DataReader_ptr,
	const DDS::LivelinessChangedStatus &)
{
	std::cout << "接收者生成或者死亡" << std::endl;
}

void DataReaderListenerImpl::on_subscription_matched(
	DDS::DataReader_ptr,
	const DDS::SubscriptionMatchedStatus &)
{
	std::cout << "匹配成功或匹配结束" << std::endl;
}

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

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

on_data_availablefor listening events.

6. Cleanup in OpenDDS client

Clients refer to publishers and subscribers, add codes in publisher.cppand subscriber.cppfiles, and clean up OpenDDS-related objects.

//实例清理
participant->delete_contained_entities();
dpf->delete_participant(participant);
TheServiceParticipant->shutdown();

7. Run the example

1. Start the DCPSInfoRepo service for centralized discovery

%DDS_ROOT%/bin/DCPSInfoRepo -ORBEndpoint iiop://localhost:12345

2. Double-click to start subscriber.exe and publisher.exe

The DCPSInfoRepo service can be used for centralized discovery, and the RTPS service can be used for peer-to-peer discovery. RTPS peer discovery is important when OpenDDS applications need to interoperate with non-OpenDDS implementations of the DDS specification that do not wish to use centralized discovery in OpenDDS deployments.

References

[1]. "Distributed System Real-time Publish Subscribe Data Distribution Technology"

[2]. https://opendds.org/

[3]. https://download.objectcomputing.com/OpenDDS/OpenDDS-latest.pdf

[4]. OpenDDS Java Development (2) - OpenDDS Test Based on Windows 10

おすすめ

転載: blog.csdn.net/wml00000/article/details/124361096