diff --git a/DownloadDCFirmware/DownloadDCFirmware.pro b/DownloadDCFirmware/DownloadDCFirmware.pro index 088f6d1..51f5641 100644 --- a/DownloadDCFirmware/DownloadDCFirmware.pro +++ b/DownloadDCFirmware/DownloadDCFirmware.pro @@ -1,7 +1,6 @@ -QT += core gui -QT += widgets serialport network +QT += core serialport -TARGET = ATBDownloadDCFirmware +TARGET = ATBUpdateDC VERSION="0.1.0" win32 { @@ -77,21 +76,21 @@ contains( CONFIG, DesktopLinux ) { SOURCES += \ main.cpp \ - mainwindow.cpp \ ../common/src/message_handler.cpp \ ../UpdatePTUDevCtrl/commandline_parser.cpp \ update.cpp \ - dc_download.cpp \ - ../common/src/System.cpp + ../common/src/System.cpp \ + ../common/src/utils_internal.cpp \ + ../common/src/command.cpp HEADERS += \ - mainwindow.h \ ../common/include/message_handler.h \ ../UpdatePTUDevCtrl/commandline_parser.h \ update.h \ - dc_download.h \ - ../common/include/System.h + ../common/include/System.h \ + ../common/include/utils_internal.h \ + ../common/include/command.h OTHER_FILES += \ diff --git a/DownloadDCFirmware/main.cpp b/DownloadDCFirmware/main.cpp index 7c39038..793db15 100644 --- a/DownloadDCFirmware/main.cpp +++ b/DownloadDCFirmware/main.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include @@ -11,12 +10,13 @@ #include #include #include +#include #include "message_handler.h" #include "commandline_parser.h" #include "utils.h" +#include "utils_internal.h" #include "update.h" -#include "mainwindow.h" #include "System.h" #include @@ -52,17 +52,22 @@ int main(int argc, char **argv) { } // qputenv("XDG_RUNTIME_DIR", "/var/run/user/0"); - openlog("ATB-UPDATE-DC-FIRMWARE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); + 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); - QApplication a(argc, argv); - QApplication::setApplicationName("ATBDownloadDCFirmware"); - QApplication::setApplicationVersion(APP_VERSION); if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling atbInstallMessageHandler(atbDebugOutput); setDebugLevel(LOG_NOTICE); } + //return 0; + +/* CommandLineParser parser; parser.process(a); parser.readSettings(); @@ -73,6 +78,7 @@ int main(int argc, char **argv) { 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(); @@ -81,6 +87,7 @@ int main(int argc, char **argv) { 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(); @@ -109,27 +116,87 @@ int main(int argc, char **argv) { 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)); - QStringList filesToUpdate; + QString const &customerRepo + = QDir::cleanPath(workingDir + QDir::separator() + QString("customer_%1").arg(customerNr)); +*/ - QThread::currentThread()->setObjectName("main thread"); - qInfo() << "Main thread" << QThread::currentThreadId(); + QString const &psaDcDir = internal::customerRepoDcDir(); + QString const &psaRepoRootDir = internal::customerRepoRoot(); + QString const &psaRepoDir = internal::customerRepoDir(); + QString const &branchName = internal::branchName(); - MainWindow mw; + bool debug = false; + bool noaction = true; + QString workingDir; + QString libDir; + QString libca; - mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint); - // mw.showFullScreen(); + std::unique_ptr settings = internal::readSettings(); - // qCritical() << "SHOW"; + if (settings) { + settings->beginGroup("DIRECTORIES"); + workingDir = settings->value("workingdir", "/tmp").toString(); + libDir = settings->value("plugin-directory", "/usr/lib/").toString(); + settings->endGroup(); - // mw.show(); + 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; - // return a.exec(); } diff --git a/DownloadDCFirmware/mainwindow.h b/DownloadDCFirmware/mainwindow.h index 72a2ea1..7a76079 100644 --- a/DownloadDCFirmware/mainwindow.h +++ b/DownloadDCFirmware/mainwindow.h @@ -1,10 +1,7 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H -#include #include -#include -#include #include #include diff --git a/DownloadDCFirmware/update.cpp b/DownloadDCFirmware/update.cpp index c42103f..7627e31 100644 --- a/DownloadDCFirmware/update.cpp +++ b/DownloadDCFirmware/update.cpp @@ -1,4 +1,5 @@ #include "update.h" +#include "command.h" #include #include @@ -21,10 +22,16 @@ #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} @@ -32,48 +39,38 @@ static const QMap baudrateMap = { QPluginLoader Update::pluginLoader; -hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { +hwinf *Update::loadDCPlugin(QString const &libCA /* absolute file path */) { 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; - } + 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 directory:" << plugInDir.absolutePath(); - qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName(); + // 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"; + 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() << "plugins directory" << plugInDir.absolutePath() - << "does not exist"; + qCritical() << libCAInfo.absoluteFilePath() << "does not exist"; return nullptr; } + return hw; } @@ -92,347 +89,389 @@ bool Update::unloadDCPlugin() { 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 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(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(); } -bool Update::doUpdate(QStringList const &filesToWorkOn, bool usbStickDetected) { - - if (!m_hw) { - qCritical() << "(" << __func__ << ":" << __LINE__ << "):" - << "ERROR!!! m_hw == nullptr"; - return false; +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:; } - - 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; + return DownloadResult::ERROR; } -bool Update::checkJsonVersions(QStringList const& jsonFileNames) { - if (!m_hw) { - qCritical() << "(" << __func__ << ":" << __LINE__ << "):" - << "ERROR!!! m_hw == nullptr"; - return false; - } +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) { - 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; + 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()); } - qCritical() << "(" << __func__ << ":" << __LINE__ << "):" - << "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")"; - m_hw->dc_autoRequest(true); - QThread::msleep(500); + + 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"; } - for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) { + return true; +} - uint8_t jsonNr = 0; +bool Update::stopBootloader() const { + qDebug() << "stopping bootloader..."; - QString const &fName = jsonFileNames[i]; + 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; - // 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 + } else { + QThread::msleep(1000); + qInfo() << "stopping bootloader...OK"; + } - 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; + 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! } - 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 + for (int i = 0; i < 3; ++i) { + qInfo() << "waiting for device controller restart...(" << i << ")"; + QThread::sleep(20); - 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; + resetDeviceController(); + if (startBootloader()) { + qInfo() << nextTimePoint().toUtf8().constData() << ""; + stopBootloader(); + return -(int)Result::SUCCESS; } - - } else { - qCritical() << "CANNOT FIND JSON-NR FOR" << fName; } } + qInfo() << nextTimePoint().toUtf8().constData() << ""; + //To Do Error handling if Dc doesnt start after download 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"); - } - } - - 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); -} diff --git a/DownloadDCFirmware/update.h b/DownloadDCFirmware/update.h index daa3037..cc8cbc6 100644 --- a/DownloadDCFirmware/update.h +++ b/DownloadDCFirmware/update.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include @@ -15,115 +17,57 @@ #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; - 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; + 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 FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6}; + enum class Result {SUCCESS=0, PLUGIN_LOAD_ERROR}; - static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn); + static hwinf *loadDCPlugin(QString const &libCA = "/usr/lib/libCAslave.so"); 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"); - + explicit Update(QString const &dcBinFile, QString const &libCA, bool debug, bool noaction); virtual ~Update() override; - bool doUpdate(QStringList const &jsonFilesToDownload, bool usbStickDetected = false); + int run(); + static QString dcVersion(QString const &dcBinFile); - bool updatePrinterTemplate(int templateIdx, QString fname) const; - bool updateConfig(QString jsFileToSendToDC); - bool updateCashConf(QString jsFileToSendToDC); - bool updateDeviceConf(QString jsFileToSendToDC); +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; - 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")}))); + QString m_fileToDownload; + uint16_t m_totalBlocks = 0; /* - 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; diff --git a/UpdatePTUDevCtrl/commandline_parser.cpp b/UpdatePTUDevCtrl/commandline_parser.cpp index 3efb346..b379b28 100644 --- a/UpdatePTUDevCtrl/commandline_parser.cpp +++ b/UpdatePTUDevCtrl/commandline_parser.cpp @@ -6,7 +6,9 @@ #include CommandLineParser::CommandLineParser() - : m_repositoryUrl("https://git.mimbach49.de/GerhardHoffmann") + : m_repositoryUrl("gitea@ptu-config.atb-comm.de:ATB/") + , m_sshKeyFile("/opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig") + , m_sshOptionStrictHostKeyChecking("false") , m_plugInDir("/usr/lib/") , m_plugInName("libCAslave.so") , m_workingDir("/opt/app/tools/atbupdate/") @@ -20,27 +22,36 @@ CommandLineParser::CommandLineParser() , m_alwaysDownloadDC("false") , m_repositoryUrlOption( QCommandLineOption( - QStringList() << "repository-url" << "repository-url", + QStringList() << "repository-url", QCoreApplication::translate("main", "Where to find a customer repository."), QCoreApplication::translate("main", "directory"))) + , m_sshKeyFileOption( + QCommandLineOption( + QStringList() << "sshKeyFile", + QCoreApplication::translate("main", "Used ssh key."), + QCoreApplication::translate("main", "file"))) + , m_sshOptionStrictHostKeyCheckingOption( + QCommandLineOption( + QStringList() << "sshStrictHostKeyChecking", + QCoreApplication::translate("main", "Enable ssh strict host key checking"))) , m_iniFileDirectoryOption( QCommandLineOption( - QStringList() << "ini-directory" << "ini-directory", + QStringList() << "ini-directory", QCoreApplication::translate("main", "Where to find an ini-file."), QCoreApplication::translate("main", "directory"))) , m_iniFileNameOption( QCommandLineOption( - QStringList() << "ini-filename" << "ini-filename", + QStringList() << "ini-filename", QCoreApplication::translate("main", "Name of ini-file."), QCoreApplication::translate("main", "file"))) , m_pluginDirectoryOption( QCommandLineOption( - QStringList() << "plugin-directory" << "plugin-directory", + QStringList() << "plugin-directory", QCoreApplication::translate("main", "Where to find dc-plugin."), QCoreApplication::translate("main", "directory"))) , m_pluginNameOption( QCommandLineOption( - QStringList() << "plugin-name" << "plugin-name", + QStringList() << "plugin-name", QCoreApplication::translate("main", "Name of dc-plugin."), QCoreApplication::translate("main", "directory"))) , m_noDownloadOption( @@ -57,17 +68,17 @@ CommandLineParser::CommandLineParser() QCoreApplication::translate("main", "Always download the dc-bin-file to DC)."))) , m_workingDirectoryOption( QCommandLineOption( - QStringList() << "working-directory" << "working-directory", + QStringList() << "working-directory", QCoreApplication::translate("main", "working directory of update-script."), QCoreApplication::translate("main", "directory"))) , m_psaConfigDirectoryOption( QCommandLineOption( - QStringList() << "psa-config-directory" << "psa-config-directory", + QStringList() << "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", + QStringList() << "psa-tariff-directory", QCoreApplication::translate("main", "tariff directory of tariff-json-files."), QCoreApplication::translate("main", "directory"))) , m_dryRunOption( @@ -85,7 +96,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."))) +{ configure(); } @@ -94,9 +115,15 @@ void CommandLineParser::configure() { m_parser.addHelpOption(); m_parser.addVersionOption(); - m_repositoryUrlOption.setDefaultValue("https://git.mimbach49.de/GerhardHoffmann"); + m_repositoryUrlOption.setDefaultValue("gitea@ptu-config.atb-comm.de:ATB/"); m_parser.addOption(m_repositoryUrlOption); + m_sshKeyFileOption.setDefaultValue("/opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig"); + m_parser.addOption(m_sshKeyFileOption); + + m_sshOptionStrictHostKeyCheckingOption.setDefaultValue(""); + m_parser.addOption(m_sshOptionStrictHostKeyCheckingOption); + m_iniFileDirectoryOption.setDefaultValue(QCoreApplication::applicationDirPath()); m_parser.addOption(m_iniFileDirectoryOption); @@ -138,6 +165,12 @@ 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() { @@ -145,8 +178,8 @@ void CommandLineParser::readSettings() { 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; + //qCritical() << __PRETTY_FUNCTION__ << " iniFileDir" << iniFileDir; + //qCritical() << __PRETTY_FUNCTION__ << "iniFileName" << m_iniFileName; if (!m_iniFileName.isEmpty()) { if (QFile(m_iniFileName).exists()) { @@ -155,12 +188,18 @@ void CommandLineParser::readSettings() { for (QString const &key: keys) { QVariant v = settings.value(key); - qCritical() << __PRETTY_FUNCTION__ - << key << " -> " << v.toString(); + //qCritical() << "(" << __func__ << ":" << __LINE__ << ")" + // << key << " -> " << v.toString(); if (key.contains("repository-url")) { m_repositoryUrl = v.toString(); } else + if (key.contains("sshKeyFile")) { + m_sshKeyFile = v.toString(); + } else + if (key.contains("sshOptionStrictHostKeyChecking")) { + m_sshOptionStrictHostKeyChecking = (v.toBool() ? "true" : "false"); + } else if (key.contains("plugin-directory")) { m_plugInDir = v.toString(); } else @@ -196,6 +235,12 @@ 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(); @@ -215,6 +260,21 @@ QString CommandLineParser::repositoryUrl() { return m_repositoryUrl; } +QString CommandLineParser::sshKeyFile() { + if (m_parser.isSet(m_sshKeyFileOption)) { + m_sshKeyFile = m_parser.value(m_sshKeyFileOption); + } + return m_sshKeyFile; +} + +bool CommandLineParser::sshOptionStrictHostKeyChecking() +{ + if (m_parser.isSet(m_sshOptionStrictHostKeyCheckingOption)) { + m_sshOptionStrictHostKeyChecking = m_parser.value(m_sshOptionStrictHostKeyCheckingOption); + } + return m_sshOptionStrictHostKeyChecking == "false" ? false : true; +} + QString CommandLineParser::plugInDir() { if (m_parser.isSet(m_pluginDirectoryOption)) { m_plugInDir = m_parser.value(m_pluginDirectoryOption); @@ -243,6 +303,20 @@ QString CommandLineParser::psaTariffDir() { 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); @@ -288,9 +362,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_alwaysDownloadConfigOption IS SET" << m_alwaysDownloadConfig; } - qCritical() << "m_alwaysDownloadConfig" << m_alwaysDownloadConfig; + // qCritical() << "m_alwaysDownloadConfig" << m_alwaysDownloadConfig; return m_alwaysDownloadConfig == "false" ? false : true; } diff --git a/UpdatePTUDevCtrl/commandline_parser.h b/UpdatePTUDevCtrl/commandline_parser.h index 22f906a..9e577c1 100644 --- a/UpdatePTUDevCtrl/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 @@ -8,11 +8,13 @@ class CommandLineParser : public QCommandLineParser { QString m_repositoryUrl; + QString m_sshKeyFile; + QString m_sshOptionStrictHostKeyChecking; QString m_plugInDir; QString m_plugInName; QString m_workingDir; - QString m_psaConfigDir; - QString m_psaTariffDir; + QString m_psaConfigDir{"etc/psa_config"}; + QString m_psaTariffDir{"etc/psa_tariff"}; QString m_dryRun; QString m_noUpdatePsaHardware; QString m_showYoctoVersion; @@ -21,8 +23,12 @@ 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_sshKeyFileOption; + QCommandLineOption m_sshOptionStrictHostKeyCheckingOption; QCommandLineOption m_iniFileDirectoryOption; QCommandLineOption m_iniFileNameOption; QCommandLineOption m_pluginDirectoryOption; @@ -37,6 +43,8 @@ class CommandLineParser : public QCommandLineParser { QCommandLineOption m_extendedVersionOption; QCommandLineOption m_yoctoVersionOption; QCommandLineOption m_yoctoInstallStatusOption; + QCommandLineOption m_dcDirectoryOption; + QCommandLineOption m_readDCVersionOption; QCommandLineParser m_parser; @@ -53,6 +61,8 @@ public: QString const &iniFileName() const { return m_iniFileName; } void readSettings(); QString repositoryUrl(); + QString sshKeyFile(); + bool sshOptionStrictHostKeyChecking(); QString plugInDir(); QString plugInName(); QString workingDir(); @@ -65,5 +75,7 @@ public: bool extendedVersion(); bool alwaysDownloadConfig(); bool alwaysDownloadDC(); + bool readDCVersion(); + QString dcDir(); }; #endif // COMMAND_LINE_PARSER_H_INCLUDED diff --git a/UpdatePTUDevCtrl/git/git_client.cpp b/UpdatePTUDevCtrl/git/git_client.cpp index e194ad6..0eac2aa 100644 --- a/UpdatePTUDevCtrl/git/git_client.cpp +++ b/UpdatePTUDevCtrl/git/git_client.cpp @@ -2,6 +2,7 @@ #include "update.h" #include "worker.h" #include "utils.h" +#include "process/command.h" #include #include @@ -399,6 +400,12 @@ std::optional GitClient::gitPull() { If remote host keys are changed, then export GIT_SSH_COMMAND="ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig" + + ... or ignore host key checking: + export GIT_SSH_COMMAND="ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig -o StrictHostKeyChecking=no" + + ... or use separate known_hosts file: + export GIT_SSH_COMMAND="ssh -i /mypath/.keys/id_ed25519 -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/opt/app/tools/atbupdate/.keys/known_hosts git pull leads to the following warning/error message: diff --git a/UpdatePTUDevCtrl/git/git_client.h b/UpdatePTUDevCtrl/git/git_client.h index 31e5001..d7000e3 100644 --- a/UpdatePTUDevCtrl/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; diff --git a/UpdatePTUDevCtrl/main.cpp b/UpdatePTUDevCtrl/main.cpp index c3e8cba..524f82c 100644 --- a/UpdatePTUDevCtrl/main.cpp +++ b/UpdatePTUDevCtrl/main.cpp @@ -31,6 +31,7 @@ #include "worker.h" #include "mainwindow.h" #include "utils.h" +// #include "process/command.h" #include #include @@ -71,39 +72,43 @@ int main(int argc, char *argv[]) { CommandLineParser parser; parser.process(a); + + if (parser.isSet(parser.addHelpOption())) { + parser.showHelp(0); + return 0; + } + parser.readSettings(); QString repositoryUrl = parser.repositoryUrl(); + QString sshKeyFile = parser.sshKeyFile(); + bool strictHostKeyChecking = parser.sshOptionStrictHostKeyChecking(); if (repositoryUrl.endsWith('/')) { repositoryUrl.chop(1); } - QString gitSSHCommand(""); + // default for gitSSHCommand + QString gitSSHCommand = "ssh -i " + sshKeyFile; - 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..."; + strictHostKeyChecking ? gitSSHCommand.append(" -o StrictHostKeyChecking=yes") + : gitSSHCommand.append(" -o StrictHostKeyChecking=no"); + + QByteArray const v = qgetenv("GIT_SSH_COMMAND"); + if (v.isEmpty()) { + if (QFileInfo(sshKeyFile).exists()) { + if (!qputenv("GIT_SSH_COMMAND", QByteArray(gitSSHCommand.toStdString().c_str()))) { + qCritical() << "ERROR: GIT_SSH_COMMAND not put into env. 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; - } + 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; } QString plugInDir = parser.plugInDir(); @@ -160,7 +165,6 @@ int main(int argc, char *argv[]) { return 0; } - QThread::currentThread()->setObjectName("main thread"); qInfo() << "Main thread" << QThread::currentThreadId(); @@ -199,5 +203,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/UpdatePTUDevCtrl/mainwindow.cpp b/UpdatePTUDevCtrl/mainwindow.cpp index 1d69513..c17b24d 100644 --- a/UpdatePTUDevCtrl/mainwindow.cpp +++ b/UpdatePTUDevCtrl/mainwindow.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include @@ -24,17 +26,17 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent) , 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.setPixelSize(12); + f.setPointSize(11); this->statusBar()->setFont(f); - ui->setupUi(this); - checkOrientation(); - ui->updateProgress->setRange(0, 100); ui->updateProgress->reset(); @@ -42,15 +44,15 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent) 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("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->updateStatus->setText(lst.join('\n')); - ui->updateStatus->setEnabled(true); + ui->updateLabel->setText(lst.join('\n')); + ui->updateLabel->setEnabled(true); // ui->updateStatus->installEventFilter(this); m_startTimer = new QTimer(this); @@ -82,11 +84,15 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent) } 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))); @@ -95,6 +101,29 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent) 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; @@ -253,6 +282,12 @@ void MainWindow::scrollDownTextEdit() { 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)); diff --git a/UpdatePTUDevCtrl/mainwindow.h b/UpdatePTUDevCtrl/mainwindow.h index 4a80d18..d128d03 100644 --- a/UpdatePTUDevCtrl/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 @@ -80,5 +86,6 @@ private: //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 index 82fa12e..f88b4c1 100644 --- a/UpdatePTUDevCtrl/mainwindow.ui +++ b/UpdatePTUDevCtrl/mainwindow.ui @@ -18,54 +18,117 @@ - Source Code Pro + Misc Fixed + 11 + 75 + true MainWindow - - - - - - - Exit - - - - - - - 1 - - - - - - - true - - - - Misc Fixed - 11 - 75 - true - - - - Qt::ScrollBarAsNeeded - - - Qt::ScrollBarAsNeeded - - - - - - + + + + 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/process/command.cpp b/UpdatePTUDevCtrl/process/command.cpp index 8e0cf68..b0b8747 100644 --- a/UpdatePTUDevCtrl/process/command.cpp +++ b/UpdatePTUDevCtrl/process/command.cpp @@ -1,33 +1,86 @@ #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_exitCode(-1) + , m_p(nullptr) + , m_worker(nullptr) { } -QString Command::getCommandResult() const { - return m_commandResult; +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(); - m_commandResult += p->readAllStandardOutput(); - // qCritical() << m_commandResult; + 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(); - QByteArray buf = p->readAllStandardError(); - qCritical() << buf; + if (p) { + QByteArray buf = p->readAllStandardError(); + qCritical() << buf; + } } void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { @@ -35,12 +88,38 @@ void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { // 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)) { diff --git a/UpdatePTUDevCtrl/process/command.h b/UpdatePTUDevCtrl/process/command.h index bc796ee..71efb22 100644 --- a/UpdatePTUDevCtrl/process/command.h +++ b/UpdatePTUDevCtrl/process/command.h @@ -7,27 +7,34 @@ #include #include #include +#include - +class Worker; class Command : public QObject { Q_OBJECT QString m_command; - QString m_commandResult; + 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() const; + 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(); diff --git a/UpdatePTUDevCtrl/update.cpp b/UpdatePTUDevCtrl/update.cpp index 5b9d376..837e467 100644 --- a/UpdatePTUDevCtrl/update.cpp +++ b/UpdatePTUDevCtrl/update.cpp @@ -133,36 +133,6 @@ Update::Update(Worker *worker, qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_sys_areDCDataValid ..." << m_sys_areDCdataValid; - -#if 0 - QObject const *obj = m_hw->getAPI(); - Q_ASSERT(obj != nullptr); - - QDebug critical = qCritical(); - critical << "connect() to onReportDCDownloadStatus() ..."; - if (!connect(obj, - SIGNAL(hwapi_reportDCDownloadStatus(QString const&)), - this, - SLOT(onReportDCDownloadStatus(QString const &)))) { - critical << "FAILED"; - } else critical << "DONE"; - - critical = qCritical(); - critical << "connect() to onReportDCDownloadSuccess() ..."; - if (!connect(obj, - SIGNAL(hwapi_reportDCDownloadSuccess(QString const&)), this, - SLOT(onReportDCDownloadSuccess(QString const &)))) { - critical << "FAILED"; - } else critical << "DONE"; - - critical = qCritical(); - critical << "connect() to onReportDCDownloadFailure() ..."; - if (!connect(obj, - SIGNAL(hwapi_reportDCDownloadFailure(QString const &)), this, - SLOT(onReportDCDownloadFailure(QString const &)))) { - critical << "FAILED"; - } else critical << "DONE"; -#endif } } @@ -302,7 +272,14 @@ bool Update::isSerialOpen() const { // bootloader is really not running anymore. */ bool Update::updateBinary(QString const &fileToSendToDC) { - qInfo() << "UPDATING DEVICE CONTROLLER FIRMWARE BINARY" << 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; diff --git a/UpdatePTUDevCtrl/utils.cpp b/UpdatePTUDevCtrl/utils.cpp index 010b838..7b7ec65 100644 --- a/UpdatePTUDevCtrl/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()) { diff --git a/UpdatePTUDevCtrl/utils.h b/UpdatePTUDevCtrl/utils.h index 8fdd648..a5c93c1 100644 --- a/UpdatePTUDevCtrl/utils.h +++ b/UpdatePTUDevCtrl/utils.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace Utils { int read1stLineOfFile(QString fileName); @@ -33,6 +34,8 @@ namespace Utils { QString getParentName(); bool isATBQTRunning(); + + QVector> installedPackages(); } #endif // UTILS_H_INCLUDED diff --git a/UpdatePTUDevCtrl/worker.cpp b/UpdatePTUDevCtrl/worker.cpp index 8c1a12a..4cddd9d 100644 --- a/UpdatePTUDevCtrl/worker.cpp +++ b/UpdatePTUDevCtrl/worker.cpp @@ -26,6 +26,7 @@ #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]"); @@ -176,9 +177,14 @@ 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; @@ -284,6 +290,8 @@ void Worker::privateUpdate() { return; } + return; + QString func(__PRETTY_FUNCTION__); GUI() << (ISMAS() << (CONSOLE() << UPDATE_STEP::STARTED)); @@ -1257,6 +1265,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(); @@ -1278,6 +1287,7 @@ QStringList Worker::getDCVersion() const { } } } +#endif return lst; } @@ -1490,3 +1500,78 @@ PSAInstalled Worker::getPSAInstalled() { 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/UpdatePTUDevCtrl/worker.h b/UpdatePTUDevCtrl/worker.h index d30984d..8c54d69 100644 --- a/UpdatePTUDevCtrl/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" @@ -135,6 +134,8 @@ #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{ @@ -189,6 +190,7 @@ class Worker : public QThread{ QStringList m_ismasTriggerStatusMessage; MainWindow *m_mainWindow; + Command *m_dcDownloadFirmware; bool m_withoutIsmasDirectPort; QString m_apismVersion; @@ -458,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); @@ -470,6 +477,9 @@ signals: void restartExitTimer(); void enableExit(); void disableExit(); + void showDcDownload(QString); + void showSummary(QString); + void setDcDownloadProgress(int); private slots: bool updateTriggerSet(); @@ -487,6 +497,8 @@ private: bool computeFilesToDownload(); bool execOpkgCommands(); + QDateTime m_start; + static const QMap smap; // CONSOLE() diff --git a/common/include/System.h b/common/include/System.h index 4c59a04..18c0a45 100644 --- a/common/include/System.h +++ b/common/include/System.h @@ -25,7 +25,9 @@ public: 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(); @@ -54,7 +56,8 @@ public: 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/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 index 72358e2..2af9af8 100644 --- a/common/src/System.cpp +++ b/common/src/System.cpp @@ -265,6 +265,20 @@ std::optional System::checkForUSBStick(QString const &dirPathUnderMount 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); +} + /** @@ -315,7 +329,7 @@ QString System::getUSBMountPath(QString const &dirPathUnderMountPath) { mountLine = line.split(' '); if (mountLine.size() > 3) { - qCritical() << "System::getUSBMountPath(): " << mountLine.at(0) << " is mounted on " << mountLine.at(2); + // 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); @@ -332,6 +346,64 @@ QString System::getUSBMountPath(QString const &dirPathUnderMountPath) { 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; @@ -354,6 +426,38 @@ QStringList System::getJsonFilesOnUsbStick(QString const &mountPath) { 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. * 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/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