第一篇 一个例子
车载消息中间件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时序图
-
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
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:
rust
Subscriber* DomainParticipant::create_subscriber(
const SubscriberQos& qos,
SubscriberListener* listener,
const StatusMask& mask)
return impl_->create_subscriber(qos, listener, mask);
}
调用DomainParticipantImpl的create_subscriber
步骤2:
perl
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:
javascript
SubscriberImpl* DomainParticipantImpl::create_subscriber_impl(
const SubscriberQos& qos,
SubscriberListener* listener)
{
return new SubscriberImpl(this, qos, listener);
}
步骤4-5没啥关键信息
步骤6:
ini
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:
css
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
可以参考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:
javascript
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:
rust
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:
typescript
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
ini
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
ini
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:
rust
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类图
-
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消息