在主函数初始化NodeHandle
对象的时候,在构造函数里,会有Streaming
对象的初始化
auto streaming = std::make_shared<Streaming>(nodes, i);
然后,会在构造函数的最后,调用Streaming
的成员函数来开启线程
streamings_[i]->start();
// start()成员函数
// Start thread
void Streaming::start()
{
if (!valid_) return;
// Create thread
quit_thread_ = false;
thread_.reset(new std::thread(&Streaming::run, this));
}
run()
函数run
函数一上来先开启了自选等待,停止的条件必须是quit_thread_
值为true
(要主动调用Streaming::stop()
才会把值设为true
)或者SpinControl::ok()
返回值要是false
(主动调用kill
函数)。
while (!quit_thread_ && SpinControl::ok()) {
// 这里是判断为true才会进来,所以停止的条件就是相反的情况
}
之后就是分别利用在构造函数中初始化的type
获得的标志符,针对输入、记录以及输出三种情况做不同的处理
if (has_input_) {
processInput();
}
if (has_logging_) {
mutex_logging_.lock();
processLogging();
mutex_logging_.unlock();
}
if (has_output_) {
mutex_output_.lock();
processOutput();
mutex_output_.unlock();
}
processInput()
函数先调用了StreamerBase
类的成员函数read()
来读取
buf_size_input_ = streamer_->read(buf_input_, max_buf_size_);
而read
函数呢,又是直接调用了直接调用了RTKLIB中的strread
函数来读取各种传输机制下的文件的
virtual int read(uint8_t *buf, int max_size) {
if (disable_) return 0;
return strread(&stream_, buf, max_size);
}
返回得到的就是读取到的数据的长度,针对不同的类型调用不同的readxxxx
函数
switch (stream->type) {
case STR_SERIAL : nr=readserial((serial_t *)stream->port,buff,n,msg); break;
case STR_FILE : nr=readfile ((file_t *)stream->port,buff,n,msg); break;
case STR_TCPSVR : nr=readtcpsvr((tcpsvr_t *)stream->port,buff,n,msg); break;
case STR_TCPCLI : nr=readtcpcli((tcpcli_t *)stream->port,buff,n,msg); break;
case STR_NTRIPSVR:
case STR_NTRIPCLI: nr=readntrip ((ntrip_t *)stream->port,buff,n,msg); break;
case STR_NTRIPCAS: nr=readntripc((ntripc_t *)stream->port,buff,n,msg); break;
case STR_UDPSVR : nr=readudpsvr((udp_t *)stream->port,buff,n,msg); break;
case STR_MEMBUF : nr=readmembuf((membuf_t *)stream->port,buff,n,msg); break;
case STR_FTP : nr=readftp ((ftp_t *)stream->port,buff,n,msg); break;
case STR_HTTP : nr=readftp ((ftp_t *)stream->port,buff,n,msg); break;
default:
strunlock(stream); // 解锁
return 0;
}
剩下的就是获取一些传输速率之类的信息
读取了数据之后,要对数据进行解码。就像作者开源的数据集里,数据都是以.bin结尾的,也就是二进制文件;而其他的传输机制也是编码后的格式,而不是直接就可以读取到。所以为了之后的处理,需要对文件数据进行解码操作。
对成员变量formators_
进行遍历,这个变量也是在Streaming
类的构造函数里面初始化的;然后定义对应索引的data_clusters_[i]
的引用dataset
,之后当前format
解码得到的数据就会存放在这里。
然后就调用decode
函数
int nobs = formator->decode(buf_input_, buf_size_input_, dataset);
这里的decode
函数是在FormatBase
基类中声明的虚函数
virtual int decode(const uint8_t *buf, int size, std::vector<std::shared_ptr<DataCluster>>& data) = 0;
针对不同的format
类型,有着不同的解码函数
这里涉及到的就是各种类型具体的解码了,(TODO)之后有时间的话可以看一下,解码之后的数据就会放在dataset
里,返回得到的nobs
应该就是观测值(数据)的个数
接下来的部分感觉是涉及到数据之间的联系(TODO),没完全看懂,看看后面结合到了具体的处理流程后能不能想明白一些
// Streaming.h
DataClusters data_clusters_; // store data from each formators
std::vector<DataCallback> data_callbacks_; // call external function to send data out
using PipelinesDirect = std::map<std::string, PipelineDirect>;
PipelinesDirect pipelines_direct_; // sending data directly to logging streams
// outer string: send from whom, inner string: who encode the data
using PipelinesConvert = std::map<std::string, std::map<std::string, PipelineConvert>>;
PipelinesConvert pipelines_convert_; // sending decoded data to logging streams
// Streaming.cpp
for (int iobs = 0; iobs < nobs; iobs++) {
// Call data callback
if (data_callbacks_.size() > 0)
for (auto it : data_callbacks_) {
auto& data_callback = it; // 用了std::function,相当于把tag和数据包装起来了
data_callback(formators_[i].tag, dataset[iobs]); // 这里是为了后边可以直接调用对应的数据
}
// Call logger pipeline
auto it_i = pipelines_convert_.find(formators_[i].tag);
if (it_i == pipelines_convert_.end()) continue;
if (it_i->second.size() == 0) continue;
auto& pipelines = it_i->second;
for (auto it_j : pipelines) {
auto& pipeline = it_j.second;
pipeline(formators_[i].tag, dataset[iobs]); // 数据的编码格式
}
/// ...循环外...
// Call direct pipeline
for (auto it : pipelines_direct_) { // 数据和logging直接联系起来
auto& pipeline = it.second;
pipeline(buf_input_, buf_size_input_);
}
processOutput()
输出的处理和输入的处理差不多,也是先调用了StreamerBase
类的成员函数write()
来写入
streamer_->write(buf_output_, buf_size_output_);
write
函数的内部也是调用RTKLIB中的strwrite
函数,针对不同的格式有不同的函数,这里就不再具体描述了。
processLogging()
这个函数和processOutput()
是完全一样的,只不过一个是用来logging
的一个是用来output
的
这里只是简单看了一下Streaming
线程run()
函数干了什么,但是对于数据在程序内部运行过程中的传输还没有细看。根据manual 2.3节来看,程序里应该是会涉及到数据的传输、转换等等操作,后续在具体的代码中关注一下。