From e9cb421ac595d4c6fc20fa7eebde2c07f9b00ef3 Mon Sep 17 00:00:00 2001 From: Gerhard Hoffmann Date: Tue, 14 May 2024 16:37:45 +0200 Subject: [PATCH] Add first draft --- DownloadDCFirmware/dc_download.cpp | 443 +++++++++++++++++++++++++++++ DownloadDCFirmware/dc_download.h | 32 +++ DownloadDCFirmware/update.cpp | 438 ++++++++++++++++++++++++++++ DownloadDCFirmware/update.h | 148 ++++++++++ 4 files changed, 1061 insertions(+) create mode 100644 DownloadDCFirmware/dc_download.cpp create mode 100644 DownloadDCFirmware/dc_download.h create mode 100644 DownloadDCFirmware/update.cpp create mode 100644 DownloadDCFirmware/update.h diff --git a/DownloadDCFirmware/dc_download.cpp b/DownloadDCFirmware/dc_download.cpp new file mode 100644 index 0000000..dcd6921 --- /dev/null +++ b/DownloadDCFirmware/dc_download.cpp @@ -0,0 +1,443 @@ +#include "dc_download.h" +#include "hwapi.h" + +#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/update.cpp b/DownloadDCFirmware/update.cpp new file mode 100644 index 0000000..c42103f --- /dev/null +++ b/DownloadDCFirmware/update.cpp @@ -0,0 +1,438 @@ +#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"); + } + } + + 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 new file mode 100644 index 0000000..daa3037 --- /dev/null +++ b/DownloadDCFirmware/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