两种writer的代码分析
分析module/perception里面的segmentation和fusion组件都发出了消息,都调用了writer->Write(),但是前者的消息并没有发出来,后者的消息可以在Cyber_monitor中收到。不仅这两个,apollo中凡是channel name带了inner的都属于前者。
两者的writer定义分别如下:
segmentation:
writer_ = node_->CreateWriter<LidarFrameMessage>(output_channel_name_);
fusion:
writer_ = node_->CreateWriter<PerceptionObstacles>(
comp_config.output_obstacles_channel_name());
可以看到主要区别就在传进去的消息类型模板不同。
消息定义不同
初步分析可以看到这两个模块发出的消息定义是不同的。
segmentation的消息定义如下:
class LidarFrameMessage {
public:
LidarFrameMessage() : lidar_frame_(nullptr) {
type_name_ = "LidarFrameMessage";
}
~LidarFrameMessage() = default;
...
...
};
fusion的消息定义如下:
class PerceptionObstacles : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:apollo.perception.PerceptionObstacles) */ {
public:
PerceptionObstacles();
virtual ~PerceptionObstacles();
PerceptionObstacles(const PerceptionObstacles& from);
...
...
};
可以看到后者继承了protobuf,这是protobuf根据proto文件自动生成的。
那么对于这两种不同的消息,Write()函数到底做了什么操作呢。
Write()函数对两种消息的不同处理
可以看到`node_->CreateWriter`这个函数根据不同模板会有不同的定义,最终调用如下:
template <typename MessageT>
auto NodeChannelImpl::CreateWriter(const proto::RoleAttributes& role_attr)
-> std::shared_ptr<Writer<MessageT>> {
...
std::shared_ptr<Writer<MessageT>> writer_ptr = nullptr;
if (!is_reality_mode_) {
writer_ptr = std::make_shared<blocker::IntraWriter<MessageT>>(new_attr);
} else {
writer_ptr = std::make_shared<Writer<MessageT>>(new_attr);
}
...
return writer_ptr;
}
可见决定是否输出的是`is_reality_mode_`,如果为真,则调用else内容,即正常看到的protobuf方法,如果为假,则指向`blocker::IntraWriter<MessageT>`。再追进去看`IntraWriter`干了什么,这个IntraWriter类,里面有Write()函数,即为对inner message实际处理的Writer(),主要调用了publish:
template <typename MessageT>
bool IntraWriter<MessageT>::Write(const MessagePtr& msg_ptr) {
if (!WriterBase::IsInit()) {
return false;
}
return blocker_manager_->Publish<MessageT>(this->role_attr_.channel_name(),
msg_ptr);
}
这个publish是blocker的publish,主要干了两件事,Enqueue和Notify:
template <typename T>
void Blocker<T>::Publish(const MessagePtr& msg) {
Enqueue(msg);
Notify(msg);
}
Notify应该就是记录一下发了哪些msg,Enqueue的作用是向发送队列中加入msg。
template <typename T>
void Blocker<T>::Enqueue(const MessagePtr& msg) {
if (attr_.capacity == 0) {
return;
}
std::lock_guard<std::mutex> lock(msg_mutex_);
published_msg_queue_.push_front(msg);
while (published_msg_queue_.size() > attr_.capacity) {
published_msg_queue_.pop_back();
}
}
template <typename T>
void Blocker<T>::Notify(const MessagePtr& msg) {
std::lock_guard<std::mutex> lock(cb_mutex_);
for (const auto& item : published_callbacks_) {
item.second(msg);
}
}
其中`published_msg_queue_`是一个`MessageQueue`,后者是一列表,如下:
using MessageQueue = std::list<MessagePtr>;