diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7301e1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +tags +*.tags +*.user diff --git a/ATBUpdateTool.ini b/ATBUpdateTool.ini index 263c90a..0e2b4d2 100644 --- a/ATBUpdateTool.ini +++ b/ATBUpdateTool.ini @@ -1,9 +1,12 @@ [REPOSITORY_URL] -repository-url="https://git.mimbach49.de/GerhardHoffmann" +repository-url="gitea@ptu-config.atb-comm.de:ATB/" [DIRECTORIES] plugin-directory="/usr/lib/" working-directory="/opt/app/tools/atbupdate/" +psa-config-directory="etc/psa_config/" +psa-tariff-directory="etc/psa_tariff/" + [PLUGINS] plugin-name="libCAmaster.so" diff --git a/ATBUpdateTool.pro b/ATBUpdateTool.pro new file mode 100644 index 0000000..00205a5 --- /dev/null +++ b/ATBUpdateTool.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +CONFIG += ordered +#SUBDIRS = DownloadDCFirmware DownloadDCJsonFiles UpdatePTUDevCtrl +SUBDIRS = UpdatePTUDevCtrl diff --git a/DownloadDCFirmware/ATBDownloadDCFirmware.ini b/DownloadDCFirmware/ATBDownloadDCFirmware.ini new file mode 100644 index 0000000..e69de29 diff --git a/DownloadDCFirmware/DownloadDCFirmware.pro b/DownloadDCFirmware/DownloadDCFirmware.pro new file mode 100644 index 0000000..96da802 --- /dev/null +++ b/DownloadDCFirmware/DownloadDCFirmware.pro @@ -0,0 +1,113 @@ +QT += core serialport + +TARGET = ATBUpdateDC + +include(../common.pri) + + +VERSION="0.1.0" +win32 { + BUILD_DATE=$$system("date /t") + BUILD_TIME=$$system("time /t") +} else { + BUILD_DATE=$$system("date +%d-%m-%y") + BUILD_TIME=$$system("date +%H:%M:%S") +} + +GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1") + +EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}" + +!contains(CONFIG, INCLUDEINTERFACES) { + INCLUDEINTERFACES=/opt/ptu5/opt/DCLibraries/include +} + +INCLUDEPATH += \ + plugins \ + $${INCLUDEINTERFACES} \ + $${_PRO_FILE_PWD_}/../UpdatePTUDevCtrl \ + $${_PRO_FILE_PWD_}/../common/include + +CONFIG += c++17 + +DEFINES+=APP_VERSION=\\\"$$VERSION\\\" +DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\" +DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\" +DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\" + +# keep comments, as /* fall through */ +QMAKE_CXXFLAGS += -C +QMAKE_CXXFLAGS += -g +QMAKE_CXXFLAGS += -Wno-deprecated-copy -O + +contains( CONFIG, PTU5 ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + CONFIG += link_pkgconfig + lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport + QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7 + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + PTU5BASEPATH = /opt/devel/ptu5 + + INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include + LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library + + ARCH = PTU5 + DEFINES+=PTU5 +} +contains( CONFIG, PTU5_YOCTO ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7 + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + PTU5BASEPATH = /opt/devel/ptu5 + ARCH = PTU5 + DEFINES+=PTU5 + + # add qmqtt lib + #LIBS += -lQt5Qmqtt +} +contains( CONFIG, DesktopLinux ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + lessThan(QT_MAJOR_VERSION, 5): CONFIG += extserialport + # QMAKE_CC = ccache $$QMAKE_CC + # QMAKE_CXX = ccache $$QMAKE_CXX + QMAKE_CXXFLAGS += -std=c++17 + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments } + ARCH = DesktopLinux + DEFINES+=DesktopLinux +} + +SOURCES += \ + main.cpp \ + ../common/src/message_handler.cpp \ + ../UpdatePTUDevCtrl/commandline_parser.cpp \ + update.cpp \ + ../common/src/System.cpp \ + ../common/src/utils_internal.cpp \ + ../common/src/command.cpp + + +HEADERS += \ + ../common/include/message_handler.h \ + ../UpdatePTUDevCtrl/commandline_parser.h \ + update.h \ + ../common/include/System.h \ + ../common/include/utils_internal.h \ + ../common/include/command.h + + +OTHER_FILES += \ + ATBDownloadDCFirmware.ini \ + ../ATBUpdateTool.ini + + +FORMS += \ + mainwindow.ui + +########################################################################################## +# for running program on target through QtCreator +contains( CONFIG, PTU5 ) { + qnx: target.path = /tmp/$${TARGET}/bin + else: unix:!android: target.path = /opt/app/tools/atbupdate/ + !isEmpty(target.path): INSTALLS += target +} diff --git a/DownloadDCFirmware/README.md b/DownloadDCFirmware/README.md new file mode 100644 index 0000000..e69de29 diff --git a/DownloadDCFirmware/dc_download.cpp b/DownloadDCFirmware/dc_download.cpp new file mode 100644 index 0000000..ee41cb6 --- /dev/null +++ b/DownloadDCFirmware/dc_download.cpp @@ -0,0 +1,444 @@ +#include "dc_download.h" +#include + +#include +#include +#include +#include + + +DcDownload::DcDownload(hwinf *hw) + : m_hw(hw) + , m_fileToDownload(m_hw->dcDownloadFileName()) { + // connect(this, &QThread::finished, + // dynamic_cast(m_hw), &QThread::deleteLater); +} + +DcDownload::~DcDownload() { +} + +/* + /////////////////////////////////////////////////////////////////////////////// + // + // USING THE DC BOOTLOADER + // + /////////////////////////////////////////////////////////////////////////////// + + 1 : bl_reboot() // send to application, want DC2 to reset (in order to + // start the bootloader) + // + // NOTE: this function is NOT reliable !!! Sometimes it + // simply does not work, in which case bl_startBL, + // bl_checkBL and bl_isUp do not work as well. + // Alas, there is no feedback if bl_reboot worked! + // + // NOTE: this function can be called only once per + // minute, because once called again, the controller + // performs some self-checks consuming some time. + // + // NOTE: after a successful bl_reboot(), the device is + // waiting about 4 seconds in the bootloader. To stay in + // the bootloader, we have to send the command + // bl_startBL(), which is kind of a misnomer, as it + // should be bl_doNotLeaveBL(). + // + 2 : bl_startBL(): // send within 4s after DC power-on, otherwise + // bootloader is left. + // + // NOTE: a running bootloader is a MUST for the download + // process of a device controller firmware as it does + // the actual writing of the memory (the bl_reboot() + // from above erases the available memory). + // + 3 : bl_check(): // send command to verify if bl is up + // + // NOTE: this command is kind of a request that we want + // to check if the bootloader is up. The device + // (actually the bootloader) responds with its version. + // + 4 : bl_isUp(): // returns true if bl is up and running + // + // NOTE: we know what the bootloader version actually is + // as the bootloader does not change. By comparing the + // string received in the previous step with this known + // version string we know if the bootloader is up. + // + // NOTE FOR ALL PREVIOUS STEPS: execute them in their + // own slots each to be sure to receive any possible + // responds from the device. + // + 5 : bl_sendAddress(blockNumber) + // send start address, nr of 64-byte block, start with 0 + // will be sent only for following block-numbers: + // 0, 1024, 2048, 3072 and 4096, so basically every + // 64kByte. + // for other addresses nothing happens + + 6 : bl_wasSendingAddOK() + // return val: 0: no response by now + // 1: error + // 10: OK + + 7 : bl_sendDataBlock() + // send 64 byte from bin file + + 8 : bl_sendLastBlock() + // send this command after all data are transferred + + 9 : bl_wasSendingDataOK() + // return val: 0: no response by now + // 1: error + // 10: OK + + 10 : bl_stopBL() // leave bl and start (the new) application + // + // NOTE: this function MUST work under all conditions. + // Alas, there is no direct result for this command, so + // the only way of knowing it was successful is to ask + // the device if the bootloader is still running. + // There is no problem to repeat this command until the + // bootloader is really not running anymore. + */ +void DcDownload::doDownload() { +#if 0 +void DcDownload::run() { + // download thread running in ca-master sends the dc-file down to firmware + // TODO: send the json files as well + + m_hw->dcDownloadRequestAck(); + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): DOWNLOAD THREAD STARTED:"; + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << " DcDownload::run(): Filename:" << m_hw->dcDownloadFileName(); + + QDateTime const start = QDateTime::currentDateTime(); + +#if 1 + QFile fn(m_hw->dcDownloadFileName()); + + if (!fn.exists()) { + // output via CONSOLE() etc + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << " DcDownload::run(): Filename:" << m_hw->dcDownloadFileName() << "DOES NOT EXIST";; + } else { + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): DC-CONTROLLER SW-VERSION BEFORE" + << m_hw->dc_getSWversion(); + + // load binary device controller file into memory + QByteArray ba = loadBinaryDCFile(m_hw->dcDownloadFileName()); + if (ba.size() > 0) { + uint16_t const totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1; + m_hw->dcDownloadSetTotalBlockNumber(totalBlocks); + + // fill last block of data to be sent with 0xFF + ba = ba.leftJustified(totalBlocks*64, (char)(0xFF)); + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): TOTAL NUMBER OF BYTES TO SEND TO DC" << ba.size(); + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): TOTAL NUMBER OF BLOCKS" << totalBlocks; + + m_hw->dc_autoRequest(true); // turn auto-request setting on + + m_hw->request_DC2_HWversion(); + m_hw->request_DC2_SWversion(); + QThread::sleep(1); + + // m_hw->dc_autoRequest(false); // turn auto-request setting on + + resetDeviceController(); + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): RESET DEVICE-CONTROLLER"; + + if (startBootloader()) { + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): STARTED BOOT-LOADER"; + + m_hw->dc_autoRequest(false);// turn auto-request setting off for + // download of binary dc + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): TOTAL NUMBER OF FIRMWARE BLOCKS" << totalBlocks; + + int currentBlock = 0; // download of binary dc + DownloadResult res = DownloadResult::OK; + + while (res != DownloadResult::ERROR && currentBlock < totalBlocks) { + if ((res = sendNextAddress(currentBlock)) != DownloadResult::ERROR) { + if ((res = sendNextDataBlock(ba, currentBlock)) != DownloadResult::ERROR) { + m_hw->dcDownloadSetCurrentBlockNumber(currentBlock); + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): currentBlockNumber ..." << currentBlock; + + currentBlock += 1; + } + } + } + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << QString("DcDownload::run(): last 64-byte block %1").arg(currentBlock); + + int const rest = ba.size() % 64; + int const offset = ba.size() - rest; + char const *startAddress = ba.constData() + offset; + + if (rest > 0) { + // SHOULD NEVER HAPPEN !!! + uint8_t local[66]; + memset(local, 0xFF, sizeof(local)); + memcpy(local, startAddress, rest); + qCritical() << "DcDownload::run(): ERROR SEND REMAINING" << rest << "BYTES"; + m_hw->bl_sendDataBlock(64, local); + } else { + m_hw->bl_sendLastBlock(); + m_hw->dcDownloadSetCurrentBlockNumber(currentBlock); + + qCritical() << "DcDownload::run(): currentBlockNumber" << currentBlock; + // QThread::msleep(250); + } + qCritical() << "DcDownload::run(): last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK()); + + stopBootloader(); // stop bootloader several times: if it + QThread::sleep(1); // is not stopped, then the PSA has to be + } + // restarted manually (!!!) + stopBootloader(); + QThread::sleep(1); + } + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): STOPPED BOOT-LOADER"; + + stopBootloader(); // there is no harm in stopping the bootloader even + // if it was not started at all + + m_hw->dc_autoRequest(true); + } + +#else // test + // load binary device controller file into memory + QByteArray ba = loadBinaryDCFile(m_hw->dcDownloadFileName()); + if (ba.size() > 0) { + uint16_t const totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1; + m_hw->dcDownloadSetTotalBlockNumber(totalBlocks); + + // fill last block of data to be sent with 0xFF + ba = ba.leftJustified(totalBlocks*64, (char)(0xFF)); + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): TOTAL NUMBER OF BYTES TO SEND TO DC" << ba.size(); + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): TOTAL NUMBER OF BLOCKS" << totalBlocks; + + m_hw->dc_autoRequest(true); // turn auto-request setting on + + m_hw->request_DC2_HWversion(); + m_hw->request_DC2_SWversion(); + QThread::sleep(1); + + resetDeviceController(); + + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): RESET DEVICE-CONTROLLER"; + QThread::sleep(1); + + if (startBootloader()) { + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): STARTED BOOT-LOADER"; + + m_hw->dc_autoRequest(false);// turn auto-request setting off for + // download of binary dc + for (uint16_t currentBlock = 0; currentBlock <= totalBlocks; ++currentBlock) { + m_hw->dcDownloadSetCurrentBlockNumber(currentBlock); + qCritical() << "DcDownload::run(): currentBlockNumber" << currentBlock; + QThread::msleep(250); + } + m_hw->dc_autoRequest(true); // turn auto-request setting on again + } + + stopBootloader(); // there is no harm in stopping the bootloader even + // if it was not started at all + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "DcDownload::run(): STOPPED BOOT-LOADER"; + } +#endif + + m_hw->dcDownloadSetRunning(false); + m_hw->dcDownloadSetFinished(true); + + QDateTime const end = QDateTime::currentDateTime(); + quint64 secs = start.secsTo(end); + QString runtime; + if (secs % 60) { + runtime = QString("%1min %2s").arg(secs / 60).arg(secs % 60); + } else { + runtime = QString("%1min").arg((secs / 60) + 1); + } + + qCritical() << end.time().toString(Qt::ISODateWithMs) + << QString("DOWNLOAD THREAD FINISHED (RUNTIME %1)") + .arg(runtime); + + // the object deletes itself ! This is the last line in run(). + // Never touch the object after this statement + // m_hw->dcDcDownloadFinalize(this); +#endif +} + +DcDownload::DownloadResult DcDownload::sendStatus(int ret) const { + switch (ret) { // return values of dc are: + case 0: // 0: no answer by now + return DownloadResult::NOP; // 1: error + case 10: // 10: success + return DownloadResult::OK; + default:; + } + return DownloadResult::ERROR; +} + +DcDownload::DownloadResult +DcDownload::sendNextAddress(int bNum) const { + // sends address only if blockNumber is one of 0, 1024, 2048, 3072, 4096 + int noAnswerCount = 0; + int errorCount = 0; + if ( bNum==0 || bNum==1024 || bNum==2048 || bNum==3072 || bNum==4096 ) { + while (noAnswerCount <= 250) { + m_hw->bl_sendAddress(bNum); + QThread::msleep(100); + DownloadResult const res = sendStatus(m_hw->bl_wasSendingAddOK()); + if (res != DownloadResult::NOP) { + if (res == DownloadResult::ERROR) { + if (++errorCount >= 10) { + qCritical() << "addr-block" << bNum << "...FAILED"; + return res; + } + } else { // res == DownloadResult::OK + // qInfo() << "addr-block" << bNum << "...OK"; + + // TODO: hier ins shared-mem schreiben + + return res; + } + } else { + noAnswerCount += 1; // no answer by now + } + } + // wait max. about 3 seconds + return DownloadResult::TIMEOUT; + } + // blockNumber is not one of 0, 1024, 2048, 3072, 4096 -> do nothing + return DownloadResult::NOP; +} + +DcDownload::DownloadResult +DcDownload::sendNextDataBlock(QByteArray const &binary, int bNum) const { + uint8_t local[66]; + int const bAddr = bNum * 64; + int noAnswerCount = 0; + int errorCount = 0; + + memcpy(local, binary.constData() + bAddr, 64); + local[64] = local[65] = 0x00; + + while (noAnswerCount <= 250) { + m_hw->bl_sendDataBlock(64, local); + QThread::msleep(10); + DownloadResult const res = sendStatus(m_hw->bl_wasSendingDataOK()); + if (res != DownloadResult::NOP) { + if (res == DownloadResult::ERROR) { + if (++errorCount >= 10) { + qCritical() << "data for block" << bNum << "...FAILED"; + return res; + } + } else { + // qInfo() << "data for block" << bNum << "OK"; + // TODO: hier ins shared mem schreiben + return res; + } + } else { + noAnswerCount += 1; // no answer by now + } + } + // wait max. about 3 seconds + return DownloadResult::TIMEOUT; +} + +bool DcDownload::startBootloader() const { + qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "starting bootloader..."; + int nTry = 5; + while (--nTry >= 0) { + m_hw->bl_startBL(); + qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_startBL() ..." << nTry; + QThread::msleep(500); + m_hw->bl_checkBL(); + qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_checkBL() ..." << nTry; + QThread::msleep(500); + if (m_hw->bl_isUp()) { + qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bootloader... isUP" << nTry; + return true; + } else { + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "bootloader not up (" << nTry << ")"; + } + } + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "starting bootloader FAILED"; + return false; +} + +bool DcDownload::stopBootloader() const { + qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "stopping bootloader..."; + int nTry = 5; + while (--nTry >= 0) { + m_hw->bl_stopBL(); + qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "bl_stopBL() ..."; + QThread::msleep(500); + m_hw->bl_checkBL(); + qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) << "bl_checkBL() ..." << nTry; + if (!m_hw->bl_isUp()) { + qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "stopping bootloader OK"; + return true; + } + } + qCritical() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "stopping bootloader FAILED"; + return false; +} + +bool DcDownload::resetDeviceController() const { + qDebug() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "resetting device controller..."; + m_hw->bl_rebootDC(); + // wait maximally 3 seconds, before starting bootloader + QThread::sleep(1); + qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "resetting device controller...OK"; + return true; +} + +QByteArray DcDownload::loadBinaryDCFile(QString filename) const { + qInfo() << QDateTime::currentDateTime().time().toString(Qt::ISODateWithMs) + << "loading dc binary" << filename << "..."; + + QFile file(filename); // closed in destructor call + if (!file.exists()) { + qCritical() << file.fileName() << "does not exist"; + return QByteArray(); + } + if (!file.open(QIODevice::ReadOnly)) { + qCritical() << "cannot open file" << file.fileName(); + return QByteArray(); + } + qInfo() << "loading dc binary" << filename << "...OK"; + return file.readAll(); +} diff --git a/DownloadDCFirmware/dc_download.h b/DownloadDCFirmware/dc_download.h new file mode 100644 index 0000000..739ff53 --- /dev/null +++ b/DownloadDCFirmware/dc_download.h @@ -0,0 +1,32 @@ +#ifndef DC_DOWNLOAD_H_INCLUDED +#define DC_DOWNLOAD_H_INCLUDED + +#include +#include + +class hwinf; +class DcDownload { + +public: + enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; + + DcDownload(hwinf *hw); + ~DcDownload(); + + void doDownload(); + +private: + DownloadResult sendStatus(int ret) const; + DownloadResult sendNextAddress(int bNum) const; + DownloadResult sendNextDataBlock(QByteArray const &binary, int bNum) const; + bool startBootloader() const; + bool stopBootloader() const; + QByteArray loadBinaryDCFile(QString dcFileName) const; + bool resetDeviceController() const; + DownloadResult dcDownloadBinary(QByteArray const &b) const; + + hwinf *m_hw; + QString m_fileToDownload; +}; + +#endif // DOWNLOAD_THREAD_H_INCLUDED diff --git a/DownloadDCFirmware/main.cpp b/DownloadDCFirmware/main.cpp new file mode 100644 index 0000000..793db15 --- /dev/null +++ b/DownloadDCFirmware/main.cpp @@ -0,0 +1,202 @@ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "message_handler.h" +#include "commandline_parser.h" +#include "utils.h" +#include "utils_internal.h" +#include "update.h" +#include "System.h" + +#include + +#ifdef __linux__ +#include +#endif + +#ifdef PTU5 +#define SERIAL_PORT "ttymxc2" +#else +#define SERIAL_PORT "ttyUSB0" +#endif + +int read1stLineOfFile(QString fileName) { + QFile f(fileName); + if (f.exists()) { + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&f); + in.setCodec("UTF-8"); + while(!in.atEnd()) { + return in.readLine().toInt(); + } + } + } + return -1; +} + +int main(int argc, char **argv) { + QByteArray const value = qgetenv("LC_ALL"); + if (value != "C") { + qputenv("LC_ALL", "C"); + } + // qputenv("XDG_RUNTIME_DIR", "/var/run/user/0"); + + openlog("DC", LOG_PERROR | LOG_CONS, LOG_USER); + + QCoreApplication a(argc, argv); + QCoreApplication::setOrganizationName("ATB Automatentechnik Baumann GmBH"); + QCoreApplication::setApplicationName("ATBUpdateDC"); + QCoreApplication::setApplicationVersion(APP_VERSION); + + + if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling + atbInstallMessageHandler(atbDebugOutput); + setDebugLevel(LOG_NOTICE); + } + + //return 0; + +/* + CommandLineParser parser; + parser.process(a); + parser.readSettings(); + + QString repositoryUrl = parser.repositoryUrl(); + QString plugInDir = parser.plugInDir(); + QString plugInName = parser.plugInName(); + QString workingDir = parser.workingDir(); + QString psaConfigDir = parser.psaConfigDir(); + QString psaTariffDir = parser.psaTariffDir(); + QString psaDcDir = parser.dcDir(); + QString iniFileName = parser.iniFileName(); + bool const dryRun = parser.dryRun(); + bool const noUpdatePsaHardware = parser.noUpdatePsaHardware(); + bool const showYoctoVersion = parser.yoctoVersion(); + bool const showYoctoInstallStatus = parser.yoctoInstallStatus(); + bool const showExtendedVersion = parser.extendedVersion(); + bool const alwaysDownloadConfig = parser.alwaysDownloadConfig(); + bool const alwaysDownloadDC = parser.alwaysDownloadDC(); + bool const readDCVersion = parser.readDCVersion(); + + QString const rtPath = QCoreApplication::applicationDirPath(); + + int const machineNr = read1stLineOfFile("/mnt/system_data/machine_nr"); + int const customerNr = read1stLineOfFile("/mnt/system_data/cust_nr"); + int const zoneNr = read1stLineOfFile("/mnt/system_data/zone_nr"); + QString const branchName = (zoneNr != 0) + ? QString("zg1/zone%1").arg(zoneNr) : "master"; + + qInfo() << "pwd ......................" << rtPath; + qInfo() << "repositoryUrl ............" << repositoryUrl; + qInfo() << "plugInDir ................" << plugInDir; + qInfo() << "plugInName ..............." << plugInName; + qInfo() << "workingDir ..............." << workingDir; + qInfo() << "psaConfigDir ............." << psaConfigDir; + qInfo() << "psaTariffDir ............." << psaTariffDir; + qInfo() << "dryRun ..................." << dryRun; + qInfo() << "noUpdatePsaHardware ......" << noUpdatePsaHardware; + qInfo() << "alwaysDownloadConfig ....." << alwaysDownloadConfig; + qInfo() << "alwaysDownloadDC ........." << alwaysDownloadDC; + qInfo() << "showYoctoVersion ........." << showYoctoVersion; + qInfo() << "showYoctoInstallStatus ..." << showYoctoInstallStatus; + qInfo() << "showExtendedVersion ......" << showExtendedVersion; + qInfo() << "iniFileName .............." << iniFileName; + qInfo() << "extended-version ........." << APP_EXTENDED_VERSION; + qInfo() << "machineNr ................" << machineNr; + qInfo() << "customerNr ..............." << customerNr; + qInfo() << "zoneNr ..................." << zoneNr; + qInfo() << "readDCVersion ............" << readDCVersion; + qInfo() << "dcDir ...................." << psaDcDir; + + if (!QDir(plugInDir).exists()) { + qCritical() << plugInDir + << "does not exists, but has to contain dc-library"; + exit(-1); + } + + if (showExtendedVersion) { + printf(APP_EXTENDED_VERSION"\n"); + return 0; + } + + QString const &customerRepo + = QDir::cleanPath(workingDir + QDir::separator() + QString("customer_%1").arg(customerNr)); +*/ + + QString const &psaDcDir = internal::customerRepoDcDir(); + QString const &psaRepoRootDir = internal::customerRepoRoot(); + QString const &psaRepoDir = internal::customerRepoDir(); + QString const &branchName = internal::branchName(); + + bool debug = false; + bool noaction = true; + QString workingDir; + QString libDir; + QString libca; + + std::unique_ptr settings = internal::readSettings(); + + if (settings) { + settings->beginGroup("DIRECTORIES"); + workingDir = settings->value("workingdir", "/tmp").toString(); + libDir = settings->value("plugin-directory", "/usr/lib/").toString(); + settings->endGroup(); + + settings->beginGroup("FLAGS"); + debug = settings->value("debug", false).toBool(); + settings->endGroup(); + + settings->beginGroup("PLUGINS"); + libca = libDir + settings->value("plugin-name", "libCAslave.so").toString(); + settings->endGroup(); + } + + // etc/dc: located under mount-path + std::optional mountPath = System::checkForUSBStick(psaDcDir); + QFileInfo fi; + if (mountPath.has_value()) { + fi.setFile(mountPath.value(), System::getDCFileOnUsbStick(mountPath.value())); + } else + if ((mountPath = System::checkForSDCard(psaDcDir)).has_value()) { + fi.setFile(mountPath.value(), System::getDCFileOnSDCard(mountPath.value())); + } else { + + if (debug) { + qInfo() << "using customer repository" << psaRepoDir; + } + + std::unique_ptr c = internal::dcCandidateToInstall("/etc/dc/"); + if (c) { + fi.setFile(*c); + if (fi.exists() == false) { + qCritical() << "dc2c.bin candidate" << *c << "does not exist. STOP."; + return -1; + } + qInfo() << "dc2c.bin canditate" << fi.absoluteFilePath(); + } + } + + if (debug) { + qInfo() << "downloading dc-firmware" << fi.absoluteFilePath(); + qInfo() << "dc-firmware size (bytes)" << fi.size(); + qInfo() << "dc-version" << Update::dcVersion(fi.absoluteFilePath()); + } + + Update u(fi.absoluteFilePath(), libca, debug, noaction); + u.run(); + + qInfo() << ""; + + return 0; +} diff --git a/mainwindow.cpp b/DownloadDCFirmware/mainwindow.cpp similarity index 85% rename from mainwindow.cpp rename to DownloadDCFirmware/mainwindow.cpp index af91863..2639524 100644 --- a/mainwindow.cpp +++ b/DownloadDCFirmware/mainwindow.cpp @@ -1,10 +1,5 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include "worker.h" -#include "utils.h" -#include "progress_event.h" -#include "update_dc_event.h" -#include "plugins/interfaces.h" #include #include @@ -12,14 +7,17 @@ #include #include +#ifdef PTU5 +#define SERIAL_PORT "ttymxc2" +#else +#define SERIAL_PORT "ttyUSB0" +#endif -MainWindow::MainWindow(Worker *worker, QWidget *parent) +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) - , m_worker(worker) - , m_width(70) - , m_progressRunning(false) - , m_updateStep(UpdateDcEvent::UpdateStep::NONE) { + , m_serial(new QSerialPort(this)) + , m_width(70) { this->setStatusBar(new QStatusBar(this)); QFont f; @@ -31,9 +29,12 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent) ui->setupUi(this); - ui->updateProgress->setRange(0, 100); - ui->updateProgress->reset(); + openSerialPort(); + //ui->updateProgress->setRange(0, 100); + //ui->updateProgress->reset(); + +#if 0 QStringList lst; QString start = QDateTime::currentDateTime().toString(Qt::ISODate); lst << QString("Start: ") + start.leftJustified(m_width-10); @@ -71,14 +72,56 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent) connect(m_worker, SIGNAL(showStatusMessage(QString,QString)),this, SLOT(onShowStatusMessage(QString,QString))); connect(m_worker, SIGNAL(replaceLast(QString,QString)),this,SLOT(onReplaceLast(QString,QString))); connect(m_worker, SIGNAL(replaceLast(QStringList,QString)),this, SLOT(onReplaceLast(QStringList,QString))); +#endif } MainWindow::~MainWindow() { - delete m_startTimer; - delete m_exitTimer; + //delete m_startTimer; + //delete m_exitTimer; + closeSerialPort(); delete ui; } +bool MainWindow::openSerialPort() { + // const SettingsDialog::Settings p = m_settings->settings(); + m_serial->setPortName(SERIAL_PORT); + m_serial->setBaudRate(QSerialPort::Baud115200); + m_serial->setDataBits(QSerialPort::DataBits::Data8); + m_serial->setParity(QSerialPort::Parity::NoParity); + m_serial->setStopBits(QSerialPort::StopBits::OneStop); + m_serial->setFlowControl(QSerialPort::FlowControl::NoFlowControl); + if (m_serial->open(QIODevice::ReadWrite)) { + //m_console->setEnabled(true); + //m_console->setLocalEchoEnabled(p.localEchoEnabled); + //m_ui->actionConnect->setEnabled(false); + //m_ui->actionDisconnect->setEnabled(true); + //m_ui->actionConfigure->setEnabled(false); + //showStatusMessage(tr("Connected to %1 : %2, %3, %4, %5, %6") + // .arg(p.name, p.stringBaudRate, p.stringDataBits, + // p.stringParity, p.stringStopBits, p.stringFlowControl)); + return true; + } else { + //QMessageBox::critical(this, tr("Error"), m_serial->errorString()); + + //showStatusMessage(tr("Open error")); + } + return false; +} + +bool MainWindow::closeSerialPort() { + if (m_serial->isOpen()) { + m_serial->close(); + return true; + } + return false; + //m_console->setEnabled(false); + //m_ui->actionConnect->setEnabled(true); + //m_ui->actionDisconnect->setEnabled(false); + //m_ui->actionConfigure->setEnabled(true); + //showStatusMessage(tr("Disconnected")); +} + +#if 0 void MainWindow::customEvent(QEvent *event) { if (event->type() == ProgressEvent::type()) { ProgressEvent *pevent = (ProgressEvent *)event; @@ -306,3 +349,4 @@ void MainWindow::onShowStatusMessage(QStringList lst) { onShowMessage(lst.at(0), ""); } } +#endif diff --git a/DownloadDCFirmware/mainwindow.h b/DownloadDCFirmware/mainwindow.h new file mode 100644 index 0000000..7a76079 --- /dev/null +++ b/DownloadDCFirmware/mainwindow.h @@ -0,0 +1,55 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + + +class MainWindow : public QMainWindow { + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +public slots: + //void onAppendText(QString, QString suffix = ""); + //void onReplaceLast(QStringList, QString suffix = ""); + //void onReplaceLast(QString, QString suffix = ""); + //void onShowErrorMessage(QString, QString); + //void onShowStatusMessage(QString, QString); + //void onShowErrorMessage(QStringList); + //void onShowStatusMessage(QStringList); + //void onStopStartTimer(); + //void onRestartExitTimer(); + //void onEnableExit(); + //void onDisableExit(); + +signals: + +private slots: +// void onQuit(); + +private: +// void scrollDownTextEdit(); +// void onShowMessage(QString, QString); + + bool openSerialPort(); + bool closeSerialPort(); + + Ui::MainWindow *ui; + QSerialPort *m_serial; +// Worker *m_worker; + int const m_width; +// QTimer *m_startTimer; +// QTimer *m_exitTimer; +// bool m_progressRunning; + //int m_progressValue; +// UpdateDcEvent::UpdateStep m_updateStep; +}; +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/DownloadDCFirmware/mainwindow.ui similarity index 77% rename from mainwindow.ui rename to DownloadDCFirmware/mainwindow.ui index 4d081d4..4740c63 100644 --- a/mainwindow.ui +++ b/DownloadDCFirmware/mainwindow.ui @@ -29,21 +29,7 @@ - - - - Exit - - - - - - - 1 - - - - + true @@ -52,6 +38,7 @@ Misc Fixed 11 + 75 true diff --git a/message_handler.cpp b/DownloadDCFirmware/message_handler.cpp similarity index 100% rename from message_handler.cpp rename to DownloadDCFirmware/message_handler.cpp diff --git a/message_handler.h b/DownloadDCFirmware/message_handler.h similarity index 100% rename from message_handler.h rename to DownloadDCFirmware/message_handler.h diff --git a/DownloadDCFirmware/receiver_thread.cpp b/DownloadDCFirmware/receiver_thread.cpp new file mode 100644 index 0000000..e69de29 diff --git a/DownloadDCFirmware/receiver_thread.h b/DownloadDCFirmware/receiver_thread.h new file mode 100644 index 0000000..e69de29 diff --git a/DownloadDCFirmware/sender_thread.cpp b/DownloadDCFirmware/sender_thread.cpp new file mode 100644 index 0000000..e69de29 diff --git a/DownloadDCFirmware/sender_thread.h b/DownloadDCFirmware/sender_thread.h new file mode 100644 index 0000000..e69de29 diff --git a/DownloadDCFirmware/update.cpp b/DownloadDCFirmware/update.cpp new file mode 100644 index 0000000..7627e31 --- /dev/null +++ b/DownloadDCFirmware/update.cpp @@ -0,0 +1,477 @@ +#include "update.h" +#include "command.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX) +#include "unistd.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UPDATE_OPKG (1) +#define UPDATE_DC (0) + + +static const QMap baudrateMap = { + {"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3}, + {"57600" , 4}, {"115200" , 5} +}; + +QPluginLoader Update::pluginLoader; + +hwinf *Update::loadDCPlugin(QString const &libCA /* absolute file path */) { + hwinf *hw = nullptr; + QFileInfo libCAInfo(libCA); + if (libCAInfo.exists()) { + pluginLoader.setFileName(libCA); + // static QPluginLoader pluginLoader(pluginLibName); + if (!pluginLoader.load()) { + qCritical() << "cannot load plugin" << pluginLoader.fileName(); + qCritical() << pluginLoader.errorString(); + return nullptr; + } + + // qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName(); + + if (!pluginLoader.isLoaded()) { + qCritical() << pluginLoader.errorString(); + return nullptr; + } + QObject *plugin = pluginLoader.instance(); + if (!plugin) { + qCritical() << "cannot start instance"; + return nullptr; + } + if (! (hw = qobject_cast(plugin))) { + qCritical() << "cannot cast plugin" << plugin << "to hwinf"; + return nullptr; + } + } else { + qCritical() << libCAInfo.absoluteFilePath() << "does not exist"; + return nullptr; + } + + return hw; +} + +bool Update::unloadDCPlugin() { + if (pluginLoader.unload()) { + qCritical() << "unloaded plugin" << pluginLoader.fileName(); + // Note: will re-instantiate the library ! + // QObject *rootObject = pluginLoader.instance(); + // if (rootObject) { + // qCritical() << "reloaded plugin: root object again available"; + // return false; + // } + // qCritical()unloaded plugin: root object gone"; + return true; + } + return false; +} + +QString Update::dcVersion(QString const &dcBinFile) { + QProcess p; + QStringList params; + + params << "-c" << QString(R"(strings %1 | grep DC2c.\[0-9\] | uniq)").arg(dcBinFile); + + p.start("bash", params); + p.waitForFinished(); + return QString(p.readAllStandardOutput()).trimmed().split(QRegularExpression("\\s")).first(); +} + +class hwapi; +Update::Update(QString const &dcFileName, QString const &libCA, bool debug, bool noaction) + : m_dcFileName(dcFileName) + , m_hw(loadDCPlugin(libCA)) + , m_sys_areDCdataValid(false) + , m_debug(debug) + , m_noaction(noaction) { +} + +Update::~Update() { + unloadDCPlugin(); +} + +Update::DownloadResult Update::sendStatus(int ret) const { + switch (ret) { // return values of dc are: + case 0: // 0: no answer by now + return DownloadResult::NOP; // 1: error + case 10: // 10: success + return DownloadResult::OK; + default:; + } + return DownloadResult::ERROR; +} + +Update::DownloadResult +Update::sendNextAddress(int bNum) const { + // sends address only if blockNumber is one of 0, 1024, 2048, 3072, 4096 + int noAnswerCount = 0; + int errorCount = 0; + if ( bNum==0 || bNum==1024 || bNum==2048 || bNum==3072 || bNum==4096 ) { + // qDebug() << "addr-block" << bNum << "..."; + while (noAnswerCount <= 250) { + + DownloadResult res = DownloadResult::OK; + if (!m_debug) { + m_hw->bl_sendAddress(bNum); + + QThread::msleep(10); //from 100ms to 20ms +//################################################################################### + res = sendStatus(m_hw->bl_wasSendingAddOK()); + } + + if (res != DownloadResult::NOP) { + if (res == DownloadResult::ERROR) { + if (++errorCount >= 10) { + qCritical() << "addr-block" << bNum << "...FAILED"; + return res; + } + } else { // res == DownloadResult::OK + qInfo() << nextTimePoint().toUtf8().constData() << "addr-block" << bNum << "...done"; + return res; + } + } else { + noAnswerCount += 1; // no answer by now + } + } // while + // wait max. about 3 seconds + return DownloadResult::TIMEOUT; + } + // blockNumber is not one of 0, 1024, 2048, 3072, 4096 -> do nothing + return DownloadResult::NOP; +} + +Update::DownloadResult +Update::sendNextDataBlock(QByteArray const &binary, int bNum) const { + uint8_t local[66]; + int const bAddr = bNum * 64; + int noAnswerCount = 0; + int errorCount = 0; + + memcpy(local, binary.constData() + bAddr, 64); + local[64] = local[65] = 0x00; + + QString s = nextTimePoint(); + s += " sending block "; + s += QString("%1/%2 ...done ").arg(bNum).arg(m_totalBlocks); + s += QString::number(ceil(((bNum * 100.0) / (double)m_totalBlocks))); + + qInfo() << s.toUtf8().constData(); + + QThread::msleep(20); //reduce from 200 to 50 ms + //############################################################################ + + QByteArray b((const char *)(&local[0]), 64); + qCritical() << "SNDB" << bNum << b.size() << b.toHex(); + + while (noAnswerCount <= 250) { + + DownloadResult res = DownloadResult::OK; + + if (!m_debug) { + m_hw->bl_sendDataBlock(64, local); + res = sendStatus(m_hw->bl_wasSendingDataOK()); + } + + if (res != DownloadResult::NOP) { + if (res == DownloadResult::ERROR) { + if (++errorCount >= 10) { + qCritical() << "data for block" << bNum << "...FAILED"; + return res; + } + } else { + qInfo() << nextTimePoint().toUtf8().constData() << "data for block" + << QString("%1/%2").arg(bNum).arg(m_totalBlocks) << "done"; + return res; + } + } else { + noAnswerCount += 1; // no answer by now + } + } + // wait max. about 3 seconds + return DownloadResult::TIMEOUT; +} + +bool Update::startBootloader() const { + qDebug() << "starting bootloader..."; + + if (!m_debug) { + int nTry = 10; + while (--nTry >= 0) { + m_hw->bl_startBL(); + QThread::msleep(1000); + m_hw->bl_checkBL(); + if (m_hw->bl_isUp()) { + qInfo() << "starting bootloader...OK"; + QThread::msleep(5000); + return true; + } else { + qCritical() << "bootloader not up (" << nTry << ")"; + qCritical() << "IS BOOTLOADER INSTALLED ???"; + } + } + qCritical() << "starting bootloader...FAILED"; + return false; + } else { + QThread::msleep(1000); + qInfo() << "starting bootloader...OK"; + } + + return true; +} + +bool Update::stopBootloader() const { + qDebug() << "stopping bootloader..."; + + if (!m_debug) { + int nTry = 5; + while (--nTry >= 0) { + m_hw->bl_stopBL(); + QThread::msleep(1000); + if (!m_hw->bl_isUp()) { + qInfo() << "stopping bootloader...OK"; + return true; + } + } + qCritical() << "stopping bootloader...FAILED"; + return false; + + } else { + QThread::msleep(1000); + qInfo() << "stopping bootloader...OK"; + } + + return true; +} + +bool Update::resetDeviceController() const { + qInfo() << nextTimePoint().toUtf8().constData() << "resetting device controller"; + + if (!m_debug) { + m_hw->bl_rebootDC(); + } + + // wait maximally 3 seconds, before starting bootloader + QThread::sleep(1); + + qInfo() << nextTimePoint().toUtf8().constData() + << "resetting device controller ...done"; + + return true; +} + + +QByteArray Update::loadBinaryDCFile(QString const &filename) const { + + QFile file(filename); // closed in destructor call + if (!file.exists()) { + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << file.fileName() << "does not exist"; + return QByteArray{}; + } + if (!file.open(QIODevice::ReadOnly)) { + qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + << "cannot open file" << file.fileName(); + return QByteArray{}; + } + + qInfo() << nextTimePoint().toUtf8().constData() + << "loading dc binary to memory" << Update::dcVersion(filename) << "...done"; + + return file.readAll(); +} +/* + /////////////////////////////////////////////////////////////////////////////// + // + // USING THE DC BOOTLOADER + // + /////////////////////////////////////////////////////////////////////////////// + + 1 : bl_reboot() // send to application, want DC2 to reset (in order to + // start the bootloader) + // + // NOTE: this function is NOT reliable !!! Sometimes it + // simply does not work, in which case bl_startBL, + // bl_checkBL and bl_isUp do not work as well. + // Alas, there is no feedback if bl_reboot worked! + // + // NOTE: this function can be called only once per + // minute, because once called again, the controller + // performs some self-checks consuming some time. + // + // NOTE: after a successful bl_reboot(), the device is + // waiting about 4 seconds in the bootloader. To stay in + // the bootloader, we have to send the command + // bl_startBL(), which is kind of a misnomer, as it + // should be bl_doNotLeaveBL(). + // + 2 : bl_startBL(): // send within 4s after DC power-on, otherwise + // bootloader is left. + // + // NOTE: a running bootloader is a MUST for the download + // process of a device controller firmware as it does + // the actual writing of the memory (the bl_reboot() + // from above erases the available memory). + // + 3 : bl_check(): // send command to verify if bl is up + // + // NOTE: this command is kind of a request that we want + // to check if the bootloader is up. The device + // (actually the bootloader) responds with its version. + // + 4 : bl_isUp(): // returns true if bl is up and running + // + // NOTE: we know what the bootloader version actually is + // as the bootloader does not change. By comparing the + // string received in the previous step with this known + // version string we know if the bootloader is up. + // + // NOTE FOR ALL PREVIOUS STEPS: execute them in their + // own slots each to be sure to receive any possible + // responds from the device. + // + 5 : bl_sendAddress(blockNumber) + // send start address, nr of 64-byte block, start with 0 + // will be sent only for following block-numbers: + // 0, 1024, 2048, 3072 and 4096, so basically every + // 64kByte. + // for other addresses nothing happens + + 6 : bl_wasSendingAddOK() + // return val: 0: no response by now + // 1: error + // 10: OK + + 7 : bl_sendDataBlock() + // send 64 byte from bin file + + 8 : bl_sendLastBlock() + // send this command after all data are transferred + + 9 : bl_wasSendingDataOK() + // return val: 0: no response by now + // 1: error + // 10: OK + + 10 : bl_stopBL() // leave bl and start (the new) application + // + // NOTE: this function MUST work under all conditions. + // Alas, there is no direct result for this command, so + // the only way of knowing it was successful is to ask + // the device if the bootloader is still running. + // There is no problem to repeat this command until the + // bootloader is really not running anymore. + */ +int Update::run() { + if (!m_hw) { + qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin loaded ???"; + return -(int)Result::PLUGIN_LOAD_ERROR; + } + + m_start = QDateTime::currentDateTime(); + + if (m_debug) { + qInfo() << "start dc-update for" << m_dcFileName << "at" << m_start.toString(Qt::ISODate); + qInfo() << "" << Update::dcVersion(m_dcFileName); + } + + if (!m_debug) { + m_hw->dc_autoRequest(false); + } + + qInfo() << "DC auto request OFF"; + + qCritical() << "start dc-update for" << m_dcFileName << "at" << m_start.toString(Qt::ISODate); + + QByteArray ba = loadBinaryDCFile(m_dcFileName); + if (ba.size() > 0) { + m_totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1; + + qInfo() << nextTimePoint().toUtf8().constData() << "blocks to send" << m_totalBlocks; + + // fill last block of data to be sent with 0xFF + ba = ba.leftJustified(m_totalBlocks*64, (char)(0xFF)); + + resetDeviceController(); + if (startBootloader()) { + + int currentBlock = 0; + DownloadResult res = DownloadResult::OK; + qInfo() << nextTimePoint().toUtf8().constData() << "64-byte block" << currentBlock; + + while (res != DownloadResult::ERROR && currentBlock <= m_totalBlocks) { + if ((res = sendNextAddress(currentBlock)) != DownloadResult::ERROR) { + if ((res = sendNextDataBlock(ba, currentBlock)) != DownloadResult::ERROR) { + currentBlock += 1; + } else break; + } + } + +#if 0 + qCritical() << "DownloadThread::run(): last 64-byte block %04d" << currentBlock; + + int const rest = ba.size() % 64; + int const offset = ba.size() - rest; + char const *startAddress = ba.constData() + offset; + + if (rest > 0) { + // SHOULD NEVER HAPPEN !!! + uint8_t local[66]; + memset(local, 0xFF, sizeof(local)); + memcpy(local, startAddress, rest); + qCritical() << "DownloadThread::run(): ERROR SEND REMAINING" << rest << "BYTES"; + m_hw->bl_sendDataBlock(64, local); + } else { + m_hw->bl_sendLastBlock(); + m_hw->dcDownloadSetCurrentBlockNumber(currentBlock); + } + qCritical() << "DownloadThread::run(): last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK()); +#endif + } + stopBootloader(); // there is no harm in stopping the bootloader even + // if starting the bootloader failed + + // check if update was successful + if (!m_debug) { + m_hw->dc_autoRequest(true); //restart dc_autoRequest after download else E255! + } + + for (int i = 0; i < 3; ++i) { + qInfo() << "waiting for device controller restart...(" << i << ")"; + QThread::sleep(20); + + resetDeviceController(); + if (startBootloader()) { + qInfo() << nextTimePoint().toUtf8().constData() << ""; + stopBootloader(); + return -(int)Result::SUCCESS; + } + } + } + + qInfo() << nextTimePoint().toUtf8().constData() << ""; + //To Do Error handling if Dc doesnt start after download + return false; +} diff --git a/DownloadDCFirmware/update.h b/DownloadDCFirmware/update.h new file mode 100644 index 0000000..cc8cbc6 --- /dev/null +++ b/DownloadDCFirmware/update.h @@ -0,0 +1,92 @@ +#ifndef UPDATE_H_INCLUDED +#define UPDATE_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#ifdef PTU5 +#define SERIAL_PORT "ttymxc2" +#define BAUDRATE 115200 +#else +#define SERIAL_PORT "ttyUSB0" +#define BAUDRATE 115200 +#endif + +class QSerialPort; +class Update : public QObject { + Q_OBJECT + + QString m_dcFileName{}; + hwinf *m_hw = nullptr; + bool m_sys_areDCdataValid{}; + bool m_debug{false}; + bool m_noaction; + + static QPluginLoader pluginLoader; + + QDateTime m_start; + QString nextTimePoint() const { + float const secs = m_start.msecsTo(QDateTime::currentDateTime()) / 1000.0; + return QStringLiteral("+%1s").arg(secs, 7, 'f', 2, QChar('0')); + } + +public: + enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; + enum class Result {SUCCESS=0, PLUGIN_LOAD_ERROR}; + + static hwinf *loadDCPlugin(QString const &libCA = "/usr/lib/libCAslave.so"); + static bool unloadDCPlugin(); + static QStringList split(QString line, QChar sep = ','); + + explicit Update(QString const &dcBinFile, QString const &libCA, bool debug, bool noaction); + virtual ~Update() override; + + int run(); + static QString dcVersion(QString const &dcBinFile); + +private: + DownloadResult sendStatus(int ret) const; + DownloadResult sendNextAddress(int bNum) const; + DownloadResult sendNextDataBlock(QByteArray const &binary, int bNum) const; + bool startBootloader() const; + bool stopBootloader() const; + QByteArray loadBinaryDCFile(QString const &dcFilename) const; + bool resetDeviceController() const; + DownloadResult dcDownloadBinary(QByteArray const &b) const; + + QString m_fileToDownload; + uint16_t m_totalBlocks = 0; +/* +private: + static QString jsonType(enum FileTypeJson type); + bool openSerial(int br, QString baudrate, QString comPort) const; + void closeSerial() const; + bool isSerialOpen() const; + bool resetDeviceController() const; + QByteArray loadBinaryDCFile(QString filename) const; + bool downloadBinaryToDC(QString const &bFile) const; + bool updateBinary(QString const &fileToSendToDC); + QStringList getDcSoftAndHardWareVersion(); + QString getFileVersion(QString const& jsonFile); + +private slots: + void readyReadStandardOutput(); + void readyReadStandardError(); + void finished(int exitCode, QProcess::ExitStatus exitStatus); + void onReportDCDownloadStatus(QString const &status); + void onReportDCDownloadSuccess(QString const &msg); + void onReportDCDownloadFailure(QString const &errorMsg); +*/ +}; +#endif // UPDATE_H_INCLUDED diff --git a/DownloadDCFirmware/worker_thread.cpp b/DownloadDCFirmware/worker_thread.cpp new file mode 100644 index 0000000..ff55461 --- /dev/null +++ b/DownloadDCFirmware/worker_thread.cpp @@ -0,0 +1,13 @@ +#include "worker_thread.h" + +WorkerThread::WorkerThread(QObject *parent) + : QThread(parent) { +} + +WorkerThread::~WorkerThread() { + +} + +void WorkerThread::run() { + +} diff --git a/DownloadDCFirmware/worker_thread.h b/DownloadDCFirmware/worker_thread.h new file mode 100644 index 0000000..9c8ed6b --- /dev/null +++ b/DownloadDCFirmware/worker_thread.h @@ -0,0 +1,20 @@ +#ifndef WORKER_THREAD_H_INCLUDED +#define WORKER_THREAD_H_INCLUDED + +#include + +class WorkerThread : public QThread { + Q_OBJECT + +public: + explicit WorkerThread(QObject *parent = nullptr); + ~WorkerThread(); + + +private: + void run() override; + + bool m_quit = false; +}; + +#endif // WORKER_THREAD_H_INCLUDED diff --git a/DownloadDCJsonFiles/ATBDownloadDCJsonFiles.ini b/DownloadDCJsonFiles/ATBDownloadDCJsonFiles.ini new file mode 100644 index 0000000..e69de29 diff --git a/DownloadDCJsonFiles/DownloadDCJsonFiles.pro b/DownloadDCJsonFiles/DownloadDCJsonFiles.pro new file mode 100644 index 0000000..784fa73 --- /dev/null +++ b/DownloadDCJsonFiles/DownloadDCJsonFiles.pro @@ -0,0 +1,105 @@ +QT += core +QT += serialport network + +include(../common.pri) + +TARGET = ATBDownloadDCJsonFiles + +VERSION="0.1.0" +win32 { + BUILD_DATE=$$system("date /t") + BUILD_TIME=$$system("time /t") +} else { + BUILD_DATE=$$system("date +%d-%m-%y") + BUILD_TIME=$$system("date +%H:%M:%S") +} + +GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1") + +EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}" + +!contains(CONFIG, INCLUDEINTERFACES) { + INCLUDEINTERFACES=/opt/ptu5/opt/DCLibraries/include +} + +INCLUDEPATH += \ + plugins \ + $${INCLUDEINTERFACES} \ + $${_PRO_FILE_PWD_}/../UpdatePTUDevCtrl \ + $${_PRO_FILE_PWD_}/../common/include + +CONFIG += c++17 + +DEFINES+=APP_VERSION=\\\"$$VERSION\\\" +DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\" +DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\" +DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\" + +# keep comments, as /* fall through */ +QMAKE_CXXFLAGS += -C +QMAKE_CXXFLAGS += -g +QMAKE_CXXFLAGS += -Wno-deprecated-copy -O + +contains( CONFIG, PTU5 ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + CONFIG += link_pkgconfig + lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport + QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7 + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + + PTU5BASEPATH = /opt/devel/ptu5 + + INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include + LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library + + ARCH = PTU5 + DEFINES+=PTU5 +} +contains( CONFIG, PTU5_YOCTO ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7 + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + PTU5BASEPATH = /opt/devel/ptu5 + ARCH = PTU5 + DEFINES+=PTU5 + + # add qmqtt lib + #LIBS += -lQt5Qmqtt +} +contains( CONFIG, DesktopLinux ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + lessThan(QT_MAJOR_VERSION, 5): CONFIG += extserialport + # QMAKE_CC = ccache $$QMAKE_CC + # QMAKE_CXX = ccache $$QMAKE_CXX + QMAKE_CXXFLAGS += -std=c++17 + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments } + ARCH = DesktopLinux + DEFINES+=DesktopLinux +} + +SOURCES += \ + main.cpp \ + ../UpdatePTUDevCtrl/message_handler.cpp \ + ../UpdatePTUDevCtrl/commandline_parser.cpp \ + update.cpp \ + ../common/src/System.cpp + +HEADERS += \ + ../UpdatePTUDevCtrl/message_handler.h \ + ../UpdatePTUDevCtrl/commandline_parser.h \ + update.h \ + ../common/include/System.h + +OTHER_FILES += \ + ATBDownloadDCJsonFiles.ini \ + ../ATBUpdateTool.ini + + +########################################################################################## +# for running program on target through QtCreator +contains( CONFIG, PTU5 ) { + qnx: target.path = /tmp/$${TARGET}/bin + else: unix:!android: target.path = /opt/app/tools/atbupdate/ + !isEmpty(target.path): INSTALLS += target +} diff --git a/DownloadDCJsonFiles/DownloadDCJsonFiles.pro.user b/DownloadDCJsonFiles/DownloadDCJsonFiles.pro.user new file mode 100644 index 0000000..411e76b --- /dev/null +++ b/DownloadDCJsonFiles/DownloadDCJsonFiles.pro.user @@ -0,0 +1,445 @@ + + + + + + EnvironmentId + {6a494cc5-6dea-4681-86fc-d47b9761a1f4} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 3 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + GenericLinuxOsType + Yocto i.MX6-ATB-PTU5 + Yocto i.MX6-ATB-PTU5 + {804f60e1-6e88-41af-b072-9f5c6a606099} + 0 + 0 + 0 + + 0 + /opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Debug + /opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Debug + + + true + QtProjectManager.QMakeBuildStep + CONFIG+=PTU5 + false + + + + true + Qt4ProjectManager.MakeStep + /usr/bin/make + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + /usr/bin/make + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + /opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Release + /opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Release + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + + + 0 + /opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Profile + /opt/ptu5/opt/build-DownloadDCJsonFiles-Yocto_i_MX6_ATB_PTU5-Profile + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + 0 + + 3 + + + + true + RemoteLinux.CheckForFreeDiskSpaceStep + + + + + / + 5242880 + + + + + true + RemoteLinux.KillAppStep + + + + + + + + 2 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + DeployToGenericLinux + + 1 + + true + true + true + + 1 + + DownloadDCJsonFiles + RemoteLinuxRunConfiguration:/opt/ptu5/opt/DownloadDCJsonFiles/DownloadDCJsonFiles.pro + /opt/ptu5/opt/DownloadDCJsonFiles/DownloadDCJsonFiles.pro + 1 + false + true + false + true + :0 + + 1 + + + + ProjectExplorer.Project.Target.1 + + Desktop + Desktop Qt 5.12.12 GCC 64bit + Desktop Qt 5.12.12 GCC 64bit + qt.qt5.51212.gcc_64_kit + 0 + 0 + 0 + + 0 + /opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Debug + /opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Debug + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + /opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Release + /opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Release + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + + + 0 + /opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Profile + /opt/ptu5/opt/build-DownloadDCJsonFiles-Desktop_Qt_5_12_12_GCC_64bit-Profile + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + 0 + + 3 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + true + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 2 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/DownloadDCJsonFiles/README.md b/DownloadDCJsonFiles/README.md new file mode 100644 index 0000000..e69de29 diff --git a/DownloadDCJsonFiles/main.cpp b/DownloadDCJsonFiles/main.cpp new file mode 100644 index 0000000..aa861e3 --- /dev/null +++ b/DownloadDCJsonFiles/main.cpp @@ -0,0 +1,158 @@ + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include + +#include "message_handler.h" +#include "commandline_parser.h" +#include "utils.h" +#include "update.h" +#include "System.h" + +#include + + +//#include +//#include + +#ifdef PTU5 +#define SERIAL_PORT "ttymxc2" +#else +#define SERIAL_PORT "ttyUSB0" +#endif + +int read1stLineOfFile(QString fileName) { + QFile f(fileName); + if (f.exists()) { + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&f); + in.setCodec("UTF-8"); + while(!in.atEnd()) { + return in.readLine().toInt(); + } + } + } + return -1; +} + +int main(int argc, char **argv) { + QByteArray const value = qgetenv("LC_ALL"); + if (value != "C") { + qputenv("LC_ALL", "C"); + } + // qputenv("XDG_RUNTIME_DIR", "/var/run/user/0"); + + openlog("ATB-DL-JSON", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); + + QCoreApplication a(argc, argv); + QCoreApplication::setApplicationName("ATBDownloadDCJsonFiles"); + QCoreApplication::setApplicationVersion(APP_VERSION); + + if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling + atbInstallMessageHandler(atbDebugOutput); + setDebugLevel(LOG_NOTICE); + } + + CommandLineParser parser; + parser.process(a); + parser.readSettings(); + + QString repositoryUrl = parser.repositoryUrl(); + QString plugInDir = parser.plugInDir(); + QString plugInName = parser.plugInName(); + QString workingDir = parser.workingDir(); + QString psaConfigDir = parser.psaConfigDir(); + QString psaTariffDir = parser.psaTariffDir(); + QString iniFileName = parser.iniFileName(); + bool const dryRun = parser.dryRun(); + bool const noUpdatePsaHardware = parser.noUpdatePsaHardware(); + bool const showYoctoVersion = parser.yoctoVersion(); + bool const showYoctoInstallStatus = parser.yoctoInstallStatus(); + bool const showExtendedVersion = parser.extendedVersion(); + bool const alwaysDownloadConfig = parser.alwaysDownloadConfig(); + bool const alwaysDownloadDC = parser.alwaysDownloadDC(); + + QString const rtPath = QCoreApplication::applicationDirPath(); + + int const machineNr = read1stLineOfFile("/mnt/system_data/machine_nr"); + int const customerNr = read1stLineOfFile("/mnt/system_data/cust_nr"); + int const zoneNr = read1stLineOfFile("/mnt/system_data/zone_nr"); + QString const branchName = (zoneNr != 0) + ? QString("zg1/zone%1").arg(zoneNr) : "master"; + + qInfo() << "pwd ......................" << rtPath; + qInfo() << "repositoryUrl ............" << repositoryUrl; + qInfo() << "plugInDir ................" << plugInDir; + qInfo() << "plugInName ..............." << plugInName; + qInfo() << "workingDir ..............." << workingDir; + qInfo() << "psaConfigDir ............." << psaConfigDir; + qInfo() << "psaTariffDir ............." << psaTariffDir; + qInfo() << "dryRun ..................." << dryRun; + qInfo() << "noUpdatePsaHardware ......" << noUpdatePsaHardware; + qInfo() << "alwaysDownloadConfig ....." << alwaysDownloadConfig; + qInfo() << "alwaysDownloadDC ........." << alwaysDownloadDC; + qInfo() << "showYoctoVersion ........." << showYoctoVersion; + qInfo() << "showYoctoInstallStatus ..." << showYoctoInstallStatus; + qInfo() << "showExtendedVersion ......" << showExtendedVersion; + qInfo() << "iniFileName .............." << iniFileName; + qInfo() << "extended-version ........." << APP_EXTENDED_VERSION; + qInfo() << "machineNr ................" << machineNr; + qInfo() << "customerNr ..............." << customerNr; + qInfo() << "zoneNr ..................." << zoneNr; + + if (showExtendedVersion) { + printf(APP_EXTENDED_VERSION"\n"); + return 0; + } + + QString const &customerRepo = QDir::cleanPath(workingDir + QDir::separator() + QString("customer_%1").arg(customerNr)); + QStringList filesToUpdate; + + // etc/psa_config: located under mount-path + std::optional mountPath = System::checkForUSBStick(psaConfigDir); + if (mountPath.has_value()) { + filesToUpdate = System::getJsonFilesOnUsbStick(mountPath.value()); + } else { + qCritical() << "Using customer repository" << customerRepo; + + QDir dir(QDir::cleanPath(customerRepo + QDir::separator() + "etc/psa_config")); + if (dir.exists()) { + QStringList jsons = dir.entryList(QStringList() << "DC2C*.json", QDir::Files); + if (!jsons.isEmpty()) { + for (QStringList::size_type i=0; i +#include +#include +#include +#include + + +static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " }; +static bool installedMsgHandler = false; +static int debugLevel = LOG_NOTICE; + +int getDebugLevel() { return debugLevel; } +void setDebugLevel(int newDebugLevel) { + debugLevel = newDebugLevel; +} + +bool messageHandlerInstalled() { + return installedMsgHandler; +} + +QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) { + installedMsgHandler = (handler != 0); + static QtMessageHandler prevHandler = nullptr; + if (handler) { + prevHandler = qInstallMessageHandler(handler); + return prevHandler; + } else { + return qInstallMessageHandler(prevHandler); + } +} + +/// +/// \brief Print message according to given debug level. +/// +/// \note Install this function using qInstallMsgHandler(). +/// +/// int main(int argc, char **argv) { +/// installMsgHandler(atbDebugOutput); +/// QApplication app(argc, argv); +/// ... +/// return app.exec(); +/// } +/// +#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + Q_UNUSED(context); + QString const localMsg = QString(DBG_NAME[type]) + msg.toLocal8Bit(); + + switch (debugLevel) { + case LOG_DEBUG: { // debug-level message + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } break; + case LOG_INFO: { // informational message + if (type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_NOTICE: { // normal, but significant, condition + if (type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_WARNING: { // warning conditions + if (type != QtInfoMsg && type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_ERR: { // error conditions + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_CRIT: { // critical conditions + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_ALERT: { // action must be taken immediately + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_EMERG: { // system is unusable + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + default: { + //fprintf(stderr, "%s No ErrorLevel defined! %s\n", + // datetime.toStdString().c_str(), msg.toStdString().c_str()); + } + } +} +#endif + diff --git a/DownloadDCJsonFiles/message_handler.h b/DownloadDCJsonFiles/message_handler.h new file mode 100644 index 0000000..98c4d7e --- /dev/null +++ b/DownloadDCJsonFiles/message_handler.h @@ -0,0 +1,23 @@ +#ifndef MESSAGE_HANDLER_H_INCLUDED +#define MESSAGE_HANDLER_H_INCLUDED + +#include +#ifdef __linux__ +#include +#endif + +int getDebugLevel(); +void setDebugLevel(int newDebugLevel); + +bool messageHandlerInstalled(); +QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler); + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +// typedef void (*QtMessageHandler)(QtMsgType, const char *); +void atbDebugOutput(QtMsgType type, const char *msg); +#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &); +void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg); +#endif + +#endif // MESSAGE_HANDLER_H_INCLUDED diff --git a/DownloadDCJsonFiles/plugins/interfaces.h b/DownloadDCJsonFiles/plugins/interfaces.h new file mode 100644 index 0000000..0aa4a1e --- /dev/null +++ b/DownloadDCJsonFiles/plugins/interfaces.h @@ -0,0 +1,2492 @@ +#ifndef INTERFACE_H +#define INTERFACE_H + +#include + + + +struct T_emp +{ + + // Fixdata from EMP: + uint8_t shaft; // = changer level + uint16_t countryCode; + uint8_t scale; + uint8_t decimals; + uint8_t coinValues[16]; + uint16_t routing; + + // Master specs: + uint8_t gotSetup; // 1: got specifications from master 0: no specs + uint16_t coinAccept; // bit 0 = coin1 bit H=accept + uint8_t tokenChannel; + uint16_t denomination[16]; + + // dynamic: + uint8_t state; // step counter of EMP (electronic coin checker) FSM (finite state machine): + /* + // 0=start command + // 1=powered, do emp ini, send reset + // 2=delay + // 3=wait for response, requesting status after response + // 4,5 through, startup + // 6: wait for status + // 7: through, startup + // 8: IDLE state. EMP is up and ready, polling is running + // 9: polling on, payment not yet on + // 10: payment, check coins + // 11: through + // 12: wait 1s for last coin + // 90: stop all, 1s delay + // 99: off, all stopped + +*/ + uint8_t pollingRunning; + uint8_t paymentRunning; + +}; + +struct Trtc_DateTime +{ + uint8_t rtc_hour; + uint8_t rtc_min; + uint8_t rtc_sec; + uint8_t rtc_dayOfMonth; + uint8_t rtc_month; + uint8_t rtc_year; + uint8_t rtc_dayOfWeek; +}; + +struct Tprn_hw_state +{ + // hardware (IO's) + bool powerRdBk; // prn pwr is on + bool rsSwOk; // serial switch (printer or modem) is set to printer + bool rsDrvOk; // RS232 converter for PTU, Printer and Modem in on + + bool ReadyLine; // HW signal from printer showing ready + bool inIdle; // powered and free from errors + bool paperNearEnd; // paper roll runs out + bool noPaper; + bool ErrorTemp; + bool HeadOpen; + bool cutterJam; + bool noResponse; // printer is not connected, cable broken, wrong baudrate + bool badResponse; +}; + + struct Tprn_currentSettings +{ + uint8_t currFont; + uint8_t currSize; + uint8_t currHeigth; + uint8_t currWidth; + bool nowBold; + bool nowInvers; + bool nowUnderlined; + uint8_t currDensity; + uint8_t currSpeed; + bool nowAligned; +}; + +// obsolete + struct T_dynDat +{ + uint8_t licensePlate[8]; + uint8_t vendingPrice[8]; + uint8_t parkingEnd[8]; + uint8_t currentTime[8]; + uint8_t currentDate[8]; + uint8_t dynDat5[8]; + uint8_t dynDat6[8]; + uint8_t dynDat7[8]; +}; + + +struct T_vaultRecord +{ + + // Kassenbeleg (Abrechnungsdatensatz = Kassenwechsel-Datensatz) + char startbuffer[4]; // Psa> // never move or change this 1st entry + uint16_t AccountingNumber; + uint16_t CUNU; + uint16_t MANU; + uint16_t resint1; + //uint16_t resint2; + + char label1buffer[4]; // tim> + uint8_t year; + uint8_t month; + uint8_t dom; + uint8_t hour; + uint8_t min; + uint8_t sec; + uint8_t DoW; + uint8_t reschar3; + + char label2buffer[4]; // abs> + uint32_t AbsIncome1; + uint32_t AbsReserve; + uint32_t AbsNrOfCuts; + + char label3buffer[4]; // mw > + + // Verkauf, Tuer zu: + uint32_t VKcoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen + uint32_t VKcoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben + + // Service, Tuer offen: + uint16_t ServCoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen + uint16_t ServCoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben + uint16_t resint3; + uint16_t resint4; + uint16_t currentTubeContent[6]; // nur fuer Wechsler, aktueller Fuellstand + uint16_t resint5; + uint16_t resint6; + + char label4buffer[4]; // box> + uint16_t coinsInVault[16]; + uint16_t billsInStacker[8]; + + char label5buffer[4]; // val> + // actually constant unless exchange rate is changed + uint16_t coinDenomination[16]; // 5..50000 (z.B. 2? sind in Ungarn 760Ft) + uint16_t billDenom[8]; + uint16_t tubeDenom[6]; + uint16_t exchangeRate; + uint16_t resint9; + + // new from 1.8.23 + uint32_t cutsSinceCBchange; + uint32_t CBcontent_cent; + uint32_t CBnrofCoins; + + char endofblock[4]; // end +// 332 bytes + + uint16_t CRC16; // Xmodem16 from startbuffer[0] to endofblock[3] + uint16_t resint11; + char endofRecord[4]; // ---- + +}; + +struct T_moduleCondition +{ + // store conditon of all system components, hold in RAM + // 0 means unknown, not yet tested/used + // 1 means OK + // 50..99 = HINT / Notification + // 100..150 = WARNING + // 200..250 = ERROR + + uint8_t ram; + uint8_t intEe; + uint8_t extEe; + + uint8_t rtc; // 1: time/date OK 100: time not plausible 200: hardware error + uint8_t boardHw; + uint8_t printer; + uint8_t modem; + + uint8_t signal; // 1...99 + uint8_t regist; // 100:not 1:reg 2:ping OK 3:gotTime + uint8_t mdbBus; + uint8_t coinChecker; // EMP, OMP or mei-cashflow + + uint8_t coinEscrow; + uint8_t mifareReader; // 0: unknown 1=OK 200=no response 201=wrong response 202: Reader reports HW-error + uint8_t creditTerm; + uint8_t coinReject; + + uint8_t coinSafe; + uint8_t billSafe; + uint8_t voltage; // 1:11..14V + uint8_t temper; + + uint8_t poweronTest; + uint8_t doorState; // 1: alles zu 200: tuer offen + bit1(S) +bit2(CB) + bit3(CB) + uint8_t doorWasOpened; // 1: all doors are closed 200: any door was just opened + uint8_t changer; // can only be tested by usage + + uint8_t coinBlocker; // can only be tested by usage + uint8_t billReader; // can only be tested by usage + uint8_t ResetReason; + uint8_t allModulesChecked; + + uint8_t alarmState; + uint8_t res11; + uint8_t res12; + uint8_t res13; +// 31 +}; + +struct T_dynamicCondition +{ + char allDoorsDebounced; + char openedAuthorized; + uint8_t CBinDebounced; // 0:fehlt 1:drin + char upperDoor; // 99: undefined 0:closed 1:open + char middleDoor; // 99: undefined 0:closed 1:open + char lowerDoor; // 99: undefined 0:closed 1:open + char reserve; + char billBox; + char modeAbrech; + char onAlarm; // 0:alarm aus 1:alarm 2:alarm mit Sirene 3: Sirenentest + char nowCardTest; + char nowPayment; // not used, always 0 + char lastMifCardType; + uint8_t lastSDoorState; + uint8_t lastVDoorState; + uint8_t lastCBstate; + char paymentInProgress; + // Version Szeged: aug2023 + // 0: stopped by timeout + // 1: running 2: wait4lastCoin + // 3: payment stopped manually, coins in Escrow + // 4: payment stopped autom, amount collected, coins in Escrow + // 5: payment stopped, escrow full, coins in Escrow + // 6: coins encashed 7:coins returned + // 8: CoinChecker or MDB on Error + + // since Schoenau with bill and changer, nov2023 + //0 = no payment + //will be set to 1 by cash_startCollection() + //neu 1: wait for devices getting ready for payment + //2 = payment, + //3 = wait for last coin/bill + //4 = Bezahlvorgang manuell beendet + //5 = payment stopped autom, amount collected, coins in Escrow + //6 = Bezahlvorgang beendet weil ZK voll + //4,5,6: payment done, keep on polling, wait for cash or return command + //7 = encash collected money from coin escrow into cash box + //8 = return "amountToReturn", can be complete inserted amount or only overpayment + //9 = wait for changer result + //10= print refund receipt with "amountToReturn" + + + char res1; + uint16_t U_Batt; + uint16_t Temperatur; + uint16_t nrCoinsInBox; + uint32_t amountInBox; + uint32_t totalTransVolume; + uint32_t totalNrOfVends; + char jsonValid_config; + char jsonValid_device; + char jsonValid_cash; + char jsonValid_print; + char jsonValid_serial; + char jsonValid_time; + char lastFileType; +// 44 + uint8_t MifCardHolder[8]; + uint8_t resultOfLastTemplPrint; + // 0: unknown or printing in progress + // 1: OK, doc was printed 2: error, doc was not printed + uint8_t lastPrinterStatus; + // 0: printer OK + // bit0: near paper end bit1: no paper + // bit2: temperature error bit3: error head open + // bit4: paper jam in cutter + // bit6: no response bit7: serial rec. error + // bit5: printer not ready + uint8_t startupTestIsRunning; + + //54 +}; + +struct T_extTime +{ + uint8_t Hours; + uint8_t Min; + uint8_t Sec; + uint8_t Year; + uint8_t Month; + uint8_t Day; + uint8_t DOW; + uint8_t res1; + uint16_t MinOfDay; + uint16_t res2; + uint32_t SecOfDay; + uint8_t isLeapYear; + uint8_t nxtLeapYear; + uint8_t lastLeapYear; + uint8_t hoursOfThisWeek; + uint16_t minutesOfThisWeek; + uint16_t hoursOfThisMonth; + uint16_t daysOfThisYear; + uint16_t GetHoursOfYear; + uint16_t res3; + uint32_t GetMinutesOfYear; + uint8_t getWakeIntvSec; + uint8_t res4; + uint16_t res5; + uint32_t MinutesOfMillenium; + +}; + + +typedef uint8_t UCHAR; +typedef uint16_t UINT; + +struct T_devices +{ + // set by master, used(1) or notused (0) or type 2....20 + + UCHAR kindOfPrinter; // 0:off 1:Gebe + UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza Csquare (MW) + UCHAR kindOfMifareReader; // by now only stronglink SL025 =1 + UCHAR solarPower; // 1:sleep allowed 0: no sleep + //UCHAR suppressSleepMode; // 0:sleep allowed 1: no sleep + + UCHAR kindOfModem; // 0:off 1:Sunlink + UCHAR kindOfCreditcard; // 0:off 1:Feig NFC + UCHAR CoinEscrow; + UCHAR CoinRejectUnit; + + UCHAR CoinShutter; + UCHAR BillAcceptor; + UCHAR usevaultLock; + UCHAR autoAlarm; // 1: switch on siren for 1min in doors opened unauthorized + + UCHAR autoOpen; // 1: open door covers after valid ATBcard + UCHAR printAccReceipt; // 0/1 + UCHAR printDoorReceipt; + UCHAR printTokenTicket; + + UINT VaultFullWarnLevel; + UINT VaultFullErrorLevel; + UINT BattEmptyWarnLevel; + UINT BattEmptyErrorLevel; + +}; + +struct T_chg_Tub +{ + + uint8_t tubeLevel[8]; // [0]=nr coins of lowest value + uint8_t tubeFull[8]; // 1=full 0 else + uint16_t tubeValues[8]; // in cent [0]=lowest value 5c or 10cent + uint16_t tubeFilled[8]; // nr of every coin inserted + uint16_t tubeDispens[8]; // nr of every coin dispensed + // 64 byte +}; + +struct T_changer +{ + // Fixdata from Coin Changer + uint8_t setup; // always 1 + uint8_t state; // step of state machine + // 0..12 like EMP, 13...30 for coin dispense + + uint8_t level; // mdb-level, always 3 + uint16_t countryCode; + uint8_t scale; + uint8_t decimals; + uint8_t coinSetup[16]; // [0]=lowest coin, multiply with scale + uint16_t intendedAcceptance; //bitwise 0,1 1=accept coin, came from master + uint8_t tokenChannel; + uint8_t pollingRunning; // 1: emp is polled 0:not + uint8_t paymentRunning; // 1: coins are accepted + uint16_t denomination[16]; + uint16_t availableTubes; //bitwise 0,1 1=av. bit0 = lowest coin value +}; + +struct T_bna +{ + // reported with STATUS: + uint8_t setup; // 0: not 1: got valid parameters from device + uint8_t FeatureLevel; + uint16_t countryCode; // programmed in EMP + uint16_t scalingFactor; // z.B. 5 + uint8_t decimalPlace; + uint8_t pad1; + uint16_t stackerCap; + uint16_t billSecureLevel; + uint8_t hasEscrow; + uint8_t accBillTypes[16]; // programmed in EMP z.B. (bit 0..7): 1 2 4 10 20 40 0 0 + uint16_t currentStackerLevel; // requested number of notes in billbox + + // settings from Master + uint16_t intendedAccept; // bit 0 =5€ + uint16_t pad2; + uint16_t billDenomination[16]; + +}; + +class DownloadThread; +class hwinf +{ + +public: + virtual ~hwinf() {} + + + + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + // Use serial interface and protocol stack in Cashagent-Library + // Sending Output data to DeviceController DC2b + // Sending input requests to DC2 (single or auto-batch) + // Getting input data as receiver payload + // Furthermore the Cashagent-Library answers with status strings about sending and reading result + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + + + virtual bool dc_openSerial(int BaudNr, QString BaudStr, QString ComName, uint8_t connect) const { + Q_UNUSED(BaudNr); + Q_UNUSED(BaudStr); + Q_UNUSED(ComName); + Q_UNUSED(connect); + return false; + } + // Command: open serial interface + // BaudNr: 0:1200 1:9600 2:19200 3:38400 4:57600 5:115200 + // BaudStr: for exapmle "19200" + // ComName: for example "COM48" + // connect: 0, 1 + + virtual void dc_closeSerial(void) const {} + // Command: close serial interface in order to save power while power down + // or if another port must be used + + + virtual bool dc_isPortOpen(void) const { + return false; + } + // returns true if port open (don't send unless open. Sending to closed port will crash program) + + virtual void dc_autoRequest(bool on) const { + Q_UNUSED(on); + } + // on = true: select that all READ-Requests are sent automatically + // on = false: select that all READ-Requests are sent manually one by one + // Every input information from DC2 must be requested + // ( digital and analog sensors, get time/date, get status information ) + + virtual void dc_requTestResponse() const {} + // tell DC2 to send a test-string, useful to see if cable and baudrate is OK + + virtual bool dc_readAnswTestResponse() const { + return false; + } + // retval: true: test was successful, got right response + + virtual uint8_t dc_isRequestDone(void) const { + return 0; + } + // retval: 0: request is still in progress + // 1: answer from DC2 was OK + // 2: wrong answer from DC2 + + virtual uint16_t dc_getCompletePayLoad(uint16_t plBufSiz, uint8_t *payLoad) const { + Q_UNUSED(plBufSiz); + Q_UNUSED(payLoad); + return 0; + } + // get data back in "payLoad", max 64 byte, can be used for diagnosis + // retval = nr of bytes received. If host buffer too small then + // only plBufSiz bytes are copied to "payLoad" + // plBufSiz = size of host buffer + + virtual void dc_setWakeFrequency(uint8_t period) const { + Q_UNUSED(period); + } + // RTC wakes DC2 (and PTU) by hardware signal every 32seconds + // change wake signal period to 1...64s + + virtual void dc_OrderToReset(void) const {} + // want DC2 to reset (in order to start Bootloader) + + + virtual QString dc_getSerialState(void) const { + return QString(); + } + // get result of opening-command like "ttyS0 opened with 115200 8N1! + // or error messages like "comport not available..." + // was saved by last opening event, can be passed for 100ms + + virtual void dc_clrSerialStateText(void) const {} + // clear above text to avoid multiple repetive displaying + + + virtual void bl_sendDataDirectly(uint8_t length, uint8_t *buf) const { + Q_UNUSED(length); + Q_UNUSED(buf); + } + // send without protocol frame, needed for the DC bootloader + + virtual uint8_t getRawRecLength(void) const { + return 0; + } + // only needed if protocol stack in Cashagent-Library is bypassed + + virtual uint8_t getRawReceivedData(uint8_t *receivedData) const { + Q_UNUSED(receivedData); + return 0; + } + // only needed if protocol stack in Cashagent-Library is bypassed + + virtual QString dc_getSerialParams(void) const { + return QString(); + } + // get DC2 serial settings (not very meaningful as they will not come if different from master settings) + + virtual QString dc_getHWversion(void) const { + return QString(); + } + // get DC2 hardware version + + virtual QString dc_getSWversion(void) const { + return QString(); + } + // get DC2 software version + + virtual QString dc_getState(void) const { + return QString(); + } + // get DC2 status (every OK or any error/warning? ) + + +// ---------------------------------------------------------------------------------------------------------- +// Date and Time +// ---------------------------------------------------------------------------------------------------------- + + virtual uint8_t rtc_getDateTime(struct Trtc_DateTime *rtc_DateTime) const { + Q_UNUSED(rtc_DateTime); + return 0; + } + + virtual uint8_t rtc_setDateTime(void) const { + return 0; + } + // synch DC2 with PC or PTU system time and date + + virtual void rtc_getTime(uint8_t *hh, uint8_t *mm, uint8_t *ss) const { + Q_UNUSED(hh); + Q_UNUSED(mm); + Q_UNUSED(ss); + } + // get time directly + + virtual void rtc_getDate(uint8_t *yy, uint8_t *mm, uint8_t *dd) const { + Q_UNUSED(yy); + Q_UNUSED(mm); + Q_UNUSED(dd); + } + // get date directly + + virtual uint8_t rtc_getToday(uint8_t *dow, uint16_t *minOfToday, uint32_t *secOfToday) const { + Q_UNUSED(dow); + Q_UNUSED(minOfToday); + Q_UNUSED(secOfToday); + return 0; + } + // dow=day of week, 1=monday...7 + // minOfToday: 0=midnight...1439= 23:59 + // secOfToday: 0=midnight...86399= 23:59:59 + + virtual bool rtc_isLeapYear(uint8_t *lastLeapYear, uint8_t *NextLeapYear) const { + Q_UNUSED(lastLeapYear); + Q_UNUSED(NextLeapYear); + return false; + } + // retval true: this year is leap year + + virtual bool rtc_isLeapYear() const { + return false; + } + + virtual void rtc_getWeek(uint8_t *DayOfWeek, uint8_t *HoursOfWeek, uint16_t *MinutesOfWeek) const { + Q_UNUSED(DayOfWeek); + Q_UNUSED(HoursOfWeek); + Q_UNUSED(MinutesOfWeek); + } + // DayOfWeek: 1=monday...7 + // HoursOfWeek: 0=Monday 0:00 o'clock...167=Sunday 23:00 + // MinutesOfWeek: 0=Monday 0:00 o'clock...10079=Sunday 23:59 + + virtual void rtc_getMonth(uint8_t *DayOfMonth, uint16_t *HoursOfMonth, uint16_t *MinutesOfMonth) const { + Q_UNUSED(DayOfMonth); + Q_UNUSED(HoursOfMonth); + Q_UNUSED(MinutesOfMonth); + } + // DayOfMonth: 1...31 + // HoursOfMonth: 0 = 0:00o'clock of 1.day in month up to 743 + // MinutesOfMonth:0 = 0:00o'clock of 1.day in month up to 44639 + + virtual void rtc_getYear(uint16_t *DayOfYear, uint16_t *HoursOfYear, uint32_t *MinutesOfYear) const { + Q_UNUSED(DayOfYear); + Q_UNUSED(HoursOfYear); + Q_UNUSED(MinutesOfYear); + } + // DayOfYear: 1...366 1= 1.Jan of this current year + // HoursOfYear: 0=1.Jan 0:00o'clock ...8783=31.12 23 o'clock + // MinutesOfYear: 0=1.Jan 0:00o'clock ...527039=31.12 23:59 o'clock + + virtual QString rtc_getTimStr(void) const { + return QString(); + } + virtual QString rtc_getDatStr(void) const { + return QString(); + } + virtual QString rtc_getTimDatStr(void) const { + return QString(); + } + + + // UID - unique number, different in every DC + virtual void dc_getUID8byte(uint8_t *buf8byteUid) const { + Q_UNUSED(buf8byteUid); + } + // get 8 single bytes in buffer + + virtual QString dc_getUIDstr() const { + return QString(); + } + // get as string + + virtual uint64_t dc_getUIDnumber(void) const { + return 0; + } + // get UID as one long number + +// ---------------------------------------------------------------------------------------------------------- +// analogs +// ---------------------------------------------------------------------------------------------------------- + + // Analog values: + virtual uint32_t dc_getTemperature(void) const { + return 0; + } + // in Sax-Format 0...400 (0=-50,0degC 100=0,0degC 141=20,5degC 400=150,0degC) + + virtual QString dc_getTemperaturStr(void) const { + return QString(); + } + // as string like "-12,5degC" + + virtual uint32_t dc_getVoltage(void) const { + return 0; + } + // as value in mV, 0...65,535V + + virtual QString dc_getVoltagStr(void) const { + return QString(); + } + // as string in mV + + virtual bool dc_mainFuseIsOk(void) const { + return false; + } + // true if 12V fuse is OK + // false: fuse blown, DC will continue working but no 12V device can be used! + +// ------------------------------------------------------------------------------ +// digital outputs +// ------------------------------------------------------------------------------ + + virtual uint8_t lock_switchUpperLock(uint8_t dir) const { + Q_UNUSED(dir); + return 0; + } + // dir 0=off 1=up 2=down + // move lock until stop cmd (0) + + virtual uint8_t lock_switchLowerLock(uint8_t dir) const { + Q_UNUSED(dir); + return 0; + } + // dir 0=off 1=up 2=down + // move lock until stop cmd (0) + + virtual void lock_switchVaultDoor(void) const {} + + virtual void coin_switchRejectMotor(uint8_t dir) const { + Q_UNUSED(dir); + } + + virtual void coin_rejectCoins(void) const {} + + // LEDs + virtual void led_switchLedService(uint8_t on) const { + Q_UNUSED(on); + } + virtual void led_switchLedPaper(uint8_t on, uint8_t ton, uint8_t tof) const { + Q_UNUSED(on); + Q_UNUSED(ton); + Q_UNUSED(tof); + } + virtual void led_switchLedPinPad(uint8_t on, uint8_t ton, uint8_t tof) const { + Q_UNUSED(on); + Q_UNUSED(ton); + Q_UNUSED(tof); + } + virtual void led_switchLedStart(uint8_t on, uint8_t ton, uint8_t tof) const { + Q_UNUSED(on); + Q_UNUSED(ton); + Q_UNUSED(tof); + } + virtual void led_switchLedCoinbassin(uint8_t on, uint8_t ton, uint8_t tof) const { + Q_UNUSED(on); + Q_UNUSED(ton); + Q_UNUSED(tof); + } + + virtual void fan_switchFan(bool on) const { + Q_UNUSED(on); + } + virtual void alarm_switchSiren(bool on) const { + Q_UNUSED(on); + } + virtual void bar_OpenBarrier(bool open) const { + Q_UNUSED(open); + } + virtual void ptu_switchWake(bool WAKEACTIVE) const { + Q_UNUSED(WAKEACTIVE); + } + + virtual void aux_power(bool on) const { + Q_UNUSED(on); + } + virtual void aux_setUsage(uint8_t PinDirection) const { + Q_UNUSED(PinDirection); + } + virtual void aux_setOutputs(uint8_t PinIsHigh) const { + Q_UNUSED(PinIsHigh); + } + + virtual void lock_switchContactPower(bool on) const { + Q_UNUSED(on); + } + + virtual void prn_switchPower(bool on) const { + Q_UNUSED(on); + } + + virtual void mif_readerOn(bool on) const { + Q_UNUSED(on); + } + + virtual void mif_creatAtbCard(uint8_t cardType) const { + Q_UNUSED(cardType); + } + + virtual void mod_switchPower(bool on) const { + Q_UNUSED(on); + } + + virtual void mod_switchWake(bool WAKEACTIVE) const { + Q_UNUSED(WAKEACTIVE); + } + + virtual void mdb_switchPower(bool on) const { + Q_UNUSED(on); + } + + virtual void mdb_switchWake(bool WAKEACTIVE) const { + Q_UNUSED(WAKEACTIVE); + } + + virtual void credit_switchPower(bool on) const { + Q_UNUSED(on); + } + // the same as modem power + + virtual void credit_switchWake(bool WAKEACTIVE) const { + Q_UNUSED(WAKEACTIVE); + } + + virtual void shut_move(bool open) const { + Q_UNUSED(open); + } + + virtual void esc_moveFlaps(uint8_t flap ) const { + Q_UNUSED(flap); + } + // 0: close both 1: open take-flap 2: open return + + +// ------------------------------------------------------------------------------ +// door, cashbox and lock switches +// ------------------------------------------------------------------------------ + + virtual uint8_t door_getSwitches(void) const { + return 0; + } + // retval: bit0: upper door 1: low door 2:vault door + + virtual bool door_isUpperDoorOpen(void) const { + return false; + } + + virtual bool door_isLowerDoorOpen(void) const { + return false; + } + + virtual bool vault_isVaultDoorOpen(void) const { + return false; + } + + virtual uint8_t vault_getSwitches(void) const { + return 0; + } + // retval bit0: cash box, bit 1: bill box + + virtual bool vault_isCoinVaultIn(void) const { + return false; + } + + virtual bool vault_isBillVaultIn(void) const { + return false; + } + + virtual uint8_t door_getLocks(void) const { + return 0; + } + // retval bit0: upper lever is up (=open) + // bit1: upper lever is down (=locked) + // bit2: lower lever is up + // bit3: lower lever is down (=locked) + + virtual bool door_upperDoorIsLocked(void) const { + return false; + } + + virtual bool door_upperDoorIsUnlocked(void) const { + return false; + } + + virtual bool door_lowerDoorIsLocked(void) const { + return false; + } + + virtual bool door_lowerDoorIsUnlocked(void) const { + return false; + } + + virtual bool bar_optoIn1isOn(void) const { + return false; + } + + virtual bool bar_optoIn2isOn(void) const { + return false; + } + + virtual uint8_t aux_getAuxInputs(void) const { + return 0; + } + + virtual bool ptu_WakeINisActive(void) const { + return false; + } + + virtual bool mdb_WakeINisActive(void) const { + return false; + } + + virtual bool prn_readyINisActive(void) const { + return false; + } + + virtual bool coid_isAttached(void) const { + return false; + } + + virtual bool coin_escrowIsOpen(void) const { + return false; + } + + virtual bool mif_cardIsAttached(void) const { + return false; + } + +//bool hwapi::mod_WakeINisActive(void); + + virtual bool door_isContactPowerOn(void) const { + return false; + } + + virtual bool mif_isMifarePowerOn(void) const { + return false; + } + + virtual bool mdb_testIsmdbTxDon(void) const { + return false; + } + + virtual bool aux_isAuxPowerOn(void) const { + return false; + } + + virtual bool mod_isGsmPowerOn(void) const { + return false; + } + + virtual bool cred_isCreditPowerOn(void) const { + return false; + } + + virtual bool prn_isPrinterPowerOn(void) const { + return false; + } + + virtual uint8_t prn_PrnFuseIsOk(void) const { + return 0; + } + //retval: 0: fuse blown 1: fuse OK 2:unknown as printer power is off + + virtual bool mdb_isMdbPowerOn(void) const { + return false; + } + + virtual bool cash_getRejectMotorHomePos(void) const { + return false; + } + + virtual uint8_t cash_getLowPaperSensor(void) const { + return 0; + } + // 0: Sensor sees paper 1: no paper 99: off + + + // ------------------------------------------------------------------------------ + // request commands + // ------------------------------------------------------------------------------ + + // all read-requests can be sent manually by the following functions + // or automatically in background by: void hwapi::dc_autoRequest(bool on) + + + virtual void request_DC2serialConfig() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + + virtual void request_DC2_HWversion() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_SWversion() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_condition() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_UID() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_TimeAndDate() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_analogues() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_digitalInputs() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_digitalOutputs() const {} + // read-request can be sent manually by this function, not needed if auto-request is on + + // ------------------------------------------------------------------------------ + + // the following device state requests are deployed only if device is powered up: + virtual void request_PrinterHwState() const {} + virtual void request_PrinterCurrentFonts() const {} + virtual void request_PrinterStateComplete() const {} + + virtual void request_MifareReaderState() const {} + virtual void request_MifareCardType() const {} + virtual void request_MifareAtbType() const {} + virtual void request_MifareID() const {} + virtual void request_MifareData(uint8_t dataBlockNumber) const { + Q_UNUSED(dataBlockNumber); + } + //virtual void request_MifareData() const =0; + + virtual void request_MDB_Status() const {} + virtual void request_MDB_lastResponse() const {} + virtual void request_EMP_allParameters() const {} + virtual void request_EMP_lastCoin() const {} + + + // ------------------------------------------------------------------------------ + // readback digital outputs of connected devices + // these functions are not needed for normal operation + // but can be used to test and verify conditions + // ------------------------------------------------------------------------------ + + + virtual bool test_getDO_mdbRXtst(void) const { + return false; + } + // readback digital outputs of connected devices + // these functions are not needed for normal operation + // but can be used to test and verify conditions + + virtual uint8_t lock_getDO_motors(void) const { + return 0; + } + // bit0: upper lock forward bit 1 backward + // bit2: lower lock forward bit 3 backward + + virtual uint8_t test_serialState(void) const { + return 0; + } + // test on-board signals for the serials + // serial drv on/off, Serial mux1, Serial mux2 + + virtual bool test_serialIsOn(void) const { + return false; + } + + virtual bool test_serialMux1isSetToPrinter(void) const { + return false; + } + virtual bool test_serialMux1isSetToModem(void) const { + return false; + } + virtual bool test_serialMux2isSetToCredit(void) const { + return false; + } + virtual bool test_serialMux2isSetToMifare(void) const { + return false; + } + + virtual bool led_coinIsOn(void) const { + return false; + } + virtual bool led_frontIsOn(void) const { + return false; + } + virtual bool led_ticketIsOn(void) const { + return false; + } + virtual bool led_pinIsOn(void) const { + return false; + } + virtual bool led_StartIsOn(void) const { + return false; + } + virtual bool led_insideIsOn(void) const { + return false; + } + + virtual bool fan_isOn(void) const { + return false; + } + virtual bool siren_isOn(void) const { + return false; + } + virtual bool bar_relayIsOn(void) const { + return false; + } + virtual bool ptu_WakeOutIsOn(void) const { + return false; + } + + virtual bool aux_powerIsOn(void) const { + return false; + } + + virtual bool coin_shutterIsOpen(void) const { + return false; + } + virtual bool coin_shutterTestOutput(void) const { + return false; + } + + virtual uint8_t coin_escrowFlapOpened(void) const { + return 0; + } + // retval: 1:return flap is open 2:take flap is open 0:closed + + + +// ------------------------------------------------------------------------------ +// devices are operated by DC +// processes with more then one devices +// timer controlled or long term processes +// ------------------------------------------------------------------------------ + + + virtual void sendDeviceSettings(uint8_t kindOfPrinter, uint8_t kindOfCoinChecker, + uint8_t kindOfMifareReader, uint8_t suppressSleep, + uint8_t kindOfModem, uint8_t kindOfCredit ) const { + Q_UNUSED(kindOfPrinter); + Q_UNUSED(kindOfCoinChecker); + Q_UNUSED(kindOfMifareReader); + Q_UNUSED(suppressSleep); + Q_UNUSED(kindOfModem); + Q_UNUSED(kindOfCredit); + } + // enable hardware in device controller: + // kindOfPrinter: 0:off 1: GPT4672 (only this one implemented) + // kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: Csquare_changer + // kindOfMifareReader: 0:off 1: SL025 (only this one implemented) + // suppressSleep: 0:sleep allowed 1: sleep surpressed for special reason + // kindOfModem: 0:off 1: ATB_Sunlink_LTE (not yet implemented) + // kindOfCredit: 0:off 1: cVendTopp 2:cVendPin (not yet implemented) + + virtual void request_ReadbackDeviceSettings() const {} + + virtual void readback_DeviceSettings(uint8_t *length, uint8_t *data) const { + Q_UNUSED(length); + Q_UNUSED(data); + } + // refer to DC2 manual for exact content + // state 5.5.21: byte[0]=kindOfPrinter byte[1]=kindOfCoinChecker + // byte[2]=kindOfMifarereadr byte[3]=suppress sleep mode + // byte[4]=kindOfModem byte[5]=kind of cc terminal + + virtual void sendMachineID(uint16_t customerNr, uint16_t machineNr, + uint16_t borough, uint16_t zone, + uint16_t alias, char *location) const { + Q_UNUSED(customerNr); + Q_UNUSED(machineNr); + Q_UNUSED(borough); + Q_UNUSED(zone); + Q_UNUSED(alias); + Q_UNUSED(location); + } + + virtual void request_ReadbackMachineID() const =0; + + virtual void readback_machineIDdata(uint8_t *length, uint8_t *data) const { + Q_UNUSED(length); + Q_UNUSED(data); + } + // state 5.5.21: byte[0,1]=customer number byte[2,3]=machine number + // byte[4,5]=borough byte[6,7]=zone byte[8,9]=alias name + // byte[10...41]=location + + // Locks stops automatically at end switch or by timeout + virtual uint8_t lock_openUpperDoor(void) const { + return 0; + } + virtual uint8_t lock_closeUpperDoor(void) const { + return 0; + } + + virtual uint8_t lock_openLowerDoor(void) const { + return 0; + } + virtual uint8_t lock_closeLowerDoor(void) const { + return 0; + } + + virtual void shut_openOnce(void) const {} + // and close automatic after shutter time + + virtual void shut_openForCoin(bool start) const { + Q_UNUSED(start); + } + // open flap if coin is attached + // once process is started it runs until stop command + + virtual void shut_sendOpeningTime(uint16_t timeIn_ms ) const { + Q_UNUSED(timeIn_ms); + } + // after this time without retrigger the flap is closed + + virtual void esc_takeMoney(void) const {} + // and close automatically after escrow time (1s) + + virtual void esc_returnMoney(void) const {} + // and close automatically after escrow time (1s) + + +// ---------------------------------------------------------------------------------------------------------- +// --------------------------------------------- MIFARE ----------------------------------------------------- +// ---------------------------------------------------------------------------------------------------------- + +// obsolete + virtual uint8_t mif_returnReaderStateAndCardType(uint8_t *buf, uint8_t maxBufferSize) const { + Q_UNUSED(buf); + Q_UNUSED(maxBufferSize); + return 0; + } + // retval 0=OK 1=error host buffer too small + /* data description, new fast version: + byte 0= still the same: current read state: + 0=power off 1=reader-fault 2=ready + 3=just reading 4=read complete + 5=read partial, removed too early + 6=state unknown + + byte 1: reader state 1=ok 0=nok + byte 2: card preent (0,1) + byte 3: card selected (0) + byte 4: card type: 0...5 + byte 5: card allowed (0=no 1=MifareClassic 1k or 4k) + byte 6: CardSize: 1 or 4 (kB) + byte 7: length of UID 4 or 7 (byte) + */ + + virtual bool mif_readerIsOK(void) const { + return false; + } + + virtual bool mif_cardAttached(void) const { + return false; + } + // not working! use mif_cardIsAttached() instead + + virtual uint8_t mif_readResult(void) const { + return 0; + } + // result: 0: unknown or still in progress + // 1: card read successful + // 2: reading error + // not working! + + virtual QString mif_cardUID(void) const { + return QString(); + } + // not working + + virtual uint8_t mif_getCardDataDec(uint8_t blkNr, uint8_t *buf, uint8_t maxBufferSize) const { + Q_UNUSED(blkNr); + Q_UNUSED(buf); + Q_UNUSED(maxBufferSize); + return 0; + } + + virtual QString mif_getCardDataStr(uint8_t blockNumber) const =0; + // with blockNumber=0...11 + + + + // ---------------------------------------------------------------------------------------------------------- + // --------------------------------------------- PRINTER ---------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------- + + // read printer condition and settings + + virtual uint8_t prn_getHwState(struct Tprn_hw_state *prn_hw_state) const { + Q_UNUSED(prn_hw_state); + return 0; + } + // retval: status byte + // byte 0 = 0: prnter OK, >0: error + // bit0: paper low 1: no paper 2: temperature error + // 3: head open 4: paper jam in cutter + // 6: no response 7: bad response from printer + // and return struct "Tprn_hw_state" + + virtual bool prn_isUpAndReady(void) const { + return false; + } + // true: printer is powered, serial is ok, no error, printer is connected and resonding + + virtual void prn_getCurrentFontSetting(struct Tprn_currentSettings *prn_fonts) const { + Q_UNUSED(prn_fonts); + } + + + // send Commands to printer: + + virtual void prn_sendText(QByteArray *buf) const { + Q_UNUSED(buf); + } + // up to 1280 bytes + + virtual void prn_sendPrnSysCmd(uint8_t para1, uint8_t para2, uint32_t para3) const { + Q_UNUSED(para1); + Q_UNUSED(para2); + Q_UNUSED(para3); + } + // send three byte through to printer, see printers manual + + virtual void prn_sendPrnEscCmd(uint8_t para1, uint8_t para2, uint8_t para3, uint8_t para4) const { + Q_UNUSED(para1); + Q_UNUSED(para2); + Q_UNUSED(para3); + Q_UNUSED(para4); + } + // send four byte through to printer, see printers manual + + + virtual void prn_sendPrnSetup(uint16_t paperSpeed, uint8_t density, uint8_t alignment, uint8_t orientation) const { + Q_UNUSED(paperSpeed); + Q_UNUSED(density); + Q_UNUSED(alignment); + Q_UNUSED(orientation); + } + // send 5 byte: byte 0,1: speed 5...250 mm/s + // byte2: density 0....(25)....50 + // byte3: alignment 'l', 'c', 'r' = left, center, right + // byte4: orientation 0, 90, 180 = 0deg, 90deg, 180deg rotation (by now not supported!) + // not batched! don't use twice within 100ms + + virtual void prn_movePaper(uint8_t wayInMm, uint8_t direction) const { + Q_UNUSED(wayInMm); + Q_UNUSED(direction); + } + //direction: 1=forward 2=backward + // + virtual void prn_setFonts(uint8_t font, uint8_t size, uint8_t width, uint8_t height) const { + Q_UNUSED(font); + Q_UNUSED(size); + Q_UNUSED(width); + Q_UNUSED(height); + } + // font = kind of font 5...11 (0..22) + // size = 6...20, 9..9: too tiny 10: small ...12 = normal size ...20=huge + // width: 0...4 0=1x 1=2x 2=4x (huge!) 3=8x 4=16x (3,4 make no sense) + // heigth: 0...7 = 1x...8x only 0,1,2,(3) make sense + + virtual void prn_setLetters(uint8_t bold, uint8_t invers, uint8_t underlined) const { + Q_UNUSED(bold); + Q_UNUSED(invers); + Q_UNUSED(underlined); + } + // bold: 0/1 + // invers: 0/1 + // underlined: 0/1 + + virtual void prn_cut(uint8_t kindof) const { + Q_UNUSED(kindof); + } + // kindof: 1=full cut 2=partial cut 3=eject (5xLF + full cut) + + virtual void prn_newLine(uint8_t nrOfLines) const { + Q_UNUSED(nrOfLines); + } + + virtual void prn_printCompleteFontTable(void) const {} + + + virtual void prn_printBarcode(uint8_t kindOf, uint8_t withText, uint8_t offset, uint8_t rotation, uint8_t dataLeng, uint8_t *data) const { + Q_UNUSED(kindOf); + Q_UNUSED(withText); + Q_UNUSED(offset); + Q_UNUSED(rotation); + Q_UNUSED(dataLeng); + Q_UNUSED(data); + } + // kind of barcode: 0=Code39 1=Code128 2=EAN13 3= 2/5interleaved 4=UPC-A 5=EAN8 + // withText: print readable text below + // offset: move by pixel from left border + // rotation + // dataLeng in byte + + virtual void prn_sendQRdata(QByteArray *buf) const { + Q_UNUSED(buf); + } + // maximal 150 alphanummeric bytes + + virtual void prn_printQRcode(void) const {} + // QRcode may have 1...150 alphanummeric data, must be transfered in advance + + + virtual void prn_printLogo(uint8_t nrOfLogo, uint8_t offset ) const { + Q_UNUSED(nrOfLogo); + Q_UNUSED(offset); + } + // nrOfLogo: 1..4 in flash 5...8 in Ram + // offset: in mm form left border + + + // ......................................................... + // Parking Ticket (print-out document) designer TD - obsolete + // ......................................................... + + + + // ---------------------------------------------------------------------------------------------------------- + // --------------------------------------------- MDB Bus ---------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------- + + virtual void mdb_sendBusReset(void) const {} + + virtual void mdb_sendCommand(uint8_t toMdbDevice, uint8_t mdbCommand) const { + Q_UNUSED(toMdbDevice); + Q_UNUSED(mdbCommand); + } + // send one bus command directly over mdb bus, refer to mdb manual for commands + // this command is not needed in normal operation, just for new or special things + + virtual void mdb_sendMessage(uint8_t toMdbDevice, uint8_t mdbCommand, uint8_t nrOfData, uint8_t *dataBuffer) const { + Q_UNUSED(toMdbDevice); + Q_UNUSED(mdbCommand); + Q_UNUSED(nrOfData); + Q_UNUSED(dataBuffer); + } + // nrOfData = sizeOf(dataBuffer) maximal 34 byte according mdb specs + // same as mdb_sendCommand, just with data + + virtual bool mdb_busIsReadyToWork() const { + return false; + } + + virtual bool mdb_deviceVoltageOK() const { + return false; + } + + virtual bool mdb_busVoltageOk() const { + return false; + } + + virtual uint8_t mdb_getLastDeviceResponse(uint8_t *fromDevice, uint8_t *lastRequest, + uint8_t *responseLength, uint8_t *responseBuffer) const { + Q_UNUSED(fromDevice); + Q_UNUSED(lastRequest); + Q_UNUSED(responseLength); + Q_UNUSED(responseBuffer); + return 0; + } + // fromDevice: device nr from which data was requested 0,1,2,3 + // lastRequest: sent mdb command + // responseLength: nr of payload data (after mdb-ack) 0...34 + // responseBuffer holds payload data (answer from mdb device) + // return val: mdb result of this request: 1=got ACK 2=got 3xNAK 3=no or bad response 4:got Data (after ACK) + + + // ---------------------------------------------------------------------------------------------------------- + // ---------------------------------- Electronic Coin Validator EMP ----------------------------------------- + // ---------------------------------------------------------------------------------------------------------- + + + virtual void emp_sendSettings(uint16_t coinAcceptance, uint8_t tokenChannel, uint16_t *coinDenomination ) const { + Q_UNUSED(coinAcceptance); + Q_UNUSED(tokenChannel); + Q_UNUSED(coinDenomination); + } + // coinAcceptance: bit0=coin1 (lowest donomination) bit15=coin16 bitH=accept bit L = deny coin (no validation) + // tokenChannel 0...31: if this signal comes from emp then a token was inserted + // coinDenomination = array of 16 coin values (e.g. 5, 10, 20...) + + virtual void emp_pollingOnOff(uint8_t on) const { + Q_UNUSED(on); + } + + virtual void emp_startCoinAcceptance() const {} + + virtual void emp_stopCoinAcceptance() const {} + + virtual void emp_getAllParameters(struct T_emp *emp) const { + Q_UNUSED(emp); + } + // see struct in hwapi.h + // usage example: + // hwapi *HWaccess const override; + // HWaccess = new hwapi() const override; + // struct T_emp myEmp const override; + // HWaccess->emp_getAllParameters(&myEmp) const override; + // readval=myEmp.pollingRunning const override; + + virtual uint8_t emp_chkIfCoinInserted(void) const { + return 0; + } + // retval: 0...16 coins left in FIFO + + virtual void emp_getNewCoinRecord(uint8_t *valid, uint8_t *signal, uint8_t *error, uint16_t *value) const { + Q_UNUSED(value); + Q_UNUSED(signal); + Q_UNUSED(error); + Q_UNUSED(valid); + } + // with every call ONE coin is taken out of FIFO and pointer decremented + // valid: should be 1 + // signal: comes right from coin checker, 0...15 (0=first programmed coin type) 0xFF=no signal + // error: was reported from EMP as dynamic signal right after coin insertion (instead of + // coin signal), example: 3=unknown coin 4=coin is blocked by host. 0xFF=no error + // value: of the coin. Depends on parameter "coinDenomination" in function "emp_sendSettings" + // if coinDenomination[coin 0..15] = 0 then the value programmed in coin checker is taken + // if coinDenomination > 0 then this value is taken. + // Useful in case of two currencies (adapt to local currency) or for token. + + // function gives more details as "emp getLastCoin()" but "emp getLastCoin()" is easier to use + + // alternativ to emp_getNewCoinRecord( ): + virtual uint8_t emp_giveLastCoin(uint16_t *value, uint8_t *signal) const { + Q_UNUSED(value); + Q_UNUSED(signal); + return 0; + } + // retval: 0: NO coin stored 1: valid coin 2: got wrong coin or coin denied + // value: if retval1: value of the coin if reval=2: error number + // 0xFF means NO error or NO signal (as 0 is a valid error/signal) + // signal: channel nr reported from checker 0...15 + + + virtual uint8_t emp_returnLastCoin(uint16_t *value, uint8_t *signal) const { + Q_UNUSED(value); + Q_UNUSED(signal); + return 0; + } + // use for changer + + + + virtual QString dc_getTxt4RsDiagWin(void) const { + return QString(); + } + virtual void dc_clrTxt4RsDiagWin(void) const {} + virtual QString dc_get2ndTxt4RsDiagWin(void) const { + return QString(); + } + virtual void dc_clr2ndTxt4RsDiagWin(void) const {} + virtual QString dc_getTxt4HsStateLine(void) const { + return QString(); + } + virtual void dc_clrTxt4HsStateLine(void) const {} + virtual QString dc_getTxt4masterStateLine(void) const { + return QString(); + } + virtual void dc_clrTxt4masterStateLine(void) const {} + virtual QString dc_getTxt4resultStateLine(void) const { + return QString(); + } + virtual void dc_clrTxt4resultStateLine(void) const {} + virtual QString dc_getdataStateLine(void) const { + return QString(); + } + virtual void dc_clrTxt4dataStateLine(void) const {} + virtual QString dc_getdatifLine(void) const { + return QString(); + } + virtual void dc_clrTxt4datifLine(void) const {} + + + +// ---------------------------------------------------------------------------------------------------------- +// -------- DC Bootloader 1.Version +// ---------------------------------------------------------------------------------------------------------- + + // using DC2 Bootloader + virtual void bl_iniChain() const {} + virtual bool bl_importBinFile(QByteArray readBinFile, uint32_t fileSize, char withDispl) const { + Q_UNUSED(readBinFile); + Q_UNUSED(fileSize); + Q_UNUSED(withDispl); + return false; + } + virtual uint8_t bl_activatBootloader(uint8_t *sendData) const { + Q_UNUSED(sendData); + return 0; + } + virtual uint8_t bl_startChain() const { + return 0; + } + virtual uint8_t bl_readBLversion(uint8_t *sendData) const { + Q_UNUSED(sendData); + return 0; + } + // minimum size of sendData-buffer: 5byte retval: length + virtual uint8_t bl_readFWversion(uint8_t *sendData) const { + Q_UNUSED(sendData); + return 0; + } + // minimum size of sendData-buffer: 5byte retval: length + + virtual uint8_t bl_prepareDC_BLcmd(uint8_t Cmd, uint8_t SendDataLength, uint8_t *sendData, uint8_t *outBuf) const { + Q_UNUSED(Cmd); + Q_UNUSED(SendDataLength); + Q_UNUSED(sendData); + Q_UNUSED(outBuf); + return 0; + } + // make BL protocol, retval = outbuf length (5...133) + // bring data in correct form: start always with 0x02 finish with 0x03 and append checksum + // 0x02 Cmd < ...sendData ..> CRC CRC 0x03 + // Data length = 0...64 + // special conversion: if data contain 2 or 3 (STX, ETX) then write two bytes: 0x1B (=ESC) and data|0x80 + // so maxlength = 5 + 2 x 64 (if all data are 2 or 3) without 2,3: maxlength = 5 + 64 + + virtual uint8_t bl_exitBL(uint8_t *sendData) const { + Q_UNUSED(sendData); + return 0; + } + // minimum size of sendData-buffer: 5byte retval: length + + virtual void led_switchLedIllumination(uint8_t on) const { + Q_UNUSED(on); + } + + + + +// ------------------------------------------------------------------------------------ +// 27.3.2023: Use Device-Controller's Bootloader to send hex-file +// ------------------------------------------------------------------------------------ + + virtual void bl_rebootDC(void) const {} + + virtual void bl_startBL() const {} + virtual void bl_checkBL() const {} + virtual bool bl_isUp() const { + return false; + } + // return true is bl is up and running + // also initializes "sendFile" + + virtual void bl_sendAddress(uint16_t blockNumber) const { + Q_UNUSED(blockNumber); + } + // send start address, nr of 64byte-block, start with 0 + // will be sent only for folling block-numbers: + // 0, 1024, 2048, 3072 and 4096, so basically every 64kByte + + virtual uint8_t bl_wasSendingAddOK() const { + return 0; + } + // return val: 0: no response by now 1:error 10: OK + + virtual void bl_openBinary() const {} + + virtual void bl_sendDataBlock(uint8_t length, uint8_t *buffer) const { + Q_UNUSED(length); + Q_UNUSED(buffer); + } + // send 64 byte from bin file + + virtual void bl_sendLastBlock() const {} + + virtual uint8_t bl_wasSendingDataOK() const { + return false; + } + // return val: 0: no response by now 1:error 10: OK + + virtual void bl_stopBL() const {} + + +// ------------------------------------------------------------------------------------ +// 6.4.2023: new functions for coin collection and printing +// some new system functions +// ------------------------------------------------------------------------------------ + + + virtual bool rtc_setTimeDateDirect(struct Trtc_DateTime *DateTime) const { + Q_UNUSED(DateTime); + return false; + } + // return true if successful. could fail if more the 8 commands are waiting + + virtual bool rtc_getExtendedTime(uint8_t *leng, uint8_t *data) const { + Q_UNUSED(leng); + Q_UNUSED(data); + return false; + } + + virtual bool rtc_getExtendedTime(struct T_extTime *exTime) const { + Q_UNUSED(exTime); + return false; + } + + virtual bool sys_runCompleteTest() const { + return false; + } + // warning: lasts 20s in one pace + + virtual bool sys_ready4sending() const { + return false; + } + // return true if a Json-file can be sent + + virtual bool sys_sendJsonFileToDc(uint8_t kindOfFile, uint8_t nrOfTemplate, uint8_t *content ) const { + Q_UNUSED(kindOfFile); + Q_UNUSED(nrOfTemplate); + Q_UNUSED(content); + return false; + } + // kindOfFile: 1=config, 2=device, 3=cash, 4=serial, 5=time, 6=printer + // nrOfTemplate=1...32 if kindOfFile==6 + // content = content of the Json file, max 800byte ascii signs + // file is 0-terminated! + // return false if sending is not possible, wait a second + + virtual bool prn_sendDynamicPrnValues(uint8_t *dynPrnVal ) const { + Q_UNUSED(dynPrnVal); + return false; + } + // dynPrnVal = array of 8 Variables with 8 byte each, come as ascii string + // like: char prn_dynPrnVal[8][8]; + // return true if sending, false if cmd-stack is full + + virtual bool prn_printTemplate(uint8_t nrOftemplate) const { + Q_UNUSED(nrOftemplate); + return false; + } + // print one of the templates loaded by Json prior + // nr = 1..32 + // return true if sending, false if cmd-stack is full + + virtual void log_getHoldAccountNumbers(uint8_t *nrOfVals, uint16_t *accNr ) const { + Q_UNUSED(nrOfVals); + Q_UNUSED(accNr); + } + // returns all acc nrs of the backuped vault records + // use: uint16_t backupedAccNumbers[8] + + virtual bool log_selectVaultRecord(uint16_t accountNr ) const { + Q_UNUSED(accountNr); + return false; + } + // return true if sending, false if cmd-stack is full + // and trigger transfer + + virtual bool log_chkIfVaultRecordAvailable() const { + return false; + } + // return true if completly received + + virtual bool log_getVaultRecord(struct T_vaultRecord *retVR) const =0; + // which was selected by: log_selectVaultRecord() + // to be forwarded to Ismas + + virtual bool prn_printAccountReceipt() const { + return false; + } + // print all 8 backuped accounting receipts + // return true if sending to DC OK, false if cmd-stack is full + + virtual bool prn_printTestTicket() const { + return false; + } + // return true if sending to DC OK, false if cmd-stack is full + + + virtual bool cash_startPayment(uint32_t amount) const { + Q_UNUSED(amount); + return false; + } + // 17.4.23TS: extended to 32bit + + virtual uint8_t cash_paymentProcessing() const { + return 0; + } + // run this function periodically while coin payment process to generate necessary signals + // return value: + // 0: stopped 1: starting up 2: coin collection + // 3: finished by User (Push button) 4: finished, Max-Value collected + // 5: finished by escrow + // 10,11: error cannot start + // 12: timeout while payment, coins returned + // 13: stopped by unexpected error + + virtual uint32_t getInsertedAmount() const { + return 0; + } + + virtual uint16_t getLastInsertedCoin(void) const { + return 0; + } + + virtual bool getAllInsertedCoins(uint16_t *types, uint16_t *values) const { + Q_UNUSED(types); + Q_UNUSED(values); + return false; + } + // alle bei diesem Verkauf eingeworfenen Muenzen sind gespeichert, max 64 + + + virtual bool cash_cancelPayment() const { + return false; + } + // and return coins + + virtual bool cash_stopPayment() const { + return false; + } + // and keep coins in escrow + + + // after ticket/goods issue: + virtual bool vend_success() const { + return false; + } + // conclude payment process, encash all inserted coins to vault. Printing was successful + // if possible return change + + virtual bool vend_failed() const { + return false; + } + // conclude payment process and return all inserted coins + + + + + + virtual uint8_t mif_getCardType(QString *cardholder) const { + Q_UNUSED(cardholder); + return 0; + } + // return 1,2,3,4 = upper, lower access card, printer test, coin test + // cardholder: 7byte Name-String + + virtual uint64_t sys_getWakeSource() const { + return 0; + } + // retval: 6 bytes, bit coded, 1=event keeps DC awake + + virtual uint8_t sys_getWakeReason() const { + return 0; + } + // Master was woken by following reason: + // 1: MDB Event + // 2: Coin Event + // ( 3: Master Event) - will not set the wake line + // ( 4: 32s pulse) - will not set the wake line + // 5: Door Event + // ( 6: Diag Event) - will not set the wake line + // 7: 30min-Pulse for HB + + virtual void sys_getDeviceConditions(uint8_t *leng, uint8_t *data) const { + Q_UNUSED(leng); + Q_UNUSED(data); + } + + virtual void sys_getDeviceConditions(struct T_moduleCondition *devCond) const { + Q_UNUSED(devCond); + } + + virtual void sys_getDynMachineConditions(uint8_t *leng, uint8_t *data) const { + Q_UNUSED(leng); + Q_UNUSED(data); + } + + virtual void sys_getDynMachineConditions(struct T_dynamicCondition *dynMachCond) const { + Q_UNUSED(dynMachCond); + } + + + virtual uint32_t cash_getAmountInVault() const { + return 0; + } + + virtual uint16_t cash_getNrCoinsInVault() const { + return 0; + } + + virtual uint8_t prn_getPrintResult() const { + return 0; + } + // return: 0: just printing, wait + // 1: OK - last print was succesful + // 2: error - not printed + + + + virtual uint8_t prn_getCurrentPrinterState() const { + return 0; + } + // 0: printer OK + // bit0: near paper end bit1: no paper + // bit2: temperature error bit3: error head open + // bit4: paper jam in cutter + // bit6: no response bit7: serial rec. error + // bit5: printer not ready + + + virtual void sys_sendDeviceParameter(struct T_devices *deviceSettings) const { + Q_UNUSED(deviceSettings); + } + + virtual void sys_restoreDeviceParameter(struct T_devices *deviceSettings) const { + Q_UNUSED(deviceSettings); + } + // attention: only applies if function "sys_sendDeviceParameter()" was used to send this settings before + // cannot be used to see settings programmed by JsonFile + + virtual bool sys_areDCdataValid() const { + return false; + } + + virtual bool sys_sendingTest() const { + return false; + } + + virtual void prn_requestCurrentDynData() const {} + + virtual bool prn_getCurrentDynamicPrnValuesFromDC(uint8_t *dynPrnVal ) const { + Q_UNUSED(dynPrnVal); + return false; + } + // which was sent with: bool prn_sendDynamicPrnValues(uint8_t *dynPrnVal ) const =0; + + virtual bool prn_dynDataAreEqual(uint8_t *buf) const { + Q_UNUSED(buf); + return false; + } + + virtual bool prn_printKombiticket(uint8_t nrOfKombi) const { + Q_UNUSED(nrOfKombi); + return false; + } + // print four of the templates loaded by Json prior + // nr = 1..8 + + virtual void lock_triggerUpperSolenoid(void) const {} + + virtual void lock_triggerLowerSolenoid(void) const {} + + virtual bool doors_supervise() const { + return false; + } + + virtual bool dc_isAutoRequestOn() const { + return false; + } + + virtual uint16_t log_getLatestAccountNumber() const { + return 0; + } + // new function 27.6.2023 + // latest = highest of the backup's + + virtual uint8_t log_getAvailableVaultBlocks() const { + return 0; + } + // return 0x0011 1111 if all 6 blocks are loaded (one bit per block) + + virtual uint8_t log_getAnswerToLastSlaveRequest() const { + return 0; + } + // use only for ONE request/command + // return: 0xFF: result unknown by now as sending is ongoing + // 0=OK + // 1= wrong length 2=wrong start sign 5= wrong crc + // 6= slave: master cmd was wrong 7: slave: could not write/read data + // 8=timeout, got no response from slave + // 0,8 work, 1..6 not yet tested. 8 comes immed. and stays 8 until reconnect + + // use for important and extended commands (print several templates, print ticket...) + virtual void log_startSupervision() const {} + + virtual uint8_t log_getAnswerToLastCmdBatch() const { + return 0; + } + // 0xFF: no command sent by now + // 0: started, in progress + // 1: done and OK + // 2: done and error + // not working properly, always 0 + + virtual bool log_getVaultData(uint8_t *data) const { + Q_UNUSED(data); + return false; + } + // get vault record in linear 8bit buffer with 384 byte + + + + // new from 1.8.23 + virtual bool prn_printOneAccountReceipt(uint16_t accountNr) const { + Q_UNUSED(accountNr); + return false; + } + // print one out of eight stored last accounting receipts + // function log_getHoldAccountNumbers() gives a list of acc-Nr. of the stored receipts + + virtual bool prn_printAllAvailAccountReceipts() const { + return false; + } + // same as: prn_printAccountReceipt() from line 1153 + // return true if sending to DC OK, false if cmd-stack is full + + virtual bool log_verifyVaultRecordByCrc() const { + return false; + } + // return true if CRC16 is correct, data are 100% OK. Security level 1:65536 + // verification is strongly recommended before further processing + // in case of "false"-result please reload from DC + + + virtual uint16_t log_DC_getNextAccountNumber() const { + return 0; + } + // the current cash box content will be backuped with this number on next cashbox-change + + virtual void log_DC_setNextAccountNumber(uint16_t newAccountingNumber) const { + Q_UNUSED(newAccountingNumber); + } + // the current cash box content will be backuped with this number on next cashbox-change + // use only in case of hardware replacements or errors which derailed the number + + virtual void log_DC_deleteAllVaultrecordsInDc() const {} + // use only in case of hardware replacements or errors which derailed the number + + virtual void log_DC_deleteAllTotalCounters() const {} + // use only in case of hardware replacements or errors which derailed the number + + virtual void dc_setNewCustomerNumber(uint16_t newCustNr) const { + Q_UNUSED(newCustNr); + } + + virtual void dc_setNewMachineNumber(uint16_t newMachNr) const { + Q_UNUSED(newMachNr); + } + + virtual void dc_setNewBorough(uint16_t newBorough) const { + Q_UNUSED(newBorough); + } + + virtual void dc_setNewZone(uint16_t newZone) const { + Q_UNUSED(newZone); + } + + + + // new functions from 8.9.23 + virtual QString mif_getReaderType() const { + return QString(); + } + // return "SL025" if correct reader is connected + + virtual void mif_getCardSize(uint8_t *cardSize, uint8_t *idLeng) const { + Q_UNUSED(cardSize); + Q_UNUSED(idLeng); + } + // cardSize=1k or 4kByte + // idLeng =4Byte or 7 byte + + virtual char mif_getAtbCardData(uint8_t *buf, uint8_t maxBuffSiz) const { + Q_UNUSED(buf); + Q_UNUSED(maxBuffSiz); + return 0; + } + // return complete buffer binary, just for test purpose + + virtual bool mif_isValidAtbCard() const { + return false; + } + + virtual uint32_t mif_getAtbCardCuNu() const { + return 0; + } + + virtual uint8_t mif_getAtbCardTyp() const { + return 0; + } + // return 1=upper door card 1=lower door 3=printer-test 4=coin-test + // 0: not a valid atb2020 card + + virtual QString mif_getAtbCardPerso() const { + return QString(); + } + // e.g. "PNsax001" used for personal number, name shortcode, card number + // free to use, can be set in AtbMcw23.exe tool + + virtual void mif_getAtbCardExpire(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *hour, uint8_t *minute) const { + Q_UNUSED(year); + Q_UNUSED(month); + Q_UNUSED(day); + Q_UNUSED(hour); + Q_UNUSED(minute); + } + + + // ------------------------------------------------------------------------------------ + // Device-Controller-Bootloader convenient version 21.09.2023 + // ------------------------------------------------------------------------------------ + + // use this to jump to BL: + //void hwapi::bl_rebootDC(void) const + + // use this to start BL: + // void hwapi::bl_startBL(void) const + + // call this in order to get response from BL: + //void hwapi::bl_checkBL(void) const + + // evaluate BL response: + //bool hwapi::bl_isUp(void) const + + // 29.9.23: new: "completeStart" function, replaces bl_rebootDC(), bl_startBL() and bl_checkBL() + // result can be verified with bl_isUp() as before + virtual bool bl_completeStart() const { + return false; + } + // must be used in DC's normal operation + + // select binfile-name in GUI + + // send binfile-name to BL-processor: + virtual bool bl_storeFirmware(QString fileName) const { + Q_UNUSED(fileName); + return false; + } + // load binary file 3x and compare + // return true if loaded correctly + // return false: error, could not load correctly + + // request the number of blocks for this file + virtual uint16_t bl_getNrOfFirmwareBlocks() const { + return 0; + } + // size of the loaded bin file in 64byte blocks + // call after bl_storeFirmware() + + // call the next two function's repetitive with "blockNumber"=0,1,2,3....."bl_getNrOfFirmwareBlocks()" + + virtual bool bl_blockAutoLoad(uint16_t blockNumber) const { + Q_UNUSED(blockNumber); + return false; + } + // call in loop from block number 0 up to <= "dcBL_getNrOfBlocks()" + //the last block "bl_getNrOfFirmwareBlocks()" is sent as conclusion command (important!) + // but after every call WAIT (!) for response "bl_blockAutoResponse()" !!!! + // data will be sent to DC, if neccesary addr will be sent additionally + // if neccesary sending will automatically repeat up to 3times + // retval: false if blockNumber>4095, true else + + // check out this response after every block-sending, wait until >0!!! + virtual int8_t bl_blockAutoResponse() const { + return 0; + } + // after every "bl_blockAutoLoad()" call this until response + // retval 0: wait 1: OK, blk was sent 2: OK, transfer complete + // 3: error despite repeating, cancel. probably bin file corrupted + // Max duration: 3x no response from BL = 900ms + + + // finally call: + // void hwapi::bl_stopBL(void) const + // -------------- end of bootloader --------------------------------------------------- + + + + // new from 28.9.23 and earliest from DC version 4.45 + // get all versions of the DC-Jsons + virtual void sys_requestJsonVersions(uint8_t jsonNr) const { + Q_UNUSED(jsonNr); + } + // send one request for every single version + // jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res. + // 5=printer template 1 ..... 36= template 32 + + virtual void sys_getJsonVersions(uint8_t jsonNr, char *versionString) const { + Q_UNUSED(jsonNr); + Q_UNUSED(versionString); + } + // jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res. + // 5=printer template 1 ..... 36= template 32 + // length of buffer is always 16 byte + + // ------------------------------------------------------------------------------------ + // Coin Changer + // ------------------------------------------------------------------------------------ + + // find above (same as for coin checker (EMP)): + + // mdb_switchPower(true); // power on/off + // cash_startPayment(amount_cent); // start polling, enable coins, changer turns to green + + // getInsertedAmount() + // getLastInsertedCoin() + // getAllInsertedCoins(uint16_t *types, uint16_t *values) + // all inserted coins of this past transaction are stored, max 64 + + // cash_stopPayment(); // and wait for further command (changer blinks yellow) + // cash_cancelPayment(); // and return complete paid amount + + // after ticket/goods issue: + // vend_success() + // conclude payment process, keep all coins. Printing was successful + // return change (payment above start-amount), so it works only if + // Payment was started with real vending price (pre selection). + // if payment was started with maximum price (for direct coin insertion) + // then use "changer_returnCoins()", not vend_success() + + // vend_failed() + // conclude payment process and return complete paid amount + + // also valid for changer: + // emp_sendSettings(uint16_t coinAcceptance, uint8_t tokenChannel, uint16_t *coinDenomination ) const =0; + // coinAcceptance: bit0=coin1 (lowest donomination) bit15=coin16 bitH=accept bit L = deny coin (no validation) + // tokenChannel 0...31: if this signal comes from emp then a token was inserted + // coinDenomination = array of 16 coin values (e.g. 5, 10, 20...) + + virtual void changer_returnCoins(uint32_t amountInCent) const { + Q_UNUSED(amountInCent); + } + + virtual void changer_requestChangeResult() const {} + + virtual uint8_t changer_getChangeResult(uint32_t *returnedAmount) const { + Q_UNUSED(returnedAmount); + return 0; + } + // get result of coin dispensing + // receivedData[0]: 0: not yet started 1:amount returned + // 2:only partial return 3: no return possible + // receivedData[2,3,4,5]: returned amount + + virtual void changer_getAllParameters(struct T_changer *mw) const { + Q_UNUSED(mw); + } + // requested automatically with 23, same like EMP + + virtual void changer_requestTubelevel(void) const {} + + virtual void changer_getTubelevel(struct T_chg_Tub *tubLevel) const { + Q_UNUSED(tubLevel); + } + // don't use tubeDispens[], it's not yet correct! + + + + // ------------------------------------------------------------------------------------ + // bank note acceptor + // ------------------------------------------------------------------------------------ + + // already defiened functions applying for BNA as well: + // mdb_switchPower(true); // power on/off + + // bank notes are integrated in the normal transaction process, so use the usual functions to run a vending cycle: + // cash_startPayment(amount_cent); // start polling, enable bills, reader shows green + // getInsertedAmount() + // cash_stopPayment(); + // cash_cancelPayment(); + // vend_success() + // vend_failed() + + virtual void bna_sendSettings(uint16_t notesToAccept, uint16_t parameters, + uint16_t *billDenomination, + uint32_t acceptanceLimit) const { + Q_UNUSED(notesToAccept); + Q_UNUSED(parameters); + Q_UNUSED(billDenomination); + Q_UNUSED(acceptanceLimit); + + } + // notesToAccept: bit0=bill1 (lowest donomination) bitH=accept bit L = deny + // parameters: e.g. use escrow function for some notes + // billDenomination = array of 16 bill values (e.g. 5, 10, 20...) + // these can be set alternatively by Json-File DC2C_cash.json + // acceptanceLimit: device will stop acceptance once this amount is reached and + // optionally keep last bill in escrow until vend_success() + // is called (put bill to stacker) or vend_failed() is called (return bill) + + virtual void bna_setCurrentAcceptance(uint16_t notesToAccept) const { + Q_UNUSED(notesToAccept); + } + // can be used to block notes dynamically, example: from now only 5€ bills are allowed (if only 3€ are to be paid) + // only valid till next start-payment. Cannot add notes which are not activated in Json + + virtual void bna_requestParameters() const {} + // send command to DC in order to get static invariable device parameters like currency + // device must be powered and polled to get these + + virtual bool bna_getAllParameters(struct T_bna *bna) const { + Q_UNUSED(bna); + return false; + } + // get all constant data from reader (e.g. currency) + // and actual Host-Settings (sent with bna_sendSettings() or json) + // retval = true if data are valid + + virtual void bna_requestCurrentNotes() const {} + // send command to DC in order to get transaction data + + virtual uint8_t bna_getCurrentNotes(uint16_t latestBill, uint16_t *currentNotes) const + { + Q_UNUSED(latestBill); + Q_UNUSED(currentNotes); + return 0; + } + // returns number of collected bank notes since start-command (current transaction) + // return value: numbers of bills or 99 in case of error + // latestBill: not used + // in case of error: currentNotes[0,1,2,3] = 1..4 error number(s) + // in normal case: + // currentNotes[0]: last bill in cent (e.g. 1000 = 10€) + // currentNotes[1]: bin 1 = bill is still in escrow else bill is stacked + // note: by now (dec2023) escrow is not used, bills always go to stacker (box) + // currentNotes[2]: total sum of bills in cent, low word (16bit) + // currentNotes[3]: total sum of bills in cent, high word (16bit) + + + virtual void bna_requestStackerLevel() const {} + + virtual uint16_t bna_getStackerLevel(uint32_t *amountInStacker, uint16_t *countOfBills) const { + Q_UNUSED(amountInStacker); + Q_UNUSED(countOfBills); + return 0; + } + // return val: nr of bills in stacker + // countOfBills: array of up to 16 sums, countOfBills[0]=nr of 5€-bills in stacker + // countOfBills[1] for 10€ and so on + + + // download device controller + virtual bool dcDownloadRequest(QString const &fileToDownload) const { + Q_UNUSED(fileToDownload); + return false; + } + virtual bool dcDownloadRequested() const { return false; } + virtual bool dcDownloadResetRequest() const { return false; } + virtual bool dcDownloadRequestAck() const { return false; } + virtual bool dcDownloadRunning() const { return false; } + virtual bool dcDownloadFinished() { return false; } + + virtual bool dcDownloadReportStart() const { return false; } + virtual bool dcDownloadReportRunning() const { return true; } + virtual bool dcDownloadReportFinished() { return true; } + + virtual bool dcDownloadThreadStart() { return false; } + virtual bool dcDownloadThreadRunning() const { return true; } + virtual void dcDownloadThreadFinalize(DownloadThread *) {} + virtual bool dcDownloadThreadFinished() const { return true; } + virtual bool dcDownloadReportThreadStart() { return false; } + virtual bool dcDownloadReportThreadRunning() const { return true; } + virtual void dcDownloadReportThreadFinalize() {} + virtual void dcDownloadReportThreadQuit() {} + virtual bool dcDownloadReportThreadFinished() const { return true; } + + + virtual QString dcDownloadFileName() const { return ""; } + virtual bool dcDownloadSetRequested(bool requested) { + Q_UNUSED(requested); return false; + } + virtual bool dcDownloadSetRunning(bool running) { + Q_UNUSED(running); return false; + } + virtual bool dcDownloadSetFinished(bool finished) { + Q_UNUSED(finished); return false; + } + virtual void dcDownloadSetTotalBlockNumber(uint16_t totalBlockNumber) { + Q_UNUSED(totalBlockNumber); + } + virtual void dcDownloadSetCurrentBlockNumber(uint16_t currentBlockNumber) { + Q_UNUSED(currentBlockNumber); + } + virtual bool dcDownloadGetRequested() const { return false; } + virtual bool dcDownloadGetRunning() const { return false; } + virtual bool dcDownloadGetFinished() const { return false; } + virtual uint16_t dcDownloadGetTotalBlockNumber() const { return 0; } + virtual uint16_t dcDownloadGetCurrentBlockNumber() const { return 0; } + + virtual QObject const *getAPI() { return nullptr; } + + +signals: + /* + NOTE: the difference between a virtual Qt signal and a normal Qt signal: + A Qt virtual signal is a connection that is established using a pointer + or reference and is not connected to an object or data. It is therefore + not bound to a particular object, but to a specific class (object type). + Qt virtual signals are useful because they allow you to create + connections without worrying about whether an object or a specific data + element has been destroyed. + https://www.youtube.com/watch?v=HTH3VFfqsXw + + */ + virtual void hwapi_reportDCDownloadStatus(QString const&) const {} + virtual void hwapi_reportDCDownloadSuccess(QString const&) const {} + virtual void hwapi_reportDCDownloadFailure(QString const&) const {} + + virtual void hwapi_templatePrintFinished_OK(void) const=0; + virtual void hwapi_templatePrintFinished_Err(void) const=0; + + virtual void hwapi_coinCollectionJustStarted(void) const=0; + virtual void hwapi_coinCollectionAborted(void) const=0; + + virtual void hwapi_gotNewCoin(void) const=0; + virtual void hwapi_payStopByMax(void) const=0; + virtual void hwapi_payStopByPushbutton(void) const=0; + + virtual void hwapi_payStopByEscrow(void) const=0; + virtual void hwapi_payStopByError(void) const=0; + virtual void hwapi_payStopByTimeout(void) const=0; + virtual void hwapi_payCancelled(void) const=0; + virtual void hwapi_coinProcessJustStopped(void) const=0; + + virtual void hwapi_doorServiceDoorOpened(void) const=0; + virtual void hwapi_doorVaultDoorOpened(void) const=0; + virtual void hwapi_doorCoinBoxRemoved(void) const=0; + virtual void hwapi_doorCoinBoxInserted(void) const=0; + virtual void hwapi_doorCBinAndAllDoorsClosed(void) const=0; + virtual void hwapi_doorAllDoorsClosed(void) const=0; + + + // NOTE: declaring a "pure virtual" "signal" should be an error and thus not valid. + /* GH Version, bringt Fehler + void hwapi_templatePrintFinished_OK() const; + void hwapi_templatePrintFinished_Err() const; + + void hwapi_coinCollectionJustStarted() const; + void hwapi_coinCollectionAborted() const; + + void hwapi_gotNewCoin() const; + void hwapi_payStopByMax() const; + void hwapi_payStopByPushbutton() const; + + void hwapi_payStopByEscrow() const; + void hwapi_payStopByError() const; + void hwapi_payStopByTimeout() const; + void hwapi_payCancelled() const; + void hwapi_coinProcessJustStopped() const; + + void hwapi_doorServiceDoorOpened() const; + void hwapi_doorVaultDoorOpened() const; + void hwapi_doorCoinBoxRemoved() const; + void hwapi_doorCoinBoxInserted() const; + void hwapi_doorCBinAndAllDoorsClosed() const; + void hwapi_doorAllDoorsClosed() const; + */ +}; + + +// History +// 11.10.2021: V1.0 222 functions +// 23.12.2021: V1.1 added block-parameter to function "read mifare data" +// 30.12.2021: V1.2 added function: mif_clearDataBuffer(), mif_isBlockAvailable(uint8_t blkNr) and mif_getAvailableDataBlocks() +// 1.1.2022: V1.3 Mifare extended. ( background: read 16 x 48byte from card to DC, read 12 x 64byte from DC to CA) +// new: read full card with 768bytes from HWapi without block borders +// added: mif_getNrOfAvailableDataBytes mif_getCardData768byteDec(uint8_t *buf, uint16_t bufferSize) +// mif_getCardDataDec(uint16_t fromAddr, uint16_t toAddr, uint8_t *buf, uint16_t bufferSize) +// mif_getCardDataStr(bool useHexFormat, char seperator) + +// 29.03.2023: V3.1 some extensions for PSA1256_ptu5, +// V3.2 Bootloader improvement +// 12.04.2023: V3.3 new features extended: loading and using Json-files, cash-collection, cash-data-logging +// 14.04.2023: V3.4 new features extended: sys_getDynMachineConditions, sys_getDeviceConditions and +// rtc_getExtendedTime return struct in addition. New function to select and get VaultRecord +// +// 19.04.2023: V3.5 new function: sys_getWakeReason(); +// 17.05.2023: V3.6 new function: cash_isCollectionActive(), cash_isPayProcessActive() +// new signals: hwapi_coinCollectionJustStopped, hwapi_coinCollectionJustStarted +// getAllInsertedCoins() fixed, also in datif and storeINdata + +// 15.06.2023 V4.2 bring into same order as hwapi in order to set the THIS_IS_CA_MASTER correct +// 19.06.2023 V4.3 added some qCriticals to see emits + +// 01.08.2023 V4.4 some new values at the end of struct T_vaultRecord +// two more values in struct T_devices +// 7 new functions at the end of the file + + +//#define HWINF_iid "Atb.Psa2020.software.HWapi/3.1" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.1" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.3" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.4" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.5" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.6" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.0" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.1" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.2" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.3" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.4" + // 8.9.2023 two new functions (end of file) for mifare test +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.5" + // 18.9.2023 major improvements for DC data exchange + // verification of door and cash box signals + // intensive verification of Json-Programming Master-Slave (PTU to DC), 100% ok +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.6" + // 20.9.2023: speeding up door and cash box signals +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.7" + // 26.09.2023: added improved DC-bootloader files +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.8" +// 28.09.2023: added version request of DC-Json-Files +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/5.0" + // 10.10.2023: added coin changer + +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/5.1" +// 20.10.2023: added bill validator + +// hier dazwischen: Umstellung ALLER Prototypen +// hier von =0 auf {} durch Gerhard + +#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/5.2" + // 7.11.2023: bna functions activated + + +Q_DECLARE_INTERFACE(hwinf, HWINF_iid) + + +#endif + + + diff --git a/process/command.cpp b/DownloadDCJsonFiles/process/command.cpp similarity index 100% rename from process/command.cpp rename to DownloadDCJsonFiles/process/command.cpp diff --git a/process/command.h b/DownloadDCJsonFiles/process/command.h similarity index 99% rename from process/command.h rename to DownloadDCJsonFiles/process/command.h index bc796ee..372e293 100644 --- a/process/command.h +++ b/DownloadDCJsonFiles/process/command.h @@ -1,6 +1,5 @@ #ifndef COMMAND_H_INCLUDED #define COMMAND_H_INCLUDED -#endif // COMMAND_H_INCLUDED #include #include @@ -33,3 +32,5 @@ private slots: void readyReadStandardError(); void finished(int exitCode, QProcess::ExitStatus exitStatus); }; + +#endif // COMMAND_H_INCLUDED diff --git a/DownloadDCJsonFiles/update.cpp b/DownloadDCJsonFiles/update.cpp new file mode 100644 index 0000000..cf82f30 --- /dev/null +++ b/DownloadDCJsonFiles/update.cpp @@ -0,0 +1,434 @@ +#include "update.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX) +#include "unistd.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define UPDATE_OPKG (1) +#define UPDATE_DC (0) + +static const QMap baudrateMap = { + {"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3}, + {"57600" , 4}, {"115200" , 5} +}; + +QPluginLoader Update::pluginLoader; + +hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { + hwinf *hw = nullptr; + if (plugInDir.exists()) { + QString pluginLibName(fname); + pluginLibName = plugInDir.absoluteFilePath(pluginLibName); + QFileInfo info(pluginLibName); + if (info.exists()) { + pluginLibName = plugInDir.absoluteFilePath(pluginLibName); + pluginLoader.setFileName(pluginLibName); + // static QPluginLoader pluginLoader(pluginLibName); + if (!pluginLoader.load()) { + qCritical() << "in directory" << plugInDir.absolutePath(); + qCritical() << "cannot load plugin" << pluginLoader.fileName(); + qCritical() << pluginLoader.errorString(); + return nullptr; + } + + qCritical() << "loadDCPlugin() plugin directory:" << plugInDir.absolutePath(); + qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName(); + + if (!pluginLoader.isLoaded()) { + qCritical() << pluginLoader.errorString(); + return nullptr; + } + QObject *plugin = pluginLoader.instance(); + if (!plugin) { + qCritical() << "cannot start instance"; + return nullptr; + } + if (! (hw = qobject_cast(plugin))) { + qCritical() << "cannot cast plugin" << plugin << "to hwinf"; + return nullptr; + } + } else { + qCritical() << pluginLibName << "does not exist"; + return nullptr; + } + } else { + qCritical() << "plugins directory" << plugInDir.absolutePath() + << "does not exist"; + return nullptr; + } + return hw; +} + +bool Update::unloadDCPlugin() { + if (pluginLoader.unload()) { + qCritical() << "unloaded plugin" << pluginLoader.fileName(); + // Note: will re-instantiate the library ! + // QObject *rootObject = pluginLoader.instance(); + // if (rootObject) { + // qCritical() << "reloaded plugin: root object again available"; + // return false; + // } + // qCritical()unloaded plugin: root object gone"; + return true; + } + return false; +} + +class hwapi; +Update::Update(QString customerRepository, + QString customerNrStr, + QString branchName, + QString plugInDir, + QString pluginName, + QString workingDir, + bool dryRun, + QObject *parent, + char const *serialInterface, + char const *baudrate) + : QObject(parent) + , m_hw(loadDCPlugin(QDir(plugInDir), pluginName)) + , m_serialInterface(serialInterface) + , m_baudrate(baudrate) + , m_customerRepository(customerRepository) + , m_customerNrStr(customerNrStr) + , m_branchName(branchName) + , m_pluginName(pluginName) + , m_workingDir(workingDir) + , m_dryRun(dryRun) + , m_sys_areDCdataValid(false) { + + if (!m_hw) { + qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin loaded ???"; + } else { + int tries = 20; + while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { + // must deliver 'true', only then are all data from hwapi valid + if (--tries < 0) { + qCritical() << "ERROR!!! DC DATA NOT VALID -> CA-MASTER-PLUGIN NOT CONNECTED"; + break; + } + m_hw->dc_autoRequest(true); + QThread::msleep(500); + } + + qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_sys_areDCDataValid ..." + << m_sys_areDCdataValid; + } +} + +Update::~Update() { + unloadDCPlugin(); +} + +bool Update::doUpdate(QStringList const &filesToWorkOn, bool usbStickDetected) { + + if (!m_hw) { + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! m_hw == nullptr"; + return false; + } + + int tries = 20; + while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { + // must deliver 'true', only then are all data from hwapi valid + if (--tries < 0) { + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED"; + return false; + } + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")"; + m_hw->dc_autoRequest(true); + QThread::msleep(500); + } + + bool res = false; + + QList::const_iterator it; + for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) { + QString const &fToWorkOn = usbStickDetected ? QDir::cleanPath(it->trimmed()) + : QDir::cleanPath(m_customerRepository + QDir::separator() + it->trimmed()); + if (fToWorkOn.contains("DC2C_print", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; + int i = fToWorkOn.indexOf("DC2C_print", Qt::CaseInsensitive); + int const templateIdx = fToWorkOn.mid(i).midRef(10, 2).toInt(); + if ((templateIdx < 1) || (templateIdx > 32)) { + qCritical() << "WRONG TEMPLATE INDEX" << templateIdx; + res = false; + } else { + if ((res = updatePrinterTemplate(templateIdx, fToWorkOn))) { + qCritical() << + QString("DOWNLOADED PRINTER TEMPLATE %1 WITH INDEX=%2") + .arg(fToWorkOn) + .arg(templateIdx); + } + } + } else if (fToWorkOn.contains("DC2C_cash", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + if ((res = updateCashConf(fToWorkOn))) { + qCritical() << QString("DOWNLOADED CASH TEMPLATE %1").arg(fToWorkOn); + } + } else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + if ((res = updateConfig(fToWorkOn))) { + qCritical() << QString("DOWNLOADED CONFIG TEMPLATE %1").arg(fToWorkOn); + } + } else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + if ((res = updateDeviceConf(fToWorkOn))) { + qCritical() << QString("DOWNLOADED DEVICE TEMPLATE %1").arg(fToWorkOn); + } + } else { + qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn; + res = false; + } + } + + return res; +} + +bool Update::checkJsonVersions(QStringList const& jsonFileNames) { + if (!m_hw) { + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! m_hw == nullptr"; + return false; + } + + int tries = 20; + while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { + // must deliver 'true', only then are all data from hwapi valid + if (--tries < 0) { + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED"; + return false; + } + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")"; + m_hw->dc_autoRequest(true); + QThread::msleep(500); + } + + for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) { + + uint8_t jsonNr = 0; + + QString const &fName = jsonFileNames[i]; + + // send one request for every single version + // jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res. + // 6=printer template 1 ..... 36= template 32 + + if (fName.endsWith("conf.json")) { + jsonNr = 1; + } else + if (fName.endsWith("device.json")) { + jsonNr = 2; + } else + if (fName.endsWith("cash.json")) { + jsonNr = 3; + } else { + QRegularExpressionMatch match; + static const QRegularExpression re("^(.*print)([0-3][0-9])\\.json\\s*$"); + int idx = fName.indexOf(re, 0, &match); + if (idx != -1) { + QString captured = match.captured(match.lastCapturedIndex()); + bool ok = false; + int n = captured.toInt(&ok); + if (ok) { + // note: use 5 (instead of 4 -> index has been shifted) + jsonNr = n + 5; + } + } + } + + if (jsonNr != 0) { + // send one request for every single version + // jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res. + // 5=printer template 1 ..... 36= template 32 + + m_hw->sys_requestJsonVersions(jsonNr); + QThread::msleep(500); + + char buf[64]; + memset(buf, 0x00, sizeof(buf)); + m_hw->sys_getJsonVersions(jsonNr, buf); + buf[16] = '\0'; // the DC only handles 16 bytes + + static const QByteArray cb(16, (char)0xff); + + QString const installedVersion(QString::fromStdString(buf)); + QString const fileVersion = getFileVersion(jsonFileNames[i]); + + QFileInfo fi(jsonFileNames[i]); + + qCritical() << endl; + qCritical() << " json request nr:" << jsonNr; + + if (installedVersion == fileVersion) { + qCritical() << " json file:" << fi.fileName(); + qCritical() << " installed version in DC:" << installedVersion; + } else + if (cb == QByteArray(buf) && fileVersion == "") { + qCritical() << "unknown json file (repo and DC):" << fi.fileName(); + } else { + qCritical() << " json file:" << fi.fileName(); + qCritical() << " installed version in DC:" << installedVersion; + qCritical() << " file version in repository:" << fileVersion; + } + + } else { + qCritical() << "CANNOT FIND JSON-NR FOR" << fName; + } + } + + return false; +} + + +QString Update::getFileVersion(QString const& jsonFileName) { + // "version":"15.10.2023 14:55 02.00.06", + static const QRegularExpression re("^.*(\\\"[Vv]ersion\\\":)([\\s\\\"]{0,})([^,\\\"]{0,}).*$"); + + QString fileVersion(""); + QFile inputFile(QDir::cleanPath(m_customerRepository + QDir::separator() + jsonFileName)); + + if (inputFile.exists()) { + if (inputFile.open(QIODevice::ReadOnly)) { + QTextStream in(&inputFile); + while (!in.atEnd()) { + QString line = in.readLine(); + + QRegularExpressionMatch match; + int idx = line.indexOf(re, 0, &match); + if (idx != -1) { + int const lastCaptured = match.lastCapturedIndex(); + // the dc only sends 16 Byte + fileVersion = match.captured(lastCaptured); + fileVersion.truncate(16); + break; + } + } + inputFile.close(); + } + } else { + // qCritical() << "ERROR" << inputFile.fileName() << "does not exist"; + } + + return fileVersion; +} + +bool Update::downloadJson(enum FileTypeJson type, + int templateIdx, + QString jsFileToSendToDC) const { + + m_hw->dc_autoRequest(true); // downloading Json needs the AutoEmission flag + qDebug() << "SET AUTO-REQUEST=TRUE"; + QThread::sleep(1); // make sure the auto-request flag is acknowledged + + QStringList lst; + bool ready = false; + int nTry = 25; + while ((ready = m_hw->sys_ready4sending()) == false) { + QThread::msleep(200); + if (--nTry <= 0) { + qCritical() << "SYS NOT READY FOR SENDING AFTER 5 SECONDS"; + break; + } + } + + bool ret = false; + QString msg; + lst.clear(); + if (ready) { + QFile file(jsFileToSendToDC); + QFileInfo fi(jsFileToSendToDC); // max. size of template file is 800 bytes + if (file.exists()) { + if (file.open(QIODevice::ReadOnly)) { + if (fi.size() > 0 && fi.size() <= 800) { + QByteArray ba = file.readAll(); + // kindOfFile: 1=config, 2=device, 3=cash, 4=serial, 5=time, 6=printer + // nrOfTemplate=1...32 if kindOfFile==6 + // content = content of the Json file, max 800byte ascii signs + if (m_hw->sys_sendJsonFileToDc((uint8_t)(type), + templateIdx, + (uint8_t *)ba.data())) { + + /* + * Note: the machine id is contained in DC2C_conf.json. + * The idea was to use this to check if the download of + * the json-file was correct. It did not work, as the + * update of the PSA (to reflect a change in the + * machine id) did not happen immediately. + * + m_hw->dc_autoRequest(true); + QThread::msleep(500); + + // testing + m_hw->request_ReadbackMachineID(); + QThread::msleep(500); + + uint8_t data[64]; + memset(data, 0x00, sizeof(data)); + uint8_t length = 0; + + m_hw->readback_machineIDdata(&length, data); + + QThread::msleep(500); + + QByteArray ba((const char*)data, length); + + qCritical() << length << "MACHINE ID =" << ba.toHex(':'); + */ + + ret = true; + } else { + qCritical() << QString("ERROR SEND JSON-FILE %1 TO DC").arg(file.fileName()); + } + } else { + qCritical() << QString("SIZE OF %1 TOO BIG (%2 BYTES)").arg(jsFileToSendToDC).arg(fi.size()); + } + } else { + qCritical() << QString("CAN NOT OPEN ") + jsFileToSendToDC + " FOR READING"; + } + } else { + qCritical() << (QString(jsFileToSendToDC) + " DOES NOT EXIST"); + } + } + + return ret; +} + +bool Update::updatePrinterTemplate(int templateIdx, QString jsFile) const { + return downloadJson(FileTypeJson::PRINTER, templateIdx, jsFile); +} + +bool Update::updateConfig(QString jsFile) { + return downloadJson(FileTypeJson::CONFIG, 0, jsFile); +} + +bool Update::updateCashConf(QString jsFile) { + return downloadJson(FileTypeJson::CASH, 0, jsFile); +} + +bool Update::updateDeviceConf(QString jsFile) { + return downloadJson(FileTypeJson::DEVICE, 0, jsFile); +} diff --git a/DownloadDCJsonFiles/update.h b/DownloadDCJsonFiles/update.h new file mode 100644 index 0000000..daa3037 --- /dev/null +++ b/DownloadDCJsonFiles/update.h @@ -0,0 +1,148 @@ +#ifndef UPDATE_H_INCLUDED +#define UPDATE_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#ifdef PTU5 +#define SERIAL_PORT "ttymxc2" +#else +#define SERIAL_PORT "ttyUSB0" +#endif + +class Update : public QObject { + Q_OBJECT + + hwinf *m_hw = nullptr; + char const *m_serialInterface; + char const *m_baudrate; + QString m_customerRepository; + QString m_customerNrStr; + QString m_branchName; + QString m_pluginName; + QString m_workingDir; + bool m_maintenanceMode; + bool m_dryRun; + bool m_sys_areDCdataValid; + + static QPluginLoader pluginLoader; + +public: + enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; + enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6}; + + static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn); + static bool unloadDCPlugin(); + static QStringList split(QString line, QChar sep = ','); + + + explicit Update(QString customerRepository, + QString customerNrStr, + QString branchName, + QString plugInDir, + QString pluginName, + QString workingDir, + bool dryRun = false, + QObject *parent = nullptr, + char const *serialInterface = SERIAL_PORT, + char const *baudrate = "115200"); + + virtual ~Update() override; + + bool doUpdate(QStringList const &jsonFilesToDownload, bool usbStickDetected = false); + + bool updatePrinterTemplate(int templateIdx, QString fname) const; + bool updateConfig(QString jsFileToSendToDC); + bool updateCashConf(QString jsFileToSendToDC); + bool updateDeviceConf(QString jsFileToSendToDC); + + bool downloadJson(enum FileTypeJson type, int templateIdx, + QString jsFileToSendToDC) const; + + + QString getFileVersion(QString const& jsonFileName); + bool checkJsonVersions(QStringList const& jsonFileNames = + QStringList( + QList( + std::initializer_list{ + QString("etc/psa_config/DC2C_conf.json"), + QString("etc/psa_config/DC2C_cash.json"), + QString("etc/psa_config/DC2C_device.json"), + QString("etc/psa_config/DC2C_print01.json"), + QString("etc/psa_config/DC2C_print02.json"), + QString("etc/psa_config/DC2C_print03.json"), + QString("etc/psa_config/DC2C_print04.json"), + QString("etc/psa_config/DC2C_print05.json"), + QString("etc/psa_config/DC2C_print06.json"), + QString("etc/psa_config/DC2C_print07.json"), + QString("etc/psa_config/DC2C_print08.json"), + QString("etc/psa_config/DC2C_print09.json"), + QString("etc/psa_config/DC2C_print10.json"), + QString("etc/psa_config/DC2C_print11.json"), + QString("etc/psa_config/DC2C_print12.json"), + QString("etc/psa_config/DC2C_print13.json"), + QString("etc/psa_config/DC2C_print14.json"), + QString("etc/psa_config/DC2C_print15.json"), + QString("etc/psa_config/DC2C_print16.json"), + QString("etc/psa_config/DC2C_print17.json"), + QString("etc/psa_config/DC2C_print18.json"), + QString("etc/psa_config/DC2C_print19.json"), + QString("etc/psa_config/DC2C_print20.json"), + QString("etc/psa_config/DC2C_print21.json"), + QString("etc/psa_config/DC2C_print22.json"), + QString("etc/psa_config/DC2C_print23.json"), + QString("etc/psa_config/DC2C_print24.json"), + QString("etc/psa_config/DC2C_print25.json"), + QString("etc/psa_config/DC2C_print26.json"), + QString("etc/psa_config/DC2C_print27.json"), + QString("etc/psa_config/DC2C_print28.json"), + QString("etc/psa_config/DC2C_print29.json"), + QString("etc/psa_config/DC2C_print30.json"), + QString("etc/psa_config/DC2C_print31.json"), + QString("etc/psa_config/DC2C_print32.json")}))); +/* + bool checkDownloadedJsonVersions(QStringList const& jsonFileNames); + + hwinf *hw() { return m_hw; } + hwinf const *hw() const { return m_hw; } + + //QString customerId() { return m_customerId; } + //QString const customerId() const { return m_customerId; } + + QString branchName() { return m_branchName; } + QString const branchName() const { return m_branchName; } + + //QString repositoryPath() { return m_repositoryPath; } + //QString const repositoryPath() const { return m_repositoryPath; } + +private: + static QString jsonType(enum FileTypeJson type); + bool openSerial(int br, QString baudrate, QString comPort) const; + void closeSerial() const; + bool isSerialOpen() const; + bool resetDeviceController() const; + QByteArray loadBinaryDCFile(QString filename) const; + bool downloadBinaryToDC(QString const &bFile) const; + bool updateBinary(QString const &fileToSendToDC); + QStringList getDcSoftAndHardWareVersion(); + QString getFileVersion(QString const& jsonFile); + +private slots: + void readyReadStandardOutput(); + void readyReadStandardError(); + void finished(int exitCode, QProcess::ExitStatus exitStatus); + void onReportDCDownloadStatus(QString const &status); + void onReportDCDownloadSuccess(QString const &msg); + void onReportDCDownloadFailure(QString const &errorMsg); +*/ +}; +#endif // UPDATE_H_INCLUDED diff --git a/DC2C_print01.json b/UpdatePTUDevCtrl/DC2C_print01.json similarity index 100% rename from DC2C_print01.json rename to UpdatePTUDevCtrl/DC2C_print01.json diff --git a/DC2C_print02.json b/UpdatePTUDevCtrl/DC2C_print02.json similarity index 100% rename from DC2C_print02.json rename to UpdatePTUDevCtrl/DC2C_print02.json diff --git a/DC2C_print03.json b/UpdatePTUDevCtrl/DC2C_print03.json similarity index 100% rename from DC2C_print03.json rename to UpdatePTUDevCtrl/DC2C_print03.json diff --git a/OnDemandUpdatePTU.pro b/UpdatePTUDevCtrl/OnDemandUpdatePTU.pro similarity index 92% rename from OnDemandUpdatePTU.pro rename to UpdatePTUDevCtrl/OnDemandUpdatePTU.pro index 121914f..5bd21cf 100644 --- a/OnDemandUpdatePTU.pro +++ b/UpdatePTUDevCtrl/OnDemandUpdatePTU.pro @@ -92,8 +92,20 @@ DEFINES += QT_DEPRECATED_WARNINGS # Changes: # (1) the ini-File now uses the libCAmaster.so. # 1.3._25_: Again special version for szeged, using interface.h, version 4.4. -VERSION="1.3._25_" - +# 1.4.0 : Start with version at 1.4.0 (mainly to see a difference with Szeged) +# Set hash-value in EVENT-objects. Set location (project), version +# and info in send-last-version. +# If the customer repository does not exist, then do not check the +# ISMAS trigger, but proceed with the update procedure. Otherwise, +# check the ISMAS update-trigger as first step. +# If the current time is between 0.00 - 4.00 o'clock, then a wrong +# trigger-value will result in an UPDATE_STEP_NOT_NECESSARY. +# Move final processing to subclass UpdateProcessRunning. +# Disable EXIT-button for the whole update-process, except for the +# checking of the ISMAS-trigger-button (aka WAIT-button). +# 1.4.1 : Sync files in the customer repository (under ./etc) as the very +# first step +VERSION="1.4.1" # PLANNED TODOS: # 1: Das Repository wird repariert bwz. neu geklont. Unabhaengig vom WAIT. # 2: Wenn der WAIT-Button aktiv ist, dann wird ein Repository repariert (neu @@ -107,7 +119,6 @@ VERSION="1.3._25_" # ISMAS eine entsprechende Meldung anzeigen als Teil von SEND-LAST-VERSION. # Wenn der WAIT-button aktiv ist, dann werden zumindest die opkg-commands # ausgefuehrt. -# 4: rsync: immer alle Dateien soiegeln (bis auf opkg-commands) # 5: Falls das Tool mal abstuerzt, dann einen Signal-Handler (fuer TERM) # installieren, sodass zumnidest SEND-LAST-VERSION mit rausgeht. # 6: rsync: explizites Binary, nicht das in busybox enthaltene. @@ -123,9 +134,6 @@ VERSION="1.3._25_" # Stellung des WAIT-Button. Grund: es koennte sein, dass andernfalls ein # PSA weit hiter anderen steht, und dann ploetzlich einmal alle vorher- # gehenden Aenderungen anzieht, die gar nicht fuer ihn gemeint waren. -# 10: Bei einer Neuinstallation (Neuhauser) immer JSON files runterladen, -# Tariff-Files syncen (d.h. nur wenn noch kein Repo vorhanden ist), und -# zwar auch ohne WAIT-Button. # 11: Das Edit-Fenster teilen um die Anzeige zu verbessern. # 12: Bei einem Update muss immer ersichtlich sein, warum es ueberhaupt # angestossen wurde. Steht kein "WAIT" im ISMAS-Trigger, dann kann man @@ -179,7 +187,7 @@ DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\" # keep comments, as /* fall through */ QMAKE_CXXFLAGS += -C QMAKE_CXXFLAGS += -g -QMAKE_CXXFLAGS += -Wno-deprecated-copy +QMAKE_CXXFLAGS += -Wno-deprecated-copy -O contains( CONFIG, PTU5 ) { greaterThan(QT_MAJOR_VERSION, 4): QT += serialport @@ -239,6 +247,7 @@ HEADERS += \ process/command.h \ message_handler.h \ worker.h \ + interfaces.h \ commandline_parser.h \ plugins/interfaces.h diff --git a/UpdatePTUDevCtrl/UpdatePTUDevCtrl.pro b/UpdatePTUDevCtrl/UpdatePTUDevCtrl.pro new file mode 100644 index 0000000..f8a620c --- /dev/null +++ b/UpdatePTUDevCtrl/UpdatePTUDevCtrl.pro @@ -0,0 +1,318 @@ +QT += core gui +QT += widgets serialport network + +include(../common.pri) + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = ATBUpdateTool + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +# 1.3.6 : Do not update device-controller/json files, but have the library +# (in a later step) do that. +# Fixed sending messages to ISMAS. +# Always execute contents of opkg_commands-file (even if there are no +# changes). +# 1.3.7 : Wait forever for git-commands to finish in QProcess executing such +# a command. +# 1.3.8 : Remove accessing opkg_commands under file-system-path /etc/psa_update. +# Activate download of json-configuration files. +# 1.3.9 : Fix sendLastVersion: use configured branch and not master branch in +# git show origin/master -s --format="c=%h m=%s d=%cI" ==> +# git show origin/zg1/zone1 -s --format="c=%h m=%s d=%cI" +# Use dynamic values for os-release and apism-version when sending +# last version info. +# 1.3.10: Fix premature killing opkg-commands: detected timeout of 100s was +# too small when updating apism. +# Fix display of UPDATE_SUCCESS when opkg_command fails. Detected when +# updating apsim failed. +# 1.3.11: Integrate version of ATBUpdateTool in SendLastVersion-ISMAS-message. +# 1.3.12: Add command parameters for output of yocto-infos about ATBUpdateTool. +# Use 'git pull' instead of 'git fetch'. +# Use 'git clone --filter=blob:none' instead of 'git clone' to speed +# up cloning of customer repository. +# 1.3.13: Fix: if the customer repository is corrupted, remove it and re-clone +# the repository (without checking the ISMAS-trigger (WAIT-)button. +# 1.3.14: Add additional check for sanity of customer repository using +# "git fsck". +# Stream-lined code of update process: massive refactoring. +# Added functionality: If WAIT button is not active, then an existing +# customer repository will be repaired, or a not existing repository +# will be cloned. The process stops then. +# However, if the WAIT button is active, the at least the commands in +# opkg_commands will be executed. Changed files in the customer +# repository will be worked on: tariff-files will be synced with the +# local filesystem, json-files will be downloaded to firmware. +# The device-controller firmware will be handled in a later version. +# 1.3.15: Bug fixes found during testing. +# Do not disable Exit-button during update-process. +# Removed worker-thread with an own event-loop: only the GUI thread +# has an event loop. Tested JSON-downloads several times successfully +# (using the slave lib where the CA helper tool was active as master). +# Turned previous worker-object into its own thread, but without any +# own event-loop (so it cannot block anything inside the CA-plugin). +# 1.3.16: Bug fixes found during testing. +# 1.3.17: Add ATBUpdateTool.ini and custom command line parser. Settings +# given in ATBUpdateTool.ini can be overwritten on the command-line. +# 1.3.18: Bug fixes found during testing. +# 1.3.19: Bug fixes found during testing. +# 1.3.20: Bug fixes found during testing. +# 1.3.21: Bug fixes found during testing: +# Fix directory of ATBUpdateTool.ini to be the working directory of +# the application rather than just ".". +# Check existance of etc-directory inside customer repository. +# Check for valid ISMAS trigger (button) 15x (=90s). +# NOTE: if the customer repository is cloned (or repaired and cloned +# again), and if the settings always-download-config=true and +# always-download-dc=true in the ATBUpdateTool.ini file, the download +# the printer-json files and the device controller file, even without +# an activated ISMAS trigger (button). The tariff-files are rsynced to +# the local filesystem for such clone. +# Set new filename for device controller: dc2c.bin. +# 1.3.22: Bug fixes found during testing: +# Fix the path-names of the json-files and the device-controller. +# Set automatic download of json-file in ATBUpdateTool.ini file for +# a fresh clone of the repository. +# 1.3.23: Added a 'break' to prevent a possible endless loop when checking if +# the device is alive. +# +# NOTE: The versioning info has to be shifted up by one version, i.e. what +# happened for 1.3.23 was actually done in 1.3.24. +# 1.3.24 +# +# 1.3._24_: Special version for szeged using a old dc-controller (4.42): +# Changes: +# (1) the ini-File now uses the libCAmaster.so. +# 1.3._25_: Again special version for szeged, using interface.h, version 4.4. +# 1.4.0 : Start with version at 1.4.0 (mainly to see a difference with Szeged) +# Set hash-value in EVENT-objects. Set location (project), version +# and info in send-last-version. +# If the customer repository does not exist, then do not check the +# ISMAS trigger, but proceed with the update procedure. Otherwise, +# check the ISMAS update-trigger as first step. +# If the current time is between 0.00 - 4.00 o'clock, then a wrong +# trigger-value will result in an UPDATE_STEP_NOT_NECESSARY. +# Move final processing to subclass UpdateProcessRunning. +# Disable EXIT-button for the whole update-process, except for the +# checking of the ISMAS-trigger-button (aka WAIT-button). +# 1.4.1 : Sync files in the customer repository (under ./etc) as the very +# first step +# 1.4.2 : Do not check if /etc/psa_tariff and /etc/psa_tariff are +# the same after an rsync. They might be noy after a change of the +# customer-number. +# 1.4.3 : Use global directory for device-controller interfaces.h-file. +# 1.4.4 : Add additional debug messages when downloading json-files. +# Move rsyncing of the customer-repository after the actual fetching +# of the repository. Otherwise, the update of, for instance +# tariff-files, would always be a step behind. +# 1.4.5 : In case a new branch has been created in a remote +# customer-repository (e.g. origin/zg1/zone101), then fetch/pull +# this branch before switching to this now locally existen branch. +# : Improve output of GUI/Console and messages sent to ISMAS. +# : Fix: do not send the json-files to dc in any case. Bug introduced +# when pulling new branch, but branch already existed locally. +# 1.4.6 : No exit()-call if loading CA-plugin fails. +# Check m_hw (pointer to CA-(Slave)Plugin) before its use. +# Only exit() if firmware is configured to be possibly updated, but +# loading the CA-plugin failed. +# 1.4.7 : Read Json-file versions of Json-files actually loaded into DC. +# 1.4.8 : Improve reading Json-file versions of Json-files actually loaded +# in DC. If available, show version info instead of blob-info of +# Json file. +# 1.4.9 : Improve ISMAS output: add messages for JSON downloads. +# Do not send CHECK_ISMAS_TRIGGER_SUCCESS to ISMAS for an automatic +# update. +# In case there is an automatic update (00:00-04:00) use M0100 instead +# of U0002. +# 1.4.10 : Fix: for the time being, never set the autoRequest-flag to false. +# 1.4.11 : Fix: remove hard-coded path to git-repos of gitea in mimbach. Read +# from ATBUpdateTool.ini the url for the gitrepositories. +# 1.5.0 : Use ptu-config.atb-comm.de (given in ini-file) as new server for +# customer repositories. +# Fix: allow empty lines in opkg_commands. +# 1.5.1 : Fix: do not use cleanPath() on a url-address. +# 1.5.2 : Remove .ipk and .gz files in /var/cache/opkg. +# 1.5.3 : Build customer_(id) name without right justification. +# 1.5.4 : Try to run opkg-commands even under some error conditions (failure +# of customerEnvironment(), filesToUpdate() or +# syncCustomerRepositoryAndFS(). +# 1.5.5 : Call into binary ptuPackageVersion to get installed package +# versions. +# 1.5.6 : Show additional update progress info in status bar. +# 1.5.7 : Add support for dynamic portrait / landscape. +# 1.5.8 : Use EVENT_ID= for CMD_EVENT. +# 1.5.9 : Removed restart of Apism if ISMAS unreachable. +VERSION="1.5.9" +# PLANNED TODOS: +# 1: Das Repository wird repariert bwz. neu geklont. Unabhaengig vom WAIT. +# 2: Wenn der WAIT-Button aktiv ist, dann wird ein Repository repariert (neu +# geklont), aber zusaetzlich werden alle verfuegbaren Dateien als neu +# angesehen und die entsprechenden Aktionen durchgefuehrt: tariff-files +# spiegeln, json-files laden und dc laden. Also VORSICHT: das repository +# muss in diesem fall wirklich in ordnung sein. +# 3: Wurde keine Datei geaendert, kein initiales Clone und der WAIT-button +# nicht aktiv, so (passiert natuerlich nichts) kann man davon ausgehen, +# dass es sich um ein automatisches Update handelt. Dann koennte man im +# ISMAS eine entsprechende Meldung anzeigen als Teil von SEND-LAST-VERSION. +# Wenn der WAIT-button aktiv ist, dann werden zumindest die opkg-commands +# ausgefuehrt. +# 5: Falls das Tool mal abstuerzt, dann einen Signal-Handler (fuer TERM) +# installieren, sodass zumnidest SEND-LAST-VERSION mit rausgeht. +# 6: rsync: explizites Binary, nicht das in busybox enthaltene. +# 7: Versionen der Json-Files lassen sich auslesen. +# Problem: Einstellungen in den Json-Files lassen sich auch mittels +# Funktionen in der CD-Library ueberschreiben. Damit ist dann wieder nicht +# mehr so klar, was jetzt eigentlich aktiv ist. +# 8: m_alwaysDownloadConfig und m_alwaysDownloadDC: vorbereitet: man koennte +# es so arrangieren, dass der DC plus die Json-files im Repository immer +# runtergeladen werden, obwohl sich im Repository gar nicts veraendert +# hat. Eeventuell nuetzlich beim initialen Setuo eines PSA. +# 9: Das Kunden-Repository sollte immer gezogen werden, unabhaengig von der +# Stellung des WAIT-Button. Grund: es koennte sein, dass andernfalls ein +# PSA weit hiter anderen steht, und dann ploetzlich einmal alle vorher- +# gehenden Aenderungen anzieht, die gar nicht fuer ihn gemeint waren. +# 11: Das Edit-Fenster teilen um die Anzeige zu verbessern. +# 12: Bei einem Update muss immer ersichtlich sein, warum es ueberhaupt +# angestossen wurde. Steht kein "WAIT" im ISMAS-Trigger, dann kann man +# davon ausgehen, dass es sich um ein automatisches Update handelt. +# In jedem Fall wird bei einem automatischen Update, bei dem der WAIT- +# Button nicht gesetzt war, ein "OK" gesendet, falls sonst nichts weiter +# zu tun ist. Beachte aber: wir haben auch noch den Fall, dass eine SD- +# Karte gesteckt wird. In diesem Fall wird ein komplettes Update gefahren, +# und zwar explizit auch ohne WAIT-Button. +# Am Ende eines Updates steht im ISMAS entweder ein "OK" oder ein "FAIL". +# 13: SendLastVersion: fuer jedes erfolgreich installierte Paket eine +# Send-Last-Version-Nachricht an ISMAS. Dadurch entsteht im ISMAS eine +# History (Christian darueber informieren). +# 14: Installiert werden nur Dateien, die neu sind oder geaendert wurden. +# Nicht etwas Dateien, die geloescht wurden: sicherstellen, dass man hier +# immer direkt im repository arbeitet, nicht auf dem Filesystem. +# Ferner: der DeviceController heisst dc2c.bin, auch fuer die Jsons +# sind Dtandard-Namen vergeben. Alternativ: alle vorhandenen Jsons +# werden runtergeladen: Thomas ist eh fuer deren Inhalte verantworlich. +# WICHTIG: immer ueberpruefen, ob die Dateien im Customer-Repository +# wirklich die richtigen Dateien sind. +# 15: Der WAIT-Button laesst sich auf WAIT zuruecksetzen (etwa wenn git +# selber Probleme hatte). +# 16: Der Download-Thread sollte sowohl die auto-Variable auf false setzen +# als auch den cycle-Timer stoppen, damit sichergestellt ist, dass der +# Download des DC nicht gestoert wird. + + +win32 { + BUILD_DATE=$$system("date /t") + BUILD_TIME=$$system("time /t") +} else { + BUILD_DATE=$$system("date +%d-%m-%y") + BUILD_TIME=$$system("date +%H:%M:%S") +} + +GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1") + +EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}" + +INCLUDEPATH += plugins + +CONFIG += c++17 +# CONFIG -= app_bundle + +DEFINES+=APP_VERSION=\\\"$$VERSION\\\" +DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\" +DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\" +DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\" + +# keep comments, as /* fall through */ +QMAKE_CXXFLAGS += -C +QMAKE_CXXFLAGS += -g +QMAKE_CXXFLAGS += -Wno-deprecated-copy -O + +contains( CONFIG, PTU5 ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + CONFIG += link_pkgconfig + lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport + QMAKE_CXXFLAGS += -O2 -std=c++17 # for GCC >= 4.7 + + PTU5BASEPATH = /opt/devel/ptu5 + + INCLUDEPATH += $$PTU5BASEPATH/qt/libs/devicecontroller/include + LIBS += -L$$PTU5BASEPATH/qt/libs/devicecontroller/library + + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + ARCH = PTU5 + DEFINES+=PTU5 +} +contains( CONFIG, PTU5_YOCTO ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7 + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + PTU5BASEPATH = /opt/devel/ptu5 + ARCH = PTU5 + DEFINES+=PTU5 + + LIBS += -lCAslave + LIBS += -lCAmaster +} +contains( CONFIG, DesktopLinux ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + lessThan(QT_MAJOR_VERSION, 5): CONFIG += extserialport + # QMAKE_CC = ccache $$QMAKE_CC + # QMAKE_CXX = ccache $$QMAKE_CXX + QMAKE_CXXFLAGS += -std=c++17 + # QMAKE_CXXFLAGS += -Wno-deprecated-copy + linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments } + ARCH = DesktopLinux + DEFINES+=DesktopLinux +} + +SOURCES += \ + main.cpp \ + progress_event.cpp \ + update_dc_event.cpp \ + mainwindow.cpp \ + utils.cpp \ + update.cpp \ + git/git_client.cpp \ + ismas/ismas_client.cpp \ + process/command.cpp \ + message_handler.cpp \ + worker.cpp \ + commandline_parser.cpp + +HEADERS += \ + update.h \ + progress_event.h \ + update_dc_event.h \ + utils.h \ + mainwindow.h \ + git/git_client.h \ + apism/ismas_data.h \ + ismas/ismas_client.h \ + process/command.h \ + message_handler.h \ + worker.h \ + commandline_parser.h + +OTHER_FILES += \ + ATBUpdateTool.ini + +FORMS += \ + mainwindow.ui + + +########################################################################################## +# for running program on target through QtCreator +contains( CONFIG, PTU5 ) { + qnx: target.path = /tmp/$${TARGET}/bin + else: unix:!android: target.path = /opt/app/tools/atbupdate/ + !isEmpty(target.path): INSTALLS += target +} diff --git a/UpdatePTUDevCtrl.pro.user b/UpdatePTUDevCtrl/UpdatePTUDevCtrl.pro.user similarity index 100% rename from UpdatePTUDevCtrl.pro.user rename to UpdatePTUDevCtrl/UpdatePTUDevCtrl.pro.user diff --git a/allgemein.txt b/UpdatePTUDevCtrl/allgemein.txt similarity index 100% rename from allgemein.txt rename to UpdatePTUDevCtrl/allgemein.txt diff --git a/apism/ismas_data.h b/UpdatePTUDevCtrl/apism/ismas_data.h similarity index 100% rename from apism/ismas_data.h rename to UpdatePTUDevCtrl/apism/ismas_data.h diff --git a/commandline_parser.cpp b/UpdatePTUDevCtrl/commandline_parser.cpp similarity index 72% rename from commandline_parser.cpp rename to UpdatePTUDevCtrl/commandline_parser.cpp index 7fbeae8..67613d7 100644 --- a/commandline_parser.cpp +++ b/UpdatePTUDevCtrl/commandline_parser.cpp @@ -60,6 +60,16 @@ CommandLineParser::CommandLineParser() QStringList() << "working-directory" << "working-directory", QCoreApplication::translate("main", "working directory of update-script."), QCoreApplication::translate("main", "directory"))) + , m_psaConfigDirectoryOption( + QCommandLineOption( + QStringList() << "psa-config-directory" << "psa-config-directory", + QCoreApplication::translate("main", "config directory of json-files sent to dc."), + QCoreApplication::translate("main", "directory"))) + , m_psaTariffDirectoryOption( + QCommandLineOption( + QStringList() << "psa-tariff-directory" << "psa-tariff-directory", + QCoreApplication::translate("main", "tariff directory of tariff-json-files."), + QCoreApplication::translate("main", "directory"))) , m_dryRunOption( QCommandLineOption( QStringList() << "d" << "dry-run", @@ -75,7 +85,17 @@ CommandLineParser::CommandLineParser() , m_yoctoInstallStatusOption( QCommandLineOption( QStringList() << "Y" << "yocto-install", - QCoreApplication::translate("main", "Show yocto install status of ATBUpdateTool."))) { + QCoreApplication::translate("main", "Show yocto install status of ATBUpdateTool."))) + , m_dcDirectoryOption( + QCommandLineOption( + QStringList() << "dc-directory" << "dc-directory", + QCoreApplication::translate("main", "device controller directory."), + QCoreApplication::translate("main", "directory"))) + , m_readDCVersionOption( + QCommandLineOption( + QStringList() << "D" << "read-dc-version", + QCoreApplication::translate("main", "Show version of device controller."), + QCoreApplication::translate("main", "Show version of device controller."))) { configure(); } @@ -111,6 +131,12 @@ void CommandLineParser::configure() { m_workingDirectoryOption.setDefaultValue("/opt/app/tools/atbupdate/"); m_parser.addOption(m_workingDirectoryOption); + m_psaConfigDirectoryOption.setDefaultValue("etc/psa_config/"); + m_parser.addOption(m_psaConfigDirectoryOption); + + m_psaTariffDirectoryOption.setDefaultValue("etc/psa_tariff/"); + m_parser.addOption(m_psaTariffDirectoryOption); + m_dryRunOption.setDefaultValue("false"); m_parser.addOption(m_dryRunOption); @@ -122,18 +148,32 @@ void CommandLineParser::configure() { m_yoctoInstallStatusOption.setDefaultValue("false"); m_parser.addOption(m_yoctoInstallStatusOption); + + m_dcDirectoryOption.setDefaultValue("etc/dc/"); + m_parser.addOption(m_dcDirectoryOption); + + m_readDCVersionOption.setDefaultValue("false"); + m_parser.addOption(m_readDCVersionOption); } void CommandLineParser::readSettings() { QString const iniFileDir = m_parser.value(m_iniFileDirectoryOption); QString const iniFileName = m_parser.value(m_iniFileNameOption); m_iniFileName = QDir::cleanPath(iniFileDir + QDir::separator() + iniFileName); + + //qCritical() << __PRETTY_FUNCTION__ << " iniFileDir" << iniFileDir; + //qCritical() << __PRETTY_FUNCTION__ << "iniFileName" << m_iniFileName; + if (!m_iniFileName.isEmpty()) { if (QFile(m_iniFileName).exists()) { QSettings settings(m_iniFileName, QSettings::IniFormat); QStringList keys = settings.allKeys(); for (QString const &key: keys) { QVariant v = settings.value(key); + + //qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + // << key << " -> " << v.toString(); + if (key.contains("repository-url")) { m_repositoryUrl = v.toString(); } else @@ -143,6 +183,12 @@ void CommandLineParser::readSettings() { if (key.contains("working-directory")) { m_workingDir = v.toString(); } else + if (key.contains("psa-config-directory")) { + m_psaConfigDir = v.toString(); + } else + if (key.contains("psa-tariff-directory")) { + m_psaTariffDir = v.toString(); + } else if (key.contains("dry-run")) { m_dryRun = (v.toBool() ? "true" : "false"); } else @@ -166,8 +212,20 @@ void CommandLineParser::readSettings() { } else if (key.contains("plugin-name")) { m_plugInName = v.toString(); + } else + if (key.contains("dc-directory")) { + m_dcDir = v.toString(); + } else + if (key.contains("read-dc-version")) { + m_readDCVersion = (v.toBool() ? "true" : "false"); + } else { + qCritical() << __PRETTY_FUNCTION__ + << key << " -> (UNKNOWN) " << v.toString(); } } + } else { + qCritical() << __PRETTY_FUNCTION__ << "iniFileName" << m_iniFileName + << "DOES NOT EXIST"; } } } @@ -193,6 +251,34 @@ QString CommandLineParser::plugInName() { return m_plugInName; } +QString CommandLineParser::psaConfigDir() { + if (m_parser.isSet(m_psaConfigDirectoryOption)) { + m_psaConfigDir = m_parser.value(m_psaConfigDirectoryOption); + } + return m_psaConfigDir; +} + +QString CommandLineParser::psaTariffDir() { + if (m_parser.isSet(m_psaTariffDirectoryOption)) { + m_psaTariffDir = m_parser.value(m_psaTariffDirectoryOption); + } + return m_psaTariffDir; +} + +QString CommandLineParser::dcDir() { + if (m_parser.isSet(m_dcDirectoryOption)) { + m_dcDir = m_parser.value(m_dcDirectoryOption); + } + return m_dcDir; +} + +bool CommandLineParser::readDCVersion() { + if (m_parser.isSet(m_readDCVersionOption)) { + m_readDCVersion = m_parser.value(m_readDCVersionOption); + } + return m_readDCVersion == "false" ? false : true; +} + QString CommandLineParser::workingDir() { if (m_parser.isSet(m_workingDirectoryOption)) { m_workingDir = m_parser.value(m_workingDirectoryOption); @@ -238,7 +324,9 @@ bool CommandLineParser::extendedVersion() { bool CommandLineParser::alwaysDownloadConfig() { if (m_parser.isSet(m_alwaysDownloadConfigOption)) { m_alwaysDownloadConfig = m_parser.value(m_alwaysDownloadConfigOption); + // qCritical() << "m_alwaysDownloadConfigOption IS SET" << m_alwaysDownloadConfig; } + // qCritical() << "m_alwaysDownloadConfig" << m_alwaysDownloadConfig; return m_alwaysDownloadConfig == "false" ? false : true; } diff --git a/commandline_parser.h b/UpdatePTUDevCtrl/commandline_parser.h similarity index 79% rename from commandline_parser.h rename to UpdatePTUDevCtrl/commandline_parser.h index 62d6452..082680d 100644 --- a/commandline_parser.h +++ b/UpdatePTUDevCtrl/commandline_parser.h @@ -1,4 +1,4 @@ -#ifndef COMMAND_LINE_PARSER_H_INCLUDED +#ifndef COMMAND_LINE_PARSER_H_INCLUDED #define COMMAND_LINE_PARSER_H_INCLUDED #include @@ -11,6 +11,8 @@ class CommandLineParser : public QCommandLineParser { QString m_plugInDir; QString m_plugInName; QString m_workingDir; + QString m_psaConfigDir{"etc/psa_config"}; + QString m_psaTariffDir{"etc/psa_tariff"}; QString m_dryRun; QString m_noUpdatePsaHardware; QString m_showYoctoVersion; @@ -19,6 +21,8 @@ class CommandLineParser : public QCommandLineParser { QString m_iniFileName; QString m_alwaysDownloadConfig; QString m_alwaysDownloadDC; + QString m_readDCVersion{"false"}; + QString m_dcDir{"etc/dc/"}; QCommandLineOption m_repositoryUrlOption; QCommandLineOption m_iniFileDirectoryOption; @@ -29,10 +33,14 @@ class CommandLineParser : public QCommandLineParser { QCommandLineOption m_alwaysDownloadConfigOption; QCommandLineOption m_alwaysDownloadDCOption; QCommandLineOption m_workingDirectoryOption; + QCommandLineOption m_psaConfigDirectoryOption; + QCommandLineOption m_psaTariffDirectoryOption; QCommandLineOption m_dryRunOption; QCommandLineOption m_extendedVersionOption; QCommandLineOption m_yoctoVersionOption; QCommandLineOption m_yoctoInstallStatusOption; + QCommandLineOption m_dcDirectoryOption; + QCommandLineOption m_readDCVersionOption; QCommandLineParser m_parser; @@ -52,6 +60,8 @@ public: QString plugInDir(); QString plugInName(); QString workingDir(); + QString psaConfigDir(); + QString psaTariffDir(); bool dryRun(); bool noUpdatePsaHardware(); bool yoctoVersion(); @@ -59,5 +69,7 @@ public: bool extendedVersion(); bool alwaysDownloadConfig(); bool alwaysDownloadDC(); + bool readDCVersion(); + QString dcDir(); }; #endif // COMMAND_LINE_PARSER_H_INCLUDED diff --git a/dc2c4.bin b/UpdatePTUDevCtrl/dc2c4.bin similarity index 100% rename from dc2c4.bin rename to UpdatePTUDevCtrl/dc2c4.bin diff --git a/doc/ATBUpdateTool.drawio b/UpdatePTUDevCtrl/doc/ATBUpdateTool.drawio similarity index 100% rename from doc/ATBUpdateTool.drawio rename to UpdatePTUDevCtrl/doc/ATBUpdateTool.drawio diff --git a/doc/ATBUpdateTool.drawio-3.pdf b/UpdatePTUDevCtrl/doc/ATBUpdateTool.drawio-3.pdf similarity index 100% rename from doc/ATBUpdateTool.drawio-3.pdf rename to UpdatePTUDevCtrl/doc/ATBUpdateTool.drawio-3.pdf diff --git a/UpdatePTUDevCtrl/doc/ATBUpdateTool.drawio-4.pdf b/UpdatePTUDevCtrl/doc/ATBUpdateTool.drawio-4.pdf new file mode 100644 index 0000000..ad1a13b Binary files /dev/null and b/UpdatePTUDevCtrl/doc/ATBUpdateTool.drawio-4.pdf differ diff --git a/doc/ATBUpdateTool4.pdf b/UpdatePTUDevCtrl/doc/ATBUpdateTool4.pdf similarity index 100% rename from doc/ATBUpdateTool4.pdf rename to UpdatePTUDevCtrl/doc/ATBUpdateTool4.pdf diff --git a/doc/update_ptu.pdf b/UpdatePTUDevCtrl/doc/update_ptu.pdf similarity index 100% rename from doc/update_ptu.pdf rename to UpdatePTUDevCtrl/doc/update_ptu.pdf diff --git a/doc/update_ptu.tex b/UpdatePTUDevCtrl/doc/update_ptu.tex similarity index 100% rename from doc/update_ptu.tex rename to UpdatePTUDevCtrl/doc/update_ptu.tex diff --git a/git/git_client.cpp b/UpdatePTUDevCtrl/git/git_client.cpp similarity index 66% rename from git/git_client.cpp rename to UpdatePTUDevCtrl/git/git_client.cpp index b23d7a3..4f433d6 100644 --- a/git/git_client.cpp +++ b/UpdatePTUDevCtrl/git/git_client.cpp @@ -2,20 +2,23 @@ #include "update.h" #include "worker.h" #include "utils.h" +#include "process/command.h" #include #include #include +#include -GitClient::GitClient(QString const &customerNrStr, +GitClient::GitClient(QString const &customerRepositoryPath, + QString const &customerNrStr, QString const &customerRepository, QString const &workingDirectory, QString const &branchName, QObject *parent) : QObject(parent) , m_worker(qobject_cast(parent)) - , m_repositoryPath(QString("https://git.mimbach49.de/GerhardHoffmann/%1.git").arg(customerNrStr)) + , m_repositoryPath(customerRepositoryPath) , m_customerNr(customerNrStr) , m_workingDirectory(workingDirectory) , m_branchName(branchName) @@ -192,7 +195,7 @@ QStringList GitClient::gitShowReason(QString branchName) { int const m = s.indexOf("m="); int const d = s.indexOf("d="); - QString msg = IsmasClient::getReasonForLastSendVersion(); + QString msg{""}; // IsmasClient::getReasonForLastSendVersion(); QString commit{""}, date{""}; if (c != -1) { @@ -204,9 +207,9 @@ QStringList GitClient::gitShowReason(QString branchName) { start = m + 2; if (d >= start) { length = d - start; - msg += " ("; + // msg = " ("; msg = s.mid(start, length).trimmed(); - msg += ")"; + // msg += ")"; start = d + 2; date = s.mid(start); @@ -290,6 +293,100 @@ bool GitClient::gitFsck() { } return r; } + +bool GitClient::branchExistsRemotely() { + bool remoteBranchExists = false; + if (QDir(m_customerRepository).exists()) { + qInfo() << "BRANCH NAME" << m_branchName; + QString const cmd = QString("git ls-remote --exit-code --heads origin %1").arg(m_branchName); + Command c(cmd); + if (c.execute(m_customerRepository)) { + // expected result: c16c833c8778c1b3691a74afee5a469177e4e69b refs/heads/zg1/zone1000 + QString const s = c.getCommandResult().trimmed(); + + + if (!s.isEmpty()) { + // the result is only one line + if ((remoteBranchExists = s.contains(m_branchName)) == true) { + qCritical() << "(" << __func__ << ":" << __LINE__ << ") branch" + << m_branchName << "EXISTS REMOTELY. (" << s << ")"; + } + } else { + Utils::printCriticalErrorMsg(QString("EMPTY RESULT FOR CMD %1").arg(cmd)); + } + } else { + Utils::printCriticalErrorMsg(QString("FAILED TO EXEC '%1'").arg(cmd)); + } + } + return remoteBranchExists; +} + +bool GitClient::branchExistsLocally() { + Command c("git branch -l"); + if (c.execute(m_customerRepository)) { + QString const s = c.getCommandResult().trimmed(); + if (!s.isEmpty()) { + QStringList lines = Update::split(s, '\n'); + if (!lines.empty()) { + for (int i=0; i < lines.size(); ++i) { + QString line = lines.at(i); + // expected: * [new branch] zg1/zone12 -> origin/zg1/zone12" + if (line.contains(m_branchName)) { + if (m_worker) { + QStringList lst(QString("BRANCH-NAME %1 CONTAINED IN RESULT %2").arg(m_branchName).arg(s)); + m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH; + } + return true; + } + } + + if (m_worker) { + QStringList lst(QString("BRANCH-NAME %1 NOT CONTAINED IN RESULT %2").arg(m_branchName).arg(s)); + m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH_FAILURE; + } + + } else { + if (m_worker) { + QStringList lst(QString("'git branch -l' RETURNED NO LINES")); + m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH_FAILURE; + } + } + } else { + if (m_worker) { + QStringList lst(QString("'git branch -l' RETURNED EMPTY RESULT")); + m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH_FAILURE; + } + } + } else { + if (m_worker) { + QStringList lst(QString("FAILED TO EXEC 'git branch -l'")); + m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH_FAILURE; + } + } + + return false; +} + +bool GitClient::gitPullNewBranches() { + + if (QDir(m_customerRepository).exists()) { + Command c("git pull"); + if (c.execute(m_customerRepository)) { + QString const s = c.getCommandResult().trimmed(); + // expected: Already up-to-date. + if (!s.isEmpty()) { + QStringList lst; + QString msg(QString("GIT-PULL-NEW-BRANCH. RESULT=%1").arg(s)); + if (m_worker) { + m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::PULL_NEW_BRANCH; + } + return true; + } + } + } + return false; +} + /* Hat sich nichts geaendert, so werden auch keine Commits <>..<> angezeigt */ @@ -297,6 +394,45 @@ std::optional GitClient::gitPull() { if (QDir(m_customerRepository).exists()) { qInfo() << "BRANCH NAME" << m_branchName; +#if 0 + IMPORTANT COMMENT: + + If remote host keys are changed, then + + export GIT_SSH_COMMAND="ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig" + git pull + + leads to the following warning/error message: + + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ + @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! + Someone could be eavesdropping on you right now (man-in-the-middle attack)! + It is also possible that a host key has just been changed. + The fingerprint for the ECDSA key sent by the remote host is + SHA256:vOD5jF2hglGktqLhK9ABxfEjwEgIK68/v9erdT05NDQ. + Please contact your system administrator. + Add correct host key in /home/root/.ssh/known_hosts to get rid of this message. + Offending ECDSA key in /home/root/.ssh/known_hosts:1 + Password authentication is disabled to avoid man-in-the-middle attacks. + Keyboard-interactive authentication is disabled to avoid man-in-the-middle attacks. + Agent forwarding is disabled to avoid man-in-the-middle attacks. + X11 forwarding is disabled to avoid man-in-the-middle attacks. + Already up to date. + + This first part is from ssh itself. Only the last line is the git message. + + Here an output of running ATBUpdateTool with a corrupted known-hosts-file: + + ... + Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: ( branchExistsRemotely : 310 ) branch "zg1/zone1" EXISTS REMOTELY. ( "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @\r\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\nIT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\r\nSomeone could be eavesdropping on you right now (man-in-the-middle attack)!\r\nIt is also possible that a host key has just been changed.\r\nThe fingerprint for the ECDSA key sent by the remote host is\nSHA256:vOD5jF2hglGktqLhK9ABxfEjwEgIK68/v9erdT05NDQ.\r\nPlease contact your system administrator.\r\nAdd correct host key in /home/root/.ssh/known_hosts to get rid of this message.\r\nOffending ECDSA key in /home/root/.ssh/known_hosts:1\r\nPassword authentication is disabled to avoid man-in-the-middle attacks.\r\nKeyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.\r\nAgent forwarding is disabled to avoid man-in-the-middle attacks.\r\nX11 forwarding is disabled to avoid man-in-the-middle attacks.\r\nbd4e8da4780b1a7d6be3d3ce8419f43ccf7e706f\trefs/heads/zg1/zone1" ) + Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: EXECUTED "git branch -l" "(runtime 16ms)" with code 0 IN "/opt/app/tools/atbupdate/customer_336" + Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: "UPDATE_STEP::PULL_NEW_BRANCH" + Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: "BRANCH-NAME zg1/zone1 CONTAINED IN RESULT master\n* zg1/zone1" ..... + + The download continues. +#endif Command c("git pull"); if (c.execute(m_customerRepository)) { QString const s = c.getCommandResult().trimmed(); @@ -316,6 +452,31 @@ std::optional GitClient::gitPull() { // 6ed893f..5d9882c zg1/zone2 -> origin/zg1/zone2 // 4384d17..77045d8 zg1/zone3 -> origin/zg1/zone3 // 89d2812..36a0d74 zg1/zone5 -> origin/zg1/zone5 + // + // More exactly: + // remote: Counting objects: 382, done. + // remote: Compressing objects: 100% (203/203), done. + // remote: Total 278 (delta 177), reused 103 (delta 59) + // Receiving objects: 100% (278/278), 4.89 MiB | 539 KiB/s, done. + // Resolving deltas: 100% (177/177), completed with 40 local objects. + // From ssh://longair@pacific.mpi-cbg.de/srv/git/fiji + // 3036acc..9eb5e40 debian-release-20081030 -> origin/debian-release-20081030 + // * [new branch] debian-release-20081112 -> origin/debian-release-20081112 + // * [new branch] debian-release-20081112.1 -> origin/debian-release-20081112.1 + // 3d619e7..6260626 master -> origin/master + // + // The most important bits here are the lines like these: + // + // 3036acc..9eb5e40 debian-release-20081030 -> origin/debian-release-20081030 + // * [new branch] debian-release-20081112 -> origin/debian-release-20081112 + // + // The first line of these two shows that your remote-tracking branch + // origin/debian-release-20081030 has been advanced from the commit 3036acc to 9eb5e40. + // The bit before the arrow is the name of the branch in the remote repository. + // The second line similarly show that since we last did this, a new remote tracking + // branch has been created. (git fetch may also fetch new tags if they have appeared + // in the remote repository.) + bool found = false; for (int i=0; i < lines.size(); ++i) { if (lines.at(i).contains(m_branchName)) { diff --git a/git/git_client.h b/UpdatePTUDevCtrl/git/git_client.h similarity index 88% rename from git/git_client.h rename to UpdatePTUDevCtrl/git/git_client.h index f6ce89e..d7000e3 100644 --- a/git/git_client.h +++ b/UpdatePTUDevCtrl/git/git_client.h @@ -4,8 +4,8 @@ #include #include #include +#include -#include "process/command.h" #include "ismas/ismas_client.h" class Worker; @@ -22,7 +22,8 @@ class GitClient : public QObject { bool copyGitConfigFromMaster(); public: - explicit GitClient(QString const &customerNrStr, + explicit GitClient(QString const &customerRepositoryUrl, + QString const &customerNrStr, QString const &repositoryPath, QString const &workingDirectory = QCoreApplication::applicationDirPath(), QString const &branchName = "master", @@ -57,6 +58,10 @@ class GitClient : public QObject { static QString gitBlob(QString fileName); QString gitCommitForBlob(QString blob); bool gitIsFileTracked(QString file2name); + + bool branchExistsRemotely(); + bool branchExistsLocally(); + bool gitPullNewBranches(); }; #endif // GIT_CLIENT_H_INCLUDED diff --git a/ismas/ismas_client.cpp b/UpdatePTUDevCtrl/ismas/ismas_client.cpp similarity index 96% rename from ismas/ismas_client.cpp rename to UpdatePTUDevCtrl/ismas/ismas_client.cpp index 50b059b..44c12fc 100644 --- a/ismas/ismas_client.cpp +++ b/UpdatePTUDevCtrl/ismas/ismas_client.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #if 0 ######################## @@ -342,8 +343,11 @@ IsmasClient::sendRequestReceiveResponse(int port, QString const &request) { return std::nullopt; } } else { - printDebugMessage(port, clientIP, clientPort, - QString("PARSE ERROR ") + response + " " + parseError.errorString()); + if (!response.contains("RECORD")) { + // maybe APISM does not send valid JSON: "RECORD SAVED" etc. + printDebugMessage(port, clientIP, clientPort, + QString("PARSE ERROR ") + response + " " + parseError.errorString()); + } ::close(sockfd); return std::nullopt; } @@ -360,12 +364,24 @@ QString IsmasClient::updateNewsToIsmas(char const *event, char buf[1024]; memset(buf, 0, sizeof(buf)); - QString const ts = QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + QDateTime const local(QDateTime::currentDateTime()); + + QDateTime utc(local); + utc.setTimeSpec(Qt::UTC); + + int const diff = (int)local.secsTo(utc); // diff between UTC and local time + + QTime t(0, 0, 0); + t = t.addSecs((uint)diff); + + QString const st(QString("%1%2").arg(diff < 0 ? "-" : "+").arg(t.toString("hh:mm"))); + QString const ts(local.toString(Qt::ISODateWithMs) + st); + snprintf(buf, sizeof(buf)-1, "{" "\"REASON\":\"SW_UP\"," "\"TIMESTAMP\":\"%s\"," - "\"EVENT_ID\":\"0\"," + "\"EVENT_ID\":\"%d\"," "\"EVENT\":\"%s\"," "\"EVENTSTATE\":1," "\"PARAMETER\": {" @@ -375,7 +391,7 @@ QString IsmasClient::updateNewsToIsmas(char const *event, "\"STEP_RESULT\" : \"%s\"," "\"VERSION\" : \"%s\"" "}" - "}", ts.toStdString().c_str(), event, percent, resultCode, + "}", ts.toStdString().c_str(), static_cast(QCoreApplication::applicationPid()), event, percent, resultCode, step, step_result, version); return buf; } @@ -384,7 +400,7 @@ QString IsmasClient::errorBackendNotConnected(QString const &info, QString const &version) { return updateNewsToIsmas("U0003", m_progressInPercent, - RESULT_CODE::INSTALL_ERROR, + RESULT_CODE::ISMAS_NO_CONNECTION_ERROR, "CHECK BACKEND CONNECTIVITY", info.toStdString().c_str(), version.toStdString().c_str()); @@ -394,7 +410,7 @@ QString IsmasClient::errorGitClone(QString const &info, QString const &version) { return updateNewsToIsmas("U0003", m_progressInPercent, - RESULT_CODE::INSTALL_ERROR, + RESULT_CODE::GIT_CLONE_ERROR, "CLONE CUSTOMER REPOSITORY FAILED", info.toStdString().c_str(), version.toStdString().c_str()); @@ -442,7 +458,7 @@ QString IsmasClient::updateTriggerSet(QString const &info, QString const &versio QString IsmasClient::errorUpdateTrigger(QString const &info, QString const &version) { return updateNewsToIsmas("U0003", m_progressInPercent, - RESULT_CODE::INSTALL_ERROR, + RESULT_CODE::ISMAS_TRIGGER_ERROR, "CHECK UPDATE TRIGGER", info.toStdString().c_str(), version.toStdString().c_str()); @@ -675,7 +691,8 @@ QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) { "\"libTCP_ZVT_CCPlugin.so\" : {" "\"VERSION\" : \"%s\"" "}" - "}" + "}," + "\"PTU-PACKAGE-VERSIONS\" : %s" "}", psa.versionInfo.reason.toStdString().c_str(), psa.versionInfo.created.toStdString().c_str(), @@ -786,9 +803,11 @@ QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) { psa.pluginVersion.mobilisisCalculatePriceConfigUi.toStdString().c_str(), psa.pluginVersion.prmCalculatePrice.toStdString().c_str(), psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(), - psa.pluginVersion.tcpZVT.toStdString().c_str()); + psa.pluginVersion.tcpZVT.toStdString().c_str(), - qInfo() << buf; + psa.ptuPackageVersion.toStdString().c_str()); + + qInfo() << buf; return buf; } diff --git a/ismas/ismas_client.h b/UpdatePTUDevCtrl/ismas/ismas_client.h similarity index 82% rename from ismas/ismas_client.h rename to UpdatePTUDevCtrl/ismas/ismas_client.h index 1811463..31c73d0 100644 --- a/ismas/ismas_client.h +++ b/UpdatePTUDevCtrl/ismas/ismas_client.h @@ -77,6 +77,8 @@ struct PSAInstalled { DC2C print[32]; + QString ptuPackageVersion; + explicit PSAInstalled() { tariff.name = "N/A"; tariff.version = "N/A"; @@ -145,13 +147,47 @@ public: DIRECT_PORT = 7778 }; - enum RESULT_CODE { SUCCESS=0, + // if between 00:00 - 04:00 Wait-button state not WAIT, then we assume + // that's an automatic nightly (not-necessary) update NO_UPDATE_NECESSARY=1, - BACKUP_FAILED=2, - WRONG_PACKAGE=3, - INSTALL_ERROR=4}; + // if APISM reports the ISMAS is not available (15x, 6s delay each) + ISMAS_NO_CONNECTION_ERROR=2, + // if not within 00:00-04:00 and WAIT-button was not in state WAIT + ISMAS_TRIGGER_ERROR=3, + // cloning git repo. not possible + GIT_CLONE_ERROR=4, + // pulling from remote git server not possible + GIT_PULL_ERROR=5, + // fetching from remote git server not possible + GIT_FETCH_ERROR=6, + // merging fetched data not possible + GIT_MERGE_ERROR=7, + // check sanity of local customer-repository failed + GIT_CHECK_REPOSITORY_ERROR=8, + // switch/checkout of branch (i.e. zone) on error + GIT_SWITCH_BRANCH_ERROR=9, + // fetch/pull of new branch failed. the new branch was not available + // when installing via SD-card followed by intial clone during the + // update process. + GIT_FETCH_NEW_BRANCH_ERROR=10, + // error computing git-blob hash-value + GIT_HASH_ERROR=11, + // update for general json files failed. + JSON_FILES_UPDATE_ERROR=12, + // error downloading config-json-files to device controller + JSON_FILES_DOWNLOAD_ERROR=13, + // error downloading device-controller + DC_DOWNLOAD_ERROR=14, + // error rsyncing json/ini-files to local filesystem + RSYN_ERROR=15, + // HASH_VALUE_ERROR=14, + // HW_COMPATIBILITY_ERROR=15, + OPKG_COMMANDS_ERROR=16, + // CLEANUP_ERROR=18, + UPDATE_IN_ERROR_STATE=99 + }; enum REASON { TIME_TRIGGERED = 0, diff --git a/main.cpp b/UpdatePTUDevCtrl/main.cpp similarity index 74% rename from main.cpp rename to UpdatePTUDevCtrl/main.cpp index 7b19f21..c8f4770 100644 --- a/main.cpp +++ b/UpdatePTUDevCtrl/main.cpp @@ -9,7 +9,7 @@ #endif #include "message_handler.h" -#include "plugins/interfaces.h" +#include #include "commandline_parser.h" #include @@ -31,6 +31,7 @@ #include "worker.h" #include "mainwindow.h" #include "utils.h" +// #include "process/command.h" #include #include @@ -50,9 +51,11 @@ // argv[1]: file to send to dc int main(int argc, char *argv[]) { QByteArray const value = qgetenv("LC_ALL"); - if (value != "C") { + if (value.isEmpty() || value != "C") { qputenv("LC_ALL", "C"); } + + // qputenv("XDG_RUNTIME_DIR", "/var/run/user/0"); openlog("ATB-UPDATE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); @@ -62,7 +65,8 @@ int main(int argc, char *argv[]) { QApplication::setApplicationVersion(APP_VERSION); if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling - atbInstallMessageHandler(atbDebugOutput); + atbInstallMessageHandler(nullptr); + //atbInstallMessageHandler(atbDebugOutput); setDebugLevel(LOG_NOTICE); } @@ -71,6 +75,38 @@ int main(int argc, char *argv[]) { parser.readSettings(); QString repositoryUrl = parser.repositoryUrl(); + + if (repositoryUrl.endsWith('/')) { + repositoryUrl.chop(1); + } + + QString gitSSHCommand(""); + + if (repositoryUrl.contains("ptu-config.atb-comm.de")) { + QByteArray const v = qgetenv("GIT_SSH_COMMAND"); + if (v.isEmpty()) { + QString sshKeyFile("/opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig"); + if (QFileInfo(sshKeyFile).exists()) { + gitSSHCommand = "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig"; + if (!qputenv("GIT_SSH_COMMAND", QByteArray(gitSSHCommand.toStdString().c_str()))) { + qCritical() << "ERROR: GIT_SSH_COMMAND not put into env. Exiting..."; + return -1; + } + } else { + qCritical() << "ERROR ssh-key-file" << sshKeyFile << "does not exists. Exiting..."; + return -1; + } + } else { + gitSSHCommand = QString(v.toStdString().c_str()); + qCritical() << "WARNING GIT_SSH_COMMAND already set in enviroment:" + << gitSSHCommand; + if (gitSSHCommand != "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig") { + qCritical() << "ERROR" << gitSSHCommand << "wrong. Exiting..."; + return -1; + } + } + } + QString plugInDir = parser.plugInDir(); QString plugInName = parser.plugInName(); QString workingDir = parser.workingDir(); @@ -93,6 +129,9 @@ int main(int argc, char *argv[]) { qInfo() << "pwd ......................" << rtPath; qInfo() << "repositoryUrl ............" << repositoryUrl; + if (!gitSSHCommand.isEmpty()) { + qInfo() << "GIT_SSH_COMMAND .........." << gitSSHCommand; + } qInfo() << "plugInDir ................" << plugInDir; qInfo() << "plugInName ..............." << plugInName; qInfo() << "workingDir ..............." << workingDir; @@ -130,7 +169,9 @@ int main(int argc, char *argv[]) { if (!QDir(plugInDir).exists()) { qCritical() << plugInDir << "does not exists, but has to contain dc-library"; - exit(-1); + if (noUpdatePsaHardware == false) { + exit(-1); + } } // before loading the library, delete all possible shared memory segments @@ -159,5 +200,9 @@ int main(int argc, char *argv[]) { mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint); mw.showFullScreen(); + // test + worker.dcUpdate(); + // worker.summary(); + return a.exec(); } diff --git a/main.cpp.bck b/UpdatePTUDevCtrl/main.cpp.bck similarity index 100% rename from main.cpp.bck rename to UpdatePTUDevCtrl/main.cpp.bck diff --git a/main.cpp.bck2 b/UpdatePTUDevCtrl/main.cpp.bck2 similarity index 100% rename from main.cpp.bck2 rename to UpdatePTUDevCtrl/main.cpp.bck2 diff --git a/main.cpp.bck3 b/UpdatePTUDevCtrl/main.cpp.bck3 similarity index 100% rename from main.cpp.bck3 rename to UpdatePTUDevCtrl/main.cpp.bck3 diff --git a/UpdatePTUDevCtrl/mainwindow.cpp b/UpdatePTUDevCtrl/mainwindow.cpp new file mode 100644 index 0000000..c17b24d --- /dev/null +++ b/UpdatePTUDevCtrl/mainwindow.cpp @@ -0,0 +1,416 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "worker.h" +#include "utils.h" +#include "progress_event.h" +#include "update_dc_event.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + + +MainWindow::MainWindow(Worker *worker, QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , m_worker(worker) + , m_width(70) + , m_progressRunning(false) + , m_updateStep(UpdateDcEvent::UpdateStep::NONE) { + + ui->setupUi(this); + checkOrientation(); + + this->setStatusBar(new QStatusBar(this)); + QFont f; + f.setStyleHint(QFont::Monospace); + f.setWeight(QFont::Bold); + f.setFamily("Misc Fixed"); + f.setPointSize(11); + this->statusBar()->setFont(f); + + ui->updateProgress->setRange(0, 100); + ui->updateProgress->reset(); + + QStringList lst; + QString start = QDateTime::currentDateTime().toString(Qt::ISODate); + lst << QString("Start: ") + start.leftJustified(m_width-10); + lst << QString("").leftJustified(m_width-3, '='); + lst << QString("Update tool version : %1 - %2 %3").arg(APP_VERSION).arg(APP_BUILD_DATE).arg(APP_BUILD_TIME).leftJustified(m_width-3); + lst << QString("Machine number : %1 ").arg(m_worker->machineNr()).leftJustified(m_width-3); + lst << QString("Customer number : %1 ").arg(m_worker->customerNr()).leftJustified(m_width-3); + lst << QString("Zone number : %1 (%2)").arg(m_worker->zoneNr()).arg(Utils::zoneName(m_worker->zoneNr())).leftJustified(m_width-3); + lst << QString("APISM version : %1").arg(m_worker->apismVersion()).leftJustified(m_width-3); + lst << QString("").leftJustified(m_width-3, '='); + + ui->updateLabel->setText(lst.join('\n')); + ui->updateLabel->setEnabled(true); + // ui->updateStatus->installEventFilter(this); + + m_startTimer = new QTimer(this); + connect(m_startTimer, SIGNAL(timeout()), m_worker, SLOT(start())); + m_startTimer->setSingleShot(true); + m_startTimer->start(1000); + + m_exitTimer = new QTimer(this); + connect(m_exitTimer, SIGNAL(timeout()), ui->exit, SLOT(click())); + m_exitTimer->setSingleShot(true); + m_exitTimer->start(1800 * 1000); + + m_statusTimer = new QTimer(this); + if (m_statusTimer) { + connect(m_statusTimer, &QTimer::timeout, [this]() { + static QString p("."); + QTime const &t = QDateTime::currentDateTime().time(); + QString s = t.toString(Qt::ISODate); + s += ": Update might take several minutes " + p; + if (p.length() >= 5) { + p = "."; + } else { + p += "."; + } + this->statusBar()->showMessage(s); + }); + m_statusTimer->setSingleShot(false); + m_statusTimer->start(1000); + } + + connect(ui->exit, SIGNAL(clicked()),this,SLOT(onQuit())); + connect(m_worker, SIGNAL(showSummary(QString)),this,SLOT(onShowSummary(QString))); + connect(m_worker, SIGNAL(disableExit()),this,SLOT(onDisableExit())); + connect(m_worker, SIGNAL(showDcDownload(QString)),this,SLOT(onShowDcDownload(QString))); + connect(m_worker, SIGNAL(setDcDownloadProgress(int)),this,SLOT(onSetDcDownloadProgress(int))); + connect(m_worker, SIGNAL(enableExit()),this,SLOT(onEnableExit())); + connect(m_worker, SIGNAL(stopStartTimer()),this,SLOT(onStopStartTimer())); + connect(m_worker, SIGNAL(restartExitTimer()),this,SLOT(onRestartExitTimer())); + connect(m_worker, SIGNAL(appendText(QString,QString)),this,SLOT(onAppendText(QString,QString))); + connect(m_worker, SIGNAL(insertText(QString)),this,SLOT(onInsertText(QString)), Qt::DirectConnection); + connect(m_worker, SIGNAL(showErrorMessage(QString,QString)),this, SLOT(onShowErrorMessage(QString,QString))); + connect(m_worker, SIGNAL(showStatusMessage(QString,QString)),this, SLOT(onShowStatusMessage(QString,QString))); + connect(m_worker, SIGNAL(showErrorMessage(QStringList)),this, SLOT(onShowErrorMessage(QStringList))); + connect(m_worker, SIGNAL(showStatusMessage(QString,QString)),this, SLOT(onShowStatusMessage(QString,QString))); + connect(m_worker, SIGNAL(replaceLast(QString,QString)),this,SLOT(onReplaceLast(QString,QString))); + connect(m_worker, SIGNAL(replaceLast(QStringList,QString)),this, SLOT(onReplaceLast(QStringList,QString))); +} + + +void MainWindow::onShowSummary(QString text) { + // QString s = ui->updateLabel->text(); + QString s("\n"); + + ui->updateLabel->setText(s); + ui->updateLabel->hide(); + ui->stepLabel->hide(); + + s += text; + ui->updateStatus->setText(s); +} + +void MainWindow::onSetDcDownloadProgress(int v) { + ui->updateProgress->setValue(v); +} + +void MainWindow::onShowDcDownload(QString version) { + m_targetDcVersion = version; + ui->exit->setEnabled(false); + ui->stepLabel->setText(QString("Device controller update. Target version: %1").arg(version).leftJustified(m_width-3)); +} + +MainWindow::~MainWindow() { + delete m_startTimer; + delete m_exitTimer; + delete m_statusTimer; + delete ui; +} + + +// ----------------------------- Ui::LAYOUT setting ------------------------------------- + +void MainWindow::checkOrientation() +{ + QScreen *screen = QGuiApplication::primaryScreen(); + Qt::ScreenOrientation orientation = screen->orientation(); + + switch (orientation) { + case Qt::PrimaryOrientation: + this->setLandscapeLayout(); + break; + case Qt::LandscapeOrientation: + this->setLandscapeLayout(); + break; + case Qt::PortraitOrientation: + this->setPortraitLayout(); + break; + case Qt::InvertedLandscapeOrientation: + this->setLandscapeLayout(); + break; + case Qt::InvertedPortraitOrientation: + this->setPortraitLayout(); + break; + } + + this->currentOrientation = orientation; +} + + + +void MainWindow::setPortraitLayout() +{ + // Adjust layout for portrait mode (480x800) + this->setFixedSize(480, 800); + ui->centralwidget->setFixedSize(480, 800); +} + +void MainWindow::setLandscapeLayout() +{ + // Adjust layout for landscape mode (800x480) + this->setFixedSize(800, 480); + ui->centralwidget->setFixedSize(800, 480); +} + + + + + + +void MainWindow::customEvent(QEvent *event) { + if (event->type() == ProgressEvent::type()) { + ProgressEvent *pevent = (ProgressEvent *)event; + int const progress = pevent->progressPercent(); + QObject const *sender = pevent->sender(); + if (sender == this) { + switch(progress) { + case 0: { + ui->updateProgress->reset(); + } break; + case START_PROGRESS_LOOP: { + m_progressRunning = true; + ui->updateProgress->reset(); + // m_progressValue = 10; + QApplication::postEvent(this, new ProgressEvent(this, 1)); + } break; + case STOP_PROGRESS_LOOP: { + m_progressRunning = false; + // m_progressValue -= 10; + // m_worker->setProgress(m_progressValue/10); + } break; + default: { + if (m_progressRunning) { + // m_progressValue = progress; + ui->updateProgress->setValue(progress); + // ueberpruefen: hauptfenster schickt sich selber ein event + // QApplication::postEvent(this, new ProgressEvent(this, progress)); + // QThread::msleep(500); + }} + } + } else + if (sender == m_worker) { + switch(progress) { + case 0: { + ui->updateProgress->reset(); + } break; + case START_PROGRESS_LOOP: { + QApplication::postEvent(this, new ProgressEvent(this, START_PROGRESS_LOOP)); + } break; + case STOP_PROGRESS_LOOP: { + QApplication::postEvent(this, new ProgressEvent(this, STOP_PROGRESS_LOOP)); + } break; + default:{ + ui->updateProgress->setValue(progress); + }} + } else { + qCritical() << "!!! UNKNOWN SENDER !!!"; + } + } + + QThread::yieldCurrentThread(); +} + +void MainWindow::onStopStartTimer() { + m_startTimer->stop(); +} + +void MainWindow::onDisableExit() { + ui->exit->setEnabled(false); +} + +void MainWindow::onEnableExit() { + ui->exit->setEnabled(true); +} + +void MainWindow::onRestartExitTimer() { + m_exitTimer->stop(); + m_exitTimer->start(5 * 1000); + + scrollDownTextEdit(); + ui->updateStatus->setEnabled(false); +} + +void MainWindow::onQuit() { + m_exitTimer->stop(); + int errorCode = 0; + + qCritical() + << QString("ON QUIT: CURRENT STEP %1") + .arg(m_worker->getSmap()[m_worker->currentStep()]); + + // TODO: replace SEND_LAST_VERSION with UPDATE_SUCCEEDED + if (m_worker->currentStep() != Worker::UPDATE_STEP::SEND_LAST_VERSION) { + errorCode = -1; + } + qCritical() << QString("ON QUIT: EXIT CODE %1").arg(errorCode); + qApp->exit(errorCode); +} + +void MainWindow::scrollDownTextEdit() { + // Utils::printInfoMsg(QString("SCROLL-DOWN-TEXT_EDIT CALLED AT ") + // + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + + ui->updateStatus->setEnabled(true); + + QTextCursor tmpCursor = ui->updateStatus->textCursor(); + tmpCursor.movePosition(QTextCursor::End); + ui->updateStatus->setTextCursor(tmpCursor); + ui->updateStatus->ensureCursorVisible(); +} + + +void MainWindow::onInsertText(QString text) { + scrollDownTextEdit(); + ui->updateStatus->textCursor().insertText(text); +} + +void MainWindow::onAppendText(QString text, QString suffix) { + // Utils::printInfoMsg(QString("ON APPEND CALLED AT ") + // + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + + QString editText = ui->updateStatus->toPlainText(); + scrollDownTextEdit(); + + if (!suffix.isNull() && suffix.size() > 0) { + //qInfo() << "TEXT" << text << "SUFFIX" << suffix; + if (suffix == Worker::UPDATE_STEP_SUCCESS || suffix == Worker::UPDATE_STEP_FAIL) { + ui->updateStatus->insertPlainText(QString("\n").leftJustified(m_width-3, '=') + " "); + // editText += QString("\n").leftJustified(m_width-3, '='); + // editText += " "; + } + QString const &add = (QString("\n") + text).leftJustified(m_width - (2 + suffix.size())) + suffix; + ui->updateStatus->insertPlainText(add); + // editText += add; + } else { + QString const &add = text.leftJustified(m_width-9); + ui->updateStatus->insertPlainText(add); + //editText += add; + } + + // debug + // QString editText = ui->updateStatus->toPlainText(); + // Utils::printLineEditInfo(editText.split('\n', QString::SplitBehavior::SkipEmptyParts)); + // ui->updateStatus->setText(editText.trimmed()); + + scrollDownTextEdit(); +} + +void MainWindow::onReplaceLast(QStringList newTextLines, QString suffix) { + // Utils::printInfoMsg(QString("ON REPLACE LAST (LIST) CALLED AT ") + // + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + int const s = newTextLines.size(); + if (s > 0) { + QString editText = ui->updateStatus->toPlainText(); + QStringList lines = editText.split('\n', QString::SplitBehavior::SkipEmptyParts); + QString newText; + if (lines.size() >= s) { + for (int i = 0; i < s; ++i) { + lines.removeLast(); + } + if (lines.size() > 0) { + newText = lines.join('\n'); + newText += '\n'; + } + QStringList newLines; + for (int i = 0; i < s; ++i) { + if (i == 0 && !suffix.isNull() && suffix.size() > 0 && suffix != "\n") { + newLines += Utils::rstrip(newTextLines.at(i).leftJustified(m_width-10) + suffix); + } else { + newLines += Utils::rstrip(newTextLines.at(i).leftJustified(m_width-10)); + } + } + lines += newLines; + newText += newLines.join(' '); + } + + ui->updateStatus->setText(newText); + Utils::printLineEditInfo(lines); + scrollDownTextEdit(); + } +} + +void MainWindow::onReplaceLast(QString text, QString suffix) { + // Utils::printInfoMsg(QString("ON REPLACE LAST (TEXT) CALLED AT ") + // + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + QString editText = ui->updateStatus->toPlainText(); + QStringList lines = editText.split('\n', QString::SplitBehavior::SkipEmptyParts); + if (lines.size() > 0) { + // removing the last line is really meant for refreshing the last line + // with a string very similar than the original one, typically only + // followed by a suffix. + if (lines.last().contains(text)) { + lines.removeLast(); + } + if (!suffix.isNull() && suffix.size() > 0 && suffix != "\n") { + QString const add = text.leftJustified(m_width-10) + suffix; + if (!add.isEmpty()) { + lines += text.leftJustified(m_width-10) + suffix; + } + } else { + QString const add = text.leftJustified(m_width-10); + if (!add.isEmpty()) { + lines += text.leftJustified(m_width-10); + } + } + } + + Utils::printLineEditInfo(lines); + ui->updateStatus->setText(lines.join('\n').trimmed()); + scrollDownTextEdit(); +} + +void MainWindow::onShowMessage(QString title, QString text) { + this->statusBar()->clearMessage(); + this->statusBar()->showMessage( // timeout: 10000 + QString(title + " " + text).leftJustified(80, ' '), 10000); +} + +void MainWindow::onShowErrorMessage(QString title, QString text) { + onShowMessage(title, text); +} + +void MainWindow::onShowStatusMessage(QString title, QString text) { + onShowMessage(title, text); +} + +void MainWindow::onShowErrorMessage(QStringList lst) { + if (lst.size() >= 2) { + onShowMessage(lst.at(0), lst.at(1)); + } + if (lst.size() == 1) { + onShowMessage(lst.at(0), ""); + } +} + +void MainWindow::onShowStatusMessage(QStringList lst) { + if (lst.size() >= 2) { + onShowMessage(lst.at(0), lst.at(1)); + } + if (lst.size() == 1) { + onShowMessage(lst.at(0), ""); + } +} diff --git a/mainwindow.h b/UpdatePTUDevCtrl/mainwindow.h similarity index 82% rename from mainwindow.h rename to UpdatePTUDevCtrl/mainwindow.h index c6eac86..d128d03 100644 --- a/mainwindow.h +++ b/UpdatePTUDevCtrl/mainwindow.h @@ -36,8 +36,11 @@ public: UpdateDcEvent::UpdateStep updateStep() const { return m_updateStep; } void setUpdateStep(UpdateDcEvent::UpdateStep updateStep) { m_updateStep = updateStep; } + QString targetDcVersion() {return m_targetDcVersion; } + public slots: void onAppendText(QString, QString suffix = ""); + void onInsertText(QString); void onReplaceLast(QStringList, QString suffix = ""); void onReplaceLast(QString, QString suffix = ""); void onShowErrorMessage(QString, QString); @@ -48,6 +51,9 @@ public slots: void onRestartExitTimer(); void onEnableExit(); void onDisableExit(); + void onShowDcDownload(QString); + void onSetDcDownloadProgress(int); + void onShowSummary(QString); #if EMERGENCY_LEAVE_BL==1 void emergencyLeaveBL(); #endif @@ -66,6 +72,12 @@ private: void onShowMessage(QString, QString); Ui::MainWindow *ui; + void checkOrientation(); + void setPortraitLayout(); + void setLandscapeLayout(); + Qt::ScreenOrientation currentOrientation; + + Worker *m_worker; int const m_width; QTimer *m_startTimer; @@ -73,5 +85,7 @@ private: bool m_progressRunning; //int m_progressValue; UpdateDcEvent::UpdateStep m_updateStep; + QTimer *m_statusTimer; + QString m_targetDcVersion; }; #endif // MAINWINDOW_H diff --git a/UpdatePTUDevCtrl/mainwindow.ui b/UpdatePTUDevCtrl/mainwindow.ui new file mode 100644 index 0000000..f88b4c1 --- /dev/null +++ b/UpdatePTUDevCtrl/mainwindow.ui @@ -0,0 +1,136 @@ + + + MainWindow + + + + 0 + 0 + 800 + 480 + + + + + 480 + 480 + + + + + Misc Fixed + 11 + 75 + true + + + + MainWindow + + + + + + 10 + 10 + 781 + 441 + + + + + + + + 0 + 18 + + + + + Misc Fixed + 11 + 75 + true + + + + + + + + + + + true + + + + Misc Fixed + 11 + 75 + true + + + + Qt::NoFocus + + + QFrame::WinPanel + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + false + + + + + + + + 0 + 100 + + + + + Misc Fixed + 11 + 75 + true + + + + + + + + + + + 1 + + + + + + + Exit + + + + + + + + + + diff --git a/UpdatePTUDevCtrl/message_handler.cpp b/UpdatePTUDevCtrl/message_handler.cpp new file mode 100755 index 0000000..996bd2f --- /dev/null +++ b/UpdatePTUDevCtrl/message_handler.cpp @@ -0,0 +1,97 @@ +#include "message_handler.h" + +#include +#include +#include +#include +#include + + +static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " }; +static bool installedMsgHandler = false; +static int debugLevel = LOG_NOTICE; + +int getDebugLevel() { return debugLevel; } +void setDebugLevel(int newDebugLevel) { + debugLevel = newDebugLevel; +} + +bool messageHandlerInstalled() { + return installedMsgHandler; +} + +QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) { + installedMsgHandler = (handler != 0); + static QtMessageHandler prevHandler = nullptr; + if (handler) { + prevHandler = qInstallMessageHandler(handler); + return prevHandler; + } else { + return qInstallMessageHandler(prevHandler); + } +} + +/// +/// \brief Print message according to given debug level. +/// +/// \note Install this function using qInstallMsgHandler(). +/// +/// int main(int argc, char **argv) { +/// installMsgHandler(atbDebugOutput); +/// QApplication app(argc, argv); +/// ... +/// return app.exec(); +/// } +/// +#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + Q_UNUSED(context); + QString const localMsg = QString(DBG_NAME[type]) + msg.toLocal8Bit(); + + switch (debugLevel) { + case LOG_DEBUG: { // debug-level message + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } break; + case LOG_INFO: { // informational message + if (type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_NOTICE: { // normal, but significant, condition + if (type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_WARNING: { // warning conditions + if (type != QtInfoMsg && type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_ERR: { // error conditions + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_CRIT: { // critical conditions + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_ALERT: { // action must be taken immediately + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_EMERG: { // system is unusable + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + default: { + //fprintf(stderr, "%s No ErrorLevel defined! %s\n", + // datetime.toStdString().c_str(), msg.toStdString().c_str()); + } + } +} +#endif + diff --git a/UpdatePTUDevCtrl/message_handler.h b/UpdatePTUDevCtrl/message_handler.h new file mode 100755 index 0000000..98c4d7e --- /dev/null +++ b/UpdatePTUDevCtrl/message_handler.h @@ -0,0 +1,23 @@ +#ifndef MESSAGE_HANDLER_H_INCLUDED +#define MESSAGE_HANDLER_H_INCLUDED + +#include +#ifdef __linux__ +#include +#endif + +int getDebugLevel(); +void setDebugLevel(int newDebugLevel); + +bool messageHandlerInstalled(); +QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler); + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +// typedef void (*QtMessageHandler)(QtMsgType, const char *); +void atbDebugOutput(QtMsgType type, const char *msg); +#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &); +void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg); +#endif + +#endif // MESSAGE_HANDLER_H_INCLUDED diff --git a/UpdatePTUDevCtrl/process/command.cpp b/UpdatePTUDevCtrl/process/command.cpp new file mode 100644 index 0000000..b0b8747 --- /dev/null +++ b/UpdatePTUDevCtrl/process/command.cpp @@ -0,0 +1,203 @@ +#include "command.h" +#include "worker.h" + +#include +#include +#include +#include +#include +#include + + +Command::Command(QString const &command, int start_timeout, int finish_timeout) + : m_command(command.trimmed()) + , m_commandResult("") + , m_waitForStartTimeout(start_timeout) + , m_waitForFinishTimeout(finish_timeout) + , m_exitCode(-1) + , m_p(nullptr) + , m_worker(nullptr) { +} + +QString Command::getCommandResult(bool reset) const { + QMutexLocker locker(&m_mtx); + + if (reset == false) { + return m_commandResult; + } + + QString commandResult = m_commandResult; + m_commandResult.clear(); + + return commandResult; +} + +void Command::readyReadStandardOutput() { + QMutexLocker locker(&m_mtx); + QProcess *p = (QProcess *)sender(); + if (p) { + QString s = p->readAllStandardOutput(); + if (m_worker) { + int i = -1; + if ((i = s.indexOf("")) != -1) { + s = s.mid(i+12).trimmed(); + if ((i = s.indexOf("\"")) != -1) { + s = s.mid(i+1); + if ((i = s.indexOf("\"")) != -1) { + s = s.mid(0, i).trimmed(); + } + } + emit m_worker->showDcDownload(s); + } else + if ((i = s.indexOf("")) != -1) { + bool ok; + int v = s.mid(i+10).trimmed().toInt(&ok); + if (ok) { + emit m_worker->setDcDownloadProgress(v); + emit m_worker->insertText(s.mid(0,i) + "\n"); + } + } else + if ((i = s.indexOf("")) != -1) { + m_worker->summary(); + // qApp->exit(0); + } else + if ((i = s.indexOf("")) != -1) { + m_worker->summary(); + } else + if ((i = s.indexOf("")) != -1) { + m_worker->summary(); + //qApp->exit(-1); + } else { + emit m_worker->insertText(s); + } + } + m_commandResult += s; + } +} + +void Command::readyReadStandardError() { + QProcess *p = (QProcess *)sender(); + if (p) { + QByteArray buf = p->readAllStandardError(); + qCritical() << buf; + } +} + +void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { + QProcess *p = (QProcess *)sender(); + // read all remaining data sent to the process, just in case + QString d = p->readAllStandardOutput(); + if (!d.isEmpty()) { + QMutexLocker locker(&m_mtx); + m_commandResult += d; + } + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput())); + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError())); +} + +bool Command::start(QString workingDirectory, QStringList args) { + if (!QDir::setCurrent(workingDirectory)) { + qCritical() << "SET WORKING_DIRECTORY" << workingDirectory + << "FAILED FOR" << m_command; + return false; + } + if (m_p != nullptr) { + delete m_p; + } + m_p = new QProcess(this); + m_p->setWorkingDirectory(workingDirectory); + m_p->setProcessChannelMode(QProcess::MergedChannels); + + connect(m_p, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput())); + connect(m_p, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError())); + + if (!args.isEmpty()) { + m_p->start(m_command, args); + } else { + m_p->start(m_command); + } + + return m_p->waitForStarted(m_waitForStartTimeout); +} + +bool Command::execute(QString workingDirectory, QStringList args) { + + if (!QDir::setCurrent(workingDirectory)) { + qCritical() << "SET WORKING_DIRECTORY" << workingDirectory + << "FAILED FOR" << m_command; + return false; + } + + QScopedPointer p(new QProcess(this)); + p->setWorkingDirectory(workingDirectory); + p->setProcessChannelMode(QProcess::MergedChannels); + + connect(&(*p), SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput())); + connect(&(*p), SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError())); + + if (!args.isEmpty()) { + //qDebug() << "START COMMAND" << m_command << "WITH ARGS" << args + // << "IN" << p->workingDirectory(); + p->start(m_command, args); + } else { + //qDebug() << "START COMMAND" << m_command + // << "IN" << p->workingDirectory(); + p->start(m_command); + } + + qint64 const start = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + if (p->waitForStarted(m_waitForStartTimeout)) { + // qDebug() << "PROCESS" << m_command << "STARTED IN" << p->workingDirectory(); + if (p->state() == QProcess::ProcessState::Running) { + // qDebug() << "PROCESS" << m_command << "RUNNING IN" << p->workingDirectory(); + // wait forever for git/opkg-commands to finish + int wait = m_waitForFinishTimeout; + if (m_command.trimmed().startsWith("git", Qt::CaseInsensitive) || + m_command.trimmed().startsWith("opkg", Qt::CaseInsensitive)) { + wait = -1; + } + bool const no_timeout = p->waitForFinished(wait); + if (no_timeout) { + // qDebug() << "PROCESS" << m_command << "FINISHED IN" << p->workingDirectory(); + if (p->exitStatus() == QProcess::NormalExit) { + if ((m_exitCode = p->exitCode()) == 0) { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qDebug() << "EXECUTED" << m_command + << QString("(runtime %1ms)").arg(end-start) + << "with code" << m_exitCode + << "IN" << p->workingDirectory(); + return true; + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "EXECUTED" << m_command + << QString("(runtime %1ms)").arg(end-start) + << "with code" << m_exitCode + << "IN" << p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command << "CRASHED with code" + << p->exitCode() + << QString("(after %1ms)").arg(end-start) + << "IN" << p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command + << "DID NOT FINISH WITH" << wait + << "MS IN" << p->workingDirectory() + << QString("(runtime %1ms)").arg(end-start); + } + } else { + qCritical() << "WRONG PROCESS STATE" << p->state() + << "IN" << p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command << "TIMEOUT AT START" + << QString("(runtime %1ms)").arg(end-start) + << "IN" << p->workingDirectory(); + } + return false; +} diff --git a/UpdatePTUDevCtrl/process/command.h b/UpdatePTUDevCtrl/process/command.h new file mode 100644 index 0000000..71efb22 --- /dev/null +++ b/UpdatePTUDevCtrl/process/command.h @@ -0,0 +1,42 @@ +#ifndef COMMAND_H_INCLUDED +#define COMMAND_H_INCLUDED +#endif // COMMAND_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +class Worker; +class Command : public QObject { + Q_OBJECT + + QString m_command; + mutable QString m_commandResult; + int m_waitForStartTimeout; + int m_waitForFinishTimeout; + int m_exitCode; + mutable QMutex m_mtx; + QProcess *m_p; + Worker *m_worker; +public: + explicit Command(QString const &command, + int start_timeout = 100000, + int finish_timeout = 100000); + + QString getCommandResult(bool reset = false) const; + QString command() const { return m_command; } + + bool execute(QString workingDirectory, QStringList args = QStringList()); + bool start(QString workingDirectory, QStringList args = QStringList()); + int exitCode() const { return m_exitCode; } + + void setWorker(Worker *worker) {m_worker = worker; } + +private slots: + void readyReadStandardOutput(); + void readyReadStandardError(); + void finished(int exitCode, QProcess::ExitStatus exitStatus); +}; diff --git a/progress_event.cpp b/UpdatePTUDevCtrl/progress_event.cpp similarity index 100% rename from progress_event.cpp rename to UpdatePTUDevCtrl/progress_event.cpp diff --git a/progress_event.h b/UpdatePTUDevCtrl/progress_event.h similarity index 100% rename from progress_event.h rename to UpdatePTUDevCtrl/progress_event.h diff --git a/UpdatePTUDevCtrl/update.cpp b/UpdatePTUDevCtrl/update.cpp new file mode 100644 index 0000000..4f9c27d --- /dev/null +++ b/UpdatePTUDevCtrl/update.cpp @@ -0,0 +1,1007 @@ +#include "update.h" +#include "worker.h" +#include "utils.h" +#include "update_dc_event.h" +#include "mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX) +#include "unistd.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define UPDATE_OPKG (1) +#define UPDATE_DC (0) + +static const QMap baudrateMap = { + {"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3}, + {"57600" , 4}, {"115200" , 5} +}; + +QPluginLoader Update::pluginLoader; + +hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { + hwinf *hw = nullptr; +#if 0 + if (plugInDir.exists()) { + QString pluginLibName(fname); + pluginLibName = plugInDir.absoluteFilePath(pluginLibName); + QFileInfo info(pluginLibName); + if (info.exists()) { + pluginLibName = plugInDir.absoluteFilePath(pluginLibName); + pluginLoader.setFileName(pluginLibName); + // static QPluginLoader pluginLoader(pluginLibName); + if (!pluginLoader.load()) { + qCritical() << "in directory" << plugInDir.absolutePath(); + qCritical() << "cannot load plugin" << pluginLoader.fileName(); + qCritical() << pluginLoader.errorString(); + } + + qCritical() << "loadDCPlugin() plugin directory:" << plugInDir.absolutePath(); + qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName(); + + if (!pluginLoader.isLoaded()) { + qCritical() << pluginLoader.errorString(); + } + QObject *plugin = pluginLoader.instance(); + if (!plugin) { + qCritical() << "cannot start instance"; + } + if (! (hw = qobject_cast(plugin))) { + qCritical() << "cannot cast plugin" << plugin << "to hwinf"; + } + } else { + qCritical() << pluginLibName << "does not exist"; + } + } else { + qCritical() << "plugins directory" << plugInDir.absolutePath() + << "does not exist"; + } +#endif + return hw; +} + +bool Update::unloadDCPlugin() { + if (pluginLoader.unload()) { + qCritical() << "unloaded plugin" << pluginLoader.fileName(); + // Note: will re-instantiate the library ! + // QObject *rootObject = pluginLoader.instance(); + // if (rootObject) { + // qCritical() << "reloaded plugin: root object again available"; + // return false; + // } + // qCritical()unloaded plugin: root object gone"; + return true; + } + return false; +} + +class hwapi; +Update::Update(Worker *worker, + QString customerRepository, + QString customerNrStr, + QString branchName, + QString plugInDir, + QString pluginName, + QString workingDir, + bool dryRun, + QObject *parent, + char const *serialInterface, + char const *baudrate) + : QObject(parent) + , m_hw(loadDCPlugin(QDir(plugInDir), pluginName)) + , m_worker(worker) + , m_serialInterface(serialInterface) + , m_baudrate(baudrate) + , m_customerRepository(customerRepository) + , m_customerNrStr(customerNrStr) + , m_branchName(branchName) + , m_pluginName(pluginName) + , m_workingDir(workingDir) + , m_dryRun(dryRun) + , m_sys_areDCdataValid(false) { + +#if 0 + if (!m_hw) { + qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin not loaded"; + } else { + int tries = 20; + while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { + // must deliver 'true', only then are all data from hwapi valid + if (--tries < 0) { + qCritical() << "ERROR!!! DC DATA NOT VALID -> CA-MASTER-PLUGIN NOT CONNECTED"; + break; + } + m_hw->dc_autoRequest(true); + QThread::msleep(500); + } + + qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_sys_areDCDataValid ..." + << m_sys_areDCdataValid; + } +#endif +} + +Update::~Update() { +} + +void Update::onReportDCDownloadStatus(QString const &status) { + emit m_worker->showStatusMessage("DL", status); +} + +void Update::onReportDCDownloadSuccess(QString const &msg) { + qCritical() << "msg" << msg; +} + +void Update::onReportDCDownloadFailure(QString const &errorMsg) { + qCritical() << "msg" << errorMsg; +} + + + +// br is a index into a table, used for historical reasons. +bool Update::openSerial(int br, QString baudrate, QString comPort) const { + if (m_hw) { + qDebug() << "opening serial" << br << baudrate << comPort << "..."; + if (m_hw->dc_openSerial(br, baudrate, comPort, 1) == true) { // 1 for connect + Utils::printInfoMsg( + QString("OPENING SERIAL %1").arg(br) + + " " + baudrate + " " + comPort + "...OK"); + + // m_hw->dc_autoRequest(true); + // m_hw->dc_autoRequest(false); + // QThread::sleep(1); + + Utils::printInfoMsg(QString("IS PORT OPEN %1").arg(m_hw->dc_isPortOpen())); + return true; + } + + Utils::printCriticalErrorMsg( + QString("OPENING SERIAL %1").arg(br) + + " " + baudrate + " " + comPort + "...FAILED"); + } + return false; +} + +void Update::closeSerial() const { + qInfo() << "CLOSED SERIAL" << m_baudrate << m_serialInterface; + if (m_hw) { + m_hw->dc_closeSerial(); + } +} + +bool Update::isSerialOpen() const { + return m_hw ? m_hw->dc_isPortOpen() : false; +} + +/* + + /////////////////////////////////////////////////////////////////////////////// + // + // USING THE DC BOOTLOADER + // + /////////////////////////////////////////////////////////////////////////////// + + 1 : bl_reboot() // send to application, want DC2 to reset (in order to + // start the bootloader) + // + // NOTE: this function is NOT reliable !!! Sometimes it + // simply does not work, in which case bl_startBL, + // bl_checkBL and bl_isUp do not work as well. + // Alas, there is no feedback if bl_reboot worked! + // + // NOTE: this function can be called only once per + // minute, because once called again, the controller + // performs some self-checks consuming some time. + // + // NOTE: after a successful bl_reboot(), the device is + // waiting about 4 seconds in the bootloader. To stay in + // the bootloader, we have to send the command + // bl_startBL(), which is kind of a misnomer, as it + // should be bl_doNotLeaveBL(). + // + 2 : bl_startBL(): // send within 4s after DC power-on, otherwise + // bootloader is left. + // + // NOTE: a running bootloader is a MUST for the download + // process of a device controller firmware as it does + // the actual writing of the memory (the bl_reboot() + // from above erases the available memory). + // + 3 : bl_check(): // send command to verify if bl is up + // + // NOTE: this command is kind of a request that we want + // to check if the bootloader is up. The device + // (actually the bootloader) responds with its version. + // + 4 : bl_isUp(): // returns true if bl is up and running + // + // NOTE: we know what the bootloader version actually is + // as the bootloader does not change. By comparing the + // string received in the previous step with this known + // version string we know if the bootloader is up. + // + // NOTE FOR ALL PREVIOUS STEPS: execute them in their + // own slots each to be sure to receive any possible + // responds from the device. + // + 5 : bl_sendAddress(blockNumber) + // send start address, nr of 64-byte block, start with 0 + // will be sent only for following block-numbers: + // 0, 1024, 2048, 3072 and 4096, so basically every + // 64kByte. + // for other addresses nothing happens + + 6 : bl_wasSendingAddOK() + // return val: 0: no response by now + // 1: error + // 10: OK + + 7 : bl_sendDataBlock() + // send 64 byte from bin file + + 8 : bl_sendLastBlock() + // send this command after all data are transferred + + 9 : bl_wasSendingDataOK() + // return val: 0: no response by now + // 1: error + // 10: OK + + 10 : bl_stopBL() // leave bl and start (the new) application + // + // NOTE: this function MUST work under all conditions. + // Alas, there is no direct result for this command, so + // the only way of knowing it was successful is to ask + // the device if the bootloader is still running. + // There is no problem to repeat this command until the + // bootloader is really not running anymore. + */ +bool Update::updateBinary(QString const &fileToSendToDC) { + + QFile dc("/opt/app/tools/atbupdate/ATBDownloadDCFirmware"); + if (dc.exists()) { + qCritical() << "ERROR: dc-binary does not exist" << fileToSendToDC; + return false; + } + + qInfo() << "updating dc-binary" << fileToSendToDC << "..."; + + return false; + +#if 0 + QFile fn(fileToSendToDC); + if (!fn.exists()) { + // output via CONSOLE() etc + return false; + } + + bool bl_isUp = false; + if (m_hw->bl_completeStart()) { + int cnt = 5; + while (--cnt > 0) { + if (m_hw->bl_isUp()) { + bl_isUp = true; + break; + } + } + } + + if (!bl_isUp) { + return false; + } + + if (!m_hw->bl_storeFirmware(fileToSendToDC)) { + m_hw->bl_stopBL(); + return false; + } + + uint16_t const nrOfFirmwareBlocks = m_hw->bl_getNrOfFirmwareBlocks(); + + for (uint16_t blockNr = 0; blockNr <= nrOfFirmwareBlocks; ++blockNr) { + m_hw->bl_blockAutoLoad(blockNr); + + int sleepTime = 0; + while (1) { + if (sleepTime > 1500) { + m_hw->bl_stopBL(); + return false; + } + + int8_t const r = m_hw->bl_blockAutoResponse(); + + // after every "bl_blockAutoLoad()" call this until response + // retval 0: wait 1: OK, blk was sent 2: OK, transfer complete + // 3: error despite repeating, cancel. probably bin file corrupted + // Max duration: 3x no response from BL = 900ms + + switch(r) { + case 1: + /* fall through */ + case 2: + sleepTime = 0; + break; + case 0: { + QThread::msleep(100); + sleepTime += 100; + } break; + case 3: + m_hw->bl_stopBL(); + return false; + default: + m_hw->bl_stopBL(); + return false; // unknown error code + } + } + + m_hw->bl_stopBL(); + } + + return true; +#endif +} + +QString Update::jsonType(enum FileTypeJson type) { + switch (type) { + case FileTypeJson::CASH: return "CASH"; + case FileTypeJson::CONFIG: return "CONFIG"; + case FileTypeJson::PRINTER: return "PRINTER"; + case FileTypeJson::SERIAL: return "SERIAL"; + case FileTypeJson::DEVICE: return "DEVICE"; + case FileTypeJson::TIME: return "TIME"; + } + return "N/A"; +} + +bool Update::downloadJson(enum FileTypeJson type, + int templateIdx, + QString jsFileToSendToDC) const { + + if (m_hw) { + m_hw->dc_autoRequest(true); // downloading Json needs the AutoEmission flag + qDebug() << "SET AUTO-REQUEST=TRUE"; + QThread::sleep(1); // make sure the auto-request flag is acknowledged + + QStringList lst; + bool ready = false; + int nTry = 25; + while ((ready = m_hw->sys_ready4sending()) == false) { + QThread::msleep(200); + if (--nTry <= 0) { + QString msg("SYS NOT READY FOR SENDING AFTER 5 SECONDS"); + Utils::printCriticalErrorMsg(msg); + lst << msg; + + if (m_worker) { + m_worker->ISMAS(lst) << (m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE)); + } + break; + } + } + + lst.clear(); + if (ready) { + QString msg; + QFile file(jsFileToSendToDC); + QFileInfo fi(jsFileToSendToDC); // max. size of template file is 800 bytes + if (file.exists()) { + if (file.open(QIODevice::ReadOnly)) { + if (fi.size() > 0 && fi.size() <= 800) { + QByteArray ba = file.readAll(); + + // kindOfFile: 1=config, 2=device, 3=cash, 4=serial, 5=time, 6=printer + // nrOfTemplate=1...32 if kindOfFile==6 + // content = content of the Json file, max 800byte ascii signs + + // type == kindOfFile + if (m_hw->sys_sendJsonFileToDc((uint8_t)(type), + templateIdx, + (uint8_t *)ba.data())) { + + /* + * Note: the machine id is contained in DC2C_conf.json. + * The idea was to use this to check if the download of + * the json-file was correct. It did not work, as the + * update of the PSA (to reflect a change in the + * machine id) did not happen immediately. + * + m_hw->dc_autoRequest(true); + QThread::msleep(500); + + // testing + m_hw->request_ReadbackMachineID(); + QThread::msleep(500); + + uint8_t data[64]; + memset(data, 0x00, sizeof(data)); + uint8_t length = 0; + + m_hw->readback_machineIDdata(&length, data); + + QThread::msleep(500); + + QByteArray ba((const char*)data, length); + + qCritical() << length << "MACHINE ID =" << ba.toHex(':'); + */ + + if (m_worker) { + lst.clear(); + if (templateIdx >= 1 && templateIdx <= 32) { + lst << QString("LOADED %1 (%2) TO DC").arg(fi.fileName()).arg(templateIdx); + } else { + lst << QString("LOADED %1 (%2) TO DC").arg(fi.fileName()).arg((uint8_t)type); + } + m_worker->ISMAS(lst) << (m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_JSON_FILE)); + } + } else { + msg = QString("ERROR SEND JSON-FILE %1 TO DC").arg(file.fileName()); + Utils::printCriticalErrorMsg(msg); + lst << msg; + if (m_worker) { + m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE); + } + } + } else { + msg = QString("SIZE OF %1 TOO BIG (%2 BYTES)").arg(jsFileToSendToDC).arg(fi.size()); + Utils::printCriticalErrorMsg(msg); + lst << msg; + if (m_worker) { + m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE); + } + } + } else { + msg = QString("CAN NOT OPEN ") + jsFileToSendToDC + " FOR READING"; + Utils::printCriticalErrorMsg(msg); + lst << msg; + + if (m_worker) { + m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE); + } + } + } else { + msg = QString(jsFileToSendToDC) + " DOES NOT EXIST"; + Utils::printCriticalErrorMsg(msg); + lst << msg; + + if (m_worker) { + m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE); + } + } + } + + // m_hw->dc_autoRequest(false); + // qDebug() << "SET AUTO-REQUEST=FALSE"; + // QThread::sleep(1); // make sure the auto-request flag is acknowledged + } + + return true; +} + +bool Update::updatePrinterTemplate(int templateIdx, QString jsFile) const { + return downloadJson(FileTypeJson::PRINTER, templateIdx, jsFile); +} + +bool Update::updateConfig(QString jsFile) { + // IMPORTANT: the JSON-file has a 'title'-line: the value MUST be 'DC2C_config' + return downloadJson(FileTypeJson::CONFIG, 0, jsFile); +} + +bool Update::updateCashConf(QString jsFile) { + // IMPORTANT: the JSON-file has a 'title'-line: the value MUST be 'DC2C_cash ' + // Note the twp spaces after DC2C_cash !! + return downloadJson(FileTypeJson::CASH, 0, jsFile); +} + +bool Update::updateDeviceConf(QString jsFile) { + // IMPORTANT: the JSON-file has a 'title'-line: the value MUST be 'DC2C_device' + return downloadJson(FileTypeJson::DEVICE, 0, jsFile); +} + +QStringList Update::split(QString line, QChar sep) { + QStringList lst; + QString next; + int start = 0, end; + + while ((end = line.indexOf(sep, start)) != -1) { + next = line.mid(start, end - start).trimmed(); + lst << next; + start = end + 1; + } + next = line.mid(start, end - start).trimmed(); + lst << next; + + return lst; +} + +void Update::readyReadStandardOutput() { + QProcess *p = (QProcess *)sender(); + QByteArray buf = p->readAllStandardOutput(); + qCritical() << buf; +} + +void Update::readyReadStandardError() { + QProcess *p = (QProcess *)sender(); + QByteArray buf = p->readAllStandardError(); + qCritical() << buf; +} + +void Update::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { + QProcess *p = (QProcess *)sender(); + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput())); + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError())); +} + +QStringList Update::getDcSoftAndHardWareVersion() { + if (m_hw) { + m_hw->dc_autoRequest(true); + QThread::sleep(1); // make sure the timer-slots are active + + for (int i=0; i < 3; ++i) { // send explicit reuests to get + // current SW/HW-versions + m_hw->request_DC2_SWversion(); + m_hw->request_DC2_HWversion(); + QThread::sleep(1); + } + + QString const &hwVersion = m_hw->dc_getHWversion().toLower().trimmed(); + QString const &swVersion = m_hw->dc_getSWversion().toLower().trimmed(); + + //m_hw->dc_autoRequest(false); + //QThread::sleep(1); // make sure the timer-slots are inactive + + if (!hwVersion.isEmpty() && !swVersion.isEmpty()) { + return QStringList() << hwVersion << swVersion; + } + } + + return QStringList() << "DC HW-version not available" + << "DC SW-version not available"; +} + +QString Update::getFileVersion(QString const& jsonFileName) { + // "version":"15.10.2023 14:55 02.00.06", + static const QRegularExpression re("^.*(\\\"[Vv]ersion\\\":)([\\s\\\"]{0,})([^,\\\"]{0,}).*$"); + + QString fileVersion(""); + QFile inputFile(QDir::cleanPath(m_customerRepository + QDir::separator() + jsonFileName)); + + if (inputFile.exists()) { + if (inputFile.open(QIODevice::ReadOnly)) { + QTextStream in(&inputFile); + while (!in.atEnd()) { + QString line = in.readLine(); + + QRegularExpressionMatch match; + int idx = line.indexOf(re, 0, &match); + if (idx != -1) { + int const lastCaptured = match.lastCapturedIndex(); + // the dc only sends 16 Byte + fileVersion = match.captured(lastCaptured); + fileVersion.truncate(16); + break; + } + } + inputFile.close(); + } + } else { + // qCritical() << "ERROR" << inputFile.fileName() << "does not exist"; + } + + return fileVersion; +} + +bool Update::checkDownloadedJsonVersions(QStringList const& jsonFileNames) { +// note: 2026-02-09: this method is not used! +#if 0 + + for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) { + + uint8_t jsonNr = 0; + + QString const &fName = jsonFileNames[i]; + + // send one request for every single version + // jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res. + // 6=printer template 1 ..... 36= template 32 + + if (fName.endsWith("conf.json")) { + jsonNr = 1; + } else + if (fName.endsWith("device.json")) { + jsonNr = 2; + } else + if (fName.endsWith("cash.json")) { + jsonNr = 3; + } else { + QRegularExpressionMatch match; + static const QRegularExpression re("^(.*print)([0-3][0-9])\\.json\\s*$"); + int idx = fName.indexOf(re, 0, &match); + if (idx != -1) { + QString captured = match.captured(match.lastCapturedIndex()); + bool ok = false; + int n = captured.toInt(&ok); + if (ok) { + // note: the '5' is correct + jsonNr = n + 5; + } + } + } + + if (jsonNr != 0) { + // send one request for every single version + // jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res. + // 5=printer template 1 ..... 36= template 32 + + m_hw->sys_requestJsonVersions(jsonNr); + QThread::msleep(500); + + char buf[64]; + memset(buf, 0x00, sizeof(buf)); + m_hw->sys_getJsonVersions(jsonNr, buf); + buf[16] = '\0'; // the DC only handles 16 bytes + + static const QByteArray cb(16, (char)0xff); + + QString const installedVersion(QString::fromStdString(buf)); + QString const fileVersion = getFileVersion(jsonFileNames[i]); + + QFileInfo fi(jsonFileNames[i]); + + qCritical() << endl; + qCritical() << " json request nr:" << jsonNr; + + if (installedVersion == fileVersion) { + qCritical() << " json file:" << fi.fileName(); + qCritical() << " installed version in DC:" << installedVersion; + } else + if (cb == QByteArray(buf) && fileVersion == "") { + qCritical() << "unknown json file (repo and DC):" << fi.fileName(); + } else { + qCritical() << " json file:" << fi.fileName(); + qCritical() << " installed version in DC:" << installedVersion; + qCritical() << " file version in repository:" << fileVersion; + } + + } else { + qCritical() << "CANNOT FIND JSON-NR FOR" << fName; + } + } +#endif + return false; +} + +QMap +Update::getInstalledJsonVersions(QStringList const& jsonFileNames) { + QMap map; +#if 0 + if (!m_hw) { + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! m_hw == nullptr"; + return map; + } + + int tries = 20; + while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { + // must deliver 'true', only then are all data from hwapi valid + if (--tries < 0) { + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED"; + return map; + } + qCritical() << "(" << __func__ << ":" << __LINE__ << "):" + << "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")"; + m_hw->dc_autoRequest(true); + QThread::msleep(500); + } + + for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) { + + uint8_t jsonNr = 0; + + QString const &fName = jsonFileNames[i]; + + // send one request for every single version + // jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res. + // 6=printer template 1 ..... 36= template 32 + + if (fName.endsWith("conf.json")) { + jsonNr = 1; + } else + if (fName.endsWith("device.json")) { + jsonNr = 2; + } else + if (fName.endsWith("cash.json")) { + jsonNr = 3; + } else { + QRegularExpressionMatch match; + static const QRegularExpression re("^(.*print)([0-3][0-9])\\.json\\s*$"); + int idx = fName.indexOf(re, 0, &match); + if (idx != -1) { + QString captured = match.captured(match.lastCapturedIndex()); + bool ok = false; + int n = captured.toInt(&ok); + if (ok) { + // note: use 5 (instead of 4 -> index has been shifted) + jsonNr = n + 5; + } + } + } + + if (jsonNr != 0) { + // send one request for every single version + // jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res. + // 5=printer template 1 ..... 36= template 32 + + m_hw->sys_requestJsonVersions(jsonNr); + QThread::msleep(500); + + char buf[64]; + memset(buf, 0x00, sizeof(buf)); + m_hw->sys_getJsonVersions(jsonNr, buf); + buf[16] = '\0'; // the DC only handles 16 bytes + + static const QByteArray cb(16, (char)0xff); + + QString const installedVersion(QString::fromStdString(buf)); + QString const fileVersion = getFileVersion(jsonFileNames[i]); + + QFileInfo fi(jsonFileNames[i]); + + if (cb == QByteArray(buf)) { + map.insert(fi.fileName(), "inst.vers.not.avail"); + } else { + map.insert(fi.fileName(), installedVersion); + } + + qCritical() << endl; + qCritical() << " json request nr:" << jsonNr; + + if (installedVersion == fileVersion) { + qCritical() << " json file:" << fi.fileName(); + qCritical() << " installed version in DC:" << installedVersion; + } else + if (cb == QByteArray(buf) && fileVersion == "") { + qCritical() << "unknown json file (repo and DC):" << fi.fileName(); + } else { + qCritical() << " json file:" << fi.fileName(); + qCritical() << " installed version in DC:" << installedVersion; + qCritical() << " file version in repository:" << fileVersion; + } + + } else { + qCritical() << "CANNOT FIND JSON-NR FOR" << fName; + } + } + +#endif + return map; +} + +bool Update::doUpdate(int &displayIndex, QStringList const &filesToWorkOn) { + + if (!m_hw) { + Utils::printInfoMsg("CA-PLUGIN NOT LOADED"); + return false; + } + + int tries = 20; + while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { + // must deliver 'true', only then are all data from hwapi valid + if (--tries < 0) { + Utils::printCriticalErrorMsg("ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED"); + return false; + } + Utils::printCriticalErrorMsg("DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED"); + m_hw->dc_autoRequest(true); + QThread::msleep(500); + } + + bool res = false; + bool dcDownloadPossible = true; + + QList::const_iterator it; + for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) { + m_worker->startProgressLoop(); + QString const &fToWorkOn = QDir::cleanPath(m_customerRepository + QDir::separator() + it->trimmed()); + if (fToWorkOn.endsWith("/dc2c.bin") && dcDownloadPossible) { +#if 0 + // download for dc possible only once + // download of device-controller should always be the last step + dcDownloadPossible = false; + + if (!m_hw->dcDownloadRequest(fToWorkOn)) { // initiate download process + qCritical() << "DOWNLOAD-REQUEST-ERROR FOR" << fToWorkOn; + continue; + } + + QThread::sleep(2); + + int tries = 5; + while (!m_hw->dcDownloadRunning()) { // may take some time + if (--tries < 0) { + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "(" << __func__ << ":" << __LINE__ << ") DOWNLOAD NOT RUNNING"; + break; + } + QThread::sleep(1); + continue; + } + + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "(" << __func__ << ":" << __LINE__ << ") DOWNLOAD RUNNING"; + + QThread::sleep(2); + + tries = 5; + while (!m_hw->dcDownloadReportThreadStart()) { // may take some time + if (--tries < 0) { + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "(" << __func__ << ":" << __LINE__ << ") REPORT THREAD NOT STARTED"; + break; + } + continue; + } + + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "(" << __func__ << ":" << __LINE__ << ") REPORT THREAD STARTED"; + + QThread::sleep(2); + + tries = 5; + while (!m_hw->dcDownloadReportRunning()) { // may take some time + if (--tries < 0) { + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "(" << __func__ << ":" << __LINE__ << ") DOWNLOAD REPORT NOT RUNNING"; + break; + } + continue; + } + + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "(" << __func__ << ":" << __LINE__ << ") DOWNLOAD REPORT RUNNING"; + + tries = 1200; + while (m_hw->dcDownloadReportRunning()) { + QThread::msleep(1000); + if (--tries < 0) { + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "(" << __func__ << ":" << __LINE__ + << ") DOWNLOAD REPORT STILL RUNNING AFTER 20mins"; + break; + } + } +#endif + bool updateBinaryRes = true; + + // CONSOLE() +#if 0 + m_hw->dc_autoRequest(false);// default: turn auto-request setting off + QThread::sleep(1); // wait to be sure that there are no more + // commands sent to dc-hardware + + if ((updateBinaryRes = updateBinary(fToWorkOn)) == true) { + + // qCritical() << "downloaded binary" << fToWorkOn; + + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } + + m_hw->dc_autoRequest(true); // turn auto-request setting on + + // qInfo() << "SET AUTO-REQUEST=TRUE"; + + QStringList const &versions = Update::getDcSoftAndHardWareVersion(); + if (versions.size() >= 2) { + if (updateBinaryRes == true) { + qInfo() << "dc-hardware-version (UPDATED)" << versions[0]; + qInfo() << "dc-firmware-version (UPDATED)" << versions[1]; + } else { + qInfo() << "dc-hardware-version (NOT UPDATED)" << versions[0]; + qInfo() << "dc-firmware-version (NOT UPDATED)" << versions[1]; + } + } +#endif + res = updateBinaryRes; + + } else if (fToWorkOn.contains("DC2C_print", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; + int i = fToWorkOn.indexOf("DC2C_print", Qt::CaseInsensitive); + int const templateIdx = fToWorkOn.mid(i).midRef(10, 2).toInt(); + if ((templateIdx < 1) || (templateIdx > 32)) { + qCritical() << "WRONG TEMPLATE INDEX" << templateIdx; + res = false; + } else { + if ((res = updatePrinterTemplate(templateIdx, fToWorkOn))) { + if (m_worker) { + QStringList lst(QString("DL:%1 (%2)").arg(QFileInfo(fToWorkOn).fileName()).arg(templateIdx)); + m_worker->ISMAS(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_JSON_FILE_SUCCESS); + + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } + } + } + } else if (fToWorkOn.contains("DC2C_cash", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; + if ((res = updateCashConf(fToWorkOn))) { + if (m_worker) { + QStringList lst(QString("DL:%1").arg(QFileInfo(fToWorkOn).fileName())); + m_worker->ISMAS(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_CASH_FILE_SUCCESS); + + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } + } + } else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; + if ((res= updateConfig(fToWorkOn))) { + if (m_worker) { + QStringList lst(QString("DL:%1").arg(QFileInfo(fToWorkOn).fileName())); + m_worker->ISMAS(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_CONFIG_FILE_SUCCESS); + + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } + } + } else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; + if ((res = updateDeviceConf(fToWorkOn))) { + if (m_worker) { + QStringList lst(QString("DL:%1").arg(QFileInfo(fToWorkOn).fileName())); + m_worker->ISMAS(lst) << (m_worker->CONSOLE(lst) << Worker::UPDATE_STEP::DOWNLOAD_DEVICE_FILE_SUCCESS); + + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } + } + } else { + qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn; + res = false; + } + // m_worker->stopProgressLoop(); + // m_worker->setProgress(100); + + if (res == false) { + break; + } + } // for (it = openLines.cbegin(); it != openLines.end(); ++it) { + + m_hw->dc_autoRequest(true); // ALWAYS turn autoRequest ON + qDebug() << "SET AUTO-REQUEST=TRUE"; + + return res; +} diff --git a/UpdatePTUDevCtrl/update.h b/UpdatePTUDevCtrl/update.h new file mode 100644 index 0000000..b70c02a --- /dev/null +++ b/UpdatePTUDevCtrl/update.h @@ -0,0 +1,143 @@ +#ifndef UPDATE_H_INCLUDED +#define UPDATE_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef PTU5 +#define SERIAL_PORT "ttymxc2" +#else +#define SERIAL_PORT "ttyUSB0" +#endif + +class Worker; +class Update : public QObject { + Q_OBJECT + + hwinf *m_hw = nullptr; + Worker *m_worker = nullptr; + char const *m_serialInterface; + char const *m_baudrate; + QString m_customerRepository; + QString m_customerNrStr; + QString m_branchName; + QString m_pluginName; + QString m_workingDir; + bool m_maintenanceMode; + bool m_dryRun; + bool m_sys_areDCdataValid; + + static QPluginLoader pluginLoader; + +public: + enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; + enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6}; + + static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn); + static bool unloadDCPlugin(); + static QStringList split(QString line, QChar sep = ','); + + + explicit Update(Worker *worker, + QString customerRepository, + QString customerNrStr, + QString branchName, + QString plugInDir, + QString pluginName, + QString workingDir, + bool dryRun = false, + QObject *parent = nullptr, + char const *serialInterface = SERIAL_PORT, + char const *baudrate = "115200"); + virtual ~Update() override; + bool doUpdate(int &displayIndex, QStringList const &linesToWorkOn); + bool checkDownloadedJsonVersions(QStringList const& jsonFileNames); + + QMap + getInstalledJsonVersions(QStringList const& jsonFileNames = + QStringList( + QList( + std::initializer_list{ + QString("etc/psa_config/DC2C_conf.json"), + QString("etc/psa_config/DC2C_cash.json"), + QString("etc/psa_config/DC2C_device.json"), + QString("etc/psa_config/DC2C_print01.json"), + QString("etc/psa_config/DC2C_print02.json"), + QString("etc/psa_config/DC2C_print03.json"), + QString("etc/psa_config/DC2C_print04.json"), + QString("etc/psa_config/DC2C_print05.json"), + QString("etc/psa_config/DC2C_print06.json"), + QString("etc/psa_config/DC2C_print07.json"), + QString("etc/psa_config/DC2C_print08.json"), + QString("etc/psa_config/DC2C_print09.json"), + QString("etc/psa_config/DC2C_print10.json"), + QString("etc/psa_config/DC2C_print11.json"), + QString("etc/psa_config/DC2C_print12.json"), + QString("etc/psa_config/DC2C_print13.json"), + QString("etc/psa_config/DC2C_print14.json"), + QString("etc/psa_config/DC2C_print15.json"), + QString("etc/psa_config/DC2C_print16.json"), + QString("etc/psa_config/DC2C_print17.json"), + QString("etc/psa_config/DC2C_print18.json"), + QString("etc/psa_config/DC2C_print19.json"), + QString("etc/psa_config/DC2C_print20.json"), + QString("etc/psa_config/DC2C_print21.json"), + QString("etc/psa_config/DC2C_print22.json"), + QString("etc/psa_config/DC2C_print23.json"), + QString("etc/psa_config/DC2C_print24.json"), + QString("etc/psa_config/DC2C_print25.json"), + QString("etc/psa_config/DC2C_print26.json"), + QString("etc/psa_config/DC2C_print27.json"), + QString("etc/psa_config/DC2C_print28.json"), + QString("etc/psa_config/DC2C_print29.json"), + QString("etc/psa_config/DC2C_print30.json"), + QString("etc/psa_config/DC2C_print31.json"), + QString("etc/psa_config/DC2C_print32.json")}))); + + hwinf *hw() { return m_hw; } + hwinf const *hw() const { return m_hw; } + + //QString customerId() { return m_customerId; } + //QString const customerId() const { return m_customerId; } + + QString branchName() { return m_branchName; } + QString const branchName() const { return m_branchName; } + + //QString repositoryPath() { return m_repositoryPath; } + //QString const repositoryPath() const { return m_repositoryPath; } + +private: + static QString jsonType(enum FileTypeJson type); + bool openSerial(int br, QString baudrate, QString comPort) const; + void closeSerial() const; + bool isSerialOpen() const; + bool resetDeviceController() const; + QByteArray loadBinaryDCFile(QString filename) const; + bool downloadBinaryToDC(QString const &bFile) const; + bool updatePrinterTemplate(int templateIdx, QString fname) const; + bool updateBinary(QString const &fileToSendToDC); + bool updateConfig(QString jsFileToSendToDC); + bool updateCashConf(QString jsFileToSendToDC); + bool updateDeviceConf(QString jsFileToSendToDC); + bool downloadJson(enum FileTypeJson type, int templateIdx, + QString jsFileToSendToDC) const; + QStringList getDcSoftAndHardWareVersion(); + QString getFileVersion(QString const& jsonFile); + +private slots: + void readyReadStandardOutput(); + void readyReadStandardError(); + void finished(int exitCode, QProcess::ExitStatus exitStatus); + void onReportDCDownloadStatus(QString const &status); + void onReportDCDownloadSuccess(QString const &msg); + void onReportDCDownloadFailure(QString const &errorMsg); +}; +#endif // UPDATE_H_INCLUDED diff --git a/update_dc_event.cpp b/UpdatePTUDevCtrl/update_dc_event.cpp similarity index 100% rename from update_dc_event.cpp rename to UpdatePTUDevCtrl/update_dc_event.cpp diff --git a/update_dc_event.h b/UpdatePTUDevCtrl/update_dc_event.h similarity index 100% rename from update_dc_event.h rename to UpdatePTUDevCtrl/update_dc_event.h diff --git a/utils.cpp b/UpdatePTUDevCtrl/utils.cpp similarity index 57% rename from utils.cpp rename to UpdatePTUDevCtrl/utils.cpp index 870af4e..7b7ec65 100644 --- a/utils.cpp +++ b/UpdatePTUDevCtrl/utils.cpp @@ -13,9 +13,65 @@ #include #include #include +#include +#include +#include +#include +#include #include +QVector> Utils::installedPackages() { + QVector> vec; + if (QFile::exists("/usr/bin/ptuPackageVersions")) { + QProcess p; + QStringList params; + params << "-c" << R"(/usr/bin/ptuPackageVersions -i -o json)"; + + p.start("bash", params); + p.waitForFinished(); + + QString r = p.readAllStandardOutput(); + + // ptuPackageVersions returns a json-array + QJsonArray const &ja = QJsonDocument::fromJson(r.remove(QRegExp("\\n")).toUtf8()).array(); + if (!ja.empty()) { + qCritical() << __LINE__; + // transform the array into an object, containing the objects + // of the array (christian needs it this way) + foreach (QJsonValue const &value, ja) { + if (value.isObject()) { + QJsonObject obj = value.toObject(); + QStringList keys = obj.keys(); + if (!keys.isEmpty()) { + QString const &k = keys.first(); + QJsonValue const &v = obj.value(k); + if (v.isObject()) { + obj = v.toObject(); + if (obj.keys().contains("Version")) { + QJsonValue const &w = obj.value("Version"); + if (w.isString()) { + QString s = w.toString(); + QPair p(k, s); + vec.push_back(p); + } + } + } + } + } + } + } else { + qCritical() << __func__ << ":" << __LINE__ + << "ERROR array return by ptuPackageVersions empty"; + } + } else { + qCritical() << __func__ << ":" << __LINE__ + << "ERROR executing ptuPackageVersions"; + } + + return vec; +} + int Utils::read1stLineOfFile(QString fileName) { QFile f(fileName); if (f.exists()) { @@ -30,20 +86,99 @@ int Utils::read1stLineOfFile(QString fileName) { return -1; } -QString Utils::zoneName(quint8 i) { - static constexpr char const *zName[] = { - "", - "purple", - "blue", - "yellow", - "green", - "yellow (mars)", - "green (mars)" - }; - if (i < (sizeof(zName)/sizeof(char const *))) { - return zName[i]; +QString Utils::getLocation(QString fileName) { + QString location("N/A"); + QFile f(fileName); + if (f.exists()) { + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&f); + in.setCodec("UTF-8"); + while(!in.atEnd()) { + QString const &line = in.readLine(); + if (line.indexOf("Project", Qt::CaseInsensitive) != -1) { + int const c = line.indexOf(":"); + if (c != -1) { + location = line.mid(c+1); + if (!location.isEmpty()) { + location = location.replace(QChar(','), QString("")); + return location.replace(QChar('"'), QString("")).trimmed(); + } + } + } + } + } } - return "N/A"; + + return location; +} + +QString Utils::getTariffVersion(QString fileName) { + QString version("N/A"); + QFile f(fileName); + if (f.exists()) { + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&f); + in.setCodec("UTF-8"); + while(!in.atEnd()) { + QString const &line = in.readLine(); + if (line.indexOf("Version", Qt::CaseInsensitive) != -1) { + int const c = line.indexOf(":"); + if (c != -1) { + version = line.mid(c+1); + if (!version.isEmpty()) { + version = version.replace(QChar(','), QString("")); + return version.replace(QChar('"'), QString("")).trimmed(); + } + } + } + } + } + } + + return version; +} + +QString Utils::getTariffInfo(QString fileName) { + QString info("N/A"); + QFile f(fileName); + if (f.exists()) { + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&f); + in.setCodec("UTF-8"); + while(!in.atEnd()) { + QString const &line = in.readLine(); + if (line.indexOf("Info", Qt::CaseInsensitive) != -1) { + int const c = line.indexOf(":"); + if (c != -1) { + info = line.mid(c+1); + if (!info.isEmpty()) { + info = info.replace(QChar(','), QString("")); + return info.replace(QChar('"'), QString("")).trimmed(); + } + } + } + } + } + } + + return info; + +} + +QString Utils::zoneName(quint8 i) { + //static constexpr char const *zName[] = { + // "", + // "purple", + // "blue", + // "yellow", + // "green", + // "yellow (mars)", + // "green (mars)" + //}; + //if (i < (sizeof(zName)/sizeof(char const *))) { + // return zName[i]; + //} + return "---"; } void Utils::printCriticalErrorMsg(QString const &errorMsg, bool upper, bool lower) { @@ -201,21 +336,24 @@ bool Utils::sameFilesInDirs(QDir const &dir1, QDir const &dir2, fileNameLst2 << i2.next().fileName(); } + QString dirPath1 = dir1.absolutePath(); + QString dirPath2 = dir2.absolutePath(); + if (fileNameLst1.isEmpty()) { - qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES"; + qCritical() << "DIR1" << dirPath1 << " DOES NOT CONTAIN EXPECTED FILES"; return false; } if (fileNameLst2.isEmpty()) { - qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES"; + qCritical() << "DIR1" << dirPath2 << " DOES NOT CONTAIN EXPECTED FILES"; return false; } if (fileNameLst1 != fileNameLst2) { - printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName() - + " DIFFER: [" + fileNameLst1.join(',') + "],[" + printCriticalErrorMsg(dirPath1 + " AND " + dirPath2 + + " HAVE DIFFERENT FILES: [" + fileNameLst1.join(',') + "],[" + fileNameLst2.join(',') + "]"); return false; } else { - printInfoMsg(dir1.dirName() + " AND " + dir2.dirName() + printInfoMsg(dirPath1 + " AND " + dirPath2 + " ARE EQUAL: [" + fileNameLst1.join(',') + "]"); } @@ -231,21 +369,21 @@ bool Utils::sameFilesInDirs(QDir const &dir1, QDir const &dir2, } if (gitBlobLst1.isEmpty()) { - qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES"; + qCritical() << "DIR1" << dirPath1 << " DOES NOT CONTAIN EXPECTED FILES"; return false; } if (gitBlobLst2.isEmpty()) { - qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES"; + qCritical() << "DIR1" << dirPath2 << " DOES NOT CONTAIN EXPECTED FILES"; return false; } if (gitBlobLst1 != gitBlobLst2) { - printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName() - + " DIFFER: [" + gitBlobLst1.join(',') + "],[" + printCriticalErrorMsg(dirPath1 + " AND " + dirPath2 + + " HAVE DIFFERENT BLOBS: [" + gitBlobLst1.join(',') + "],[" + gitBlobLst2.join(',') + "]"); return false; } else { - printInfoMsg(dir1.dirName() + " AND " + dir2.dirName() + printInfoMsg(dirPath1 + " AND " + dirPath2 + " CONTAIN SAME GIT-BLOBS FOR FILES: [" + fileNameLst1.join(',') + "]"); } diff --git a/utils.h b/UpdatePTUDevCtrl/utils.h similarity index 85% rename from utils.h rename to UpdatePTUDevCtrl/utils.h index 1e77a17..a5c93c1 100644 --- a/utils.h +++ b/UpdatePTUDevCtrl/utils.h @@ -9,9 +9,13 @@ #include #include #include +#include namespace Utils { int read1stLineOfFile(QString fileName); + QString getLocation(QString fileName); + QString getTariffVersion(QString fileName); + QString getTariffInfo(QString fileName); QString zoneName(quint8 i); void printCriticalErrorMsg(QString const &errorMsg, bool upper=false, bool lower=false); void printCriticalErrorMsg(QStringList const &errorMsg); @@ -30,6 +34,8 @@ namespace Utils { QString getParentName(); bool isATBQTRunning(); + + QVector> installedPackages(); } #endif // UTILS_H_INCLUDED diff --git a/worker.cpp b/UpdatePTUDevCtrl/worker.cpp similarity index 60% rename from worker.cpp rename to UpdatePTUDevCtrl/worker.cpp index 239b562..c04fb0e 100644 --- a/worker.cpp +++ b/UpdatePTUDevCtrl/worker.cpp @@ -17,13 +17,16 @@ #include #include #include +#include +#include #include "message_handler.h" -#include "plugins/interfaces.h" +#include #include "ismas/ismas_client.h" #include "progress_event.h" #include "mainwindow.h" #include "utils.h" +#include "process/command.h" QString const Worker::UPDATE_STEP_OK ( " [ ok]"); QString const Worker::UPDATE_STEP_DONE ( " [done]"); @@ -31,6 +34,8 @@ QString const Worker::UPDATE_STEP_WRONG ( "[WRONG]"); QString const Worker::UPDATE_STEP_FAIL ( " [FAIL]"); QString const Worker::UPDATE_STEP_SUCCESS(" [SUCCESS]"); +bool Worker::sendLastVersionOnce = false; + using UPDATE_STEP = Worker::UPDATE_STEP; const QMap Worker::smap ( std::initializer_list>{ @@ -56,6 +61,9 @@ const QMap Worker::smap ( INSERT_ELEMENT(UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE), INSERT_ELEMENT(UPDATE_STEP::INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER), INSERT_ELEMENT(UPDATE_STEP::INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER), + INSERT_ELEMENT(UPDATE_STEP::PULL_NEW_BRANCH), + INSERT_ELEMENT(UPDATE_STEP::PULL_NEW_BRANCH_FAILURE), + INSERT_ELEMENT(UPDATE_STEP::PULL_NEW_BRANCH_SUCCESS), INSERT_ELEMENT(UPDATE_STEP::CHECKOUT_BRANCH), INSERT_ELEMENT(UPDATE_STEP::CHECKOUT_BRANCH_SUCCESS), INSERT_ELEMENT(UPDATE_STEP::CHECKOUT_BRANCH_FAILURE), @@ -83,6 +91,15 @@ const QMap Worker::smap ( INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CONFIG_FILE), INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CONFIG_FILE_SUCCESS), INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CONFIG_FILE_FAILURE), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CASH_FILE), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CASH_FILE_FAILURE), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_CASH_FILE_SUCCESS), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_FILE), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_FILE_FAILURE), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_FILE_SUCCESS), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_JSON_FILE), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE), + INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_JSON_FILE_SUCCESS), INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_CONTROLLER), INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_CONTROLLER_SUCCESS), INSERT_ELEMENT(UPDATE_STEP::DOWNLOAD_DEVICE_CONTROLLER_FAILURE), @@ -96,12 +113,15 @@ const QMap Worker::smap ( INSERT_ELEMENT(UPDATE_STEP::SAVE_LOGS_SUCCESS), INSERT_ELEMENT(UPDATE_STEP::SAVE_LOGS_FAILURE), INSERT_ELEMENT(UPDATE_STEP::SEND_LAST_VERSION), + INSERT_ELEMENT(UPDATE_STEP::UPDATE_FINALIZE), INSERT_ELEMENT(UPDATE_STEP::UPDATE_SUCCEEDED), + INSERT_ELEMENT(UPDATE_STEP::UPDATE_NOT_NECESSARY), INSERT_ELEMENT(UPDATE_STEP::UPDATE_FAILED), INSERT_ELEMENT(UPDATE_STEP::UPDATE_ACTIVATED), INSERT_ELEMENT(UPDATE_STEP::FINISHED), INSERT_ELEMENT(UPDATE_STEP::DEBUG), - INSERT_ELEMENT(UPDATE_STEP::ERROR) + INSERT_ELEMENT(UPDATE_STEP::ERROR), + INSERT_ELEMENT(UPDATE_STEP::NONE) #undef INSERT_ELEMENT }); @@ -123,7 +143,7 @@ Worker::Worker(int customerNr, char const *serialInterface, char const *baudrate) : m_customerNr(customerNr) - , m_customerNrStr(QString("customer_") + QString::number(m_customerNr).rightJustified(3, '0')) + , m_customerNrStr(QString("customer_") + QString::number(m_customerNr)) , m_machineNr(machineNr) , m_zoneNr(zoneNr) , m_pluginDir(pluginDir) @@ -139,7 +159,8 @@ Worker::Worker(int customerNr, , m_parent(parent) , m_serialInterface(serialInterface) , m_baudrate(baudrate) - , m_gc(m_customerNrStr, m_customerRepository, m_workingDirectory, m_branchName, this) + , m_gc(m_customerRepositoryPath, m_customerNrStr, m_customerRepository, m_workingDirectory, m_branchName, this) + , m_versionInfo(QStringList()) , m_osVersion(getOsVersion()) , m_atbqtVersion(getATBQTVersion()) , m_atbUpdateToolVersion(getATBUpdateToolVersion()) @@ -156,11 +177,27 @@ Worker::Worker(int customerNr, , m_filesToUpdate() , m_updateProcessRunning(true) , m_mainWindow(nullptr) /* contains plugin */ + , m_dcDownloadFirmware(new Command("/opt/app/tools/atbupdate/ATBDownloadDCFirmware --read-dc-version true")) //, m_withoutIsmasDirectPort(true) /* useful for testing */ { , m_withoutIsmasDirectPort(false) /* useful for testing */ { + + m_start = QDateTime::currentDateTime(); + m_dcDownloadFirmware->setWorker(this); + // TODO: turn object into singleton instance = this; + m_lastFailedUpdateStep = UPDATE_STEP::NONE; + + if (m_noUpdatePsaHardware == false) { + m_update = new Update(this, + QDir::cleanPath(m_workingDirectory + QDir::separator() + m_customerNrStr), + m_customerNrStr, + m_branchName, + m_pluginDir, + m_pluginName, + m_workingDirectory); + } this->setObjectName("worker-object"); QDir::setCurrent(m_workingDirectory); @@ -169,6 +206,10 @@ Worker::Worker(int customerNr, } Worker::~Worker() { + if (m_update != nullptr) { + delete m_update; + m_update = nullptr; + } } void Worker::displayProgressInMainWindow(int progress) { @@ -249,20 +290,79 @@ void Worker::privateUpdate() { return; } + //return; + QString func(__PRETTY_FUNCTION__); GUI() << (ISMAS() << (CONSOLE() << UPDATE_STEP::STARTED)); QScopedPointer upr(new UpdateProcessRunning(this)); + QStringList lst; + + //////////////////////////////////////////////////////////////////////////// + // + // CHECK UPDATE TRIGGER + // + //////////////////////////////////////////////////////////////////////////// + // NOTE: make sure that nothing is sent to ISMAS during updateTriggerSet + ISMAS() << UPDATE_STEP::CHECK_ISMAS_TRIGGER; + m_ismasTriggerActive = false; + m_updateNotNecessary = false; + + if (QDir(m_customerRepository).exists()) { // ignore a possibly corrupted repository + m_ismasTriggerActive = updateTriggerSet(); + if (m_ismasTriggerActive == false) { + QDateTime const ¤t = QDateTime::currentDateTime(); + m_automaticUpdate = (current.time().hour() < 4); + m_versionInfo = m_gc.gitShowReason(m_branchName); + + qCritical() << "***"; + qCritical() << "privateUpdate ............. m_versionInfo:" << m_versionInfo; + qCritical() << "privateUpdate ......... m_automaticUpdate:" << m_automaticUpdate; + + if (m_automaticUpdate) { // update has been triggered within [00:00:00, 00:03:59] + m_updateNotNecessary = true; + m_ismasTriggerStatusMessage = QStringList(QString("NO UPDATE NECESSARY (%1)").arg(current.toString(Qt::ISODate))); + + qCritical() << "privateUpdate m_ismasTriggerStatusMessage:" + << QStringList(m_ismasTriggerStatusMessage); + qCritical() << "***"; + + // the customer-repository does exist, and the ISMAS-trigger is + // *NOT* "WAIT", but from 00:00:00 - 00:03:59 this counts as an + // automatic update + + QStringList lst = m_ismasTriggerStatusMessage; + // trigger message to ISMAS and CONSOLE + CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_SUCCESS); + // overwrite m_lastFailedUpdateStep + m_lastFailedUpdateStep = UPDATE_STEP::NONE; + return; + } + + qCritical() << "***"; + + // the customer-repository does exist, but the ISMAS-trigger is + // *NOT* "WAIT", so STOP the update procedure + return; + } + // the customer-repository does exist, and the ISMAS-trigger is "WAIT", + // so continue the update procedure + } else { + // the customer-repository does not exist, so PROCEED with the + // update procedure, even if ISMAS-trigger is not correctly set ("WAIT") + } + + emit this->disableExit(); QDir customerRepository(m_customerRepository); - QDir customerRepositoryEtc(QDir::cleanPath(m_customerRepository + QDir::separator() + "etc/")); - CONSOLE() << UPDATE_STEP::CHECK_SANITY; + CONSOLE() << (ISMAS() << UPDATE_STEP::CHECK_SANITY); m_clone = false; m_repairClone = false; m_initialClone = false; + m_pulledNewBranch = false; // the customer repository is cloned or // repaired/re-cloned without checking the // ISMAS-trigger (WAIT-)button. @@ -281,26 +381,27 @@ void Worker::privateUpdate() { if (isRepositoryCorrupted()) { // a not-existing repository is not meant // to be corrupted - CONSOLE() << UPDATE_STEP::CHECK_SANITY_FAILURE; + CONSOLE() << (ISMAS() << UPDATE_STEP::CHECK_SANITY_FAILURE); if ((continueUpdate = repairCorruptedRepository()) == true) { m_repairClone = true; - CONSOLE() << UPDATE_STEP::REPOSITORY_RECOVERED_SUCCESS; + CONSOLE() << (ISMAS() << UPDATE_STEP::REPOSITORY_RECOVERED_SUCCESS); } else { ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::REPOSITORY_RECOVERED_FAILURE)); return; } } - CONSOLE() << UPDATE_STEP::CHECK_SANITY_SUCCESS; + CONSOLE() << (ISMAS() << UPDATE_STEP::CHECK_SANITY_SUCCESS); if (continueUpdate) { if ((continueUpdate = customerRepository.exists()) == false) { m_initialClone = (m_repairClone == false); - GUI() << (CONSOLE() << UPDATE_STEP::CLONE_REPOSITORY); + ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::CLONE_REPOSITORY)); for (int i = 0; i < 5; ++i) { // try to checkout git repository setProgress(i); // and switch to branch if (m_gc.gitCloneAndCheckoutBranch()) { if (!isRepositoryCorrupted()) { + m_versionInfo = m_gc.gitShowReason(m_branchName); GUI() << (ISMAS() << (CONSOLE() << UPDATE_STEP::CLONE_REPOSITORY_SUCCESS)); continueUpdate = true; m_clone = true; @@ -311,7 +412,7 @@ void Worker::privateUpdate() { } if (continueUpdate == false) { - GUI() << (ISMAS() << (CONSOLE() << UPDATE_STEP::CLONE_REPOSITORY_FAILURE)); + GUI() << (ISMAS() << (CONSOLE() << (m_lastFailedUpdateStep = UPDATE_STEP::CLONE_REPOSITORY_FAILURE))); return; } @@ -325,23 +426,18 @@ void Worker::privateUpdate() { CONSOLE() << UPDATE_STEP::CHECK_REPOSITORY; if (isRepositoryCorrupted()) { - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::CHECK_REPOSITORY_FAILURE)); + ISMAS() << (GUI() << (CONSOLE() << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_REPOSITORY_FAILURE))); return; } } } + m_versionInfo = m_gc.gitShowReason(m_branchName); + CONSOLE() << UPDATE_STEP::CHECK_REPOSITORY_SUCCESS; setProgress(_CHECKOUT_REPOSITORY_SUCCESS); - - //////////////////////////////////////////////////////////////////////////// - // - // CHECK UPDATE TRIGGER - // - //////////////////////////////////////////////////////////////////////////// - m_ismasTriggerActive = false; if (m_clone == false) { - if ((continueUpdate = updateTriggerSet()) == false) { + if (m_ismasTriggerActive == false) { return; } else { GUI() << (ISMAS() << (CONSOLE() << UPDATE_STEP::CHECK_ISMAS_TRIGGER_SUCCESS)); @@ -353,9 +449,6 @@ void Worker::privateUpdate() { } } - qCritical() << "**** m_ismasTriggerActive:" << m_ismasTriggerActive; - qCritical() << "**** m_clone:" << m_clone; - if (m_ismasTriggerActive == false) {// make it explicit again: only if the // ismas trigger is active ('WAIT'), // then proceed @@ -372,9 +465,19 @@ void Worker::privateUpdate() { // //////////////////////////////////////////////////////////////////////////// if ((continueUpdate = customerEnvironment()) == false) { + // even if something goes wrong creating the environment, try to execute + // opkg_commands + if (QDir(m_customerRepository).exists()) { + // always execute contents of opkg_commands-file + m_filesToUpdate.clear(); + m_filesToUpdate << "etc/psa_update/opkg_commands"; + execOpkgCommands(); + } return; } - CONSOLE() << UPDATE_STEP::CHECKOUT_BRANCH_SUCCESS; + m_versionInfo = m_gc.gitShowReason(m_branchName); + lst = QStringList(QString(smap[UPDATE_STEP::CHECKOUT_BRANCH_SUCCESS])); + ISMAS(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECKOUT_BRANCH); setProgress(_CHECKOUT_BRANCH_SUCCESS); @@ -384,11 +487,41 @@ void Worker::privateUpdate() { // //////////////////////////////////////////////////////////////////////////// if ((continueUpdate = filesToUpdate()) == false) { + // even if something goes wrong in filesToUpdate, try to execute + // opkg_commands + if (QDir(m_customerRepository).exists()) { + // always execute contents of opkg_commands-file + m_filesToUpdate.clear(); + m_filesToUpdate << "etc/psa_update/opkg_commands"; + execOpkgCommands(); + } return; } - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::UPDATE_REPOSITORY_SUCCESS)); + m_versionInfo = m_gc.gitShowReason(m_branchName); + lst = QStringList(QString(smap[UPDATE_STEP::UPDATE_REPOSITORY_SUCCESS])); + ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::UPDATE_REPOSITORY)); setProgress(_UPDATE_REPOSITORY_SUCCESS); + //////////////////////////////////////////////////////////////////////////// + // + // (R)SYNC THE REPOSITORY WITH THE LOCAL FILEYSTEM + // + //////////////////////////////////////////////////////////////////////////// + if ((continueUpdate = syncCustomerRepositoryAndFS()) == false) { + // even if something goes wrong with rsync, try to execute + // opkg_commands + if (QDir(m_customerRepository).exists()) { + // always execute contents of opkg_commands-file + m_filesToUpdate.clear(); + m_filesToUpdate << "etc/psa_update/opkg_commands"; + execOpkgCommands(); + } + return; + } + lst = QStringList(QString(smap[UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS])); + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS)); + setProgress(_SYNC_CUSTOMER_REPOSITORY_SUCCESS); + //////////////////////////////////////////////////////////////////////////// // @@ -398,7 +531,8 @@ void Worker::privateUpdate() { if ((continueUpdate = execOpkgCommands()) == false) { return; } - GUI() << (CONSOLE() << UPDATE_STEP::EXEC_OPKG_COMMAND_SUCCESS); + lst = QStringList(QString(smap[UPDATE_STEP::EXEC_OPKG_COMMAND_SUCCESS])); + GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::EXEC_OPKG_COMMANDS); setProgress(_EXEC_OPKG_COMMAND_SUCCESS); @@ -407,24 +541,12 @@ void Worker::privateUpdate() { // UPDATE THE PSA USING THE CHANGED FILES // //////////////////////////////////////////////////////////////////////////// - if ((continueUpdate = downloadFilesToPSAHardware()) == false) { - return; - } - GUI() << (CONSOLE() << UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE_SUCCESS); - setProgress(_DOWNLOAD_FILES_TO_PSA_HARDWARE_SUCCESS); - - - //////////////////////////////////////////////////////////////////////////// - // - // (R)SYNC THE REPOSITORY WITH THE LOCAL FILEYSTEM - // - //////////////////////////////////////////////////////////////////////////// - if ((continueUpdate = syncCustomerRepositoryAndFS()) == false) { - return; - } - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS)); - setProgress(_SYNC_CUSTOMER_REPOSITORY_SUCCESS); - + // for 281/Szeged: skip uploading files to DeviceController + //if ((continueUpdate = downloadFilesToPSAHardware()) == false) { + // return; + //} + lst = QStringList(QString("DONE")); + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE_SUCCESS)); //////////////////////////////////////////////////////////////////////////// // @@ -437,28 +559,8 @@ void Worker::privateUpdate() { // ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::SAVE_LOGS_SUCCESS)); setProgress(_SAVE_LOGS_SUCCESS); - - //////////////////////////////////////////////////////////////////////////// - // - // FINAL MESSAGES (PART 1) - // - //////////////////////////////////////////////////////////////////////////// - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::UPDATE_SUCCEEDED)); - setProgress(_UPDATE_SUCCEEDED); - - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::UPDATE_ACTIVATED)); - setProgress(_UPDATE_ACTIVATED); - - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::FINISHED)); - setProgress(_FINISHED); - - - //////////////////////////////////////////////////////////////////////////// - // - // FINAL MESSAGES (PART 2): SEND-LAST-VERSION - // (destructor of struct UpdateProcessRunning) - // - //////////////////////////////////////////////////////////////////////////// + // final messages: see destructor of UpdateProcessRunning subclass + m_lastFailedUpdateStep = UPDATE_STEP::NONE; } bool Worker::updateTriggerSet() { @@ -466,11 +568,15 @@ bool Worker::updateTriggerSet() { // (WAIT-button) is activated even in case of initial checkout static const QString func = "UPDATE-TRIGGER-SET"; - if (m_withoutIsmasDirectPort) { // useful for testing - return true; - } + // if (m_withoutIsmasDirectPort) { // useful for testing + // return true; + //} - GUI() << (ISMAS() << (CONSOLE() << UPDATE_STEP::CHECK_ISMAS_TRIGGER)); + m_ismasTriggerStatusMessage.clear(); + + GUI() << (CONSOLE() << UPDATE_STEP::CHECK_ISMAS_TRIGGER); + + bool const automaticUpdate = (QDateTime::currentDateTime().time().hour() < 4); QString triggerValue("NOT CHECKED YET"); static constexpr int const repeats = 15; @@ -480,20 +586,11 @@ bool Worker::updateTriggerSet() { int const startMs = QTime::currentTime().msecsSinceStartOfDay(); int const durationMs = QTime::currentTime().msecsSinceStartOfDay() - startMs; QString const &s = QString("elapsed: %1.%2s").arg(durationMs / 1000).arg(durationMs % 1000); - CONSOLE(QStringList(func) << s) << UPDATE_STEP::DEBUG; - } else { - QStringList lst = (QStringList(func) << QString("-> REPEAT=%1 (%2)").arg(repeat).arg(repeats-repeat)); - // GUI(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER; + QStringList lst = (m_ismasTriggerStatusMessage = (QStringList(func) << s)); + CONSOLE(lst) << UPDATE_STEP::DEBUG; + } else { + QStringList lst = (m_ismasTriggerStatusMessage = (QStringList(func) << QString("-> REPEAT=%1 (%2)").arg(repeat).arg(repeats-repeat))); CONSOLE(lst) << UPDATE_STEP::DEBUG; - } - - if ((repeat % 8) == 0) { - CONSOLE(QStringList(func) << "RESTART APISM") << UPDATE_STEP::DEBUG; - Command c("systemctl restart apism"); - if (c.execute("/tmp")) { - QThread::sleep(20); // give APISM some time to reconnect - CONSOLE(QStringList(func) << "RESTART APISM DONE") << UPDATE_STEP::DEBUG; - } } if (std::optional result @@ -501,31 +598,35 @@ bool Worker::updateTriggerSet() { IsmasClient::APISM::DIRECT_PORT, "#M=APISM#C=REQ_ISMASPARAMETER#J={}")) { QString const &msg = QString("APISM RESPONSE(%1)=(").arg(repeat) + result.value() + ")"; - CONSOLE(QStringList(func) << msg) << UPDATE_STEP::DEBUG; + QStringList lst = (m_ismasTriggerStatusMessage = (QStringList(func) << msg)); + CONSOLE(lst) << UPDATE_STEP::DEBUG; QJsonParseError parseError; QJsonDocument document(QJsonDocument::fromJson(result.value().toUtf8(), &parseError)); if (parseError.error != QJsonParseError::NoError) { - QStringList lst(QString("INVALID JSON MSG: PARSING FAILED (json=%1 error=[%2] str=[%3] offset=[%4])") - .arg(msg) - .arg(parseError.error) - .arg(parseError.errorString()) - .arg(parseError.offset)); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("INVALID JSON MSG: PARSING FAILED (json=%1 error=[%2] str=[%3] offset=[%4])") + .arg(msg) + .arg(parseError.error) + .arg(parseError.errorString()) + .arg(parseError.offset)); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } if (!document.isObject()) { - QStringList lst(QString("not a json-object %1").arg(result.value())); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("not a json-object %1").arg(result.value())); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } QJsonObject obj = document.object(); // always look for an 'error' first if (obj.contains("error")) { - QStringList lst(obj.value("error").toString()); - CONSOLE(QStringList(lst)) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE; + m_ismasTriggerStatusMessage = QStringList(obj.value("error").toString()); + QStringList lst = m_ismasTriggerStatusMessage; + CONSOLE(QStringList(lst)) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); QThread::sleep(6); continue; } @@ -540,31 +641,35 @@ bool Worker::updateTriggerSet() { int const customerNr = obj.value("Custom_ID").toInt(-1); int const machineNr = obj.value("Device_ID").toInt(-1); if (customerNr != m_customerNr) { - QStringList lst(QString("CUSTOMER-NR (%1) != LOCAL CUSTOMER-NR (%2)") - .arg(customerNr).arg(m_customerNr)); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("CUSTOMER-NR (%1) != LOCAL CUSTOMER-NR (%2)") + .arg(customerNr).arg(m_customerNr)); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } if (machineNr != m_machineNr) { - QStringList lst(QString("MACHINE-NR (%1) != LOCAL MACHINE-NR (%2)") - .arg(machineNr).arg(m_machineNr)); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("MACHINE-NR (%1) != LOCAL MACHINE-NR (%2)") + .arg(machineNr).arg(m_machineNr)); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } } else { - QStringList lst("Dev_ID DOES NOT CONTAIN Custom_ID AND/OR Device_ID"); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + QStringList lst(QString("Dev_ID DOES NOT CONTAIN Custom_ID AND/OR Device_ID (LINE=%1)").arg(__LINE__)); + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } } else { - QStringList lst("Dev_ID KEY NOT A JSON-OBJECT"); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("Dev_ID KEY NOT A JSON-OBJECT (LINE=%1)").arg(__LINE__)); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } } else { - QStringList lst("Dev_ID KEY NOT AVAILABLE"); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("Dev_ID KEY NOT AVAILABLE (LINE=%1)").arg(__LINE__)); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } if (obj.contains("Fileupload")) { @@ -573,56 +678,86 @@ bool Worker::updateTriggerSet() { obj = v.toObject(); if (obj.contains("TRG")) { if ((triggerValue = obj.value("TRG").toString()) == "WAIT") { + m_ismasTriggerStatusMessage = QStringList("ISMAS_UPDATE-TRIGGER SET TO WAIT"); m_ismasTriggerActive = true; return m_ismasTriggerActive; } else if (QRegExp("\\s*").exactMatch(triggerValue)) { // check for whitespace - QStringList lst(QString("%1 empty update trigger (%2)").arg(repeat).arg(repeats-repeat)); + m_ismasTriggerStatusMessage = QStringList(QString("%1 EMPTY UPDATE TRIGGER (%2)").arg(repeat).arg(repeats-repeat)); + QStringList lst = m_ismasTriggerStatusMessage; if (m_clone) { - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE); + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE))); // if the customer repository has just been cloned - break; // it is OK the ISMAS trigger might not be 'WAIT' - } else { - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); + return false; // it is OK the ISMAS trigger might not be 'WAIT' } + // not a clone and empty update-trigger + if (automaticUpdate) { + // do not inform ISMAS in case of automatic update, because the + // update is not necessary as the trigger-button is not set to WAIT. + GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE)); + return false; + } + + CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); + QThread::sleep(6); continue; } else { - // if the download-button once has the wrong value, it will never recover + // if the download-button once has a wrong value, it will never recover if (m_clone) { - GUI() << (CONSOLE() << UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE); + m_ismasTriggerStatusMessage = QStringList(QString("TRIGGER-VALUE='%1' != 'WAIT'").arg(triggerValue)); + QStringList lst = m_ismasTriggerStatusMessage; + if (automaticUpdate) { + // do not inform ISMAS in case of automatic update + GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE)); + } else { + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE))); + } } else { - QStringList lst(QString("TRIGGER-VALUE=<%1> NOT 'WAIT'").arg(triggerValue)); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); + m_ismasTriggerStatusMessage = QStringList(QString("TRIGGER-VALUE='%1' != 'WAIT'").arg(triggerValue)); + QStringList lst = m_ismasTriggerStatusMessage; + if (automaticUpdate) { + // do not inform ISMAS in case of automatic update + GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE)); + } else { + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + } } - break; + return false; } } else { - QStringList lst("TRG key not available"); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("TRG key not available (LINE=%1)").arg(__LINE__)); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } } else { - QStringList lst("Fileupload not a json-object"); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("Fileupload not a json-object (LINE=%1)").arg(__LINE__)); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } } else { - QStringList lst(QString("Fileupload not available")); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); - break; + m_ismasTriggerStatusMessage = QStringList(QString("Fileupload not available (LINE=%1)").arg(__LINE__)); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } } else { - QStringList lst = QStringList(QString("no ISMAS response")); - GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); + m_ismasTriggerStatusMessage = QStringList(QString("no ISMAS response (LINE=%1)").arg(__LINE__)); + QStringList lst = m_ismasTriggerStatusMessage; + CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE); QThread::sleep(6); + continue; } } if (m_initialClone == false) { if (!triggerValue.contains("WAIT", Qt::CaseInsensitive)) { - QStringList lst(QString("ISMAS_UPDATE-TRIGGER-NOT-SET-OR-WRONG: VALUE=(") + triggerValue + ")"); - ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE)); + m_ismasTriggerStatusMessage = QStringList(QString("ISMAS_UPDATE-TRIGGER-NOT-SET-OR-WRONG: VALUE=(") + triggerValue + ")"); + QStringList lst = m_ismasTriggerStatusMessage; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE))); + return false; } } @@ -636,15 +771,43 @@ bool Worker::customerEnvironment() { ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::CHECKOUT_BRANCH)); if (QDir(m_customerRepository).exists()) { + if (m_clone == false) { + if (m_gc.branchExistsRemotely()) { + QString msg; + QStringList lst; + if (!m_gc.branchExistsLocally()) { + lst.clear(); + msg = QString("PULLING OF NEW BRANCH " + m_branchName + " DOES NOT EXIST LOCALLY"); + lst << msg; + CONSOLE(lst) << UPDATE_STEP::PULL_NEW_BRANCH; + if (!m_gc.gitPullNewBranches()) { + lst.clear(); + msg = QString("PULLING OF NEW BRANCH " + m_branchName + " FAILED"); + lst << msg; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::PULL_NEW_BRANCH_FAILURE))); + return false; + } else { + lst.clear(); + msg = QString("PULLING OF NEW BRANCH " + m_branchName + " SUCCESS"); + lst << msg; + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::PULL_NEW_BRANCH_SUCCESS))); + m_pulledNewBranch = true; + } + } else { + m_pulledNewBranch = false; + } + } + } + if (m_gc.gitCheckoutBranch()) { return true; } else { QStringList lst(QString("CHECKOUT OF " + m_customerRepository + "FAILED")); - ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECKOUT_BRANCH_FAILURE)); + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECKOUT_BRANCH_FAILURE))); } } else {// cannot happen QStringList lst(QString(m_customerRepository + " DOES NOT EXIST")); - ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::CHECKOUT_BRANCH_FAILURE)); + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::CHECKOUT_BRANCH_FAILURE))); } return false; @@ -659,7 +822,7 @@ bool Worker::filesToUpdate() { // always execute contents of opkg_commands-file m_filesToUpdate << "etc/psa_update/opkg_commands"; - if (m_clone && m_alwaysDownloadConfig) { + if ((m_clone || m_pulledNewBranch) && m_alwaysDownloadConfig) { // always download all json-config files, even if none of them have been // changed in the git repository. useful for first installation. QDir dir(QDir::cleanPath(m_customerRepository + QDir::separator() + "etc/psa_config")); @@ -673,7 +836,7 @@ bool Worker::filesToUpdate() { } } - if (m_clone && m_alwaysDownloadDC) { + if ((m_clone || m_pulledNewBranch) && m_alwaysDownloadConfig) { // always download the last dc-binary, even if not changed in the // git repository. useful for first installation. QDir dir(QDir::cleanPath(m_customerRepository + QDir::separator() + "etc/dc")); @@ -694,12 +857,12 @@ bool Worker::filesToUpdate() { } m_filesToUpdate.removeDuplicates(); - qCritical() << __PRETTY_FUNCTION__ << "FILES-TO-UPDATE" << m_filesToUpdate; + qCritical() << "(" << __func__ << ":" << __LINE__ << ") FILES-TO-UPDATE" << m_filesToUpdate; GUI(m_filesToUpdate) << (CONSOLE(m_filesToUpdate) << UPDATE_STEP::FILES_TO_UPDATE); setProgress(_FILES_TO_UPDATE); } else { - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::UPDATE_REPOSITORY_FAILURE)); + ISMAS() << (GUI() << (CONSOLE() << (m_lastFailedUpdateStep = UPDATE_STEP::UPDATE_REPOSITORY_FAILURE))); return false; } @@ -725,7 +888,24 @@ bool Worker::computeFilesToDownload() { return (m_filesToDownload.size() > 0); } + +bool Worker::cleanUpOpkgCache() { + bool removedFiles = true; + QDir dir("/var/cache/opkg"); + if (dir.exists()) { + dir.setNameFilters(QStringList() << ".gz" << ".ipk"); + dir.setFilter(QDir::Files); + foreach(QString dirFile, dir.entryList()) { + removedFiles &= dir.remove(dirFile); + } + } + return removedFiles; +} + bool Worker::execOpkgCommands() { + if (!cleanUpOpkgCache()) { + CONSOLE() << "INFO: some cached opkg files not removed"; + } for (int i = 0; i < m_filesToUpdate.size(); ++i) { QString const fName = m_filesToUpdate.at(i); if (fName.contains("opkg_commands", Qt::CaseInsensitive)) { @@ -740,10 +920,13 @@ bool Worker::execOpkgCommands() { QStringList opkgErrorLst; while (!in.atEnd()) { QString line = in.readLine(); - static const QRegularExpression comment("^\\s*#.*$"); - if (line.indexOf(comment, 0) == -1) { - // found opkg command + // TODO: "^\\s*[#]{0,}$" : empty line or comment line starting with # + static const QRegularExpression comment("^\\s*[#].*$"); + static const QRegularExpression emptyLine("^\\s*$"); + if (line.indexOf(emptyLine, 0) == -1 && + line.indexOf(comment, 0) == -1) { QString opkgCommand = line.trimmed(); + qCritical() << "Found opkg-command" << opkgCommand; if (!executeOpkgCommand(opkgCommand)) { opkgErrorLst << opkgCommand; } else { @@ -807,7 +990,7 @@ bool Worker::execOpkgCommands() { } } else { m_displayIndex = 1; - ISMAS(opkgErrorLst) << (GUI(opkgErrorLst) << (CONSOLE() << UPDATE_STEP::EXEC_OPKG_COMMAND_FAILURE)); + ISMAS(opkgErrorLst) << (GUI(opkgErrorLst) << (CONSOLE() << (m_lastFailedUpdateStep = UPDATE_STEP::EXEC_OPKG_COMMAND_FAILURE))); GUI() << UPDATE_STEP::EXEC_OPKG_COMMAND_FAILURE; setProgress(_EXEC_OPKG_COMMAND_FAILURE); return false; @@ -822,23 +1005,19 @@ bool Worker::execOpkgCommands() { bool Worker::downloadFilesToPSAHardware() { m_displayIndex = 0; + QStringList lst(QString("START")); - GUI(m_filesToDownload) << (CONSOLE(m_filesToDownload) << UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE); - setProgress(_DOWNLOAD_FILES_TO_PSA_HARDWARE); + ISMAS(lst) << (GUI(m_filesToDownload) << (CONSOLE(m_filesToDownload) << UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE)); if (m_noUpdatePsaHardware == false) { - if (computeFilesToDownload()) { - CONSOLE(m_filesToDownload) << UPDATE_STEP::FILES_TO_DOWNLOAD; + if (computeFilesToDownload() > 0) { + lst.clear(); + for (int i = 0; i < m_filesToDownload.size(); ++i) { + lst << QFileInfo(m_filesToDownload.at(i)).fileName(); + } + ISMAS(lst) << (CONSOLE(lst) << UPDATE_STEP::FILES_TO_DOWNLOAD); - Update update(this, - QDir::cleanPath(m_workingDirectory + QDir::separator() + m_customerNrStr), - m_customerNrStr, - m_branchName, - m_pluginDir, - m_pluginName, - m_workingDirectory); - - if (update.doUpdate(m_displayIndex, m_filesToDownload)) { + if (m_update && m_update->doUpdate(m_displayIndex, m_filesToDownload)) { // prepared for use: at the moment, the dc-library does not work // as expected. @@ -847,10 +1026,11 @@ bool Worker::downloadFilesToPSAHardware() { return true; } - return false; - } else { - CONSOLE(QStringList("NO FILES TO DOWNLOAD TO PSA-HW")) << UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE_FAILURE; + + ISMAS(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE_FAILURE)); setProgress(_DOWNLOAD_FILES_TO_PSA_HARDWARE_FAILURE); + + return false; } } @@ -858,14 +1038,20 @@ bool Worker::downloadFilesToPSAHardware() { } bool Worker::syncCustomerRepositoryAndFS() { - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY)); + QString msg("START"); + QStringList lst(msg); + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY)); + lst.clear(); if (QDir(m_customerRepository).exists()) { if (QDir::setCurrent(m_customerRepository)) { Command md("bash"); if (!md.execute(m_customerRepository, QStringList() << "-c" << "mkdir -p /etc/psa_config /etc/dc /etc/psa_tariff")) { - qCritical() << "COULD NOT EXECUTE '" << md.command() << "' exitCode=(" << md.exitCode() << ")"; + msg = QString("COULD NOT EXECUTE '%1', exitCode=%2").arg(md.command()).arg(md.exitCode()); + qCritical() << msg; + QStringList lst2(msg); + ISMAS(lst2) << (CONSOLE(lst2) << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY); } QString const params("-v " "--recursive " @@ -924,20 +1110,36 @@ bool Worker::syncCustomerRepositoryAndFS() { error = true; } } + if (!error) { + lst.clear(); + lst << QString("SUCCESS %1").arg(cmd); + ISMAS(lst) << (CONSOLE(lst) << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY); + } else { + msg = QString("FAILURE %1").arg(cmd); + lst << msg; + QStringList lst2(msg); + ISMAS(lst2) << (CONSOLE(lst2) << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY); + } } if (!error) { // now check tariff-files in etc and /etc/psa_tariff QDir dir1(QDir::cleanPath(m_customerRepository + QDir::separator() + "etc/psa_tariff")); QDir dir2("/etc/psa_tariff"); - if (Utils::sameFilesInDirs(dir1, dir2)) { - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS)); - setProgress(_SYNC_CUSTOMER_REPOSITORY_SUCCESS); - return true; + if (Utils::sameFilesInDirs(dir1, dir2) == false) { + CONSOLE() << QDir::cleanPath(m_customerRepository + QDir::separator() + "etc/psa_tariff") + << "AND /etc/psa_tariff ARE DIFFERENT: CHANGED CUSTOMER-NUMBER?"; } + CONSOLE() << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS; + setProgress(_SYNC_CUSTOMER_REPOSITORY_SUCCESS); + return true; } + } else { + lst << QString("CAN NOT CD TO CUST-REPO %1").arg(m_customerRepository); } + } else { + lst << QString("CUST-REPO %1 DOES NOT EXIST").arg(m_customerRepository); } - ISMAS() << (GUI() << (CONSOLE() << UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_FAILURE)); + ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << (m_lastFailedUpdateStep = UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_FAILURE))); setProgress(_SYNC_CUSTOMER_REPOSITORY_FAILURE); return false; } @@ -970,7 +1172,7 @@ QString Worker::getATBUpdateToolYoctoVersion() { QString Worker::getAPISMYoctoVersion() { if (QFile::exists("/var/lib/opkg/status")) { - QString const cmd = QString("echo -n $(cat /var/lib/opkg/status | grep -A1 apism | tail -n 1 | cut -d':' -f2 | cut -d' ' -f2)"); + QString const cmd = QString("echo -n $(cat /var/lib/opkg/status | grep -A1 -e apism[[:space:]]*$ | tail -n 1 | cut -d':' -f2 | cut -d' ' -f2)"); Command c("bash"); if (c.execute("/tmp", QStringList() << "-c" << cmd)) { return c.getCommandResult(); // 1.4.1.0-r4 @@ -1001,6 +1203,17 @@ QString Worker::getAPISMYoctoInstallationStatus() { return "N/A"; } +QString Worker::getDCVersionPreparedForDownload(QString const &filename) { + if (QFile::exists(filename)) { // + QString const cmd = QString("strings %1 | grep -e DC2[Cc]\\. | head -n1").arg(filename); + Command c("bash"); + if (c.execute("/tmp", QStringList() << "-c" << cmd)) { + return c.getCommandResult(); // DC2c.04.42 14.09.2023 + } + } + return "N/A"; +} + QString Worker::getATBQTVersion() const { QString const cmd = QString("echo -n $(/opt/app/ATBAPP/ATBQT -v | head -n 2 | cut -d':' -f2)"); @@ -1053,6 +1266,7 @@ QString Worker::getPluginVersion(QString const &pluginFileName) const { QStringList Worker::getDCVersion() const { QStringList lst = (QStringList() << "N/A" << "N/A"); +#if 0 Update const *up = update(); if (up) { hwinf const *caPlugin = up->hw(); @@ -1074,6 +1288,7 @@ QStringList Worker::getDCVersion() const { } } } +#endif return lst; } @@ -1118,17 +1333,53 @@ PSAInstalled Worker::getPSAInstalled() { QString absPathName; QString absPathNameRepository; - psaInstalled.versionInfo.lastCommit = ""; - psaInstalled.versionInfo.reason = ""; - psaInstalled.versionInfo.created = ""; + psaInstalled.versionInfo.lastCommit = "N/A"; + psaInstalled.versionInfo.reason = "N/A"; + psaInstalled.versionInfo.created = "N/A"; - QStringList versionInfo = m_gc.gitShowReason(m_branchName); - if (versionInfo.size() == 3) { - psaInstalled.versionInfo.lastCommit = versionInfo.at(0); - psaInstalled.versionInfo.reason = versionInfo.at(1); - psaInstalled.versionInfo.created = versionInfo.at(2); + if (m_versionInfo.size() == 3) { + QString const &lastCommit = m_versionInfo.at(0); + QString reason = m_versionInfo.at(1); + QDateTime const dt = QDateTime::fromString(m_versionInfo.at(2), Qt::ISODate); + QString version{""}; + QString date{""}; + if (dt.isValid()) { + date += " "; + date += dt.date().toString(Qt::ISODate); + } + static const QRegularExpression re("^\\s*(\\d+)\\.(\\d+)\\.(\\d+)(.*$)"); + QRegularExpressionMatch match = re.match(reason); + if (match.hasMatch()) { + int const lastCapturedIndex = match.lastCapturedIndex(); + if (lastCapturedIndex >= 1) { + version += " v"; + version += match.captured(1); // major + } + if (lastCapturedIndex >= 2) { + version += "."; + version += match.captured(2); // minor + } + if (lastCapturedIndex >= 3) { + version += "."; + version += match.captured(3); // patch + } + if (lastCapturedIndex >= 4) { // rest after version + reason = match.captured(4); + } + } + + psaInstalled.versionInfo.lastCommit = QString("%1%2").arg(lastCommit).arg(version); + psaInstalled.versionInfo.reason = reason; + psaInstalled.versionInfo.created = m_versionInfo.at(2); } + //qCritical() << ""; + //qCritical() << "VERSION-INFO"; + //qCritical() << "LAST-COMMIT" << psaInstalled.versionInfo.lastCommit; + //qCritical() << "REASON" << psaInstalled.versionInfo.reason; + //qCritical() << "CREATED" << psaInstalled.versionInfo.created; + //qCritical() << ""; + if (m_zoneNr != 0) { QString const &n = QString("%1").arg(m_zoneNr).rightJustified(2, '0'); psaInstalled.tariff.name = QString("tariff%1.json").arg(n); @@ -1139,10 +1390,10 @@ PSAInstalled Worker::getPSAInstalled() { psaInstalled.tariff.size = getFileSize(absPathName); psaInstalled.tariff.zone = m_zoneNr; psaInstalled.tariff.loadTime = Utils::getTariffLoadTime(absPathName); + psaInstalled.tariff.project = Utils::getLocation(absPathName); + psaInstalled.tariff.version = Utils::getTariffVersion(absPathName); + psaInstalled.tariff.info = Utils::getTariffInfo(absPathName); } - psaInstalled.tariff.project = "Szeged"; - psaInstalled.tariff.info = "N/A"; - psaInstalled.tariff.version = "N/A"; psaInstalled.hw.linuxVersion = getOsVersion(); psaInstalled.hw.cpuSerial = m_cpuSerial; @@ -1170,20 +1421,36 @@ PSAInstalled Worker::getPSAInstalled() { psaInstalled.pluginVersion.prmCalculatePriceConfigUi = m_pluginVersionPrmCalcConfig; psaInstalled.pluginVersion.tcpZVT = m_pluginVersionTcpZvt; + // key: conf-json-filename; value: installed version on DC + QMap map; + if (m_update) { + map = m_update->getInstalledJsonVersions(); + } + psaInstalled.cash.name = "DC2C_cash.json"; absPathName = QDir::cleanPath(printSysDir + QDir::separator() + psaInstalled.cash.name); + psaInstalled.cash.blob = m_gc.gitBlob(absPathName); psaInstalled.cash.size = getFileSize(absPathName); + if (map.contains("DC2C_cash.json")) { + psaInstalled.cash.blob = map.value("DC2C_cash.json", "inst.vers.not.avail"); + } psaInstalled.conf.name = "DC2C_conf.json"; absPathName = QDir::cleanPath(printSysDir + QDir::separator() + psaInstalled.conf.name); psaInstalled.conf.blob = m_gc.gitBlob(absPathName); psaInstalled.conf.size = getFileSize(absPathName); + if (map.contains("DC2C_conf.json")) { + psaInstalled.conf.blob = map.value("DC2C_conf.json", "inst.vers.not.avail"); + } psaInstalled.device.name = "DC2C_device.json"; absPathName = QDir::cleanPath(printSysDir + QDir::separator() + psaInstalled.device.name); psaInstalled.device.blob = m_gc.gitBlob(absPathName); psaInstalled.device.size = getFileSize(absPathName); + if (map.contains("DC2C_device.json")) { + psaInstalled.device.blob = map.value("DC2C_device.json", "inst.vers.not.avail"); + } for (int i=0; i < 32; ++i) { QString const &n = QString("%1").arg(i+1).rightJustified(2, '0'); @@ -1191,7 +1458,121 @@ PSAInstalled Worker::getPSAInstalled() { absPathName = QDir::cleanPath(printSysDir + QDir::separator() + psaInstalled.print[i].name); psaInstalled.print[i].blob = m_gc.gitBlob(absPathName); psaInstalled.print[i].size = getFileSize(absPathName); + if (map.contains(psaInstalled.print[i].name)) { + psaInstalled.print[i].blob = map.value(psaInstalled.print[i].name, "inst.vers.not.avail"); + } + } + + psaInstalled.ptuPackageVersion = "{}"; + if (QFile::exists("/usr/bin/ptuPackageVersions")) { + Command c("/usr/bin/ptuPackageVersions -i -o json"); + if (c.execute(m_workingDirectory)) { + QString r = c.getCommandResult(); + // ptuPackageVersions returns a json-array + QJsonArray const &ja = QJsonDocument::fromJson(r.remove(QRegExp("\\n")).toUtf8()).array(); + if (!ja.empty()) { + // transform the array into an object, containing the objects + // of the array (christian needs it this way) + QJsonObject o; + foreach (QJsonValue const &value, ja) { + if (value.isObject()) { + QJsonObject obj = value.toObject(); + QStringList keys = obj.keys(); + if (!keys.isEmpty()) { + QString const &k = obj.keys().first(); + QJsonValue const &v = obj.value(k); + o.insert(k, v); + } + } + } + + psaInstalled.ptuPackageVersion = + QJsonDocument(o).toJson(QJsonDocument::Compact); + + } else { + qCritical() << __func__ << ":" << __LINE__ + << "ERROR array return by ptuPackageVersions empty"; + } + } else { + qCritical() << __func__ << ":" << __LINE__ + << "ERROR executing ptuPackageVersions"; + } } return psaInstalled; } + +bool Worker::dcUpdate() { + return m_dcDownloadFirmware->start("/opt/app/tools/atbupdate"); +} + +void Worker::summary() { + + QString summary, first, second, line, tmp; + QVector> vec = Utils::installedPackages(); + + int max_first = 0, max_second = 0; + for (int i = 0; i < vec.size(); ++i) { + max_first = std::max(max_first, vec[i].first.length()); + max_second = std::max(max_second, vec[i].second.length()); + } + + max_first += 5; + + summary = "UPDATE SUMMARY\n\n"; + + first = QString("%1").arg("start", max_first, QChar(' ')); + tmp = QString("%1").arg(start().toString(Qt::ISODate)); + second = QString("%1").arg(tmp, -max_second, QChar(' ')); + line = first + ": " + second; + summary += line + "\n"; + + first = QString("%1").arg("update tool version", max_first, QChar(' ')); + tmp = QString("%1 - %2 %3").arg(APP_VERSION).arg(APP_BUILD_DATE).arg(APP_BUILD_TIME); + second = QString("%1").arg(tmp, -max_second, QChar(' ')); + line = first + ": " + second; + summary += line + "\n"; + + first = QString("%1").arg("machine number", max_first, QChar(' ')); + tmp = QString("%1").arg(machineNr()); + second = QString("%1").arg(tmp, -max_second, QChar(' ')); + line = first + ": " + second; + summary += line + "\n"; + + first = QString("%1").arg("customer number", max_first, QChar(' ')); + tmp = QString("%1").arg(customerNr()); + second = QString("%1").arg(tmp, -max_second, QChar(' ')); + line = first + ": " + second; + summary += line + "\n"; + + first = QString("%1").arg("zone number", max_first, QChar(' ')); + tmp = QString("%1").arg(zoneNr()); + second = QString("%1").arg(tmp, -max_second, QChar(' ')); + line = first + ": " + second; + summary += line + "\n"; + + if (m_mainWindow) { + tmp = m_mainWindow->targetDcVersion(); + if (!tmp.isEmpty()) { + first = QString("%1").arg("target device controller", max_first, QChar(' ')); + second = QString("%1").arg(tmp, -max_second, QChar(' ')); + line = first + ": " + second; + summary += line + "\n"; + } + } + + first = QString("%1").arg("apism", max_first, QChar(' ')); + tmp = QString("%1").arg(apismVersion()); + second = QString("%1").arg(tmp, -max_second, QChar(' ')); + line = first + ": " + second; + summary += line + "\n"; + + for (int i = 0; i < vec.size(); ++i) { + first = QString("%1").arg(vec[i].first, max_first, QChar(' ')); + second = QString("%1").arg(vec[i].second, -max_second, QChar(' ')); + line = first + ": " + second; + summary += line + "\n"; + } + + emit showSummary(summary); +} diff --git a/worker.h b/UpdatePTUDevCtrl/worker.h similarity index 53% rename from worker.h rename to UpdatePTUDevCtrl/worker.h index 6ea9bb7..8c54d69 100644 --- a/worker.h +++ b/UpdatePTUDevCtrl/worker.h @@ -15,7 +15,6 @@ #include #include -#include "update.h" #include "git/git_client.h" #include "ismas/ismas_client.h" #include "utils.h" @@ -26,88 +25,117 @@ #define SERIAL_PORT "ttyUSB0" #endif -#define _ISMAS_DONE "U0001" // 100% -#define _ISMAS_SET_WAIT_OK "U0002" // empty WAIT-button ("") -#define _ISMAS_FAILURE "U0003" // FAIL -#define _ISMAS_CONTINUE "U0010" // %-values -#define _ISMAS_RESET_WAIT "ISMAS" // reset WAIT-button to "WAIT" +/* + Note: + ! After U0002 immer ein CMD_SENDVERSION + ! Only U0002 and U0003 finish the Update process. + ! U0001: Update finished but not activated + ! U0002: Update finished and activated + ! U0003: Update finished but FAILed. + */ + +#define _ISMAS_DONE "U0001" // 100%, Check: Resultcode: 0 +#define _ISMAS_SET_WAIT_OK "U0002" // empty WAIT-button (""), ResultCode: 0 +#define _ISMAS_NO_UPDATE_NECESSARY "M0100" // empty WAIT-button (""), ResultCode: 0 +#define _ISMAS_FAILURE "U0003" // FAIL +#define _ISMAS_CONTINUE "U0010" // %-values: Update laeuft, Resultcodes entsprechend laufender Schritt +#define _ISMAS_RESET_WAIT "ISMAS" // reset WAIT-button to "WAIT" +#define _ISMAS_TEST_TRIGGER "U0099" // check the WAIT-button #define _STARTED (1) -#define _CHECK_SANITY (2) -#define _CHECK_SANITY_FAILURE (3) -#define _CHECK_SANITY_SUCCESS (4) -#define _REPOSITORY_RECOVERED_FAILURE (5) -#define _REPOSITORY_RECOVERED_SUCCESS (6) -#define _CHECK_REPOSITORY (7) -#define _CHECK_REPOSITORY_FAILURE (8) -#define _CHECK_REPOSITORY_SUCCESS (9) -#define _CLONE_REPOSITORY (10) -#define _CLONE_REPOSITORY_FAILURE (11) -#define _CLONE_REPOSITORY_SUCCESS (12) -#define _CHECKOUT_REPOSITORY (13) -#define _CHECKOUT_REPOSITORY_FAILURE (14) -#define _CHECKOUT_REPOSITORY_SUCCESS (15) -#define _INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER (16) -#define _INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER (17) -#define _CHECK_ISMAS_TRIGGER (19) -#define _CHECK_ISMAS_TRIGGER_WRONG_VALUE (20) -#define _CHECK_ISMAS_TRIGGER_SUCCESS (21) -#define _CHECK_ISMAS_TRIGGER_FAILURE (22) -#define _CHECKOUT_BRANCH (23) -#define _CHECKOUT_BRANCH_FAILURE (24) -#define _CHECKOUT_BRANCH_SUCCESS (25) -#define _UPDATE_REPOSITORY (26) -#define _UPDATE_REPOSITORY_FAILURE (28) -#define _UPDATE_REPOSITORY_SUCCESS (29) -#define _CHECK_FOR_REPOSITORY_CHANGES (30) -#define _CHECK_FOR_REPOSITORY_CHANGES_SUCCESS (36) -#define _FILES_TO_UPDATE (37) -#define _CHECK_FOR_REPOSITORY_CHANGES_FAILURE (38) -#define _FILES_TO_DOWNLOAD (39) -#define _EXEC_OPKG_COMMANDS (41) -#define _EXEC_OPKG_COMMAND_1 (42) -#define _EXEC_OPKG_COMMAND_2 (43) -#define _EXEC_OPKG_COMMAND_3 (44) -#define _EXEC_OPKG_COMMAND_4 (45) -#define _EXEC_OPKG_COMMAND_5 (46) -#define _EXEC_OPKG_COMMAND_6 (47) -#define _EXEC_OPKG_COMMAND_7 (48) -#define _EXEC_OPKG_COMMAND_8 (49) -#define _EXEC_OPKG_COMMAND_9 (50) -#define _EXEC_OPKG_COMMAND_LAST (51) -#define _EXEC_OPKG_COMMAND_FAILURE (52) -#define _EXEC_OPKG_COMMAND_SUCCESS (53) -#define _DOWNLOAD_FILES_TO_PSA_HARDWARE (54) -#define _DOWNLOAD_CONFIG_FILE (55) -#define _DOWNLOAD_CONFIG_FILE_SUCCESS (56) -#define _DOWNLOAD_CONFIG_FILE_FAILURE (57) -#define _DOWNLOAD_DEVICE_CONTROLLER (65) +#define _CHECK_ISMAS_TRIGGER (2) +#define _CHECK_SANITY (3) +#define _CHECK_SANITY_FAILURE (4) +#define _CHECK_SANITY_SUCCESS (5) +#define _REPOSITORY_RECOVERED_FAILURE (6) +#define _REPOSITORY_RECOVERED_SUCCESS (7) +#define _CHECK_REPOSITORY (8) +#define _CHECK_REPOSITORY_FAILURE (9) +#define _CHECK_REPOSITORY_SUCCESS (10) +#define _CLONE_REPOSITORY (11) +#define _CLONE_REPOSITORY_FAILURE (12) +#define _CLONE_REPOSITORY_SUCCESS (13) +#define _CHECKOUT_REPOSITORY (14) +#define _CHECKOUT_REPOSITORY_FAILURE (15) +#define _CHECKOUT_REPOSITORY_SUCCESS (16) +#define _INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER (17) +#define _INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER (18) +#define _CHECK_ISMAS_TRIGGER_WRONG_VALUE (19) +#define _CHECK_ISMAS_TRIGGER_SUCCESS (20) +#define _CHECK_ISMAS_TRIGGER_FAILURE (21) +#define _PULL_NEW_BRANCH (22) +#define _PULL_NEW_BRANCH_FAILURE (23) +#define _PULL_NEW_BRANCH_SUCCESS (24) +#define _CHECKOUT_BRANCH (25) +#define _CHECKOUT_BRANCH_FAILURE (26) +#define _CHECKOUT_BRANCH_SUCCESS (27) +#define _UPDATE_REPOSITORY (28) +#define _UPDATE_REPOSITORY_FAILURE (29) +#define _UPDATE_REPOSITORY_SUCCESS (30) +#define _CHECK_FOR_REPOSITORY_CHANGES (31) +#define _CHECK_FOR_REPOSITORY_CHANGES_SUCCESS (32) +#define _SYNC_CUSTOMER_REPOSITORY (33) +#define _SYNC_CUSTOMER_REPOSITORY_FAILURE (34) +#define _SYNC_CUSTOMER_REPOSITORY_SUCCESS (35) +#define _FILES_TO_UPDATE (36) +#define _CHECK_FOR_REPOSITORY_CHANGES_FAILURE (37) +#define _EXEC_OPKG_COMMANDS (39) +#define _EXEC_OPKG_COMMAND_1 (40) +#define _EXEC_OPKG_COMMAND_2 (41) +#define _EXEC_OPKG_COMMAND_3 (42) +#define _EXEC_OPKG_COMMAND_4 (43) +#define _EXEC_OPKG_COMMAND_5 (44) +#define _EXEC_OPKG_COMMAND_6 (45) +#define _EXEC_OPKG_COMMAND_7 (46) +#define _EXEC_OPKG_COMMAND_8 (47) +#define _EXEC_OPKG_COMMAND_9 (48) +#define _EXEC_OPKG_COMMAND_LAST (49) +#define _EXEC_OPKG_COMMAND_FAILURE (50) +#define _EXEC_OPKG_COMMAND_SUCCESS (51) +#define _DOWNLOAD_FILES_TO_PSA_HARDWARE (59) +#define _FILES_TO_DOWNLOAD (60) +#define _DOWNLOAD_CASH_FILE (61) +#define _DOWNLOAD_CASH_FILE_FAILURE (62) +#define _DOWNLOAD_CASH_FILE_SUCCESS (63) +#define _DOWNLOAD_CONFIG_FILE (64) +#define _DOWNLOAD_CONFIG_FILE_FAILURE (65) +#define _DOWNLOAD_CONFIG_FILE_SUCCESS (66) +#define _DOWNLOAD_DEVICE_FILE (67) +#define _DOWNLOAD_DEVICE_FILE_FAILURE (68) +#define _DOWNLOAD_DEVICE_FILE_SUCCESS (69) +#define _DOWNLOAD_JSON_FILE (70) +#define _DOWNLOAD_JSON_FILE_FAILURE (71) +#define _DOWNLOAD_JSON_FILE_SUCCESS (72) +#define _DOWNLOAD_DEVICE_CONTROLLER (84) +#define _DOWNLOAD_DEVICE_CONTROLLER_FAILURE (85) #define _DOWNLOAD_DEVICE_CONTROLLER_SUCCESS (86) -#define _DOWNLOAD_DEVICE_CONTROLLER_FAILURE (87) -#define _DOWNLOAD_FILES_TO_PSA_HARDWARE_FAILURE (88) -#define _DOWNLOAD_FILES_TO_PSA_HARDWARE_SUCCESS (89) -#define _SYNC_CUSTOMER_REPOSITORY (90) -#define _SYNC_CUSTOMER_REPOSITORY_FAILURE (91) -#define _SYNC_CUSTOMER_REPOSITORY_SUCCESS (92) -#define _SAVE_LOGS (93) -#define _SAVE_LOGS_FAILURE (94) -#define _SAVE_LOGS_SUCCESS (95) -#define _SEND_LAST_VERSION (96) -#define _UPDATE_SUCCEEDED (97) -#define _UPDATE_FAILED (98) -#define _UPDATE_ACTIVATED (99) +#define _DOWNLOAD_FILES_TO_PSA_HARDWARE_FAILURE (87) +#define _DOWNLOAD_FILES_TO_PSA_HARDWARE_SUCCESS (88) +#define _SAVE_LOGS (92) +#define _SAVE_LOGS_FAILURE (93) +#define _SAVE_LOGS_SUCCESS (94) +#define _SEND_LAST_VERSION (95) +#define _UPDATE_FINALIZE (95) +#define _UPDATE_SUCCEEDED (96) +#define _UPDATE_FAILED (97) +#define _UPDATE_ACTIVATED (98) +#define _UPDATE_NOT_NECESSARY (99) #define _FINISHED (100) #define _DEBUG (1000) #define _ERROR (1001) +#define _NONE (1002) -#define _SEND_LAST_VERSION_CORRECTION (4) -#define _UPDATE_SUCCEEDED_CORRECTION (3) -#define _UPDATE_FAILED_CORRECTION (2) -#define _UPDATE_ACTIVATED_CORRECTION (1) +#define _SEND_LAST_VERSION_CORRECTION (5) +#define _UPDATE_SUCCEEDED_CORRECTION (4) +#define _UPDATE_FAILED_CORRECTION (3) +#define _UPDATE_ACTIVATED_CORRECTION (2) +#define _UPDATE_NOT_NECESSARY_CORRECTION (1) #define ISMAS_UPDATE_REQUESTS (10) #define CHECK_UPDATE_TRIGGER_SET "Check update trigger ..." +class Command; +class Update; class MainWindow; class hwinf; class Worker : public QThread{ @@ -132,6 +160,7 @@ class Worker : public QThread{ QString const m_baudrate; IsmasClient m_ismasClient; GitClient m_gc; + QStringList m_versionInfo; QString const m_osVersion; QString const m_atbqtVersion; QString const m_atbUpdateToolVersion; @@ -155,12 +184,18 @@ class Worker : public QThread{ bool m_initialClone = false; bool m_repairClone = false; bool m_ismasTriggerActive = false; + bool m_updateNotNecessary = false; + bool m_automaticUpdate = false; + bool m_pulledNewBranch = false; + QStringList m_ismasTriggerStatusMessage; MainWindow *m_mainWindow; + Command *m_dcDownloadFirmware; bool m_withoutIsmasDirectPort; QString m_apismVersion; bool executeOpkgCommand(QString opkgCommand); + bool cleanUpOpkgCache(); QString getOsVersion() const; QString getATBQTVersion() const; QString getATBUpdateToolVersion() const; @@ -198,10 +233,50 @@ class Worker : public QThread{ } virtual ~UpdateProcessRunning() { - m_worker->ISMAS() << (m_worker->GUI() << (m_worker->CONSOLE() - << UPDATE_STEP::SEND_LAST_VERSION)); - m_worker->stopProgressLoop(); + if (m_worker->m_lastFailedUpdateStep == UPDATE_STEP::NONE) { + if (m_worker->m_updateNotNecessary) { + QStringList lst = m_worker->m_ismasTriggerStatusMessage; + m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << (m_worker->ISMAS(lst) << UPDATE_STEP::UPDATE_NOT_NECESSARY)); + } else { + QStringList lst(QString(m_worker->smap[UPDATE_STEP::UPDATE_SUCCEEDED])); + m_worker->ISMAS(lst) << (m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << UPDATE_STEP::UPDATE_SUCCEEDED)); + m_worker->setProgress(_UPDATE_SUCCEEDED); + + lst = QStringList(QString(m_worker->smap[UPDATE_STEP::UPDATE_ACTIVATED])); + m_worker->ISMAS(lst) << (m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << UPDATE_STEP::UPDATE_ACTIVATED)); + m_worker->setProgress(_UPDATE_ACTIVATED); + + lst = QStringList(QString(m_worker->smap[UPDATE_STEP::FINISHED])); + m_worker->CONSOLE(lst) << UPDATE_STEP::FINISHED; + m_worker->setProgress(_FINISHED); + } + } else { + // QStringList lst = m_worker->m_ismasTriggerStatusMessage; + QStringList lst; + UPDATE_STEP last = m_worker->m_lastFailedUpdateStep; + if (m_worker->smap.contains(last)) { + lst << QString(" (last failed step: %1)").arg(m_worker->smap[last]); + } else { + lst << QString(" (last failed step unknown: %1)").arg((int)(last)); + } + m_worker->GUI(lst) << (m_worker->CONSOLE(lst) << (m_worker->ISMAS(lst) << UPDATE_STEP::UPDATE_FAILED)); + } + + if (m_worker->m_automaticUpdate) { + QStringList lst(QString(m_worker->smap[UPDATE_STEP::SEND_LAST_VERSION])); + lst << "AUTOMATIC UPDATE"; + if (m_worker->m_updateNotNecessary) { + lst << "UPDATE NOT NECESSARY"; + } + m_worker->ISMAS(lst) << (m_worker->GUI(lst) << (m_worker->CONSOLE(lst) + << UPDATE_STEP::SEND_LAST_VERSION)); + } else { + QStringList lst(QString(m_worker->smap[UPDATE_STEP::SEND_LAST_VERSION])); + m_worker->ISMAS(lst) << (m_worker->GUI(lst) << (m_worker->CONSOLE(lst) + << UPDATE_STEP::SEND_LAST_VERSION)); + } + m_worker->stopProgressLoop(); m_worker->m_updateProcessRunning = false; emit m_worker->enableExit(); emit m_worker->restartExitTimer(); @@ -233,6 +308,9 @@ public: CHECK_ISMAS_TRIGGER_FAILURE = _CHECK_ISMAS_TRIGGER_FAILURE, INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER = _INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER, INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER = _INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER, + PULL_NEW_BRANCH = _PULL_NEW_BRANCH, + PULL_NEW_BRANCH_FAILURE = _PULL_NEW_BRANCH_FAILURE, + PULL_NEW_BRANCH_SUCCESS = _PULL_NEW_BRANCH_SUCCESS, CHECKOUT_BRANCH = _CHECKOUT_BRANCH, CHECKOUT_BRANCH_SUCCESS = _CHECKOUT_BRANCH_SUCCESS, CHECKOUT_BRANCH_FAILURE = _CHECKOUT_BRANCH_FAILURE, @@ -261,6 +339,15 @@ public: DOWNLOAD_CONFIG_FILE = _DOWNLOAD_CONFIG_FILE, DOWNLOAD_CONFIG_FILE_SUCCESS = _DOWNLOAD_CONFIG_FILE_SUCCESS, DOWNLOAD_CONFIG_FILE_FAILURE = _DOWNLOAD_CONFIG_FILE_FAILURE, + DOWNLOAD_CASH_FILE = _DOWNLOAD_CASH_FILE, + DOWNLOAD_CASH_FILE_FAILURE = _DOWNLOAD_CASH_FILE_FAILURE, + DOWNLOAD_CASH_FILE_SUCCESS = _DOWNLOAD_CASH_FILE_SUCCESS, + DOWNLOAD_DEVICE_FILE = _DOWNLOAD_DEVICE_FILE, + DOWNLOAD_DEVICE_FILE_FAILURE = _DOWNLOAD_DEVICE_FILE_FAILURE, + DOWNLOAD_DEVICE_FILE_SUCCESS = _DOWNLOAD_DEVICE_FILE_SUCCESS, + DOWNLOAD_JSON_FILE = _DOWNLOAD_JSON_FILE, + DOWNLOAD_JSON_FILE_FAILURE = _DOWNLOAD_JSON_FILE_FAILURE, + DOWNLOAD_JSON_FILE_SUCCESS = _DOWNLOAD_JSON_FILE_SUCCESS, DOWNLOAD_DEVICE_CONTROLLER = _DOWNLOAD_DEVICE_CONTROLLER, DOWNLOAD_DEVICE_CONTROLLER_SUCCESS = _DOWNLOAD_DEVICE_CONTROLLER_SUCCESS, DOWNLOAD_DEVICE_CONTROLLER_FAILURE = _DOWNLOAD_DEVICE_CONTROLLER_FAILURE, @@ -273,12 +360,15 @@ public: SAVE_LOGS_SUCCESS = _SAVE_LOGS_SUCCESS, SAVE_LOGS_FAILURE = _SAVE_LOGS_FAILURE, SEND_LAST_VERSION = _SEND_LAST_VERSION, + UPDATE_FINALIZE = _UPDATE_FINALIZE, UPDATE_SUCCEEDED = _UPDATE_SUCCEEDED, UPDATE_ACTIVATED = _UPDATE_ACTIVATED, + UPDATE_NOT_NECESSARY = _UPDATE_NOT_NECESSARY, UPDATE_FAILED = _UPDATE_FAILED, FINISHED = _FINISHED, DEBUG = _DEBUG, - ERROR = _ERROR + ERROR = _ERROR, + NONE = _NONE }; private: @@ -289,6 +379,8 @@ private: static Worker *instance; QStringList m_opkgCommands; Update *m_update = nullptr; + hwinf *m_hw = nullptr; + UPDATE_STEP m_lastFailedUpdateStep = UPDATE_STEP::NONE; protected: virtual void run(); @@ -317,6 +409,7 @@ public: static QString getATBUpdateToolYoctoInstallationStatus(); static QString getAPISMYoctoVersion(); static QString getAPISMYoctoInstallationStatus(); + static QString getDCVersionPreparedForDownload(QString const &filename); static const QString UPDATE_STEP_OK; static const QString UPDATE_STEP_DONE; @@ -346,6 +439,8 @@ public: void displayProgressInMainWindow(int progress); void startProgressLoop(); void stopProgressLoop(); + void setHW(hwinf *hw) { m_hw = hw; } + hwinf *getHW() { return m_hw; } IsmasClient &getIsmasClient() { return m_ismasClient; } IsmasClient const &getIsmasClient() const { return m_ismasClient; } @@ -365,8 +460,13 @@ public: Update *update() { return m_update; } Update const *update() const { return m_update; } + bool dcUpdate(); + void summary(); + QDateTime start() { return m_start; } + signals: void appendText(QString, QString suffix = ""); + void insertText(QString); void replaceLast(QString, QString); void replaceLast(QStringList, QString); void showErrorMessage(QString title, QString description); @@ -377,6 +477,9 @@ signals: void restartExitTimer(); void enableExit(); void disableExit(); + void showDcDownload(QString); + void showSummary(QString); + void setDcDownloadProgress(int); private slots: bool updateTriggerSet(); @@ -389,10 +492,13 @@ private slots: private: PSAInstalled getPSAInstalled(); + static bool sendLastVersionOnce; void privateUpdate(); bool computeFilesToDownload(); bool execOpkgCommands(); + QDateTime m_start; + static const QMap smap; // CONSOLE() @@ -405,6 +511,8 @@ private: QStringList lst = QStringList(smap[step]); switch (step) { + case UPDATE_STEP::NONE: { + } break; case UPDATE_STEP::STARTED: { Utils::printUpdateStatusMsg( debug, @@ -496,6 +604,18 @@ private: break; case UPDATE_STEP::INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER: break; + case UPDATE_STEP::PULL_NEW_BRANCH: { + lst << instance->m_debugMsg; + Utils::printUpdateStatusMsg(debug, lst); + } break; + case UPDATE_STEP::PULL_NEW_BRANCH_FAILURE: { + lst << instance->m_debugMsg; + Utils::printUpdateStatusMsg(debug, lst); + } break; + case UPDATE_STEP::PULL_NEW_BRANCH_SUCCESS: { + lst << instance->m_debugMsg; + Utils::printUpdateStatusMsg(debug, lst); + } break; case UPDATE_STEP::CHECKOUT_BRANCH: lst << instance->m_debugMsg; Utils::printUpdateStatusMsg(debug, lst); @@ -567,6 +687,24 @@ private: break; case UPDATE_STEP::DOWNLOAD_CONFIG_FILE_FAILURE: break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE: { + } break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE_FAILURE: { + } break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE_SUCCESS: { + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE: { + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE_FAILURE: { + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE_SUCCESS: { + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE: { + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE: { + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE_SUCCESS: { + } break; case UPDATE_STEP::DOWNLOAD_DEVICE_CONTROLLER: lst << instance->m_debugMsg; Utils::printUpdateStatusMsg(debug, lst); @@ -612,6 +750,10 @@ private: lst << instance->m_debugMsg; Utils::printUpdateStatusMsg(debug, lst); break; + case UPDATE_STEP::UPDATE_NOT_NECESSARY: + lst << instance->m_debugMsg; + Utils::printUpdateStatusMsg(debug, lst); + break; case UPDATE_STEP::UPDATE_FAILED: break; case UPDATE_STEP::FINISHED: @@ -642,48 +784,111 @@ private: } Worker::instance->m_currentStep = step; - QStringList lst = QStringList(smap[step]); + // QStringList lst = QStringList(smap[step]); switch (step) { + case UPDATE_STEP::NONE: { + } break; case UPDATE_STEP::STARTED: { ismasClient.setProgressInPercent(_STARTED); } break; case UPDATE_STEP::CHECK_REPOSITORY: ismasClient.setProgressInPercent(_CHECK_REPOSITORY); break; - case UPDATE_STEP::CHECK_REPOSITORY_SUCCESS: - break; - case UPDATE_STEP::CHECK_REPOSITORY_FAILURE: - break; - case UPDATE_STEP::CHECK_SANITY: + case UPDATE_STEP::CHECK_REPOSITORY_SUCCESS: { + ismasClient.setProgressInPercent(_CHECK_REPOSITORY_SUCCESS); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 + _CHECKOUT_REPOSITORY_SUCCESS, + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::CHECK_REPOSITORY], + QString("REPOSITORY %1 AND BRANCH %2 OK") + .arg(instance->m_customerRepository) + .arg(instance->m_gc.branchName()).toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::CHECK_REPOSITORY_FAILURE: { + ismasClient.setProgressInPercent(_CHECK_REPOSITORY_FAILURE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 + _CHECKOUT_REPOSITORY_FAILURE, + IsmasClient::RESULT_CODE::GIT_SWITCH_BRANCH_ERROR, + smap[UPDATE_STEP::CHECKOUT_REPOSITORY], + QString("REPOSITORY %1 and BRANCH %2 ***NOT OK***") + .arg(instance->m_customerRepository) + .arg(instance->m_gc.branchName()).toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::CHECK_SANITY: { ismasClient.setProgressInPercent(_CHECK_SANITY); - break; - case UPDATE_STEP::CHECK_SANITY_SUCCESS: + } break; + case UPDATE_STEP::CHECK_SANITY_SUCCESS: { ismasClient.setProgressInPercent(_CHECK_SANITY_SUCCESS); - break; - case UPDATE_STEP::CHECK_SANITY_FAILURE: + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 + _CHECK_SANITY_SUCCESS, + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::CHECK_SANITY], + QString("SANITY OF %1 (BRANCH %2) OK") + .arg(instance->m_customerRepository) + .arg(instance->m_gc.branchName()).toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::CHECK_SANITY_FAILURE: { ismasClient.setProgressInPercent(_CHECK_SANITY_FAILURE); - break; - case UPDATE_STEP::REPOSITORY_RECOVERED_SUCCESS: - break; - case UPDATE_STEP::REPOSITORY_RECOVERED_FAILURE: - break; - case UPDATE_STEP::CLONE_REPOSITORY: + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 + _CHECK_SANITY_FAILURE, + IsmasClient::RESULT_CODE::GIT_CHECK_REPOSITORY_ERROR, + smap[UPDATE_STEP::CHECK_SANITY], + QString("SANITY OF %1 and BRANCH %2 ***NOT*** OK") + .arg(instance->m_customerRepository) + .arg(instance->m_gc.branchName()).toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::REPOSITORY_RECOVERED_SUCCESS: { + ismasClient.setProgressInPercent(_REPOSITORY_RECOVERED_SUCCESS); + } break; + case UPDATE_STEP::REPOSITORY_RECOVERED_FAILURE: { + ismasClient.setProgressInPercent(_REPOSITORY_RECOVERED_FAILURE); + } break; + case UPDATE_STEP::CLONE_REPOSITORY: { ismasClient.setProgressInPercent(_CLONE_REPOSITORY); - break; + } break; case UPDATE_STEP::CLONE_REPOSITORY_SUCCESS: { ismasClient.setProgressInPercent(_CLONE_REPOSITORY_SUCCESS); QString const &ismasUpdateNews = QString("#M=APISM#C=CMD_EVENT#J=") + ismasClient.updateNewsToIsmas( - "U0010", + _ISMAS_CONTINUE, // U0010 _CLONE_REPOSITORY_SUCCESS, IsmasClient::RESULT_CODE::SUCCESS, - smap[step], + smap[UPDATE_STEP::CLONE_REPOSITORY], QString("CLONED REPOSITORY %1 AND CHECKED OUT BRANCH %2") .arg(instance->m_customerRepository) .arg(instance->m_gc.branchName()).toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; @@ -692,67 +897,196 @@ private: QString const &ismasUpdateNews = QString("#M=APISM#C=CMD_EVENT#J=") + ismasClient.updateNewsToIsmas( - "U0003", + _ISMAS_CONTINUE, // U0010 -> even on error: U0002/3 are sent only once _CLONE_REPOSITORY_FAILURE, - IsmasClient::RESULT_CODE::INSTALL_ERROR, - smap[step], + IsmasClient::RESULT_CODE::GIT_CLONE_ERROR, + smap[UPDATE_STEP::CHECKOUT_REPOSITORY], QString("CLONING REPOSITORY %1 OR CHECKING OUT BRANCH %2 FAILED") .arg(instance->m_customerRepository) .arg(instance->m_gc.branchName()).toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; case UPDATE_STEP::CHECKOUT_REPOSITORY: ismasClient.setProgressInPercent(_CHECKOUT_REPOSITORY); break; - case UPDATE_STEP::CHECKOUT_REPOSITORY_SUCCESS: + case UPDATE_STEP::CHECKOUT_REPOSITORY_SUCCESS: { ismasClient.setProgressInPercent(_CHECKOUT_REPOSITORY_SUCCESS); ismasClient.sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, QString("#M=APISM#C=CMD_EVENT#J=") + ismasClient.cloneAndCheckoutCustomerRepository( QString("CHECKED OUT REPOSITORY %1 AND CHECKED OUT BRANCH %2") .arg(instance->m_customerRepository) - .arg(instance->m_gc.branchName()))); - break; - case UPDATE_STEP::CHECKOUT_REPOSITORY_FAILURE: + .arg(instance->m_gc.branchName()), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A")); + } break; + case UPDATE_STEP::CHECKOUT_REPOSITORY_FAILURE: { ismasClient.setProgressInPercent(_CHECKOUT_REPOSITORY_FAILURE); - break; - case UPDATE_STEP::CHECK_ISMAS_TRIGGER: - ismasClient.setProgressInPercent(_CHECK_ISMAS_TRIGGER); - break; - case UPDATE_STEP::CHECK_ISMAS_TRIGGER_SUCCESS: - ismasClient.setProgressInPercent(_CHECK_ISMAS_TRIGGER_SUCCESS); - ismasClient.sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString const &ismasUpdateNews = QString("#M=APISM#C=CMD_EVENT#J=") + - ismasClient.updateTriggerSet("ISMAS TRIGGER SET", "")); - break; - case UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE: + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 -> even on error: U0002/3 are sent only once + _CHECKOUT_REPOSITORY_FAILURE, + IsmasClient::RESULT_CODE::GIT_SWITCH_BRANCH_ERROR, + smap[UPDATE_STEP::CHECKOUT_REPOSITORY], + QString("%1: CHECKING OUT BRANCH %2 FAILED") + .arg(instance->m_customerRepository) + .arg(instance->m_gc.branchName()).toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::CHECK_ISMAS_TRIGGER: { + ismasClient.setProgressInPercent(_CHECK_ISMAS_TRIGGER); + } break; + case UPDATE_STEP::CHECK_ISMAS_TRIGGER_SUCCESS: { + ismasClient.setProgressInPercent(_CHECK_ISMAS_TRIGGER_SUCCESS); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // + _CHECK_ISMAS_TRIGGER_SUCCESS, + (instance->m_updateNotNecessary) + ? IsmasClient::RESULT_CODE::NO_UPDATE_NECESSARY + : IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::CHECK_ISMAS_TRIGGER_SUCCESS], + instance->m_updateNotNecessary + ? QString("NO-UPDATE-NECESSARY (%1:%2)") + .arg(instance->m_customerRepository) + .arg(instance->m_gc.branchName()).toStdString().c_str() + : QString("ISMAS-TRIGGER ON WAIT").toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::CHECK_ISMAS_TRIGGER_WRONG_VALUE: { ismasClient.setProgressInPercent(_CHECK_ISMAS_TRIGGER_WRONG_VALUE); - break; + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_TEST_TRIGGER, // U0099 + _CHECK_ISMAS_TRIGGER_WRONG_VALUE, + IsmasClient::RESULT_CODE::ISMAS_TRIGGER_ERROR, + smap[UPDATE_STEP::CHECK_ISMAS_TRIGGER], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; case UPDATE_STEP::CHECK_ISMAS_TRIGGER_FAILURE: { ismasClient.setProgressInPercent(_CHECK_ISMAS_TRIGGER_FAILURE); QString const &ismasUpdateNews = QString("#M=APISM#C=CMD_EVENT#J=") + ismasClient.updateNewsToIsmas( - "U0003", + _ISMAS_TEST_TRIGGER, // U0099 _CHECK_ISMAS_TRIGGER_FAILURE, - IsmasClient::RESULT_CODE::INSTALL_ERROR, - smap[step], + IsmasClient::RESULT_CODE::ISMAS_TRIGGER_ERROR, + smap[UPDATE_STEP::CHECK_ISMAS_TRIGGER], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; - case UPDATE_STEP::INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER: + case UPDATE_STEP::INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER: { ismasClient.setProgressInPercent(_INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER); - break; - case UPDATE_STEP::INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER: + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 + _INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER, + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::INITIAL_CLONE_WITHOUT_ACTIVE_ISMAS_TRIGGER], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER: { ismasClient.setProgressInPercent(_INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 + _INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER, + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::PULL_NEW_BRANCH: { + ismasClient.setProgressInPercent(_PULL_NEW_BRANCH); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 + _PULL_NEW_BRANCH, + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::CHECKOUT_BRANCH], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; - case UPDATE_STEP::CHECKOUT_BRANCH: + case UPDATE_STEP::PULL_NEW_BRANCH_FAILURE: { + ismasClient.setProgressInPercent(_PULL_NEW_BRANCH_FAILURE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 -> even on error: U0002/3 are sent only once + _PULL_NEW_BRANCH_FAILURE, + IsmasClient::RESULT_CODE::GIT_PULL_ERROR, + smap[UPDATE_STEP::CHECKOUT_BRANCH], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } + break; + case UPDATE_STEP::PULL_NEW_BRANCH_SUCCESS: { + ismasClient.setProgressInPercent(_PULL_NEW_BRANCH_SUCCESS); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, // U0010 + _PULL_NEW_BRANCH_SUCCESS, + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::CHECKOUT_BRANCH], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } + break; + case UPDATE_STEP::CHECKOUT_BRANCH: { ismasClient.setProgressInPercent(_CHECKOUT_BRANCH); - break; + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + "U0010", + _CHECKOUT_BRANCH_SUCCESS, + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::CHECKOUT_BRANCH], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; case UPDATE_STEP::CHECKOUT_BRANCH_SUCCESS: { ismasClient.setProgressInPercent(_CHECKOUT_BRANCH_SUCCESS); QString const &ismasUpdateNews = @@ -761,9 +1095,10 @@ private: "U0010", _CHECKOUT_BRANCH_SUCCESS, IsmasClient::RESULT_CODE::SUCCESS, - smap[step], + smap[UPDATE_STEP::CHECKOUT_BRANCH], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; @@ -772,12 +1107,13 @@ private: QString const &ismasUpdateNews = QString("#M=APISM#C=CMD_EVENT#J=") + ismasClient.updateNewsToIsmas( - "U0003", + _ISMAS_FAILURE, // U0003 _CHECKOUT_BRANCH_FAILURE, - IsmasClient::RESULT_CODE::INSTALL_ERROR, - smap[step], + IsmasClient::RESULT_CODE::GIT_SWITCH_BRANCH_ERROR, + smap[UPDATE_STEP::CHECKOUT_BRANCH], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 + ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; @@ -802,12 +1138,34 @@ private: case UPDATE_STEP::FILES_TO_UPDATE: ismasClient.setProgressInPercent(_FILES_TO_UPDATE); break; - case UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE: + case UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE: { ismasClient.setProgressInPercent(_DOWNLOAD_FILES_TO_PSA_HARDWARE); - break; - case UPDATE_STEP::FILES_TO_DOWNLOAD: + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::FILES_TO_DOWNLOAD: { ismasClient.setProgressInPercent(_FILES_TO_DOWNLOAD); - break; + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_JSON_FILE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; case UPDATE_STEP::EXEC_OPKG_COMMANDS: ismasClient.setProgressInPercent(_EXEC_OPKG_COMMANDS); break; @@ -911,12 +1269,23 @@ private: IsmasClient::RESULT_CODE::SUCCESS, smap[step], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; case UPDATE_STEP::EXEC_OPKG_COMMAND_SUCCESS: { ismasClient.setProgressInPercent(_EXEC_OPKG_COMMAND_SUCCESS); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::EXEC_OPKG_COMMANDS], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; case UPDATE_STEP::EXEC_OPKG_COMMAND_FAILURE: { ismasClient.setProgressInPercent(_EXEC_OPKG_COMMAND_FAILURE); @@ -925,22 +1294,187 @@ private: ismasClient.updateNewsToIsmas( _ISMAS_FAILURE, ismasClient.getProgressInPercent(), - IsmasClient::RESULT_CODE::INSTALL_ERROR, + IsmasClient::RESULT_CODE::OPKG_COMMANDS_ERROR, smap[step], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; - case UPDATE_STEP::DOWNLOAD_CONFIG_FILE: - ismasClient.setProgressInPercent(_DOWNLOAD_CONFIG_FILE); - break; - case UPDATE_STEP::DOWNLOAD_CONFIG_FILE_SUCCESS: - ismasClient.setProgressInPercent(_DOWNLOAD_CONFIG_FILE_SUCCESS); - break; - case UPDATE_STEP::DOWNLOAD_CONFIG_FILE_FAILURE: + case UPDATE_STEP::DOWNLOAD_CONFIG_FILE: { + //ismasClient.setProgressInPercent(_DOWNLOAD_CONFIG_FILE); + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_CONFIG_FILE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_CONFIG_FILE_SUCCESS: { + //ismasClient.setProgressInPercent(_DOWNLOAD_CONFIG_FILE_SUCCESS); + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_CONFIG_FILE_SUCCESS], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_CONFIG_FILE_FAILURE: { ismasClient.setProgressInPercent(_DOWNLOAD_CONFIG_FILE_FAILURE); - break; + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::JSON_FILES_DOWNLOAD_ERROR, + smap[UPDATE_STEP::DOWNLOAD_CASH_FILE_FAILURE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE: { + //ismasClient.setProgressInPercent(_DOWNLOAD_CASH_FILE); + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_CASH_FILE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE_FAILURE: { + ismasClient.setProgressInPercent(_DOWNLOAD_CASH_FILE_FAILURE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::JSON_FILES_DOWNLOAD_ERROR, + smap[UPDATE_STEP::DOWNLOAD_CASH_FILE_FAILURE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE_SUCCESS: { + //ismasClient.setProgressInPercent(_DOWNLOAD_CASH_FILE_SUCCESS); + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_CASH_FILE_SUCCESS], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE: { + //ismasClient.setProgressInPercent(_DOWNLOAD_DEVICE_FILE); + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_DEVICE_FILE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE_FAILURE: { + ismasClient.setProgressInPercent(_DOWNLOAD_DEVICE_FILE_FAILURE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::JSON_FILES_DOWNLOAD_ERROR, + smap[UPDATE_STEP::DOWNLOAD_DEVICE_FILE_FAILURE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE_SUCCESS: { + //ismasClient.setProgressInPercent(_DOWNLOAD_DEVICE_FILE_SUCCESS); + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_DEVICE_FILE_SUCCESS], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE: { + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_JSON_FILE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE: { + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE_FAILURE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::JSON_FILES_DOWNLOAD_ERROR, + smap[UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE_SUCCESS: { + ismasClient.setProgressInPercent(_DOWNLOAD_JSON_FILE); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::DOWNLOAD_JSON_FILE_SUCCESS], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; case UPDATE_STEP::DOWNLOAD_DEVICE_CONTROLLER: ismasClient.setProgressInPercent(_DOWNLOAD_DEVICE_CONTROLLER); break; @@ -952,9 +1486,9 @@ private: _ISMAS_CONTINUE, ismasClient.getProgressInPercent(), IsmasClient::RESULT_CODE::SUCCESS, - smap[step], + smap[UPDATE_STEP::DOWNLOAD_DEVICE_CONTROLLER], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; @@ -969,18 +1503,29 @@ private: _ISMAS_CONTINUE, ismasClient.getProgressInPercent(), IsmasClient::RESULT_CODE::SUCCESS, - smap[step], + smap[UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; case UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE_FAILURE: ismasClient.setProgressInPercent(_DOWNLOAD_FILES_TO_PSA_HARDWARE_FAILURE); break; - case UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY: + case UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY: { ismasClient.setProgressInPercent(_SYNC_CUSTOMER_REPOSITORY); - break; + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_CONTINUE, + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; case UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS: { ismasClient.setProgressInPercent(_SYNC_CUSTOMER_REPOSITORY_SUCCESS); QString const &ismasUpdateNews = @@ -989,9 +1534,9 @@ private: _ISMAS_CONTINUE, ismasClient.getProgressInPercent(), IsmasClient::RESULT_CODE::SUCCESS, - smap[step], + smap[UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; @@ -1011,7 +1556,7 @@ private: IsmasClient::RESULT_CODE::SUCCESS, smap[step], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; @@ -1019,36 +1564,55 @@ private: ismasClient.setProgressInPercent(_SAVE_LOGS_FAILURE); break; case UPDATE_STEP::SEND_LAST_VERSION: { - ismasClient.setProgressInPercent(_SEND_LAST_VERSION + _SEND_LAST_VERSION_CORRECTION); - ismasClient.sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, - QString("#M=APISM#C=CMD_SENDVERSION#J=") + - ismasClient.updateOfPSASendVersion(instance->getPSAInstalled())); + if (Worker::sendLastVersionOnce == false) { + ismasClient.setProgressInPercent(_SEND_LAST_VERSION + _SEND_LAST_VERSION_CORRECTION); + ismasClient.sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_SENDVERSION#J=") + + ismasClient.updateOfPSASendVersion(instance->getPSAInstalled())); + Worker::sendLastVersionOnce = true; + } } break; case UPDATE_STEP::UPDATE_SUCCEEDED: { ismasClient.setProgressInPercent(_UPDATE_SUCCEEDED + _UPDATE_SUCCEEDED_CORRECTION); - QString const &ismasUpdateNews = - QString("#M=APISM#C=CMD_EVENT#J=") + - ismasClient.updateNewsToIsmas( - "U0001", // WAIT-button set to 100% - ismasClient.getProgressInPercent(), - IsmasClient::RESULT_CODE::SUCCESS, - smap[step], - instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); - ismasClient.sendRequestReceiveResponse( - IsmasClient::APISM::DB_PORT, ismasUpdateNews); + if (instance->m_automaticUpdate == false) { + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + _ISMAS_DONE, // WAIT-button set to 100% + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::SUCCESS, + smap[UPDATE_STEP::UPDATE_FINALIZE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } } break; case UPDATE_STEP::UPDATE_ACTIVATED: { ismasClient.setProgressInPercent(_UPDATE_ACTIVATED + _UPDATE_ACTIVATED_CORRECTION); QString const &ismasUpdateNews = QString("#M=APISM#C=CMD_EVENT#J=") + ismasClient.updateNewsToIsmas( - "U0002", // reset WAIT-button to "" (empty string) + (instance->m_automaticUpdate ? _ISMAS_NO_UPDATE_NECESSARY : _ISMAS_SET_WAIT_OK), // U0002 or M0100 ismasClient.getProgressInPercent(), IsmasClient::RESULT_CODE::SUCCESS, - smap[step], + smap[UPDATE_STEP::UPDATE_FINALIZE], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); + ismasClient.sendRequestReceiveResponse( + IsmasClient::APISM::DB_PORT, ismasUpdateNews); + } break; + case UPDATE_STEP::UPDATE_NOT_NECESSARY: { + ismasClient.setProgressInPercent(_UPDATE_NOT_NECESSARY + _UPDATE_NOT_NECESSARY_CORRECTION); + QString const &ismasUpdateNews = + QString("#M=APISM#C=CMD_EVENT#J=") + + ismasClient.updateNewsToIsmas( + (instance->m_automaticUpdate ? _ISMAS_NO_UPDATE_NECESSARY : _ISMAS_SET_WAIT_OK), // U0002 or M0100 + ismasClient.getProgressInPercent(), + IsmasClient::RESULT_CODE::NO_UPDATE_NECESSARY, + smap[UPDATE_STEP::UPDATE_FINALIZE], + instance->m_ismasMsg.join(' ').toStdString().c_str(), + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; @@ -1057,12 +1621,12 @@ private: QString const &ismasUpdateNews = QString("#M=APISM#C=CMD_EVENT#J=") + ismasClient.updateNewsToIsmas( - "U0003", + _ISMAS_FAILURE, // U0003 is sent only once ismasClient.getProgressInPercent(), - IsmasClient::RESULT_CODE::INSTALL_ERROR, - smap[step], + IsmasClient::RESULT_CODE::UPDATE_IN_ERROR_STATE, + smap[UPDATE_STEP::UPDATE_FINALIZE], instance->m_ismasMsg.join(' ').toStdString().c_str(), - ""); + instance->m_versionInfo.size() >= 1 ? instance->m_versionInfo.at(0).toUtf8().constData() : "N/A"); ismasClient.sendRequestReceiveResponse( IsmasClient::APISM::DB_PORT, ismasUpdateNews); } break; @@ -1089,6 +1653,8 @@ private: Worker::instance->m_currentStep = step; switch (step) { + case UPDATE_STEP::NONE: { + } break; case UPDATE_STEP::STARTED: break; case UPDATE_STEP::CHECK_REPOSITORY: @@ -1145,6 +1711,12 @@ private: break; case UPDATE_STEP::INITIAL_CLONE_WITH_ACTIVE_ISMAS_TRIGGER: break; + case UPDATE_STEP::PULL_NEW_BRANCH: + break; + case UPDATE_STEP::PULL_NEW_BRANCH_FAILURE: + break; + case UPDATE_STEP::PULL_NEW_BRANCH_SUCCESS: + break; case UPDATE_STEP::CHECKOUT_BRANCH: emit worker->appendText("\nPrepare customer environment ..."); break; @@ -1230,6 +1802,24 @@ private: break; case UPDATE_STEP::DOWNLOAD_CONFIG_FILE_FAILURE: break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE: { + } break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE_FAILURE: { + } break; + case UPDATE_STEP::DOWNLOAD_CASH_FILE_SUCCESS: { + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE: { + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE_FAILURE: { + } break; + case UPDATE_STEP::DOWNLOAD_DEVICE_FILE_SUCCESS: { + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE: { + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE_FAILURE: { + } break; + case UPDATE_STEP::DOWNLOAD_JSON_FILE_SUCCESS: { + } break; case UPDATE_STEP::DOWNLOAD_DEVICE_CONTROLLER: break; case UPDATE_STEP::DOWNLOAD_DEVICE_CONTROLLER_SUCCESS: @@ -1263,6 +1853,8 @@ private: break; case UPDATE_STEP::UPDATE_ACTIVATED: break; + case UPDATE_STEP::UPDATE_NOT_NECESSARY: + break; case UPDATE_STEP::UPDATE_FAILED: emit worker->appendText(QString("UPDATE "), UPDATE_STEP_FAIL); break; diff --git a/worker_thread.cpp b/UpdatePTUDevCtrl/worker_thread.cpp similarity index 100% rename from worker_thread.cpp rename to UpdatePTUDevCtrl/worker_thread.cpp diff --git a/worker_thread.h b/UpdatePTUDevCtrl/worker_thread.h similarity index 100% rename from worker_thread.h rename to UpdatePTUDevCtrl/worker_thread.h diff --git a/common.pri b/common.pri new file mode 100644 index 0000000..35ceac4 --- /dev/null +++ b/common.pri @@ -0,0 +1 @@ +INCLUDEPATH += $$PWD/common/include diff --git a/interfaces.h b/common/include/DeviceController/interfaces.h similarity index 100% rename from interfaces.h rename to common/include/DeviceController/interfaces.h diff --git a/common/include/System.h b/common/include/System.h new file mode 100644 index 0000000..18c0a45 --- /dev/null +++ b/common/include/System.h @@ -0,0 +1,63 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + + +#include +#include + +#include +#include + +class System : public QObject +{ + Q_OBJECT + +private: + explicit System(QObject *parent = 0); + + static QString errorMsg; + +public: + + static const QStringList allowedJsonFiles; + + static bool checkForSDCard(); + static bool umountSDCard(); + + static std::optional checkForUSBStick(QString const &dirPathUnderMountPath = "."); + static std::optional checkForSDCard(QString const &dirPathUnderMountPath = "."); + static QString getUSBMountPath(QString const &dirPathUnderMountPath = "."); + static QString getSDMountPath(QString const &dirPathUnderMountPath = "."); + + //static QString getUSBDeviceName(); + static bool umountUSBStick(); + + static bool test_mount(const QString& dev, const QString &mountpoint); + static bool test_dir(const QString& dir); + + static quint32 getFreeDiskSpace(const QString& dev); + static quint32 getFreeDiskSpaceSDCard(); + static bool isSDCardWritable(); + static quint32 getFreeDiskSpaceUSBStick(); + static bool isUSBStickWritable(); + + static quint8 createLogFileBackup(const QString & targzfile, const QString &filelistfile = ""); + static QString getSDCardMountPath(); + static QString getSDCardDeviceName(); + + static QString getUniqueDeviceId(); + + static QString getErrorMessage(); + + static QString readStringFromFile(const QString & filename); + static int readIntFromFile(const QString & filename); + + static QString getPTU4SerialNumber(); + static QString getPTU4MACAddress(); + + static QStringList getJsonFilesOnUsbStick(QString const &mountPath); + static QString getDCFileOnUsbStick(QString const &mountPath); + static QString getDCFileOnSDCard(QString const &mountPath); +}; + +#endif // SYSTEM_H diff --git a/common/include/command.h b/common/include/command.h new file mode 100644 index 0000000..33b155f --- /dev/null +++ b/common/include/command.h @@ -0,0 +1,49 @@ +#ifndef COMMAND_H_INCLUDED +#define COMMAND_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +class Command : public QObject { + Q_OBJECT + + QString m_command; + QString m_commandResult; + + int m_waitForStartTimeout; + int m_waitForFinishTimeout; + bool m_verbose; + int m_exitCode; + QString m_workingDirectory; + + QScopedPointer m_p; + + QStringList m_args; + +public: + Command(QString command, + QStringList args, + QString workingDirectory, + bool verbose = true, + int start_timeout = 100000, + int finish_timeout = 100000); + + void resetCommandResult() { m_commandResult.clear(); } + QString getCommandResult(bool reset = false); + QString const &command() const { return m_command; } + QString const &commandResult() const { return m_commandResult; } + QStringList const &args() const { return m_args; } + + bool exec(); + int exitCode() const { return m_exitCode; } + +private slots: + virtual void readyReadStandardOutput(); + virtual void readyReadStandardError(); +}; + +#endif // COMMAND_H_INCLUDED diff --git a/common/include/message_handler.h b/common/include/message_handler.h new file mode 100755 index 0000000..98c4d7e --- /dev/null +++ b/common/include/message_handler.h @@ -0,0 +1,23 @@ +#ifndef MESSAGE_HANDLER_H_INCLUDED +#define MESSAGE_HANDLER_H_INCLUDED + +#include +#ifdef __linux__ +#include +#endif + +int getDebugLevel(); +void setDebugLevel(int newDebugLevel); + +bool messageHandlerInstalled(); +QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler); + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +// typedef void (*QtMessageHandler)(QtMsgType, const char *); +void atbDebugOutput(QtMsgType type, const char *msg); +#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &); +void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg); +#endif + +#endif // MESSAGE_HANDLER_H_INCLUDED diff --git a/common/include/utils_internal.h b/common/include/utils_internal.h new file mode 100644 index 0000000..4cdb23a --- /dev/null +++ b/common/include/utils_internal.h @@ -0,0 +1,92 @@ +#ifndef UTILS_INTERNAL_H_INCLUDED +#define UTILS_INTERNAL_H_INCLUDED + +#include +#include +#include + +namespace internal { + + static constexpr const char *UPDATE_NOT_NECESSARY{"not necessary"}; + static constexpr const char *UPDATE_NOT_REQUESTED{"not requested"}; + static constexpr const char *UPDATE_INITIAL{"initial update"}; + static constexpr const char *UPDATE_REQUESTED{"requested"}; + + static constexpr const char *NO_CUSTOMER_REPOSITORY{"no customer repository"}; + static constexpr const int NO_CUSTOMER_REPOSITORY_CODE{-8}; + static constexpr const char *NO_ETC_CUSTOMER_REPOSITORY{"no etc/ in customer repository"}; + static constexpr const int NO_ETC_CUSTOMER_REPOSITORY_CODE{-9}; + static constexpr const char *NO_OPT_CUSTOMER_REPOSITORY{"no opt/ in customer repository"}; + static constexpr const int NO_OPT_CUSTOMER_REPOSITORY_CODE{-10}; + + static constexpr const char *ISMAS_CONNECTED{"connected"}; + static constexpr const char *ISMAS_DISCONNECTED{"disconnected"}; + static constexpr const char *ISMAS_DISCONNECTING{"disconnecting"}; + static constexpr const char *ISMAS_NOT_CONNECTED{"not connected"}; + static constexpr const char *ISMAS_CONNECTION_IN_PROGRESS{"connecting"}; + + static constexpr const char *BROKER_CONNECTED{"connected"}; + static constexpr const char *BROKER_DISCONNECTED{"disconnected"}; + static constexpr const char *BROKER_DISCONNECTING{"disconnecting"}; + static constexpr const char *BROKER_NOT_CONNECTED{"not connected"}; + static constexpr const char *BROKER_CONNECTION_IN_PROGRESS{"connecting"}; + + static constexpr const int GIT_CHECKOUT_ERROR_CODE{-2}; + static constexpr const int GIT_PULL_ERROR_CODE{-4}; + static constexpr const int GIT_NOT_NECESSARY_CODE{1}; + static constexpr const int GIT_UPDATED_CODE{2}; + static constexpr const int GIT_CLONED_CODE{3}; + + static constexpr const char *GIT_CUSTOMER_REPO_CHECKOUT_ERROR{"checkout error"}; + static constexpr const char *GIT_CUSTOMER_REPO_PULL_ERROR{"pull error"}; + static constexpr const char *GIT_CUSTOMER_REPO_UP_TO_DATE{"up to date"}; + static constexpr const char *GIT_CUSTOMER_REPO_NO_UPDATE_NECESSARY{"no repository update necessary"}; + static constexpr const char *GIT_CUSTOMER_REPO_NOT_NECESSARY{"not necessary"}; + static constexpr const char *GIT_CUSTOMER_REPO_UPDATED{"repository updated"}; + static constexpr const char *GIT_CUSTOMER_REPO_CLONED{"repository cloned"}; + static constexpr const char *GIT_UPDATED{"updated"}; + + static constexpr const char *EXEC_OPKG_COMMANDS_SUCCESS{"success"}; + static constexpr const char *EXEC_OPKG_COMMANDS_FAIL{"FAIL"}; + static constexpr const char *EXEC_OPKG_COMMANDS_NOACTION_SUCCESS{"success"}; + static constexpr const char *EXEC_OPKG_COMMANDS_NOACTION_FAIL{"FAIL"}; + + + static constexpr const char *UPDATE_DC_JSON_FILES_SUCCESS{"success"}; + + static constexpr const char *SYNC_CUSTOMER_REPO_FILES_SUCCESS{"success"}; + + static constexpr const char *UPDATE_DC_FIRMARE_SUCCESS{"success"}; + + static constexpr const char *OPKG_MARKER{""}; + static constexpr const char *SYNC_MARKER{""}; + static constexpr const char *DC_MARKER{""}; + static constexpr const char *GIT_MARKER{""}; + static constexpr const char *ISMAS_MARKER{""}; + + static constexpr const int PERCENT_CHECK_ISMAS_CONNECIVITY{10}; + static constexpr const int PERCENT_CHECK_UPDATE_REQUEST{20}; + static constexpr const int PERCENT_CHECK_CUSTOMER_REPOSITORY{30}; + static constexpr const int PERCENT_INSTALL_SW_PACKETS_NOACTION{40}; + static constexpr const int PERCENT_INSTALL_SW_PACKETS{50}; + static constexpr const int PERCENT_INSTALL_DC_CONFIGURATION{60}; + static constexpr const int PERCENT_SYNCHRONIZE_REPO_AND_FILESYS{70}; + static constexpr const int PERCENT_UPDATE_DC{80}; + static constexpr const int PERCENT_SHOW_FINAL_STATUS{90}; + + static constexpr const char *DEFAULT_INI_DIR{"/etc/tools/atbupdate/"}; + static constexpr const char *DEFAULT_INSTALL_DIR{"/opt/app/tools/atbupdate/"}; + + int read1stLineOfFile(QString fileName); + QString customerRepoRoot(); + QString customerRepoDir(); + QString customerRepoDcDir(); + QString customerRepoDirName(); + QString repositoryUrl(); + QString branchName(); + bool customerRepoExists(); + std::unique_ptr readSettings(QString const &optionalDirName = ""); + std::unique_ptr dcCandidateToInstall(QString const &dcDirectory); + } + + #endif // UTILS_INTERNAL_H_INCLUDED diff --git a/common/src/System.cpp b/common/src/System.cpp new file mode 100644 index 0000000..2af9af8 --- /dev/null +++ b/common/src/System.cpp @@ -0,0 +1,815 @@ +#include "System.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "version.h" + + + + +const QStringList System::allowedJsonFiles = +std::initializer_list{ + QString("DC2C_conf.json"), + QString("DC2C_cash.json"), + QString("DC2C_device.json"), + QString("DC2C_print01.json"), + QString("DC2C_print02.json"), + QString("DC2C_print03.json"), + QString("DC2C_print04.json"), + QString("DC2C_print05.json"), + QString("DC2C_print06.json"), + QString("DC2C_print07.json"), + QString("DC2C_print08.json"), + QString("DC2C_print09.json"), + QString("DC2C_print10.json"), + QString("DC2C_print11.json"), + QString("DC2C_print12.json"), + QString("DC2C_print13.json"), + QString("DC2C_print14.json"), + QString("DC2C_print15.json"), + QString("DC2C_print16.json"), + QString("DC2C_print17.json"), + QString("DC2C_print18.json"), + QString("DC2C_print19.json"), + QString("DC2C_print20.json"), + QString("DC2C_print21.json"), + QString("DC2C_print22.json"), + QString("DC2C_print23.json"), + QString("DC2C_print24.json"), + QString("DC2C_print25.json"), + QString("DC2C_print26.json"), + QString("DC2C_print27.json"), + QString("DC2C_print28.json"), + QString("DC2C_print29.json"), + QString("DC2C_print30.json"), + QString("DC2C_print31.json"), + QString("DC2C_print32.json") +}; + +QString System::errorMsg = ""; + +System::System(QObject *parent) : + QObject(parent) +{ + +} + + + +/******************************************************************************** + * static function to return error message. + * + */ +QString System::getErrorMessage() +{ + return errorMsg; +} + + + +/******************************************************************************** + * static function to check if a writable SD-card is in the cardslot. + * + * This function only checks a certain path or a mount entry. It does not mount + * a SD-card, this must be provided by the OS (automounter). + * + * returns 1 if a SD-card is available, 0 otherwise. + */ +bool System::checkForSDCard() +{ +#if defined (ARCH_DesktopLinux) + // DEBUG / TEST: + return true; +#endif + + + + if (!test_mount(getSDCardDeviceName(), getSDCardMountPath())) { + qDebug() << "check for SD-card failed: \n" + << " device name is: " << getSDCardDeviceName() + << " mount path is: " << getSDCardMountPath(); + return false; + } + + return true; +} + + +/******************************************************************************** + * static function for umount sd-card. + * + */ +bool System::umountSDCard() +{ +#if defined (ARCH_DesktopLinux) + // DEBUG / TEST: + return true; +#endif + + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + + QString commandString = "umount " + getSDCardMountPath(); + + process.start(commandString); + if (!process.waitForStarted()) { + errorMsg = "System::umountSDCard(): ERROR: waitForStarted()"; + return false; + } + if (!process.waitForFinished(600000)) { + errorMsg = "System::umountSDCard(): ERROR: " + process.errorString(); + qDebug() << errorMsg; + return false; + } + else { + QByteArray bytes = process.readAll(); + QStringList lines = QString(bytes).split("\n"); + foreach (QString line, lines) { + qDebug() << "System::umountSDCard() line: " << line; + } + } + + + return true; +} + + + +/******************************************************************************** + * static function for umount usb-stick. + * + */ +bool System::umountUSBStick() +{ +#if defined (ARCH_DesktopLinux) + // DEBUG / TEST: + return true; +#endif + + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + + QString commandString = "umount " + getUSBMountPath(); + + process.start(commandString); + if (!process.waitForStarted()) { + errorMsg = "System::umountUSBStick(): ERROR: waitForStarted()"; + return false; + } + if (!process.waitForFinished(600000)) { + errorMsg = "System::umountUSBStick(): ERROR: " + process.errorString(); + qDebug() << errorMsg; + return false; + } + else { + QByteArray bytes = process.readAll(); + QStringList lines = QString(bytes).split("\n"); + foreach (QString line, lines) { + qDebug() << "System::umountUSBStick() line: " << line; + } + } + + + return true; +} + + + +/***************************************************************************** + * test, if a certain device is already mounted + * + * e.g. a mounted SD-card on PTU4: + * - device is '/dev/mmcblk0p1' + * - mount path is '/media/mmcblk0p1' + * + * e.g. a mounted USB-Stick on PTU4: + * - device is '/dev/sda' + * - mount path is '/media/sda' + */ +bool System::test_mount(const QString& dev, const QString& mountpoint) +{ + bool mounted = false; + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + + qDebug() << "System::test_mount() dev = " << dev; + qDebug() << "System::test_mount() mountpoint = " << mountpoint; + + //normalize mountpoint: (mountpoint is without trailing '/') + QString tmp_mountpoint = QString(mountpoint).remove(QRegExp("/*$")); + + QString commandString = "mount"; + + process.start(commandString); + if (!process.waitForStarted()) { + errorMsg = "System::test_mount(): ERROR: waitForStarted()"; + return false; + } + if (!process.waitForFinished(600000)) { + errorMsg = "System::test_mount(): ERROR: " + process.errorString(); + qDebug() << errorMsg; + return false; + } + else { + QByteArray bytes = process.readAll(); + QStringList lines = QString(bytes).split("\n"); + foreach (QString line, lines) { + //qDebug() << "System::test_mount() line: " << line; + + //if (line.contains(dev)) qDebug() << "System::test_mount() line contains dev"; + //if (line.contains(tmp_mountpoint)) qDebug() << "System::test_mount() line contains mountpoint"; + + if (line.contains(dev) && line.contains(tmp_mountpoint)) { + qDebug() << "System::test_mount(): " << dev << " is mounted on " << tmp_mountpoint; + mounted = true; + } + } + } + + + return mounted; +} + + +/***************************************************************************** + * test, if a certain directory is existing and readable + */ +bool System::test_dir(const QString& dir) +{ + QFileInfo fileInfo(dir); + if (! fileInfo.isDir() ) { return false; } + + return true; +} + + + +std::optional System::checkForUSBStick(QString const &dirPathUnderMountPath) { +#if defined (ARCH_DesktopLinux) + // DEBUG / TEST: + if (QFileInfo(getUSBMountPath()).isDir()) + return true; + else + return false; +#endif + + QString const &mountPath = getUSBMountPath(dirPathUnderMountPath); + // qCritical() << "MOUNT-PATH" << mountPath; + return mountPath.isEmpty() ? std::nullopt : std::optional(mountPath); +} + +std::optional System::checkForSDCard(QString const &dirPathUnderMountPath) { +#if defined (ARCH_DesktopLinux) + // DEBUG / TEST: + if (QFileInfo(getSDMountPath()).isDir()) + return true; + else + return false; +#endif + + QString const &mountPath = getSDMountPath(dirPathUnderMountPath); + // qCritical() << "MOUNT-PATH" << mountPath; + return mountPath.isEmpty() ? std::nullopt : std::optional(mountPath); +} + + + +/** + * @brief SupportSystem::getUSBMountPath + * @return path where a USB storage device is mounted. + * + * Note, do not return an empty string ("") here because a calling method (which could not be + * identified till now) is relying on this! + */ +QString System::getUSBMountPath(QString const &dirPathUnderMountPath) { + +#if defined (ARCH_DesktopLinux) + // DEBUG / TEST: + return QDir::homePath().append("/APconfigTest/USB"); +#endif + + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + + QStringList mountLine; + + qDebug() << "System::getUSBMountPath()"; + + QRegExp devRegExp = QRegExp("dev/sd*", Qt::CaseSensitive, QRegExp::WildcardUnix); + QRegExp mountRegExp = QRegExp("media/sd*", Qt::CaseSensitive, QRegExp::WildcardUnix); + + QString commandString = "mount"; + + process.start(commandString); + if (!process.waitForStarted()) { + errorMsg = "System::getUSBMountPath(): ERROR: waitForStarted()"; + return ""; + } + if (!process.waitForFinished(600000)) { + errorMsg = "System::getUSBMountPath(): ERROR: " + process.errorString(); + qDebug() << errorMsg; + return ""; + } + else { + QByteArray bytes = process.readAll(); + QStringList lines = QString(bytes).split("\n"); + foreach (QString line, lines) { + qDebug() << "System::getUSBMountPath() line: " << line; + + if (line.contains(devRegExp) && line.contains(mountRegExp)) { + + qDebug() << " -> this line is a usb storage device mount" << line; + + mountLine = line.split(' '); + if (mountLine.size() > 3) { + // qCritical() << "System::getUSBMountPath(): " << mountLine.at(0) << " is mounted on " << mountLine.at(2); + QDir d(QDir::cleanPath(mountLine.at(2) + QDir::separator() + dirPathUnderMountPath)); + if (d.exists()) { + return mountLine.at(2); + } else { + qCritical() << "directory" << d.absolutePath() << "does not exist"; + } + } + } + } + } + + qDebug() << "System::getUSBMountPath() no mounted usb device found!"; + + return ""; +} + +QString System::getSDMountPath(QString const &dirPathUnderMountPath) { + +#if defined (ARCH_DesktopLinux) + // DEBUG / TEST: + return QDir::homePath().append("/APconfigTest/USB"); +#endif + + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + + QStringList mountLine; + + qDebug() << "System::getSDMountPath()"; + + QRegExp devRegExp = QRegExp("dev/mmc*", Qt::CaseSensitive, QRegExp::WildcardUnix); + QRegExp mountRegExp = QRegExp("media/mmc*", Qt::CaseSensitive, QRegExp::WildcardUnix); + + QString commandString = "mount"; + + process.start(commandString); + if (!process.waitForStarted()) { + errorMsg = "System::getSDMountPath(): ERROR: waitForStarted()"; + return ""; + } + if (!process.waitForFinished(600000)) { + errorMsg = "System::getSDMountPath(): ERROR: " + process.errorString(); + qDebug() << errorMsg; + return ""; + } + else { + QByteArray bytes = process.readAll(); + QStringList lines = QString(bytes).split("\n"); + foreach (QString line, lines) { + qDebug() << "System::getSDMountPath() line: " << line; + + if (line.contains(devRegExp) && line.contains(mountRegExp)) { + + qDebug() << " -> this line is a usb storage device mount" << line; + + mountLine = line.split(' '); + if (mountLine.size() > 3) { + // qCritical() << "System::getSDMountPath(): " << mountLine.at(0) << " is mounted on " << mountLine.at(2); + QDir d(QDir::cleanPath(mountLine.at(2) + QDir::separator() + dirPathUnderMountPath)); + if (d.exists()) { + return mountLine.at(2); + } else { + qCritical() << "directory" << d.absolutePath() << "does not exist"; + } + } + } + } + } + + qDebug() << "System::getSDMountPath() no mounted usb device found!"; + + return ""; +} + +QStringList System::getJsonFilesOnUsbStick(QString const &mountPath) { + QStringList jsonFiles; + + // /media/sda2/etc/psa_config + QString const &dirPath = QDir::cleanPath(mountPath + QDir::separator() + "etc" + QDir::separator() + "psa_config"); + QDir d(dirPath); + if (d.exists()) { + QDirIterator it(dirPath, QStringList() << "*.json", QDir::Files, QDirIterator::NoIteratorFlags); + while (it.hasNext()) { + QString const &jsonFile = it.next(); + QFileInfo fi(jsonFile); + if (System::allowedJsonFiles.contains(fi.fileName())) { + jsonFiles << jsonFile; + } else { + qCritical() << "Warning:" << fi.fileName() << "unknown"; + } + } + } + + return jsonFiles; +} + +QString System::getDCFileOnUsbStick(QString const &mountPath) { + QString dcFile; + + // /media/sda2/etc/dc + QString const &dirPath = QDir::cleanPath(mountPath + QDir::separator() + "etc" + QDir::separator() + "dc"); + QDir d(dirPath); + if (d.exists()) { + QFileInfo fi(dirPath + QDir::separator() + "dc2c.bin"); + if (fi.exists()) { + dcFile = fi.absoluteFilePath(); + } + } + + return dcFile; +} + +QString System::getDCFileOnSDCard(QString const &mountPath) { + QString dcFile; + + // /media/sda2/etc/dc + QString const &dirPath = QDir::cleanPath(mountPath + QDir::separator() + "etc" + QDir::separator() + "dc"); + QDir d(dirPath); + if (d.exists()) { + QFileInfo fi(dirPath + QDir::separator() + "dc2c.bin"); + if (fi.exists()) { + dcFile = fi.absoluteFilePath(); + } + } + + return dcFile; +} + +/******************************************************************************** + * static function to check if a mounted sd-card is writable. + * + * Note: the caller must ensure (e.g. by calling checkForSDCard()) that a + * sd-card is mounted. + * + */ +bool System::isSDCardWritable() +{ + QFileInfo fi(getSDCardMountPath()); + + if(fi.isDir() && fi.isWritable()) { + return true; + } + else + { + return false; + } +} + +/******************************************************************************** + * static function to check if a mounted usb-stick is writable. + * + * Note: the caller must ensure (e.g. by calling checkForUSBStick()) that a + * usb-stick is mounted. + * + */ +bool System::isUSBStickWritable() +{ + QFileInfo fi(getUSBMountPath()); + + if(fi.isDir() && fi.isWritable()) { + return true; + } + else + { + return false; + } +} + + +/******************************************************************************** + * wrapper function for getting free space on a sd-card on ptu + * + */ +quint32 System::getFreeDiskSpaceSDCard() +{ +#if defined (ARCH_DesktopLinux) + return getFreeDiskSpace("/home/siegert/server1home"); +#endif + return getFreeDiskSpace(getSDCardMountPath()); +} + + +/******************************************************************************** + * wrapper function for getting free space on a usb storage device on ptu + * + */ +quint32 System::getFreeDiskSpaceUSBStick() +{ +#if defined (ARCH_DesktopLinux) + return getFreeDiskSpace("/home/siegert/server1home"); +#endif + return getFreeDiskSpace(getUSBMountPath()); +} + + +/******************************************************************************** + * static function to read free disk space + * + * example output of 'df' command on ptu: + * # df + * Filesystem 1K-blocks Used Available Use% Mounted on + * ubi0:rootfs 111704 78940 32764 71% / + * devtmpfs 62072 0 62072 0% /dev + * tmpfs 62332 0 62332 0% /dev/shm + * tmpfs 62332 416 61916 1% /tmp + * tmpfs 62332 24 62308 1% /run + * ubi0:data 222260 5892 216368 3% /opt + * tmpfs 62332 0 62332 0% /media + * /dev/mmcblk0p1 3878912 87360 3791552 3% /media/mmcblk0p1 + * + * in this case, if we want to get the free space on the sd-card the methode should + * be called: + * getFreeDiskSpace("/dev/mmcblk0p1") + * the result should be: + * 3791552 + * + */ +quint32 System::getFreeDiskSpace(const QString& dev) +{ + quint32 availableSpace = 0; + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + + //qDebug() << "System::getFreeDiskSpace() dev = " << dev; + + QString commandString = "df"; + + process.start(commandString); + if (!process.waitForStarted()) { + errorMsg = "System::getFreeDiskSpace(): ERROR: waitForStarted()"; + return availableSpace; + } + if (!process.waitForFinished(600000)) { + errorMsg = "System::getFreeDiskSpace(): ERROR: " + process.errorString(); + qDebug() << errorMsg; + return availableSpace; + } + else { + QByteArray bytes = process.readAll(); + QStringList lines = QString(bytes).split("\n"); + foreach (QString line, lines) { + //qDebug() << "System::getFreeDiskSpace() line: " << line; + + + + if (line.contains(dev)) { + //qDebug() << "System::getFreeDiskSpace() line contains dev"; + + QStringList processResultLine = line.split(' ', QString::SkipEmptyParts); + if (processResultLine.size() >= 4) { + bool ok; + availableSpace = processResultLine.at(3).toLong(&ok); + if (!ok) { + return 0; + } + qDebug() << "System::getFreeDiskSpace() availableSpace = " << availableSpace; + } + else { + return availableSpace; + } + + + } + } + } + + + return availableSpace; + + +} + + + + + + +/******************************************************************************** + * static function to create a backup of logfiles + * + * 'targzfile' is an absolute filename for ar tar.gz file wich will be created + * 'filelistfile' is an absolute filename of a file containing all names of files + * which should be backed up. + * + * returns: + * 1 on success + * 0 on failure + * + */ +quint8 System::createLogFileBackup(const QString & targzfile, const QString &filelistfile) +{ + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + + qDebug() << "System::createLogFileBackup() targzfile = " << targzfile; + qDebug() << "System::createLogFileBackup() filelistfile = " << filelistfile; + + QString commandString = "tar hczvf " + targzfile + " -T " + filelistfile; + + qDebug() << "System::createLogFileBackup() tar command:" << commandString; + + process.start(commandString); + if (!process.waitForStarted()) { + errorMsg = "System::createLogFileBackup(): ERROR: waitForStarted()"; + return 1; + } + if (!process.waitForFinished(600000)) { + errorMsg = "System::createLogFileBackup(): ERROR: " + process.errorString(); + qDebug() << errorMsg; + return 1; + } + else { + // DEBUG + //QByteArray bytes = process.readAll(); + //QStringList lines = QString(bytes).split("\n"); + //foreach (QString line, lines) { + // qDebug() << "System::createLogFileBackup() line: " << line; + //} + } + + QString resultMessage; + QProcess::ExitStatus exitStatus = process.exitStatus(); + int exitCode = process.exitCode(); + + if (exitStatus == QProcess::CrashExit) { + resultMessage = "local backup program crashed"; + qCritical() << resultMessage; + return 1; + } + else + + /** + * note for exit codes of process tar: + * see: http://www.gnu.org/software/tar/manual/html_section/tar_19.html#Synopsis + * + * tar returns exitCode 2 if some files in logFileList could not be read (e.g. if they are not existing) + * however, tar-archive is created although! + * This means success for our needs! + */ + + if ( (exitCode != 0) && (exitCode != 2) && (exitStatus == QProcess::NormalExit) ) { + resultMessage = "Local Backup file \"" + targzfile + "\" with exit code = " + QString::number(exitCode); + qCritical() << resultMessage; + return 1; + } + else { + resultMessage = "Local Backup file \"" + targzfile + "\" was created successfully."; + qCritical() << resultMessage; + return 0; + } + + return 0; + +} + + + +/***************************************************************************** + * get SD-card mount path. + */ +QString System::getSDCardMountPath() +{ +#if defined (ARCH_PTU4) + return "/media/mmcblk0p1"; +#elif defined (ARCH_PTU5) + return "/media/mmcblk0p1"; +#elif defined (ARCH_DesktopLinux) + // DEBUG / TEST: + return "/home/siegert/APconfigTest"; +#endif + return ""; +} + + +/***************************************************************************** + * get SD-card device name. + */ +QString System::getSDCardDeviceName() +{ +#if defined (ARCH_PTU4) + return "/dev/mmcblk0p1"; +#elif defined (ARCH_PTU5) + return "/dev/mmcblk0p1"; +#elif defined (ARCH_DesktopLinux) + return "/dev/sdf1"; +#endif + return ""; +} + + + + + + +/******************************************************************************** + * static function to build and return a unique device id. + * This id consists currently only of the mac-address of network device eth0. + */ +QString System::getUniqueDeviceId() +{ + QString filename = "/sys/class/net/eth0/address"; + QString result; + + QFile file(filename); + if(!file.open(QIODevice::ReadOnly)) { + qCritical() << "System::getUniqueDeviceId() could not open file: " << filename; + result = "0"; + } + else { + QTextStream in(&file); + result = in.readLine(20); + } + + file.close(); + + result.remove(':'); + + return result; + +} + + + +QString System::readStringFromFile(const QString & filename) +{ +#if defined (ARCH_PTU4) + QFileInfo fileinfo(filename); +#else + QFileInfo fileinfo(QDir::homePath() + filename); +#endif + if (! fileinfo.isReadable() ) { + qDebug() << "System::readStringFromFile(): \"" << filename << "\" is not readable"; + return ""; + } + +#if defined (ARCH_PTU4) + QFile file(filename); +#else + QFile file(QDir::homePath() + filename); +#endif + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "System::readStringFromFile() cannot open file: " << filename; + return ""; + } + + QTextStream in(&file); + + QString stringValue = in.readLine(100); + qDebug() << "System::readStringFromFile() stringValue = " << stringValue; + + file.close(); + + return stringValue; +} + + +int System::readIntFromFile(const QString & filename) +{ + bool ok; + int result = (int)readStringFromFile(filename).toInt(&ok); + + if (!ok) { + result = 0; + } + + return result; +} + + +/**************************************************************************** + * read ptu internal data + * + */ +QString System::getPTU4SerialNumber() +{ + return readStringFromFile("/sys/bus/i2c/devices/0-0050/SNO"); +} + +QString System::getPTU4MACAddress() +{ + return readStringFromFile("/sys/bus/i2c/devices/0-0050/MAC"); +} + diff --git a/common/src/command.cpp b/common/src/command.cpp new file mode 100644 index 0000000..28b8c19 --- /dev/null +++ b/common/src/command.cpp @@ -0,0 +1,128 @@ +#include "command.h" + +#include +#include +#include +#include +#include + +Command::Command(QString command, QStringList args, QString workingDirectory, + bool verbose, int start_timeout, int finish_timeout) + : m_command(command.trimmed()) + , m_commandResult("") + , m_waitForStartTimeout(start_timeout) + , m_waitForFinishTimeout(finish_timeout) + , m_verbose(verbose) + , m_exitCode(-1) + , m_workingDirectory(workingDirectory) + , m_args(args) { + m_p.reset(new QProcess(this)); + if (m_p) { + m_p->setWorkingDirectory(workingDirectory); + m_p->setProcessChannelMode(QProcess::MergedChannels); + + connect(m_p.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput())); + connect(m_p.get(), SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError())); + } +} + +void Command::readyReadStandardOutput() { + QProcess *p = (QProcess *)sender(); + if (p) { + QString s = p->readAllStandardOutput(); + if (m_verbose) { +// qCritical().noquote() << s; + m_commandResult += s; + } + } +} + +void Command::readyReadStandardError() { + QProcess *p = (QProcess *)sender(); + if (p) { + QString s = p->readAllStandardError(); +// qCritical().noquote() << s; + m_commandResult += s; + } +} + + +QString Command::getCommandResult(bool reset) { + if (reset == false) { + return m_commandResult; + } + + QString commandResult = m_commandResult; + m_commandResult.clear(); + + return commandResult; +} + +bool Command::exec() { + + if (!m_args.isEmpty()) { + m_p->start(m_command, m_args); + } else { + m_p->start(m_command); + } + + qint64 const start = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + bool started = false; + if ((started = m_p->waitForStarted(m_waitForStartTimeout)) == true) { + // qCritical() << "PROCESS" << m_command << "STARTED IN" << m_p->workingDirectory(); + if (m_p->state() == QProcess::ProcessState::Running) { + // qDebug() << "PROCESS" << m_command << "RUNNING IN" << p->workingDirectory(); + // wait forever for git/opkg-commands to finish + int wait = m_waitForFinishTimeout; + if (m_command.trimmed().startsWith("git", Qt::CaseInsensitive) || + m_command.trimmed().startsWith("opkg", Qt::CaseInsensitive)) { + wait = -1; + } + bool const no_timeout = m_p->waitForFinished(wait); + if (no_timeout) { + // qDebug() << "PROCESS" << m_command << "FINISHED IN" << p->workingDirectory(); + if (m_p->exitStatus() == QProcess::NormalExit) { + if ((m_exitCode = m_p->exitCode()) == 0) { + qCritical().noquote() << m_p->readAllStandardOutput(); + //qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + //qDebug() << "EXECUTED" << m_command + // << QString("(runtime %1ms)").arg(end-start) + // << "with code" << m_exitCode + // << "IN" << m_p->workingDirectory(); + return true; + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "EXECUTED" << m_command + << QString("(runtime %1ms)").arg(end-start) + << "with code" << m_exitCode + << "IN" << m_p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command << "CRASHED with code" + << m_p->exitCode() + << QString("(after %1ms)").arg(end-start) + << "IN" << m_p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command + << "DID NOT FINISH WITH" << wait + << "MS IN" << m_p->workingDirectory() + << QString("(runtime %1ms)").arg(end-start); + } + } else { + qCritical() << "WRONG PROCESS STATE" << m_p->state() + << "IN" << m_p->workingDirectory(); + } + } else { + qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qCritical() << "PROCESS" << m_command << "TIMEOUT AT START" + << QString("(runtime %1ms)").arg(end-start) + << "IN" << m_p->workingDirectory() << m_waitForStartTimeout; + } + + return false; +} + diff --git a/common/src/message_handler.cpp b/common/src/message_handler.cpp new file mode 100755 index 0000000..996bd2f --- /dev/null +++ b/common/src/message_handler.cpp @@ -0,0 +1,97 @@ +#include "message_handler.h" + +#include +#include +#include +#include +#include + + +static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " }; +static bool installedMsgHandler = false; +static int debugLevel = LOG_NOTICE; + +int getDebugLevel() { return debugLevel; } +void setDebugLevel(int newDebugLevel) { + debugLevel = newDebugLevel; +} + +bool messageHandlerInstalled() { + return installedMsgHandler; +} + +QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) { + installedMsgHandler = (handler != 0); + static QtMessageHandler prevHandler = nullptr; + if (handler) { + prevHandler = qInstallMessageHandler(handler); + return prevHandler; + } else { + return qInstallMessageHandler(prevHandler); + } +} + +/// +/// \brief Print message according to given debug level. +/// +/// \note Install this function using qInstallMsgHandler(). +/// +/// int main(int argc, char **argv) { +/// installMsgHandler(atbDebugOutput); +/// QApplication app(argc, argv); +/// ... +/// return app.exec(); +/// } +/// +#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { + Q_UNUSED(context); + QString const localMsg = QString(DBG_NAME[type]) + msg.toLocal8Bit(); + + switch (debugLevel) { + case LOG_DEBUG: { // debug-level message + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } break; + case LOG_INFO: { // informational message + if (type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_NOTICE: { // normal, but significant, condition + if (type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_WARNING: { // warning conditions + if (type != QtInfoMsg && type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_ERR: { // error conditions + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_CRIT: { // critical conditions + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_ALERT: { // action must be taken immediately + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_EMERG: { // system is unusable + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + default: { + //fprintf(stderr, "%s No ErrorLevel defined! %s\n", + // datetime.toStdString().c_str(), msg.toStdString().c_str()); + } + } +} +#endif + diff --git a/common/src/utils_internal.cpp b/common/src/utils_internal.cpp new file mode 100644 index 0000000..365c6ee --- /dev/null +++ b/common/src/utils_internal.cpp @@ -0,0 +1,167 @@ +#include "utils_internal.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace internal { + +int read1stLineOfFile(QString fileName) { + QFile f(fileName); + if (f.exists()) { + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&f); + in.setCodec("UTF-8"); + while(!in.atEnd()) { + return in.readLine().toInt(); + } + } + } + return -1; +} + +QString customerRepoRoot() { + return "/opt/app/tools/atbupdate/"; +} + +QString customerRepoDirName() { + int const customerNr = read1stLineOfFile("/mnt/system_data/cust_nr"); + return (customerNr != -1) ? QString("customer_%1").arg(customerNr) : ""; +} + +QString customerRepoDir() { + QString const &n = customerRepoDirName(); + QString const &r = customerRepoRoot(); + return !n.isEmpty() ? QDir::cleanPath(r + QDir::separator() + n) : ""; +} + +QString customerRepoDcDir() { + QString const &r = customerRepoDir(); + return QDir::cleanPath(r + QDir::separator() + "etc/dc/"); +} + +bool customerRepoExists() { + QString const repoDir{customerRepoDir()}; + return !repoDir.isEmpty() ? QDir(repoDir).exists() : false; +} + +QString repositoryUrl() { + return "gitea@ptu-config.atb-comm.de:ATB/"; +} + +QString branchName() { + int const zoneNr = read1stLineOfFile("/mnt/system_data/zone_nr"); + if (zoneNr != -1) { + return QString("zg1/zone%1").arg(zoneNr); + } + return ""; +} + +std::unique_ptr readSettings(QString const &optionalDirName) { + std::unique_ptr settings{std::make_unique()}; + + //QString const fileName{settings->applicationName() + ".ini"}; + QString const fileName{"ATBUpdateTool.ini"}; + QDir d; + + if (!optionalDirName.isEmpty()) { + d = QDir{optionalDirName}; + if (d.exists()) { // try to find ini-file under optionalDirname + QFileInfo fi{d, optionalDirName}; + if (fi.exists()) { + settings.reset(new QSettings(fi.absoluteFilePath(), QSettings::IniFormat)); + return settings; + } else { + qCritical() << fi.absoluteFilePath() << "not found." + << "Try" << internal::DEFAULT_INI_DIR; + } + } else { + qCritical() << optionalDirName << "not found." + << "Try" << internal::DEFAULT_INSTALL_DIR; + } + } + d = internal::DEFAULT_INI_DIR; + if (d.exists()) { // try to find ini-file under /etc/tools/atbupdate + QFileInfo fi{d, fileName}; + if (fi.exists()) { + settings.reset(new QSettings(fi.absoluteFilePath(), QSettings::IniFormat)); + return settings; + } else { + qCritical() << fi.absoluteFilePath() << "not found." + << "Try" << internal::DEFAULT_INSTALL_DIR; + } + } else { + qCritical() << internal::DEFAULT_INI_DIR << "not found." + << "Try" << internal::DEFAULT_INSTALL_DIR; + } + d = QDir{internal::DEFAULT_INSTALL_DIR}; + if (d.exists()) { // try to find ini-file under /opt/app/tools/atbupdate + QFileInfo fi{d, fileName}; + if (fi.exists()) { + settings.reset(new QSettings(fi.absoluteFilePath(), QSettings::IniFormat)); + return settings; + } else { + qCritical() << fi.absoluteFilePath() << "not found."; + } + } else { + qCritical() << internal::DEFAULT_INSTALL_DIR << "not found."; + } + + return settings; +} + +std::unique_ptr dcCandidateToInstall(QString const &dcDirectory) { + std::unique_ptr dcCandidate{nullptr}; + + qCritical() << __func__ << __LINE__ << dcDirectory; + + QDir dcDir{dcDirectory.isEmpty() ? customerRepoDcDir() : dcDirectory}; + if (dcDir.exists()) { + + QFileInfoList fileInfoList = + dcDir.entryInfoList(QStringList("*.bin"), + QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks); + + QFileInfo dc2cbin{dcDir.absoluteFilePath("dc2c.bin")}; + + + if (dc2cbin.exists()) { + + QCryptographicHash md5gen(QCryptographicHash::Md5); + QByteArray ba_dc2cbin{}; + { + QFile f{dc2cbin.absoluteFilePath()}; + if (f.open(QIODevice::ReadOnly)) { + md5gen.addData(f.readAll()); + ba_dc2cbin = md5gen.result(); + md5gen.reset(); + } + } + + if (ba_dc2cbin.size() > 0) { + QFileInfoList::const_iterator it; + for (it = fileInfoList.cbegin(); it != fileInfoList.cend(); ++it) { + if (it->absoluteFilePath() != dc2cbin.absoluteFilePath()) { + QFile f{it->absoluteFilePath()}; + if (f.open(QIODevice::ReadOnly)) { + md5gen.addData(f.readAll()); + if (ba_dc2cbin == md5gen.result()) { + dcCandidate.reset(new QString(f.fileName())); + break; + } + md5gen.reset(); + } + } + } + } + } + } + + return dcCandidate; +} + +} // namespace internal diff --git a/on_update_failure.sh b/on_update_failure.sh new file mode 100755 index 0000000..706ff28 --- /dev/null +++ b/on_update_failure.sh @@ -0,0 +1,184 @@ +#!/bin/bash + +# set -x + +# +############################################################################# +# Check if the ATBUpdateTool(.service) did not send U0001 (sucess), +# U0002 (activated) or U0003 (error) to ISMAS, including the case that the +# tool did not crash or was not killed under other circumstances. +# +# This script will be called in the associated atbupdatetool.service (see +# /lib/systemd/system/atbupdatetool.service). +# +############################################################################### + +readonly OUT_FILE=/tmp/out.txt +readonly SCRIPT_NAME=$(basename $0) + +if [ -z "$START_DATE" ] +then + START_DATE=$(date +"%Y-%m-%d00:00:00") +fi + +if [ -z "$STOP_DATE" ] +then + STOP_DATE=$(date +"%Y-%m-%d%H:%M:%S") +fi + +echo "EXIT_CODE=$EXIT_CODE" +echo "SERVICE_RESULT=$SERVICE_RESULT" +echo "EXIT_STATUS=$EXIT_STATUS" +echo "START_DATE=$START_DATE" +echo "STOP_DATE=$STOP_DATE" + + +last_cmd_event () { + # output json; look for start of CMD_EVENT; look for }}; extract everything between; finally re-add }}. + # could also look for ">>>", but this might change in the future. + local json=$(\ + journalctl -u atbupdatetool --since="$START_DATE" --until="$STOP_DATE" --output=json-pretty |\ + grep "#M=APISM#C=CMD_EVENT#J=" |\ + awk '{split($0, a, "#M=APISM#C=CMD_EVENT#J="); print a[2]}' |\ + awk '{split($0, a, /\}[\s]*\}/); print a[1]}' |\ + tr -d '\\' |\ + tail -1) + json+="}}" + echo "$json" +} + +last_percent_value () { + echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(\"PERCENT\"\\s*:\)\(\\s*[0-9]\+\)\(.*$\)/\3/p" | xargs | awk '{print $NF}') +} + +last_step () { + echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(\"PERCENT\"\\s*:\)\(\\s*[0-9]\+\)\(.*$\)/\1\2\3\4/p" | tail -1 | awk '{split($0, a, ","); print a[8]}' | awk '{split($0, b, " : "); print b[2]}' | tr -d '"\\') +} + +last_step_result () { + echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(\"PERCENT\"\\s*:\)\(\\s*[0-9]\+\)\(.*$\)/\1\2\3\4/p" | tail -1 | awk '{split($0, a, ","); print a[9]}' | awk '{split($0, b, " : "); print b[2]}' | tr -d '"\\') +} + +pid_atbupdatetool () { + echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(ATBUpdateTool\[\)\(\\s*[0-9]\+\)\(\].*$\)/\3/p" | uniq) +} + +last_event () { + echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(\"PERCENT\"\\s*:\)\(\\s*[0-9]\+\)\(.*$\)/\1\2\3\4/p" | tail -1 | awk '{split($0, a, ","); print a[4]}' | awk '{split($0, b, ":"); print b[2]}' | tr -d ' ' | tr -d '"\\') +} + +sendU0003 () { + LAST_CMD_EVENT=$(last_cmd_event) + echo "LAST=$LAST_CMD_EVENT" + + local json_detected=0 + local temp + local x + + if [[ ! -z "$LAST_CMD_EVENT" ]] + then + temp=$(mktemp -p /tmp) + echo "$LAST_CMD_EVENT" > "$temp" + x=$(jq .EVENT $temp) + if [ $? -eq 0 ]; then + json_detected=1 + fi + fi + + if [ $json_detected -eq 1 ] + then + # local pid=$(jq .EVENT_ID $temp) + + # if json has been detected, use "jq". + + x=$(jq .PARAMETER.PERCENT $temp | tr -d '"') + [[ -z "$x" ]] && PERCENT=0 || PERCENT=$x + + x=$(jq .PARAMETER.STEP $temp | tr -d '"') + [[ -z "$x" ]] && STEP="unknown" || STEP="$x" + + x=$(jq .PARAMETER.STEP_RESULT $temp | tr -d '"') + [[ -z "$x" ]] && STEP_RESULT="unknown" || STEP_RESULT="$x" + + x=$(pid_atbupdatetool) + [[ -z "$x" ]] && PID=99999 || PID=$x + + x=$(last_event) + [[ -z "$x" ]] && EVENT="unknown" || EVENT="$x" + + else + # cannot parse valid json. try original journal text file. + + x=$(last_percent_value) + [[ -z "$x" ]] && PERCENT=0 || PERCENT=$x + + x=$(last_step) + [[ -z "$x" ]] && STEP="unknown" || STEP="$x" + + x=$(last_step_result) + [[ -z "$x" ]] && STEP_RESULT="unknown" || STEP_RESULT="$x" + + x=$(pid_atbupdatetool) + [[ -z "$x" ]] && PID=99999 || PID=$x + + x=$(last_event) + [[ -z "$x" ]] && EVENT="unknown" || EVENT="$x" + fi + + echo "PERCENT=$PERCENT" + echo "STEP=$STEP" + echo "STEP_RESULT=$STEP_RESULT" + echo "PID=$PID" + echo "EVENT=$EVENT" + + # build json to be sent to ISMAS with "U0003" + + CURRENT_DATE=$(date +"%Y-%m-%d %H:%M:%S.000%z") + U0003Msg="{\ + \"REASON\":\"SW_UP\",\ + \"TIMESTAMP\":\"$CURRENT_DATE\",\ + \"EVENT_ID\":\"$PID\",\ + \"EVENT\":\"U0003\",\ + \"EVENTSTATE\":1,\ + \"PARAMETER\": {\ + \"PERCENT\" : $PERCENT,\ + \"RESULTCODE\" : 99,\ + \"STEP\" : \"$1 -> monitored last step: $STEP\",\ + \"STEP_RESULT\" : \"$1 -> monitored last step result: $STEP_RESULT\",\ + \"VERSION\" : \"\"\ + }\ + }" + + echo -e "U0003Msg=$U0003Msg" + + (echo "#M=APISM#C=CMD_EVENT#J=$U0003Msg"; sleep 5) | nc localhost 7777 +} + +# save journal for the last run of atbupdatetool in $OUT_FILE +echo $(journalctl -u atbupdatetool --since="$START_DATE" --until=$STOP_DATE) > $OUT_FILE + +if [[ "$EXIT_CODE" = "exited" && "$SERVICE_RESULT" = "success" && "$EXIT_STATUS" = "0" ]] +then + # almost normal run; at least there was no crash. + if cat $OUT_FILE | grep -o "U0003" | wc -l; then + echo "$SCRIPT_NAME: Failure code U0003 already sent to ISMAS" + exit 0 + fi + + if cat $OUT_FILE | grep -o "U0002" | wc -l; then + echo "$SCRIPT_NAME: Success code U0002 already sent to ISMAS" + exit 0 + else + sendU0003 "$SCRIPT_NAME: Success code U0002 not sent to ISMAS" + fi + + if cat $OUT_FILE | grep -o "U0001" | wc -l; then + echo "$SCRIPT_NAME: Success code U0001 already sent to ISMAS" + exit 0 + else + sendU0003 "$SCRIPT_NAME: Success code U0001 not sent to ISMAS" + fi +else + # something unexpected has happened. + sendU0003 "$SCRIPT_NAME: EXIT_CODE=$EXIT_CODE;SERVICE_RESULT=$SERVICE_RESULT;EXIT_STATUS=$EXIT_STATUS" +fi diff --git a/plugins/interfaces.h b/plugins/interfaces.h deleted file mode 100755 index d9f31b6..0000000 --- a/plugins/interfaces.h +++ /dev/null @@ -1,1427 +0,0 @@ -#ifndef INTERFACE_H -#define INTERFACE_H - -#include - - - -struct T_emp -{ - - // Fixdata from EMP: - uint8_t shaft; // = changer level - uint16_t countryCode; - uint8_t scale; - uint8_t decimals; - uint8_t coinValues[16]; - uint16_t routing; - - // Master specs: - uint8_t gotSetup; // 1: got specifications from master 0: no specs - uint16_t coinAccept; // bit 0 = coin1 bit H=accept - uint8_t tokenChannel; - uint16_t denomination[16]; - - // dynamic: - uint8_t state; // step counter of EMP (electronic coin checker) FSM (finite state machine): - // 0=start command - // 1=powered, do emp ini, send reset - // 2=delay - // 3=wait for response, requesting status after response - // 4,5 through, startup - // 6: wait for status - // 7: through, startup - // 8: IDLE state. EMP is up and ready, polling is running - // 9: polling on, payment not yet on - // 10: payment, check coins - // 11: through - // 12: wait 1s for last coin - // 90: stop all, 1s delay - // 99: off, all stopped - - - uint8_t pollingRunning; - uint8_t paymentRunning; - -}; - -struct Trtc_DateTime -{ - uint8_t rtc_hour; - uint8_t rtc_min; - uint8_t rtc_sec; - uint8_t rtc_dayOfMonth; - uint8_t rtc_month; - uint8_t rtc_year; - uint8_t rtc_dayOfWeek; -}; - -struct Tprn_hw_state -{ - // hardware (IO's) - bool powerRdBk; // prn pwr is on - bool rsSwOk; // serial switch (printer or modem) is set to printer - bool rsDrvOk; // RS232 converter for PTU, Printer and Modem in on - - bool ReadyLine; // HW signal from printer showing ready - bool inIdle; // powered and free from errors - bool paperNearEnd; // paper roll runs out - bool noPaper; - bool ErrorTemp; - bool HeadOpen; - bool cutterJam; - bool noResponse; // printer is not connected, cable broken, wrong baudrate - bool badResponse; -}; - - struct Tprn_currentSettings -{ - uint8_t currFont; - uint8_t currSize; - uint8_t currHeigth; - uint8_t currWidth; - bool nowBold; - bool nowInvers; - bool nowUnderlined; - uint8_t currDensity; - uint8_t currSpeed; - bool nowAligned; -}; - -// obsolete - struct T_dynDat -{ - uint8_t licensePlate[8]; - uint8_t vendingPrice[8]; - uint8_t parkingEnd[8]; - uint8_t currentTime[8]; - uint8_t currentDate[8]; - uint8_t dynDat5[8]; - uint8_t dynDat6[8]; - uint8_t dynDat7[8]; -}; - - -struct T_vaultRecord -{ - - // Kassenbeleg (Abrechnungsdatensatz = Kassenwechsel-Datensatz) - char startbuffer[4]; // Psa> // never move or change this 1st entry - uint16_t AccountingNumber; - uint16_t CUNU; - uint16_t MANU; - uint16_t resint1; - //uint16_t resint2; - - char label1buffer[4]; // tim> - uint8_t year; - uint8_t month; - uint8_t dom; - uint8_t hour; - uint8_t min; - uint8_t sec; - uint8_t DoW; - uint8_t reschar3; - - char label2buffer[4]; // abs> - uint32_t AbsIncome1; - uint32_t AbsReserve; - uint32_t AbsNrOfCuts; - - char label3buffer[4]; // mw > - - // Verkauf, Tuer zu: - uint32_t VKcoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen - uint32_t VKcoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben - - // Service, Tuer offen: - uint16_t ServCoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen - uint16_t ServCoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben - uint16_t resint3; - uint16_t resint4; - uint16_t currentTubeContent[6]; // nur fuer Wechsler, aktueller Fuellstand - uint16_t resint5; - uint16_t resint6; - - char label4buffer[4]; // box> - uint16_t coinsInVault[16]; - uint16_t billsInStacker[8]; - - char label5buffer[4]; // val> - // actually constant unless exchange rate is changed - uint16_t coinDenomination[16]; // 5..50000 (z.B. 2? sind in Ungarn 760Ft) - uint16_t billDenom[8]; - uint16_t tubeDenom[6]; - uint16_t exchangeRate; - uint16_t resint9; - - // new from 1.8.23 - uint32_t cutsSinceCBchange; - uint32_t CBcontent_cent; - uint32_t CBnrofCoins; - - char endofblock[4]; // end -// 332 bytes - - uint16_t CRC16; // Xmodem16 from startbuffer[0] to endofblock[3] - uint16_t resint11; - char endofRecord[4]; // ---- - -}; - -struct T_moduleCondition -{ - // store conditon of all system components, hold in RAM - // 0 means unknown, not yet tested/used - // 1 means OK - // 50..99 = HINT / Notification - // 100..150 = WARNING - // 200..250 = ERROR - - uint8_t ram; - uint8_t intEe; - uint8_t extEe; - - uint8_t rtc; // 1: time/date OK 100: time not plausible 200: hardware error - uint8_t boardHw; - uint8_t printer; - uint8_t modem; - - uint8_t signal; // 1...99 - uint8_t regist; // 100:not 1:reg 2:ping OK 3:gotTime - uint8_t mdbBus; - uint8_t coinChecker; // EMP, OMP or mei-cashflow - - uint8_t coinEscrow; - uint8_t mifareReader; - uint8_t creditTerm; - uint8_t coinReject; - - uint8_t coinSafe; - uint8_t billSafe; - uint8_t voltage; // 1:11..14V - uint8_t temper; - - uint8_t poweronTest; - uint8_t doorState; // 1: alles zu 200: tuer offen + bit1(S) +bit2(CB) + bit3(CB) - uint8_t doorWasOpened; // 1: all doors are closed 200: any door was just opened - uint8_t changer; // can only be tested by usage - - uint8_t coinBlocker; // can only be tested by usage - uint8_t billReader; // can only be tested by usage - uint8_t ResetReason; - uint8_t allModulesChecked; - - uint8_t alarmState; - uint8_t res11; - uint8_t res12; - uint8_t res13; -// 31 -}; - -struct T_dynamicCondition -{ - char allDoorsDebounced; - char openedAuthorized; - uint8_t CBinDebounced; // 0:fehlt 1:drin - char upperDoor; // 99: undefined 0:closed 1:open - char middleDoor; // 99: undefined 0:closed 1:open - char lowerDoor; // 99: undefined 0:closed 1:open - char reserve; - char billBox; - char modeAbrech; - char onAlarm; // 0:alarm aus 1:alarm 2:alarm mit Sirene 3: Sirenentest - char nowCardTest; - char nowPayment; // not used, always 0 - char lastMifCardType; - uint8_t lastSDoorState; - uint8_t lastVDoorState; - uint8_t lastCBstate; - char paymentInProgress; - // 0: stopped by timeout - // 1: running 2: wait4lastCoin - // 3: payment stopped manually, coins in Escrow - // 4: payment stopped autom, amount collected, coins in Escrow - // 5: payment stopped, escrow full, coins in Escrow - // 6: coins encashed 7:coins returned - // 8: CoinChecker or MDB on Error - - - char res1; - uint16_t U_Batt; - uint16_t Temperatur; - uint16_t nrCoinsInBox; - uint32_t amountInBox; - uint32_t totalTransVolume; - uint32_t totalNrOfVends; - char jsonValid_config; - char jsonValid_device; - char jsonValid_cash; - char jsonValid_print; - char jsonValid_serial; - char jsonValid_time; - char lastFileType; -// 44 - uint8_t MifCardHolder[8]; - uint8_t resultOfLastTemplPrint; - // 0: unknown or printing in progress - // 1: OK, doc was printed 2: error, doc was not printed - uint8_t lastPrinterStatus; - // 0: printer OK - // bit0: near paper end bit1: no paper - // bit2: temperature error bit3: error head open - // bit4: paper jam in cutter - // bit6: no response bit7: serial rec. error - // bit5: printer not ready - uint8_t startupTestIsRunning; - - //54 -}; - -struct T_extTime -{ - uint8_t Hours; - uint8_t Min; - uint8_t Sec; - uint8_t Year; - uint8_t Month; - uint8_t Day; - uint8_t DOW; - uint8_t res1; - uint16_t MinOfDay; - uint16_t res2; - uint32_t SecOfDay; - uint8_t isLeapYear; - uint8_t nxtLeapYear; - uint8_t lastLeapYear; - uint8_t hoursOfThisWeek; - uint16_t minutesOfThisWeek; - uint16_t hoursOfThisMonth; - uint16_t daysOfThisYear; - uint16_t GetHoursOfYear; - uint16_t res3; - uint32_t GetMinutesOfYear; - uint8_t getWakeIntvSec; - uint8_t res4; - uint16_t res5; - uint32_t MinutesOfMillenium; - -}; - - -typedef uint8_t UCHAR; -typedef uint16_t UINT; - -struct T_devices -{ - // set by master, used(1) or notused (0) or type 2....20 - - UCHAR kindOfPrinter; // 0:off 1:Gebe - UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza Csquare (MW) - UCHAR kindOfMifareReader; // by now only stronglink SL025 =1 - UCHAR solarPower; // 1:sleep allowed 0: no sleep - //UCHAR suppressSleepMode; // 0:sleep allowed 1: no sleep - - UCHAR kindOfModem; // 0:off 1:Sunlink - UCHAR kindOfCreditcard; // 0:off 1:Feig NFC - UCHAR CoinEscrow; - UCHAR CoinRejectUnit; - - UCHAR CoinShutter; - UCHAR BillAcceptor; - UCHAR usevaultLock; - UCHAR autoAlarm; // 1: switch on siren for 1min in doors opened unauthorized - - UCHAR autoOpen; // 1: open door covers after valid ATBcard - UCHAR printAccReceipt; // 0/1 - UCHAR printDoorReceipt; - UCHAR printTokenTicket; - - UINT VaultFullWarnLevel; - UINT VaultFullErrorLevel; - UINT BattEmptyWarnLevel; - UINT BattEmptyErrorLevel; - -}; - - - -class hwinf -{ - -public: - virtual ~hwinf() {} - - - - // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ - // Use serial interface and protocol stack in Cashagent-Library - // Sending Output data to DeviceController DC2b - // Sending input requests to DC2 (single or auto-batch) - // Getting input data as receiver payload - // Furthermore the Cashagent-Library answers with status strings about sending and reading result - // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ - - - virtual bool dc_openSerial(int BaudNr, QString BaudStr, QString ComName, uint8_t connect) const =0; - // Command: open serial interface - // BaudNr: 0:1200 1:9600 2:19200 3:38400 4:57600 5:115200 - // BaudStr: for exapmle "19200" - // ComName: for example "COM48" - // connect: 0, 1 - - virtual void dc_closeSerial(void) const =0; - // Command: close serial interface in order to save power while power down - // or if another port must be used - - - virtual bool dc_isPortOpen(void) const =0; - // returns true if port open (don't send unless open. Sending to closed port will crash program) - - virtual void dc_autoRequest(bool on) const =0; - // on = true: select that all READ-Requests are sent automatically - // on = false: select that all READ-Requests are sent manually one by one - // Every input information from DC2 must be requested - // ( digital and analog sensors, get time/date, get status information ) - - virtual void dc_requTestResponse() const =0; - // tell DC2 to send a test-string, useful to see if cable and baudrate is OK - - virtual bool dc_readAnswTestResponse() const =0; - // retval: true: test was successful, got right response - - virtual uint8_t dc_isRequestDone(void) const =0; - // retval: 0: request is still in progress - // 1: answer from DC2 was OK - // 2: wrong answer from DC2 - - virtual uint16_t dc_getCompletePayLoad(uint16_t plBufSiz, uint8_t *payLoad) const =0; - // get data back in "payLoad", max 64 byte, can be used for diagnosis - // retval = nr of bytes received. If host buffer too small then - // only plBufSiz bytes are copied to "payLoad" - // plBufSiz = size of host buffer - - virtual void dc_setWakeFrequency(uint8_t period) const =0; - // RTC wakes DC2 (and PTU) by hardware signal every 32seconds - // change wake signal period to 1...64s - - virtual void dc_OrderToReset(void) const =0; - // want DC2 to reset (in order to start Bootloader) - - - virtual QString dc_getSerialState(void) const =0; - // get result of opening-command like "ttyS0 opened with 115200 8N1! - // or error messages like "comport not available..." - // was saved by last opening event, can be passed for 100ms - - virtual void dc_clrSerialStateText(void) const =0; - // clear above text to avoid multiple repetive displaying - - - virtual void bl_sendDataDirectly(uint8_t length, uint8_t *buf) const =0; - // send without protocol frame, needed for the DC bootloader - - virtual uint8_t getRawRecLength(void) const =0; - // only needed if protocol stack in Cashagent-Library is bypassed - - virtual uint8_t getRawReceivedData(uint8_t *receivedData) const =0; - // only needed if protocol stack in Cashagent-Library is bypassed - - virtual QString dc_getSerialParams(void) const =0; - // get DC2 serial settings (not very meaningful as they will not come if different from master settings) - - virtual QString dc_getHWversion(void) const =0; - // get DC2 hardware version - - virtual QString dc_getSWversion(void) const =0; - // get DC2 software version - - virtual QString dc_getState(void) const =0; - // get DC2 status (every OK or any error/warning? ) - - -// ---------------------------------------------------------------------------------------------------------- -// Date and Time -// ---------------------------------------------------------------------------------------------------------- - - virtual uint8_t rtc_getDateTime(struct Trtc_DateTime *rtc_DateTime) const =0; - - virtual uint8_t rtc_setDateTime(void) const =0; - // synch DC2 with PC or PTU system time and date - - virtual void rtc_getTime(uint8_t *hh, uint8_t *mm, uint8_t *ss) const =0; - // get time directly - - virtual void rtc_getDate(uint8_t *yy, uint8_t *mm, uint8_t *dd) const =0; - // get date directly - - virtual uint8_t rtc_getToday(uint8_t *dow, uint16_t *minOfToday, uint32_t *secOfToday) const =0; - // dow=day of week, 1=monday...7 - // minOfToday: 0=midnight...1439= 23:59 - // secOfToday: 0=midnight...86399= 23:59:59 - - virtual bool rtc_isLeapYear(uint8_t *lastLeapYear, uint8_t *NextLeapYear) const =0; - // retval true: this year is leap year - - virtual bool rtc_isLeapYear() const =0; - - virtual void rtc_getWeek(uint8_t *DayOfWeek, uint8_t *HoursOfWeek, uint16_t *MinutesOfWeek) const =0; - // DayOfWeek: 1=monday...7 - // HoursOfWeek: 0=Monday 0:00 o'clock...167=Sunday 23:00 - // MinutesOfWeek: 0=Monday 0:00 o'clock...10079=Sunday 23:59 - - virtual void rtc_getMonth(uint8_t *DayOfMonth, uint16_t *HoursOfMonth, uint16_t *MinutesOfMonth) const =0; - // DayOfMonth: 1...31 - // HoursOfMonth: 0 = 0:00o'clock of 1.day in month up to 743 - // MinutesOfMonth:0 = 0:00o'clock of 1.day in month up to 44639 - - virtual void rtc_getYear(uint16_t *DayOfYear, uint16_t *HoursOfYear, uint32_t *MinutesOfYear) const =0; - // DayOfYear: 1...366 1= 1.Jan of this current year - // HoursOfYear: 0=1.Jan 0:00o'clock ...8783=31.12 23 o'clock - // MinutesOfYear: 0=1.Jan 0:00o'clock ...527039=31.12 23:59 o'clock - - virtual QString rtc_getTimStr(void) const =0; - virtual QString rtc_getDatStr(void) const =0; - virtual QString rtc_getTimDatStr(void) const =0; - - - // UID - unique number, different in every DC - virtual void dc_getUID8byte(uint8_t *buf8byteUid) const =0; - // get 8 single bytes in buffer - - virtual QString dc_getUIDstr() const =0; - // get as string - - virtual uint64_t dc_getUIDnumber(void) const =0; - // get UID as one long number - -// ---------------------------------------------------------------------------------------------------------- -// analogs -// ---------------------------------------------------------------------------------------------------------- - - // Analog values: - virtual uint32_t dc_getTemperature(void) const =0; - // in Sax-Format 0...400 (0=-50,0degC 100=0,0degC 141=20,5degC 400=150,0degC) - - virtual QString dc_getTemperaturStr(void) const =0; - // as string like "-12,5degC" - - virtual uint32_t dc_getVoltage(void) const =0; - // as value in mV, 0...65,535V - - virtual QString dc_getVoltagStr(void) const =0; - // as string in mV - - virtual bool dc_mainFuseIsOk(void) const=0; - // true if 12V fuse is OK - // false: fuse blown, DC will continue working but no 12V device can be used! - -// ------------------------------------------------------------------------------ -// digital outputs -// ------------------------------------------------------------------------------ - - virtual uint8_t lock_switchUpperLock(uint8_t dir) const =0; - // dir 0=off 1=up 2=down - // move lock until stop cmd (0) - - virtual uint8_t lock_switchLowerLock(uint8_t dir) const =0; - // dir 0=off 1=up 2=down - // move lock until stop cmd (0) - - virtual void lock_switchVaultDoor(void) const =0; - - virtual void coin_switchRejectMotor(uint8_t dir) const =0; - - virtual void coin_rejectCoins(void) const =0; - - // LEDs - virtual void led_switchLedService(uint8_t on) const =0; - virtual void led_switchLedPaper(uint8_t on, uint8_t ton, uint8_t tof) const =0; - virtual void led_switchLedPinPad(uint8_t on, uint8_t ton, uint8_t tof) const =0; - virtual void led_switchLedStart(uint8_t on, uint8_t ton, uint8_t tof) const =0; - virtual void led_switchLedCoinbassin(uint8_t on, uint8_t ton, uint8_t tof) const =0; - - virtual void fan_switchFan(bool on) const =0; - virtual void alarm_switchSiren(bool on) const =0; - virtual void bar_OpenBarrier(bool open) const =0; - virtual void ptu_switchWake(bool WAKEACTIVE) const =0; - - virtual void aux_power(bool on) const =0; - virtual void aux_setUsage(uint8_t PinDirection) const =0; - virtual void aux_setOutputs(uint8_t PinIsHigh) const =0; - - virtual void lock_switchContactPower(bool on) const =0; - - virtual void prn_switchPower(bool on) const =0; - - virtual void mif_readerOn(bool on) const =0; - - virtual void mif_creatAtbCard(uint8_t cardType) const =0; - - virtual void mod_switchPower(bool on) const=0; - - virtual void mod_switchWake(bool WAKEACTIVE) const=0; - - virtual void mdb_switchPower(bool on) const =0; - - virtual void mdb_switchWake(bool WAKEACTIVE) const =0; - - virtual void credit_switchPower(bool on) const =0; - // the same as modem power - - virtual void credit_switchWake(bool WAKEACTIVE) const =0; - - virtual void shut_move(bool open) const =0; - - virtual void esc_moveFlaps(uint8_t flap ) const =0; - // 0: close both 1: open take-flap 2: open return - - -// ------------------------------------------------------------------------------ -// door, cashbox and lock switches -// ------------------------------------------------------------------------------ - - virtual uint8_t door_getSwitches(void) const =0; - // retval: bit0: upper door 1: low door 2:vault door - - virtual bool door_isUpperDoorOpen(void) const =0; - - virtual bool door_isLowerDoorOpen(void) const =0; - - virtual bool vault_isVaultDoorOpen(void) const =0; - - virtual uint8_t vault_getSwitches(void) const =0; - // retval bit0: cash box, bit 1: bill box - - virtual bool vault_isCoinVaultIn(void) const =0; - - virtual bool vault_isBillVaultIn(void) const =0; - - virtual uint8_t door_getLocks(void) const =0; - // retval bit0: upper lever is up (=open) - // bit1: upper lever is down (=locked) - // bit2: lower lever is up - // bit3: lower lever is down (=locked) - - virtual bool door_upperDoorIsLocked(void) const =0; - - virtual bool door_upperDoorIsUnlocked(void) const =0; - - virtual bool door_lowerDoorIsLocked(void) const =0; - - virtual bool door_lowerDoorIsUnlocked(void) const =0; - - virtual bool bar_optoIn1isOn(void) const =0; - - virtual bool bar_optoIn2isOn(void) const =0; - - virtual uint8_t aux_getAuxInputs(void) const =0; - - virtual bool ptu_WakeINisActive(void) const =0; - - virtual bool mdb_WakeINisActive(void) const =0; - - virtual bool prn_readyINisActive(void) const =0; - - virtual bool coid_isAttached(void) const =0; - - virtual bool coin_escrowIsOpen(void) const =0; - - virtual bool mif_cardIsAttached(void) const =0; - -//bool hwapi::mod_WakeINisActive(void); - - virtual bool door_isContactPowerOn(void) const =0; - - virtual bool mif_isMifarePowerOn(void) const =0; - - virtual bool mdb_testIsmdbTxDon(void) const =0; - - virtual bool aux_isAuxPowerOn(void) const =0; - - virtual bool mod_isGsmPowerOn(void) const =0; - - virtual bool cred_isCreditPowerOn(void) const =0; - - virtual bool prn_isPrinterPowerOn(void) const =0; - - virtual uint8_t prn_PrnFuseIsOk(void) const=0; - //retval: 0: fuse blown 1: fuse OK 2:unknown as printer power is off - - virtual bool mdb_isMdbPowerOn(void) const =0; - - virtual bool cash_getRejectMotorHomePos(void) const=0; - - virtual uint8_t cash_getLowPaperSensor(void) const=0; - // 0: Sensor sees paper 1: no paper 99: off - - - // ------------------------------------------------------------------------------ - // request commands - // ------------------------------------------------------------------------------ - - // all read-requests can be sent manually by the following functions - // or automatically in background by: void hwapi::dc_autoRequest(bool on) - - - virtual void request_DC2serialConfig() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - - virtual void request_DC2_HWversion() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - virtual void request_DC2_SWversion() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - virtual void request_DC2_condition() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - virtual void request_DC2_UID() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - virtual void request_DC2_TimeAndDate() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - virtual void request_DC2_analogues() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - virtual void request_DC2_digitalInputs() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - virtual void request_DC2_digitalOutputs() const =0; - // read-request can be sent manually by this function, not needed if auto-request is on - - // ------------------------------------------------------------------------------ - - // the following device state requests are deployed only if device is powered up: - virtual void request_PrinterHwState() const =0; - virtual void request_PrinterCurrentFonts() const =0; - virtual void request_PrinterStateComplete() const =0; - - virtual void request_MifareReaderState() const =0; - virtual void request_MifareCardType() const =0; - virtual void request_MifareAtbType() const =0; - virtual void request_MifareID() const =0; - virtual void request_MifareData(uint8_t dataBlockNumber) const =0; - //virtual void request_MifareData() const =0; - - virtual void request_MDB_Status() const =0; - virtual void request_MDB_lastResponse() const =0; - virtual void request_EMP_allParameters() const =0; - virtual void request_EMP_lastCoin() const =0; - - - // ------------------------------------------------------------------------------ - // readback digital outputs of connected devices - // these functions are not needed for normal operation - // but can be used to test and verify conditions - // ------------------------------------------------------------------------------ - - - virtual bool test_getDO_mdbRXtst(void) const =0; - // readback digital outputs of connected devices - // these functions are not needed for normal operation - // but can be used to test and verify conditions - - virtual uint8_t lock_getDO_motors(void) const =0; - // bit0: upper lock forward bit 1 backward - // bit2: lower lock forward bit 3 backward - - virtual uint8_t test_serialState(void) const =0; - // test on-board signals for the serials - // serial drv on/off, Serial mux1, Serial mux2 - - virtual bool test_serialIsOn(void) const =0; - - virtual bool test_serialMux1isSetToPrinter(void) const =0; - virtual bool test_serialMux1isSetToModem(void) const =0; - virtual bool test_serialMux2isSetToCredit(void) const =0; - virtual bool test_serialMux2isSetToMifare(void) const =0; - - virtual bool led_coinIsOn(void) const =0; - virtual bool led_frontIsOn(void) const =0; - virtual bool led_ticketIsOn(void) const =0; - virtual bool led_pinIsOn(void) const =0; - virtual bool led_StartIsOn(void) const =0; - virtual bool led_insideIsOn(void) const =0; - - virtual bool fan_isOn(void) const =0; - virtual bool siren_isOn(void) const =0; - virtual bool bar_relayIsOn(void) const =0; - virtual bool ptu_WakeOutIsOn(void) const =0; - - virtual bool aux_powerIsOn(void) const =0; - - virtual bool coin_shutterIsOpen(void) const =0; - virtual bool coin_shutterTestOutput(void) const =0; - - virtual uint8_t coin_escrowFlapOpened(void) const =0; - // retval: 1:return flap is open 2:take flap is open 0:closed - - - -// ------------------------------------------------------------------------------ -// devices are operated by DC -// processes with more then one devices -// timer controlled or long term processes -// ------------------------------------------------------------------------------ - - - virtual void sendDeviceSettings(uint8_t kindOfPrinter, uint8_t kindOfCoinChecker, - uint8_t kindOfMifareReader, uint8_t suppressSleep, - uint8_t kindOfModem, uint8_t kindOfCredit ) const =0; - // enable hardware in device controller: - // kindOfPrinter: 0:off 1: GPT4672 (only this one implemented) - // kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: Csquare_changer - // kindOfMifareReader: 0:off 1: SL025 (only this one implemented) - // suppressSleep: 0:sleep allowed 1: sleep surpressed for special reason - // kindOfModem: 0:off 1: ATB_Sunlink_LTE (not yet implemented) - // kindOfCredit: 0:off 1: cVendTopp 2:cVendPin (not yet implemented) - - virtual void request_ReadbackDeviceSettings() const =0; - - virtual void readback_DeviceSettings(uint8_t *length, uint8_t *data) const =0; - // refer to DC2 manual for exact content - // state 5.5.21: byte[0]=kindOfPrinter byte[1]=kindOfCoinChecker - // byte[2]=kindOfMifarereadr byte[3]=suppress sleep mode - // byte[4]=kindOfModem byte[5]=kind of cc terminal - - virtual void sendMachineID(uint16_t customerNr, uint16_t machineNr, - uint16_t borough, uint16_t zone, - uint16_t alias, char *location) const =0; - - virtual void request_ReadbackMachineID() const =0; - - virtual void readback_machineIDdata(uint8_t *length, uint8_t *data) const =0; - // state 5.5.21: byte[0,1]=customer number byte[2,3]=machine number - // byte[4,5]=borough byte[6,7]=zone byte[8,9]=alias name - // byte[10...41]=location - - // Locks stops automatically at end switch or by timeout - virtual uint8_t lock_openUpperDoor(void) const =0; - virtual uint8_t lock_closeUpperDoor(void) const =0; - - virtual uint8_t lock_openLowerDoor(void) const =0; - virtual uint8_t lock_closeLowerDoor(void) const =0; - - virtual void shut_openOnce(void) const =0; - // and close automatic after shutter time - - virtual void shut_openForCoin(bool start) const =0; - // open flap if coin is attached - // once process is started it runs until stop command - - virtual void shut_sendOpeningTime(uint16_t timeIn_ms ) const =0; - // after this time without retrigger the flap is closed - - virtual void esc_takeMoney(void) const =0; - // and close automatically after escrow time (1s) - - virtual void esc_returnMoney(void) const =0; - // and close automatically after escrow time (1s) - - -// ---------------------------------------------------------------------------------------------------------- -// --------------------------------------------- MIFARE ----------------------------------------------------- -// ---------------------------------------------------------------------------------------------------------- - - - virtual uint8_t mif_returnReaderStateAndCardType(uint8_t *buf, uint8_t maxBufferSize) const =0; - // retval 0=OK 1=error host buffer too small - /* data description, new fast version: - byte 0= still the same: current read state: - 0=power off 1=reader-fault 2=ready - 3=just reading 4=read complete - 5=read partial, removed too early - 6=state unknown - - byte 1: reader state 1=ok 0=nok - byte 2: card preent (0,1) - byte 3: card selected (0) - byte 4: card type: 0...5 - byte 5: card allowed (0=no 1=MifareClassic 1k or 4k) - byte 6: CardSize: 1 or 4 (kB) - byte 7: length of UID 4 or 7 (byte) - */ - - virtual bool mif_readerIsOK(void) const =0; - - virtual bool mif_cardAttached(void) const =0; - - virtual uint8_t mif_readResult(void) const =0; - // result: 0: unknown or still in progress - // 1: card read successful - // 2: reading error - - virtual QString mif_cardUID(void) const =0; - - virtual uint8_t mif_getCardDataDec(uint8_t blkNr, uint8_t *buf, uint8_t maxBufferSize) const =0; - - virtual QString mif_getCardDataStr(uint8_t blockNumber) const =0; - // with blockNumber=0...11 - - - - // ---------------------------------------------------------------------------------------------------------- - // --------------------------------------------- PRINTER ---------------------------------------------------- - // ---------------------------------------------------------------------------------------------------------- - - // read printer condition and settings - - virtual uint8_t prn_getHwState(struct Tprn_hw_state *prn_hw_state) const =0; - // retval: status byte - // byte 0 = 0: prnter OK, >0: error - // bit0: paper low 1: no paper 2: temperature error - // 3: head open 4: paper jam in cutter - // 6: no response 7: bad response from printer - // and return struct "Tprn_hw_state" - - virtual bool prn_isUpAndReady(void) const =0; - // true: printer is powered, serial is ok, no error, printer is connected and resonding - - virtual void prn_getCurrentFontSetting(struct Tprn_currentSettings *prn_fonts) const =0; - - - // send Commands to printer: - - virtual void prn_sendText(QByteArray *buf) const =0; - // up to 1280 bytes - - virtual void prn_sendPrnSysCmd(uint8_t para1, uint8_t para2, uint32_t para3) const =0; - // send three byte through to printer, see printers manual - - virtual void prn_sendPrnEscCmd(uint8_t para1, uint8_t para2, uint8_t para3, uint8_t para4) const =0; - // send four byte through to printer, see printers manual - - - virtual void prn_sendPrnSetup(uint16_t paperSpeed, uint8_t density, uint8_t alignment, uint8_t orientation) const =0; - // send 5 byte: byte 0,1: speed 5...250 mm/s - // byte2: density 0....(25)....50 - // byte3: alignment 'l', 'c', 'r' = left, center, right - // byte4: orientation 0, 90, 180 = 0deg, 90deg, 180deg rotation (by now not supported!) - // not batched! don't use twice within 100ms - - virtual void prn_movePaper(uint8_t wayInMm, uint8_t direction) const =0; - //direction: 1=forward 2=backward - // - virtual void prn_setFonts(uint8_t font, uint8_t size, uint8_t width, uint8_t height) const =0; - // font = kind of font 5...11 (0..22) - // size = 6...20, 9..9: too tiny 10: small ...12 = normal size ...20=huge - // width: 0...4 0=1x 1=2x 2=4x (huge!) 3=8x 4=16x (3,4 make no sense) - // heigth: 0...7 = 1x...8x only 0,1,2,(3) make sense - - virtual void prn_setLetters(uint8_t bold, uint8_t invers, uint8_t underlined) const =0; - // bold: 0/1 - // invers: 0/1 - // underlined: 0/1 - - virtual void prn_cut(uint8_t kindof) const =0; - // kindof: 1=full cut 2=partial cut 3=eject (5xLF + full cut) - - virtual void prn_newLine(uint8_t nrOfLines) const =0; - - virtual void prn_printCompleteFontTable(void) const =0; - - - virtual void prn_printBarcode(uint8_t kindOf, uint8_t withText, uint8_t offset, uint8_t rotation, uint8_t dataLeng, uint8_t *data) const =0; - // kind of barcode: 0=Code39 1=Code128 2=EAN13 3= 2/5interleaved 4=UPC-A 5=EAN8 - // withText: print readable text below - // offset: move by pixel from left border - // rotation - // dataLeng in byte - - virtual void prn_sendQRdata(QByteArray *buf) const =0; - // maximal 150 alphanummeric bytes - - virtual void prn_printQRcode(void) const =0; - // QRcode may have 1...150 alphanummeric data, must be transfered in advance - - - virtual void prn_printLogo(uint8_t nrOfLogo, uint8_t offset ) const =0; - // nrOfLogo: 1..4 in flash 5...8 in Ram - // offset: in mm form left border - - - // ......................................................... - // Parking Ticket (print-out document) designer TD - obsolete - // ......................................................... - - - - // ---------------------------------------------------------------------------------------------------------- - // --------------------------------------------- MDB Bus ---------------------------------------------------- - // ---------------------------------------------------------------------------------------------------------- - - virtual void mdb_sendBusReset(void) const =0; - - virtual void mdb_sendCommand(uint8_t toMdbDevice, uint8_t mdbCommand) const =0; - // send one bus command directly over mdb bus, refer to mdb manual for commands - // this command is not needed in normal operation, just for new or special things - - virtual void mdb_sendMessage(uint8_t toMdbDevice, uint8_t mdbCommand, uint8_t nrOfData, uint8_t *dataBuffer) const =0; - // nrOfData = sizeOf(dataBuffer) maximal 34 byte according mdb specs - // same as mdb_sendCommand, just with data - - virtual bool mdb_busIsReadyToWork() const =0; - - virtual bool mdb_deviceVoltageOK() const =0; - - virtual bool mdb_busVoltageOk() const =0; - - virtual uint8_t mdb_getLastDeviceResponse(uint8_t *fromDevice, uint8_t *lastRequest, - uint8_t *responseLength, uint8_t *responseBuffer) const =0; - // fromDevice: device nr from which data was requested 0,1,2,3 - // lastRequest: sent mdb command - // responseLength: nr of payload data (after mdb-ack) 0...34 - // responseBuffer holds payload data (answer from mdb device) - // return val: mdb result of this request: 1=got ACK 2=got 3xNAK 3=no or bad response 4:got Data (after ACK) - - - // ---------------------------------------------------------------------------------------------------------- - // ---------------------------------- Electronic Coin Validator EMP ----------------------------------------- - // ---------------------------------------------------------------------------------------------------------- - - - virtual void emp_sendSettings(uint16_t coinAcceptance, uint8_t tokenChannel, uint16_t *coinDenomination ) const =0; - // coinAcceptance: bit0=coin1 (lowest donomination) bit15=coin16 bitH=accept bit L = deny coin (no validation) - // tokenChannel 0...31: if this signal comes from emp then a token was inserted - // coinDenomination = array of 16 coin values (e.g. 5, 10, 20...) - - virtual void emp_pollingOnOff(uint8_t on) const =0; - - virtual void emp_startCoinAcceptance(void) const =0; - - virtual void emp_stopCoinAcceptance(void) const =0; - - virtual void emp_getAllParameters(struct T_emp *emp) const =0; - // see struct in hwapi.h - // usage example: - // hwapi *HWaccess const override; - // HWaccess = new hwapi() const override; - // struct T_emp myEmp const override; - // HWaccess->emp_getAllParameters(&myEmp) const override; - // readval=myEmp.pollingRunning const override; - - virtual uint8_t emp_chkIfCoinInserted(void) const =0; - // retval: 0...16 coins left in FIFO - - virtual void emp_getNewCoinRecord(uint8_t *valid, uint8_t *signal, uint8_t *error, uint16_t *value) const =0; - // with every call ONE coin is taken out of FIFO and pointer decremented - // valid: should be 1 - // signal: comes right from coin checker, 0...15 (0=first programmed coin type) 0xFF=no signal - // error: was reported from EMP as dynamic signal right after coin insertion (instead of - // coin signal), example: 3=unknown coin 4=coin is blocked by host. 0xFF=no error - // value: of the coin. Depends on parameter "coinDenomination" in function "emp_sendSettings" - // if coinDenomination[coin 0..15] = 0 then the value programmed in coin checker is taken - // if coinDenomination > 0 then this value is taken. - // Useful in case of two currencies (adapt to local currency) or for token. - - // function gives more details as "emp getLastCoin()" but "emp getLastCoin()" is easier to use - - // alternativ to emp_getNewCoinRecord( ): - virtual uint8_t emp_giveLastCoin(uint16_t *value, uint8_t *signal) const =0; - // retval: 0: NO coin stored 1: valid coin 2: got wrong coin or coin denied - // value: if retval1: value of the coin if reval=2: error number - // 0xFF means NO error or NO signal (as 0 is a valid error/signal) - // signal: channel nr reported from checker 0...15 - - - virtual uint8_t emp_returnLastCoin(uint16_t *value, uint8_t *signal) const =0; - // use for changer - - - - virtual QString dc_getTxt4RsDiagWin(void) const =0; - virtual void dc_clrTxt4RsDiagWin(void) const =0; - virtual QString dc_get2ndTxt4RsDiagWin(void) const =0; - virtual void dc_clr2ndTxt4RsDiagWin(void) const =0; - virtual QString dc_getTxt4HsStateLine(void) const =0; - virtual void dc_clrTxt4HsStateLine(void) const =0; - virtual QString dc_getTxt4masterStateLine(void) const =0; - virtual void dc_clrTxt4masterStateLine(void) const =0; - virtual QString dc_getTxt4resultStateLine(void) const =0; - virtual void dc_clrTxt4resultStateLine(void) const =0; - virtual QString dc_getdataStateLine(void) const =0; - virtual void dc_clrTxt4dataStateLine(void) const =0; - virtual QString dc_getdatifLine(void) const =0; - virtual void dc_clrTxt4datifLine(void) const =0; - - - -// ---------------------------------------------------------------------------------------------------------- -// -------- DC Bootloader 1.Version -// ---------------------------------------------------------------------------------------------------------- - - // using DC2 Bootloader - virtual void bl_iniChain(void) const =0; - virtual bool bl_importBinFile(QByteArray readBinFile, uint32_t fileSize, char withDispl) const =0; - virtual uint8_t bl_activatBootloader(uint8_t *sendData) const =0; - virtual uint8_t bl_startChain(void) const =0; - virtual uint8_t bl_readBLversion(uint8_t *sendData) const =0; - // minimum size of sendData-buffer: 5byte retval: length - virtual uint8_t bl_readFWversion(uint8_t *sendData) const =0; - // minimum size of sendData-buffer: 5byte retval: length - - virtual uint8_t bl_prepareDC_BLcmd(uint8_t Cmd, uint8_t SendDataLength, uint8_t *sendData, uint8_t *outBuf) const =0; - // make BL protocol, retval = outbuf length (5...133) - // bring data in correct form: start always with 0x02 finish with 0x03 and append checksum - // 0x02 Cmd < ...sendData ..> CRC CRC 0x03 - // Data length = 0...64 - // special conversion: if data contain 2 or 3 (STX, ETX) then write two bytes: 0x1B (=ESC) and data|0x80 - // so maxlength = 5 + 2 x 64 (if all data are 2 or 3) without 2,3: maxlength = 5 + 64 - - virtual uint8_t bl_exitBL(uint8_t *sendData) const =0; - // minimum size of sendData-buffer: 5byte retval: length - - virtual void led_switchLedIllumination(uint8_t on) const =0; - - - - -// ------------------------------------------------------------------------------------ -// 27.3.2023: Use Device-Controller's Bootloader to send hex-file -// ------------------------------------------------------------------------------------ - - virtual void bl_rebootDC(void) const =0; - - virtual void bl_startBL(void) const =0; - virtual void bl_checkBL(void) const =0; - virtual bool bl_isUp(void) const =0; - // return true is bl is up and running - // also initializes "sendFile" - - virtual void bl_sendAddress(uint16_t blockNumber) const =0; - // send start address, nr of 64byte-block, start with 0 - // will be sent only for folling block-numbers: - // 0, 1024, 2048, 3072 and 4096, so basically every 64kByte - - virtual uint8_t bl_wasSendingAddOK(void) const =0; - // return val: 0: no response by now 1:error 10: OK - - virtual void bl_openBinary(void) const =0; - - virtual void bl_sendDataBlock(uint8_t length, uint8_t *buffer) const =0; - // send 64 byte from bin file - - virtual void bl_sendLastBlock(void) const =0; - - virtual uint8_t bl_wasSendingDataOK(void) const =0; - // return val: 0: no response by now 1:error 10: OK - - virtual void bl_stopBL(void) const =0; - - -// ------------------------------------------------------------------------------------ -// 6.4.2023: new functions for coin collection and printing -// some new system functions -// ------------------------------------------------------------------------------------ - - - virtual bool rtc_setTimeDateDirect(struct Trtc_DateTime *DateTime) const =0; - // return true if successful. could fail if more the 8 commands are waiting - - virtual bool rtc_getExtendedTime(uint8_t *leng, uint8_t *data) const =0; - - virtual bool rtc_getExtendedTime(struct T_extTime *exTime) const =0; - - virtual bool sys_runCompleteTest(void) const =0; - // warning: lasts 20s in one pace - - virtual bool sys_ready4sending(void) const =0; - // return true if a Json-file can be sent - - virtual bool sys_sendJsonFileToDc(uint8_t kindOfFile, uint8_t nrOfTemplate, uint8_t *content ) const =0; - // kindOfFile: 1=config, 2=device, 3=cash, 4=serial, 5=time, 6=printer - // nrOfTemplate=1...32 if kindOfFile==6 - // content = content of the Json file, max 800byte ascii signs - // file is 0-terminated! - // return false if sending is not possible, wait a second - - virtual bool prn_sendDynamicPrnValues(uint8_t *dynPrnVal ) const =0; - // dynPrnVal = array of 8 Variables with 8 byte each, come as ascii string - // like: char prn_dynPrnVal[8][8]; - // return true if sending, false if cmd-stack is full - - virtual bool prn_printTemplate(uint8_t nrOftemplate) const =0; - // print one of the templates loaded by Json prior - // nr = 1..32 - // return true if sending, false if cmd-stack is full - - virtual void log_getHoldAccountNumbers(uint8_t *nrOfVals, uint16_t *accNr ) const =0; - // returns all acc nrs of the backuped vault records - // use: uint16_t backupedAccNumbers[8] - - virtual bool log_selectVaultRecord(uint16_t accountNr ) const =0; - // return true if sending, false if cmd-stack is full - // and trigger transfer - - virtual bool log_chkIfVaultRecordAvailable(void) const =0; - // return true if completly received - - virtual bool log_getVaultRecord(struct T_vaultRecord *retVR) const =0; - // which was selected by: log_selectVaultRecord() - // to be forwarded to Ismas - - virtual bool prn_printAccountReceipt(void) const =0; - // print all 8 backuped accounting receipts - // return true if sending to DC OK, false if cmd-stack is full - - virtual bool prn_printTestTicket(void) const =0; - // return true if sending to DC OK, false if cmd-stack is full - - - virtual bool cash_startPayment(uint32_t amount) const =0; - // 17.4.23TS: extended to 32bit - - virtual uint8_t cash_paymentProcessing(void) const =0; - // run this function periodically while coin payment process to generate necessary signals - // return value: - // 0: stopped 1: starting up 2: coin collection - // 3: finished by User (Push button) 4: finished, Max-Value collected - // 5: finished by escrow - // 10,11: error cannot start - // 12: timeout while payment, coins returned - // 13: stopped by unexpected error - - virtual uint32_t getInsertedAmount(void) const =0; - - virtual uint16_t getLastInsertedCoin(void) const =0; - - virtual bool getAllInsertedCoins(uint16_t *types, uint16_t *values) const =0; - // alle bei diesem Verkauf eingeworfenen Muenzen sind gespeichert, max 64 - - - virtual bool cash_cancelPayment(void) const =0; - // and return coins - - virtual bool cash_stopPayment(void) const =0; - // and keep coins in escrow - - - // after ticket/goods issue: - virtual bool vend_success(void) const =0; - // conclude payment process, encash all inserted coins to vault. Printing was successful - // if possible return change - - virtual bool vend_failed(void) const =0; - // conclude payment process and return all inserted coins - - - - - - virtual uint8_t mif_getCardType(QString *cardholder) const =0; - // return 1,2,3,4 = upper, lower access card, printer test, coin test - // cardholder: 7byte Name-String - - virtual uint64_t sys_getWakeSource(void) const =0; - // retval: 6 bytes, bit coded, 1=event keeps DC awake - - virtual uint8_t sys_getWakeReason(void) const =0; - // Master was woken by following reason: - // 1: MDB Event - // 2: Coin Event - // ( 3: Master Event) - will not set the wake line - // ( 4: 32s pulse) - will not set the wake line - // 5: Door Event - // ( 6: Diag Event) - will not set the wake line - // 7: 30min-Pulse for HB - - virtual void sys_getDeviceConditions(uint8_t *leng, uint8_t *data) const =0; - - virtual void sys_getDeviceConditions(struct T_moduleCondition *devCond) const =0; - - virtual void sys_getDynMachineConditions(uint8_t *leng, uint8_t *data) const =0; - - virtual void sys_getDynMachineConditions(struct T_dynamicCondition *dynMachCond) const =0; - - - virtual uint32_t cash_getAmountInVault(void) const =0; - - virtual uint16_t cash_getNrCoinsInVault(void) const =0; - - virtual uint8_t prn_getPrintResult() const =0; - - - - virtual uint8_t prn_getCurrentPrinterState() const =0; - // 0: printer OK - // bit0: near paper end bit1: no paper - // bit2: temperature error bit3: error head open - // bit4: paper jam in cutter - // bit6: no response bit7: serial rec. error - // bit5: printer not ready - - - virtual void sys_sendDeviceParameter(struct T_devices *deviceSettings) const =0; - - virtual void sys_restoreDeviceParameter(struct T_devices *deviceSettings) const =0; - // attention: only applies if function "sys_sendDeviceParameter()" was used to send this settings before - // cannot be used to see settings programmed by JsonFile - - virtual bool sys_areDCdataValid(void) const =0; - - virtual bool sys_sendingTest(void) const =0; - - virtual void prn_requestCurrentDynData(void) const =0; - - virtual bool prn_getCurrentDynamicPrnValuesFromDC(uint8_t *dynPrnVal ) const =0; - // which was sent with: bool prn_sendDynamicPrnValues(uint8_t *dynPrnVal ) const =0; - - virtual bool prn_dynDataAreEqual(uint8_t *buf) const =0; - - virtual bool prn_printKombiticket(uint8_t nrOfKombi) const =0; - // print four of the templates loaded by Json prior - // nr = 1..8 - - virtual void lock_triggerUpperSolenoid(void) const =0; - - virtual void lock_triggerLowerSolenoid(void) const =0; - - virtual bool doors_supervise(void) const =0; - - virtual bool dc_isAutoRequestOn(void) const =0; - - virtual uint16_t log_getLatestAccountNumber(void) const=0; - // new function 27.6.2023 - // latest = highest of the backup's - - virtual uint8_t log_getAvailableVaultBlocks(void) const=0; - // return 0x0011 1111 if all 6 blocks are loaded (one bit per block) - - virtual uint8_t log_getAnswerToLastSlaveRequest(void) const =0; - // use only for ONE request/command - // return: 0xFF: result unknown by now as sending is ongoing - // 0=OK - // 1= wrong length 2=wrong start sign 5= wrong crc - // 6= slave: master cmd was wrong 7: slave: could not write/read data - // 8=timeout, got no response from slave - - - // use for important and extended commands (print several templates, print ticket...) - virtual void log_startSupervision(void) const =0; - - virtual uint8_t log_getAnswerToLastCmdBatch(void) const =0; - // 0xFF: no command sent by now - // 0: started, in progress - // 1: done and OK - // 2: done and error - - virtual bool log_getVaultData(uint8_t *data) const =0; - // get vault record in linear 8bit buffer with 384 byte - - - - // new from 1.8.23 - virtual bool prn_printOneAccountReceipt(uint16_t accountNr) const =0; - // print one out of eight stored last accounting receipts - // function log_getHoldAccountNumbers() gives a list of acc-Nr. of the stored receipts - - virtual bool prn_printAllAvailAccountReceipts(void) const =0; - // same as: prn_printAccountReceipt() from line 1153 - // return true if sending to DC OK, false if cmd-stack is full - - virtual bool log_verifyVaultRecordByCrc(void) const =0; - // return true if CRC16 is correct, data are 100% OK. Security level 1:65536 - // verification is strongly recommended before further processing - // in case of "false"-result please reload from DC - - - virtual uint16_t log_DC_getNextAccountNumber(void) const=0; - // the current cash box content will be backuped with this number on next cashbox-change - - virtual void log_DC_setNextAccountNumber(uint16_t newAccountingNumber) const=0; - // the current cash box content will be backuped with this number on next cashbox-change - // use only in case of hardware replacements or errors which derailed the number - - virtual void log_DC_deleteAllVaultrecordsInDc(void) const=0; - // use only in case of hardware replacements or errors which derailed the number - - virtual void log_DC_deleteAllTotalCounters(void) const=0; - // use only in case of hardware replacements or errors which derailed the number - - virtual void dc_setNewCustomerNumber(uint16_t newCustNr) const =0; - - virtual void dc_setNewMachineNumber(uint16_t newMachNr) const =0; - - virtual void dc_setNewBorough(uint16_t newBorough) const =0; - - virtual void dc_setNewZone(uint16_t newZone) const =0; - - - -signals: - virtual void hwapi_templatePrintFinished_OK(void) const=0; - virtual void hwapi_templatePrintFinished_Err(void) const=0; - - virtual void hwapi_coinCollectionJustStarted(void) const=0; - virtual void hwapi_coinCollectionAborted(void) const=0; - - virtual void hwapi_gotNewCoin(void) const=0; - virtual void hwapi_payStopByMax(void) const=0; - virtual void hwapi_payStopByPushbutton(void) const=0; - - virtual void hwapi_payStopByEscrow(void) const=0; - virtual void hwapi_payStopByError(void) const=0; - virtual void hwapi_payStopByTimeout(void) const=0; - virtual void hwapi_payCancelled(void) const=0; - virtual void hwapi_coinProcessJustStopped(void) const=0; - - - // new from 2023.06.12 - virtual void hwapi_doorServiceDoorOpened(void) const=0; - virtual void hwapi_doorVaultDoorOpened(void) const=0; - virtual void hwapi_doorCoinBoxRemoved(void) const=0; - virtual void hwapi_doorCoinBoxInserted(void) const=0; - virtual void hwapi_doorCBinAndAllDoorsClosed(void) const=0; - virtual void hwapi_doorAllDoorsClosed(void) const=0; - - - -}; - - -// History -// 11.10.2021: V1.0 222 functions -// 23.12.2021: V1.1 added block-parameter to function "read mifare data" -// 30.12.2021: V1.2 added function: mif_clearDataBuffer(), mif_isBlockAvailable(uint8_t blkNr) and mif_getAvailableDataBlocks() -// 1.1.2022: V1.3 Mifare extended. ( background: read 16 x 48byte from card to DC, read 12 x 64byte from DC to CA) -// new: read full card with 768bytes from HWapi without block borders -// added: mif_getNrOfAvailableDataBytes mif_getCardData768byteDec(uint8_t *buf, uint16_t bufferSize) -// mif_getCardDataDec(uint16_t fromAddr, uint16_t toAddr, uint8_t *buf, uint16_t bufferSize) -// mif_getCardDataStr(bool useHexFormat, char seperator) - -// 29.03.2023: V3.1 some extensions for PSA1256_ptu5, -// V3.2 Bootloader improvement -// 12.04.2023: V3.3 new features extended: loading and using Json-files, cash-collection, cash-data-logging -// 14.04.2023: V3.4 new features extended: sys_getDynMachineConditions, sys_getDeviceConditions and -// rtc_getExtendedTime return struct in addition. New function to select and get VaultRecord -// -// 19.04.2023: V3.5 new function: sys_getWakeReason(); -// 17.05.2023: V3.6 new function: cash_isCollectionActive(), cash_isPayProcessActive() -// new signals: hwapi_coinCollectionJustStopped, hwapi_coinCollectionJustStarted -// getAllInsertedCoins() fixed, also in datif and storeINdata - -// 15.06.2023 V4.2 bring into same order as hwapi in order to set the THIS_IS_CA_MASTER correct -// 19.06.2023 V4.3 added some qCriticals to see emits - -// 01.08.2023 V4.4 some new values at the end of struct T_vaultRecord -// two more values in struct T_devices -// 7 new functions at the end of the file - - -//#define HWINF_iid "Atb.Psa2020.software.HWapi/3.1" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.1" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.3" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.4" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.5" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.6" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.0" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.1" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.2" -//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.3" -#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.4" - - - - -Q_DECLARE_INTERFACE(hwinf, HWINF_iid) - - -#endif - - - diff --git a/plugins/libCAmaster.so b/plugins/libCAmaster.so deleted file mode 100755 index 39e1557..0000000 Binary files a/plugins/libCAmaster.so and /dev/null differ diff --git a/plugins/libCAslave.so b/plugins/libCAslave.so deleted file mode 100755 index 74a1fc9..0000000 Binary files a/plugins/libCAslave.so and /dev/null differ diff --git a/update.cpp b/update.cpp deleted file mode 100644 index 85e8938..0000000 --- a/update.cpp +++ /dev/null @@ -1,709 +0,0 @@ -#include "update.h" -#include "worker.h" -#include "utils.h" -#include "update_dc_event.h" -#include "mainwindow.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX) -#include "unistd.h" -#endif - -#include "plugins/interfaces.h" - -#include -#include -#include -#include -#include -#include -#include - -#define UPDATE_OPKG (1) -#define UPDATE_DC (0) - -static const QMap baudrateMap = { - {"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3}, - {"57600" , 4}, {"115200" , 5} -}; - -QPluginLoader Update::pluginLoader; - -hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { - hwinf *hw = nullptr; - if (plugInDir.exists()) { - QString pluginLibName(fname); - pluginLibName = plugInDir.absoluteFilePath(pluginLibName); - QFileInfo info(pluginLibName); - if (info.exists()) { - pluginLibName = plugInDir.absoluteFilePath(pluginLibName); - pluginLoader.setFileName(pluginLibName); - // static QPluginLoader pluginLoader(pluginLibName); - if (!pluginLoader.load()) { - qCritical() << "in directory" << plugInDir.absolutePath(); - qCritical() << "cannot load plugin" << pluginLoader.fileName(); - qCritical() << pluginLoader.errorString(); - exit(-1); - } - - qCritical() << "loadDCPlugin() plugin directory:" << plugInDir.absolutePath(); - qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName(); - - if (!pluginLoader.isLoaded()) { - qCritical() << pluginLoader.errorString(); - exit(-2); - } - QObject *plugin = pluginLoader.instance(); - if (!plugin) { - qCritical() << "cannot start instance"; - exit(-3); - } - if (! (hw = qobject_cast(plugin))) { - qCritical() << "cannot cast plugin" << plugin << "to hwinf"; - exit(-4); - } - } else { - qCritical() << pluginLibName << "does not exist"; - exit(-5); - } - } else { - qCritical() << "plugins directory" << plugInDir.absolutePath() - << "does not exist"; - exit(-6); - } - return hw; -} - -bool Update::unloadDCPlugin() { - if (pluginLoader.unload()) { - qCritical() << "unloaded plugin" << pluginLoader.fileName(); - // Note: will re-instantiate the library ! - // QObject *rootObject = pluginLoader.instance(); - // if (rootObject) { - // qCritical() << "reloaded plugin: root object again available"; - // return false; - // } - // qCritical()unloaded plugin: root object gone"; - return true; - } - return false; -} - -Update::Update(Worker *worker, - QString customerRepository, - QString customerNrStr, - QString branchName, - QString plugInDir, - QString pluginName, - QString workingDir, - bool dryRun, - QObject *parent, - char const *serialInterface, - char const *baudrate) - : QObject(parent) - , m_hw(loadDCPlugin(QDir(plugInDir), pluginName)) - , m_worker(worker) - , m_serialInterface(serialInterface) - , m_baudrate(baudrate) - , m_customerRepository(customerRepository) - , m_customerNrStr(customerNrStr) - , m_branchName(branchName) - , m_pluginName(pluginName) - , m_workingDir(workingDir) - , m_dryRun(dryRun) - , m_sys_areDCdataValid(false) { - - //int tries = 20; - //while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { - // must deliver 'true', only then are all data from hwapi valid - // if (--tries < 0) { - // qCritical() << "ERROR!!! DC DATA NOT VALID -> CA-MASTER-PLUGIN NOT CONNECTED"; - // break; - // } - // m_hw->dc_autoRequest(true); - // QThread::msleep(500); - //} - - //qCritical() << "UPDATE: m_sys_areDCDataValid ..." << m_sys_areDCdataValid; - - //qInfo() << "UPDATE: m_serialInterface ..." << m_serialInterface; - //qInfo() << "UPDATE: m_baudrate ..." << m_baudrate; - //qInfo() << "UPDATE: m_customerRepository ..." << m_customerRepository; - //qInfo() << "UPDATE: m_customerNr ..........." << m_customerNrStr; - //qInfo() << "UPDATE: m_branchName ..........." << m_branchName; - //qInfo() << "UPDATE: m_pluginName ..........." << m_pluginName; - //qInfo() << "UPDATE: m_workingDirectory ....." << m_workingDir; -} - -Update::~Update() { -} - -// br is a index into a table, used for historical reasons. -bool Update::openSerial(int br, QString baudrate, QString comPort) const { - qDebug() << "opening serial" << br << baudrate << comPort << "..."; - if (m_hw->dc_openSerial(br, baudrate, comPort, 1) == true) { // 1 for connect - Utils::printInfoMsg( - QString("OPENING SERIAL %1").arg(br) - + " " + baudrate + " " + comPort + "...OK"); - - // m_hw->dc_autoRequest(true); - m_hw->dc_autoRequest(false); - QThread::sleep(1); - - Utils::printInfoMsg(QString("IS PORT OPEN %1").arg(m_hw->dc_isPortOpen())); - return true; - } - - Utils::printCriticalErrorMsg( - QString("OPENING SERIAL %1").arg(br) - + " " + baudrate + " " + comPort + "...FAILED"); - return false; -} - -void Update::closeSerial() const { - qInfo() << "CLOSED SERIAL" << m_baudrate << m_serialInterface; - m_hw->dc_closeSerial(); -} - -bool Update::isSerialOpen() const { - return m_hw->dc_isPortOpen(); -} - -/* - - /////////////////////////////////////////////////////////////////////////////// - // - // USING THE DC BOOTLOADER - // - /////////////////////////////////////////////////////////////////////////////// - - 1 : bl_reboot() // send to application, want DC2 to reset (in order to - // start the bootloader) - // - // NOTE: this function is NOT reliable !!! Sometimes it - // simply does not work, in which case bl_startBL, - // bl_checkBL and bl_isUp do not work as well. - // Alas, there is no feedback if bl_reboot worked! - // - // NOTE: this function can be called only once per - // minute, because once called again, the controller - // performs some self-checks consuming some time. - // - // NOTE: after a successful bl_reboot(), the device is - // waiting about 4 seconds in the bootloader. To stay in - // the bootloader, we have to send the command - // bl_startBL(), which is kind of a misnomer, as it - // should be bl_doNotLeaveBL(). - // - 2 : bl_startBL(): // send within 4s after DC power-on, otherwise - // bootloader is left. - // - // NOTE: a running bootloader is a MUST for the download - // process of a device controller firmware as it does - // the actual writing of the memory (the bl_reboot() - // from above erases the available memory). - // - 3 : bl_check(): // send command to verify if bl is up - // - // NOTE: this command is kind of a request that we want - // to check if the bootloader is up. The device - // (actually the bootloader) responds with its version. - // - 4 : bl_isUp(): // returns true if bl is up and running - // - // NOTE: we know what the bootloader version actually is - // as the bootloader does not change. By comparing the - // string received in the previous step with this known - // version string we know if the bootloader is up. - // - // NOTE FOR ALL PREVIOUS STEPS: execute them in their - // own slots each to be sure to receive any possible - // responds from the device. - // - 5 : bl_sendAddress(blockNumber) - // send start address, nr of 64-byte block, start with 0 - // will be sent only for following block-numbers: - // 0, 1024, 2048, 3072 and 4096, so basically every - // 64kByte. - // for other addresses nothing happens - - 6 : bl_wasSendingAddOK() - // return val: 0: no response by now - // 1: error - // 10: OK - - 7 : bl_sendDataBlock() - // send 64 byte from bin file - - 8 : bl_sendLastBlock() - // send this command after all data are transferred - - 9 : bl_wasSendingDataOK() - // return val: 0: no response by now - // 1: error - // 10: OK - - 10 : bl_stopBL() // leave bl and start (the new) application - // - // NOTE: this function MUST work under all conditions. - // Alas, there is no direct result for this command, so - // the only way of knowing it was successful is to ask - // the device if the bootloader is still running. - // There is no problem to repeat this command until the - // bootloader is really not running anymore. - */ -bool Update::updateBinary(QString const &fileToSendToDC) { - qInfo() << "UPDATING DEVICE CONTROLLER FIRMWARE BINARY" << fileToSendToDC; - - return false; - -#if 0 - QFile fn(fileToSendToDC); - if (!fn.exists()) { - // output via CONSOLE() etc - return false; - } - - bool bl_isUp = false; - if (m_hw->bl_completeStart()) { - int cnt = 5; - while (--cnt > 0) { - if (m_hw->bl_isUp()) { - bl_isUp = true; - break; - } - } - } - - if (!bl_isUp) { - return false; - } - - if (!m_hw->bl_storeFirmware(fileToSendToDC)) { - m_hw->bl_stopBL(); - return false; - } - - uint16_t const nrOfFirmwareBlocks = m_hw->bl_getNrOfFirmwareBlocks(); - - for (uint16_t blockNr = 0; blockNr <= nrOfFirmwareBlocks; ++blockNr) { - m_hw->bl_blockAutoLoad(blockNr); - - int sleepTime = 0; - while (1) { - if (sleepTime > 1500) { - m_hw->bl_stopBL(); - return false; - } - - int8_t const r = m_hw->bl_blockAutoResponse(); - - // after every "bl_blockAutoLoad()" call this until response - // retval 0: wait 1: OK, blk was sent 2: OK, transfer complete - // 3: error despite repeating, cancel. probably bin file corrupted - // Max duration: 3x no response from BL = 900ms - - switch(r) { - case 1: - /* fall through */ - case 2: - sleepTime = 0; - break; - case 0: { - QThread::msleep(100); - sleepTime += 100; - } break; - case 3: - m_hw->bl_stopBL(); - return false; - default: - m_hw->bl_stopBL(); - return false; // unknown error code - } - } - - m_hw->bl_stopBL(); - } - - return true; -#endif -} - -QString Update::jsonType(enum FileTypeJson type) { - switch (type) { - case FileTypeJson::CASH: return "CASH"; - case FileTypeJson::CONFIG: return "CONFIG"; - case FileTypeJson::PRINTER: return "PRINTER"; - case FileTypeJson::SERIAL: return "SERIAL"; - case FileTypeJson::DEVICE: return "DEVICE"; - case FileTypeJson::TIME: return "TIME"; - } - return "N/A"; -} - -bool Update::downloadJson(enum FileTypeJson type, - int templateIdx, - QString jsFileToSendToDC) const { - - m_hw->dc_autoRequest(true); // downloading Json needs the AutoEmission flag - qDebug() << "SET AUTO-REQUEST=TRUE"; - QThread::sleep(1); // make sure the auto-request flag is acknowledged - - bool ready = false; - int nTry = 25; - while ((ready = m_hw->sys_ready4sending()) == false) { - QThread::msleep(200); - if (--nTry <= 0) { - Utils::printCriticalErrorMsg("SYS NOT READY FOR SENDING AFTER 5 SECONDS"); - break; - } - } - - bool ret = false; - if (ready) { - QFile file(jsFileToSendToDC); - QFileInfo fi(jsFileToSendToDC); // max. size of template file is 800 bytes - if (file.exists()) { - if (file.open(QIODevice::ReadOnly)) { - if (fi.size() <= 800) { - QByteArray ba = file.readAll(); - // kindOfFile: 1=config, 2=device, 3=cash, 4=serial, 5=time, 6=printer - // nrOfTemplate=1...32 if kindOfFile==6 - // content = content of the Json file, max 800byte ascii signs - if (m_hw->sys_sendJsonFileToDc((uint8_t)(type), - templateIdx, - (uint8_t *)ba.data())) { - - /* - * Note: the machine id is contained in DC2C_conf.json. - * The idea was to use this to check if the download of - * the json-file was correct. It did not work, as the - * update of the PSA (to reflect a change in the - * machine id) did not happen immediately. - * - m_hw->dc_autoRequest(true); - QThread::msleep(500); - - // testing - m_hw->request_ReadbackMachineID(); - QThread::msleep(500); - - uint8_t data[64]; - memset(data, 0x00, sizeof(data)); - uint8_t length = 0; - - m_hw->readback_machineIDdata(&length, data); - - QThread::msleep(500); - - QByteArray ba((const char*)data, length); - - qCritical() << length << "MACHINE ID =" << ba.toHex(':'); - */ - - ret = true; - } - } else { - Utils::printCriticalErrorMsg( - QString("SIZE OF %1 TOO BIG (%2 BYTES)") - .arg(jsFileToSendToDC).arg(fi.size())); - } - } else { - Utils::printCriticalErrorMsg( - QString("CAN NOT OPEN ") + jsFileToSendToDC + " FOR READING"); - } - } else { - Utils::printCriticalErrorMsg( - QString(jsFileToSendToDC) + " DOES NOT EXIST"); - } - } - - m_hw->dc_autoRequest(false); - qDebug() << "SET AUTO-REQUEST=FALSE"; - QThread::sleep(1); // make sure the auto-request flag is acknowledged - - return ret; -} - -bool Update::updatePrinterTemplate(int templateIdx, QString jsFile) const { - return downloadJson(FileTypeJson::PRINTER, templateIdx, jsFile); -} - -bool Update::updateConfig(QString jsFile) { - return downloadJson(FileTypeJson::CONFIG, 0, jsFile); -} - -bool Update::updateCashConf(QString jsFile) { - return downloadJson(FileTypeJson::CASH, 0, jsFile); -} - -bool Update::updateDeviceConf(QString jsFile) { - return downloadJson(FileTypeJson::DEVICE, 0, jsFile); -} - -QStringList Update::split(QString line, QChar sep) { - QStringList lst; - QString next; - int start = 0, end; - - while ((end = line.indexOf(sep, start)) != -1) { - next = line.mid(start, end - start).trimmed(); - lst << next; - start = end + 1; - } - next = line.mid(start, end - start).trimmed(); - lst << next; - - return lst; -} - -void Update::readyReadStandardOutput() { - QProcess *p = (QProcess *)sender(); - QByteArray buf = p->readAllStandardOutput(); - qCritical() << buf; -} - -void Update::readyReadStandardError() { - QProcess *p = (QProcess *)sender(); - QByteArray buf = p->readAllStandardError(); - qCritical() << buf; -} - -void Update::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { - QProcess *p = (QProcess *)sender(); - disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput())); - disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError())); -} - -QStringList Update::getDcSoftAndHardWareVersion() { - m_hw->dc_autoRequest(true); - QThread::sleep(1); // make sure the timer-slots are active - - for (int i=0; i < 3; ++i) { // send explicit reuests to get - // current SW/HW-versions - m_hw->request_DC2_SWversion(); - m_hw->request_DC2_HWversion(); - QThread::sleep(1); - } - - QString const &hwVersion = m_hw->dc_getHWversion().toLower().trimmed(); - QString const &swVersion = m_hw->dc_getSWversion().toLower().trimmed(); - - m_hw->dc_autoRequest(false); - QThread::sleep(1); // make sure the timer-slots are inactive - - if (!hwVersion.isEmpty() && !swVersion.isEmpty()) { - return QStringList() << hwVersion << swVersion; - } - - return QStringList() << "DC HW-version not available" - << "DC SW-version not available"; -} - -QString Update::getFileVersion(QString const& jsonFileName) { - // "version":"15.10.2023 14:55 02.00.06", - static const QRegularExpression re("^.*(\\\"version\\\":)(.*)$"); - - QString fileVersion; - QFile inputFile(jsonFileName); - if (inputFile.open(QIODevice::ReadOnly)) { - QTextStream in(&inputFile); - while (!in.atEnd()) { - QString line = in.readLine(); - - QRegularExpressionMatch match; - int idx = line.indexOf(re, 0, &match); - if (idx != -1) { - fileVersion = match.captured(match.lastCapturedIndex()); - break; - } - } - inputFile.close(); - } - - return fileVersion; -} - -bool Update::checkDownloadedJsonVersions(QStringList const& jsonFileNames) { - - for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) { - - uint8_t jsonNr = 0; - QFileInfo fInfo(jsonFileNames[i]); - - if (fInfo.fileName().endsWith("conf.json")) { - jsonNr = 1; - } else - if (fInfo.fileName().endsWith("device.json")) { - jsonNr = 2; - } else - if (fInfo.fileName().endsWith("cash.json")) { - jsonNr = 3; - } else { - QRegularExpressionMatch match; - static const QRegularExpression re("^(.*print)([0-3][0-9])\\.json\\s*$"); - int idx = fInfo.fileName().indexOf(re, 0, &match); - if (idx != -1) { - QString captured = match.captured(match.lastCapturedIndex()); - bool ok = false; - int n = captured.toInt(&ok); - if (ok) { - jsonNr = n + 4; - } - } - } - - if (jsonNr != 0) { -#if 0 - m_hw->sys_requestJsonVersions(jsonNr); - QThread::msleep(500); - - char buf[64]; - memset(buf, 0x00, sizeof(buf)); - m_hw->sys_getJsonVersions(jsonNr, buf); - buf[sizeof(buf)-1] = '\0'; - - QString const installedVersion(buf); - QString const fileVersion = getFileVersion(jsonFileNames[i]); - - qCritical() << "installed version:" << installedVersion; - qCritical() << " file version:" << fileVersion; - - if (installedVersion == fileVersion) { - - } -#endif - - } else { - qCritical() << "CANNOT FIND JSON-NR FOR" << jsonFileNames[i]; - } - } - - return false; -} - -bool Update::doUpdate(int &displayIndex, QStringList const &filesToWorkOn) { - //if (m_sys_areDCdataValid == false) { - // qCritical() << "ERROR!!! DC DATA NOT VALID -> CA-MASTER-PLUGIN NOT CONNECTED"; - // return false; - //} - - bool res = false; - QList::const_iterator it; - for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) { - m_worker->startProgressLoop(); - - QString const &fToWorkOn = QDir::cleanPath(m_customerRepository + QDir::separator() + it->trimmed()); - - if (fToWorkOn.contains("dc2c.bin")) { - bool updateBinaryRes = true; - - // CONSOLE() - - m_hw->dc_autoRequest(false);// default: turn auto-request setting off - QThread::sleep(1); // wait to be sure that there are no more - // commands sent to dc-hardware - - if ((updateBinaryRes = updateBinary(fToWorkOn)) == true) { - - // qCritical() << "downloaded binary" << fToWorkOn; - - ++displayIndex; - emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")") - + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), - Worker::UPDATE_STEP_DONE); - } - - m_hw->dc_autoRequest(true); // turn auto-request setting on - - // qInfo() << "SET AUTO-REQUEST=TRUE"; - - QStringList const &versions = Update::getDcSoftAndHardWareVersion(); - if (versions.size() >= 2) { - if (updateBinaryRes == true) { - qInfo() << "dc-hardware-version (UPDATED)" << versions[0]; - qInfo() << "dc-firmware-version (UPDATED)" << versions[1]; - } else { - qInfo() << "dc-hardware-version (NOT UPDATED)" << versions[0]; - qInfo() << "dc-firmware-version (NOT UPDATED)" << versions[1]; - } - } - - res = updateBinaryRes; - - } else { - if (fToWorkOn.contains("DC2C_print", Qt::CaseInsensitive) - && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { - res = true; - int i = fToWorkOn.indexOf("DC2C_print", Qt::CaseInsensitive); - int const templateIdx = fToWorkOn.mid(i).midRef(10, 2).toInt(); - if ((templateIdx < 1) || (templateIdx > 32)) { - qCritical() << "WRONG TEMPLATE INDEX" << templateIdx; - res = false; - } else { - if ((res = updatePrinterTemplate(templateIdx, fToWorkOn))) { - Utils::printInfoMsg( - QString("DOWNLOADED PRINTER TEMPLATE %1 WITH INDEX=%2") - .arg(fToWorkOn) - .arg(templateIdx)); - ++displayIndex; - emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")") - + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), - Worker::UPDATE_STEP_DONE); - } - } - } else if (fToWorkOn.contains("DC2C_cash", Qt::CaseInsensitive) - && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { - res = true; - if ((res = updateCashConf(fToWorkOn))) { - Utils::printInfoMsg(QString("DOWNLOADED CASH TEMPLATE %1").arg(fToWorkOn)); - ++displayIndex; - emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")") - + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), - Worker::UPDATE_STEP_DONE); - } - } else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive) - && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { - res = true; - if ((res= updateConfig(fToWorkOn))) { - Utils::printInfoMsg(QString("DOWNLOADED CONFIG TEMPLATE %1").arg(fToWorkOn)); - ++displayIndex; - emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")") - + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), - Worker::UPDATE_STEP_DONE); - } - } else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive) - && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { - res = true; - if ((res = updateDeviceConf(fToWorkOn))) { - Utils::printInfoMsg(QString("DOWNLOADED DEVICE TEMPLATE %1").arg(fToWorkOn)); - ++displayIndex; - emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(3, ' ') + QString(")") - + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), - Worker::UPDATE_STEP_DONE); - } - } else { - qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn; - res = false; - } - } - - if (res == false) { - break; - } - } // for (it = openLines.cbegin(); it != openLines.end(); ++it) { - - m_hw->dc_autoRequest(true); // ALWAYS turn autoRequest ON - qDebug() << "SET AUTO-REQUEST=TRUE"; - - return res; -} diff --git a/update.h b/update.h deleted file mode 100644 index 8dcfaea..0000000 --- a/update.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef UPDATE_H_INCLUDED -#define UPDATE_H_INCLUDED - -#include -#include -#include -#include -#include -#include -#include - -#include "plugins/interfaces.h" - -#ifdef PTU5 -#define SERIAL_PORT "ttymxc2" -#else -#define SERIAL_PORT "ttyUSB0" -#endif - -class Worker; -class Update : public QObject { - Q_OBJECT - - hwinf *m_hw = nullptr; - Worker *m_worker = nullptr; - char const *m_serialInterface; - char const *m_baudrate; - QString m_customerRepository; - QString m_customerNrStr; - QString m_branchName; - QString m_pluginName; - QString m_workingDir; - bool m_maintenanceMode; - bool m_dryRun; - bool m_sys_areDCdataValid; - - static QPluginLoader pluginLoader; - -public: - enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; - enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6}; - - static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn); - static bool unloadDCPlugin(); - static QStringList split(QString line, QChar sep = ','); - - - explicit Update(Worker *worker, - QString customerRepository, - QString customerNrStr, - QString branchName, - QString plugInDir, - QString pluginName, - QString workingDir, - bool dryRun = false, - QObject *parent = nullptr, - char const *serialInterface = SERIAL_PORT, - char const *baudrate = "115200"); - virtual ~Update() override; - bool doUpdate(int &displayIndex, QStringList const &linesToWorkOn); - bool checkDownloadedJsonVersions(QStringList const& jsonFileNames); - - hwinf *hw() { return m_hw; } - hwinf const *hw() const { return m_hw; } - - //QString customerId() { return m_customerId; } - //QString const customerId() const { return m_customerId; } - - QString branchName() { return m_branchName; } - QString const branchName() const { return m_branchName; } - - //QString repositoryPath() { return m_repositoryPath; } - //QString const repositoryPath() const { return m_repositoryPath; } - -private: - static QString jsonType(enum FileTypeJson type); - bool openSerial(int br, QString baudrate, QString comPort) const; - void closeSerial() const; - bool isSerialOpen() const; - bool resetDeviceController() const; - QByteArray loadBinaryDCFile(QString filename) const; - bool downloadBinaryToDC(QString const &bFile) const; - bool updatePrinterTemplate(int templateIdx, QString fname) const; - bool updateBinary(QString const &fileToSendToDC); - bool updateConfig(QString jsFileToSendToDC); - bool updateCashConf(QString jsFileToSendToDC); - bool updateDeviceConf(QString jsFileToSendToDC); - bool downloadJson(enum FileTypeJson type, int templateIdx, - QString jsFileToSendToDC) const; - QStringList getDcSoftAndHardWareVersion(); - QString getFileVersion(QString const& jsonFile); - -private slots: - void readyReadStandardOutput(); - void readyReadStandardError(); - void finished(int exitCode, QProcess::ExitStatus exitStatus); - -}; -#endif // UPDATE_H_INCLUDED