第一篇 一个例子
车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用
第二篇fastdds的组成
车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)
车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)
车载消息中间件FastDDS 源码解析(四)RtpsParticipant的创建(下)
车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)
车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP
车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager
车载消息中间件FastDDS 源码解析(八)TimedEvent
第三篇组网建立连接
pdp建连
车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)
FastDDS 源码解析(十二)发送第一条PDP消息(下)---异步发送
FastDDS 源码解析(十三)发送第一条PDP消息---跨进程发送
FastDDS 源码解析(十六)处理PDP消息——PDP匹配
EDP建连
FastDDS 源码解析(十七)处理PDP消息——EDP匹配
FastDDS 源码解析(十八)EDP阶段发送心跳heartbeat
FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息
FastDDS 源码解析(二十一)创建User的DataWriter(上)
FastDDS 源码解析(二十二)创建User的DataWriter(下)
上几篇,我们介绍了用户程序如何创建自己的DataWriter的
现在我们介绍一下用户程序如何创建自己的DataReader的
我们可以参考FastDDS 源码解析(二十一)创建User的DataWriter(上) 创建DataWriter和第一篇车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用的代码例子,和第一篇相关内容比较类似先创建 subscriber
1.用户程序创建DataReader
1.1时序图
sequenceDiagram
participant Application
participant DomainParticipantFactory
participant DomainParticipant
participant DomainParticipantImpl
participant TypeSupport
participant Subscriber
Application ->> DomainParticipantFactory:1.get_instance
DomainParticipantFactory ->> DomainParticipantFactory: 2.create_participant
DomainParticipantFactory ->> DomainParticipant: 3.new DomainParticipant
DomainParticipantFactory ->> DomainParticipantImpl: 4.new DomainParticipantImpl
DomainParticipantImpl ->> DomainParticipantFactory: return DomainParticipantImpl
DomainParticipantFactory ->> DomainParticipant: 5.enable
Application ->> TypeSupport:6.register_type
Application ->> DomainParticipantImpl: 7.create_subscriber
Application ->> DomainParticipantImpl: 8.create_topic
Application ->> Subscriber: 9.create_dataReader
-
DomainParticipantFactory::get_instance() 获取一个DomainParticipantFactory的对象
-
DomainParticipantFactory 调用create_participant主要干了三件事情:
a.new DomainParticipant() 见步骤3
b.new DomainParticipantImpl() 在这里DomainParticipant 和DomainParticipantImpl对象是一一对应关系 见步骤4
c.调用DomainParticipant对象的enable函数, 最终会调用到 DomainParticipantImpl对象 的enable函数,就是启动participant 见步骤5
-
new 了一个DomainParticipant
-
new了一个 DomainParticipantImpl()
-
调用DomainParticipant对象的enable函数
-
注册type,这个type 和 topic 是关联关系
-
只有同一个topic的2个participant 才能互相通信,通信内容的解析需要使用type解析
-
调用DomainParticipantImpl的create_subscriber函数,配置qos
-
调用DomainParticipantImpl的create_topic函数,配置qos
-
调用Publihser的create_datawriter函数,配置qos,发送消息就是由这个datawriter执行的
1.2源码
不再展开
参考第一篇车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用接收端代码示例部分
2.创建Subscriber
sequenceDiagram
participant User
participant DomainParticipant
participant DomainParticipantImpl
participant Subscriber
participant SubscriberImpl
User ->> DomainParticipant: 1.create_subscriber
DomainParticipant ->> DomainParticipantImpl: 2.create_subscriber
DomainParticipantImpl ->> DomainParticipantImpl: 3.create_subscriber_impl
DomainParticipantImpl ->> SubscriberImpl: 4.new
DomainParticipantImpl ->> Subscriber: 5.new
DomainParticipantImpl ->> Subscriber: 6.enable
Subscriber ->> SubscriberImpl: 7.enable
1.用户程序调用DomainParticipant的create_subscriber函数,这个函数调用的是DomainParticipantImpl的create_subscriber函数
2.DomainParticipantImpl的create_subscriber函数 主要干了4件事
a.创建SubscriberImpl对象 见3
b.在SubscriberImpl对象的基础上创建Subscriber 见4
c.给SubscriberImpl对象分配instance_handle(guid) 见5
d.调用Subscriber对象的enable函数 见6
3.create_subscriber_impl 创建SubscriberImpl对象
4.创建SubscriberImpl对象
5.在SubscriberImpl的基础上创建Subscriber
6.调用Subscriber的enable函数
7.调用SubscriberImpl对象的enable函数
步骤1:
Subscriber* DomainParticipant::create_subscriber(
const SubscriberQos& qos,
SubscriberListener* listener,
const StatusMask& mask)
return impl_->create_subscriber(qos, listener, mask);
}
调用DomainParticipantImpl的create_subscriber
步骤2:
Subscriber* DomainParticipantImpl::create_subscriber(
const SubscriberQos& qos,
SubscriberListener* listener,
const StatusMask& mask)
{
if (!SubscriberImpl::check_qos(qos))
{
// The SubscriberImpl::check_qos() function is not yet implemented and always returns ReturnCode_t::RETCODE_OK.
// It will be implemented in future releases of Fast DDS.
// EPROSIMA_LOG_ERROR(PARTICIPANT, "SubscriberQos inconsistent or not supported");
// return nullptr;
}
//TODO CONSTRUIR LA IMPLEMENTACION DENTRO DEL OBJETO DEL USUARIO.
SubscriberImpl* subimpl = create_subscriber_impl(qos, listener);
Subscriber* sub = new Subscriber(subimpl, mask);
subimpl->user_subscriber_ = sub;
subimpl->rtps_participant_ = get_rtps_participant();
// Create InstanceHandle for the new subscriber
InstanceHandle_t sub_handle;
bool enabled = get_rtps_participant() != nullptr;
// Create InstanceHandle for the new subscriber
create_instance_handle(sub_handle);
subimpl->handle_ = sub_handle;
//SAVE THE PUBLISHER INTO MAPS
std::lock_guard<std::mutex> lock(mtx_subs_);
subscribers_by_handle_[sub_handle] = sub;
subscribers_[sub] = subimpl;
// Enable subscriber if appropriate
if (enabled && qos_.entity_factory().autoenable_created_entities)
{
ReturnCode_t ret_subscriber_enable = sub->enable();
assert(ReturnCode_t::RETCODE_OK == ret_subscriber_enable);
(void)ret_subscriber_enable;
}
return sub;
}
主要干了4件事
1.创建SubscriberImpl对象 见步骤3
2.在SubscriberImpl对象的基础上创建Subscriber 见步骤4
3.给SubscriberImpl对象分配instance_handle(guid) 见步骤5
4.调用Subscriber对象的enable函数 见步骤6
步骤3:
SubscriberImpl* DomainParticipantImpl::create_subscriber_impl(
const SubscriberQos& qos,
SubscriberListener* listener)
{
return new SubscriberImpl(this, qos, listener);
}
步骤4-5没啥关键信息
步骤6:
ReturnCode_t Subscriber::enable()
{
if (enable_)
{
return ReturnCode_t::RETCODE_OK;
}
if (false == impl_->get_participant()->is_enabled())
{
return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET;
}
enable_ = true;
ReturnCode_t ret_code = impl_->enable();
enable_ = ReturnCode_t::RETCODE_OK == ret_code;
return ret_code;
}
主要是调用了SubscriberImpl的enable函数 见步骤7:
步骤7:
ReturnCode_t SubscriberImpl::enable()
{
if (qos_.entity_factory().autoenable_created_entities)
{
std::lock_guard<std::mutex> lock(mtx_readers_);
for (auto topic_readers : readers_)
{
for (DataReaderImpl* dr : topic_readers.second)
{
dr->user_datareader_->enable();
}
}
}
return ReturnCode_t::RETCODE_OK;
}
调用SubscriberImpl中的DataReaderImpl的DataReader的enable函数
3.创建DataReader
sequenceDiagram
participant User
participant Subscriber
participant SubscriberImpl
participant DataReaderImpl
participant DataReader
participant RTPSDomainImpl
participant RTPSParticipantImpl
User ->> Subscriber:1.create_datareader
Subscriber ->> SubscriberImpl:2.create_datareader
SubscriberImpl ->> SubscriberImpl:3.create_datareader_impl
SubscriberImpl ->> DataReaderImpl:4.new
SubscriberImpl ->> DataReader:5.new
SubscriberImpl ->> DataReader:6.enable
DataReader ->> DataReaderImpl:7.enable
DataReaderImpl ->> RTPSDomainImpl:8.create_rtps_reader
RTPSDomainImpl ->> RTPSParticipantImpl:9.create_reader
DataReaderImpl ->> RTPSParticipantImpl:10.registerReader
可以参考FastDDS 源码解析(二十一)创建User的DataWriter(上) 创建DataWriter,这里介绍的是创建DataReader,基本是类似的。
1.Subscriber的create_datareader函数调用的是SubscriberImpl的create_datareader函数
2.SubscriberImpl的create_datareader函数最终干了3件事
a.获取TypeSupport
b.调用create_datareader_impl函数来创建DataReaderImpl对象,传入参数TypeSupport对象 见步骤3
c.调用create_datareader创建 DataReader对象,传入参数DataReaderImpl对象 见步骤 5
d.调用DataReader的enable函数 见 步骤6
3.SubscriberImpl的create_datareader_impl函数,调用了DataReaderImpl的初始化函数 创建了一个DataReaderImpl的对象
4.new 了一个DataReaderImpl对象
5.new 了一个DataReader对象
6.DataReader对象的enable函数 调用的是DataReaderImpl对象的enable函数
7.DataReaderImpl对象的enable函数 主要干了2件事
a.调用RTPSDomainImpl的create_rtps_reader函数 见 8
b.调用RTPSParticipantImpl的registerReader函数 见 10
8.RTPSDomainImpl的create_rtps_reader函数主要是调用了RTPSParticipantImpl的create_reader函数
9.RTPSParticipantImpl的create_reader函数主要是创建了RTPSReader
10.RTPSParticipantImpl的registerReader函数主要是将reader的信息,存入PDP信息中,同时和writer进行匹配
步骤1:
DataReader* Subscriber::create_datareader(
TopicDescription* topic,
const DataReaderQos& reader_qos,
DataReaderListener* listener,
const StatusMask& mask)
{
return impl_->create_datareader(topic, reader_qos, listener, mask);
}
调用的是SubscriberImpl的create_datareader函数
步骤2:
DataReader* SubscriberImpl::create_datareader(
TopicDescription* topic,
const DataReaderQos& qos,
DataReaderListener* listener,
const StatusMask& mask)
{
EPROSIMA_LOG_INFO(SUBSCRIBER, "CREATING SUBSCRIBER IN TOPIC: " << topic->get_name());
//Look for the correct type registration
TypeSupport type_support = participant_->find_type(topic->get_type_name());
/// Preconditions
// Check the type was registered.
if (type_support.empty())
{
EPROSIMA_LOG_ERROR(SUBSCRIBER, "Type : " << topic->get_type_name() << " Not Registered");
return nullptr;
}
if (!DataReaderImpl::check_qos_including_resource_limits(qos, type_support))
{
return nullptr;
}
topic->get_impl()->reference();
DataReaderImpl* impl = create_datareader_impl(type_support, topic, qos, listener);
DataReader* reader = new DataReader(impl, mask);
impl->user_datareader_ = reader;
{
std::lock_guard<std::mutex> lock(mtx_readers_);
readers_[topic->get_name()].push_back(impl);
}
if (user_subscriber_->is_enabled() && qos_.entity_factory().autoenable_created_entities)
{
if (ReturnCode_t::RETCODE_OK != reader->enable())
{
delete_datareader(reader);
return nullptr;
}
}
return reader;
}
主要干了3件事
1.获取TypeSupport
2.调用create_datareader_impl函数来创建DataReaderImpl对象,传入参数TypeSupport对象 见步骤3
3.new了一个DataReader对象,传入参数DataReaderImpl对象
4.DataReader对象的enable函数 见 步骤6
步骤3:
DataReaderImpl* SubscriberImpl::create_datareader_impl(
const TypeSupport& type,
TopicDescription* topic,
const DataReaderQos& qos,
DataReaderListener* listener)
{
return new DataReaderImpl(this, type, topic, qos, listener);
}
步骤4 new DataReaderImpl 没什么关键代码
步骤5 new DataReader 没什么关键代码
步骤6
ReturnCode_t DataReader::enable()
{
if (enable_)
{
return ReturnCode_t::RETCODE_OK;
}
if (false == impl_->get_subscriber()->is_enabled())
{
return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET;
}
ReturnCode_t ret_code = impl_->enable();
enable_ = ReturnCode_t::RETCODE_OK == ret_code;
return ret_code;
}
主要调用了DataReaderImpl的enable函数
步骤7
ReturnCode_t DataReaderImpl::enable()
{
assert(reader_ == nullptr);
ReaderAttributes att;
att.endpoint.durabilityKind = qos_.durability().durabilityKind();
att.endpoint.endpointKind = READER;
att.endpoint.reliabilityKind = qos_.reliability().kind == RELIABLE_RELIABILITY_QOS ? RELIABLE : BEST_EFFORT;
att.endpoint.topicKind = type_->m_isGetKeyDefined ? WITH_KEY : NO_KEY;
att.endpoint.multicastLocatorList = qos_.endpoint().multicast_locator_list;
att.endpoint.unicastLocatorList = qos_.endpoint().unicast_locator_list;
att.endpoint.remoteLocatorList = qos_.endpoint().remote_locator_list;
att.endpoint.external_unicast_locators = qos_.endpoint().external_unicast_locators;
att.endpoint.ignore_non_matching_locators = qos_.endpoint().ignore_non_matching_locators;
att.endpoint.properties = qos_.properties();
att.endpoint.ownershipKind = qos_.ownership().kind;
att.endpoint.setEntityID(qos_.endpoint().entity_id);
att.endpoint.setUserDefinedID(qos_.endpoint().user_defined_id);
att.times = qos_.reliable_reader_qos().times;
att.liveliness_lease_duration = qos_.liveliness().lease_duration;
att.liveliness_kind_ = qos_.liveliness().kind;
att.matched_writers_allocation = qos_.reader_resource_limits().matched_publisher_allocation;
att.expectsInlineQos = qos_.expects_inline_qos();
att.disable_positive_acks = qos_.reliable_reader_qos().disable_positive_ACKs.enabled;
// TODO(Ricardo) Remove in future
// Insert topic_name and partitions
Property property;
property.name("topic_name");
property.value(topic_->get_impl()->get_rtps_topic_name().c_str());
att.endpoint.properties.properties().push_back(std::move(property));
std::string* endpoint_partitions = PropertyPolicyHelper::find_property(qos_.properties(), "partitions");
if (endpoint_partitions)
{
property.name("partitions");
property.value(*endpoint_partitions);
att.endpoint.properties.properties().push_back(std::move(property));
}
else if (subscriber_->get_qos().partition().names().size() > 0)
{
property.name("partitions");
std::string partitions;
bool is_first_partition = true;
for (auto partition : subscriber_->get_qos().partition().names())
{
partitions += (is_first_partition ? "" : ";") + partition;
is_first_partition = false;
}
property.value(std::move(partitions));
att.endpoint.properties.properties().push_back(std::move(property));
}
bool is_datasharing_compatible = false;
ReturnCode_t ret_code = check_datasharing_compatible(att, is_datasharing_compatible);
if (ret_code != ReturnCode_t::RETCODE_OK)
{
return ret_code;
}
if (is_datasharing_compatible)
{
DataSharingQosPolicy datasharing(qos_.data_sharing());
if (datasharing.domain_ids().empty())
{
datasharing.add_domain_id(utils::default_domain_id());
}
att.endpoint.set_data_sharing_configuration(datasharing);
}
else
{
DataSharingQosPolicy datasharing;
datasharing.off();
att.endpoint.set_data_sharing_configuration(datasharing);
}
std::shared_ptr<IPayloadPool> pool = get_payload_pool();
RTPSReader* reader = RTPSDomain::createRTPSReader(
subscriber_->rtps_participant(),
guid_.entityId,
att, pool,
static_cast<ReaderHistory*>(&history_),
static_cast<ReaderListener*>(&reader_listener_));
if (reader == nullptr)
{
release_payload_pool();
EPROSIMA_LOG_ERROR(DATA_READER, "Problem creating associated Reader");
return ReturnCode_t::RETCODE_ERROR;
}
auto content_topic = dynamic_cast<ContentFilteredTopicImpl*>(topic_->get_impl());
if (nullptr != content_topic)
{
reader->set_content_filter(content_topic);
content_topic->add_reader(this);
}
reader_ = reader;
deadline_timer_ = new TimedEvent(subscriber_->get_participant()->get_resource_event(),
[&]() -> bool
{
return deadline_missed();
},
qos_.deadline().period.to_ns() * 1e-6);
lifespan_timer_ = new TimedEvent(subscriber_->get_participant()->get_resource_event(),
[&]() -> bool
{
return lifespan_expired();
},
qos_.lifespan().duration.to_ns() * 1e-6);
// Register the reader
ReaderQos rqos = qos_.get_readerqos(subscriber_->get_qos());
if (!is_datasharing_compatible)
{
rqos.data_sharing.off();
}
if (endpoint_partitions)
{
std::istringstream partition_string(*endpoint_partitions);
std::string partition_name;
rqos.m_partition.clear();
while (std::getline(partition_string, partition_name, ';'))
{
rqos.m_partition.push_back(partition_name.c_str());
}
}
rtps::ContentFilterProperty* filter_property = nullptr;
if (nullptr != content_topic && !content_topic->filter_property.filter_expression.empty())
{
filter_property = &content_topic->filter_property;
}
if (!subscriber_->rtps_participant()->registerReader(reader_, topic_attributes(), rqos, filter_property))
{
EPROSIMA_LOG_ERROR(DATA_READER, "Could not register reader on discovery protocols");
reader_->setListener(nullptr);
stop();
return ReturnCode_t::RETCODE_ERROR;
}
return ReturnCode_t::RETCODE_OK;
}
主要干了这几件事情
1.调用RTPSDomainImpl::create_rtps_reader创建了一个RTPSReader,这是真正发送消息的对象 见步骤8
RTPSReader的ReaderHistory和DataReaderImpl的DataReaderHistory是共用的,DataReaderHistory是ReaderHistory的子类
2.创建了2个TimedEvent
deadline_timer_
fastdds 有个qos 叫deadlineqos ,这个qos的参数一般是个周期性的时间,规定了在周期性的时间内,发送端必须发送一个消息出来。
在接收端在这个周期性的时间内需要收到一个消息。
lifespan_timer_
fastdds有个qos 叫lifespanqos,这个qos规定了,一个消息的存活时间,超过时间,会在发送端将消息移除,这个默认设置,会把存货时间设置为无限。
3.调用RTPSParticipant::registerReader,注册这个RTPSReader 见步骤10
步骤8:
RTPSReader* RTPSDomain::createRTPSReader(
RTPSParticipant* p,
const EntityId_t& entity_id,
ReaderAttributes& ratt,
const std::shared_ptr<IPayloadPool>& payload_pool,
ReaderHistory* rhist,
ReaderListener* rlisten)
{
RTPSParticipantImpl* impl = p->mp_impl;
if (impl)
{
RTPSReader* reader;
if (impl->createReader(&reader, ratt, payload_pool, rhist, rlisten, entity_id))
{
return reader;
}
}
return nullptr;
}
调用RTPSParticipantImpl对象的create_reader,来创建RTPSReader对象,如果是reliable的RTPSReader,就是StatefulReader,如果是besteffort的RTPSReader,就是StatlessReader。
这个函数我们在之前介绍过,创建RTPSReader,创建ReceiverResources,将ReceiverResources的MessageReceiver 与RTPSReader关联,当MessageReceiver收到消息后,将会分配给相应的RTPSWriter或RTPSReader,让他们进行消息处理
步骤10:
这块内容,我们在下一篇介绍
总结:我们看到,创建DataReader的过程和之前创建DataWriter的过程基本是一致的,略有不同
2.3类图
classDiagram
DomainParticipantImpl *-- SubscriberImpl
DomainParticipantImpl *-- Subscriber
Subscriber *-- SubscriberImpl
SubscriberImpl *-- DataReaderImpl
DataReaderImpl *-- RTPSReader
class DomainParticipantImpl {
+PublisherQos default_pub_qos_
+std::map<Publisher*, PublisherImpl*> publishers_
+SubscriberQos default_sub_qos_
+std::map<Subscriber*, SubscriberImpl*> subscribers_
}
class SubscriberImpl {
+std::map<std::string, std::vector<DataReaderImpl*>> readers_
}
class Subscriber {
+SubscriberImpl* mp_impl
}
class DataReaderImpl {
+RTPSReader reader_
}
-
DomainParticipantImpl管理所有的Subscriber对象
Subscriber 和 SubscriberImpl 存储在一个map中( std::map<Subscriber, SubscriberImpl> publishers_ )
-
Subscriber 类似于 SubscriberImpl的代理类,Subscriber 和 SubscriberImpl 是一一对应的。
-
SubscriberImpl管理着DataReaderImpl对象。
-
DataReader 和 RTPSReader 是一一对应的。
这块代码我理解有些部分是有点冗余的,Subscriber 和 SubscriberImpl,DataReader和DataReaderImpl没必要分成2个类。
第一篇 一个例子
车载消息中间件FastDDS 源码解析(一)FastDDS 介绍和使用
第二篇fastdds的组成
车载消息中间件FastDDS 源码解析(二)RtpsParticipant的创建(上)
车载消息中间件FastDDS 源码解析(三)RtpsParticipant的创建(中)
车载消息中间件FastDDS 源码解析(四)RtpsParticipant的创建(下)
车载消息中间件FastDDS 源码解析(五)BuiltinProtocols(上)
车载消息中间件FastDDS 源码解析(六)BuiltinProtocols(中)EDP
车载消息中间件FastDDS 源码解析(七)BuiltinProtocols(下)WLP&TypeLookupManager
车载消息中间件FastDDS 源码解析(八)TimedEvent
第三篇组网建立连接
pdp建连
车载消息中间件FastDDS 源码解析(十)发送第一条PDP消息(上)
FastDDS 源码解析(十二)发送第一条PDP消息(下)---异步发送
FastDDS 源码解析(十三)发送第一条PDP消息---跨进程发送
FastDDS 源码解析(十六)处理PDP消息——PDP匹配
EDP建连
FastDDS 源码解析(十七)处理PDP消息——EDP匹配
FastDDS 源码解析(十八)EDP阶段发送心跳heartbeat
FastDDS 源码解析(十九)EDP阶段处理heartbeat消息,发送acknack消息