370 lines
13 KiB
C++
370 lines
13 KiB
C++
#include "serialportmanager.h"
|
||
#include <QSerialPortInfo>
|
||
#include <QThread>
|
||
#include <QDebug>
|
||
#include <QModbusRtuSerialMaster>
|
||
#include <QModbusDataUnit>
|
||
#include <QtEndian>
|
||
#include <cmath>
|
||
|
||
SerialPortManager::SerialPortManager(QObject *parent)
|
||
: QObject(parent),
|
||
serialPort(nullptr) {
|
||
|
||
}
|
||
|
||
SerialPortManager::~SerialPortManager() {
|
||
if (serialPort) {
|
||
delete serialPort;
|
||
}
|
||
}
|
||
|
||
void SerialPortManager::setConnectionMode(ConnectionMode mode) {
|
||
connectionMode = mode;
|
||
}
|
||
|
||
void SerialPortManager::configureSerialPort(const QString &portName, int baudRate, int dataBits, int stopBits, int parity) {
|
||
this->portName = portName;
|
||
this->baudRate = baudRate;
|
||
this->dataBits = dataBits;
|
||
this->stopBits = stopBits;
|
||
this->parity = parity;
|
||
}
|
||
|
||
bool SerialPortManager::connectSerialPort() {
|
||
if (serialPort) {
|
||
disconnectSerialPort();
|
||
}
|
||
|
||
if (connectionMode == Manual) {
|
||
serialPort = new QSerialPort(this);
|
||
serialPort->setPortName(portName);
|
||
serialPort->setBaudRate(baudRate);
|
||
serialPort->setDataBits(static_cast<QSerialPort::DataBits>(dataBits));
|
||
serialPort->setStopBits(static_cast<QSerialPort::StopBits>(stopBits));
|
||
serialPort->setParity(static_cast<QSerialPort::Parity>(parity));
|
||
|
||
} else {
|
||
QString portName = autoConnect(command, expectedResponse);
|
||
if (portName != "-1") {
|
||
serialPort = new QSerialPort(portName, this);
|
||
serialPort->setBaudRate(QSerialPort::Baud9600);
|
||
serialPort->setDataBits(QSerialPort::Data8);
|
||
serialPort->setStopBits(QSerialPort::OneStop);
|
||
serialPort->setParity(QSerialPort::NoParity);
|
||
}
|
||
}
|
||
|
||
if (serialPort && serialPort->open(QIODevice::ReadWrite)) {
|
||
qDebug() << "open serialPort ok";
|
||
return true;
|
||
} else {
|
||
if (serialPort) {
|
||
delete serialPort;
|
||
serialPort = nullptr;
|
||
}
|
||
qDebug() << "open serialPort fail";
|
||
return false;
|
||
}
|
||
}
|
||
|
||
void SerialPortManager::disconnectSerialPort() {
|
||
if (serialPort && serialPort->isOpen()) {
|
||
serialPort->close();
|
||
}
|
||
if (serialPort) {
|
||
delete serialPort;
|
||
serialPort = nullptr;
|
||
}
|
||
}
|
||
|
||
QSerialPort* SerialPortManager::getSerialPort() const {
|
||
return serialPort;
|
||
}
|
||
|
||
QString SerialPortManager::autoConnect(const QByteArray &command, const QByteArray &expectedResponse) {
|
||
const auto ports = QSerialPortInfo::availablePorts();
|
||
for (const QSerialPortInfo &portInfo : ports) {
|
||
QSerialPort port(portInfo);
|
||
port.setBaudRate(QSerialPort::Baud9600);
|
||
port.setDataBits(QSerialPort::Data8);
|
||
port.setStopBits(QSerialPort::OneStop);
|
||
port.setParity(QSerialPort::NoParity);
|
||
|
||
qDebug() << "Trying port:" << portInfo.portName();
|
||
if (port.open(QIODevice::ReadWrite)) {
|
||
if (testPort(port, command, expectedResponse)) {
|
||
port.close();
|
||
qDebug() << "Port found:" << portInfo.portName();
|
||
return portInfo.portName();
|
||
}
|
||
port.close();
|
||
} else {
|
||
qDebug() << "Failed to open port:" << portInfo.portName();
|
||
}
|
||
}
|
||
return "-1";
|
||
}
|
||
|
||
|
||
bool SerialPortManager::testPort(QSerialPort &port, const QByteArray &command, const QByteArray &expectedResponse) {
|
||
for (int attempt = 0; attempt < 3; ++attempt) {
|
||
port.write(hexStringToByteArray(command));
|
||
if (port.waitForBytesWritten(1000) && port.waitForReadyRead(1000)) {
|
||
|
||
QByteArray responseData = port.readAll();
|
||
while (port.waitForReadyRead(100)) {
|
||
responseData += port.readAll();
|
||
}
|
||
qDebug() << "Attempt" << attempt + 1 << ": Received response:" << responseData.toHex();
|
||
|
||
QString responseHex = responseData.toHex();
|
||
QString expectedResponseHex = expectedResponse.toHex();
|
||
|
||
// qDebug() << "test responseHex " <<responseHex;
|
||
|
||
if (responseHex.contains(hexStringToByteArray(expectedResponseHex))) {
|
||
qDebug() << "auto conn test ok";
|
||
return true;
|
||
}
|
||
} else {
|
||
qDebug() << "Attempt" << attempt + 1 << ": No response or timeout.";
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
|
||
QByteArray SerialPortManager::hexStringToByteArray(const QString &hex) {
|
||
QByteArray byteArray;
|
||
for (int i = 0; i < hex.length(); i += 2) {
|
||
bool ok;
|
||
byteArray.append(static_cast<char>(hex.mid(i, 2).toInt(&ok, 16)));
|
||
}
|
||
return byteArray;
|
||
}
|
||
|
||
uint16_t SerialPortManager::CRC16_Calculate(const QByteArray &data)
|
||
{
|
||
uint16_t crc = 0xFFFF;
|
||
|
||
for (int pos = 0; pos < data.size(); pos++) {
|
||
crc ^= static_cast<uint8_t>(data[pos]);
|
||
|
||
for (int i = 8; i != 0; i--) {
|
||
if ((crc & 0x0001) != 0) {
|
||
crc >>= 1;
|
||
crc ^= 0xA001;
|
||
} else {
|
||
crc >>= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
return crc;
|
||
}
|
||
|
||
void SerialPortManager::sendModbusCommand(const QString &hexCommand)
|
||
{
|
||
if (serialPort && serialPort->isOpen())
|
||
{
|
||
QByteArray command = hexStringToByteArray(hexCommand);
|
||
uint16_t crc = CRC16_Calculate(command);
|
||
|
||
command.append(static_cast<char>(crc & 0xFF)); //低
|
||
command.append(static_cast<char>((crc >> 8) & 0xFF)); //高
|
||
|
||
serialPort->write(command);
|
||
}
|
||
else
|
||
{
|
||
qWarning() << "Serial port is not open!";
|
||
}
|
||
}
|
||
|
||
|
||
|
||
double SerialPortManager::receiveModbusResponse(int RESPONSE_LENGTH) {
|
||
if (serialPort && serialPort->isOpen()) {
|
||
|
||
QByteArray responseData = serialPort->readAll();
|
||
while (serialPort->waitForReadyRead(100)) {
|
||
responseData += serialPort->readAll();
|
||
}
|
||
|
||
// qDebug() << "Raw response data:" << responseData.toHex();
|
||
|
||
while (responseData.size() >= RESPONSE_LENGTH) {
|
||
QByteArray singleResponse = responseData.left(RESPONSE_LENGTH);
|
||
responseData.remove(0, RESPONSE_LENGTH);
|
||
|
||
// qDebug() << "Processing single response:" << singleResponse.toHex();
|
||
|
||
// Extract the received CRC
|
||
uint16_t receivedCrc = static_cast<uint8_t>(singleResponse[singleResponse.size() - 2]) |
|
||
(static_cast<uint8_t>(singleResponse[singleResponse.size() - 1]) << 8);
|
||
|
||
// Remove the CRC from the response data
|
||
singleResponse.chop(2);
|
||
|
||
// Calculate the CRC of the received data
|
||
uint16_t calculatedCrc = CRC16_Calculate(singleResponse);
|
||
|
||
qDebug() << "Data without CRC:" << singleResponse.toHex();
|
||
qDebug() << "Received CRC:" << QString::number(receivedCrc, 16).toUpper();
|
||
qDebug() << "Calculated CRC:" << QString::number(calculatedCrc, 16).toUpper();
|
||
|
||
if (receivedCrc == calculatedCrc) {
|
||
qDebug() << "Received valid response with correct CRC:" << singleResponse;
|
||
|
||
// Extract the specific data part (e.g., 03E80003)
|
||
if (singleResponse.size() == 5 ) {
|
||
QByteArray specificData = singleResponse.mid(3, 2); // Extract 4 bytes starting from the 4th byte
|
||
// qDebug() << "Specific data part:" << specificData.toHex();
|
||
|
||
if(specificData.toHex() == "7fff"){
|
||
return -2;
|
||
} else if(specificData.toHex() == "7ffc"){
|
||
return -3;
|
||
}
|
||
|
||
// Extract the first 2 bytes for the integer part
|
||
uint16_t integerPart = (static_cast<uint8_t>(specificData[0]) << 8) |
|
||
static_cast<uint8_t>(specificData[1]);
|
||
|
||
// Extract the second 2 bytes for the decimal places
|
||
uint16_t decimalPlaces = (static_cast<uint8_t>(specificData[2]) << 8) |
|
||
static_cast<uint8_t>(specificData[3]);
|
||
|
||
/**
|
||
* @brief value 1000
|
||
*/
|
||
|
||
//一位小数除10,三位除1000
|
||
double value = static_cast<double>(integerPart) / 1000;
|
||
qDebug() << "Converted value:" << value;
|
||
qDebug() << "Converted value:" << QString::number(value, 'f', decimalPlaces);
|
||
|
||
return value; // Return the calculated value
|
||
} else if (singleResponse.size() == 7 ) {
|
||
QByteArray specificData = singleResponse.mid(3, 4); // Extract 4 bytes starting from the 4th byte
|
||
qDebug() << "Specific data part:" << specificData.toHex();
|
||
|
||
if(specificData.toHex() == "7fff"){
|
||
return -2;
|
||
} else if(specificData.toHex() == "7ffc"){
|
||
return -3;
|
||
}
|
||
|
||
// Extract the first 2 bytes for the integer part
|
||
uint16_t integerPart = (static_cast<uint8_t>(specificData[0]) << 8) |
|
||
static_cast<uint8_t>(specificData[1]);
|
||
|
||
// Extract the second 2 bytes for the decimal places
|
||
uint16_t decimalPlaces = (static_cast<uint8_t>(specificData[2]) << 8) |
|
||
static_cast<uint8_t>(specificData[3]);
|
||
|
||
double value = static_cast<double>(integerPart) / std::pow(10, decimalPlaces);
|
||
qDebug() << "Converted value:" << value;
|
||
qDebug() << "Converted value:" << QString::number(value, 'f', decimalPlaces);
|
||
} else {
|
||
qWarning() << "The response is too short to extract the specific data part.";
|
||
}
|
||
|
||
} else {
|
||
qWarning() << "CRC check failed. Received CRC:" << receivedCrc << "Calculated CRC:" << calculatedCrc;
|
||
}
|
||
}
|
||
|
||
if (!responseData.isEmpty()) {
|
||
qWarning() << "Remaining data after processing:" << responseData.toHex();
|
||
}
|
||
} else {
|
||
qWarning() << "receiveModbusResponse";
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
|
||
|
||
|
||
double SerialPortManager::sendModbus_receive(const QString &hexCommand ,int RESPONSE_LENGTH) {
|
||
if (serialPort && serialPort->isOpen()) {
|
||
QByteArray command = hexStringToByteArray(hexCommand);
|
||
uint16_t crc = CRC16_Calculate(command);
|
||
|
||
command.append(static_cast<char>(crc & 0xFF)); //低
|
||
command.append(static_cast<char>((crc >> 8) & 0xFF)); //高
|
||
|
||
serialPort->write(command);
|
||
|
||
|
||
if (serialPort->waitForBytesWritten(1000)) {
|
||
return receiveModbusResponse(RESPONSE_LENGTH);
|
||
} else {
|
||
qWarning() << "Failed to write command to serial port.";
|
||
}
|
||
} else {
|
||
qWarning() << "sendModbus_receive Serial port is not open!";
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
bool SerialPortManager::sendModbus_receive_2(const QString &hexCommand, int RESPONSE_LENGTH) {
|
||
if (serialPort && serialPort->isOpen()) {
|
||
QByteArray command = hexStringToByteArray(hexCommand);
|
||
uint16_t crc = CRC16_Calculate(command);
|
||
|
||
command.append(static_cast<char>(crc & 0xFF)); //低
|
||
command.append(static_cast<char>((crc >> 8) & 0xFF)); //高
|
||
|
||
serialPort->write(command);
|
||
|
||
if (serialPort->waitForBytesWritten(1000)) {
|
||
QByteArray responseData = serialPort->readAll();
|
||
while (serialPort->waitForReadyRead(100)) {
|
||
responseData += serialPort->readAll();
|
||
}
|
||
|
||
// qDebug() << "Raw response data:" << responseData.toHex();
|
||
|
||
while (responseData.size() >= RESPONSE_LENGTH) {
|
||
QByteArray singleResponse = responseData.left(RESPONSE_LENGTH);
|
||
responseData.remove(0, RESPONSE_LENGTH);
|
||
|
||
// qDebug() << "Processing single response:" << singleResponse.toHex();
|
||
|
||
uint16_t receivedCrc = static_cast<uint8_t>(singleResponse[singleResponse.size() - 2]) |
|
||
(static_cast<uint8_t>(singleResponse[singleResponse.size() - 1]) << 8);
|
||
|
||
singleResponse.chop(2);
|
||
|
||
uint16_t calculatedCrc = CRC16_Calculate(singleResponse);
|
||
|
||
// qDebug() << "Data without CRC:" << singleResponse.toHex();
|
||
// qDebug() << "Received CRC:" << QString::number(receivedCrc, 16).toUpper();
|
||
// qDebug() << "Calculated CRC:" << QString::number(calculatedCrc, 16).toUpper();
|
||
|
||
if (receivedCrc == calculatedCrc) {
|
||
// qDebug() << "Received valid response with correct CRC:" << singleResponse;
|
||
return true;
|
||
} else {
|
||
qWarning() << "CRC check failed. Received CRC:" << receivedCrc << "Calculated CRC:" << calculatedCrc;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (!responseData.isEmpty()) {
|
||
qWarning() << "Remaining data after processing:" << responseData.toHex();
|
||
}
|
||
} else {
|
||
qWarning() << "Failed to write command to serial port.";
|
||
}
|
||
} else {
|
||
qWarning() << "Serial port is not open!";
|
||
}
|
||
return false;
|
||
}
|