有对DDS技术了解、学习、开发和培训需求的,请加入QQ群:707895641(DDS专业技术辅导)。
***************************************************************************************************
基于OpenDDS的应用开发(Linux),大体和Windows平台类似,也分两个部分的工作:
(1)定义自己的IDL文件,并编译成传输数据类型通讯动态库.so文件;
(2)分别编写pub和sub程序,运行
具体步骤,有以下几个:
- 定义idl文件,如HelloMsg.ild
- 运行脚本,产生相应的消息类型符号导出头文件HelloMsgCommon_Export.h
- 编写mwc、mpc工作台和项目文件,如HelloMsg.mwc、HelloMsg.mpc
- 编写HelloMsgPub.cpp和HelloMsgSub.cpp代码;
- 执行make编译,产生动态库libHelloMsgCommon.so、HelloMsgPub、HelloMsgSub
- 运行发布和订阅程序,查看运行结果
步骤一、定义HelloMsg.idl
module Hello { #pragma DCPS_DATA_TYPE "Hello::HelloMsg" #pragma DCPS_DATA_KEY "Hello::HelloMsg msg" struct HelloMsg{ string msg; }; }; |
步骤二、制作HelloMsg消息导出符号头文件HelloMsgCommon_Export.h
perl %ACE_ROOT%/bin/generate_export_file.pl HelloMsgCommon > HelloMsgCommon_Export.h
步骤三、定义HelloMsg.mwc、HelloMsg.mpc
workspace { // the -relative and -include cmdlines make it so this workspace // does not have to be in the $DDS_ROOT directory tree. // tell MPC to substitute our DDS_ROOT environment variables for relative paths cmdline += -relative DDS_ROOT=$DDS_ROOT // tell the projects where to find the DDS base projects (*.mpb) cmdline += -include $DDS_ROOT/MPC/config } |
project(*Common) : dcps { sharedname = HelloMsgCommon dynamicflags = HELLOMSGCOMMON_BUILD_DLL libout = . requires += tao_orbsvcs requires += no_opendds_safety_profile after += Svc_Utils includes += $(TAO_ROOT)/orbsvcs idlflags += -I$(TAO_ROOT)/orbsvcs \ -Wb,export_macro=HelloMsgCommon_Export \ -Wb,export_include=HelloMsgCommon_Export.h dcps_ts_flags += -Wb,export_macro=HelloMsgCommon_Export TypeSupport_Files { HelloMsg.idl } IDL_Files { HelloMsgTypeSupport.idl HelloMsg.idl } // We only want the generated files Header_Files { } // We only want the generated files Source_Files { } } project(HelloMsgPub) : dcpsexe, dcps_tcp, svc_utils { after += *Common exename = HelloMsgPub requires += tao_orbsvcs requires += no_opendds_safety_profile includes += $(TAO_ROOT)/orbsvcs libs += HelloMsgCommon IDL_Files { } TypeSupport_Files { } Header_Files { } Source_Files { HelloMsgPub.cpp } Documentation_Files { dds_tcp_conf.ini dds_udp_conf.ini } } project(HelloMsgSub) : dcpsexe, dcps_tcp { after += *Common exename = HelloMsgSub requires += tao_orbsvcs requires += no_opendds_safety_profile includes += $(TAO_ROOT)/orbsvcs libs += HelloMsgCommon TypeSupport_Files { } IDL_Files { } Header_Files { } Source_Files { HelloMsgSub.cpp } Documentation_Files { dds_tcp_conf.ini dds_udp_conf.ini } } |
步骤五、编写HelloMsgPub.cpp和HelloMsgSub.cpp代码
HelloMsgPub.cpp
/**************************************************************** * * file: HelloMsgPub.cpp * desc: Provides a simple C++ 'hello world' DDS publisher. * This publishing application will send data * to the example 'hello world' subscribing * application (hello_sub). * ****************************************************************/ #include <dds/DCPS/Service_Participant.h> #include <dds/DCPS/Marked_Default_Qos.h> #include <dds/DCPS/PublisherImpl.h> #include "dds/DCPS/StaticIncludes.h" #include <ace/streams.h> #include <orbsvcs/Time_Utilities.h> #include <stdio.h> #include <string.h> #ifdef _WIN32 #include <windows.h> #else #include <unistd.h> #endif #include "HelloMsgTypeSupportImpl.h" /**************************************************************** * main() * * Perform OpenDDS setup activities: * - create a Domain Participant * - create a Publisher * - register the StringMsg data type * - create a Topic * - create a DataWriter * Write data ****************************************************************/ int main(int argc, char * argv[]) { DDS::DomainParticipant * domain; DDS::Publisher * publisher; DDS::Topic * topic; DDS::DataWriter * dw; Hello::StringMsg stringMsg; DDS::ReturnCode_t retval; DDS::DomainParticipantFactory *dpf = TheParticipantFactoryWithArgs(argc, argv); if ( dpf == NULL ) { printf("ERROR initializing domainParticipantFactory.\n"); return -1; } /* create a DomainParticipant */ domain = dpf->create_participant( 2, PARTICIPANT_QOS_DEFAULT, NULL, 0 ); if ( domain == NULL ) { printf("ERROR creating domain participant.\n"); return -1; } /* create a Publisher */ publisher = domain->create_publisher(PUBLISHER_QOS_DEFAULT, NULL, 0 ); if ( publisher == NULL ) { printf("ERROR creating publisher.\n"); return -1; } /* Register the data type with the OpenDDS middleware. * This is required before creating a Topic with * this data type. */ Hello::StringMsgTypeSupport *stringMsgTS = new Hello::StringMsgTypeSupportImpl();; retval = stringMsgTS->register_type( domain, "StringMsg" ); if (retval != DDS::RETCODE_OK) { printf("ERROR registering type: %s\n", "DDS_error(retval)"); return -1; } /* Create a DDS Topic with the StringMsg data type. */ topic = domain->create_topic("helloTopic", "StringMsg", TOPIC_QOS_DEFAULT, NULL, 0 ); if ( topic == NULL ) { printf("ERROR creating topic.\n"); return -1; } /* Create a DataWriter on the hello topic, with * default QoS settings and no listeners. */ dw = publisher->create_datawriter( topic, DATAWRITER_QOS_DEFAULT, NULL, 0 ); if (dw == NULL) { printf("ERROR creating data writer\n"); return -1; } /* Initialize the data to send. The StringMsg data type * has just one string member. * Note: Alwyas initialize a string member with * allocated memory -- the destructor will free * all string members. */ stringMsg.msg = new char[1024]; strcpy((char*)stringMsg.msg.in(), "Hello World from C++!"); int counter = 1; Hello::StringMsgDataWriter* sm_dw = Hello::StringMsgDataWriter::_narrow(dw); while ( 1 ) { sprintf((char*)stringMsg.msg.in(), "Hello World from OpenDDS C++! index=%d", counter); DDS::ReturnCode_t ret = sm_dw->write ( stringMsg, DDS::HANDLE_NIL ); printf("OpenDDS wrote a sample, index=%d\n", counter); fflush(stdout); if ( ret != DDS::RETCODE_OK) { printf("ERROR writing sample\n"); return -1; } #ifdef _WIN32 Sleep(1000); #else sleep(1); #endif counter++; } /* Cleanup */ retval = domain -> delete_contained_entities(); if ( retval != DDS::RETCODE_OK ) printf("ERROR (%s): Unable to cleanup DDS entities\n", "DDS_error(retval)"); dpf->delete_participant(domain); TheServiceParticipant->shutdown(); return 0; } |
HelloMsgSub.cpp
/**************************************************************** * * file: HelloMsgSub.cpp * desc: Provides a simple C++ 'Hello World' DDS subscriber. * This subscribing application will receive data * from the example 'hello world' publishing * application (hello_pub). * ****************************************************************/ #include <dds/DCPS/Service_Participant.h> #include <dds/DCPS/Marked_Default_Qos.h> #include <dds/DCPS/PublisherImpl.h> #include <ace/streams.h> #include <orbsvcs/Time_Utilities.h> #include "dds/DCPS/StaticIncludes.h" #include <stdio.h> #ifdef _WIN32 #include <windows.h> #else #include <unistd.h> #endif #include "HelloMsgTypeSupportImpl.h" #ifdef _WIN32 # define SLEEP(a) Sleep(a*1000) #else # define SLEEP(a) sleep(a); #endif int all_done = 0; /**************************************************************** * Construct a DataReaderListener and override the * on_data_available() method with our own. All other * listener methods will be default (no-op) functions. ****************************************************************/ class SubListener : public DDS::DataReaderListener { public: void on_data_available( DDS::DataReader * dr ); void on_requested_deadline_missed ( DDS::DataReader_ptr reader, const DDS::RequestedDeadlineMissedStatus & status); void on_requested_incompatible_qos ( DDS::DataReader_ptr reader, const DDS::RequestedIncompatibleQosStatus & status); 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 ); void on_sample_rejected( DDS::DataReader_ptr reader, const DDS::SampleRejectedStatus& status ); void on_sample_lost( DDS::DataReader_ptr reader, const DDS::SampleLostStatus& status ); }; /**************************************************************** * DataReader Listener Method: on_data_avail() * * This listener method is called when data is available to * be read on this DataReader. ****************************************************************/ void SubListener::on_data_available( DDS::DataReader * dr) { Hello::StringMsgSeq samples; DDS::SampleInfoSeq samples_info; DDS::ReturnCode_t retval; DDS::SampleStateMask ss = DDS::ANY_SAMPLE_STATE; DDS::ViewStateMask vs = DDS::ANY_VIEW_STATE; DDS::InstanceStateMask is = DDS::ANY_INSTANCE_STATE; /* Convert to our type-specific DataReader */ Hello::StringMsgDataReader* reader = Hello::StringMsgDataReader::_narrow( dr ); /* Take any and all available samples. The take() operation * will remove the samples from the DataReader so they * won't be available on subsequent read() or take() calls. */ retval = reader->take( samples, samples_info, DDS::LENGTH_UNLIMITED, ss, vs, is ); if ( retval == DDS::RETCODE_OK ) { /* iterrate through the samples */ for ( unsigned int i = 0;i < samples.length(); i++) { /* If this sample does not contain valid data, * it is a dispose or other non-data command, * and, accessing any member from this sample * would be invalid. */ if ( samples_info[i].valid_data) printf("OpenDDS received a sample, No=%d/%d, [%s]\n", i, samples.length(), samples[i].msg.in()); } fflush(stdout); /* read() and take() always "loan" the data, we need to * return it so OpenDDS can release resources associated * with it. */ reader->return_loan( samples, samples_info ); } else { printf("ERROR (%s) taking samples from DataReader\n", "DDS_error(retval)"); } } void SubListener::on_requested_deadline_missed ( DDS::DataReader_ptr reader, const DDS::RequestedDeadlineMissedStatus & status) { printf("ERROR (%s) on_requested_deadline_missed\n", "DDS_error(retval)"); } void SubListener::on_requested_incompatible_qos ( DDS::DataReader_ptr reader, const DDS::RequestedIncompatibleQosStatus & status) { printf("ERROR (%s) on_requested_incompatible_qos\n", "DDS_error(retval)"); } void SubListener::on_liveliness_changed ( DDS::DataReader_ptr reader, const DDS::LivelinessChangedStatus & status) { printf("ERROR (%s) on_liveliness_changed\n", "DDS_error(retval)"); } void SubListener::on_subscription_matched ( DDS::DataReader_ptr reader, const DDS::SubscriptionMatchedStatus & status ) { printf("ERROR (%s) on_subscription_matched\n", "DDS_error(retval)"); } void SubListener::on_sample_rejected( DDS::DataReader_ptr reader, const DDS::SampleRejectedStatus& status ) { printf("ERROR (%s) on_sample_rejected\n", "DDS_error(retval)"); } void SubListener::on_sample_lost( DDS::DataReader_ptr reader, const DDS::SampleLostStatus& status ) { printf("ERROR (%s) on_sample_lost\n", "DDS_error(retval)"); } /**************************************************************** * main() * * Perform OpenDDS setup activities: * - create a Domain Participant * - create a Subscriber * - register the StringMsg data type * - create a Topic * - create a DataReader and attach the listener created above * And wait for data ****************************************************************/ #if defined(__vxworks) && !defined(__RTP__) int hello_sub(char * args) #else int main(int argc, char * argv[]) #endif { DDS::DomainParticipant * domain; DDS::Subscriber * subscriber; DDS::Topic * topic; DDS::DataReader * dr; SubListener drListener; DDS::ReturnCode_t retval; /* Get an instance of the DDS DomainPartiticpantFactory -- * we will use this to create a DomainParticipant. */ DDS::DomainParticipantFactory *dpf = TheParticipantFactoryWithArgs(argc, argv); if ( dpf == NULL ) { printf("ERROR initializing domainParticipantFactory.\n"); return -1; } /* create a DomainParticipant */ domain = dpf->create_participant( 2, PARTICIPANT_QOS_DEFAULT, NULL, 0 ); if ( domain == NULL ) { printf("ERROR creating domain participant.\n"); return -1; } /* create a Subscriber */ subscriber = domain->create_subscriber(SUBSCRIBER_QOS_DEFAULT, NULL, 0 ); if ( subscriber == NULL ) { printf("ERROR creating subscriber\n"); return -1; } /* Register the data type with the OpenDDS middleware. * This is required before creating a Topic with * this data type. */ Hello::StringMsgTypeSupport *stringMsgTS = new Hello::StringMsgTypeSupportImpl();; retval = stringMsgTS->register_type( domain, "StringMsg" ); if (retval != DDS::RETCODE_OK) { printf("ERROR registering type: %s\n", "DDS_error(retval)"); return -1; } /* create a DDS Topic with the StringMsg data type. */ topic = domain->create_topic( "helloTopic", "StringMsg", TOPIC_QOS_DEFAULT, NULL, 0 ); if ( ! topic ) { printf("ERROR creating topic\n"); return -1; } /* create a DDS_DataReader on the hello topic (notice * the TopicDescription is used) with default QoS settings, * and attach our listener with our on_data_available method. */ dr = subscriber->create_datareader( (DDS::TopicDescription*)topic, DATAREADER_QOS_DEFAULT, &drListener, DDS::DATA_AVAILABLE_STATUS ); if ( ! dr ) { printf("ERROR creating data reader\n"); return -1; } /* Wait forever. When data arrives at our DataReader, * our dr_on_data_avilalbe method will be invoked. */ while ( !all_done ) SLEEP(30); /* Cleanup */ retval = domain -> delete_contained_entities(); if ( retval != DDS::RETCODE_OK ) printf("ERROR (%s): Unable to cleanup DDS entities\n", "DDS_error(retval)"); dpf->delete_participant(domain); TheServiceParticipant->shutdown(); return 0; } |
步骤五、执行make编译
执行$ACE_ROOT/bin/mwc.pl -type make产生Makefile,
执行make产生libHelloMsg_Common.so和发布订阅程序HelloMsgPub和HelloMsgSub
步骤六、运行发布订阅程序
HelloMsgPub -ORBDebugLevel 0 -DCPSDebugLevel 0 -DCPSTransportDebugLevel 0 -DCPSConfigFile ../../etc/dds_udp_conf.ini -ORBLogFile publisher.log
HelloMsgSub -ORBDebugLevel 0 -DCPSDebugLevel 0 -DCPSTransportDebugLevel 0 -DCPSConfigFile ../../etc/dds_udp_conf.ini -ORBLogFile subscriber.log
Linux平台的编译脚本compile.sh
$ACE_ROOT/bin/generate_export_file.pl HelloCommon > HelloCommon_Export.h $ACE_ROOT/bin/mwc.pl -type make make |
**************************************************************************************************************