diff --git a/src/GPSManager.cpp b/src/GPSManager.cpp index 4c90fea..9faacb9 100644 --- a/src/GPSManager.cpp +++ b/src/GPSManager.cpp @@ -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(); qRegisterMetaType(); } +// 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); // 发送卫星更新信号 } + diff --git a/src/gimbal_server.cpp b/src/gimbal_server.cpp index 39c1fde..c8d9f4a 100644 --- a/src/gimbal_server.cpp +++ b/src/gimbal_server.cpp @@ -16,30 +16,40 @@ #include // 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 ¢) { + // 如果已经设置了停止追踪的标志,则直接返回,不执行追踪。 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(g_gimbal)->isGetRealVelocity = req.data; resp.success = true; + // 设置响应消息,表明是返回真实角速度还是计算角速度。 resp.message = static_cast(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; } diff --git a/src/serial.cpp b/src/serial.cpp index a1dae49..57f17ce 100644 --- a/src/serial.cpp +++ b/src/serial.cpp @@ -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; + + // 指向实现类的指针,使用了Pimpl(Pointer 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: + // 指向实现类的指针,使用了Pimpl(Pointer to Implementation)技术 + SerialImpl *pimpl_; +}; size_t Serial::read(std::vector &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(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或std::array来替代 uint8_t *buffer_ = static_cast(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(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(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 Serial::readlines(size_t size, string eol) { - ScopedReadLock lock(this->pimpl_); - std::vector lines; - size_t eol_len = eol.length(); + ScopedReadLock lock(this->pimpl_); // 使用ScopedReadLock对读取操作进行加锁 + std::vector lines; // 创建一个字符串向量来存储读取到的多行数据 + size_t eol_len = eol.length(); // 获取行结束符的长度 + + // 注意:虽然这里使用了alloca来分配内存,但在C++中更推荐使用std::vector或std::array + // 这里为了保持与原始代码的一致性,仍然使用alloca uint8_t *buffer_ = static_cast(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(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(buffer_ + read_so_far - eol_len), eol_len) == eol) { - // EOL found + // 发现行结束符,将当前行添加到lines中 lines.push_back( string(reinterpret_cast(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(buffer_ + start_of_line), read_so_far - start_of_line)); } - break; // Reached the maximum read length + break; // 达到最大读取长度,退出循环 } } + + // 返回读取到的多行数据 return lines; }