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/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__); +}