diff --git a/CArunGUI/CArunGUI.pro b/CArunGUI/CArunGUI.pro index 1e34ec4..41050e3 100644 --- a/CArunGUI/CArunGUI.pro +++ b/CArunGUI/CArunGUI.pro @@ -21,14 +21,15 @@ INCLUDEPATH += ../include win32 { BUILD_DATE=$$system("date /t") BUILD_TIME=$$system("time /t") + GIT_COMMIT="" + EXTENDED_VERSION="" } else { BUILD_DATE=$$system("date +%d-%m-%y") BUILD_TIME=$$system("date +%H:%M:%S") + GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1") + EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}" } -GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1") - -EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}" DEFINES+=APP_VERSION=\\\"$$VERSION\\\" DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\" diff --git a/dCArun/dCArun.pro b/dCArun/dCArun.pro index 82bfba6..c2f8967 100644 --- a/dCArun/dCArun.pro +++ b/dCArun/dCArun.pro @@ -25,15 +25,16 @@ INCLUDEPATH += ../include win32 { BUILD_DATE=$$system("date /t") BUILD_TIME=$$system("time /t") + GIT_COMMIT="" + EXTENDED_VERSION="" } else { BUILD_DATE=$$system("date +%d-%m-%y") BUILD_TIME=$$system("date +%H:%M:%S") + + GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1") + EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}" } -GIT_COMMIT=$$system("git log -1 --format=oneline | cut -d' ' -f1") - -EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}" - DEFINES+=APP_VERSION=\\\"$$VERSION\\\" DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\" DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\" diff --git a/include/download_thread.h b/include/download_thread.h new file mode 100644 index 0000000..941cd26 --- /dev/null +++ b/include/download_thread.h @@ -0,0 +1,37 @@ +#ifndef DOWNLOAD_THREAD_H_INCLUDED +#define DOWNLOAD_THREAD_H_INCLUDED + +#include +#include +#include + +class hwinf; +class DownloadThread : public QThread { + Q_OBJECT + +public: + enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; + + DownloadThread(hwinf *hw); + ~DownloadThread(); + +protected: + // download thread does not have a running event queue, and therefore + // no slots. signals work the usual way. + void run() override; + +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/include/hwapi.h b/include/hwapi.h index 89d745d..f34692d 100644 --- a/include/hwapi.h +++ b/include/hwapi.h @@ -80,7 +80,8 @@ V4.0 6.9.2023: activating DC-Bootloader in slve-lib (SM) class QSharedMemory; - +class DownloadThread; +class ReportingThread; class hwapi : public QObject, public hwinf { @@ -92,6 +93,8 @@ private: void sub_storeSendingText(QByteArray *buf) const; QTimer *hwapi_TimerPayment; QSharedMemory *m_sharedMem; + ReportingThread *m_reportingThread; + DownloadThread *m_downloadThread; //QTimer *hwapi_triggerBL; public: @@ -1316,10 +1319,47 @@ public: // countOfBills: array of up to 16 sums, countOfBills[0]=nr of 5€-bills in stacker // countOfBills[1] for 10€ and so on + // download device controller + bool dcDownloadRequest(QString const &fileToDownload) const override; + bool dcDownloadRequested() const override; + bool dcDownloadResetRequest() const override; + bool dcDownloadRequestAck() const override; + bool dcDownloadRunning() const override; + bool dcDownloadFinished() override; + bool dcDownloadReportStart() const override; + bool dcDownloadReportRunning() const override; + bool dcDownloadReportFinished() override; + bool dcDownloadThreadStart() override; + bool dcDownloadThreadRunning() const override; + void dcDownloadThreadFinalize(DownloadThread *) override; + bool dcDownloadThreadFinished() const override; + bool dcDownloadReportThreadStart() override; + bool dcDownloadReportThreadRunning() const override; + void dcDownloadReportThreadFinalize() override; + void dcDownloadReportThreadQuit() override; + bool dcDownloadReportThreadFinished() const override; + + QString dcDownloadFileName() const override; + bool dcDownloadSetRequested(bool) override; + bool dcDownloadSetRunning(bool) override; + bool dcDownloadSetFinished(bool) override; + void dcDownloadSetTotalBlockNumber(uint16_t totalBlockNumber) override; + void dcDownloadSetCurrentBlockNumber(uint16_t currentBlockNumber) override; + bool dcDownloadGetRequested() const override; + bool dcDownloadGetRunning() const override; + bool dcDownloadGetFinished() const override; + uint16_t dcDownloadGetTotalBlockNumber() const override; + uint16_t dcDownloadGetCurrentBlockNumber() const override; + + virtual QObject const *getAPI() override; signals: + void hwapi_reportDCDownloadStatus(QString const&) const; + void hwapi_reportDCDownloadSuccess(QString const&) const; + void hwapi_reportDCDownloadFailure(QString const&) const; + void hwapi_templatePrintFinished_OK(void) const override; void hwapi_templatePrintFinished_Err(void) const override; @@ -1344,9 +1384,7 @@ signals: void hwapi_doorCBinAndAllDoorsClosed(void) const override; void hwapi_doorAllDoorsClosed(void) const override; - - - + void hwapi_coinAttached() const override; private slots: //void hwapi_slotPrintFinished_OK(void); @@ -1374,8 +1412,7 @@ signals: void sub_slotCoin15(void); void sub_slotCoin16(void); - - + void coinAttached(); }; diff --git a/include/interfaces.h b/include/interfaces.h index 51ec7e1..81afbdd 100755 --- a/include/interfaces.h +++ b/include/interfaces.h @@ -414,6 +414,7 @@ struct T_bna }; +class DownloadThread; class hwinf { @@ -2299,9 +2300,73 @@ public: // countOfBills[1] for 10€ and so on + // download device controller + virtual bool dcDownloadRequest(QString const &fileToDownload) const { + Q_UNUSED(fileToDownload); + return false; + } + virtual bool dcDownloadRequested() const { return false; } + virtual bool dcDownloadResetRequest() const { return false; } + virtual bool dcDownloadRequestAck() const { return false; } + virtual bool dcDownloadRunning() const { return false; } + virtual bool dcDownloadFinished() { return false; } + + virtual bool dcDownloadReportStart() const { return false; } + virtual bool dcDownloadReportRunning() const { return true; } + virtual bool dcDownloadReportFinished() { return true; } + + virtual bool dcDownloadThreadStart() { return false; } + virtual bool dcDownloadThreadRunning() const { return true; } + virtual void dcDownloadThreadFinalize(DownloadThread *) {} + virtual bool dcDownloadThreadFinished() const { return true; } + virtual bool dcDownloadReportThreadStart() { return false; } + virtual bool dcDownloadReportThreadRunning() const { return true; } + virtual void dcDownloadReportThreadFinalize() {} + virtual void dcDownloadReportThreadQuit() {} + virtual bool dcDownloadReportThreadFinished() const { return true; } + + + virtual QString dcDownloadFileName() const { return ""; } + virtual bool dcDownloadSetRequested(bool requested) { + Q_UNUSED(requested); return false; + } + virtual bool dcDownloadSetRunning(bool running) { + Q_UNUSED(running); return false; + } + virtual bool dcDownloadSetFinished(bool finished) { + Q_UNUSED(finished); return false; + } + virtual void dcDownloadSetTotalBlockNumber(uint16_t totalBlockNumber) { + Q_UNUSED(totalBlockNumber); + } + virtual void dcDownloadSetCurrentBlockNumber(uint16_t currentBlockNumber) { + Q_UNUSED(currentBlockNumber); + } + virtual bool dcDownloadGetRequested() const { return false; } + virtual bool dcDownloadGetRunning() const { return false; } + virtual bool dcDownloadGetFinished() const { return false; } + virtual uint16_t dcDownloadGetTotalBlockNumber() const { return 0; } + virtual uint16_t dcDownloadGetCurrentBlockNumber() const { return 0; } + + virtual QObject const *getAPI() { return nullptr; } signals: + /* + NOTE: the difference between a virtual Qt signal and a normal Qt signal: + A Qt virtual signal is a connection that is established using a pointer + or reference and is not connected to an object or data. It is therefore + not bound to a particular object, but to a specific class (object type). + Qt virtual signals are useful because they allow you to create + connections without worrying about whether an object or a specific data + element has been destroyed. + https://www.youtube.com/watch?v=HTH3VFfqsXw + + */ + virtual void hwapi_reportDCDownloadStatus(QString const&) const {} + virtual void hwapi_reportDCDownloadSuccess(QString const&) const {} + virtual void hwapi_reportDCDownloadFailure(QString const&) const {} + virtual void hwapi_templatePrintFinished_OK(void) const=0; virtual void hwapi_templatePrintFinished_Err(void) const=0; @@ -2325,6 +2390,7 @@ signals: virtual void hwapi_doorCBinAndAllDoorsClosed(void) const=0; virtual void hwapi_doorAllDoorsClosed(void) const=0; + virtual void hwapi_coinAttached() const = 0; // NOTE: declaring a "pure virtual" "signal" should be an error and thus not valid. /* GH Version, bringt Fehler diff --git a/include/reporting_thread.h b/include/reporting_thread.h new file mode 100644 index 0000000..6e38364 --- /dev/null +++ b/include/reporting_thread.h @@ -0,0 +1,25 @@ +#ifndef REPORTING_THREAD_H_INCLUDED +#define REPORTING_THREAD_H_INCLUDED + +#include +#include + +class hwapi; +class ReportingThread : public QThread { + Q_OBJECT + +public: + ReportingThread(hwapi *hw); + ~ReportingThread(); + +protected: + // reporting thread does not have a running event queue, and therefore + // no slots. signals work the usual way. + void run() override; + +private: + hwapi *m_hw; + QString m_fileToDownload; +}; + +#endif // REPORTING_THREAD_H_INCLUDED diff --git a/include/runProc.h b/include/runProc.h index 0e7d724..4179216 100755 --- a/include/runProc.h +++ b/include/runProc.h @@ -15,6 +15,7 @@ #include #include "datIf.h" #include +#include #include "sendWRcmd.h" #include "controlBus.h" #include "storeINdata.h" @@ -35,6 +36,10 @@ class T_runProc : public QObject void restoreDeviceParameter(struct T_devices *deviceSettings); +#ifndef THIS_IS_CA_MASTER + std::atomic_bool m_coinAttached{false}; +#endif + private slots: void runProc_slotProcess(void); bool bl_performComplStart(void); @@ -80,6 +85,7 @@ signals: void runProc_doorCBinAndAllDoorsClosed(void); void runProc_doorAllDoorsClosed(void); + void runProc_coinAttached(); }; diff --git a/include/shared_mem_buffer.h b/include/shared_mem_buffer.h index 03095ba..6b0130e 100644 --- a/include/shared_mem_buffer.h +++ b/include/shared_mem_buffer.h @@ -300,9 +300,26 @@ struct SharedMem uint8_t p_nextFDcmdsInQueue; - - - + // download of device controller and json files + struct DCDownload { + enum class FILE_INDEX { + DC_BINARY = 0, DC2C_CASH = 1, DC2C_CONF = 2, DC2C_SERIAL=3, + DC2C_PRINT_01, DC2C_PRINT_02, DC2C_PRINT_03, DC2C_PRINT_04, + DC2C_PRINT_05, DC2C_PRINT_06, DC2C_PRINT_07, DC2C_PRINT_08, + DC2C_PRINT_09, DC2C_PRINT_10, DC2C_PRINT_11, DC2C_PRINT_12, + DC2C_PRINT_13, DC2C_PRINT_14, DC2C_PRINT_15, DC2C_PRINT_16, + DC2C_PRINT_17, DC2C_PRINT_18, DC2C_PRINT_19, DC2C_PRINT_20, + DC2C_PRINT_21, DC2C_PRINT_22, DC2C_PRINT_23, DC2C_PRINT_24, + DC2C_PRINT_25, DC2C_PRINT_26, DC2C_PRINT_27, DC2C_PRINT_28, + DC2C_PRINT_29, DC2C_PRINT_30, DC2C_PRINT_31, DC2C_PRINT_32 + }; + char m_filename[(int)FILE_INDEX::DC2C_PRINT_32][512]; + std::atomic_ushort m_totalBlocks; + std::atomic_ushort m_currentblockNumber; + std::atomic_bool m_requested{false}; + std::atomic_bool m_running{false}; + std::atomic_bool m_finished{false}; + } m_downLoadDC; static QSharedMemory *getShm(std::size_t s = 0); diff --git a/lib_ca_master/lib_ca_master.pro b/lib_ca_master/lib_ca_master.pro index 9c4eaf1..b29764c 100644 --- a/lib_ca_master/lib_ca_master.pro +++ b/lib_ca_master/lib_ca_master.pro @@ -1,19 +1,40 @@ TEMPLATE = lib TARGET = CAmaster -VERSION="1.0.0" +VERSION="1.0.1" HEADERS += \ ../include/com.h \ ../include/datIf.h \ - ../include/prot.h + ../include/prot.h \ + ../include/download_thread.h SOURCES += \ ../src/com.cpp \ ../src/datIf.cpp \ - ../src/prot.cpp + ../src/prot.cpp \ + ../src/download_thread.cpp include(../DCLibraries.pri) +win32 { + BUILD_DATE=$$system("date /t") + BUILD_TIME=$$system("time /t") + GIT_COMMIT="" + EXTENDED_VERSION="" + EXTENDED_VERSION_LIB="" +} else { + BUILD_DATE=$$system("date +%d-%m-%y") + BUILD_TIME=$$system("date +%H:%M:%S") + + GIT_COMMIT=$$system("git log -1 --format=oneline . | cut -d' ' -f1") + EXTENDED_VERSION_LIB="libCAmaster-$${VERSION}-$${GIT_COMMIT}" +} + +DEFINES+=APP_VERSION=\\\"$$VERSION\\\" +DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\" +DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\" +DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\" +DEFINES+=APP_EXTENDED_VERSION_LIB=\\\"$$EXTENDED_VERSION_LIB\\\" DEFINES+=THIS_IS_CA_MASTER DESTDIR=$${_PRO_FILE_PWD_}/../build diff --git a/lib_ca_slave/lib_ca_slave.pro b/lib_ca_slave/lib_ca_slave.pro index 0acb9e4..7430efa 100644 --- a/lib_ca_slave/lib_ca_slave.pro +++ b/lib_ca_slave/lib_ca_slave.pro @@ -1,9 +1,35 @@ TEMPLATE = lib TARGET = CAslave -VERSION="1.0.0" +VERSION="1.0.1" + +HEADERS += \ + ../include/reporting_thread.h + +SOURCES += \ + ../src/reporting_thread.cpp include(../DCLibraries.pri) +win32 { + BUILD_DATE=$$system("date /t") + BUILD_TIME=$$system("time /t") + GIT_COMMIT="" + EXTENDED_VERSION="" + EXTENDED_VERSION_LIB="" +} else { + BUILD_DATE=$$system("date +%d-%m-%y") + BUILD_TIME=$$system("date +%H:%M:%S") + + GIT_COMMIT=$$system("git log -1 --format=oneline . | cut -d' ' -f1") + EXTENDED_VERSION_LIB_="libCAslave-$${VERSION}-$${GIT_COMMIT}" +} + +DEFINES+=APP_VERSION=\\\"$$VERSION\\\" +DEFINES+=APP_BUILD_DATE=\\\"$$BUILD_DATE\\\" +DEFINES+=APP_BUILD_TIME=\\\"$$BUILD_TIME\\\" +DEFINES+=APP_EXTENDED_VERSION=\\\"$$EXTENDED_VERSION\\\" +DEFINES+=APP_EXTENDED_VERSION_LIB=\\\"$$EXTENDED_VERSION_LIB\\\" + DEFINES+=THIS_IS_CA_SLAVE DESTDIR=$${_PRO_FILE_PWD_}/../build diff --git a/src/download_thread.cpp b/src/download_thread.cpp new file mode 100644 index 0000000..a5f0e9e --- /dev/null +++ b/src/download_thread.cpp @@ -0,0 +1,320 @@ +#include "download_thread.h" +#include "shared_mem_buffer.h" +#include "hwapi.h" + +#include +#include + + +DownloadThread::DownloadThread(hwinf *hw) + : m_hw(hw) + , m_fileToDownload(m_hw->dcDownloadFileName()) { + // connect(this, &QThread::finished, + // dynamic_cast(m_hw), &QThread::deleteLater); +} + +DownloadThread::~DownloadThread() { +} + +/* + /////////////////////////////////////////////////////////////////////////////// + // + // 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 DownloadThread::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() << "DownloadThread::run(): DOWNLOAD THREAD STARTED"; + + // 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)); + + resetDeviceController(); + if (startBootloader()) { + + qCritical() << "DownloadThread::run(): TOTAL NUMBER OF BYTES TO SEND TO DC" << ba.size(); + qCritical() << "DownloadThread::run(): TOTAL NUMBER OF BLOCKS" << totalBlocks; + + int currentBlock = 0; + DownloadResult res = DownloadResult::OK; + qCritical() << "64-byte block " << currentBlock; + while (res != DownloadResult::ERROR && currentBlock < totalBlocks) { + if ((res = sendNextAddress(currentBlock)) != DownloadResult::ERROR) { + if ((res = sendNextDataBlock(ba, currentBlock)) != DownloadResult::ERROR) { + m_hw->dcDownloadSetCurrentBlockNumber(currentBlock); + currentBlock += 1; + } + } + } + + 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()); + } + stopBootloader(); // there is no harm in stopping the bootloader even + } // if it was not started at all + +#if 0 + // test code: + uint16_t const totalBlocks = 100; + m_hw->dcDownloadSetTotalBlockNumber(totalBlocks); + + for (uint16_t currentBlock = 0; currentBlock <= totalBlocks; ++currentBlock) { + m_hw->dcDownloadSetCurrentBlockNumber(currentBlock); + QThread::msleep(100); + } +#endif + + m_hw->dcDownloadSetRunning(false); + m_hw->dcDownloadSetFinished(true); + + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + "DOWNLOAD THREAD FINISHED"; + + // the object deletes itself ! This is the last line in run(). + // Never touch the object after this statement + // m_hw->dcDownloadThreadFinalize(this); +} + +DownloadThread::DownloadResult DownloadThread::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; +} + +DownloadThread::DownloadResult +DownloadThread::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) { + 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; +} + +DownloadThread::DownloadResult +DownloadThread::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; + + // QByteArray b((const char *)(&local[0]), 64); + // qCritical() << "SNDB" << bNum << b.size() << b.toHex(); + + 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 DownloadThread::startBootloader() const { + qDebug() << "starting bootloader..."; + int nTry = 5; + while (--nTry >= 0) { + m_hw->bl_startBL(); + QThread::msleep(5000); + 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() << "starting bootloader...FAILED"; + return false; +} + +bool DownloadThread::stopBootloader() const { + qDebug() << "stopping bootloader..."; + int nTry = 5; + while (--nTry >= 0) { + m_hw->bl_stopBL(); + QThread::msleep(500); + if (!m_hw->bl_isUp()) { + qInfo() << "stopping bootloader...OK"; + return true; + } + } + qCritical() << "stopping bootloader...FAILED"; + return false; +} + +bool DownloadThread::resetDeviceController() const { + qDebug() << "resetting device controller..."; + m_hw->bl_rebootDC(); + // wait maximally 3 seconds, before starting bootloader + QThread::sleep(1); + qInfo() << "resetting device controller...OK"; + return true; +} + +QByteArray DownloadThread::loadBinaryDCFile(QString filename) const { + qDebug() << "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/src/hwapi.cpp b/src/hwapi.cpp index 5c10889..f76fc33 100644 --- a/src/hwapi.cpp +++ b/src/hwapi.cpp @@ -20,6 +20,13 @@ */ #include "hwapi.h" +#include "download_thread.h" +#include "reporting_thread.h" + +#include +#include +#include + static uint32_t hwapi_lastStartAmount; static uint32_t hwapi_lastTotalAmount; @@ -33,6 +40,12 @@ static uint8_t bl_startupStep; hwapi::hwapi(QWidget *parent) : QObject(parent) { // constructor + qCritical() << " hwapi::hwapi() APP_VERSION:" << APP_VERSION; + qCritical() << " hwapi::hwapi() APP_BUILD_DATE:" << APP_BUILD_DATE; + qCritical() << " hwapi::hwapi() APP_BUILD_TIME:" << APP_BUILD_TIME; + qCritical() << " hwapi::hwapi() APP_EXTENDED_VERSION:" << APP_EXTENDED_VERSION; + qCritical() << "hwapi::hwapi() APP_EXTENDED_VERSION_LIB:" << APP_EXTENDED_VERSION_LIB; + // create or attach shared memory segment m_sharedMem = SharedMem::getShm(sizeof(SharedMem)); @@ -115,8 +128,7 @@ hwapi::hwapi(QWidget *parent) : QObject(parent) connect(runProcess, SIGNAL(runProc_doorCoinBoxInserted()), this, SLOT(sub_slotCoin14())); // hwapi_doorCoinBoxInserted())); connect(runProcess, SIGNAL(runProc_doorCBinAndAllDoorsClosed()), this, SLOT(sub_slotCoin15())); // hwapi_doorCBinAndAllDoorsClosed())); connect(runProcess, SIGNAL(runProc_doorAllDoorsClosed()), this, SLOT(sub_slotCoin16())); // hwapi_doorAllDoorsClosed())); - - + connect(runProcess, SIGNAL(runProc_coinAttached()), this, SLOT(coinAttached())); } void hwapi::hwapi_slotPayProc(void) @@ -214,6 +226,9 @@ void hwapi::sub_slotCoin16(void) emit hwapi_doorAllDoorsClosed(); } +void hwapi::coinAttached() { + emit hwapi_coinAttached(); +} /* @@ -3319,22 +3334,15 @@ void hwapi::sys_restoreDeviceParameter(struct T_devices *deviceSettings) const // attention: only applies if function "sys_sendDeviceParameter()" was used to send this settings before // cannot be used to see settings programmed by JsonFile uint8_t buf[64]; - uint8_t LL, nn; + uint8_t LL; tslib_strclr(buf,0,64); - uint8_t *start; //runProcess->epi_restore64BdevParameter(&LL, buf); // wozu die??? epi_restoreRbDeviceSettings(&LL, buf); // viel besser, stimmt immer - // Puffer in struct eintragen: - start = &deviceSettings->kindOfPrinter; - nn=0; - do - { - *start = buf[nn]; - start++; - } while(++nnm_downLoadDC.m_filename[(int)SharedMem::DCDownload::FILE_INDEX::DC_BINARY]; + size_t const size = sizeof(data->m_downLoadDC.m_filename); + + std::memset(fNameBuffer, 0x00, size); + std::memcpy(fNameBuffer, dcFileToDownload.toStdString().c_str(), + std::min(size, strlen(fNameBuffer)-1)); + + data->m_downLoadDC.m_totalBlocks = 0; + data->m_downLoadDC.m_currentblockNumber = 0; + + data->m_downLoadDC.m_requested = true; + data->m_downLoadDC.m_running = false; // download thread is not running + data->m_downLoadDC.m_finished = true; + + return true; +} + +bool hwapi::dcDownloadRequested() const { + SharedMem const *data = SharedMem::getData(); + // should be false at entry + return data ? data->m_downLoadDC.m_requested.load() : false; +} + +bool hwapi::dcDownloadResetRequest() const { + SharedMem *data = SharedMem::getData(); + if (data) { + data->m_downLoadDC.m_requested = false; + } + return true; +} + +bool hwapi::dcDownloadRequestAck() const { + SharedMem *data = SharedMem::getData(); + if (data) { + if (data->m_downLoadDC.m_requested) { + data->m_downLoadDC.m_requested = false; + data->m_downLoadDC.m_running = true; + data->m_downLoadDC.m_finished = false; + } + } + return false; +} + +bool hwapi::dcDownloadRunning() const { + SharedMem const *data = SharedMem::getDataConst(); + if (data) { + int cnt = 10; + while (--cnt > 0) { + bool running = data->m_downLoadDC.m_running.load(); + bool finished = data->m_downLoadDC.m_finished.load(); + if (!running || finished) { + if (cnt < 3) { + qCritical() << "DOWNLOAD THREAD NOT RUNNING" << running << finished; + } + QThread::msleep(500); + } else break; + } + // qCritical() << "DOWNLOAD RUNNING" << cnt << (cnt > 0); + return (cnt > 0); + } + return false; +} + +void hwapi::dcDownloadThreadFinalize(DownloadThread *dthread) { + delete dthread; + m_downloadThread = nullptr; +} + +bool hwapi::dcDownloadFinished() { + SharedMem const *data = SharedMem::getDataConst(); + if (data) { + int cnt = 10; + while ((--cnt > 0) && + ((data->m_downLoadDC.m_running.load() == true) && + (data->m_downLoadDC.m_finished.load() == false))) { + QThread::sleep(1); + } + + //if (cnt > 0) { + // delete m_downloadThread; + // m_downloadThread = nullptr; + // return true; + //} + } + return false; +} + +// download thread + +bool hwapi::dcDownloadThreadStart() { + m_downloadThread = new DownloadThread(this); + if (m_downloadThread) { + m_downloadThread->start(); + int cnt = 10; + while (--cnt > 0 && !dcDownloadThreadRunning()) { + QThread::msleep(200); + } + return (cnt > 0); + } + return false; +} + +bool hwapi::dcDownloadThreadRunning() const { + return (dcDownloadGetRunning() == true) + && (dcDownloadGetFinished() == false); +} + +bool hwapi::dcDownloadThreadFinished() const { + return (dcDownloadThreadRunning() == false); +} + + +// report thread +bool hwapi::dcDownloadReportThreadStart() { // only start reporting thread + int cnt = 10; // if download thread is running + while (--cnt > 0 && !dcDownloadRunning()) { + QThread::msleep(500); + } + if (cnt > 0) { + m_reportingThread = new ReportingThread(this); + if (m_reportingThread) { + m_reportingThread->start(); + cnt = 10; + while (--cnt > 0 && !dcDownloadReportThreadRunning()) { + QThread::msleep(200); + } + return (cnt > 0); + } + } + return false; +} + +bool hwapi::dcDownloadReportThreadRunning() const { + return m_reportingThread ? m_reportingThread->isRunning() : false; +} + +void hwapi::dcDownloadReportThreadFinalize() { + if (m_reportingThread) { + if (m_reportingThread->isFinished()) { + delete m_reportingThread; + m_reportingThread = nullptr; + } + } +} + +void hwapi::dcDownloadReportThreadQuit() { + if (m_reportingThread) { + m_reportingThread->quit(); + } +} + +bool hwapi::dcDownloadReportThreadFinished() const { + return m_reportingThread ? m_reportingThread->isFinished() : false; +} + +bool hwapi::dcDownloadReportStart() const { + int cnt = 10; + while (--cnt > 0 && !dcDownloadRunning()) { + QThread::msleep(200); + } + return (cnt == 0); +} + +bool hwapi::dcDownloadReportRunning() const { + return dcDownloadReportThreadRunning(); +} + +bool hwapi::dcDownloadReportFinished() { + int cnt = 10; + while (--cnt > 0 && !dcDownloadReportThreadFinished()) { + QThread::sleep(1); + } + + if (cnt == 0 && !dcDownloadReportThreadFinished()) { + return false; + } + + if (dcDownloadReportThreadFinished()) { + delete m_reportingThread; + m_reportingThread = nullptr; + } + + return true; +} + + +QString hwapi::dcDownloadFileName() const { + SharedMem const *data = SharedMem::getDataConst(); + return data ? data->m_downLoadDC.m_filename[(int)SharedMem::DCDownload::FILE_INDEX::DC_BINARY] : ""; +} + +bool hwapi::dcDownloadSetRequested(bool requested) { + SharedMem *data = SharedMem::getData(); + if (data) { + data->m_downLoadDC.m_requested = requested; + return true; + } + return false; +} + +bool hwapi::dcDownloadSetRunning(bool running) { + SharedMem *data = SharedMem::getData(); + if (data) { + data->m_downLoadDC.m_running = running; + return true; + } + return false; +} + +bool hwapi::dcDownloadSetFinished(bool finished) { + SharedMem *data = SharedMem::getData(); + if (data) { + data->m_downLoadDC.m_finished = finished; + return true; + } + return false; +} + +void hwapi::dcDownloadSetTotalBlockNumber(uint16_t totalBlockNumber) { + SharedMem::getData()->m_downLoadDC.m_totalBlocks = totalBlockNumber; +} + +void hwapi::dcDownloadSetCurrentBlockNumber(uint16_t currentBlockNumber) { + SharedMem::getData()->m_downLoadDC.m_currentblockNumber = currentBlockNumber; +} + +uint16_t hwapi::dcDownloadGetTotalBlockNumber() const { + SharedMem const *data = SharedMem::getDataConst(); + return data ? data->m_downLoadDC.m_totalBlocks.load() : 0; +} + +uint16_t hwapi::dcDownloadGetCurrentBlockNumber() const { + SharedMem const *data = SharedMem::getDataConst(); + return data ? data->m_downLoadDC.m_currentblockNumber.load() : 0; +} + +bool hwapi::dcDownloadGetRequested() const { + SharedMem const *data = SharedMem::getDataConst(); + return data ? data->m_downLoadDC.m_requested.load() : 0; +} + +bool hwapi::dcDownloadGetRunning() const { + SharedMem const *data = SharedMem::getDataConst(); + return data ? data->m_downLoadDC.m_running.load() : 0; +} + +bool hwapi::dcDownloadGetFinished() const { + SharedMem const *data = SharedMem::getDataConst(); + return data ? data->m_downLoadDC.m_running.load() : 0; +} diff --git a/src/main.cpp b/src/main.cpp index 9d98dca..073f587 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,11 +18,11 @@ int main(int argc, char *argv[]) #include // Must define the interpreter to be the dynamic linker -#ifdef __LP64__ -#error "__LP64__ not defined for PTU5" -#else -const char service_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux-armhf.so.3"; -#endif +//#ifdef __LP64__ +//#error "__LP64__ not defined for PTU5" +//#else +//const char service_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux-armhf.so.3"; +//#endif int main(int argc, char *argv[]) { diff --git a/src/reporting_thread.cpp b/src/reporting_thread.cpp new file mode 100644 index 0000000..e27e44a --- /dev/null +++ b/src/reporting_thread.cpp @@ -0,0 +1,155 @@ +#include "reporting_thread.h" +#include "shared_mem_buffer.h" +#include "hwapi.h" + +#include +#include + +ReportingThread::ReportingThread(hwapi *hw) + : m_hw(hw) + , m_fileToDownload(m_hw->dcDownloadFileName()) { +} + +ReportingThread::~ReportingThread() { +} + +// download thread running in ca-slave sends reports of download process to +// each component which has connects for the corresponding signals. +void ReportingThread::run() { + + qCritical() << QDateTime::currentDateTime() << "START DOWNLOAD THREAD"; + + static QString report(""); + + int cnt = 5; + while (!m_hw->dcDownloadGetRunning()) { + if (--cnt > 0) { + report = QString("%1 waiting for download to start %2") + .arg(QDateTime::currentDateTime().toString(Qt::ISODate)) + .arg(cnt); + qCritical() << __LINE__ << "STATUS" << report; + emit m_hw->hwapi_reportDCDownloadStatus(report); + QThread::sleep(1); + } else break; + } + +#if 0 + if (cnt == 0) { + m_hw->dcDownloadResetRequest(); + status = QString("%1 reset download request") + .arg(QDateTime::currentDateTime().toString(Qt::ISODate)); + qCritical() << __LINE__ << "STATUS" << status; + emit m_hw->hwapi_reportDCDownloadStatus(status); + + cnt = 5; + while (!m_hw->dcDownloadRunning()) { + if (--cnt > 0) { + QThread::sleep(1); + } else break; + } + + if (cnt == 0) { + status = QString("%1 download request failure") + .arg(QDateTime::currentDateTime().toString(Qt::ISODate)); + qCritical() << __LINE__ << "STATUS" << status; + emit m_hw->hwapi_reportDCDownloadFailure(status); + return; + } + } + + uint16_t const tnr = 1750; + uint16_t cnr = 0; + + while (cnr <= tnr) { + QThread::msleep(100); + QString report(""); + + if (cnr > 0) { + double percent = ((double)cnr / (double)tnr) * 100.0; + report = QString(": total blocks %1, current block %2 [%3]") + .arg(tnr).arg(cnr).arg(percent, 0, 'f', 2); + } else { + report = QString(": total blocks %1, current block %2 [0]") + .arg(tnr).arg(cnr); + } + status = QDateTime::currentDateTime().toString(Qt::ISODate) + report; + + qCritical() << "STATUS" << status; + + emit m_hw->hwapi_reportDCDownloadStatus(status); + cnr += 1; + } + + if (tnr == cnr) { + m_hw->hwapi_reportDCDownloadSuccess( + QString("SUCCESS DOWNLOADING") + m_fileToDownload); + } else { + m_hw->hwapi_reportDCDownloadFailure( + QString("ERROR DOWNLOADING %1 (total blocks=%2, sent blocks=%3)") + .arg(m_fileToDownload).arg(tnr).arg(cnr)); + } + +#endif + + uint16_t const totalBlocks = m_hw->dcDownloadGetTotalBlockNumber(); + + qCritical() << QDateTime::currentDateTime() << "TOTAL BLOCKS" << totalBlocks; + + if (totalBlocks) { + qint64 const start = QDateTime::currentMSecsSinceEpoch(); + double durationMillis = 0; + uint16_t currentBlockNumber = 0; + + while (m_hw->dcDownloadGetRunning()) { + currentBlockNumber = m_hw->dcDownloadGetCurrentBlockNumber(); + + durationMillis += QDateTime::currentMSecsSinceEpoch() - start; + + double const timeAveragePerBlock = (currentBlockNumber > 0) ? (durationMillis / currentBlockNumber) : durationMillis; + double const estimatedSecondsLeft = (timeAveragePerBlock * (totalBlocks - currentBlockNumber)) / 1000.0; + + double percent = ((double)currentBlockNumber / (double)totalBlocks) * 100.0; + report = QString(": total blocks %1, current block %2 [%3] (est. time left: %4s)") + .arg(totalBlocks) + .arg(currentBlockNumber) + .arg(percent, 0, 'f', 2) + .arg(estimatedSecondsLeft, 0, 'f', 2); + + qCritical() << "RT report" << report; + + emit m_hw->hwapi_reportDCDownloadStatus(report); + QThread::msleep(100); + } + + QThread::msleep(100); + + if (totalBlocks == currentBlockNumber) { + m_hw->hwapi_reportDCDownloadSuccess( + QString("SUCCESS DOWNLOADING") + m_fileToDownload); + } else { + m_hw->hwapi_reportDCDownloadFailure( + QString("ERROR DOWNLOADING %1 (total blocks=%2, sent blocks=%3)") + .arg(m_fileToDownload).arg(totalBlocks).arg(currentBlockNumber)); + } + } + + qCritical() << QDateTime::currentDateTime() << __PRETTY_FUNCTION__ + << QString("line=%1 REPORT THREAD ABOUT TO FINISH").arg(__LINE__); + + cnt = 10; + + bool running = m_hw->dcDownloadGetRunning(); + bool finished = m_hw->dcDownloadGetFinished(); + + while (--cnt > 0 && (running && !finished)) { + qCritical() << QDateTime::currentDateTime() << __PRETTY_FUNCTION__ + << QString("line=%1 REPORT THREAD: WAIT FOR END OF DOWNLOAD THREAD %2 %3 (%4)") + .arg(__LINE__).arg(running).arg(finished).arg(cnt); + QThread::sleep(1); + running = m_hw->dcDownloadGetRunning(); + finished = m_hw->dcDownloadGetFinished(); + } + + qCritical() << QDateTime::currentDateTime() << __PRETTY_FUNCTION__ + << QString("line=%1 FINISH REPORT THREAD").arg(__LINE__); +} diff --git a/src/runProc.cpp b/src/runProc.cpp index 137e7e5..a7783d8 100644 --- a/src/runProc.cpp +++ b/src/runProc.cpp @@ -48,6 +48,16 @@ T_runProc::T_runProc() void T_runProc::runProc_slotProcess(void) { #ifndef THIS_IS_CA_MASTER + bool const coinAttached = epi_getDI_CoinAttach(); + // exchange: (atomically) replaces the underlying value of m_coinAttached + // with the value of coinAttached, returns the value of m_coinAttached + // before the call + if (m_coinAttached.exchange(coinAttached) == false) { + if (coinAttached) { + // old value was false, and new value is true + emit runProc_coinAttached(); + } + } cash_paymentProcessing(); doors_supervise(); bl_performComplStart(); // neu 1.12.23 diff --git a/src/storeINdata.cpp b/src/storeINdata.cpp index c4a3b9d..9aa684b 100644 --- a/src/storeINdata.cpp +++ b/src/storeINdata.cpp @@ -12,6 +12,7 @@ + // store power on/off condition of the devices to control the data request //static bool indat_savePrnPwr;