#include "DataController.h" #include #include #include #include #include #include #include DataController::DataController(QObject* parent) : QObject{parent} , serialPort_(new QSerialPort) { connect(serialPort_.data(), &QSerialPort::readyRead, this, &DataController::serialReadyRead); connect(serialPort_.data(), &QSerialPort::errorOccurred, this, &DataController::serialError); // for correct qApp->exit(); QTimer::singleShot(0, this, [this]{ openSerialPort(); }); } DataController::~DataController() { } QObject* DataController::qmlInstance(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/) { return new DataController; } void DataController::serialReadyRead() { const auto data = serialPort_->readAll(); qDebug() << "Serial data received (HEX):" << QDateTime::currentDateTime() << data.toHex(); serialData_ += data; parseSerialData(); } void DataController::serialError() { const auto error = serialPort_->error(); if (error == QSerialPort::NoError) { return; } qCritical() << "A serial port error has occurred. Port:" << serialPort_->portName() << "Error:" << serialPort_->errorString(); qApp->exit(1); } QVariantMap DataController::readSettings() { QSettings settings("settings.ini", QSettings::IniFormat); const auto keys = settings.allKeys(); if (keys.isEmpty()) { qCritical() << "File settings.ini is empty. Please put the file next to the executable file."; qApp->exit(1); } QVariantMap settingsMap; for (const auto& key: keys) { settingsMap[key] = settings.value(key); } return settingsMap; } void DataController::openSerialPort() { const auto settings = readSettings(); if (settings.isEmpty()) { return; } const auto portName = settings["name"].toString(); // for testing if (portName == "fake") { mockSerialData(); parseSerialData(); return; } serialPort_->setPortName(portName); if (settings.contains("baudRate")) { serialPort_->setBaudRate(settings["baudRate"].toInt()); } if (settings.contains("dataBits")) { serialPort_->setDataBits((QSerialPort::DataBits)settings["dataBits"].toInt()); } if (settings.contains("parity")) { serialPort_->setParity((QSerialPort::Parity)settings["parity"].toInt()); } if (settings.contains("stopBits")) { serialPort_->setStopBits((QSerialPort::StopBits)settings["stopBits"].toInt()); } if (settings.contains("flowControl")) { serialPort_->setFlowControl((QSerialPort::FlowControl)settings["flowControl"].toInt()); } if (!serialPort_->open(QIODevice::ReadOnly)) { qCritical() << "Can't open serial port. Port:" << serialPort_->portName() << "Error:" << serialPort_->errorString(); qApp->exit(1); } } void DataController::parseSerialData() { const char* startBytes = "\xD0\x0D"; const auto startPosition = serialData_.indexOf(startBytes); if (startPosition == -1) { serialData_.clear(); return; } if (startPosition > 0) { serialData_ = serialData_.mid(startPosition); } if (serialData_.size() < 36) { return; } auto extractInt = [](const QByteArray& array, int start, int length) -> uint32_t { uint32_t result = 0; if (length > 4) { return result; } // reversed byte order for (int i = 0; i < length; ++i) { memcpy(reinterpret_cast(&result) + i, array.data() + start + length - i - 1, 1); } // same byte order // memcpy(&result, array.data() + start, length); return result; }; auto statusToString = [](uint32_t status) -> QString { auto hexStatus = "0x" + QString("%1").arg(status, 2, 16, QLatin1Char('0')).toUpper(); if (hexStatus.compare("0xFF", Qt::CaseInsensitive) == 0) { hexStatus = " — "; } return hexStatus; }; auto shortIntToString = [](uint16_t parameter) -> QString { return parameter == std::numeric_limits::max() ? " — " : QString::number(parameter); }; auto shortDoubleToString = [](uint16_t parameter) -> QString { return parameter == std::numeric_limits::max() ? " — " : QString::number(parameter / 10.0, 'f', 1); }; const auto package = serialData_.mid(0, 36); serialData_ = serialData_.mid(36); quint16 calculatedChecksum = 0; for (int i = 0; i < 34; ++i) { calculatedChecksum += static_cast(package.at(i)); } const auto checksum = extractInt(package, 34, 2); if (calculatedChecksum != checksum) { qWarning() << "incorrect checksum"; return; } setProperty("firstStatus", statusToString(extractInt(package, 2, 1))); setProperty("secondStatus", statusToString(extractInt(package, 3, 1))); setProperty("psnInputVoltage", shortIntToString(extractInt(package, 4, 2))); setProperty("mainVoltage", shortIntToString(extractInt(package, 6, 2))); setProperty("ainMode", extractInt(package, 8, 1)); setProperty("ainReservedMask", extractInt(package, 9, 1)); setProperty("ain1Voltage", shortIntToString(extractInt(package, 10, 2))); setProperty("ain1Current", shortDoubleToString(extractInt(package, 12, 2))); setProperty("ain2Voltage", shortIntToString(extractInt(package, 14, 2))); setProperty("ain2Current", shortDoubleToString(extractInt(package, 16, 2))); setProperty("ain3Voltage", shortIntToString(extractInt(package, 18, 2))); setProperty("ain3Current", shortDoubleToString(extractInt(package, 20, 2))); setProperty("ain4Voltage", shortIntToString(extractInt(package, 22, 2))); setProperty("ain4Current", shortDoubleToString(extractInt(package, 24, 2))); setProperty("ain5Voltage", shortIntToString(extractInt(package, 26, 2))); setProperty("ain5Current", shortDoubleToString(extractInt(package, 28, 2))); setProperty("ain6Voltage", shortIntToString(extractInt(package, 30, 2))); setProperty("ain6Current", shortDoubleToString(extractInt(package, 32, 2))); } void DataController::mockSerialData() { serialData_.clear(); serialData_.append((char)0xD0); serialData_.append((char)0x0D); char status1 = 0x0; char status2 = 0x0; serialData_.append(status1); serialData_.append(status2); uint16_t inputVoltage = qToBigEndian((uint16_t)3010); uint16_t mainVoltage = qToBigEndian((uint16_t)384); serialData_.append(reinterpret_cast(&inputVoltage), 2); serialData_.append(reinterpret_cast(&mainVoltage), 2); char mode = 0; char reservedMask = 0x12; serialData_.append(mode); serialData_.append(reservedMask); uint16_t voltage1 = qToBigEndian((uint16_t)380); uint16_t current1 = qToBigEndian((uint16_t)535); serialData_.append(reinterpret_cast(&voltage1), 2); serialData_.append(reinterpret_cast(¤t1), 2); uint16_t voltage2 = qToBigEndian((uint16_t)381); uint16_t current2 = qToBigEndian((uint16_t)527); serialData_.append(reinterpret_cast(&voltage2), 2); serialData_.append(reinterpret_cast(¤t2), 2); uint16_t voltage3 = qToBigEndian((uint16_t)383); uint16_t current3 = qToBigEndian((uint16_t)729); serialData_.append(reinterpret_cast(&voltage3), 2); serialData_.append(reinterpret_cast(¤t3), 2); uint16_t voltage4 = qToBigEndian((uint16_t)381); uint16_t current4 = qToBigEndian((uint16_t)536); serialData_.append(reinterpret_cast(&voltage4), 2); serialData_.append(reinterpret_cast(¤t4), 2); uint16_t voltage5 = qToBigEndian((uint16_t)382); uint16_t current5 = qToBigEndian((uint16_t)528); serialData_.append(reinterpret_cast(&voltage5), 2); serialData_.append(reinterpret_cast(¤t5), 2); uint16_t voltage6 = qToBigEndian((uint16_t)383); uint16_t current6 = qToBigEndian((uint16_t)730); serialData_.append(reinterpret_cast(&voltage6), 2); serialData_.append(reinterpret_cast(¤t6), 2); quint16 checksum = 0; for (int i = 0; i < serialData_.size(); ++i) { checksum += static_cast(serialData_.at(i)); } checksum = qToBigEndian(checksum); serialData_.append(reinterpret_cast(&checksum), 2); }