添加注释

master
laptoy 6 months ago
parent 0459704fde
commit 16db2ead30

@ -7,36 +7,45 @@
#include "SettingsManager.h"
#include "RTKSettings.h"
// GPSManager类的构造函数它是一个工具类用于管理GPS连接和数据处理。
GPSManager::GPSManager(QGCApplication* app, QGCToolbox* toolbox)
: QGCTool(app, toolbox)
: QGCTool(app, toolbox) // 调用基类QGCTool的构造函数
{
// 注册元类型以便在信号和槽之间传递GPSPositionMessage和GPSSatelliteMessage类型的对象
qRegisterMetaType<GPSPositionMessage>();
qRegisterMetaType<GPSSatelliteMessage>();
}
// GPSManager类的析构函数
GPSManager::~GPSManager()
{
// 断开与GPS的连接
disconnectGPS();
}
// 连接到指定的GPS设备
void GPSManager::connectGPS(const QString& device, const QString& gps_type)
{
// 获取RTK设置实例用于访问RTK相关的配置参数
RTKSettings* rtkSettings = qgcApp()->toolbox()->settingsManager()->rtkSettings();
// 根据传入的gps_type确定GPS设备的类型
GPSProvider::GPSType type;
if (gps_type.contains("trimble", Qt::CaseInsensitive)) {
type = GPSProvider::GPSType::trimble;
qCDebug(RTKGPSLog) << "Connecting Trimble device";
qCDebug(RTKGPSLog) << "Connecting Trimble device"; // 打印连接Trimble设备的调试信息
} else if (gps_type.contains("septentrio", Qt::CaseInsensitive)) {
type = GPSProvider::GPSType::septentrio;
qCDebug(RTKGPSLog) << "Connecting Septentrio device";
qCDebug(RTKGPSLog) << "Connecting Septentrio device"; // 打印连接Septentrio设备的调试信息
} else {
type = GPSProvider::GPSType::u_blox;
qCDebug(RTKGPSLog) << "Connecting U-blox device";
qCDebug(RTKGPSLog) << "Connecting U-blox device"; // 打印连接U-blox设备的调试信息
}
// 断开现有的GPS连接
disconnectGPS();
_requestGpsStop = false;
_requestGpsStop = false; // 清除停止GPS请求的标志
// 创建一个新的GPSProvider实例并传入设备名称和类型以及相关的RTK设置
_gpsProvider = new GPSProvider(device,
type,
true, /* enableSatInfo */
@ -48,46 +57,58 @@ void GPSManager::connectGPS(const QString& device, const QString& gps_type)
rtkSettings->fixedBasePositionAltitude()->rawValue().toFloat(),
rtkSettings->fixedBasePositionAccuracy()->rawValue().toFloat(),
_requestGpsStop);
_gpsProvider->start();
_gpsProvider->start(); // 启动GPSProvider
//create RTCM device
// 创建RTCM MAVLink设备
_rtcmMavlink = new RTCMMavlink(*_toolbox);
// 连接GPSProvider的RTCM数据更新信号到RTCMMavlink的槽
connect(_gpsProvider, &GPSProvider::RTCMDataUpdate, _rtcmMavlink, &RTCMMavlink::RTCMDataUpdate);
//test: connect to position update
// 连接GPSProvider的位置更新信号到GPSManager的位置更新槽
connect(_gpsProvider, &GPSProvider::positionUpdate, this, &GPSManager::GPSPositionUpdate);
// 连接GPSProvider的卫星信息更新信号到GPSManager的卫星信息更新槽
connect(_gpsProvider, &GPSProvider::satelliteInfoUpdate, this, &GPSManager::GPSSatelliteUpdate);
// 连接GPSProvider的完成信号到GPSManager的断开连接槽
connect(_gpsProvider, &GPSProvider::finished, this, &GPSManager::onDisconnect);
// 连接GPSProvider的测量状态信号到GPSManager的测量状态槽
connect(_gpsProvider, &GPSProvider::surveyInStatus, this, &GPSManager::surveyInStatus);
// 发送连接事件
emit onConnect();
}
// 断开与GPS的连接
void GPSManager::disconnectGPS(void)
{
if (_gpsProvider) {
_requestGpsStop = true;
//Note that we need a relatively high timeout to be sure the GPS thread finished.
_requestGpsStop = true; // 设置请求停止GPS的标志
// 注意我们需要一个相对较高的超时时间以确保GPS线程完成。
if (!_gpsProvider->wait(2000)) {
qWarning() << "Failed to wait for GPS thread exit. Consider increasing the timeout";
qWarning() << "Failed to wait for GPS thread exit. Consider increasing the timeout"; // 如果等待失败,打印警告信息
}
delete(_gpsProvider);
delete(_gpsProvider); // 删除GPSProvider对象
}
if (_rtcmMavlink) {
delete(_rtcmMavlink);
delete(_rtcmMavlink); // 删除RTCMMavlink对象
}
_gpsProvider = nullptr;
_rtcmMavlink = nullptr;
_gpsProvider = nullptr; // 将GPSProvider指针设置为nullptr
_rtcmMavlink = nullptr; // 将RTCMMavlink指针设置为nullptr
}
// 当接收到GPS位置更新时调用此函数
void GPSManager::GPSPositionUpdate(GPSPositionMessage msg)
{
qCDebug(RTKGPSLog) << QString("GPS: got position update: alt=%1, long=%2, lat=%3").arg(msg.position_data.alt).arg(msg.position_data.lon).arg(msg.position_data.lat);
// 打印接收到的GPS位置信息
}
// 当接收到GPS卫星信息更新时调用此函数
void GPSManager::GPSSatelliteUpdate(GPSSatelliteMessage msg)
{
qCDebug(RTKGPSLog) << QString("GPS: got satellite info update, %1 satellites").arg((int)msg.satellite_data.count);
emit satelliteUpdate(msg.satellite_data.count);
// 打印接收到的GPS卫星数量信息
emit satelliteUpdate(msg.satellite_data.count); // 发送卫星更新信号
}

@ -16,30 +16,40 @@
#include <opencv2/highgui/highgui.hpp>
// amovGimbal::gimbal *gimbal = nullptr;
// 定义一个全局的GimbalBase指针用于后续对云台的控制
GimbalBase *g_gimbal = nullptr;
// 定义一个全局的GimbalState对象可能用于发布云台的状态信息但在这段代码中并未使用
prometheus_gimbal_control::GimbalState g_pub_info;
// 定义一个全局布尔变量,用于控制是否停止追踪(可能是某种目标追踪功能)
bool g_stop_track = false;
// 回调函数当接收到GimbalControl消息时触发
void ctlCallback(const prometheus_gimbal_control::GimbalControl::ConstPtr &ctl)
{
// 根据GimbalControl消息中的rpyMode字段判断并执行相应的云台控制操作
switch (ctl->rpyMode)
{
case prometheus_gimbal_control::GimbalControl::manual:
// 定义两个数组一个用于存储角度控制roll, yaw, pitch一个用于存储速度值
uint8_t rpy_ctl[3];
double rpy_vel[3];
// 从GimbalControl消息中提取roll, yaw, pitch的角度值
rpy_ctl[0] = ctl->roll;
rpy_ctl[1] = ctl->yaw;
rpy_ctl[2] = ctl->pitch;
// 从GimbalControl消息中提取rValue, pValue, yValue的速度值
rpy_vel[0] = ctl->rValue;
rpy_vel[1] = ctl->pValue;
rpy_vel[2] = ctl->yValue;
// 调用g_gimbal对象的ctl_gimbal方法传入角度和速度值来控制云台
g_gimbal->ctl_gimbal(rpy_ctl, rpy_vel);
// 打印日志,显示当前处于手动模式,并显示角度和速度值
ROS_INFO_STREAM("Manual "
<< "rpy control mode: " << (int)rpy_ctl[0] << " " << (int)rpy_ctl[1] << " " << (int)rpy_ctl[2]
<< " value: " << rpy_vel[0] << " " << rpy_vel[1] << " " << rpy_vel[2]);
@ -91,49 +101,67 @@ void ctlCallback(const prometheus_gimbal_control::GimbalControl::ConstPtr &ctl)
}
};
// 这个回调函数用于处理视觉差分信息,并根据这些信息控制云台追踪目标。
void trackCallback(const prometheus_gimbal_control::VisionDiff::ConstPtr &cent)
{
// 如果已经设置了停止追踪的标志,则直接返回,不执行追踪。
if (g_stop_track)
return;
// 定义一个临时数组,用于存储目标中心点的坐标偏移量。
float tmp_cent[2]{0, 0};
// 计算目标中心点在图像中的x坐标偏移量。
tmp_cent[0] = cent->objectX + cent->objectWidth / 2 - cent->frameWidth / 2;
// 计算目标中心点在图像中的y坐标偏移量。
tmp_cent[1] = cent->objectY + cent->objectHeight / 2 - cent->frameHeight / 2;
// 获取云台的滚动模式。
int roll_mode = cent->ctlMode;
// 获取PID控制参数比例系数kp。
float kp = cent->kp;
// 获取PID控制参数积分系数ki。
float ki = cent->ki;
// 获取PID控制参数微分系数kd。
float kd = cent->kd;
// 定义静态变量,用于记录是否进行了变焦操作。
static bool flag_zoom = false;
// 定义静态变量,用于记录是否进行了保持操作。
static bool flag_hold = false;
// 如果检测到目标。
if (cent->detect != 0)
{
// 根据当前大小和最大变焦倍数计算变焦值。
int zoom_val = cent->currSize ? std::fmax(g_gimbal->zoom_val, 1) : 1;
// 根据变焦值计算缩放比例。
float scale = std::fmax(g_gimbal->max_zoom - zoom_val, 1.) / g_gimbal->max_zoom;
// 使用计算出的参数调用云台追踪函数。
g_gimbal->tracking(tmp_cent, roll_mode, kp * scale, ki, kd, cent->trackIgnoreError * zoom_val / g_gimbal->max_zoom);
// std::cout << "step 3 : " << std::endl;
// 如果设置了自动变焦,并且目标在可忽略误差范围内,则进行自动变焦。
if (cent->autoZoom && cent->currSize && tmp_cent[0] < cent->trackIgnoreError && tmp_cent[1] < cent->trackIgnoreError)
{
g_gimbal->auto_zoom(cent->currSize, zoom_val, cent->maxSize, cent->minSize);
flag_zoom = true;
}
// std::cout << "step 4 : " << std::endl;
// 设置保持标志为真。
flag_hold = true;
}
else
{
// 如果检测不到目标,并且之前进行了变焦操作,则停止变焦。
if (cent->currSize && flag_zoom)
{
g_gimbal->zoom_stop();
flag_zoom = false;
}
// 如果之前进行了保持操作,则调用云台保持函数。
if (flag_hold)
{
g_gimbal->hold();
flag_hold = false;
}
}
};
}
void currSizeCallback(const prometheus_gimbal_control::VisionDiff::ConstPtr &info)
{
@ -155,23 +183,32 @@ void currSizeCallback(const prometheus_gimbal_control::VisionDiff::ConstPtr &inf
}
}
// 这个函数是一个服务回调函数,用于处理视频录制的开始和停止请求。
bool videoRecord(std_srvs::SetBool::Request &ref, std_srvs::SetBool::Response &req)
{
// 如果请求的内容是开始录制视频。
if (ref.data)
{
// 调用云台对象的开始录制视频函数。
g_gimbal->start_record();
// 设置响应的成功标志为视频流是否已成功打开。
req.success = g_gimbal->outvideo.isOpened();
// 打印信息到ROS日志。
ROS_INFO_STREAM("Start Video Record ! ");
}
else
{
// 如果请求的内容是停止录制视频。
// 如果视频流没有打开,则直接返回失败响应。
if (!g_gimbal->outvideo.isOpened())
{
req.success = false;
req.message = "video record not is started ! ";
ROS_WARN_STREAM("video record not is started ! ");
}
// 调用云台对象的完成录制视频函数。
g_gimbal->finish_record();
// 如果视频流仍然打开,说明停止录制失败。
if (g_gimbal->outvideo.isOpened())
{
req.success = false;
@ -180,83 +217,97 @@ bool videoRecord(std_srvs::SetBool::Request &ref, std_srvs::SetBool::Response &r
}
else
{
// 如果视频流已关闭,说明停止录制成功。
req.success = true;
ROS_INFO_STREAM("Finish Video Record ! ");
}
}
// 返回true表示处理成功。
return true;
}
void picCallback(const std_msgs::String::ConstPtr &info)
{
ROS_INFO("Get Picture ");
g_gimbal->get_picture();
}
// 这个函数用于连续从云台获取图像帧,并通过图像发布器发布。
void callAll(image_transport::Publisher *image_pub)
{
cv::Mat frame;
while (true)
{
// 从云台获取一帧图像。
(*g_gimbal) >> frame;
// 如果帧不为空,则将其发布。
if (!frame.empty())
{
image_pub->publish(cv_bridge::CvImage(std_msgs::Header(), "bgr8", frame).toImageMsg());
}
// 等待10毫秒。
std::this_thread::sleep_for(10ms);
}
}
// 这个服务回调函数用于启动或停止搜索模式。
bool search(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &resp)
{
// 设置响应的成功标志为云台搜索操作的结果。
resp.success = g_gimbal->search(req.data);
return true;
}
// 切换反馈模式,自动?交换
// 这个服务回调函数用于切换跟踪模式,以获取真实的角速度或计算出的角速度。
bool switchTrack(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &resp)
{
// 将云台的获取真实角速度标志设置为请求中的数据。
static_cast<GimbalQ10f *>(g_gimbal)->isGetRealVelocity = req.data;
resp.success = true;
// 设置响应消息,表明是返回真实角速度还是计算角速度。
resp.message = static_cast<GimbalQ10f *>(g_gimbal)->isGetRealVelocity ? "return real angle speed" : "return compute angle speed";
return true;
}
// 屏蔽tracking控制
// 这个服务回调函数用于停止跟踪控制。
bool stopTrack(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &resp)
{
// 设置全局停止跟踪标志为请求中的数据。
g_stop_track = req.data;
resp.success = true;
return true;
}
// 这个函数是设备状态回调函数,用于处理设备的不同状态信息。
int VLK_DevStatusCallback(int iType, const char* szBuffer, int iBufLen, void* pUserParam)
{
// 根据状态类型处理不同的信息。
if (VLK_DEV_STATUS_TYPE_MODEL == iType)
{
// 如果是设备模型信息,则解析并打印模型代码和名称。
VLK_DEV_MODEL* pModel = (VLK_DEV_MODEL*)szBuffer;
std::cout << "model code: " << pModel->cModelCode << ", model name: " << pModel->szModelName << std::endl;
}
else if (VLK_DEV_STATUS_TYPE_CONFIG == iType)
{
// 如果是设备配置信息则解析并打印版本号、设备ID和序列号。
VLK_DEV_CONFIG* pDevConfig = (VLK_DEV_CONFIG*)szBuffer;
std::cout << "VersionNO: " << pDevConfig->cVersionNO << ", DeviceID: " << pDevConfig->cDeviceID << ", SerialNO: " << pDevConfig->cSerialNO << std::endl;
}
else if (VLK_DEV_STATUS_TYPE_TELEMETRY == iType)
{
/*
* once device is connected, telemetry information will keep updating,
* in order to avoid disturbing user input, comment out printing telemetry information
*/
// 如果是设备遥测信息,则解析并打印偏航角、俯仰角、传感器类型和变焦倍数。
// 为了避免干扰用户输入,注释掉了打印遥测信息的代码。
// VLK_DEV_TELEMETRY* pTelemetry = (VLK_DEV_TELEMETRY*)szBuffer;
// cout << "Yaw: " << pTelemetry->dYaw << ", Pitch: " << pTelemetry->dPitch << ", sensor type: " << pTelemetry->emSensorType << ", Zoom mag times: " << pTelemetry->sZoomMagTimes << endl;
}
else
{
// 如果是未知的状态类型,则打印错误信息。
std::cout << "error: unknown status type: " << iType << std::endl;
}
return 0;
}

@ -25,173 +25,258 @@ using serial::stopbits_t;
class Serial::ScopedReadLock {
public:
// 构造函数,传入串行实现类指针,并获取读锁
ScopedReadLock(SerialImpl *pimpl) : pimpl_(pimpl) {
this->pimpl_->readLock();
}
// 析构函数,释放读锁
~ScopedReadLock() { this->pimpl_->readUnlock(); }
private:
// Disable copy constructors
// 禁用复制构造函数
ScopedReadLock(const ScopedReadLock &);
// 禁用赋值操作符
const ScopedReadLock &operator=(ScopedReadLock);
SerialImpl *pimpl_;
};
class Serial::ScopedWriteLock {
public:
ScopedWriteLock(SerialImpl *pimpl) : pimpl_(pimpl) {
this->pimpl_->writeLock();
}
~ScopedWriteLock() { this->pimpl_->writeUnlock(); }
// 这是一个私有的类定义部分,可能是一个互斥锁封装类
class ScopedWriteLock {
private:
// Disable copy constructors there
ScopedWriteLock(const ScopedWriteLock &);
const ScopedWriteLock &operator=(ScopedWriteLock);
// 禁用拷贝构造函数,避免对象被误拷贝
// Disable copy constructors there
ScopedWriteLock(const ScopedWriteLock &); // 删除此函数或声明为=delete
// 禁用赋值运算符,避免对象被误赋值
const ScopedWriteLock &operator=(ScopedWriteLock); // 缺少引用符号应改为const ScopedWriteLock& operator=(const ScopedWriteLock&) = delete;
// 指向实现类的指针使用了PimplPointer to Implementation技术
SerialImpl *pimpl_;
};
Serial::Serial(const string &port, uint32_t baudrate, serial::Timeout timeout,
bytesize_t bytesize, parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: pimpl_(new SerialImpl(port, baudrate, bytesize, parity, stopbits,
flowcontrol)) {
pimpl_->setTimeout(timeout);
}
// Serial类定义可能是一个串口通信类
class Serial {
public:
// 构造函数,初始化串口设置
Serial(const string &port, uint32_t baudrate, serial::Timeout timeout,
bytesize_t bytesize, parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: pimpl_(new SerialImpl(port, baudrate, bytesize, parity, stopbits,
flowcontrol)) {
pimpl_->setTimeout(timeout); // 设置超时时间
}
Serial::~Serial() { delete pimpl_; }
// 析构函数,释放资源
~Serial() { delete pimpl_; }
void Serial::open() { pimpl_->open(); }
// 打开串口
void open() { pimpl_->open(); }
void Serial::close() { pimpl_->close(); }
// 关闭串口
void close() { pimpl_->close(); }
bool Serial::isOpen() const { return pimpl_->isOpen(); }
// 检查串口是否打开
bool isOpen() const { return pimpl_->isOpen(); }
size_t Serial::available() { return pimpl_->available(); }
// 获取串口上可用的字节数
size_t available() { return pimpl_->available(); }
bool Serial::waitReadable() {
serial::Timeout timeout(pimpl_->getTimeout());
return pimpl_->waitReadable(timeout.read_timeout_constant);
}
// 等待串口可读
bool waitReadable() {
serial::Timeout timeout(pimpl_->getTimeout());
return pimpl_->waitReadable(timeout.read_timeout_constant);
}
void Serial::waitByteTimes(size_t count) { pimpl_->waitByteTimes(count); }
// 等待特定数量的字节时间(可能是为了同步)
void waitByteTimes(size_t count) { pimpl_->waitByteTimes(count); }
size_t Serial::read_(uint8_t *buffer, size_t size) {
return this->pimpl_->read(buffer, size);
}
// 从串口读取数据(无锁)
size_t read_(uint8_t *buffer, size_t size) {
return this->pimpl_->read(buffer, size);
}
size_t Serial::read(uint8_t *buffer, size_t size) {
ScopedReadLock lock(this->pimpl_);
return this->pimpl_->read(buffer, size);
}
// 从串口读取数据使用ScopedReadLock加锁
size_t read(uint8_t *buffer, size_t size) {
ScopedReadLock lock(this->pimpl_); // 假设ScopedReadLock是一个读取锁类
return this->pimpl_->read(buffer, size);
}
private:
// 指向实现类的指针使用了PimplPointer to Implementation技术
SerialImpl *pimpl_;
};
size_t Serial::read(std::vector<uint8_t> &buffer, size_t size) {
// 创建一个读锁,保证读操作的安全性
ScopedReadLock lock(this->pimpl_);
// 分配一个新的缓冲区来存储读取的数据
uint8_t *buffer_ = new uint8_t[size];
// 初始化已读取的字节数为0
size_t bytes_read = 0;
try {
// 尝试从串行端口读取数据到缓冲区
bytes_read = this->pimpl_->read(buffer_, size);
} catch (const std::exception &e) {
// 如果读取过程中发生异常,删除缓冲区并重新抛出异常
delete[] buffer_;
throw;
}
// 将读取的数据插入到传入的vector中
buffer.insert(buffer.end(), buffer_, buffer_ + bytes_read);
// 删除临时缓冲区
delete[] buffer_;
// 返回实际读取的字节数
return bytes_read;
}
size_t Serial::read(std::string &buffer, size_t size) {
// 创建一个读锁,保证读操作的安全性
ScopedReadLock lock(this->pimpl_);
// 分配一个新的缓冲区来存储读取的数据
uint8_t *buffer_ = new uint8_t[size];
// 初始化已读取的字节数为0
size_t bytes_read = 0;
try {
// 尝试从串行端口读取数据到缓冲区
bytes_read = this->pimpl_->read(buffer_, size);
} catch (const std::exception &e) {
// 如果读取过程中发生异常,删除缓冲区并重新抛出异常
delete[] buffer_;
throw;
}
// 将读取的数据追加到传入的string中
buffer.append(reinterpret_cast<const char *>(buffer_), bytes_read);
// 删除临时缓冲区
delete[] buffer_;
// 返回实际读取的字节数
return bytes_read;
}
// Serial类中的成员函数用于读取指定字节数并返回字符串
string Serial::read(size_t size) {
std::string buffer;
this->read(buffer, size);
return buffer;
std::string buffer; // 创建一个字符串用于存储读取的数据
buffer.resize(size); // 预分配足够的存储空间
this->read(&buffer[0], size); // 调用重载的read函数将读取的数据填充到buffer中
return buffer; // 返回读取到的字符串
}
// Serial类中的成员函数用于读取一行数据并返回字符串
size_t Serial::readline(string &buffer, size_t size, string eol) {
ScopedReadLock lock(this->pimpl_);
size_t eol_len = eol.length();
ScopedReadLock lock(this->pimpl_); // 使用ScopedReadLock对读取操作进行加锁
size_t eol_len = eol.length(); // 获取行结束符的长度
// 使用alloca函数在栈上动态分配足够大小的字节数组来存储读取的数据
// 注意alloca函数不是C++标准库的一部分,并且在某些平台上可能不被支持或存在安全问题
// 在实践中更推荐使用std::vector<uint8_t>或std::array<uint8_t, N>来替代
uint8_t *buffer_ = static_cast<uint8_t *>(alloca(size * sizeof(uint8_t)));
size_t read_so_far = 0;
size_t read_so_far = 0; // 记录已经读取的字节数
while (true) {
// 尝试读取一个字节
size_t bytes_read = this->read_(buffer_ + read_so_far, 1);
read_so_far += bytes_read;
// 如果没有读取到任何数据(可能是超时),则退出循环
if (bytes_read == 0) {
break; // Timeout occured on reading 1 byte
break; // 读取1字节时发生超时
}
// 如果已经读取的字节数还不足以构成一个完整的行结束符,则继续读取
if (read_so_far < eol_len)
continue;
// 检查当前读取到的数据是否包含行结束符
if (string(reinterpret_cast<const char *>(buffer_ + read_so_far - eol_len),
eol_len) == eol) {
break; // EOL found
break; // 找到行结束符
}
// 如果已经读取了最大字节数,则退出循环
if (read_so_far == size) {
break; // Reached the maximum read length
break; // 达到最大读取长度
}
}
// 将读取到的数据转换为字符串并追加到传入的buffer中
buffer.append(reinterpret_cast<const char *>(buffer_), read_so_far);
// 返回实际读取的字节数
return read_so_far;
}
// Serial类中的成员函数读取一行数据并返回字符串使用临时字符串对象
string Serial::readline(size_t size, string eol) {
std::string buffer;
this->readline(buffer, size, eol);
return buffer;
std::string buffer; // 创建一个临时字符串对象
this->readline(buffer, size, eol); // 调用重载的readline函数
return buffer; // 返回读取到的字符串
}
// 中文注释:
// Serial::read函数
// 读取指定字节数的数据,并将数据转换为字符串返回。
//
// Serial::readline函数带引用参数
// 读取一行数据,直到遇到指定的行结束符或者读取了指定数量的字节或者发生超时。
// 读取到的数据会被追加到传入的字符串buffer中并返回实际读取的字节数。
// 该函数使用ScopedReadLock进行加锁操作以确保在多线程环境中线程安全。
//
// Serial::readline函数返回字符串
// 与带引用参数的readline函数功能相同但返回的是一个新创建的字符串对象。
// 内部使用了一个临时字符串对象来存储读取到的数据。
// Serial类中的成员函数用于从串口读取多行数据并返回一个string向量
vector<string> Serial::readlines(size_t size, string eol) {
ScopedReadLock lock(this->pimpl_);
std::vector<std::string> lines;
size_t eol_len = eol.length();
ScopedReadLock lock(this->pimpl_); // 使用ScopedReadLock对读取操作进行加锁
std::vector<std::string> lines; // 创建一个字符串向量来存储读取到的多行数据
size_t eol_len = eol.length(); // 获取行结束符的长度
// 注意虽然这里使用了alloca来分配内存但在C++中更推荐使用std::vector或std::array
// 这里为了保持与原始代码的一致性仍然使用alloca
uint8_t *buffer_ = static_cast<uint8_t *>(alloca(size * sizeof(uint8_t)));
size_t read_so_far = 0;
size_t start_of_line = 0;
size_t read_so_far = 0; // 记录已经读取的字节数
size_t start_of_line = 0; // 记录当前行的起始位置
// 循环读取数据,直到达到最大读取长度或发生超时
while (read_so_far < size) {
// 尝试读取一个字节
size_t bytes_read = this->read_(buffer_ + read_so_far, 1);
read_so_far += bytes_read;
// 如果读取失败(可能是超时)
if (bytes_read == 0) {
// 如果当前行有数据即start_of_line不等于当前位置则添加到lines中
if (start_of_line != read_so_far) {
lines.push_back(
string(reinterpret_cast<const char *>(buffer_ + start_of_line),
read_so_far - start_of_line));
}
break; // Timeout occured on reading 1 byte
break; // 读取1字节时发生超时退出循环
}
// 如果当前读取的字节数还不足以构成一个完整的行结束符,则继续读取
if (read_so_far < eol_len)
continue;
// 检查当前读取到的数据是否包含行结束符
if (string(reinterpret_cast<const char *>(buffer_ + read_so_far - eol_len),
eol_len) == eol) {
// EOL found
// 发现行结束符将当前行添加到lines中
lines.push_back(
string(reinterpret_cast<const char *>(buffer_ + start_of_line),
read_so_far - start_of_line));
read_so_far - eol_len));
// 更新当前行的起始位置为行结束符之后
start_of_line = read_so_far;
}
// 如果已经读取了最大字节数,则退出循环
if (read_so_far == size) {
// 如果当前行有数据即start_of_line不等于当前位置则添加到lines中
if (start_of_line != read_so_far) {
lines.push_back(
string(reinterpret_cast<const char *>(buffer_ + start_of_line),
read_so_far - start_of_line));
}
break; // Reached the maximum read length
break; // 达到最大读取长度,退出循环
}
}
// 返回读取到的多行数据
return lines;
}

Loading…
Cancel
Save