479 lines
14 KiB
C++
479 lines
14 KiB
C++
#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();
|
||
}
|
||
|