Added first project implementation

This commit is contained in:
Yury Shuvakin
2024-01-22 17:37:10 +09:00
parent 3a23721644
commit 661a1b6ba2
35 changed files with 1559 additions and 0 deletions

290
sources/DataController.cpp Normal file
View File

@@ -0,0 +1,290 @@
#include "DataController.h"
#include <QTimer>
#include <QtEndian>
#include <QDateTime>
#include <QSettings>
#include <QSerialPort>
#include <QCoreApplication>
#include <QDebug>
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<char*>(&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<uint16_t>::max() ? "" : QString::number(parameter);
};
auto shortDoubleToString = [](uint16_t parameter) -> QString
{
return parameter == std::numeric_limits<uint16_t>::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<quint8>(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<char*>(&inputVoltage), 2);
serialData_.append(reinterpret_cast<char*>(&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<char*>(&voltage1), 2);
serialData_.append(reinterpret_cast<char*>(&current1), 2);
uint16_t voltage2 = qToBigEndian((uint16_t)381);
uint16_t current2 = qToBigEndian((uint16_t)527);
serialData_.append(reinterpret_cast<char*>(&voltage2), 2);
serialData_.append(reinterpret_cast<char*>(&current2), 2);
uint16_t voltage3 = qToBigEndian((uint16_t)383);
uint16_t current3 = qToBigEndian((uint16_t)729);
serialData_.append(reinterpret_cast<char*>(&voltage3), 2);
serialData_.append(reinterpret_cast<char*>(&current3), 2);
uint16_t voltage4 = qToBigEndian((uint16_t)381);
uint16_t current4 = qToBigEndian((uint16_t)536);
serialData_.append(reinterpret_cast<char*>(&voltage4), 2);
serialData_.append(reinterpret_cast<char*>(&current4), 2);
uint16_t voltage5 = qToBigEndian((uint16_t)382);
uint16_t current5 = qToBigEndian((uint16_t)528);
serialData_.append(reinterpret_cast<char*>(&voltage5), 2);
serialData_.append(reinterpret_cast<char*>(&current5), 2);
uint16_t voltage6 = qToBigEndian((uint16_t)383);
uint16_t current6 = qToBigEndian((uint16_t)730);
serialData_.append(reinterpret_cast<char*>(&voltage6), 2);
serialData_.append(reinterpret_cast<char*>(&current6), 2);
quint16 checksum = 0;
for (int i = 0; i < serialData_.size(); ++i)
{
checksum += static_cast<quint8>(serialData_.at(i));
}
checksum = qToBigEndian(checksum);
serialData_.append(reinterpret_cast<char*>(&checksum), 2);
}

108
sources/DataController.h Normal file
View File

@@ -0,0 +1,108 @@
#ifndef DATACONTROLLER_H
#define DATACONTROLLER_H
#include <QObject>
class QQmlEngine;
class QJSEngine;
class QSerialPort;
class DataController : public QObject
{
Q_OBJECT
Q_PROPERTY(QString firstStatus MEMBER firstStatus_ NOTIFY firstStatusChanged)
Q_PROPERTY(QString secondStatus MEMBER secondStatus_ NOTIFY secondStatusChanged)
Q_PROPERTY(QString psnInputVoltage MEMBER psnInputVoltage_ NOTIFY psnInputVoltageChanged)
Q_PROPERTY(QString mainVoltage MEMBER mainVoltage_ NOTIFY mainVoltageChanged)
Q_PROPERTY(int ainMode MEMBER ainMode_ NOTIFY ainModeChanged)
Q_PROPERTY(int ainReservedMask MEMBER ainReservedMask_ NOTIFY ainReservedMaskChanged)
Q_PROPERTY(QString ain1Voltage MEMBER ain1Voltage_ NOTIFY ain1VoltageChanged)
Q_PROPERTY(QString ain1Current MEMBER ain1Current_ NOTIFY ain1CurrentChanged)
Q_PROPERTY(QString ain2Voltage MEMBER ain2Voltage_ NOTIFY ain2VoltageChanged)
Q_PROPERTY(QString ain2Current MEMBER ain2Current_ NOTIFY ain2CurrentChanged)
Q_PROPERTY(QString ain3Voltage MEMBER ain3Voltage_ NOTIFY ain3VoltageChanged)
Q_PROPERTY(QString ain3Current MEMBER ain3Current_ NOTIFY ain3CurrentChanged)
Q_PROPERTY(QString ain4Voltage MEMBER ain4Voltage_ NOTIFY ain4VoltageChanged)
Q_PROPERTY(QString ain4Current MEMBER ain4Current_ NOTIFY ain4CurrentChanged)
Q_PROPERTY(QString ain5Voltage MEMBER ain5Voltage_ NOTIFY ain5VoltageChanged)
Q_PROPERTY(QString ain5Current MEMBER ain5Current_ NOTIFY ain5CurrentChanged)
Q_PROPERTY(QString ain6Voltage MEMBER ain6Voltage_ NOTIFY ain6VoltageChanged)
Q_PROPERTY(QString ain6Current MEMBER ain6Current_ NOTIFY ain6CurrentChanged)
public:
explicit DataController(QObject* parent = nullptr);
~DataController();
static QObject* qmlInstance(QQmlEngine* engine, QJSEngine* scriptEngine);
signals:
void firstStatusChanged();
void secondStatusChanged();
void psnInputVoltageChanged();
void mainVoltageChanged();
void ainModeChanged();
void ainReservedMaskChanged();
void ain1VoltageChanged();
void ain1CurrentChanged();
void ain2VoltageChanged();
void ain2CurrentChanged();
void ain3VoltageChanged();
void ain3CurrentChanged();
void ain4VoltageChanged();
void ain4CurrentChanged();
void ain5VoltageChanged();
void ain5CurrentChanged();
void ain6VoltageChanged();
void ain6CurrentChanged();
private slots:
void serialReadyRead();
void serialError();
private:
QVariantMap readSettings();
void openSerialPort();
void parseSerialData();
void mockSerialData();
QString firstStatus_ = "";
QString secondStatus_ = "";
QString psnInputVoltage_;
QString mainVoltage_;
int ainMode_ = 0;
int ainReservedMask_ = 0;
QString ain1Voltage_;
QString ain1Current_;
QString ain2Voltage_;
QString ain2Current_;
QString ain3Voltage_;
QString ain3Current_;
QString ain4Voltage_;
QString ain4Current_;
QString ain5Voltage_;
QString ain5Current_;
QString ain6Voltage_;
QString ain6Current_;
QByteArray serialData_;
QScopedPointer<QSerialPort> serialPort_;
};
#endif // DATACONTROLLER_H

72
sources/main.cpp Normal file
View File

@@ -0,0 +1,72 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QLocale>
#include <QTranslator>
#include <QFontDatabase>
#include "DataController.h"
int main(int argc, char *argv[])
{
//#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
//#endif
QGuiApplication app(argc, argv);
QCoreApplication::setOrganizationName("Tksi");
QCoreApplication::setOrganizationDomain("Tksi.rus");
QCoreApplication::setApplicationName("Tksi monitor");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-Black.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-BlackItalic.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-Bold.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-BoldItalic.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-Italic.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-Light.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-LightItalic.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-Medium.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-MediumItalic.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-Regular.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-Thin.ttf");
QFontDatabase::addApplicationFont(":/fonts/Roboto/Roboto-ThinItalic.ttf");
QTranslator translator;
const auto uiLanguages = QLocale::system().uiLanguages();
for (const auto& locale : uiLanguages)
{
const auto baseName = "TksiMonitor_" + QLocale(locale).name();
if (translator.load(":/i18n/" + baseName))
{
app.installTranslator(&translator);
break;
}
}
QQmlApplicationEngine engine;
qmlRegisterSingletonType<DataController>("Tksi", 1, 0, "DataController", &DataController::qmlInstance);
auto isDebug = false;
#ifdef QT_DEBUG
isDebug = true;
#endif
engine.rootContext()->setContextProperty("isDebug", isDebug);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app,
[url](QObject *obj, const QUrl &objUrl)
{
if (!obj && url == objUrl)
{
QCoreApplication::exit(-1);
}
}, Qt::QueuedConnection);
engine.addImportPath(QStringLiteral("qrc:/"));
engine.load(url);
return app.exec();
}