LCOV - code coverage report
Current view: top level - backend - runner.cpp (source / functions) Hit Total Coverage
Test: "6f9243f" Lines: 414 608 68.1 %
Date: 2024-02-24 14:58:06 Functions: 65 84 77.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // ---------------------------------------------------------------------------
       2             : // USB EPROM/Flash Programmer
       3             : //
       4             : // Copyright (2023) Robson Martins
       5             : //
       6             : // This work is licensed under a Creative Commons Attribution-NonCommercial-
       7             : // ShareAlike 4.0 International License.
       8             : // ---------------------------------------------------------------------------
       9             : /**
      10             :  * @ingroup Software
      11             :  * @file backend/runner.cpp
      12             :  * @brief Implementation of the Runner Class.
      13             :  *
      14             :  * @author Robson Martins (https://www.robsonmartins.com)
      15             :  */
      16             : // ---------------------------------------------------------------------------
      17             : 
      18             : #include <QDateTime>
      19             : #include <QApplication>
      20             : #include <QLoggingCategory>
      21             : 
      22             : #include <chrono>
      23             : #include <thread>
      24             : #include <cstring>
      25             : 
      26             : #include "backend/runner.hpp"
      27             : #include "devices/device.hpp"
      28             : #include "config.hpp"
      29             : 
      30             : #ifdef Q_OS_WINDOWS
      31             : #include <windows.h>
      32             : #endif
      33             : 
      34             : // ---------------------------------------------------------------------------
      35             : // Logging
      36             : 
      37         452 : Q_LOGGING_CATEGORY(backendRunner, "backend.runner")
      38             : 
      39             : #define DEBUG qCDebug(backendRunner)
      40             : #define INFO qCInfo(backendRunner)
      41             : #define WARNING qCWarning(backendRunner)
      42             : #define CRITICAL qCCritical(backendRunner)
      43             : #define FATAL qCFatal(backendRunner)
      44             : 
      45             : // ---------------------------------------------------------------------------
      46             : 
      47             : #ifdef TEST_BUILD
      48             : bool QSerialPort::connected = false;
      49             : #endif
      50             : 
      51             : // ---------------------------------------------------------------------------
      52             : 
      53             : /* @brief Timeout (in milliseconds) to consider disconnect. */
      54             : constexpr int kDisconnectTimeOut = 5000;
      55             : /* @brief Timeout (in milliseconds) to read byte. */
      56             : constexpr int kReadTimeOut = 3000;
      57             : 
      58             : // ---------------------------------------------------------------------------
      59             : 
      60          64 : bool TRunnerCommand::responseIsOk() const {
      61          64 :     return OpCode::isOk(response.data(), response.size());
      62             : }
      63             : 
      64           7 : float TRunnerCommand::responseAsFloat() const {
      65           7 :     return OpCode::getValueAsFloat(response.data(), response.size());
      66             : }
      67             : 
      68           2 : int TRunnerCommand::responseAsByte() const {
      69           2 :     return OpCode::getValueAsByte(response.data(), response.size());
      70             : }
      71             : 
      72           2 : int TRunnerCommand::responseAsWord() const {
      73           2 :     return OpCode::getValueAsWord(response.data(), response.size());
      74             : }
      75             : 
      76           1 : int TRunnerCommand::responseAsDWord() const {
      77           1 :     return OpCode::getValueAsDWord(response.data(), response.size());
      78             : }
      79             : 
      80           1 : bool TRunnerCommand::responseAsBool() const {
      81           1 :     return OpCode::getValueAsBool(response.data(), response.size());
      82             : }
      83             : 
      84          37 : void TRunnerCommand::set(kCmdOpCodeEnum code) {
      85          37 :     opcode = OpCode::getOpCode(code);
      86          37 :     params.resize(1);
      87          37 :     params[0] = opcode.code;
      88          37 : }
      89             : 
      90           8 : void TRunnerCommand::setFloat(kCmdOpCodeEnum code, float param) {
      91           8 :     opcode = OpCode::getOpCode(code);
      92           8 :     params.resize(opcode.params + 1);
      93           8 :     params[0] = opcode.code;
      94           8 :     OpCode::setFloat(params.data(), params.size(), param);
      95           8 : }
      96             : 
      97           4 : void TRunnerCommand::setByte(kCmdOpCodeEnum code, uint8_t param) {
      98           4 :     opcode = OpCode::getOpCode(code);
      99           4 :     params.resize(opcode.params + 1);
     100           4 :     params[0] = opcode.code;
     101           4 :     OpCode::setByte(params.data(), params.size(), param);
     102           4 : }
     103             : 
     104           4 : void TRunnerCommand::setWord(kCmdOpCodeEnum code, uint16_t param) {
     105           4 :     opcode = OpCode::getOpCode(code);
     106           4 :     params.resize(opcode.params + 1);
     107           4 :     params[0] = opcode.code;
     108           4 :     OpCode::setWord(params.data(), params.size(), param);
     109           4 : }
     110             : 
     111           4 : void TRunnerCommand::setDWord(kCmdOpCodeEnum code, uint32_t param) {
     112           4 :     opcode = OpCode::getOpCode(code);
     113           4 :     params.resize(opcode.params + 1);
     114           4 :     params[0] = opcode.code;
     115           4 :     OpCode::setDWord(params.data(), params.size(), param);
     116           4 : }
     117             : 
     118          47 : void TRunnerCommand::setBool(kCmdOpCodeEnum code, bool param) {
     119          47 :     opcode = OpCode::getOpCode(code);
     120          47 :     params.resize(opcode.params + 1);
     121          47 :     params[0] = opcode.code;
     122          47 :     OpCode::setBool(params.data(), params.size(), param);
     123          47 : }
     124             : 
     125           0 : TRunnerCommand& TRunnerCommand::operator=(const TRunnerCommand& src) {
     126           0 :     this->opcode = src.opcode;
     127           0 :     this->params = src.params;
     128           0 :     this->response = src.response;
     129           0 :     return *this;
     130             : }
     131             : 
     132           1 : bool operator==(const TRunnerCommand& a, const TRunnerCommand& b) {
     133           2 :     return (a.opcode == b.opcode && a.params == b.params &&
     134           2 :             a.response == b.response);
     135             : }
     136             : 
     137             : // ---------------------------------------------------------------------------
     138             : 
     139           8 : Runner::Runner(QObject* parent)
     140             :     : QObject(parent),
     141           8 :       serial_(this),
     142           8 :       timeout_(kReadTimeOut),
     143           8 :       running_(false),
     144           8 :       error_(false),
     145           8 :       address_(0),
     146           8 :       bufferSize_(1) {
     147           8 :     flags_.is16bit = false;
     148           8 :     flags_.pgmCePin = false;
     149           8 :     flags_.pgmPositive = false;
     150           8 :     flags_.progWithVpp = false;
     151           8 :     flags_.skipFF = false;
     152           8 :     flags_.vppOePin = false;
     153           8 : }
     154             : 
     155           8 : Runner::~Runner() {
     156           8 :     close();
     157           8 : }
     158             : 
     159           1 : TSerialPortList Runner::list() const {
     160           1 :     TSerialPortList result;
     161           2 :     for (const auto item : QSerialPortInfo::availablePorts()) {
     162           2 :         if (item.vendorIdentifier() == kUsbVendorId &&
     163           1 :             item.productIdentifier() == kUsbProductId) {
     164             : #ifdef Q_OS_MACOS
     165             :             if (item.portName().startsWith("cu.usbmodem")) {
     166             :                 continue;
     167             :             }
     168             : #endif  // Q_OS_MACOS
     169             : #ifdef Q_OS_FREEBSD
     170             :             if (item.portName().startsWith("tty")) {
     171             :                 continue;
     172             :             }
     173             : #endif  // Q_OS_FREEBSD
     174           1 :             result.push_back(item);
     175             :         }
     176           2 :     }
     177           1 :     return result;
     178           0 : }
     179             : 
     180          11 : bool Runner::open(const QString& path) {
     181          11 :     if (running_) close();
     182          11 :     if (path.isNull() || path.isEmpty()) return false;
     183          20 :     DEBUG << "Opening serial port:" << path << "...";
     184          10 :     serial_.setPortName(path);
     185          10 :     bool result = serial_.open(QIODevice::ReadWrite);
     186          10 :     if (result) {
     187          10 :         running_ = true;
     188             :         // need under Windows
     189             :         // https://community.platformio.org/t/
     190             :         //   serial-communication-micro-usb-on-pi-pico-c/27512/5
     191          10 :         serial_.setDataTerminalReady(true);
     192          20 :         DEBUG << "Open serial port OK";
     193          20 :         DEBUG << "Setting timeout:" << QString("%1").arg(timeout_);
     194             :     } else {
     195           0 :         WARNING << "Error opening serial port";
     196             :     }
     197          10 :     error_ = !result;
     198          10 :     return result;
     199             : }
     200             : 
     201          18 : void Runner::close() {
     202          18 :     if (serial_.isOpen()) {
     203          20 :         DEBUG << "Closing serial port...";
     204             :     }
     205          18 :     serial_.close();
     206          18 :     running_ = false;
     207          18 :     error_ = false;
     208          18 : }
     209             : 
     210           3 : bool Runner::isOpen() const {
     211           3 :     return running_;
     212             : }
     213             : 
     214           1 : bool Runner::hasError() const {
     215           1 :     return error_;
     216             : }
     217             : 
     218           1 : QString Runner::getPath() const {
     219           1 :     return serial_.portName();
     220             : }
     221             : 
     222           2 : uint32_t Runner::getTimeOut() const {
     223           2 :     return timeout_;
     224             : }
     225             : 
     226           4 : void Runner::setTimeOut(uint32_t value) {
     227           4 :     if (timeout_ == value) return;
     228           3 :     timeout_ = value;
     229           6 :     DEBUG << "Setting timeout:" << QString("%1").arg(value);
     230             : }
     231             : 
     232           0 : uint8_t Runner::getBufferSize() const {
     233           0 :     return bufferSize_;
     234             : }
     235             : 
     236           0 : void Runner::setBufferSize(uint8_t value) {
     237           0 :     if (!value) value = 1;
     238             :     // rounds to next power of two
     239           0 :     if (value & (value - 1)) {
     240           0 :         uint32_t exp = 1;
     241             :         // clang-format off
     242           0 :         for (exp = 1; (1 << exp) < value; exp++) {}
     243             :         // clang-format on
     244           0 :         if (exp > 7) exp = 7;  // max 128
     245           0 :         value = 1 << exp;
     246             :     }
     247           0 :     if (flags_.is16bit && value == 1) value = 2;
     248           0 :     if (bufferSize_ == value) return;
     249           0 :     bufferSize_ = value;
     250           0 :     DEBUG << "Setting buffer size:" << QString("%1").arg(value);
     251             : }
     252             : 
     253           2 : bool Runner::nop() {
     254           2 :     TRunnerCommand cmd;
     255           2 :     cmd.set(kCmdNop);
     256           2 :     if (!sendCommand_(cmd)) return false;
     257           1 :     return true;
     258           2 : }
     259             : 
     260           7 : bool Runner::vddCtrl(bool on) {
     261           7 :     TRunnerCommand cmd;
     262           7 :     cmd.setBool(kCmdVddCtrl, on);
     263           7 :     if (!sendCommand_(cmd)) return false;
     264           5 :     return true;
     265           7 : }
     266             : 
     267           2 : bool Runner::vddSet(float value) {
     268           2 :     TRunnerCommand cmd;
     269           2 :     cmd.setFloat(kCmdVddSetV, value);
     270           2 :     if (!sendCommand_(cmd)) return false;
     271           1 :     return true;
     272           2 : }
     273             : 
     274           2 : float Runner::vddGet() {
     275           2 :     TRunnerCommand cmd;
     276           2 :     cmd.set(kCmdVddGetV);
     277           2 :     if (!sendCommand_(cmd)) return -1.0f;
     278           1 :     return cmd.responseAsFloat();
     279           2 : }
     280             : 
     281           2 : float Runner::vddGetDuty() {
     282           2 :     TRunnerCommand cmd;
     283           2 :     cmd.set(kCmdVddGetDuty);
     284           2 :     if (!sendCommand_(cmd)) return -1.0f;
     285           1 :     return cmd.responseAsFloat();
     286           2 : }
     287             : 
     288           3 : bool Runner::vddInitCal() {
     289           3 :     TRunnerCommand cmd;
     290           3 :     cmd.set(kCmdVddInitCal);
     291           3 :     if (!sendCommand_(cmd)) return false;
     292           1 :     return true;
     293           3 : }
     294             : 
     295           2 : bool Runner::vddSaveCal(float value) {
     296           2 :     TRunnerCommand cmd;
     297           2 :     cmd.setFloat(kCmdVddSaveCal, value);
     298             :     // no retry
     299           2 :     if (!sendCommand_(cmd, 0)) {
     300           2 :         DEBUG << "Error in vddSaveCal()"
     301           1 :               << "Trying use vddInitCal() and retrying.";
     302             :         // error, use vddInitCal
     303           1 :         if (!vddInitCal()) return false;
     304             :         // retry
     305           0 :         if (!sendCommand_(cmd, 0)) return false;
     306             :     }
     307           1 :     return true;
     308           2 : }
     309             : 
     310           2 : float Runner::vddGetCal() {
     311           2 :     TRunnerCommand cmd;
     312           2 :     cmd.set(kCmdVddGetCal);
     313           2 :     if (!sendCommand_(cmd)) return -1.0f;
     314           1 :     return cmd.responseAsFloat();
     315           2 : }
     316             : 
     317           4 : bool Runner::vppCtrl(bool on) {
     318           4 :     TRunnerCommand cmd;
     319           4 :     cmd.setBool(kCmdVppCtrl, on);
     320           4 :     if (!sendCommand_(cmd)) return false;
     321           3 :     return true;
     322           4 : }
     323             : 
     324           2 : bool Runner::vppSet(float value) {
     325           2 :     TRunnerCommand cmd;
     326           2 :     cmd.setFloat(kCmdVppSetV, value);
     327           2 :     if (!sendCommand_(cmd)) return false;
     328           1 :     return true;
     329           2 : }
     330             : 
     331           2 : float Runner::vppGet() {
     332           2 :     TRunnerCommand cmd;
     333           2 :     cmd.set(kCmdVppGetV);
     334           2 :     if (!sendCommand_(cmd)) return -1.0f;
     335           1 :     return cmd.responseAsFloat();
     336           2 : }
     337             : 
     338           2 : float Runner::vppGetDuty() {
     339           2 :     TRunnerCommand cmd;
     340           2 :     cmd.set(kCmdVppGetDuty);
     341           2 :     if (!sendCommand_(cmd)) return -1.0f;
     342           1 :     return cmd.responseAsFloat();
     343           2 : }
     344             : 
     345           3 : bool Runner::vppInitCal() {
     346           3 :     TRunnerCommand cmd;
     347           3 :     cmd.set(kCmdVppInitCal);
     348           3 :     if (!sendCommand_(cmd)) return false;
     349           1 :     return true;
     350           3 : }
     351             : 
     352           2 : bool Runner::vppSaveCal(float value) {
     353           2 :     TRunnerCommand cmd;
     354           2 :     cmd.setFloat(kCmdVppSaveCal, value);
     355             :     // no retry
     356           2 :     if (!sendCommand_(cmd, 0)) {
     357           2 :         DEBUG << "Error in vppSaveCal()"
     358           1 :               << "Trying use vppInitCal() and retrying.";
     359             :         // error, use vppInitCal
     360           1 :         if (!vppInitCal()) return false;
     361             :         // retry
     362           0 :         if (!sendCommand_(cmd, 0)) return false;
     363             :     }
     364           1 :     return true;
     365           2 : }
     366             : 
     367           2 : float Runner::vppGetCal() {
     368           2 :     TRunnerCommand cmd;
     369           2 :     cmd.set(kCmdVppGetCal);
     370           2 :     if (!sendCommand_(cmd)) return -1.0f;
     371           1 :     return cmd.responseAsFloat();
     372           2 : }
     373             : 
     374           4 : bool Runner::vddOnVpp(bool on) {
     375           4 :     TRunnerCommand cmd;
     376           4 :     cmd.setBool(kCmdVddOnVpp, on);
     377           4 :     if (!sendCommand_(cmd)) return false;
     378           3 :     return true;
     379           4 : }
     380             : 
     381           4 : bool Runner::vppOnA9(bool on) {
     382           4 :     TRunnerCommand cmd;
     383           4 :     cmd.setBool(kCmdVppOnA9, on);
     384           4 :     if (!sendCommand_(cmd)) return false;
     385           3 :     return true;
     386           4 : }
     387             : 
     388           4 : bool Runner::vppOnA18(bool on) {
     389           4 :     TRunnerCommand cmd;
     390           4 :     cmd.setBool(kCmdVppOnA18, on);
     391           4 :     if (!sendCommand_(cmd)) return false;
     392           3 :     return true;
     393           4 : }
     394             : 
     395           4 : bool Runner::vppOnCE(bool on) {
     396           4 :     TRunnerCommand cmd;
     397           4 :     cmd.setBool(kCmdVppOnCE, on);
     398           4 :     if (!sendCommand_(cmd)) return false;
     399           3 :     return true;
     400           4 : }
     401             : 
     402           4 : bool Runner::vppOnOE(bool on) {
     403           4 :     TRunnerCommand cmd;
     404           4 :     cmd.setBool(kCmdVppOnOE, on);
     405           4 :     if (!sendCommand_(cmd)) return false;
     406           3 :     return true;
     407           4 : }
     408             : 
     409           4 : bool Runner::vppOnWE(bool on) {
     410           4 :     TRunnerCommand cmd;
     411           4 :     cmd.setBool(kCmdVppOnWE, on);
     412           4 :     if (!sendCommand_(cmd)) return false;
     413           3 :     return true;
     414           4 : }
     415             : 
     416           4 : bool Runner::setCE(bool on) {
     417           4 :     TRunnerCommand cmd;
     418           4 :     cmd.setBool(kCmdBusCE, on);
     419           4 :     if (!sendCommand_(cmd)) return false;
     420           3 :     return true;
     421           4 : }
     422             : 
     423           4 : bool Runner::setOE(bool on) {
     424           4 :     TRunnerCommand cmd;
     425           4 :     cmd.setBool(kCmdBusOE, on);
     426           4 :     if (!sendCommand_(cmd)) return false;
     427           3 :     return true;
     428           4 : }
     429             : 
     430           4 : bool Runner::setWE(bool on) {
     431           4 :     TRunnerCommand cmd;
     432           4 :     cmd.setBool(kCmdBusWE, on);
     433           4 :     if (!sendCommand_(cmd)) return false;
     434           3 :     return true;
     435           4 : }
     436             : 
     437           5 : bool Runner::addrClr() {
     438           5 :     TRunnerCommand cmd;
     439           5 :     cmd.set(kCmdBusAddrClr);
     440           5 :     if (!sendCommand_(cmd)) return false;
     441           4 :     address_ = 0;
     442           4 :     return true;
     443           5 : }
     444             : 
     445           2 : bool Runner::addrInc() {
     446           2 :     TRunnerCommand cmd;
     447           2 :     cmd.set(kCmdBusAddrInc);
     448             :     // no retry
     449           2 :     if (!sendCommand_(cmd, 0)) {
     450           2 :         DEBUG << "Error in addrInc(). Last address:"
     451           2 :               << QString("0x%1").arg(address_, 6, 16, QChar('0'))
     452           1 :               << "Trying use addrSet(). New address:"
     453           1 :               << QString("0x%1").arg(address_ + 1, 6, 16, QChar('0'));
     454             :         // error, use addrSet
     455           1 :         return addrSet(address_ + 1);
     456             :     }
     457           1 :     address_++;
     458           1 :     return true;
     459           2 : }
     460             : 
     461           5 : bool Runner::addrSet(uint32_t value) {
     462           5 :     if (!value) return addrClr();
     463           4 :     TRunnerCommand cmd;
     464           4 :     cmd.setDWord(kCmdBusAddrSet, value);
     465           4 :     if (!sendCommand_(cmd)) return false;
     466           2 :     address_ = value;
     467           2 :     return true;
     468           4 : }
     469             : 
     470           3 : bool Runner::addrSetB(uint8_t value) {
     471           3 :     if (!value) return addrClr();
     472           2 :     TRunnerCommand cmd;
     473           2 :     cmd.setByte(kCmdBusAddrSetB, value);
     474           2 :     if (!sendCommand_(cmd)) return false;
     475           1 :     address_ = value;
     476           1 :     return true;
     477           2 : }
     478             : 
     479           3 : bool Runner::addrSetW(uint16_t value) {
     480           3 :     if (!value) return addrClr();
     481           2 :     TRunnerCommand cmd;
     482           2 :     cmd.setWord(kCmdBusAddrSetW, value);
     483           2 :     if (!sendCommand_(cmd)) return false;
     484           1 :     address_ = value;
     485           1 :     return true;
     486           2 : }
     487             : 
     488           1 : uint32_t Runner::addrGet() const {
     489           1 :     return address_;
     490             : }
     491             : 
     492           4 : bool Runner::dataClr() {
     493           4 :     TRunnerCommand cmd;
     494           4 :     cmd.set(kCmdBusDataClr);
     495           4 :     if (!sendCommand_(cmd)) return false;
     496           3 :     return true;
     497           4 : }
     498             : 
     499           3 : bool Runner::dataSet(uint8_t value) {
     500           3 :     if (!value) return dataClr();
     501           2 :     TRunnerCommand cmd;
     502           2 :     cmd.setByte(kCmdBusDataSet, value);
     503           2 :     if (!sendCommand_(cmd)) return false;
     504           1 :     return true;
     505           2 : }
     506             : 
     507           3 : bool Runner::dataSetW(uint16_t value) {
     508           3 :     if (!value) return dataClr();
     509           2 :     TRunnerCommand cmd;
     510           2 :     cmd.setWord(kCmdBusDataSetW, value);
     511           2 :     if (!sendCommand_(cmd)) return false;
     512           1 :     return true;
     513           2 : }
     514             : 
     515           2 : uint8_t Runner::dataGet() {
     516           2 :     TRunnerCommand cmd;
     517           2 :     cmd.set(kCmdBusDataGet);
     518           2 :     if (!sendCommand_(cmd)) return 0xFF;
     519           1 :     return cmd.responseAsByte();
     520           2 : }
     521             : 
     522           4 : uint16_t Runner::dataGetW() {
     523           4 :     TRunnerCommand cmd;
     524           4 :     cmd.set(kCmdBusDataGetW);
     525           4 :     if (!sendCommand_(cmd)) return 0xFFFF;
     526           1 :     return cmd.responseAsWord();
     527           4 : }
     528             : 
     529           0 : bool Runner::deviceConfigure(kCmdDeviceAlgorithmEnum algo,
     530             :                              const TDeviceFlags& flags) {
     531           0 :     TRunnerCommand cmd;
     532           0 :     uint16_t value = algo;
     533           0 :     value <<= 8;
     534           0 :     flags_ = flags;
     535           0 :     if (flags.is16bit && bufferSize_ == 1) setBufferSize(2);
     536             :     // clang-format off
     537           0 :     if (flags.skipFF     ) value |= 0x01;
     538           0 :     if (flags.progWithVpp) value |= 0x02;
     539           0 :     if (flags.vppOePin   ) value |= 0x04;
     540           0 :     if (flags.pgmCePin   ) value |= 0x08;
     541           0 :     if (flags.pgmPositive) value |= 0x10;
     542           0 :     if (flags.is16bit    ) value |= 0x20;
     543             :     // clang-format on
     544           0 :     cmd.setWord(kCmdDeviceConfigure, value);
     545           0 :     if (!sendCommand_(cmd)) return false;
     546           0 :     return true;
     547           0 : }
     548             : 
     549           0 : bool Runner::deviceSetTwp(uint32_t value) {
     550           0 :     TRunnerCommand cmd;
     551           0 :     cmd.setDWord(kCmdDeviceSetTwp, value);
     552           0 :     if (!sendCommand_(cmd)) return false;
     553             :     // adjust timeout
     554           0 :     uint32_t calculatedTime = (value * 2048) / 1000;  // 2KB
     555           0 :     if (calculatedTime > timeout_) {
     556           0 :         timeout_ = calculatedTime * 2;
     557             :     }
     558           0 :     return true;
     559           0 : }
     560             : 
     561           0 : bool Runner::deviceSetTwc(uint32_t value) {
     562           0 :     TRunnerCommand cmd;
     563           0 :     cmd.setDWord(kCmdDeviceSetTwc, value);
     564           0 :     if (!sendCommand_(cmd)) return false;
     565             :     // adjust timeout
     566           0 :     uint32_t calculatedTime = (value * 2048) / 1000;  // 2KB
     567           0 :     if (calculatedTime > timeout_) {
     568           0 :         timeout_ = calculatedTime * 2;
     569             :     }
     570           0 :     return true;
     571           0 : }
     572             : 
     573           0 : bool Runner::deviceSetupBus(kCmdDeviceOperationEnum operation) {
     574           0 :     TRunnerCommand cmd;
     575           0 :     cmd.setByte(kCmdDeviceSetupBus, operation);
     576           0 :     if (!sendCommand_(cmd)) return false;
     577           0 :     address_ = 0;
     578           0 :     return true;
     579           0 : }
     580             : 
     581           0 : bool Runner::deviceResetBus() {
     582           0 :     return deviceSetupBus(kCmdDeviceOperationReset);
     583             : }
     584             : 
     585           0 : QByteArray Runner::deviceRead() {
     586           0 :     QByteArray result;
     587           0 :     TRunnerCommand cmd;
     588           0 :     cmd.setByte(kCmdDeviceRead, bufferSize_);
     589             :     // setup expected response size
     590           0 :     cmd.opcode.result = bufferSize_;
     591             :     // no retry
     592           0 :     if (!sendCommand_(cmd, 0)) {
     593           0 :         DEBUG << "Error in deviceRead(). Last address:"
     594           0 :               << QString("0x%1").arg(address_, 6, 16, QChar('0'))
     595           0 :               << "Trying use addrSet()";
     596             :         // error
     597             :         // use addrSet
     598           0 :         if (!addrSet(address_)) return result;
     599             :         // call deviceRead already
     600           0 :         if (!sendCommand_(cmd, 0)) return result;
     601             :     }
     602             :     // set data
     603           0 :     result.resize(bufferSize_);
     604           0 :     memset(result.data(), 0xFF, bufferSize_);
     605           0 :     memcpy(result.data(), cmd.response.data() + 1,
     606           0 :            qMin(cmd.response.size() - 1, static_cast<int>(bufferSize_)));
     607           0 :     address_ += (flags_.is16bit ? (bufferSize_ / 2) : bufferSize_);
     608           0 :     return result;
     609           0 : }
     610             : 
     611           0 : bool Runner::deviceWrite(const QByteArray& data) {
     612           0 :     TRunnerCommand cmd;
     613           0 :     cmd.setByte(kCmdDeviceWrite, bufferSize_);
     614             :     // set data
     615           0 :     cmd.params.resize(bufferSize_ + 2);
     616           0 :     memset(cmd.params.data() + 2, 0xFF, bufferSize_);
     617           0 :     memcpy(cmd.params.data() + 2, data.data(),
     618           0 :            qMin(data.size(), static_cast<int>(bufferSize_)));
     619             :     // no retry
     620           0 :     if (!sendCommand_(cmd, 0)) {
     621           0 :         DEBUG << "Error in deviceWrite(). Last address:"
     622           0 :               << QString("0x%1").arg(address_, 6, 16, QChar('0'))
     623           0 :               << "Trying use addrSet()";
     624             :         // error
     625             :         // use addrSet
     626           0 :         if (!addrSet(address_)) return false;
     627             :         // call deviceWrite already
     628           0 :         if (!sendCommand_(cmd, 0)) return false;
     629             :     }
     630           0 :     address_ += (flags_.is16bit ? (bufferSize_ / 2) : bufferSize_);
     631           0 :     return true;
     632           0 : }
     633             : 
     634           0 : bool Runner::deviceWriteSector(const QByteArray& data, uint16_t sectorSize) {
     635           0 :     TRunnerCommand cmd;
     636           0 :     cmd.setWord(kCmdDeviceWriteSector, sectorSize);
     637             :     // set data
     638           0 :     cmd.params.resize(sectorSize + 3);
     639           0 :     memset(cmd.params.data() + 3, 0xFF, sectorSize);
     640           0 :     memcpy(cmd.params.data() + 3, data.data(),
     641           0 :            qMin(data.size(), static_cast<int>(sectorSize)));
     642             :     // no retry
     643           0 :     if (!sendCommand_(cmd, 0)) {
     644           0 :         DEBUG << "Error in deviceWriteSector(). Last address:"
     645           0 :               << QString("0x%1").arg(address_, 6, 16, QChar('0'))
     646           0 :               << "Trying use addrSet()";
     647             :         // error
     648             :         // use addrSet
     649           0 :         if (!addrSet(address_)) return false;
     650             :         // call deviceWriteSector already
     651           0 :         if (!sendCommand_(cmd, 0)) return false;
     652             :     }
     653           0 :     address_ += (flags_.is16bit ? (bufferSize_ / 2) : bufferSize_);
     654           0 :     return true;
     655           0 : }
     656             : 
     657           0 : bool Runner::deviceVerify(const QByteArray& data) {
     658           0 :     TRunnerCommand cmd;
     659           0 :     cmd.setByte(kCmdDeviceVerify, bufferSize_);
     660             :     // set data
     661           0 :     cmd.params.resize(bufferSize_ + 2);
     662           0 :     memset(cmd.params.data() + 2, 0xFF, bufferSize_);
     663           0 :     memcpy(cmd.params.data() + 2, data.data(),
     664           0 :            qMin(data.size(), static_cast<int>(bufferSize_)));
     665             :     // no retry
     666           0 :     if (!sendCommand_(cmd, 0)) {
     667           0 :         DEBUG << "Error in deviceVerify(). Last address:"
     668           0 :               << QString("0x%1").arg(address_, 6, 16, QChar('0'))
     669           0 :               << "Trying use addrSet()";
     670             :         // error
     671             :         // use addrSet
     672           0 :         if (!addrSet(address_)) return false;
     673             :         // call deviceVerify already
     674           0 :         if (!sendCommand_(cmd, 0)) return false;
     675             :     }
     676           0 :     address_ += (flags_.is16bit ? (bufferSize_ / 2) : bufferSize_);
     677           0 :     return true;
     678           0 : }
     679             : 
     680           0 : bool Runner::deviceBlankCheck() {
     681           0 :     TRunnerCommand cmd;
     682           0 :     cmd.setByte(kCmdDeviceBlankCheck, bufferSize_);
     683             :     // no retry
     684           0 :     if (!sendCommand_(cmd, 0)) {
     685           0 :         DEBUG << "Error in deviceBlankCheck(). Last address:"
     686           0 :               << QString("0x%1").arg(address_, 6, 16, QChar('0'))
     687           0 :               << "Trying use addrSet()";
     688             :         // error
     689             :         // use addrSet
     690           0 :         if (!addrSet(address_)) return false;
     691             :         // call deviceBlankCheck already
     692           0 :         if (!sendCommand_(cmd, 0)) return false;
     693             :     }
     694           0 :     address_ += (flags_.is16bit ? (bufferSize_ / 2) : bufferSize_);
     695           0 :     return true;
     696           0 : }
     697             : 
     698           0 : TDeviceID Runner::deviceGetId() {
     699           0 :     TDeviceID result;
     700           0 :     TRunnerCommand cmd;
     701           0 :     cmd.set(kCmdDeviceGetId);
     702           0 :     if (!sendCommand_(cmd)) return result;
     703           0 :     uint32_t rawCode = cmd.responseAsDWord();
     704           0 :     result.manufacturer = (rawCode & 0xFFFF0000) >> 16;
     705           0 :     result.device = (rawCode & 0xFFFF);
     706           0 :     return result;
     707           0 : }
     708             : 
     709           0 : bool Runner::deviceErase() {
     710           0 :     TRunnerCommand cmd;
     711           0 :     cmd.set(kCmdDeviceErase);
     712           0 :     if (!sendCommand_(cmd)) return false;
     713           0 :     return true;
     714           0 : }
     715             : 
     716           0 : bool Runner::deviceUnprotect() {
     717           0 :     TRunnerCommand cmd;
     718           0 :     cmd.set(kCmdDeviceUnprotect);
     719           0 :     if (!sendCommand_(cmd)) return false;
     720           0 :     return true;
     721           0 : }
     722             : 
     723           0 : bool Runner::deviceProtect() {
     724           0 :     TRunnerCommand cmd;
     725           0 :     cmd.set(kCmdDeviceProtect);
     726           0 :     if (!sendCommand_(cmd)) return false;
     727           0 :     return true;
     728           0 : }
     729             : 
     730           5 : void Runner::usDelay(uint64_t value) {
     731           6 :     if (!value) return;
     732           5 :     if (value >= 10000) {
     733           1 :         msDelay(value / 1000);
     734           1 :         return;
     735             :     }
     736           4 :     auto start = std::chrono::steady_clock::now();
     737           4 :     auto end = start;
     738           4 :     int64_t elapsed = 0;
     739             :     do {
     740          24 :         std::this_thread::sleep_for(std::chrono::microseconds(1));
     741          24 :         end = std::chrono::steady_clock::now();
     742             :         elapsed =
     743          24 :             std::chrono::duration_cast<std::chrono::microseconds>(end - start)
     744          24 :                 .count();
     745          24 :     } while (elapsed < value);
     746             : }
     747             : 
     748           6 : void Runner::msDelay(uint32_t value) {
     749           6 :     if (!value) return;
     750           6 :     auto start = std::chrono::steady_clock::now();
     751           6 :     auto end = start;
     752           6 :     int64_t elapsed = 0;
     753             :     do {
     754             : #ifdef Q_OS_WINDOWS
     755             :         Sleep(1);
     756             : #else
     757        3364 :         std::this_thread::sleep_for(std::chrono::milliseconds(1));
     758             : #endif
     759        3364 :         end = std::chrono::steady_clock::now();
     760             :         elapsed =
     761        3364 :             std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
     762        3364 :                 .count();
     763        3364 :         if (value > 50 && (elapsed % 50) == 0) processEvents();
     764        3364 :     } while (elapsed < value);
     765             : }
     766             : 
     767          68 : void Runner::processEvents() {
     768          68 :     QApplication::processEvents();
     769             : #ifdef Q_OS_WINDOWS
     770             :     Sleep(1);
     771             : #endif
     772          68 : }
     773             : 
     774         104 : bool Runner::sendCommand_(TRunnerCommand& cmd, int retry) {
     775         104 :     if (!serial_.isOpen()) {
     776          74 :         WARNING << "Serial port not open. Error running command"
     777          37 :                 << cmd.opcode.descr.c_str();
     778          37 :         error_ = true;
     779          37 :         return false;
     780             :     }
     781         134 :     DEBUG << "Running" << cmd.opcode.descr.c_str()
     782         134 :           << "[ 0x" + QString(cmd.params.toHex()) << "]"
     783          67 :           << "Current Address:"
     784          67 :           << QString("0x%1").arg(address_, 6, 16, QChar('0'));
     785          67 :     bool success = false;
     786          76 :     for (int i = 0; i < (retry + 1); i++) {
     787         137 :         if (!write_(cmd.params) ||
     788          64 :             !read_(&cmd.response, cmd.opcode.result + 1)) {
     789          18 :             DEBUG << "Retrying."
     790           9 :                   << "Command" << cmd.opcode.descr.c_str();
     791           9 :             continue;
     792           9 :         }
     793          64 :         error_ = false;
     794          64 :         success = true;
     795          64 :         break;
     796             :     }
     797          67 :     if (!success) {
     798           3 :         if (cmd.opcode.code != kCmdBusAddrInc) {
     799           6 :             WARNING << "Error writing to or reading from serial port."
     800           3 :                     << "Command" << cmd.opcode.descr.c_str();
     801             :         }
     802           3 :         error_ = true;
     803           3 :         return false;
     804             :     }
     805          64 :     if (!cmd.responseIsOk()) {
     806           0 :         WARNING << "Response NOK."
     807           0 :                 << "Command" << cmd.opcode.descr.c_str() << ". Response"
     808           0 :                 << "[ 0x" + QString(cmd.response.toHex()) << "]";
     809           0 :         error_ = true;
     810           0 :         return false;
     811             :     } else {
     812         128 :         DEBUG << "Response"
     813          64 :               << "[ 0x" + QString(cmd.response.toHex()) << "]";
     814             :     }
     815          64 :     return true;
     816             : }
     817             : 
     818          73 : bool Runner::write_(const QByteArray& data) {
     819          73 :     if (data.isEmpty()) return true;
     820          73 :     serial_.clear();
     821          73 :     return serial_.write(data) == data.size();
     822             : }
     823             : 
     824          64 : bool Runner::read_(QByteArray* data, uint32_t size) {
     825          64 :     if (data == nullptr || !size) return true;
     826          64 :     data->clear();
     827         128 :     while (data->size() < size) {
     828          64 :         auto start = std::chrono::steady_clock::now();
     829          64 :         auto end = start;
     830          64 :         int64_t elapsed = 0;
     831          64 :         bool hasData = false;
     832             :         do {
     833          64 :             if (serial_.waitForReadyRead(1)) {
     834          64 :                 hasData = true;
     835          64 :                 break;
     836             :             }
     837             : #ifdef Q_OS_WINDOWS
     838             :             Sleep(1);
     839             : #else
     840           0 :             std::this_thread::sleep_for(std::chrono::milliseconds(1));
     841             : #endif
     842           0 :             end = std::chrono::steady_clock::now();
     843           0 :             elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
     844           0 :                           end - start)
     845           0 :                           .count();
     846           0 :             if (timeout_ > 50 && (elapsed % 50) == 0) processEvents();
     847           0 :         } while (elapsed < timeout_);
     848          64 :         if (!hasData) {
     849           0 :             DEBUG << "Error reading serial port: timeout";
     850           0 :             checkAlive_();
     851           0 :             return false;
     852             :         }
     853          64 :         data->append(serial_.readAll());
     854          64 :         aliveTick_ = QDateTime::currentMSecsSinceEpoch();
     855             :     }
     856          64 :     if (data->size() > size) data->resize(size);
     857          64 :     if (data->size() != size) {
     858           0 :         DEBUG << "Error reading serial port: sizes are different";
     859           0 :         return false;
     860             :     }
     861          64 :     return true;
     862             : }
     863             : 
     864           0 : void Runner::checkAlive_() {
     865           0 :     if (!running_) return;
     866           0 :     if (QDateTime::currentMSecsSinceEpoch() - aliveTick_ > kDisconnectTimeOut) {
     867           0 :         WARNING << "Serial port: disconnected by timeout";
     868           0 :         running_ = false;
     869           0 :         if (serial_.isOpen()) serial_.close();
     870             :     }
     871             : }

Generated by: LCOV version 1.14