Files
airborne_CO2/othersoft/co2correct/calibrator.cpp
2025-12-24 09:10:08 +08:00

479 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "calibrator.h"
#include <QDateTime>
#include <QRegularExpression>
#include <QDebug>
Calibrator::Calibrator(QObject *parent)
: QObject(parent)
, serialPort(new QSerialPort(this))
, calibrationTimer(new QTimer(this))
, receiveTimeoutTimer(new QTimer(this))
, consoleOutput(new QTextStream(stdout))
, currentState(Idle)
, retryCount(0)
, manualMode(false)
{
connect(serialPort, &QSerialPort::readyRead, this, &Calibrator::onSerialDataReceived);
connect(calibrationTimer, &QTimer::timeout, this, &Calibrator::onCalibrationStepTimeout);
connect(receiveTimeoutTimer, &QTimer::timeout, this, &Calibrator::onReceiveTimeout);
receiveTimeoutTimer->setSingleShot(true);
logInfo("CO2传感器校准工具已启动");
}
Calibrator::~Calibrator()
{
if (serialPort->isOpen()) {
serialPort->close();
}
}
bool Calibrator::connectSerialPort(const QString &portName, int baudRate)
{
serialPort->setPortName(portName);
serialPort->setBaudRate(baudRate);
serialPort->setDataBits(QSerialPort::Data8);
serialPort->setParity(QSerialPort::NoParity);
serialPort->setStopBits(QSerialPort::OneStop);
serialPort->setFlowControl(QSerialPort::NoFlowControl);
if (serialPort->open(QIODevice::ReadWrite)) {
logSuccess(QString("串口已连接: %1, 波特率: %2").arg(portName).arg(baudRate));
return true;
} else {
logError(QString("无法打开串口: %1, 错误: %2").arg(portName).arg(serialPort->errorString()));
return false;
}
}
void Calibrator::disconnectSerialPort()
{
if (serialPort->isOpen()) {
serialPort->close();
logInfo("串口已断开");
}
if (currentState != Idle) {
stopCalibration();
}
}
void Calibrator::startCalibration(double temperature)
{
if (!serialPort->isOpen()) {
logError("请先连接串口!");
return;
}
// 将温度转换为5位数字格式例如24.0 -> 00240
temperatureValue = QString("%1").arg(static_cast<int>(temperature * 10), 5, 10, QChar('0'));
logInfo(QString("开始校准流程,校准温度: %1℃ (编码: %2)").arg(temperature).arg(temperatureValue));
currentState = Step1_TC0;
retryCount = 0;
manualMode = false;
nextCalibrationStep();
}
void Calibrator::stopCalibration()
{
currentState = Idle;
retryCount = 0;
calibrationTimer->stop();
logInfo("校准已停止");
}
void Calibrator::sendManualCommand(const QString &command)
{
if (!serialPort->isOpen()) {
logError("请先连接串口!");
return;
}
manualMode = true;
sendCommand(command);
}
void Calibrator::onSerialDataReceived()
{
// 将接收到的数据追加到缓冲区
receiveBuffer.append(serialPort->readAll());
// 重置超时定时器(每次收到新数据都重置)
receiveTimeoutTimer->stop();
// 查找完整的响应(以换行符结尾)
while (true) {
int lineEnd = -1;
int lineLength = 0;
// 优先查找 \r\n
if (receiveBuffer.contains("\r\n")) {
lineEnd = receiveBuffer.indexOf("\r\n");
lineLength = 2;
} else if (receiveBuffer.contains('\n')) {
lineEnd = receiveBuffer.indexOf('\n');
lineLength = 1;
} else if (receiveBuffer.contains('\r')) {
lineEnd = receiveBuffer.indexOf('\r');
lineLength = 1;
}
if (lineEnd >= 0) {
// 提取一行数据(不包含换行符)
QByteArray line = receiveBuffer.left(lineEnd);
receiveBuffer.remove(0, lineEnd + lineLength);
QString response = QString::fromUtf8(line).trimmed();
if (!response.isEmpty()) {
logMessage(QString("接收: %1").arg(response));
if (manualMode) {
manualMode = false;
continue;
}
processResponse(response);
}
} else {
// 没有找到完整的行,启动超时定时器
// 如果200ms内没有新数据到达认为当前缓冲区中的数据是完整的响应
if (!receiveBuffer.isEmpty()) {
receiveTimeoutTimer->start(200); // 200ms超时
}
// 等待更多数据
break;
}
}
}
void Calibrator::onReceiveTimeout()
{
// 超时后,处理缓冲区中的数据
if (!receiveBuffer.isEmpty()) {
QString response = QString::fromUtf8(receiveBuffer).trimmed();
receiveBuffer.clear();
if (!response.isEmpty()) {
logMessage(QString("接收(超时): %1").arg(response));
if (manualMode) {
manualMode = false;
return;
}
processResponse(response);
}
}
}
void Calibrator::onCalibrationStepTimeout()
{
logError("等待响应超时,重试当前步骤...");
retryCount++;
if (retryCount > 3) {
logError("重试次数过多,停止校准");
stopCalibration();
emit calibrationFailed("等待响应超时");
return;
}
nextCalibrationStep();
}
void Calibrator::sendCommand(const QString &command)
{
if (!serialPort->isOpen()) {
return;
}
QString cmd = command;
if (!cmd.endsWith("\r\n") && !cmd.endsWith("\n")) {
cmd += "\r\n";
}
QByteArray data = cmd.toUtf8();
qint64 bytesWritten = serialPort->write(data);
if (bytesWritten == -1) {
logError(QString("发送失败: %1").arg(serialPort->errorString()));
} else {
logMessage(QString("发送: %1").arg(command));
serialPort->flush();
}
}
int timetry=0;
void Calibrator::processResponse(const QString &response)
{
// 如果正在进行校准流程,处理响应
if (currentState != Idle) {
// 检查响应是否匹配当前步骤的期望响应
bool stepComplete = false;
switch (currentState) {
case Step1_TC0:
if (response.contains("$WI,TC=0") || response.contains("TC=0")) {
stepComplete = true;
logSuccess("步骤1完成: TC0设置成功");
}
break;
case Step2_KY18_First:
if (response.contains("$WI,KY=18") || response.contains("KY=18")) {
stepComplete = true;
logSuccess("步骤2完成: KY18设置成功");
}
break;
case Step3_AC_First:
if (response.contains("$WI,AC=") || response.contains("AC=")) {
stepComplete = true;
QString acValues = extractACValues(response);
logSuccess(QString("步骤3完成: 第一次AC校准返回值: %1").arg(acValues));
}
break;
case Step4_KY18_Second:
if (response.contains("$WI,KY=18") || response.contains("KY=18")) {
stepComplete = true;
logSuccess("步骤4完成: 第二次KY18设置成功");
}
break;
case Step5_AC_Second:
if (response.contains("$WI,AC=") || response.contains("AC=")) {
QString acValues = extractACValues(response);
logInfo(QString("步骤5: 第二次AC校准返回值: %1").arg(acValues));
if (checkCalibrationComplete(response)) {
stepComplete = true;
logSuccess("校准完成所有值均小于10");
} else {
if (timetry>1) {
stepComplete = true;
logInfo("次数满了");
break;
}
timetry++;
logInfo("校准未完成需要重复步骤2-3等待5秒后重试...");
// 重新开始步骤2
currentState = Step2_KY18_First;
retryCount = 0;
calibrationTimer->stop();
QTimer::singleShot(5000, this, &Calibrator::nextCalibrationStep);
return;
}
}
break;
case Step6_Restore_KY18:
if (response.contains("$WI,KY=18") || response.contains("KY=18")) {
stepComplete = true;
logSuccess("步骤6完成: 恢复模式KY18设置成功");
}
break;
case Step7_Restore_TC:
if (response.contains("$WI,TC=00001") || response.contains("TC=00001")) {
stepComplete = true;
logSuccess("步骤7完成: 恢复模式TC设置成功校准流程全部完成");
stopCalibration();
emit calibrationCompleted();
return;
}
break;
default:
break;
}
if (stepComplete) {
calibrationTimer->stop();
retryCount = 0;
// 更新到下一步状态
switch (currentState) {
case Step1_TC0:
currentState = Step2_KY18_First;
break;
case Step2_KY18_First:
currentState = Step3_AC_First;
break;
case Step3_AC_First:
currentState = Step4_KY18_Second;
break;
case Step4_KY18_Second:
currentState = Step5_AC_Second;
break;
case Step5_AC_Second:
currentState = Step6_Restore_KY18;
break;
case Step6_Restore_KY18:
currentState = Step7_Restore_TC;
break;
case Step7_Restore_TC:
// 已完成,不需要更新状态
break;
default:
break;
}
QTimer::singleShot(500, this, &Calibrator::nextCalibrationStep);
}
}
}
void Calibrator::nextCalibrationStep()
{
if (currentState == Idle) {
return;
}
// 设置超时定时器
calibrationTimer->stop();
calibrationTimer->setSingleShot(true);
calibrationTimer->start(5000); // 5秒超时
switch (currentState) {
case Step1_TC0:
sendCommand("$01,TC0");
break;
case Step2_KY18_First:
sendCommand("$01,KY18");
break;
case Step3_AC_First:
{
QString acCommand = QString("$01,AC%1^**").arg(temperatureValue);
sendCommand(acCommand);
}
break;
case Step4_KY18_Second:
sendCommand("$01,KY18");
break;
case Step5_AC_Second:
{
QString acCommand = QString("$01,AC%1^**").arg(temperatureValue);
sendCommand(acCommand);
}
break;
case Step6_Restore_KY18:
sendCommand("$01,KY18");
break;
case Step7_Restore_TC:
sendCommand("$01,TC00001^**");
break;
default:
break;
}
}
bool Calibrator::checkCalibrationComplete(const QString &response)
{
QString acValues = extractACValues(response);
return allValuesLessThan10(acValues);
}
QString Calibrator::extractACValues(const QString &response)
{
// 提取AC=后面的所有值
// 格式: $WI,AC=01854,01853,01853,00927,00010,00010,00025,00025,00014,00013
QRegularExpression re(R"(\$WI,AC=(.+))");
QRegularExpressionMatch match = re.match(response);
if (!match.hasMatch()) {
// 尝试另一种格式
re.setPattern(R"(AC=(.+))");
match = re.match(response);
}
if (match.hasMatch()) {
return match.captured(1);
}
return "";
}
bool Calibrator::allValuesLessThan10(const QString &acValues)
{
if (acValues.isEmpty()) {
return false;
}
// 分割所有值
QStringList values = acValues.split(',');
if (values.isEmpty()) {
return false;
}
// 检查从第5个值包含到最后一个值
// 示例: 01094,01094,01091,01094,000474 -> 检查第5个值000474
// 如果有更多值检查从第5个到最后一个
int startIndex = 4; // 第5个值索引从0开始所以是4
if (startIndex >= values.size()) {
logError(QString("值数量不足5个无法检查"));
return false;
}
QStringList checkedValues;
for (int i = startIndex; i < values.size(); i++) {
QString valueStr = values[i].trimmed();
bool ok;
int value = valueStr.toInt(&ok);
if (!ok) {
logError(QString("无法解析值: %1").arg(valueStr));
return false;
}
checkedValues << valueStr;
if (value >= 10) {
logInfo(QString("检查值: %1 (第%2到最后一个值: %3),值 %4 >= 10校准未完成")
.arg(valueStr)
.arg(startIndex + 1)
.arg(checkedValues.join(","))
.arg(value));
return false;
}
}
logSuccess(QString("第%1到最后一个值均小于10 (值: %2),校准完成")
.arg(startIndex + 1)
.arg(checkedValues.join(",")));
return true;
}
void Calibrator::logMessage(const QString &message)
{
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
*consoleOutput << QString("[%1] %2\n").arg(timestamp).arg(message);
consoleOutput->flush();
}
void Calibrator::logError(const QString &message)
{
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
*consoleOutput << QString("[%1] [错误] %2\n").arg(timestamp).arg(message);
consoleOutput->flush();
}
void Calibrator::logSuccess(const QString &message)
{
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
*consoleOutput << QString("[%1] [成功] %2\n").arg(timestamp).arg(message);
consoleOutput->flush();
}
void Calibrator::logInfo(const QString &message)
{
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
*consoleOutput << ("[%1] [信息] %2\n").arg(timestamp).arg(message);
consoleOutput->flush();
}