连续的三篇博文演示如何基于OpenDDS开发应用程序,将数据从发布端节点发送到订阅端节点,该示例程序由一个发布者发布数据,一个订阅者订阅数据,使用默认的QoS策略和TCP/IP传输方式。

本文是第三篇,主要介绍开发一个简单的OpenDDS订阅端应用程序所涉及的步骤。省略一些不重要部分(如:#include部分和异常处理等)代码,只写出关键代码。

1、新建订阅端工程:

参考前一博文中MPC的用法,在Demo.mpc文件中增加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 project(*Subscriber) : dcpsexe_with_tcp {

 exename = subscriber
after += *idl TypeSupport_Files {
Demo.idl
} Source_Files {
Subscriber.cpp
DataReaderListenerImpl.cpp
}
}

Subscriber工程从父工程dcpsexe_with_tcp继承,这里直接使用idl工程中定义好的Demo.idl文件。

之后在Demo目录下新建三个文件:Subscriber.cpp、DataReaderListenerImpl.h、DataReaderListenerImpl.cpp,分别用来编写订阅端逻辑部分代码,并再次使用如下命令来生成Vs2008工程:

1
mwc.pl -type vc9

生成完成之后,使用Vs2008打开Demo.sln,就可以修改订阅端代码了:

2、初始化参与者:

初始化订阅端参与者代码同发布端是完全一样的,在Subscriber.cpp文件中增加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(int argc, char *argv[])
{
try { 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、注册数据类型并创建主题:

接下来,初始化数据类型和主题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Demo::PosTypeSupport_var mts = new Demo::PosTypeSupportImpl();
if (DDS::RETCODE_OK != mts->register_type(participant, "")) {
std::cerr << "Failed to register the PosTypeSupport." << std::endl;
return 1;
} CORBA::String_var type_name = mts->get_type_name();
DDS::Topic_var topic =
participant->create_topic("Pos Demo",
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、创建订阅者:

调用create_subscriber()操作创建一个带有默认QoS策略的订阅者:

1
2
3
4
5
6
7
8
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;
}

5、创建数据读者及监听者:

订阅端需要给数据读者关联一个监听者,用来接收数据的到达,下面的代码定义了一个监听者对象,类DataReaderListenerImpl的实现将在下一部分介绍。

1
DDS::DataReaderListener_var listener(new DataReaderListenerImpl);

现在采用默认的QoS策略创建数据读者,并将它与主题、刚刚创建的监听者对象相关联起来:

1
2
3
4
5
6
7
8
9
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;
大专栏  基于OpenDDS应用程序开发(3)订阅端实现ne"> return 1;
}

之后,主线程就可以自由的去处理其它工作了,当有数据到达时,OpenDDS会调用监听者对象的回调接口通知,只需要在DataReaderListenerImpl类的回调函数中接收需要的数据就可以了。

6、数据读者监听者实现:

监听者类继承自DDS规范的DDS::DataReaderListener接口,该接口定义了一些回调函数,每个回调函数被调用时,就是一个事件的通知,如:断开、重连等,以下是DataReaderListener接口的定义:

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
module DDS {

 local interface DataReaderListener : Listener {

   void on_requested_deadline_missed(in DataReader reader,
in RequestedDeadlineMissedStatus status); void on_requested_incompatible_qos(in DataReader reader,
in RequestedIncompatibleQosStatus status); void on_sample_rejected(in DataReader reader,
in SampleRejectedStatus status); void on_liveliness_changed(in DataReader reader,
in LivelinessChangedStatus status); void on_data_available(in DataReader reader); void on_subscription_matched(in DataReader reader,
in SubscriptionMatchedStatus status); void on_sample_lost(in DataReader reader, in SampleLostStatus status); };
};

在本例的DataReaderListenerImpl类中真正需要的实现的回调接口是on_data_available(),它也是我们需要重新派生该类的唯一成员函数:

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
void DataReaderListenerImpl::on_data_available(DDS::DataReader_ptr reader) {

 num_reads_ ++;

 try{

   Demo::PosDataReader_var reader_i = Demo::PosDataReader::_narrow(reader);
if (!reader_i) {
std::cerr << "read: _narrow failed." << std::endl;
return;
} Demo::Pos pos;
DDS::SampleInfo si ;
DDS::ReturnCode_t status = reader_i->take_next_sample(pos, si) ;
if (status == DDS::RETCODE_OK) { if (si.valid_data == 1) {
std::cout << " Pos:pos_id = " << pos. pos_id << std::endl
<< " pos_x = " << pos. pos_x << std::endl
<< " pos_y = " << pos. pos_y << std::endl;
} else if (si.instance_state == DDS::NOT_ALIVE_DISPOSED_INSTANCE_STATE) {
std::cout << "instance is disposed" << 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) {
cerr << "ERROR: reader received DDS::RETCODE_NO_DATA!" << std::endl;
} else {
cerr << "ERROR: read Pos: " << status << std::endl;
}

上面的代码将样本从数据读者中取出,如果成功并能返回有效数据,就打印出接收到数据的每一个字段。

每当有样本数据到达时,该函数就会被调用。

7、实体清理:

在订阅完数据以后,需要清理与OpenDDS相关联的资源:

1
2
3
participant->delete_contained_entities();
dpf->delete_participant(participant);
TheServiceParticipant->shutdown();

调用域参与者的delete_contained_entities()操作删除所有该参与者创建的主题、订阅者。一旦执行完该操作,就可以使用域参与者工厂删除域参与者了。

8、示例程序运行:

修改完以上代码并编译完成,就可以运行订阅端应用程序了,需要先运行DDS的信息仓库,开始中打开一个CMD窗口,执行如下命令:

1
%DDS_ROOT%/bin/DCPSInfoRepo  -ORBListenEndpoints  iiop://localhost:12345

再次打开一个CMD窗口,cd到Demo目录下,执行如下命令:

1
subscriber -DCPSInfoRepo corbaloc::localhost:12345/DCPSInfoRepo

至此,订阅端应用程序就开发完成并运行起来了。

有关OpenDDS的相关问题欢迎发送邮件至lyingbo@aliyun.com一起讨论

基于OpenDDS应用程序开发(3)订阅端实现的更多相关文章

  1. Linux网络编程:基于TCP的程序开发回顾篇《转》

    面向连接的TCP程序设计 基于TCP的程序开发分为服务器端和客户端两部分,常见的核心步骤和流程: 其实按照上面这个流程调用系统API确实可以完全实现应用层程序的开发,一点问题没有.可随着时间的推移,你 ...

  2. Linux网络编程:基于UDP的程序开发回顾篇

    基于无连接的UDP程序设计 同样,在开发基于UDP的应用程序时,其主要流程如下:   对于面向无连接的UDP应用程序在开发过程中服务端和客户端的操作流程基本差不多.对比面向连接的TCP程序,服务端少了 ...

  3. 小程序开发时PC端调试返回结果和手机端IOS不一致问题

    IOS11登录时遇到一个请求与PC返回不一致情况, 在小程序调试时IOS上始终没有wx.request() 不能发送请求 尝试解决方法 打开微信小程序调试的设置, 将TLS设为可信任的域名 设置 -- ...

  4. WCF/WPF公司内部订餐程序开发

    WCF/WPF公司内部订餐程序开发 (服务端篇) 上班的第一天,群里讨论关于订餐的问题,所以想到了要不要自己开发一个公司内部的订餐系统呢?方便公司内部员工的订餐,有了想法就简单的实践了下 . 实现还是 ...

  5. Replication的犄角旮旯(一)--变更订阅端表名的应用场景

    <Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...

  6. 基于 SailingEase WinForm Framework 开发客户端程序(3:实现菜单/工具栏按钮的解耦及状态控制)

    本系列文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以  SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客 ...

  7. 基于MINA框架快速开发网络应用程序

    1.MINA框架简介 MINA(Multipurpose Infrastructure for Network Applications)是用于开发高性能和高可用性的网络应用程序的基础框架.通过使用M ...

  8. 基于微信小程序的系统开发准备工作

    腾讯推出微信小程序也有一段时间了,在各种行业里面也都掀起一阵阵的热潮,很多APP应用被简化为小程序的功能迅速推出,同时也根据小程序的特性推出各种独具匠心的应用,相对传统的APP来说,微信小程序确实能够 ...

  9. 基于Oracle ADF的应用程序开发

    ADF简介 ADF(Application Development Framework)是Oracle公司为简化J2EE程序开发的复杂性专门开发的一种解决方案,ADF通过减少实现设计模式和应用程序框架 ...

随机推荐

  1. 估计量|估计值|矩估计|最大似然估计|无偏性|无偏化|有效性|置信区间|枢轴量|似然函数|伯努利大数定理|t分布|单侧置信区间|抽样函数|

    第二章 置信区间估计 估计量和估计值的写法? 估计值希腊字母上边有一个hat 点估计中矩估计的原理? 用样本矩来估计总体矩,用样本矩的连续函数来估计总体矩的连续函数,这种估计法称为矩估计法.Eg:如果 ...

  2. dht算法原理描述

    dht原理 dht是P2P网络(结构化P2P)核心路由算法,主要是利用一致性hash,把节点和资源都表示成一个hash值,放入到这个大的hash环中,每个节点负责路由靠近它的资源. 一.重要概念:  ...

  3. 剑指offer【12】- 二进制中1的个数

    输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. public class Solution { public int NumberOf1(int n) { String str = ...

  4. redhat7.0安装postgresql

    安装命令 sudo yum install postgresql-server 查看安装的包 sudo yum list postgres* postgresql.x86_64 postgresql- ...

  5. poj1386单词连接(欧拉欧拉欧拉)

    ///单词连接,欧拉回路通路都可以(有向图) ///主要构图:比如possibilities就构造p->s的边////题目大意:给你若干个字符串,一个单词的尾部和一个单词的头部相同那么这两个单词 ...

  6. html 基础笔记

    html 1,一套规则,浏览器认识的规则 2,开发者: 学习Html规则 开发后台程序: -写Html文件(充当模板的作用) -数据库获取数据,然后替换到html文件的指定位置(web框架) 3,本地 ...

  7. 在win10下安装ubuntu双系统总结

    在打算装双系统前两天,我事先在网上买了一个16G大小的U盘,用来坐启动盘.后来发现其实有4G大小就远远足够的,16G的太浪费了,忘后来人吸取教训.呜呜呜.....下面给大家讲讲我的安装步骤: 参考文章 ...

  8. MySQL修改最大连接数的两个方法,偏爱第一种

    总结MySQL修改最大连接数的两个方式   最大连接数是可以通过mysql进行修改的,mysql数据库修改最大连接数常用有两种方法,今天我们分析一下这两种方法之间的特点和区别,以便我们能更好的去维护m ...

  9. Java--类以及对象

    什么是类 就是将一类事物的相同的本质特性抽象出来,类具有属性和方法,属性就是特征(具有什么),方法就是行为(能做什么). 类是一种引用的数据类型,类创建的对象的过程叫做实例化 什么是对象 对象就是类中 ...

  10. iMX6Q/PLUS开发板烧写设备树内核的Ubuntu系统

    基于迅为-iMX6D.iMX6Q 和 iMX6PLUS 三个硬件版本,设备树镜像的烧写方法以及镜像所在目录,镜像名称全部一致,所以作者将烧写章节合并到一起. 请注意,如果购买的是 iMX6D 版本,想 ...