diff --git a/OnDemandUpdatePTU.pro b/OnDemandUpdatePTU.pro index f7f11e2..f58cdce 100644 --- a/OnDemandUpdatePTU.pro +++ b/OnDemandUpdatePTU.pro @@ -1,17 +1,30 @@ -# QT -= gui -QT += core -QT += widgets serialport -# QT += network +QT += core gui +QT += widgets serialport network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = ATBUpdateTool +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +VERSION=1.3.4 + INCLUDEPATH += plugins CONFIG += c++17 console # CONFIG -= app_bundle -# DEFINES+=LinuxDesktop +DEFINES+=APP_VERSION=\\\"$$VERSION\\\" +QMAKE_CXXFLAGS += -g QMAKE_CXXFLAGS += -Wno-deprecated-copy # custom target for 'git subtree' @@ -34,13 +47,15 @@ contains( CONFIG, PTU5 ) { greaterThan(QT_MAJOR_VERSION, 4): QT += serialport CONFIG += link_pkgconfig lessThan(QT_MAJOR_VERSION, 5): PKGCONFIG += qextserialport - QMAKE_CXXFLAGS += -std=c++11 # for GCC >= 4.7 + QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7 QMAKE_CXXFLAGS += -Wno-deprecated-copy ARCH = PTU5 DEFINES+=PTU5 } contains( CONFIG, PTU5_YOCTO ) { greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + QMAKE_CXXFLAGS += -std=c++17 # for GCC >= 4.7 + QMAKE_CXXFLAGS += -Wno-deprecated-copy PTU5BASEPATH = /opt/devel/ptu5 ARCH = PTU5 DEFINES+=PTU5 @@ -53,7 +68,7 @@ contains( CONFIG, DesktopLinux ) { lessThan(QT_MAJOR_VERSION, 5): CONFIG += extserialport # QMAKE_CC = ccache $$QMAKE_CC # QMAKE_CXX = ccache $$QMAKE_CXX - QMAKE_CXXFLAGS += -std=c++11 + QMAKE_CXXFLAGS += -std=c++17 QMAKE_CXXFLAGS += -Wno-deprecated-copy linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments } ARCH = DesktopLinux @@ -62,18 +77,36 @@ contains( CONFIG, DesktopLinux ) { SOURCES += \ main.cpp \ + progress_event.cpp \ + update_dc_event.cpp \ + mainwindow.cpp \ + utils.cpp \ update.cpp \ + git/git_client.cpp \ + ismas/ismas_client.cpp \ + process/command.cpp \ message_handler.cpp \ worker.cpp \ worker_thread.cpp HEADERS += \ update.h \ + progress_event.h \ + update_dc_event.h \ + utils.h \ + mainwindow.h \ + git/git_client.h \ + apism/ismas_data.h \ + ismas/ismas_client.h \ + process/command.h \ message_handler.h \ worker.h \ worker_thread.h \ plugins/interfaces.h +FORMS += \ + mainwindow.ui + OTHER_FILES += \ /opt/app/tools/atbupdate/update_log.csv \ main.cpp.bck \ @@ -84,3 +117,13 @@ OTHER_FILES += \ # git subtree add --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash # git subtree pull --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash # include(./DCPlugin/DCPlugin.pri) + + + +########################################################################################## +# for running program on target through QtCreator +contains( CONFIG, PTU5 ) { + qnx: target.path = /tmp/$${TARGET}/bin + else: unix:!android: target.path = /opt/app/tools/atbupdate/ + !isEmpty(target.path): INSTALLS += target +} diff --git a/allgemein.txt b/allgemein.txt new file mode 100644 index 0000000..6b2561e --- /dev/null +++ b/allgemein.txt @@ -0,0 +1,126 @@ +0: + Zunaechst wir immer nachgesehen, ob das Repository bereits geklont wurde. + Wenn nein, dann wird geklont. Im Master-Branch befinden sich Dateinen, die + in allen anderen Branches gemeinsam genutzt werden. Das sollte wohl auf + jeden Fall fuer den DeviceController gelten. +1: + Anfrage bei ISMAS ob neue Daten da sind. Hier faengt die ganze Sache an. + Da man nicht weiss, on ISMAS ueberhaupt antworten wird braucht es einen + Timer. Man kann die Sache dann auch gleich mehrfach versuchen. + + Die Anfrage geht also per Signal an den Apism-Client. Im Signal wird der + Zustand mitgegeben: + + ISMAS_UPDATE_REQUEST_PENDING + + und im Slot dann entsprechend uebernommen. Der Timer sollte nicht im Worker + stehen, sondern im Apism-Client. Der Worker sollte damit nicht belastet + sein. Der wird dann vom Apism-Client entsprechend benachrichtigt. + + ISMAS_UPDATE_REQUEST_FAILURE + ISMAS_UPDATE_REQUEST_TIMEOUT + ISMAS_UPDATE_REQUEST_SUCCESS + + Im 1./2.Fall wird dann ein Fehlerhandling durchgefuehrt. Insbesondere + wird Apism ueber den DB-Kanal darueber informiert (U0003). Am Ende wie + dann wie immer SendCmdVersion und beenden der Applikation. + + Im Erfolgsfall muss in der Antwort der Branch enthalten sein, fuer den das + Update durchgefuhrt werden soll. Das ist auch bereits so (Location). + + Ein Sonderfall waere der -m-Modus, da hier niemand auf der ISMAS-Seite + aktiv eingreift. Hier braucht es dann einen Ubergabeparameter an + ATBUpdateTool: --branch-name + + Normalfall ist aber die Aktivierung via ISMAS. Dann muss der in der Antwort + enthaltene Branch zunaechst ausgecheckt werden. +2: + Dazu wird ein entsprechendes Signal an den GitClient gesandet. + + GIT_CHECKOUT_BRANCH_REQUEST + + Der GitClient sieht jetzt erstmal nach, ob es diesen Branch ueberhaupt + gibt. + + Falls nein: + + GIT_CHECKOUT_BRANCH_REQUEST_FAILURE + BRANCH_NOT_EXIST + + Falls ja: + der GitClient versucht nun den Branch auszuchecken. + Geht das gut? + Falls nein: + + GIT_CHECKOUT_BRANCH_REQUEST_FAILURE + BRANCH_CHECKOUT_ERROR + + Falls ja: + GIT_CHECKOUT_BRANCH_REQUEST_SUCCESS + -> eventl. koennte man den Namen des Branches mitgeben + + + Signal an den Worker. Im entsprechenden Slot wird jetzt der eigentliche + UpdateProzess angeworfen. + 3: + Mittels git fetch werden jetzt die Aenderungen ermittelt. + + GIT_FETCH_UPDATES_REQUEST + + GIT_FETCH_UPDATES_REQUEST_FAILURE + -> Worker informieren damit Fehlerbehandlung aktiviert wird + GIT_FETCH_UPDATES_REQUEST_SUCCESS + + GIT_DIFF_UPDATES + + Die Liste der geaenderten Dateien geht an den Worker. Der kann jetzt + noch Test machen, ob das Sinn macht. Evtl. eine Statusmeldung ausgeben + drueber was gleich passieren wird. +4: + Mittels git merge/pull werden nun die Dateien tatsaechlich im Branch + aktualisiert. + + GIT_PULL_UPDATES_REQUEST + GIT_PULL_UPDATES_REQUEST_FAILURE + -> Worker informieren damit Fehlerbehandlung aktiviert wird + GIT_PULL_UPDATES_REQUEST_SUCCESS + -> Worker informieren + + Sind die Daten vorhanden, dann werden sie mittels rsync ins Dateisystem + kopiert. + + RSYNC_UPDATES + RSYNC_UPDATES_FAILURE + RSYNC_UPDATES_SUCCESS + + Fuer jede kopierte Datei geht eine Nachricht raus ans ISMAS. +5: + Sind die Daten dann kopiert, so werden sie auf den DC kopiert (falls + notwendig): hier gehen dann wieder Nachrichten an ISMAS raus: + + DC_UPDATE + DC_UPDATE_FAILURE + DC_UPDATE_SUCCESS + + JSON_UPDATE + JSON_UPDATE_FAILURE + JSON_UPDATE_SUCCESS + + Hier fuer jedes Json-File. + + Schliesslich noch eine Zusammenfasung an ISMAS: + + ISMAS_UPDATE_INFO_CONFIRM + ISMAS_UPDATE_INFO_CONFIRM_FAILURE + -> Worker informieren damit Fehlerbehandlung aktiviert wird + ISMAS_UPDATE_INFO_CONFIRM_SUCCESS + + und schliesslich der abschliessende Status an ISMAS zurueckgemeldet: + + ISMAS_CURRENT_PSA_STATUS_CONFIRM + ISMAS_CURRENT_PSA_STATUS_CONFIRM_FAILURE + ISMAS_CURRENT_PSA_STATUS_CONFIRM_SUCCESS + + + + diff --git a/apism/ismas_data.h b/apism/ismas_data.h new file mode 100644 index 0000000..b8597e8 --- /dev/null +++ b/apism/ismas_data.h @@ -0,0 +1,137 @@ +#ifndef ISMASDATA_H +#define ISMASDATA_H + +#include +#include + + + + +namespace ISMAS { + + struct TransferData : public QJsonObject { + struct : public QJsonObject { + QJsonValue tariffId; + QJsonValue group; + QJsonValue zone; + } device; + + struct : public QJsonObject { + QJsonValue state; + QJsonValue uid; + QJsonValue seq_tick_number; + QJsonValue timestamp; + QJsonValue userText; + QJsonValue userTextType; + } transaction; + + struct : public QJsonObject { + // TODO: check what is really used at the moment + QJsonValue id; // unique article id + QJsonValue name; // name + QJsonValue price; // price in cent + QJsonValue currency; // + QJsonValue startTime; // start time + QJsonValue endTime; // end time + QJsonValue userText; // additional info + QJsonValue parkingTime; + QJsonValue printText; + // QJsonValue discount; + } item; + + struct : public QJsonObject { + struct : public QJsonObject { + QJsonValue coins; // total amount of coins value + // QJsonValue notes; // total amount of notes value + QJsonValue overpaid; // in cent + QJsonValue currency; + QJsonValue change; + } cash; + + struct : public QJsonObject { + QJsonValue cardNumber; + QJsonValue value; // buchungsbetrag + QJsonValue cardType; + QJsonValue currency; + QJsonValue tid; + QJsonValue tresult; + } card; + + struct : public QJsonObject { + QJsonValue cardNumber; + QJsonValue cardType; + QJsonValue value; + QJsonValue valueOld; + QJsonValue valueNew; + QJsonValue time; + QJsonValue timeOld; + QJsonValue timeNew; + } prePaidCard; + } payment; + + struct : public QJsonObject { + QJsonValue delivery; // PRINT, OnlineTicket + QJsonValue result; // SUCCESS, ERROR + QJsonValue errorCode; // 0=OK, 1=... + QJsonValue errorMsg; + } result; + }; + + + struct AccountData : public QJsonObject { + struct : public QJsonObject { + QJsonValue UID; + QJsonValue ChangeNumber; + QJsonValue Process; // Vorgang + QJsonValue startDateTime; + QJsonValue endDateTime; + QJsonValue startHash; + QJsonValue endHash; + + struct : public QJsonObject { + QJsonValue value; // coin value + QJsonValue numberOfCoins; // number of coins + QJsonValue currency; + } coin; + + } coinBox; // Münzkasse + }; + + + struct EventData : public QJsonObject { + struct : public QJsonObject { + QJsonValue eventID; + QJsonValue deviceName; + QJsonValue reason; + QJsonValue event; + QJsonValue eventState; + QJsonValue timeStamp; + QJsonValue parameter; + QJsonValue secondLevelInfo; + } machineEvent; // + }; + + struct StateData : public QJsonObject { + QJsonValue Timestamp; + QJsonArray HW_States; + struct : public QJsonObject { + QJsonValue name; + QJsonValue value; + QJsonValue unit; + } machineState; // + }; + + + enum class REQUEST : quint8 { + NO_REQUEST, + START, + STOP, + PING, + SELF, + ISMAS_PARAMETER + }; +} + + + +#endif // ISMASDATA_H diff --git a/git/git_client.cpp b/git/git_client.cpp new file mode 100644 index 0000000..88dbe1a --- /dev/null +++ b/git/git_client.cpp @@ -0,0 +1,376 @@ +#include "git_client.h" +#include "update.h" +#include "worker.h" +#include "utils.h" + +#include +#include +#include + + +GitClient::GitClient(QString const &customerNrStr, + QString const &customerRepository, + QString const &workingDirectory, + QString const &branchName, + QObject *parent) + : QObject(parent) + , m_worker(qobject_cast(parent)) + , m_repositoryPath(QString("https://git.mimbach49.de/GerhardHoffmann/%1.git").arg(customerNrStr)) + , m_customerNr(customerNrStr) + , m_workingDirectory(workingDirectory) + , m_branchName(branchName) + , m_customerRepository(customerRepository) { + if (!m_worker) { + qCritical() << "ERROR CASTING PARENT TO WORKER FAILED"; + } +} + +bool GitClient::gitCloneCustomerRepository() { + QString gitCommand("git clone "); + gitCommand += m_repositoryPath; + Command c(gitCommand); + + qInfo() << "IN CURRENT WD" << m_workingDirectory + << "CLONE" << m_repositoryPath << "..."; + + if (c.execute(m_workingDirectory)) { // execute the command in wd + QString const result = c.getCommandResult(); + if (!result.isEmpty()) { + // Cloning into 'customer_281'...\n + static QRegularExpression re("(^\\s*Cloning\\s+into\\s+[']\\s*)(.*)(\\s*['].*$)"); + QRegularExpressionMatch match = re.match(result); + if (match.hasMatch()) { + if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches + if (match.captured(2).trimmed() == m_customerNr) { + qInfo() << "CLONING" << m_repositoryPath << "OK"; + return true; + } + } + } + } + Utils::printCriticalErrorMsg(QString("ERROR CLONE RESULT HAS WRONG FORMAT. CLONE_RESULT=") + result); + } + return false; +} + +bool GitClient::copyGitConfigFromMaster() { // only allowed when called in + // master branch (???) + if (QDir(m_customerRepository).exists()) { + QString const cp = QString("cp .gitconfig .git/config"); + Command c("bash"); + if (c.execute(m_customerRepository, QStringList() << "-c" << cp)) { + qInfo() << "cp .gitconfig .git/config OK"; + return true; + } + qCritical() << "ERROR cp .gitconfig .git/config"; + } + return false; +} + +QStringList GitClient::gitBranchNames() { + // git config --global pager.branch false + QStringList bNames; + if (QDir(m_customerRepository).exists()) { + QString gitCommand("git branch -a"); + Command c(gitCommand); + if (c.execute(m_customerRepository)) { + QString const result = c.getCommandResult(); + return result.split('\n'); + } + } + return bNames; +} + +bool GitClient::gitCheckoutBranch() { + // TODO: nachsehen, ob der Branch ueberhaupt existiert + + if (QDir(m_customerRepository).exists()) { + int zoneNr = Utils::read1stLineOfFile("/mnt/system_data/zone_nr"); + m_branchName = (zoneNr != 0) + ? QString("zg1/zone%1").arg(zoneNr) : "master"; + + QString gitCommand("git checkout "); + gitCommand += m_branchName; + + Command c(gitCommand); + return c.execute(m_customerRepository); // execute command in customerRepo + } + Utils::printCriticalErrorMsg(QString("ERROR ") + m_customerRepository + " DOES NOT EXIST"); + return false; +} + +bool GitClient::gitCloneAndCheckoutBranch() { + qInfo() << "CLONE" << m_repositoryPath << "AND CHECKOUT" << m_branchName; + if (gitCloneCustomerRepository()) { + //if (copyGitConfigFromMaster()) { + if (gitCheckoutBranch()) { + return true; + } else { + // TODO + } + //} + } + Utils::printCriticalErrorMsg(QString("CLONE ") + m_repositoryPath + " AND CHECKOUT FAILED"); + return false; +} + +QStringList GitClient::gitShowReason() { + QStringList lst; + if (QDir(m_customerRepository).exists()) { + // %h: commit (short form) + // %s: commit message + // %cI: commit date, strict ISO 8601 format + // Note: use master branch. By convention, there is a ChangeLog file + // in the root of the repository, which has to be always the last file + // to be checked in when the customer repository somehow changed. + Command c("git show origin/master -s --format=\"c=%h m=%s d=%cI\""); + if (c.execute(m_customerRepository)) { + QString const s = c.getCommandResult().trimmed(); + int const c = s.indexOf("c="); + int const m = s.indexOf("m="); + int const d = s.indexOf("d="); + + QString msg = IsmasClient::getReasonForLastSendVersion(); + QString commit{""}, date{""}; + + if (c != -1) { + int start = c + 2; + if (m >= start) { + int length = m - start; + commit = s.mid(start, length).trimmed(); + + start = m + 2; + if (d >= start) { + length = d - start; + msg += " ("; + msg = s.mid(start, length).trimmed(); + msg += ")"; + + start = d + 2; + date = s.mid(start); + } + } + + if (!commit.isEmpty() && !msg.isEmpty() && !date.isEmpty()) { + lst << commit << msg << date; + } + } + } + } else { + qCritical() << "CUSTOMER_REPOSITORY" << m_customerRepository + << "DOES NOT EXIST"; + } + return lst; +} + +/* + Zu beachten: wird eine datei neu hinzugefuegt (git add/commit) dann aber gleich + wieder geloscht, so wird sie im diff nicht angezeigt. + */ +std::optional GitClient::gitDiff(QString const &commits) { + if (QDir(m_customerRepository).exists()) { + // 409f198..6c22726 + QString gitCommand("git diff --compact-summary "); + gitCommand += commits; + + Command c(gitCommand); + if (c.execute(m_customerRepository)) { // execute command in local customerRepo + QString s = c.getCommandResult().trimmed(); + Utils::printInfoMsg("GIT DIFF RESULT " + s); + + QStringList lines = Update::split(s, '\n'); + QStringList fileNames; + // each line has the format "etc/psa_config/DC2C_print01.json | 1 + + // or the format "etc/psa_config/DC2C_print01.json (new) | 1 + + // the filenames are relativ to the repository + for (int i = 0; i < lines.size(); ++i) { + QString const &line = lines.at(i); + int newIndex = line.indexOf("(new"); // for new files + int goneIndex = line.indexOf("(gone"); // for removed files + int modeIndex = line.indexOf("(mode"); + int pipeIndex = line.indexOf('|'); + if (newIndex != -1) { + QString file = line.left(newIndex).trimmed(); + qInfo() << "FILE (NEW)" << file; + fileNames << file; + } else + if (modeIndex != -1) { + QString const file = line.left(modeIndex).trimmed(); + qInfo() << "FILE (MODE)" << file; + fileNames << file; + } else + if (goneIndex != -1) { + QString const file = line.left(goneIndex).trimmed(); + qCritical() << "FILE (GONE)" << file; + } else + if (pipeIndex != -1) { + QString const file = line.left(pipeIndex).trimmed(); + qInfo() << "FILE (PIPE)" << file; + fileNames << file; + } + } + if (!fileNames.isEmpty()) { + return fileNames; + } + } + } + return std::nullopt; +} + +/* + Hat sich nichts geaendert, so werden auch keine Commits <>..<> angezeigt + */ +std::optional GitClient::gitFetch() { + if (QDir(m_customerRepository).exists()) { + qInfo() << "BRANCH NAME" << m_branchName; + + Command c("git fetch"); + if (c.execute(m_customerRepository)) { + QString const s = c.getCommandResult().trimmed(); + if (!s.isEmpty()) { + QStringList lines = Update::split(s, '\n'); + if (!lines.empty()) { + int zoneNr = Utils::read1stLineOfFile("/mnt/system_data/zone_nr"); + m_branchName = (zoneNr != 0) ? QString("zg1/zone%1").arg(zoneNr) : "master"; + // lines can look like this: + // From https://git.mimbach49.de/GerhardHoffmann/customer_281 + // 41ec581..5d25ac3 master -> origin/master + // ff10f57..43530a1 zg1/zone1 -> origin/zg1/zone1 + // 6ed893f..5d9882c zg1/zone2 -> origin/zg1/zone2 + // 4384d17..77045d8 zg1/zone3 -> origin/zg1/zone3 + // 89d2812..36a0d74 zg1/zone5 -> origin/zg1/zone5 + bool found = false; + for (int i=0; i < lines.size(); ++i) { + if (lines.at(i).contains(m_branchName)) { + found = true; + // 409f198..6c22726 zg1/zone1 -> origin/zg1/zone1 + static QRegularExpression re("(^\\s*)([0-9A-Fa-f]+..[0-9A-Fa-f]+)(.*$)"); + QRegularExpressionMatch match = re.match(lines.at(i)); + if (match.hasMatch()) { + if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches + return match.captured(2); + } else { + emit m_worker->showErrorMessage("git fetch", + QString("(wrong cap-count (%1)").arg(re.captureCount())); + } + } else { + emit m_worker->showErrorMessage("git fetch", + "no regex-match for commits"); + Utils::printCriticalErrorMsg("NO REGEX MATCH FOR COMMITS"); + } + } + } + if (!found) { + emit m_worker->showErrorMessage("git fetch", + QString("unkown branch name ") + m_branchName); + Utils::printCriticalErrorMsg("UNKNOWN BRANCH NAME " + m_branchName); + } + } else { + emit m_worker->showErrorMessage("git fetch", + QString("wrong format for result of 'git fetch' ") + s); + Utils::printCriticalErrorMsg(QString("WRONG FORMAT FOR RESULT OF 'GIT FETCH' ") + s); + } + } else { + emit m_worker->showErrorMessage("git fetch", "empty result for 'git fetch'"); + Utils::printCriticalErrorMsg("EMPTY RESULT FOR 'GIT FETCH'"); + } + } + } else { + emit m_worker->showErrorMessage("git fetch", QString("repository ") + m_customerRepository + " does not exist"); + Utils::printCriticalErrorMsg(QString("REPOSITORY ") + m_customerRepository + " DOES NOT EXIST"); + } + return std::nullopt; +} + +bool GitClient::gitFetchAndDiff() { + if (gitFetch()) { + QString gitCommand("git diff --compact-summary HEAD..FETCH_HEAD"); + Command c(gitCommand); + return c.execute(m_workingDirectory); + } + return false; +} + +bool GitClient::gitPull() { + if (QDir(m_customerRepository).exists()) { + Command c("git pull"); + if (c.execute(m_customerRepository)) { + qInfo() << "PULLED INTO" << m_customerRepository; + return true; + } + Utils::printCriticalErrorMsg(QString("PULL INTO " + m_customerRepository + " FAILED")); + } + return false; +} + +std::optional GitClient::gitMerge() { + Command c("git merge"); + if (c.execute(m_workingDirectory)) { + QString s = c.getCommandResult(); + QStringList lst = Update::split(s, '\n'); + return lst; + } + return std::nullopt; +} + +QString GitClient::gitLastCommit(QString fileName) { + if (QDir(m_customerRepository).exists()) { + QString const filePath + = QDir::cleanPath(m_customerRepository + QDir::separator() + fileName); + if (QFile(filePath).exists()) { + QString const gitCommand = QString("git log %1 | head -n 1").arg(fileName); + Command c("bash"); + if (c.execute(m_customerRepository, QStringList() << "-c" << gitCommand)) { + QString const r = c.getCommandResult(); + int const idx = r.indexOf("commit "); + if (idx != -1) { + return r.mid(idx + 8).trimmed(); + } + } + } + } + return ""; +} + +// fileName has to an absolute path +QString GitClient::gitBlob(QString fileName) { + QFileInfo fi(fileName); + if (fi.exists()) { + QString const gitCommand = QString("git hash-object %1").arg(fileName); + Command c(gitCommand); + if (c.execute("/tmp")) { + return c.getCommandResult().trimmed(); + } + } + return "N/A"; +} + +QString GitClient::gitCommitForBlob(QString blob) { + if (QDir(m_customerRepository).exists()) { + QString const gitCommand + = QString("git whatchanged --all --find-object=%1 | head -n 1").arg(blob); + Command c(gitCommand); + if (c.execute(m_customerRepository)) { + return c.getCommandResult(); + } + } + return ""; +} + +bool GitClient::gitIsFileTracked(QString fName) { + if (QDir(m_customerRepository).exists()) { + QString const gitCommand + = QString("git ls-files --error-unmatch %1").arg(fName); + Command c(gitCommand); + return c.execute(m_customerRepository); + } + return false; +} + + +//get_commit_for_blob () { +// # search for the blob in all commits for the file(name) $1 +// echo $(git log --all --pretty=format:%H -- $2 | +// xargs -I{} bash -c "git ls-tree {} -- $2 | +// grep -q $1 && echo -n {} && head -n 1") +//} diff --git a/git/git_client.h b/git/git_client.h new file mode 100644 index 0000000..63d83e4 --- /dev/null +++ b/git/git_client.h @@ -0,0 +1,59 @@ +#ifndef GIT_CLIENT_H_INCLUDED +#define GIT_CLIENT_H_INCLUDED + +#include +#include +#include + +#include "process/command.h" +#include "ismas/ismas_client.h" + +class Worker; +class GitClient : public QObject { + Q_OBJECT + + Worker *m_worker; + QString const m_repositoryPath; + QString const m_customerNr; + QString const m_workingDirectory; + QString m_branchName; + QString const m_customerRepository; + + bool copyGitConfigFromMaster(); + + public: + explicit GitClient(QString const &customerNrStr, + QString const &repositoryPath, + QString const &workingDirectory = QCoreApplication::applicationDirPath(), + QString const &branchName = "master", + QObject *parent = 0); + + bool gitCloneCustomerRepository(); + bool gitCheckoutBranch(); + QStringList gitBranchNames(); + + QString const workingDirectory() const { return m_workingDirectory; } + QString workingDirectory() { return m_workingDirectory; } + + QString const branchName() const { return m_branchName; } + QString branchName() { return m_branchName; } + + QString repositoryPath() { return m_repositoryPath; } + QString const repositoryPath() const { return m_repositoryPath; } + + bool gitCloneAndCheckoutBranch(); + + std::optional gitFetch(); + bool gitFetchAndDiff(); + bool gitPull(); + std::optional gitDiff(QString const &commit); + std::optional gitMerge(); + + QString gitLastCommit(QString fileName); + QStringList gitShowReason(); + static QString gitBlob(QString fileName); + QString gitCommitForBlob(QString blob); + bool gitIsFileTracked(QString file2name); +}; + +#endif // GIT_CLIENT_H_INCLUDED diff --git a/ismas/ismas_client.cpp b/ismas/ismas_client.cpp new file mode 100644 index 0000000..0e5ba65 --- /dev/null +++ b/ismas/ismas_client.cpp @@ -0,0 +1,904 @@ +#include "ismas/ismas_client.h" +#include "utils.h" + +#include +#include + +#include +#include // inet_addr() +#include +#include +#include +#include +#include +#include +#include // bzero() +#include +#include // read(), write(), close() +#include + + +#include +#include +#include + +#if 0 +# $1: EVENT: U0001 update finished: 100% +# U0002 reset TRG +# U0003 error +# U0010 for update process +# $2: PERCENT : "only for ISMAS: 0-100%", +# $3: RESULTCODE : "only for ISMAS", +# 0: Success +# 1: no Update nessesary +# 2: Backup failed +# 3: Package error/ Wrong package +# 4: Install Error +# $4: STEP : "running step (only for us): update_psa...", +# $5: STEP_RESULT : "error and result text", +# $6: VERSION : "opkg and conf info; what will be updated" +# +#endif + +#include +#include + +void IsmasClient::printDebugMessage(int port, + QString const &clientIP, + int clientPort, + QString const &message) { + qDebug().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "hostname ........" << "127.0.0.1" << "\n" + << "port ............" << port << "\n" + << "local address ..." << clientIP << "\n" + << "local port ......" << clientPort << "\n" + << message; +} + +void IsmasClient::printInfoMessage(int port, + QString const &clientIP, + int clientPort, + QString const &message) { + qInfo().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "hostname ........" << "127.0.0.1" << "\n" + << "port ............" << port << "\n" + << "local address ..." << clientIP << "\n" + << "local port ......" << clientPort << "\n" + << message; +} + +void IsmasClient::printErrorMessage(int port, + QString const &clientIP, + int clientPort, + QString const &message) { + qCritical().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "hostname ........" << "127.0.0.1" << "\n" + << "port ............" << port << "\n" + << "local address ..." << clientIP << "\n" + << "local port ......" << clientPort << "\n" + << message; +} + +std::optional +IsmasClient::sendRequestReceiveResponse(int port, QString const &request) { + + qInfo() << "REQUEST" << request; + + int sockfd; + int r; + errno = 0; + // socket create and verification + if ((sockfd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) { + qCritical().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "SOCKET CREATION FAILED (" << strerror(errno) << ")"; + return std::nullopt; + } + + struct sockaddr_in servAddr; + bzero(&servAddr, sizeof(servAddr)); + // assign IP, PORT + servAddr.sin_family = AF_INET; + servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + servAddr.sin_port = htons(port); + // connect the client socket to server socket + if ((r = ::connect(sockfd, (struct sockaddr *)(&servAddr), sizeof(servAddr))) != 0) { + qCritical().noquote() + << "\n" + << "SEND-REQUEST-RECEIVE-RESPONSE ..." << "\n" + << "CONNECTION WITH SERVER FAILED (" << strerror(r) << ")"; + ::close(sockfd); + return std::nullopt; + } + + struct sockaddr_in clientAddr; + bzero(&clientAddr, sizeof(clientAddr)); + socklen_t sockLen = sizeof(clientAddr); + + char clientIP[16]; + bzero(&clientIP, sizeof(clientIP)); + getsockname(sockfd, (struct sockaddr *)(&clientAddr), &sockLen); + inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, sizeof(clientIP)); + unsigned int clientPort = ntohs(clientAddr.sin_port); + + printDebugMessage(port, clientIP, clientPort, QString("CONNECTED TO SERVER")); + + struct timeval tv; + tv.tv_sec = 10; /* 10 secs timeout for read and write */ + + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = 0; + + int maxfdp1; + fd_set rset; + fd_set wset; + + setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger)); + // no reliable, but does not harm, as we use select() as well + setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + int flag = 1; + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + + static char buf[1024*8]; + bzero(buf, sizeof(buf)); + int const bytesToWrite = strlen(request.toStdString().c_str()); + strncpy(buf, request.toStdString().c_str(), sizeof(buf)-1); + + int loop = 0; + int bytesWritten = 0; + while (bytesWritten < bytesToWrite) { + errno = 0; + FD_ZERO(&wset); + FD_SET(sockfd, &wset); + maxfdp1 = sockfd + 1; + tv.tv_sec = 60; /* 60 secs timeout for read and write -> APISM cuts the connection after 30s */ + tv.tv_usec = 0; + + int const w = select(maxfdp1, NULL, &wset, NULL, &tv); + if (w < 0) { // error + if (errno == EINTR) { + printErrorMessage(port, clientIP, clientPort, + QString("INTERRUPTED BY SIGNAL (1) (") + strerror(errno) + ")"); + continue; + } else { + printErrorMessage(port, clientIP, clientPort, + QString("SELECT-ERROR (WRITE) %1(").arg(loop) + strerror(errno) + ")"); + ::close(sockfd); + return std::nullopt; + } + } else + if (w == 0) { // timeout + printErrorMessage(port, clientIP, clientPort, + QString("SELECT-TIMEOUT (WRITE) %1(").arg(loop) + strerror(errno) + ")"); + if (++loop < 10) { + QThread::msleep(500); + continue; + } + ::close(sockfd); + return std::nullopt; + } else + if (w > 0) { + int n = ::sendto(sockfd, buf+bytesWritten, bytesToWrite-bytesWritten, 0, NULL, 0); + if (n >= 0) { + bytesWritten += n; + } else { + if (errno == EWOULDBLOCK) { + if (++loop < 10) { + QThread::msleep(500); + continue; + } + printErrorMessage(port, clientIP, clientPort, + QString("WRITE TIMEOUT %1(").arg(loop) + strerror(errno) + ")"); + ::close(sockfd); + return std::nullopt; + } else + if (errno == EINTR) { + printErrorMessage(port, clientIP, clientPort, + QString("WRITE INTERRUPTED BY SIGNAL (1) (") + strerror(errno) + ")"); + continue; + } + } + } + } + + // DO NOT USE SHUTDOWN! APISM CAN NOT COPE WITH IT + // errno = 0; + // if (shutdown(sockfd, SHUT_WR) < 0) { + // printErrorMessage(port, clientIP, clientPort, + // QString("CANNOT CLOSE WRITING END (") + strerror(errno) + ")"); + // } + + printInfoMessage(port, clientIP, clientPort, QString("MESSAGE SENT <<<") + buf + ">>>"); + + loop = 0; + bzero(buf, sizeof(buf)); + int bytesToRead = sizeof(buf)-1; + int bytesRead = 0; + while (bytesRead < bytesToRead) { + errno = 0; + FD_ZERO(&rset); + FD_SET(sockfd, &rset); + maxfdp1 = sockfd + 1; + tv.tv_sec = 60; /* 60 secs timeout for read and write */ + tv.tv_usec = 0; + + QString const selectStart = QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + + int const r = select(maxfdp1, &rset, NULL, NULL, &tv); + if (r < 0) { // error + if (errno == EINTR) { + printErrorMessage(port, clientIP, clientPort, + QString("INTERRUPTED BY SIGNAL (2) (") + strerror(errno) + ")"); + continue; + } else { + printErrorMessage(port, clientIP, clientPort, + QString("SELECT-ERROR (READ) %1(").arg(loop) + strerror(errno) + ")"); + ::close(sockfd); + return std::nullopt; + } + } else + if (r == 0) { // timeout + printErrorMessage(port, clientIP, clientPort, + QString("SELECT-TIMEOUT (READ) %1(").arg(loop) + strerror(errno) + ")"); + if (++loop < 10) { + QThread::msleep(500); + continue; + } + ::close(sockfd); + return std::nullopt; + } else + if (r > 0) { + if (FD_ISSET(sockfd, &rset)) { + int n = ::recvfrom(sockfd, buf+bytesRead, bytesToRead-bytesRead, + 0, NULL, NULL); + if (n > 0) { // + bytesRead += n; + } else + if (n == 0) { + // The return value will be 0 when the peer has performed an orderly shutdown. + printErrorMessage(port, clientIP, clientPort, + QString("PEER CLOSED CONNECTION (") + strerror(errno) + ") START AT" + + selectStart + " NOW " + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + ::close(sockfd); + return std::nullopt; + } else + if (n < 0) { + if (errno == EWOULDBLOCK) { // check just in case + if (++loop < 10) { + QThread::msleep(500); + continue; + } + printErrorMessage(port, clientIP, clientPort, + QString("READ TIMEOUT %1(").arg(loop) + strerror(errno) + ")"); + ::close(sockfd); + return std::nullopt; + } + if (errno == EINTR) { + printErrorMessage(port, clientIP, clientPort, + QString("INTERRUPTED BY SIGNAL (2) (") + strerror(errno) + ")"); + continue; + } + } + } + } + + printInfoMessage(port, clientIP, clientPort, QString("MESSAGE RECEIVED ") + buf); + QString response(buf); + + if (int idx = response.indexOf("{\"error\":\"ISMAS is offline\"}")) { + response = response.mid(0, idx); + } else + if (response.contains("RECORD")) { // RECORD SAVED or RECORD WRITE ABORTED + printInfoMessage(port, clientIP, clientPort, QString("IGNORED '") + response + "' RESPONSE"); + ::close(sockfd); + return std::nullopt; + } + + QJsonParseError parseError; + QJsonDocument document(QJsonDocument::fromJson(response.toUtf8(), &parseError)); + if (parseError.error == QJsonParseError::NoError) { + if (document.isObject()) { // done: received valid APISM response + printInfoMessage(port, clientIP, clientPort, + QString("VALID APISM RESPONSE .. \n") + response); + ::close(sockfd); + return response; + } else { + printInfoMessage(port, clientIP, clientPort, + QString("CORRUPTED RESPONSE ") + response); + ::close(sockfd); + return std::nullopt; + } + } else { + printDebugMessage(port, clientIP, clientPort, + QString("PARSE ERROR ") + response + " " + parseError.errorString()); + ::close(sockfd); + return std::nullopt; + } + } + return std::nullopt; +} + +QString IsmasClient::updateNewsToIsmas(char const *event, + int percent, + int resultCode, + char const *step, + char const *step_result, + char const *version) { + char buf[1024]; + memset(buf, 0, sizeof(buf)); + + QString const ts = QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + snprintf(buf, sizeof(buf)-1, + "{" + "\"REASON\":\"SW_UP\"," + "\"TIMESTAMP\":\"%s\"," + "\"EVENT_ID\":\"0\"," + "\"EVENT\":\"%s\"," + "\"EVENTSTATE\":1," + "\"PARAMETER\": {" + "\"PERCENT\" : %d," + "\"RESULTCODE\" : %d," + "\"STEP\" : \"%s\"," + "\"STEP_RESULT\" : \"%s\"," + "\"VERSION\" : \"%s\"" + "}" + "}", ts.toStdString().c_str(), event, percent, resultCode, + step, step_result, version); + return buf; +} + +QString IsmasClient::errorBackendNotConnected(QString const &info, + QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + RESULT_CODE::INSTALL_ERROR, + "CHECK BACKEND CONNECTIVITY", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::errorGitClone(int percent, QString const &info, QString const &version) { + return updateNewsToIsmas("U0003", + percent, + RESULT_CODE::INSTALL_ERROR, + "CLONE CUSTOMER REPOSITORY FAILED", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::backendConnected(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "CHECK BACKEND CONNECTIVITY", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::execOpkgCommand(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "EXECUTE OPKG COMMAND", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::rsyncFile(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "RSYNC FILE", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::updateTriggerSet(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "CHECK UPDATE TRIGGER", + info.toStdString().c_str(), + version.toStdString().c_str()); + +} + +QString IsmasClient::errorUpdateTrigger(QString const &info, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + RESULT_CODE::INSTALL_ERROR, + "CHECK UPDATE TRIGGER", + info.toStdString().c_str(), + version.toStdString().c_str()); + +} + +QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) { + //static int const constexpr SIZE = 4096*8; + static char buf[4096*2]; + memset(buf, 0, sizeof(buf)); + + // local data="#M=APISM#C=CMD_SENDVERSION#J= + snprintf(buf, sizeof(buf)-1, + "{" + "\"VERSION_INFO\" : {" + "\"REASON\":\"%s\"," + "\"CREATED\":\"%s\"," + "\"HASH\":\"%s\"" + "}," + "\"TARIFF\" : {" + "\"VERSION\" : \"%s\"," + "\"PROJECT\" : \"%s\"," + "\"ZONE\" : %d," + "\"INFO\" : \"%s\"," + "\"BLOB\" : \"%s\"," + "\"LAST-COMMIT\" : \"%s\"," + "\"SIZE\" : %d," + "\"LOADED\" : \"%s\"" + "}," + "\"OPKG_COMMANDS\" : {" + "\"BLOB\" : \"%s\"," + "\"LAST-COMMIT\" : \"%s\"," + "\"SIZE\" : %d," + "\"LOADED\" : \"%s\"" + "}," + "\"JSON\" : {" + "\"DC2C_CASH\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_CONF\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_DEVICE\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_01\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_02\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_03\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_04\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_05\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_06\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_07\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_08\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_09\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_10\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_11\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_12\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_13\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_14\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_15\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_16\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_17\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_18\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_19\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_20\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_21\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_22\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_23\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_24\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_25\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_26\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_27\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_28\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_29\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_30\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_31\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}," + "\"DC2C_PRINT_32\" : {" + "\"BLOB\" : \"%s\"," + "\"SIZE\" : %d" + "}" + "}," + "\"HARDWARE\" : {" + "\"DEVICES\" : [\"PTU5\", \"DC\", \"PRINTER\", \"BNA\"]" + "}," + "\"OS\" : {" + "\"Linux\": \"%s\"" + "}," + "\"CONFIG\" : {" + "\"PTU5\" : {" + "\"CPU_SERIAL\" : \"%s\"" + "}," + "\"DC\" : {" + "\"HW-VERSION\" : \"%s\"," + "\"SW-VERSION\" : \"%s\"," + "\"SIZE\" : %d," + "\"GITBLOB\" : \"%s\"," + "\"GITLASTCOMMIT\" : \"%s\"" + "}," + "\"PRINTER\" : {" + "}," + "\"BNA\" : {" + "}" + "}," + "\"SOFTWARE\": {" + "\"APISM\" : {" + "\"VERSION\" : \"%s\"" + "}," + "\"ATBQT\" : {" + "\"VERSION\" : \"%s\"" + "}" + "}," + "\"PLUGINS\" : {" + "\"libATBDeviceControllerPlugin.so\" : {" + "\"VERSION\" : \"%s\"" + "}," + "\"libIngenicoISelf_CCPlugin.so\" : {" + "\"VERSION\" : \"%s\"" + "}," + "\"libMOBILISIS_CalculatePricePlugin.so\" : {" + "\"VERSION\" : \"%s\"" + "}," + "\"libMOBILISIS_CalculatePricePlugin_ConfigUi.so\" : {" + "\"VERSION\" : \"%s\"" + "}," + "\"libPRM_CalculatePricePlugin.so\" : {" + "\"VERSION\" : \"%s\"" + "}," + "\"libPRM_CalculatePricePlugin_ConfigUi.so\" : {" + "\"VERSION\" : \"%s\"" + "}," + "\"libTCP_ZVT_CCPlugin.so\" : {" + "\"VERSION\" : \"%s\"" + "}" + "}" + "}", + psa.versionInfo.reason.toStdString().c_str(), + psa.versionInfo.created.toStdString().c_str(), + psa.versionInfo.lastCommit.toStdString().c_str(), + + psa.tariff.version.toStdString().c_str(), + psa.tariff.project.toStdString().c_str(), + psa.tariff.zone, + psa.tariff.info.toStdString().c_str(), + psa.tariff.blob.toStdString().c_str(), + psa.tariff.lastCommit.toStdString().c_str(), + psa.tariff.size, + psa.tariff.loadTime.toStdString().c_str(), + + psa.opkg.blob.toStdString().c_str(), + psa.opkg.lastCommit.toStdString().c_str(), + psa.opkg.size, + psa.opkg.loadTime.toStdString().c_str(), + + psa.cash.blob.toStdString().c_str(), + psa.cash.size, + psa.conf.blob.toStdString().c_str(), + psa.conf.size, + psa.device.blob.toStdString().c_str(), + psa.device.size, + + psa.print[0].blob.toStdString().c_str(), + psa.print[0].size, + psa.print[1].blob.toStdString().c_str(), + psa.print[1].size, + psa.print[2].blob.toStdString().c_str(), + psa.print[2].size, + psa.print[3].blob.toStdString().c_str(), + psa.print[3].size, + psa.print[4].blob.toStdString().c_str(), + psa.print[4].size, + psa.print[5].blob.toStdString().c_str(), + psa.print[5].size, + psa.print[6].blob.toStdString().c_str(), + psa.print[6].size, + psa.print[7].blob.toStdString().c_str(), + psa.print[7].size, + psa.print[8].blob.toStdString().c_str(), + psa.print[8].size, + psa.print[9].blob.toStdString().c_str(), + psa.print[9].size, + psa.print[10].blob.toStdString().c_str(), + psa.print[10].size, + psa.print[11].blob.toStdString().c_str(), + psa.print[11].size, + psa.print[12].blob.toStdString().c_str(), + psa.print[12].size, + psa.print[13].blob.toStdString().c_str(), + psa.print[13].size, + psa.print[14].blob.toStdString().c_str(), + psa.print[14].size, + psa.print[15].blob.toStdString().c_str(), + psa.print[15].size, + psa.print[16].blob.toStdString().c_str(), + psa.print[16].size, + psa.print[17].blob.toStdString().c_str(), + psa.print[17].size, + psa.print[18].blob.toStdString().c_str(), + psa.print[18].size, + psa.print[19].blob.toStdString().c_str(), + psa.print[19].size, + psa.print[20].blob.toStdString().c_str(), + psa.print[20].size, + psa.print[21].blob.toStdString().c_str(), + psa.print[21].size, + psa.print[22].blob.toStdString().c_str(), + psa.print[22].size, + psa.print[23].blob.toStdString().c_str(), + psa.print[23].size, + psa.print[24].blob.toStdString().c_str(), + psa.print[24].size, + psa.print[25].blob.toStdString().c_str(), + psa.print[25].size, + psa.print[26].blob.toStdString().c_str(), + psa.print[26].size, + psa.print[27].blob.toStdString().c_str(), + psa.print[27].size, + psa.print[28].blob.toStdString().c_str(), + psa.print[28].size, + psa.print[29].blob.toStdString().c_str(), + psa.print[29].size, + psa.print[30].blob.toStdString().c_str(), + psa.print[30].size, + psa.print[31].blob.toStdString().c_str(), + psa.print[31].size, + + psa.hw.linuxVersion.toStdString().c_str(), + psa.hw.cpuSerial.toStdString().c_str(), + + psa.dc.versionHW.toStdString().c_str(), + psa.dc.versionSW.toStdString().c_str(), + psa.dc.size, + psa.dc.gitBlob.toStdString().c_str(), + psa.dc.gitLastCommit.toStdString().c_str(), + + psa.sw.apismVersion.toStdString().c_str(), + psa.sw.atbQTVersion.toStdString().c_str(), + + psa.pluginVersion.deviceController.toStdString().c_str(), + psa.pluginVersion.ingenicoISelfCC.toStdString().c_str(), + psa.pluginVersion.mobilisisCalculatePrice.toStdString().c_str(), + psa.pluginVersion.mobilisisCalculatePriceConfigUi.toStdString().c_str(), + psa.pluginVersion.prmCalculatePrice.toStdString().c_str(), + psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(), + psa.pluginVersion.tcpZVT.toStdString().c_str()); + + qInfo() << buf; + + return buf; +} + +QString IsmasClient::updateOfPSAContinues(QString currentStage, + QString currentStageInfo, + QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + currentStage.toStdString().c_str(), + currentStageInfo.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::updateOfPSAStarted(QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "START", + "detected WAIT state: start update process", + version.toStdString().c_str()); +} + +QString IsmasClient::checkoutBranch(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "BRANCH-CHECKOUT", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::cloneAndCheckoutCustomerRepository(QString const &info, QString const &version) { // clone and checkout customer repository + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "CLONE-CHECKOUT", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::gitFetch(QString const &info, QString const &version) { + return updateNewsToIsmas("U0010", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "GIT-FETCH", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::errorGitFetch(int resultCode, QString const &info, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + resultCode, + "GIT-FETCH-FAILED", + info.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::updateOfPSAActivated(QString const &version) { // sent even after success + m_progressInPercent = 0; + return updateNewsToIsmas("U0002", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "UPDATE ACTIVATED", + "reset WAIT state", + version.toStdString().c_str()); +} + +QString IsmasClient::updateOfPSASucceeded(QString const &version) { + m_progressInPercent = 100; + return updateNewsToIsmas("U0001", + m_progressInPercent, + RESULT_CODE::SUCCESS, + "UPDATE SUCCESS", + "update process succeeded", + version.toStdString().c_str()); +} + +QString IsmasClient::sanityCheckFailed(int resultCode, QString reason, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + resultCode, + "SANITY-CHECK-FAILED", + reason.toStdString().c_str(), + version.toStdString().c_str()); +} + +QString IsmasClient::jsonParseFailed(int resultCode, QString reason, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + resultCode, + "JSON-PARSE-ERROR", + reason.toStdString().c_str(), + version.toStdString().c_str()); +} + +std::optional IsmasClient::finalResult(int resultCode, QString reason, QString const &version) { + m_progressInPercent = 100; + if (resultCode == RESULT_CODE::SUCCESS) { + return updateNewsToIsmas("U0002", + m_progressInPercent, + resultCode, + "FINAL-UPDATE-RESULT", + reason.toStdString().c_str(), + version.toStdString().c_str()); + } + if (resultCode == RESULT_CODE::INSTALL_ERROR) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + resultCode, + "FINAL-UPDATE-RESULT", + reason.toStdString().c_str(), + version.toStdString().c_str()); + } + return std::nullopt; +} + +QString IsmasClient::updateOfPSAFailed(int resultCode, QString step, + QString reason, QString const &version) { + return updateNewsToIsmas("U0003", + m_progressInPercent, + resultCode, + step.toStdString().c_str(), + reason.toStdString().c_str(), + version.toStdString().c_str()); +} + +char const *IsmasClient::reason[REASON::ENTRIES] = { + "TIME-TRIGGERED", "SERVICE", "DEV-TEST" +}; + +QString IsmasClient::getReasonForLastSendVersion() { + QString const &parentName = Utils::getParentName(); + if (parentName == "ATBQT") { + return reason[REASON::SERVICE]; + } + if (parentName == "systemd") { + return reason[REASON::TIME_TRIGGERED]; + } + return reason[REASON::DEV_TEST]; +} diff --git a/ismas/ismas_client.h b/ismas/ismas_client.h new file mode 100644 index 0000000..a43fe1f --- /dev/null +++ b/ismas/ismas_client.h @@ -0,0 +1,206 @@ +#ifndef ISMAS_CLIENT_H_INCLUDED +#define ISMAS_CLIENT_H_INCLUDED + +#include +#include + +#include + +struct PSAInstalled { + struct VersionInfo { + QString created; + QString reason; + QString lastCommit; + } versionInfo; + + struct Tariff { + QString name; + QString version; + QString project; + int zone; + int size; + QString blob; + QString lastCommit; + QString info; + QString loadTime; + } tariff; + + struct HardWare { + QString linuxVersion; + QString cpuSerial; + } hw; + + struct Opkg { + int size; + QString blob; + QString lastCommit; + QString loadTime; + } opkg; + + struct DC { + QString versionHW; + QString versionSW; + QString gitBlob; + QString gitLastCommit; + int size; + } dc; + + struct SoftWare { + QString apismVersion; + QString atbQTVersion; + } sw; + + struct PluginVersion { + QString deviceController; + QString ingenicoISelfCC; + QString mobilisisCalculatePrice; + QString mobilisisCalculatePriceConfigUi; + QString prmCalculatePrice; + QString prmCalculatePriceConfigUi; + QString tcpZVT; + } pluginVersion; + + struct DC2C { + QString name; + QString blob; + int size; + }; + + DC2C cash; + DC2C conf; + DC2C device; + + DC2C print[32]; + + explicit PSAInstalled() { + tariff.name = "N/A"; + tariff.version = "N/A"; + tariff.project = "N/A"; + tariff.zone = -1; + tariff.size = -1; + tariff.blob = "N/A"; + tariff.info = "N/A"; + tariff.loadTime = "N/A"; + + hw.linuxVersion = "N/A"; + hw.cpuSerial = "N/A"; + + opkg.size = -1; + opkg.blob = "N/A"; + opkg.lastCommit = "N/A"; + opkg.loadTime = "N/A"; + + dc.versionHW = "N/A"; + dc.versionSW = "N/A"; + dc.gitBlob = "N/A"; + dc.gitLastCommit = "N/A"; + dc.size = -1; + + sw.apismVersion = "N/A"; + sw.atbQTVersion = "N/A"; + + pluginVersion.deviceController = "N/A"; + pluginVersion.ingenicoISelfCC = "N/A"; + pluginVersion.mobilisisCalculatePrice = "N/A"; + pluginVersion.mobilisisCalculatePriceConfigUi = "N/A"; + pluginVersion.prmCalculatePrice = "N/A"; + pluginVersion.prmCalculatePriceConfigUi = "N/A"; + pluginVersion.tcpZVT = "N/A"; + + cash.name = "N/A"; + cash.blob = "N/A"; + cash.size = -1; + + conf.name = "N/A"; + conf.blob = "N/A"; + conf.size = -1; + + device.size = -1; + device.blob = "N/A"; + device.size = -1; + + for (int i=0; i < 32; ++i) { + print[i].size = -1; + print[i].blob = "N/A"; + print[i].size = -1; + } + } +}; + +class IsmasClient : public QObject { + Q_OBJECT + + int m_progressInPercent; +public: + explicit IsmasClient() : m_progressInPercent(1) {} + + enum APISM { + DB_PORT = 7777, + DIRECT_PORT = 7778 + }; + + + enum RESULT_CODE { + SUCCESS=0, + NO_UPDATE_NECESSARY=1, + BACKUP_FAILED=2, + WRONG_PACKAGE=3, + INSTALL_ERROR=4}; + + enum REASON { + TIME_TRIGGERED = 0, + SERVICE, + DEV_TEST, + ENTRIES + }; + + static char const *reason[REASON::ENTRIES]; + + static std::optional + sendRequestReceiveResponse(int port, QString const &request); + + static QString getReasonForLastSendVersion(); + + int getProgressInPercent() const {return m_progressInPercent; } + void setProgressInPercent(int procent) { m_progressInPercent = procent; } + + QString updateNewsToIsmas(char const *event, + int percent, + int resultCode, + char const *step, + char const *step_result, + char const *version); + + QString updateOfPSAStarted(QString const &version = QString()); // start of update process + QString cloneAndCheckoutCustomerRepository(QString const &info, QString const &version = QString()); // clone and checkout customer repository + QString checkoutBranch(QString const &info, QString const &version = QString()); // checkout branch + QString errorBackendNotConnected(QString const &info, QString const &version = QString()); // checkout branch + QString errorGitClone(int percent, QString const &info, QString const &version = QString()); + QString backendConnected(QString const &info, QString const &version = QString()); + QString updateTriggerSet(QString const &info, QString const &version = QString()); + QString errorUpdateTrigger(QString const &info, QString const &version = QString()); + QString gitFetch(QString const &info, QString const &version = QString()); + QString execOpkgCommand(QString const &info, QString const &version = QString()); + QString rsyncFile(QString const &info, QString const &version = QString()); + QString errorGitFetch(int resultCode, QString const &info, QString const &version = QString()); + QString updateOfPSAActivated(QString const &version = QString()); + // and update accepted + QString updateOfPSASucceeded(QString const &version = QString()); // update process succeeded + QString updateOfPSAContinues(QString currentStage, QString currentStageInfo, QString const &version = QString()); + QString updateOfPSAFailed(int resultCode, QString step, QString reason, QString const &version = QString()); + QString sanityCheckFailed(int resultCode, QString reason, QString const &version = QString()); + QString jsonParseFailed(int resultCode, QString reason, QString const &version = QString()); + std::optional finalResult(int resultCode, QString reason, QString const &version = QString()); + + QString updateOfPSASendVersion(PSAInstalled const &psa); + + private: + static void printDebugMessage(int port, QString const &clientIP, int clientPort, + QString const &message); + static void printInfoMessage(int port, QString const &clientIP, int clientPort, + QString const &message); + static void printErrorMessage(int port, QString const &clientIP, int clientPort, + QString const &message); +}; + +#endif // ISMAS_CLIENT_H_INCLUDED diff --git a/main.cpp b/main.cpp index 721f742..877ebae 100644 --- a/main.cpp +++ b/main.cpp @@ -12,7 +12,6 @@ #include "plugins/interfaces.h" #include -#include #include #include #include @@ -21,12 +20,24 @@ #include #include #include +#include #include "update.h" +#include "git/git_client.h" +#include "ismas/ismas_client.h" #include "worker_thread.h" #include "worker.h" +#include "mainwindow.h" +#include "utils.h" -#include +#include +#include +#include + +#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX) +#include +#include +#endif #ifdef PTU5 #define SERIAL_PORT "ttymxc2" @@ -34,34 +45,43 @@ #define SERIAL_PORT "ttyUSB0" #endif -class hwinf; -static void doWork(hwinf *hw, QString update_ctrl_file, - QString workingDir, bool maintenanceMode) { - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - Update update(hw, update_ctrl_file, workingDir, maintenanceMode); - update.doUpdate(); - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - QCoreApplication::quit(); -} - // argv[1]: file to send to dc int main(int argc, char *argv[]) { - - QByteArray const value = qgetenv("XDG_RUNTIME_DIR"); - if (value.size() == 0) { - qputenv("XDG_RUNTIME_DIR", "/run/user/0"); + QByteArray const value = qgetenv("LC_ALL"); + if (value != "C") { + qputenv("LC_ALL", "C"); } + // qputenv("XDG_RUNTIME_DIR", "/run/user/0"); + + openlog("ATB-UPDATE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); QApplication a(argc, argv); QApplication::setApplicationName("ATBUpdateTool"); - QApplication::setApplicationVersion("1.0"); + QApplication::setApplicationVersion(APP_VERSION); if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling atbInstallMessageHandler(atbDebugOutput); - setDebugLevel(QtMsgType::QtDebugMsg); - //setDebugLevel(QtMsgType::QtDebugMsg); + setDebugLevel(LOG_NOTICE); } +//#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX) +//#ifdef _POSIX_THREAD_PROCESS_SHARED +// errno = 0; +// int res = 0; +// if ((res = sysconf(_SC_THREAD_PROCESS_SHARED)) < 0) { +// if (errno != 0) { +// qCritical() << "_POSIX_THREAD_PROCESS_SHARED NOT SUPPORTED" +// << strerror(errno); +// exit(-1); +// } +// } else { +// if (res == _POSIX_THREAD_PROCESS_SHARED) { +// Utils::printInfoMsg("_POSIX_THREAD_PROCESS_SHARED SUPPORTED"); +// } +// } +//#endif +//#endif + QCommandLineParser parser; parser.setApplicationDescription("Download tool for downloading device controller firmware, printer json-files and executing opkg-commands."); parser.addHelpOption(); @@ -70,56 +90,47 @@ int main(int argc, char *argv[]) { QCommandLineOption pluginDirectoryOption(QStringList() << "plugin-directory" << "plugin-directory", QCoreApplication::translate("main", "Where to find dc-plugin."), QCoreApplication::translate("main", "directory")); - QString const pluginDefault = "./plugins"; + QString const pluginDefault = "/usr/lib"; pluginDirectoryOption.setDefaultValue(pluginDefault); parser.addOption(pluginDirectoryOption); QCommandLineOption pluginNameOption(QStringList() << "plugin-name" << "plugin-name", QCoreApplication::translate("main", "Name of dc-plugin."), QCoreApplication::translate("main", "directory")); - QString const pluginNameDefault = "libCAmaster.so"; + QString const pluginNameDefault = "libCAslave.so"; pluginNameOption.setDefaultValue(pluginNameDefault); parser.addOption(pluginNameOption); QCommandLineOption workingDirectoryOption(QStringList() << "working-directory" << "working-directory", QCoreApplication::translate("main", "working directory of update-script."), QCoreApplication::translate("main", "directory")); - QString const workingDirectoryDefault = "."; + QString const workingDirectoryDefault = "/opt/app/tools/atbupdate"; workingDirectoryOption.setDefaultValue(workingDirectoryDefault); parser.addOption(workingDirectoryOption); - QCommandLineOption maintenanceOption("m", - QCoreApplication::translate("main", "Maintenance mode for underlying script")); - parser.addOption(maintenanceOption); - - // TODO: - // add some additional parameters - // --dry-run - // -d: only update device-controller firmware - // -j: only update json-files - // -o: only execute opkg-commnds + QCommandLineOption dryRunOption(QStringList() << "d" << "dry-run", + QCoreApplication::translate("main", "Start ATBUpdateTool in dry-run-mode. No actual actions.")); + parser.addOption(dryRunOption); // Process the actual command line arguments given by the user parser.process(a); QString plugInDir = parser.value(pluginDirectoryOption); QString plugInName = parser.value(pluginNameOption); QString workingDir = parser.value(workingDirectoryOption); - bool maintenanceMode = parser.isSet(maintenanceOption); + bool dryRun = parser.isSet(dryRunOption); QString const rtPath = QCoreApplication::applicationDirPath(); - if (plugInDir == pluginDefault) { - plugInDir = (rtPath + "/" + pluginDefault); - } if (!QDir(plugInDir).exists()) { qCritical() << plugInDir << "does not exists, but has to contain dc-library"; exit(-1); } - qInfo() << "pwd" << "=" << rtPath; - qInfo() << "plugInDir" << "=" << plugInDir; - qInfo() << "plugInName" << "=" << plugInName; - qInfo() << "workingDir" << "=" << workingDir; - qInfo() << "maintenanceMode" << "=" << maintenanceMode; + + qInfo() << "pwd ..............." << rtPath; + qInfo() << "plugInDir ........." << plugInDir; + qInfo() << "plugInName ........" << plugInName; + qInfo() << "workingDir ........" << workingDir; + qInfo() << "dryRun ............" << dryRun; // before loading the library, delete all possible shared memory segments #if defined Q_OS_LINUX || defined Q_OS_UNIX @@ -129,13 +140,48 @@ int main(int argc, char *argv[]) { #endif hwinf *hw = Update::loadDCPlugin(QDir(plugInDir), plugInName); - hw->dc_autoRequest(false); + hw->dc_autoRequest(true); + // hw->dc_openSerial(5, "115200", "ttymxc2", 1); - QString const update_ctrl_file = "/opt/app/tools/atbupdate/update_log.csv"; - std::thread t(doWork, hw, update_ctrl_file, workingDir, maintenanceMode); + int machineNr = Utils::read1stLineOfFile("/etc/machine_nr"); + int customerNr = Utils::read1stLineOfFile("/etc/cust_nr"); + int zoneNr = Utils::read1stLineOfFile("/etc/zone_nr"); + QString const branchName = (zoneNr != 0) + ? QString("zg1/zone%1").arg(zoneNr) : "master"; - int ret = a.exec(); - t.join(); + QThread::currentThread()->setObjectName("main thread"); + qInfo() << "Main thread" << QThread::currentThreadId(); - return ret; + Worker worker(customerNr, + machineNr, + zoneNr, + branchName, + plugInName, + workingDir, + dryRun); + + QString const customerNrStr( + QString("customer_") + QString::number(customerNr).rightJustified(3, '0')); + + QScopedPointer update( + new Update(hw, + &worker, + QDir::cleanPath(workingDir + QDir::separator() + customerNrStr), + customerNrStr, + branchName, + plugInName, + workingDir, + dryRun, + nullptr, + SERIAL_PORT, + "115200")); + + MainWindow mw(hw, &worker, update.get()); + worker.setMainWindow(&mw); + + mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint); + mw.setWindowState(Qt::WindowFullScreen); + mw.show(); + + return a.exec(); } diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..d1989cd --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,445 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "worker.h" +#include "utils.h" +#include "progress_event.h" +#include "update_dc_event.h" +#include "plugins/interfaces.h" + +#include +#include +#include +#include + + + +#if EMERGENCY_LEAVE_BL==1 +static int step = 0; + +void MainWindow::emergencyLeaveBL() { + // + qCritical() << __func__ << step; + switch(step) { + case 0: + if (m_hw->dc_openSerial(5, "115200", "ttymxc2", 1)) { + qCritical() << __func__ << "open ok"; + step++; + QThread::msleep(2000); + m_hw->dc_autoRequest(false); + emit leaveBL(); + } + break; + case 1: + m_hw->bl_rebootDC(); + QThread::msleep(1000); + qCritical() << __func__ << "reboot ok" << QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + step++; + emit leaveBL(); + break; + case 2: + case 3: + case 4: + case 5: + case 6: + m_hw->bl_startBL(); + QThread::msleep(1000); + qCritical() << __func__ << "start" << QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + step++; + emit leaveBL(); + break; + case 7: + case 9: + case 11: + case 13: + case 15: + m_hw->bl_checkBL(); + qCritical() << __func__ << "check" << QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + QThread::msleep(1500); + ++step; + emit leaveBL(); + break; + case 8: + case 10: + case 12: + case 14: + case 16: + qCritical() << __func__ << "is Up..." << QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + if (m_hw->bl_isUp()) { + qCritical() << __func__ << "is Up...OK" << step << QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + QThread::msleep(5000); + step = 16; + } else { + qCritical() << __func__ << "is Up...NO" << step << QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + } + ++step; + emit leaveBL(); + break; + case 17: + case 18: + case 19: + qCritical() << __func__ << "stop" << QDateTime::currentDateTime().toString(Qt::ISODateWithMs); + m_hw->bl_stopBL(); + QThread::msleep(1000); + //m_hw->dc_closeSerial(); + ++step; + emit leaveBL(); + break; + } +} +#endif + +MainWindow::MainWindow(hwinf *hw, Worker *worker, Update *update, QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) + , m_hw(hw) + , m_worker(worker) + , m_width(70) + , m_progressRunning(false) + , m_progressValue(0) + , m_update(update) + , m_updateStep(UpdateDcEvent::UpdateStep::NONE) { + +#if EMERGENCY_LEAVE_BL==1 + QTimer *t = new QTimer(this); + connect(t, SIGNAL(timeout()), this, SLOT(emergencyLeaveBL())); + connect(this, SIGNAL(leaveBL()), this, SLOT(emergencyLeaveBL()), Qt::QueuedConnection); + t->setSingleShot(true); + t->start(1000); + return; +#endif + + this->setStatusBar(new QStatusBar(this)); + QFont f; + f.setStyleHint(QFont::Monospace); + f.setWeight(QFont::Bold); + f.setFamily("Misc Fixed"); + f.setPixelSize(12); + this->statusBar()->setFont(f); + + ui->setupUi(this); + + ui->updateProgress->setRange(0, 100); + ui->updateProgress->reset(); + + QStringList lst; + QString start = QDateTime::currentDateTime().toString(Qt::ISODate); + lst << QString("Start: ") + start.leftJustified(m_width-10); + lst << QString("").leftJustified(m_width-3, '='); + lst << QString("Machine number : %1 ").arg(m_worker->machineNr()).leftJustified(m_width-3); + lst << QString("Customer number : %1 ").arg(m_worker->customerNr()).leftJustified(m_width-3); + lst << QString("Zone number : %1 (%2)").arg(m_worker->zoneNr()).arg(Utils::zoneName(m_worker->zoneNr())).leftJustified(m_width-3); + lst << QString("APISM version : %1").arg(m_worker->apismVersion()).leftJustified(m_width-3); + lst << QString("").leftJustified(m_width-3, '='); + + ui->updateStatus->setText(lst.join('\n')); + ui->updateStatus->setEnabled(true); + // ui->updateStatus->installEventFilter(this); + + m_startTimer = new QTimer(this); + connect(m_startTimer, SIGNAL(timeout()), m_worker, SLOT(update())); + m_startTimer->setSingleShot(true); + m_startTimer->start(1000); + + m_exitTimer = new QTimer(this); + connect(m_exitTimer, SIGNAL(timeout()), ui->exit, SLOT(click())); + m_exitTimer->setSingleShot(true); + m_exitTimer->start(1800 * 1000); + + connect(ui->exit, SIGNAL(clicked()),this,SLOT(onQuit())); + connect(m_worker, SIGNAL(disableExit()),this,SLOT(onDisableExit())); + connect(m_worker, SIGNAL(enableExit()),this,SLOT(onEnableExit())); + connect(m_worker, SIGNAL(stopStartTimer()),this,SLOT(onStopStartTimer())); + connect(m_worker, SIGNAL(restartExitTimer()),this,SLOT(onRestartExitTimer())); + connect(m_worker, SIGNAL(appendText(QString,QString)),this,SLOT(onAppendText(QString,QString))); + connect(m_worker, SIGNAL(showErrorMessage(QString,QString)),this, SLOT(onShowErrorMessage(QString,QString))); + connect(m_worker, SIGNAL(showStatusMessage(QString,QString)),this, SLOT(onShowStatusMessage(QString,QString))); + connect(m_worker, SIGNAL(replaceLast(QString,QString)),this,SLOT(onReplaceLast(QString,QString))); + connect(m_worker, SIGNAL(replaceLast(QStringList,QString)),this, SLOT(onReplaceLast(QStringList,QString))); +} + +MainWindow::~MainWindow() { + delete m_startTimer; + delete m_exitTimer; + delete ui; +} + +void MainWindow::customEvent(QEvent *event) { + if (event->type() == ProgressEvent::type()) { + ProgressEvent *pevent = (ProgressEvent *)event; + int const progress = pevent->progressPercent(); + QObject const *sender = pevent->sender(); + if (sender == this) { + switch(progress) { + case 0: { + ui->updateProgress->reset(); + } break; + case START_PROGRESS_LOOP: { + m_progressRunning = true; + ui->updateProgress->reset(); + m_progressValue = 10; + QApplication::postEvent(this, new ProgressEvent(this, m_progressValue)); + } break; + case STOP_PROGRESS_LOOP: { + m_progressRunning = false; + m_progressValue -= 10; + m_worker->setProgress(m_progressValue/10); + } break; + default: { + if (m_progressRunning) { + m_progressValue = progress; + ui->updateProgress->setValue(progress/10); + QApplication::postEvent(this, new ProgressEvent(this, progress+10)); + QThread::msleep(500); + } + } + } + } else + if (sender == m_worker) { + switch(progress) { + case 0: { + ui->updateProgress->reset(); + } break; + case START_PROGRESS_LOOP: { + QApplication::postEvent(this, new ProgressEvent(this, START_PROGRESS_LOOP)); + } break; + case STOP_PROGRESS_LOOP: { + QApplication::postEvent(this, new ProgressEvent(this, STOP_PROGRESS_LOOP)); + } break; + default:{ + ui->updateProgress->setValue(progress); + }} + } else { + qCritical() << "!!! UNKNOWN SENDER !!!"; + } + } else + if (event->type() == UpdateDcEvent::type()) { + UpdateDcEvent *pevent = (UpdateDcEvent *)event; + UpdateDcEvent::UpdateStep const updateStep = pevent->updateStep(); + QObject const *sender = pevent->sender(); + if (sender == m_worker) { + QDateTime const &recv = QDateTime::currentDateTime(); + QDateTime const &send = pevent->sendDateTime(); + qint64 const delay = recv.toMSecsSinceEpoch() - send.toMSecsSinceEpoch(); + switch(updateStep) { + case UpdateDcEvent::UpdateStep::NONE: + break; + case UpdateDcEvent::UpdateStep::DC_REBOOT: { + m_hw->bl_rebootDC(); + QString msg = QDateTime::currentDateTime().toString(Qt::ISODateWithMs) + + QString(": reset device controller (delay=%1ms").arg(delay); + emit m_worker->showStatusMessage("dc update", msg); + Utils::printInfoMsg(msg.toUpper()); + m_updateStep = UpdateDcEvent::UpdateStep::DC_REBOOT; + } break; + case UpdateDcEvent::UpdateStep::BL_START: { + QString const &msg = recv.toString(Qt::ISODateWithMs) + + QString(": start bootloader (%1, delay=%2ms)").arg(pevent->count()).arg(delay); + emit m_worker->showStatusMessage("dc update", msg); + Utils::printInfoMsg(msg.toUpper()); + m_hw->bl_startBL(); + if (pevent->count() == BL_START_COUNT) { + m_updateStep = UpdateDcEvent::UpdateStep::BL_START; + } + } break; + case UpdateDcEvent::UpdateStep::BL_CHECK: { + if (m_updateStep != UpdateDcEvent::UpdateStep::BL_IS_UP) { + QString const &msg = recv.toString(Qt::ISODateWithMs) + + QString(": request bootloader version (%1, delay=%2ms)").arg(pevent->count()).arg(delay); + emit m_worker->showStatusMessage("dc update", msg); + Utils::printInfoMsg(msg.toUpper()); + m_hw->bl_checkBL(); + //m_updateStep = UpdateDcEvent::UpdateStep::BL_CHECK; + } + } break; + case UpdateDcEvent::UpdateStep::BL_IS_UP: { + QString msg = recv.toString(Qt::ISODateWithMs) + + QString(": check running bootloader (%1, delay=%2ms)").arg(pevent->count()).arg(delay); + emit m_worker->showStatusMessage("dc update", msg); + Utils::printInfoMsg(msg.toUpper()); + if (m_updateStep != UpdateDcEvent::UpdateStep::BL_IS_UP) { + if (m_hw->bl_isUp()) { + msg = recv.toString(Qt::ISODateWithMs) + + QString(": bootloader running (%1, delay=%2ms)").arg(pevent->count()).arg(delay); + emit m_worker->showStatusMessage("dc update", msg); + Utils::printInfoMsg(msg.toUpper()); + m_updateStep = UpdateDcEvent::UpdateStep::BL_IS_UP; + } else { + msg = recv.toString(Qt::ISODateWithMs) + + QString(": bootloader stop requested (%1, delay=%2ms)").arg(pevent->count()).arg(delay); + emit m_worker->showStatusMessage("dc update", msg); + Utils::printInfoMsg(msg.toUpper()); + if (m_updateStep == UpdateDcEvent::UpdateStep::BL_STOP) { + msg = QDateTime::currentDateTime().toString(Qt::ISODateWithMs) + + QString(": bootloader down (%1, delay=%2ms)").arg(pevent->count()).arg(delay); + emit m_worker->showStatusMessage("dc update", msg); + Utils::printInfoMsg(msg.toUpper()); + m_updateStep = UpdateDcEvent::UpdateStep::BL_IS_DOWN; + } + } + } + } break; + case UpdateDcEvent::UpdateStep::BL_STOP: { + QString const &msg = QDateTime::currentDateTime().toString(Qt::ISODateWithMs) + + QString(": stop bootloader (%1, delay=%2ms)").arg(pevent->count()).arg(delay); + emit m_worker->showStatusMessage("dc update", msg); + Utils::printInfoMsg(msg.toUpper()); + //if (m_bootLoaderIsUp) { + m_hw->bl_stopBL(); + m_updateStep = UpdateDcEvent::UpdateStep::BL_STOP; + //} + } break; + default: ; + } + } + } + + QThread::yieldCurrentThread(); +} + +void MainWindow::onStopStartTimer() { + m_startTimer->stop(); +} + +void MainWindow::onDisableExit() { + ui->exit->setEnabled(false); +} + +void MainWindow::onEnableExit() { + ui->exit->setEnabled(true); +} + +//bool MainWindow::eventFilter(QObject *obj, QEvent *ev) { +// if (obj == ui->updateStatus) { +// qCritical() << "REc. event for text edit" << ev->type(); +// } +// return QMainWindow::eventFilter(obj, ev); +//} + +void MainWindow::onRestartExitTimer() { + m_exitTimer->stop(); + m_exitTimer->start(60 * 1000); + + // ui->updateStatus->blockSignals(true); +} + +void MainWindow::onQuit() { + m_exitTimer->stop(); + qCritical() << QString("ON QUIT: EXIT CODE %1").arg(m_worker->returnCode()); + qApp->exit(m_worker->returnCode()); +} + +void MainWindow::scrollDownTextEdit() { + Utils::printInfoMsg(QString("SCROLL-DOWN-TEXT_EDIT CALLED AT ") + + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + + ui->updateStatus->setEnabled(true); + + QTextCursor tmpCursor = ui->updateStatus->textCursor(); + tmpCursor.movePosition(QTextCursor::End); + ui->updateStatus->setTextCursor(tmpCursor); + ui->updateStatus->ensureCursorVisible(); +} + +void MainWindow::onAppendText(QString text, QString suffix) { + Utils::printInfoMsg(QString("ON APPEND CALLED AT ") + + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + + QString editText = ui->updateStatus->toPlainText(); + if (!suffix.isNull() && suffix.size() > 0) { + //qInfo() << "TEXT" << text << "SUFFIX" << suffix; + if (suffix == Worker::UPDATE_STEP_SUCCESS || suffix == Worker::UPDATE_STEP_FAIL) { + ui->updateStatus->insertPlainText(QString("\n").leftJustified(m_width-3, '=') + " "); + // editText += QString("\n").leftJustified(m_width-3, '='); + // editText += " "; + } + QString const &add = (QString("\n") + text).leftJustified(m_width - (2 + suffix.size())) + suffix; + ui->updateStatus->insertPlainText(add); + // editText += add; + } else { + QString const &add = text.leftJustified(m_width-9); + ui->updateStatus->insertPlainText(add); + //editText += add; + } + + // debug + // QString editText = ui->updateStatus->toPlainText(); + // Utils::printLineEditInfo(editText.split('\n', QString::SplitBehavior::SkipEmptyParts)); + // ui->updateStatus->setText(editText.trimmed()); + + scrollDownTextEdit(); +} + +void MainWindow::onReplaceLast(QStringList newTextLines, QString suffix) { + Utils::printInfoMsg(QString("ON REPLACE LAST (LIST) CALLED AT ") + + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + + int const s = newTextLines.size(); + if (s > 0) { + QString editText = ui->updateStatus->toPlainText(); + QStringList lines = editText.split('\n', QString::SplitBehavior::SkipEmptyParts); + QString newText; + if (lines.size() >= s) { + for (int i = 0; i < s; ++i) { + lines.removeLast(); + } + if (lines.size() > 0) { + newText = lines.join('\n'); + newText += '\n'; + } + QStringList newLines; + for (int i = 0; i < s; ++i) { + if (i == 0 && !suffix.isNull() && suffix.size() > 0 && suffix != "\n") { + newLines += Utils::rstrip(newTextLines.at(i).leftJustified(m_width-10) + suffix); + } else { + newLines += Utils::rstrip(newTextLines.at(i).leftJustified(m_width-10)); + } + } + lines += newLines; + newText += newLines.join(' '); + } + + ui->updateStatus->setText(newText); + Utils::printLineEditInfo(lines); + scrollDownTextEdit(); + } +} + +void MainWindow::onReplaceLast(QString text, QString suffix) { + Utils::printInfoMsg(QString("ON REPLACE LAST (TEXT) CALLED AT ") + + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); + + QString editText = ui->updateStatus->toPlainText(); + QStringList lines = editText.split('\n', QString::SplitBehavior::SkipEmptyParts); + if (lines.size() > 0) { + lines.removeLast(); + if (!suffix.isNull() && suffix.size() > 0 && suffix != "\n") { + QString const add = text.leftJustified(m_width-10) + suffix; + if (!add.isEmpty()) { + lines += text.leftJustified(m_width-10) + suffix; + } + } else { + QString const add = text.leftJustified(m_width-10); + if (!add.isEmpty()) { + lines += text.leftJustified(m_width-10); + } + } + } + + Utils::printLineEditInfo(lines); + ui->updateStatus->setText(lines.join('\n').trimmed()); + scrollDownTextEdit(); +} + +void MainWindow::onShowMessage(QString title, QString text) { + this->statusBar()->clearMessage(); + this->statusBar()->showMessage( // timeout: 10000 + QString(title + " " + text).leftJustified(80, ' '), 10000); +} + +void MainWindow::onShowErrorMessage(QString title, QString text) { + onShowMessage(title, text); +} + +void MainWindow::onShowStatusMessage(QString title, QString text) { + onShowMessage(title, text); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..88f6ca1 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,83 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +#include "worker.h" +#include "update.h" +#include "update_dc_event.h" + +#define EMERGENCY_LEAVE_BL 0 + +class hwinf; +class MainWindow : public QMainWindow { + Q_OBJECT + +protected: + void customEvent(QEvent *event) override; + // bool eventFilter(QObject *obj, QEvent *ev) override; + +public: + MainWindow(hwinf *hw, Worker *worker, Update *update, QWidget *parent = nullptr); + ~MainWindow(); + + static const int START_PROGRESS_LOOP = -1; + static const int STOP_PROGRESS_LOOP = -2; + static const int BL_START_COUNT = 5; + static const int BL_CHECK_COUNT = 5; + static const int BL_IS_UP_COUNT = 5; + static const int BL_STOP_COUNT = 5; + + int progressValue() const { return m_progressValue; } + hwinf *getPlugin() { return m_hw; } + hwinf const *getPlugin() const { return m_hw; } + Update *getUpdate() { return m_update; } + Update const *getUpdate() const { return m_update; } + UpdateDcEvent::UpdateStep updateStep() const { return m_updateStep; } + void setUpdateStep(UpdateDcEvent::UpdateStep updateStep) { m_updateStep = updateStep; } + +public slots: + void onAppendText(QString, QString suffix = ""); + void onReplaceLast(QStringList, QString suffix = ""); + void onReplaceLast(QString, QString suffix = ""); + void onShowErrorMessage(QString, QString); + void onShowStatusMessage(QString, QString); + void onStopStartTimer(); + void onRestartExitTimer(); + void onEnableExit(); + void onDisableExit(); +#if EMERGENCY_LEAVE_BL==1 + void emergencyLeaveBL(); +#endif + +signals: + +#if EMERGENCY_LEAVE_BL==1 + void leaveBL(); +#endif + +private slots: + void onQuit(); + +private: + void scrollDownTextEdit(); + void onShowMessage(QString, QString); + + Ui::MainWindow *ui; + hwinf *m_hw; + Worker *m_worker; + int const m_width; + QTimer *m_startTimer; + QTimer *m_exitTimer; + bool m_progressRunning; + int m_progressValue; + Update *m_update; + UpdateDcEvent::UpdateStep m_updateStep; +}; +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..4d081d4 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,75 @@ + + + MainWindow + + + + 0 + 0 + 800 + 480 + + + + + Source Code Pro + + + + MainWindow + + + + + + 10 + 10 + 781 + 441 + + + + + + + Exit + + + + + + + 1 + + + + + + + true + + + + Misc Fixed + 11 + true + + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + + + + + + + + diff --git a/message_handler.cpp b/message_handler.cpp index f904a4a..996bd2f 100755 --- a/message_handler.cpp +++ b/message_handler.cpp @@ -2,14 +2,17 @@ #include #include +#include +#include +#include -#define OUTPUT_LEN (512) +static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " }; static bool installedMsgHandler = false; -static QtMsgType debugLevel = QtInfoMsg; +static int debugLevel = LOG_NOTICE; -QtMsgType getDebugLevel() { return debugLevel; } -void setDebugLevel(QtMsgType newDebugLevel) { +int getDebugLevel() { return debugLevel; } +void setDebugLevel(int newDebugLevel) { debugLevel = newDebugLevel; } @@ -42,82 +45,51 @@ QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) { /// #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - static constexpr const char *format = "hh:mm:ss"; - // static constexpr const char *format = "dd.MM.yyyy hh:mm:ss"; - QByteArray localMsg = msg.toLocal8Bit(); - const char *file = context.file ? context.file : ""; - const char *function = context.function ? context.function : ""; - const char *p = std::strstr(function, "::"); - if (p) { - function = p + 2; - } - char const* output = std::strrchr(file, '/'); - if (output) { - file = output + 1; - } - qint64 const currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); - int const fractional_part = currentMSecsSinceEpoch % 1000; - char buf[OUTPUT_LEN]{}; - memset(buf, 0x00, sizeof(buf)); - QDateTime const datetime = QDateTime::fromMSecsSinceEpoch(currentMSecsSinceEpoch); - switch (type) { - case QtDebugMsg: { - if (debugLevel == QtDebugMsg) { - snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d DEBG %s\n", - function, file, context.line, - datetime.time().toString(format).toStdString().c_str(), - fractional_part, - localMsg.constData()); - fprintf(stderr, "%s\n", buf); + Q_UNUSED(context); + QString const localMsg = QString(DBG_NAME[type]) + msg.toLocal8Bit(); + + switch (debugLevel) { + case LOG_DEBUG: { // debug-level message + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } break; + case LOG_INFO: { // informational message + if (type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); } } break; - case QtInfoMsg: { - if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg) { - snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d INFO %s\n", - function, file, context.line, - datetime.time().toString(format).toStdString().c_str(), - fractional_part, - localMsg.constData()); - fprintf(stderr, "%s\n", buf); + case LOG_NOTICE: { // normal, but significant, condition + if (type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); } } break; - case QtWarningMsg: { - if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg || debugLevel == QtWarningMsg) { - snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d WARN %s\n", - function, file, context.line, - datetime.time().toString(format).toStdString().c_str(), - fractional_part, - localMsg.constData()); - fprintf(stderr, "%s\n", buf); + case LOG_WARNING: { // warning conditions + if (type != QtInfoMsg && type != QtDebugMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); } } break; - case QtCriticalMsg: { - if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg - || debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg) { - snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d CRIT %s\n", - function, file, context.line, - datetime.time().toString(format).toStdString().c_str(), - fractional_part, - localMsg.constData()); - fprintf(stderr, "%s\n", buf); + case LOG_ERR: { // error conditions + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); } } break; - case QtFatalMsg: { - if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg - || debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg - || debugLevel == QtFatalMsg) { - snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d FATAL %s\n", - function, file, context.line, - datetime.time().toString(format).toStdString().c_str(), - fractional_part, - localMsg.constData()); - fprintf(stderr, "%s\n", buf); + case LOG_CRIT: { // critical conditions + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_ALERT: { // action must be taken immediately + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); + } + } break; + case LOG_EMERG: { // system is unusable + if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) { + syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str()); } } break; default: { - fprintf(stderr, "%*.*s.%03d No ErrorLevel defined! %s\n", OUTPUT_LEN, OUTPUT_LEN, - datetime.time().toString(format).toStdString().c_str(), fractional_part, - msg.toStdString().c_str()); + //fprintf(stderr, "%s No ErrorLevel defined! %s\n", + // datetime.toStdString().c_str(), msg.toStdString().c_str()); } } } diff --git a/message_handler.h b/message_handler.h index 18e923b..98c4d7e 100755 --- a/message_handler.h +++ b/message_handler.h @@ -2,9 +2,12 @@ #define MESSAGE_HANDLER_H_INCLUDED #include +#ifdef __linux__ +#include +#endif -QtMsgType getDebugLevel(); -void setDebugLevel(QtMsgType newDebugLevel); +int getDebugLevel(); +void setDebugLevel(int newDebugLevel); bool messageHandlerInstalled(); QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler); diff --git a/plugins/interfaces.h b/plugins/interfaces.h index 288d533..d9f31b6 100644 --- a/plugins/interfaces.h +++ b/plugins/interfaces.h @@ -3,7 +3,7 @@ #include -#define THIS_IS_CA_MASTER + struct T_emp { @@ -128,28 +128,24 @@ struct T_vaultRecord uint32_t AbsReserve; uint32_t AbsNrOfCuts; -//16 char label3buffer[4]; // mw > - // Verkauf, Tür zu: - uint32_t VKcoinsInserted[16]; // nur für Wechsler, soviel wurde eingeworfen - uint32_t VKcoinsReturned[6]; // nur für Wechsler, Anzahl Münzen pro Typ, soviel wurde zurückgegeben -//88 + // Verkauf, Tuer zu: + uint32_t VKcoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen + uint32_t VKcoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben - // Service, Tür offen: - uint16_t ServCoinsInserted[16]; // nur für Wechsler, soviel wurde eingeworfen - uint16_t ServCoinsReturned[6]; // nur für Wechsler, Anzahl Münzen pro Typ, soviel wurde zurückgegeben + // Service, Tuer offen: + uint16_t ServCoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen + uint16_t ServCoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben uint16_t resint3; uint16_t resint4; - uint16_t currentTubeContent[6]; // nur für Wechsler, aktueller Füllstand + uint16_t currentTubeContent[6]; // nur fuer Wechsler, aktueller Fuellstand uint16_t resint5; uint16_t resint6; -// 56 char label4buffer[4]; // box> uint16_t coinsInVault[16]; uint16_t billsInStacker[8]; -// 48 char label5buffer[4]; // val> // actually constant unless exchange rate is changed @@ -159,11 +155,17 @@ struct T_vaultRecord uint16_t exchangeRate; uint16_t resint9; -// 64 + // new from 1.8.23 + uint32_t cutsSinceCBchange; + uint32_t CBcontent_cent; + uint32_t CBnrofCoins; - char endofblock[4]; // end> + char endofblock[4]; // end +// 332 bytes -// 316 byte Block im Speicher + uint16_t CRC16; // Xmodem16 from startbuffer[0] to endofblock[3] + uint16_t resint11; + char endofRecord[4]; // ---- }; @@ -201,7 +203,7 @@ struct T_moduleCondition uint8_t temper; uint8_t poweronTest; - uint8_t doorState; // 1: alles zu 200: t?r offen + bit1(S) +bit2(CB) + bit3(CB) + uint8_t doorState; // 1: alles zu 200: tuer offen + bit1(S) +bit2(CB) + bit3(CB) uint8_t doorWasOpened; // 1: all doors are closed 200: any door was just opened uint8_t changer; // can only be tested by usage @@ -315,9 +317,10 @@ struct T_devices // set by master, used(1) or notused (0) or type 2....20 UCHAR kindOfPrinter; // 0:off 1:Gebe - UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza c² (MW) + UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza Csquare (MW) UCHAR kindOfMifareReader; // by now only stronglink SL025 =1 - UCHAR suppressSleepMode; // 0:sleep allowed 1: no sleep + UCHAR solarPower; // 1:sleep allowed 0: no sleep + //UCHAR suppressSleepMode; // 0:sleep allowed 1: no sleep UCHAR kindOfModem; // 0:off 1:Sunlink UCHAR kindOfCreditcard; // 0:off 1:Feig NFC @@ -336,6 +339,8 @@ struct T_devices UINT VaultFullWarnLevel; UINT VaultFullErrorLevel; + UINT BattEmptyWarnLevel; + UINT BattEmptyErrorLevel; }; @@ -357,7 +362,7 @@ public: // Furthermore the Cashagent-Library answers with status strings about sending and reading result // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ -#ifdef THIS_IS_CA_MASTER + virtual bool dc_openSerial(int BaudNr, QString BaudStr, QString ComName, uint8_t connect) const =0; // Command: open serial interface // BaudNr: 0:1200 1:9600 2:19200 3:38400 4:57600 5:115200 @@ -368,7 +373,7 @@ public: virtual void dc_closeSerial(void) const =0; // Command: close serial interface in order to save power while power down // or if another port must be used -#endif + virtual bool dc_isPortOpen(void) const =0; // returns true if port open (don't send unless open. Sending to closed port will crash program) @@ -394,7 +399,7 @@ public: // get data back in "payLoad", max 64 byte, can be used for diagnosis // retval = nr of bytes received. If host buffer too small then // only plBufSiz bytes are copied to "payLoad" - // plBufSiz­z=size of host buffer + // plBufSiz = size of host buffer virtual void dc_setWakeFrequency(uint8_t period) const =0; // RTC wakes DC2 (and PTU) by hardware signal every 32seconds @@ -403,7 +408,7 @@ public: virtual void dc_OrderToReset(void) const =0; // want DC2 to reset (in order to start Bootloader) -#ifdef THIS_IS_CA_MASTER + virtual QString dc_getSerialState(void) const =0; // get result of opening-command like "ttyS0 opened with 115200 8N1! // or error messages like "comport not available..." @@ -411,7 +416,7 @@ public: virtual void dc_clrSerialStateText(void) const =0; // clear above text to avoid multiple repetive displaying -#endif + virtual void bl_sendDataDirectly(uint8_t length, uint8_t *buf) const =0; // send without protocol frame, needed for the DC bootloader @@ -496,10 +501,10 @@ public: // Analog values: virtual uint32_t dc_getTemperature(void) const =0; - // in Sax-Format 0...400 (0=-50,0°C 100=0,0°C 141=20,5°C 400=150,0°C) + // in Sax-Format 0...400 (0=-50,0degC 100=0,0degC 141=20,5degC 400=150,0degC) virtual QString dc_getTemperaturStr(void) const =0; - // as string like "-12,5°C" + // as string like "-12,5degC" virtual uint32_t dc_getVoltage(void) const =0; // as value in mV, 0...65,535V @@ -760,7 +765,7 @@ public: uint8_t kindOfModem, uint8_t kindOfCredit ) const =0; // enable hardware in device controller: // kindOfPrinter: 0:off 1: GPT4672 (only this one implemented) - // kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: C²_changer + // kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: Csquare_changer // kindOfMifareReader: 0:off 1: SL025 (only this one implemented) // suppressSleep: 0:sleep allowed 1: sleep surpressed for special reason // kindOfModem: 0:off 1: ATB_Sunlink_LTE (not yet implemented) @@ -886,7 +891,7 @@ public: // send 5 byte: byte 0,1: speed 5...250 mm/s // byte2: density 0....(25)....50 // byte3: alignment 'l', 'c', 'r' = left, center, right - // byte4: orientation 0, 90, 180 = 0°, 90°, 180° rotation (by now not supported!) + // byte4: orientation 0, 90, 180 = 0deg, 90deg, 180deg rotation (by now not supported!) // not batched! don't use twice within 100ms virtual void prn_movePaper(uint8_t wayInMm, uint8_t direction) const =0; @@ -1018,7 +1023,7 @@ public: // use for changer -#ifdef THIS_IS_CA_MASTER + virtual QString dc_getTxt4RsDiagWin(void) const =0; virtual void dc_clrTxt4RsDiagWin(void) const =0; virtual QString dc_get2ndTxt4RsDiagWin(void) const =0; @@ -1033,7 +1038,6 @@ public: virtual void dc_clrTxt4dataStateLine(void) const =0; virtual QString dc_getdatifLine(void) const =0; virtual void dc_clrTxt4datifLine(void) const =0; -#endif @@ -1152,14 +1156,13 @@ public: // to be forwarded to Ismas virtual bool prn_printAccountReceipt(void) const =0; + // print all 8 backuped accounting receipts // return true if sending to DC OK, false if cmd-stack is full virtual bool prn_printTestTicket(void) const =0; // return true if sending to DC OK, false if cmd-stack is full - - virtual bool cash_startPayment(uint32_t amount) const =0; // 17.4.23TS: extended to 32bit @@ -1178,7 +1181,7 @@ public: virtual uint16_t getLastInsertedCoin(void) const =0; virtual bool getAllInsertedCoins(uint16_t *types, uint16_t *values) const =0; - // alle bei diesem Verkauf eingeworfenen Münzen sind gespeichert, max 64 + // alle bei diesem Verkauf eingeworfenen Muenzen sind gespeichert, max 64 virtual bool cash_cancelPayment(void) const =0; @@ -1232,6 +1235,8 @@ public: virtual uint8_t prn_getPrintResult() const =0; + + virtual uint8_t prn_getCurrentPrinterState() const =0; // 0: printer OK // bit0: near paper end bit1: no paper @@ -1244,6 +1249,8 @@ public: virtual void sys_sendDeviceParameter(struct T_devices *deviceSettings) const =0; virtual void sys_restoreDeviceParameter(struct T_devices *deviceSettings) const =0; + // attention: only applies if function "sys_sendDeviceParameter()" was used to send this settings before + // cannot be used to see settings programmed by JsonFile virtual bool sys_areDCdataValid(void) const =0; @@ -1268,6 +1275,71 @@ public: virtual bool dc_isAutoRequestOn(void) const =0; + virtual uint16_t log_getLatestAccountNumber(void) const=0; + // new function 27.6.2023 + // latest = highest of the backup's + + virtual uint8_t log_getAvailableVaultBlocks(void) const=0; + // return 0x0011 1111 if all 6 blocks are loaded (one bit per block) + + virtual uint8_t log_getAnswerToLastSlaveRequest(void) const =0; + // use only for ONE request/command + // return: 0xFF: result unknown by now as sending is ongoing + // 0=OK + // 1= wrong length 2=wrong start sign 5= wrong crc + // 6= slave: master cmd was wrong 7: slave: could not write/read data + // 8=timeout, got no response from slave + + + // use for important and extended commands (print several templates, print ticket...) + virtual void log_startSupervision(void) const =0; + + virtual uint8_t log_getAnswerToLastCmdBatch(void) const =0; + // 0xFF: no command sent by now + // 0: started, in progress + // 1: done and OK + // 2: done and error + + virtual bool log_getVaultData(uint8_t *data) const =0; + // get vault record in linear 8bit buffer with 384 byte + + + + // new from 1.8.23 + virtual bool prn_printOneAccountReceipt(uint16_t accountNr) const =0; + // print one out of eight stored last accounting receipts + // function log_getHoldAccountNumbers() gives a list of acc-Nr. of the stored receipts + + virtual bool prn_printAllAvailAccountReceipts(void) const =0; + // same as: prn_printAccountReceipt() from line 1153 + // return true if sending to DC OK, false if cmd-stack is full + + virtual bool log_verifyVaultRecordByCrc(void) const =0; + // return true if CRC16 is correct, data are 100% OK. Security level 1:65536 + // verification is strongly recommended before further processing + // in case of "false"-result please reload from DC + + + virtual uint16_t log_DC_getNextAccountNumber(void) const=0; + // the current cash box content will be backuped with this number on next cashbox-change + + virtual void log_DC_setNextAccountNumber(uint16_t newAccountingNumber) const=0; + // the current cash box content will be backuped with this number on next cashbox-change + // use only in case of hardware replacements or errors which derailed the number + + virtual void log_DC_deleteAllVaultrecordsInDc(void) const=0; + // use only in case of hardware replacements or errors which derailed the number + + virtual void log_DC_deleteAllTotalCounters(void) const=0; + // use only in case of hardware replacements or errors which derailed the number + + virtual void dc_setNewCustomerNumber(uint16_t newCustNr) const =0; + + virtual void dc_setNewMachineNumber(uint16_t newMachNr) const =0; + + virtual void dc_setNewBorough(uint16_t newBorough) const =0; + + virtual void dc_setNewZone(uint16_t newZone) const =0; @@ -1326,6 +1398,10 @@ signals: // 15.06.2023 V4.2 bring into same order as hwapi in order to set the THIS_IS_CA_MASTER correct // 19.06.2023 V4.3 added some qCriticals to see emits +// 01.08.2023 V4.4 some new values at the end of struct T_vaultRecord +// two more values in struct T_devices +// 7 new functions at the end of the file + //#define HWINF_iid "Atb.Psa2020.software.HWapi/3.1" //#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.1" @@ -1336,7 +1412,8 @@ signals: //#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.0" //#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.1" //#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.2" -#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.3" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.3" +#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.4" diff --git a/plugins/libCAmaster.so b/plugins/libCAmaster.so old mode 100644 new mode 100755 index 6aaaefb..eafb659 Binary files a/plugins/libCAmaster.so and b/plugins/libCAmaster.so differ diff --git a/process/command.cpp b/process/command.cpp new file mode 100644 index 0000000..22b03a4 --- /dev/null +++ b/process/command.cpp @@ -0,0 +1,104 @@ +#include "command.h" + +#include +#include +#include +#include + +Command::Command(QString const &command, int start_timeout, int finish_timeout) + : m_command(command.trimmed()) + , m_commandResult("") + , m_waitForStartTimeout(start_timeout) + , m_waitForFinishTimeout(finish_timeout) + , m_exitCode(-1) { +} + +QString Command::getCommandResult() const { + return m_commandResult; +} + +void Command::readyReadStandardOutput() { + QProcess *p = (QProcess *)sender(); + m_commandResult += p->readAllStandardOutput(); + // qCritical() << m_commandResult; +} + +void Command::readyReadStandardError() { + QProcess *p = (QProcess *)sender(); + QByteArray buf = p->readAllStandardError(); + qCritical() << buf; +} + +void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { + QProcess *p = (QProcess *)sender(); + // read all remaining data sent to the process, just in case + QString d = p->readAllStandardOutput(); + if (!d.isEmpty()) { + m_commandResult += d; + } + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput())); + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError())); +} + +bool Command::execute(QString workingDirectory, QStringList args) { + + if (!QDir::setCurrent(workingDirectory)) { + qCritical() << "SET WORKING_DIRECTORY" << workingDirectory + << "FAILED FOR" << m_command; + return false; + } + + QScopedPointer p(new QProcess(this)); + p->setWorkingDirectory(workingDirectory); + p->setProcessChannelMode(QProcess::MergedChannels); + + connect(&(*p), SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput())); + connect(&(*p), SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError())); + + if (!args.isEmpty()) { + qDebug() << "START COMMAND" << m_command << "WITH ARGS" << args + << "IN" << p->workingDirectory(); + p->start(m_command, args); + } else { + qDebug() << "START COMMAND" << m_command + << "IN" << p->workingDirectory(); + p->start(m_command); + } + + if (p->waitForStarted(m_waitForStartTimeout)) { + qDebug() << "PROCESS" << m_command << "STARTED IN" << p->workingDirectory(); + if (p->state() == QProcess::ProcessState::Running) { + qDebug() << "PROCESS" << m_command << "RUNNING IN" << p->workingDirectory(); + if (p->waitForFinished(m_waitForFinishTimeout)) { + qDebug() << "PROCESS" << m_command << "FINISHED IN" << p->workingDirectory(); + if (p->exitStatus() == QProcess::NormalExit) { + if ((m_exitCode = p->exitCode()) == 0) { + qDebug() << "EXECUTED" << m_command + << "with code" << m_exitCode + << "IN" << p->workingDirectory(); + return true; + } else { + qCritical() << "EXECUTED" << m_command + << "with code" << m_exitCode + << "IN" << p->workingDirectory(); + } + } else { + qCritical() << "PROCESS" << m_command << "CRASHED with code" + << p->exitCode() + << "IN" << p->workingDirectory(); + } + } else { + qCritical() << "PROCESS" << m_command + << "DID NOT FINISH" + << "IN" << p->workingDirectory(); + } + } else { + qCritical() << "WRONG PROCESS STATE" << p->state() + << "IN" << p->workingDirectory(); + } + } else { + qCritical() << "PROCESS" << m_command << "TIMEOUT AT START" + << "IN" << p->workingDirectory(); + } + return false; +} diff --git a/process/command.h b/process/command.h new file mode 100644 index 0000000..bc796ee --- /dev/null +++ b/process/command.h @@ -0,0 +1,35 @@ +#ifndef COMMAND_H_INCLUDED +#define COMMAND_H_INCLUDED +#endif // COMMAND_H_INCLUDED + +#include +#include +#include +#include +#include + + +class Command : public QObject { + Q_OBJECT + + QString m_command; + QString m_commandResult; + int m_waitForStartTimeout; + int m_waitForFinishTimeout; + int m_exitCode; +public: + explicit Command(QString const &command, + int start_timeout = 100000, + int finish_timeout = 100000); + + QString getCommandResult() const; + QString command() const { return m_command; } + + bool execute(QString workingDirectory, QStringList args = QStringList()); + int exitCode() const { return m_exitCode; } + +private slots: + void readyReadStandardOutput(); + void readyReadStandardError(); + void finished(int exitCode, QProcess::ExitStatus exitStatus); +}; diff --git a/progress_event.cpp b/progress_event.cpp new file mode 100644 index 0000000..8cbf0a0 --- /dev/null +++ b/progress_event.cpp @@ -0,0 +1,20 @@ +#include "progress_event.h" + +QEvent::Type ProgressEvent::customEventType = QEvent::None; + +ProgressEvent::ProgressEvent(QObject const *sender, int progressPercent) + : QEvent(ProgressEvent::type()) + , m_sender(sender) + , m_progressPercent(progressPercent) { +} + +ProgressEvent::~ProgressEvent() { +} + +QEvent::Type ProgressEvent::type() { + if (customEventType == QEvent::None) { + int generatedType = QEvent::registerEventType(); + customEventType = static_cast(generatedType); + } + return customEventType; +} diff --git a/progress_event.h b/progress_event.h new file mode 100644 index 0000000..e8bd4be --- /dev/null +++ b/progress_event.h @@ -0,0 +1,26 @@ +#ifndef PROGRESS_EVENT_H_INCLUDED +#define PROGRESS_EVENT_H_INCLUDED + +#include + +class ProgressEvent : public QEvent { + + QObject const *m_sender; + int m_progressPercent; +public: + explicit ProgressEvent(QObject const *sender, int progressPercent); + virtual ~ProgressEvent(); + static QEvent::Type type(); + + QObject const *sender() { return m_sender; } + QObject const *sender() const { return m_sender; } + + void setProgress(int progressPercent) { m_progressPercent = progressPercent; } + int progressPercent() { return m_progressPercent; } + int progressPercent() const { return m_progressPercent; } +private: + static QEvent::Type customEventType; +}; + + +#endif // PROGRESS_EVENT_H_INCLUDED diff --git a/update.cpp b/update.cpp index ba1274d..66d9770 100644 --- a/update.cpp +++ b/update.cpp @@ -1,4 +1,8 @@ #include "update.h" +#include "worker.h" +#include "utils.h" +#include "update_dc_event.h" +#include "mainwindow.h" #include #include @@ -7,35 +11,37 @@ #include #include #include +#include +#include + +#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX) +#include "unistd.h" +#endif #include "plugins/interfaces.h" #include #include -#include #include #include #include #include #include -#define COLUMN_REQUEST (0) -#define COLUMN_NAME (1) -#define COLUMN_DATE_TIME (2) -#define COLUMN_RESULT (3) - #define UPDATE_OPKG (1) -#define UPDATE_DC (0) -#define UPDATE_PRINTER_TEMPLATES (0) -#define UPDATE_CASH_TEMPLATE (0) -#define UPDATE_CONF_TEMPLATE (0) -#define UPDATE_DEVICE_TEMPLATE (0) +#define UPDATE_DC (1) +#define UPDATE_PRINTER_TEMPLATES (1) +#define UPDATE_CASH_TEMPLATE (1) +#define UPDATE_CONF_TEMPLATE (1) +#define UPDATE_DEVICE_TEMPLATE (1) static const QMap baudrateMap = { {"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3}, {"57600" , 4}, {"115200" , 5} }; +QPluginLoader Update::pluginLoader; + hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { hwinf *hw = nullptr; if (plugInDir.exists()) { @@ -44,7 +50,8 @@ hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { QFileInfo info(pluginLibName); if (info.exists()) { pluginLibName = plugInDir.absoluteFilePath(pluginLibName); - static QPluginLoader pluginLoader(pluginLibName); + pluginLoader.setFileName(pluginLibName); + // static QPluginLoader pluginLoader(pluginLibName); if (!pluginLoader.load()) { qCritical() << "in directory" << plugInDir.absolutePath(); qCritical() << "cannot load plugin" << pluginLoader.fileName(); @@ -76,86 +83,56 @@ hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { return hw; } +bool Update::unloadDCPlugin() { + if (pluginLoader.unload()) { + qCritical() << "unloaded plugin" << pluginLoader.fileName(); + // Note: will re-instantiate the library ! + // QObject *rootObject = pluginLoader.instance(); + // if (rootObject) { + // qCritical() << "reloaded plugin: root object again available"; + // return false; + // } + // qCritical()unloaded plugin: root object gone"; + return true; + } + return false; +} + Update::Update(hwinf *hw, - QString update_ctrl_file, + Worker *worker, + QString customerRepository, + QString customerNrStr, + QString branchName, + QString pluginName, QString workingDir, - bool maintenanceMode, + bool dryRun, QObject *parent, char const *serialInterface, char const *baudrate) : QObject(parent) , m_hw(hw) + , m_worker(worker) , m_serialInterface(serialInterface) , m_baudrate(baudrate) - , m_update_ctrl_file(update_ctrl_file) - , m_update_ctrl_file_copy(update_ctrl_file + ".copy") + , m_customerRepository(customerRepository) + , m_customerNrStr(customerNrStr) + , m_branchName(branchName) + , m_pluginName(pluginName) , m_workingDir(workingDir) - , m_maintenanceMode(maintenanceMode) - , m_init(true) { + , m_dryRun(dryRun) { - execUpdateScript(); - - if (!m_update_ctrl_file.exists()) { - qCritical() << "Update-file" << m_update_ctrl_file.fileName() - << "does not exist"; - m_init = false; - } - if (!m_update_ctrl_file_copy.exists()) { - qCritical() << "Update-file-copy" << m_update_ctrl_file_copy.fileName() - << "does not exist"; - m_init = false; - } - if (!m_update_ctrl_file.open(QIODevice::ReadWrite | QIODevice::Text)) { - qCritical() << "can not open " << m_update_ctrl_file.fileName(); - m_init = false; - } - qDebug() << "Opened" << m_update_ctrl_file.fileName(); - if (!m_update_ctrl_file_copy.open(QIODevice::ReadWrite | QIODevice::Text)) { - qCritical() << "can not open " << m_update_ctrl_file_copy.fileName(); - m_init = false; - } - qDebug() << "Opened" << m_update_ctrl_file_copy.fileName(); + qInfo() << "UPDATE: m_serialInterface ..." << m_serialInterface; + qInfo() << "UPDATE: m_baudrate ..." << m_baudrate; + qInfo() << "UPDATE: m_customerRepository ..." << m_customerRepository; + qInfo() << "UPDATE: m_customerNr ..........." << m_customerNrStr; + qInfo() << "UPDATE: m_branchName ..........." << m_branchName; + qInfo() << "UPDATE: m_pluginName ..........." << m_pluginName; + qInfo() << "UPDATE: m_workingDirectory ....." << m_workingDir; } Update::~Update() { } -bool Update::execUpdateScript() { - // path of update-script 'update_psa' - QString update_psa("/opt/app/tools/atbupdate/update_psa "); - if (m_maintenanceMode) { - update_psa += " -m "; - } - update_psa += " --wdir "; - update_psa += m_workingDir; - - qCritical() << "update_psa: " << update_psa; - - QScopedPointer p(new QProcess(this)); - p->setProcessChannelMode(QProcess::MergedChannels); - - p->start(update_psa); - if (p->waitForStarted(1000)) { - if (p->state() == QProcess::ProcessState::Running) { - int const timeout = 200000; // sometimes signal strength of modem is quite low - if (p->waitForFinished(timeout)) { - QString output = p->readAllStandardOutput().toStdString().c_str(); - QStringList lst = output.split('\n'); - for (int i = 0; i < lst.size(); ++i) { - qDebug() << lst[i]; - } - qInfo() << "EXECUTED" << update_psa; - return ((p->exitStatus() == QProcess::NormalExit) - && (p->exitCode() == 0)); - } else { - qCritical() << "update-script TIMEDOUT after" - << timeout/1000 << "seconds"; - } - } - } - return false; -} - Update::DownloadResult Update::sendStatus(int ret) const { switch (ret) { // return values of dc are: case 0: // 0: no answer by now @@ -172,10 +149,10 @@ Update::DownloadResult Update::sendNextAddress(int bNum) const { int noAnswerCount = 0; int errorCount = 0; if ( bNum==0 || bNum==1024 || bNum==2048 || bNum==3072 || bNum==4096 ) { - qDebug() << "addr-block" << bNum << "..."; + // qDebug() << "addr-block" << bNum << "..."; while (noAnswerCount <= 250) { m_hw->bl_sendAddress(bNum); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + QThread::msleep(100); DownloadResult const res = sendStatus(m_hw->bl_wasSendingAddOK()); if (res != DownloadResult::NOP) { if (res == DownloadResult::ERROR) { @@ -184,7 +161,7 @@ Update::DownloadResult Update::sendNextAddress(int bNum) const { return res; } } else { // res == DownloadResult::OK - qInfo() << "addr-block" << bNum << "...OK"; + // qInfo() << "addr-block" << bNum << "...OK"; return res; } } else { @@ -208,16 +185,12 @@ Update::DownloadResult Update::sendNextDataBlock(QByteArray const &binary, memcpy(local, binary.constData() + bAddr, 64); local[64] = local[65] = 0x00; - //for (int i=0; i<4; ++i) { - // printf("%04d ", bNum); - // for (int j=0; j < 16; ++j) { - // printf("%02x ", local[i*16 + j]); - // } printf("\n"); - //} + // QByteArray b((const char *)(&local[0]), 64); + // qCritical() << "SNDB" << bNum << b.size() << b.toHex(); while (noAnswerCount <= 250) { m_hw->bl_sendDataBlock(64, local); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + QThread::msleep(10); DownloadResult const res = sendStatus(m_hw->bl_wasSendingDataOK()); if (res != DownloadResult::NOP) { if (res == DownloadResult::ERROR) { @@ -226,7 +199,7 @@ Update::DownloadResult Update::sendNextDataBlock(QByteArray const &binary, return res; } } else { - qInfo() << "data for block" << bNum << "OK"; + // qInfo() << "data for block" << bNum << "OK"; return res; } } else { @@ -240,105 +213,160 @@ Update::DownloadResult Update::sendNextDataBlock(QByteArray const &binary, Update::DownloadResult Update::dc_downloadBinary(QByteArray const &b) const { int const nBlocks = (((b.size())%64)==0) ? (b.size()/64) : (b.size()/64)+1; - qInfo() << "total number of bytes to send to dc" << b.size(); + // fill lst block of data to be sent with 0xFF + QByteArray ba = b.leftJustified(nBlocks*64, (char)(0xFF)); + + qInfo() << "total number of bytes to send to dc" << ba.size(); qInfo() << "total number of blocks to send to dc" << nBlocks; int bNum = 0; DownloadResult res = DownloadResult::OK; + fprintf(stderr, "\n64-byte block %04d ", bNum); while (res != DownloadResult::ERROR && bNum < nBlocks) { if ((res = sendNextAddress(bNum)) != DownloadResult::ERROR) { - if ((res = sendNextDataBlock(b, bNum)) != DownloadResult::ERROR) { + if ((res = sendNextDataBlock(ba, bNum)) != DownloadResult::ERROR) { bNum += 1; + fprintf(stderr, "."); + if ((bNum % 80) == 0) { + fprintf(stderr, "\n64-byte block %04d ", bNum); + } } } } - qInfo() << "nBlocks" << nBlocks; - //if (res != DownloadResult::ERROR) { - // always send last block, even when there are no data !!! - int const rest = b.size() % 64; - int const offset = b.size() - rest; - char const *startAddress = b.constData() + offset; + fprintf(stderr, "\nlast 64-byte block %04d\n", bNum); + + int const rest = ba.size() % 64; + int const offset = ba.size() - rest; + char const *startAddress = ba.constData() + offset; - uint8_t local[66]; - memset(local, 0x00, sizeof(local)); if (rest > 0) { + // SHOULD NEVER HAPPEN !!! + uint8_t local[66]; + memset(local, 0xFF, sizeof(local)); memcpy(local, startAddress, rest); + qCritical() << "ERROR SEND REMAINING" << rest << "BYTES"; + m_hw->bl_sendDataBlock(64, local); } - //for (int i=0; i<4; ++i) { - // printf("*** %04d ", bNum); - // for (int j=0; j < 16; ++j) { - // printf("%02x ", local[i*16 + j]); - // } printf("\n"); - //} - - // bl_sendLastBlock(local); m_hw->bl_sendLastBlock(); qInfo() << "last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK()); return res; } -bool Update::startBootloader() const { - qDebug() << "starting bootloader..."; - int nTry = 5; - while (--nTry >= 0) { +bool Update::startBootloader() const { // deprecated + return false; +#if 0 + int nStartTry = 5; + while (--nStartTry >= 0) { m_hw->bl_startBL(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - m_hw->bl_checkBL(); - if (m_hw->bl_isUp()) { - qInfo() << "starting bootloader...OK"; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - return true; - } else { - qCritical() << "bootloader not up (" << nTry << ")"; + QThread::msleep(500); + int nCheckTry = 10; + while (--nCheckTry >= 0) { + m_hw->bl_checkBL(); + QThread::msleep(500); + if (m_hw->bl_isUp()) { + qInfo() << "starting bootloader...OK"; + return true; + } else { + qCritical() << "bootloader not up (" + << nStartTry << "," << nCheckTry << ")" << QThread::currentThread(); + } } } - qCritical() << "starting bootloader...FAILED"; + qCritical() << "starting bootloader...FAILED" << QThread::currentThread(); return false; +#endif } bool Update::stopBootloader() const { - qDebug() << "stopping bootloader..."; - int nTry = 5; - while (--nTry >= 0) { - m_hw->bl_stopBL(); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - if (!m_hw->bl_isUp()) { - qInfo() << "stopping bootloader...OK"; - return true; + // stop bootloader: this MUST work -> otherwise the PSA has to be restarted + // manually + emit m_worker->showErrorMessage("dc update", "stopping bootloader..."); + + int nTryFinalize = 1; // could do this in an endless loop + + do { + // in principle, any value except BL_STOP will do, as we want to detect + // change to BL_STOP + m_worker->mainWindow()->setUpdateStep(UpdateDcEvent::UpdateStep::BL_CHECK); + + QApplication::postEvent( + m_worker->mainWindow(), + new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_STOP, nTryFinalize)); + + QThread::sleep(1); + + int const cntLimit = 20; + int cnt = 0; + while (++cnt < cntLimit && + m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_STOP) { + // wait until bl_stopBL() has been sent + QThread::msleep(500); } - } - qCritical() << "stopping bootloader...FAILED"; - return false; + + QApplication::postEvent( + m_worker->mainWindow(), + new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_CHECK, nTryFinalize)); + QThread::sleep(1); + + QApplication::postEvent( + m_worker->mainWindow(), + new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_IS_UP, nTryFinalize)); + QThread::sleep(1); + + cnt = 0; + while (++cnt < cntLimit && + m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_IS_DOWN) { + // wait until done + QThread::msleep(200); + } + + } while (++nTryFinalize <= MainWindow::BL_STOP_COUNT && + m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_IS_DOWN); + + return (m_worker->mainWindow()->updateStep() == UpdateDcEvent::UpdateStep::BL_IS_DOWN); } // br is a index into a table, used for historical reasons. bool Update::openSerial(int br, QString baudrate, QString comPort) const { qDebug() << "opening serial" << br << baudrate << comPort << "..."; - if (m_hw->dc_openSerial(br, baudrate, comPort, 1)) { // 1 for connect - qInfo() << "opening serial" << br << baudrate << comPort << "...OK"; + if (m_hw->dc_openSerial(br, baudrate, comPort, 1) == true) { // 1 for connect + Utils::printInfoMsg( + QString("OPENING SERIAL %1").arg(br) + + " " + baudrate + " " + comPort + "...OK"); + + // m_hw->dc_autoRequest(true); + m_hw->dc_autoRequest(false); + QThread::sleep(1); + + Utils::printInfoMsg(QString("IS PORT OPEN %1").arg(m_hw->dc_isPortOpen())); return true; } - qCritical() << "opening serial" << br << baudrate << comPort << "...FAILED"; + + Utils::printCriticalErrorMsg( + QString("OPENING SERIAL %1").arg(br) + + " " + baudrate + " " + comPort + "...FAILED"); return false; } void Update::closeSerial() const { + qInfo() << "CLOSED SERIAL" << m_baudrate << m_serialInterface; m_hw->dc_closeSerial(); } -bool Update::resetDeviceController() const { +bool Update::isSerialOpen() const { + return m_hw->dc_isPortOpen(); +} + +bool Update::resetDeviceController() const { // deprecated + return false; +#if 0 qDebug() << "resetting device controller..."; - //if (stopBootloader()) { // first stop a (maybe) running bootloader - // std::this_thread::sleep_for(std::chrono::milliseconds(1000)); m_hw->bl_rebootDC(); // wait maximally 3 seconds, before starting bootloader - QThread::msleep(1500); qInfo() << "resetting device controller...OK"; return true; - //} - //qCritical() << "stopping bootloader...FAILED"; - //return false; +#endif } QByteArray Update::loadBinaryDCFile(QString filename) const { @@ -375,43 +403,194 @@ bool Update::downloadBinaryToDC(QString const &bFile) const { return true; } +/* + + /////////////////////////////////////////////////////////////////////////////// + // + // 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. + */ bool Update::updateBinary(char const *fileToSendToDC) { - qInfo() << "updating device controller binary" << fileToSendToDC; + qInfo() << "UPDATING DEVICE CONTROLLER FIRMWARE BINARY" << fileToSendToDC; QFile fn(fileToSendToDC); bool r; if ((r = fn.exists()) == true) { - QString const linkTarget = fn.symLinkTarget(); - QFileInfo fi(linkTarget); - qInfo() << " updating binary (size=" << linkTarget << fi.size() << ")"; - if ((r = updateDC(linkTarget)) == true) { - qInfo() << " updating binary (size=" << linkTarget << fi.size() << ") done"; + QFileInfo fi(fn); + if ((r = updateDC(fileToSendToDC)) == true) { + Utils::printInfoMsg( + QString(" UPDATING BINARY ") + fi.fileName() + + QString(" (size=%1").arg(fi.size()) + ") DONE"); } else { - qCritical() << "updating binary (size=" << linkTarget << fi.size() << ")... FAILED"; + Utils::printCriticalErrorMsg( + QString(" UPDATING BINARY ") + fi.fileName() + + QString(" (size=%1").arg(fi.size()) + ") FAILED"); } } else { - qCritical() << "symlink" << fileToSendToDC - << "does not exist -> NO UPDATE OF DC FIRMWARE"; + Utils::printCriticalErrorMsg( + QString(fileToSendToDC) + " DOES NOT EXIST -> NO UPDATE OF DC FIRMWARE"); } return r; } bool Update::updateDC(QString bFile) const { - qDebug() << "updating dc..."; - qDebug() << "updating dc: file to send" << bFile; - if (!resetDeviceController()) { + qDebug() << "IN UPDATEDC: UPDATING DC: FILE TO SEND" << bFile; + + m_worker->mainWindow()->setUpdateStep(UpdateDcEvent::UpdateStep::NONE); + + QApplication::postEvent( // step 1: reset device controller + m_worker->mainWindow(), + new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::DC_REBOOT, 1)); + QThread::sleep(1); + + for (int i=1; i <= MainWindow::BL_START_COUNT; ++i) { + QApplication::postEvent( // step 2: start bootloader (5x) + m_worker->mainWindow(), + new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_START, i)); + QThread::sleep(1); + } + + int const cntLimit = 100; // wait until its for sure that bl_startBL() + int cnt = 0; // has been excuted + while (++cnt < cntLimit && + m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_START) { + // wait until all bl_startBL() are done + QThread::msleep(200); + } + + if (cnt == cntLimit) { + // start events not received ??? + Utils::printCriticalErrorMsg("BL_START EVENT NOT RECEIVED AFTER 20 SECS"); return false; } - if (!startBootloader()) { + + m_worker->mainWindow()->setUpdateStep(UpdateDcEvent::UpdateStep::BL_CHECK); + + for (int i=1; i <= MainWindow::BL_IS_UP_COUNT; ++i) { + QApplication::postEvent(m_worker->mainWindow(), new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_CHECK, i)); + QThread::sleep(1); + QApplication::postEvent(m_worker->mainWindow(), new UpdateDcEvent(m_worker, UpdateDcEvent::UpdateStep::BL_IS_UP, i)); + if (m_worker->mainWindow()->updateStep() == UpdateDcEvent::UpdateStep::BL_IS_UP) { + break; + } + QThread::sleep(1); + } + + cnt = 0; + while (++cnt < cntLimit && + m_worker->mainWindow()->updateStep() != UpdateDcEvent::UpdateStep::BL_IS_UP) { + // wait until all bl_startBL() are done + QThread::msleep(200); + } + + if (cnt == cntLimit) { + // really not up + Utils::printCriticalErrorMsg("BL_IS_UP EVENT NOT RECEIVED AFTER 20 SECS"); + stopBootloader(); // try to stop bootloader whichhas been already started return false; } - if (!downloadBinaryToDC(bFile)) { - stopBootloader(); - qCritical() << "updating dc: " << bFile << "...FAILED"; + + if (m_worker->mainWindow()->updateStep() == UpdateDcEvent::UpdateStep::BL_IS_UP) { + // bootloader MUST be running to download device-controller +#if 0 + if (!downloadBinaryToDC(bFile)) { + Utils::printCriticalErrorMsg( + QString("UPDATING DC: ") + bFile + " ...DOWNLOAD FAILED"); + } +#endif + + } else { + Utils::printCriticalErrorMsg( + QString("UPDATING DC: ") + bFile + " BOOT LOADER NOT RUNNING -> NO DOWNLOAD (" + + QThread::currentThread()->objectName() + ")"); return false; } - qInfo() << "updating dc: " << bFile << "...OK"; - stopBootloader(); - QThread::sleep(3); + + // do this unconditionally, even if bootloader is not running at all -> + // the controller possibly tells us nonsense. + if (!stopBootloader()) { + Utils::printCriticalErrorMsg( + QString("UPDATING DC: ") + bFile + " BOOT LOADER STILL RUNNING (" + + QThread::currentThread()->objectName() + ")"); + return false; + } + + Utils::printInfoMsg(QString("UPDATING DC: ") + bFile + " ...OK"); return true; } @@ -499,22 +678,6 @@ bool Update::updateDeviceConf(QString jsFile) { return downloadJson(FileTypeJson::DEVICE, 0, jsFile); } -QStringList Update::getLinesToWorkOn() { - QStringList linesToWorkOn; - - QTextStream in(&m_update_ctrl_file); - while (!in.atEnd()) { - QString line = in.readLine().trimmed(); - if (line.startsWith("DONE")) { - m_update_ctrl_file_copy.write(line.toUtf8().constData()); - m_update_ctrl_file_copy.write("\n"); - } else { - linesToWorkOn << line; - } - } - return linesToWorkOn; -} - QStringList Update::split(QString line, QChar sep) { QStringList lst; QString next; @@ -531,200 +694,225 @@ QStringList Update::split(QString line, QChar sep) { return lst; } -bool Update::doUpdate() { - /* - The file referred to by 'update_data' has the following structure for - each line: +void Update::readyReadStandardOutput() { + QProcess *p = (QProcess *)sender(); + QByteArray buf = p->readAllStandardOutput(); + qCritical() << buf; +} - # ====================================================================== - # REQUEST | NAME | DATE | RESULT - # ====================================================================== - # where - # - # STATUS: DOWNLOAD, EXECUTE or DONE - # NAME : If starting with 'opkg' it is an opkg-command to be executed. - # Otherwise its the name of a file which has to be updated. - # DATE : 0000-00-00T00:00:00 - # RESULT: SUCCESS or ERROR (possibly with description) - # - */ +void Update::readyReadStandardError() { + QProcess *p = (QProcess *)sender(); + QByteArray buf = p->readAllStandardError(); + qCritical() << buf; +} - if (!m_init) { - return false; +void Update::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { + QProcess *p = (QProcess *)sender(); + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput())); + disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError())); +} + +QStringList Update::getDcSoftAndHardWareVersion() { + m_hw->dc_autoRequest(true); + QThread::sleep(1); // make sure the timer-slots are active + + for (int i=0; i < 3; ++i) { // send explicit reuests to get + // current SW/HW-versions + m_hw->request_DC2_SWversion(); + m_hw->request_DC2_HWversion(); + QThread::sleep(1); } - if (!openSerial(baudrateMap.value(m_baudrate), m_baudrate, m_serialInterface)) { - qCritical() << "CANNOT OPEN" << m_serialInterface << "(BAUDRATE=" - << m_baudrate << ")"; - return false; + QString const &hwVersion = m_hw->dc_getHWversion().toLower().trimmed(); + QString const &swVersion = m_hw->dc_getSWversion().toLower().trimmed(); + + m_hw->dc_autoRequest(false); + QThread::sleep(1); // make sure the timer-slots are inactive + + if (!hwVersion.isEmpty() && !swVersion.isEmpty()) { + return QStringList() << hwVersion << swVersion; } - QString fwVersion = m_hw->dc_getSWversion(); - QString const hwVersion = m_hw->dc_getHWversion(); + return QStringList() << "DC HW-version not available" + << "DC SW-version not available"; +} - qInfo() << "current dc-hardware-version" << hwVersion; - qInfo() << "current dc-firmware-version" << fwVersion; +bool Update::doUpdate(int &displayIndex, QStringList const &filesToWorkOn) { + bool serialOpened = false; - m_hw->dc_autoRequest(false);// default: turn auto-request setting off - QThread::sleep(3); // wait to be sure that there are no more - // commands sent to dc-hardware - qDebug() << "SET AUTO-REQUEST=FALSE"; + QString const &parentName = Utils::getParentName(); - QStringList linesToWorkOn = getLinesToWorkOn(); - if (linesToWorkOn.size() == 0) { - qCritical() << "No lines to handle in" << m_update_ctrl_file.fileName(); - return true; - } - qDebug() << "open lines..."; - for (int i=0; i< linesToWorkOn.size(); ++i) { - qDebug() << "line" << i << ":" << linesToWorkOn.at(i).trimmed(); + Utils::printInfoMsg( + QString("PARENT OF ATB-UPDATE-TOOL (ppid=%1) ").arg(getppid()) + parentName); + + if (parentName == "ATBQT" || parentName == "systemd") { + // the tool was not called during 'service' ot during an automatic + // update procedure. and it was called explicitly with libCAmaster.so + if (m_pluginName.contains("master", Qt::CaseInsensitive)) { + Utils::printCriticalErrorMsg(parentName + + " IS MASTER, BUT ATB-UPDATE-TOOL CALLED WITH libCAmaster.so"); + return false; + } + Utils::printInfoMsg( + QString("ATB-UPDATE-TOOL STARTED AS SLAVE OF ") + parentName); + } else + if (Utils::isATBQTRunning()) { // manual testing + if (m_pluginName.contains("master", Qt::CaseInsensitive)) { + Utils::printCriticalErrorMsg( + "ATBQT IS MASTER, BUT ATB-UPDATE-TOOL CALLED WITH libCAmaster.so"); + return false; + } + Utils::printInfoMsg( + "ATB-UPDATE-TOOL STARTED AS SLAVE-SIBLING OF ATBQT-MASTER"); + } else { + if (m_pluginName.contains("slave", Qt::CaseInsensitive)) { + Utils::printCriticalErrorMsg( + "ATB-UPDATE-TOOL CALLED WITH libCAslave.so ALTHOUGH MASTER"); + return false; + } + Utils::printInfoMsg("ATB-UPDATE-TOOL STARTED AS MASTER"); + + if ((serialOpened = openSerial(baudrateMap.value(m_baudrate), + m_baudrate, + m_serialInterface)) == false) { + Utils::printCriticalErrorMsg( + QString("CANNOT OPEN ") + + m_serialInterface + + "( BAUDRATE=" + m_baudrate + ")"); + return false; + } + + m_hw->dc_autoRequest(false); + + Utils::printInfoMsg( + QString("SERIAL OPEN ") + m_serialInterface + + " (BAUDRATE=" + m_baudrate + ")"); } + bool res = false; QList::const_iterator it; - for (it = linesToWorkOn.cbegin(); it != linesToWorkOn.cend(); ++it) { - bool res = false; - QString line = (*it).trimmed(); - if (line.size() == 0 || line.startsWith(QChar('#'))) { - continue; - } - QStringList lst = split(line.trimmed()); - if (lst.size() != 4) { - qCritical() << "PARSING ERROR FOR LINE" - << line << "IN" << m_update_ctrl_file.fileName(); - continue; - } - QString const &request = lst[COLUMN_REQUEST]; - QString const &name = lst[COLUMN_NAME]; - // QString const &datetime = lst[COLUMN_DATE_TIME]; - // QString const &result = lst[COLUMN_RESULT]; - qDebug() << "request=" << request << ", name=" << name; - if (request.trimmed() == "DOWNLOAD") { - if (name.contains("dc2c", Qt::CaseInsensitive) && - name.endsWith(".bin", Qt::CaseInsensitive)) { - qInfo() << "downloading" << name.trimmed() << "to DC"; - res = true; -#if UPDATE_DC == 1 - if ((res = updateBinary(name.toStdString().c_str())) == true) { - qInfo() << "downloaded binary" << name; - } -#endif - } else if (name.contains("DC2C_print", Qt::CaseInsensitive) - && name.endsWith(".json", Qt::CaseInsensitive)) { - res = true; -#if UPDATE_PRINTER_TEMPLATES == 1 - int i = name.indexOf("DC2C_print", Qt::CaseInsensitive); - int const templateIdx = name.mid(i).midRef(10, 2).toInt(); - if ((templateIdx < 1) || (templateIdx > 32)) { - qCritical() << "WRONG TEMPLATE INDEX" << templateIdx; - res = false; - } else { - if ((res = updatePrinterTemplate(templateIdx, name))) { - qInfo() << "downloaded printer template"<< name; - } - } -#endif - } else if (name.contains("DC2C_cash", Qt::CaseInsensitive) - && name.endsWith(".json", Qt::CaseInsensitive)) { - res = true; -#if UPDATE_CASH_TEMPLATE - if ((res = updateCashConf(name))) { - qInfo() << "downloaded cash template"<< name; - } -#endif - } else if (name.contains("DC2C_conf", Qt::CaseInsensitive) - && name.endsWith(".json", Qt::CaseInsensitive)) { - res = true; -#if UPDATE_CONF_TEMPLATE - if ((res= updateConfig(name))) { - qInfo() << "downloaded config template"<< name; - } -#endif - } else if (name.contains("DC2C_device", Qt::CaseInsensitive) - && name.endsWith(".json", Qt::CaseInsensitive)) { - res = true; -#if UPDATE_DEVICE_TEMPLATE - if ((res = updateDeviceConf(name))) { - qInfo() << "downloaded device template"<< name; - } -#endif - } else { - qCritical() << "UNKNOWN JSON FILE NAME" << name; + for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) { + m_worker->startProgressLoop(); + QString fToWorkOn = (*it).trimmed(); + fToWorkOn = QDir::cleanPath(m_customerRepository + QDir::separator() + fToWorkOn); + + static const QRegularExpression version("^.*dc2c[.][0-9]{1,2}[.][0-9]{1,2}[.]bin.*$"); + if (fToWorkOn.contains(version)) { + Utils::printInfoMsg("DO-UPDATE FILE-TO-WORK-ON " + fToWorkOn); + + QFile fn(fToWorkOn); + QFileInfo finfo(fn); + if (!fn.exists()) { // check for broken link + Utils::printCriticalErrorMsg("DO-UPDATE FILE-TO-WORK-ON " + + fToWorkOn + " DOES NOT EXIST"); res = false; - } - } else if (request == "EXECUTE" && name.contains("opkg")) { - qInfo() << "starting" << name.trimmed(); - res = true; -#if UPDATE_OPKG == 1 - QScopedPointer p(new QProcess(this)); - p->setProcessChannelMode(QProcess::MergedChannels); - p->start(name.trimmed()); - if (p->waitForStarted(1000)) { - if (p->state() == QProcess::ProcessState::Running) { - if (p->waitForFinished(100000)) { - QString output = p->readAllStandardOutput(); - QStringList outputLst = split(output, QChar('\n')); - for (int line=0; line < outputLst.size(); ++line) { - qDebug() << outputLst[line]; - } - if (p->exitStatus() == QProcess::NormalExit) { - qInfo() << "EXECUTED" << name - << "with code" << p->exitCode(); - res = true; - } else { - qCritical() << "PROCESS" << name << "CRASHED"; - } - } else { - qCritical() << "PROCESS" << name << "DID NOT FINISH"; - } - } else { - qCritical() << "WRONG PROCESS STATE" << p->state(); - } } else { - qCritical() << "PROCESS" << name << "TIMEOUT AT START"; + bool updateBinaryRes = true; + + qInfo() << "DOWNLOADING" << finfo.completeBaseName() << "TO DC"; +#if UPDATE_DC == 1 + m_hw->dc_autoRequest(false);// default: turn auto-request setting off + QThread::sleep(1); // wait to be sure that there are no more + // commands sent to dc-hardware + qInfo() << "SET AUTO-REQUEST=FALSE"; + + if ((updateBinaryRes = updateBinary(fToWorkOn.toStdString().c_str())) == true) { + qCritical() << "downloaded binary" << fToWorkOn; + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } + + m_hw->dc_autoRequest(true); // turn auto-request setting on + qInfo() << "SET AUTO-REQUEST=TRUE"; + + QStringList const &versions = Update::getDcSoftAndHardWareVersion(); + if (versions.size() >= 2) { + if (updateBinaryRes == true) { + qInfo() << "dc-hardware-version (UPDATED)" << versions[0]; + qInfo() << "dc-firmware-version (UPDATED)" << versions[1]; + } else { + qInfo() << "dc-hardware-version (NOT UPDATED)" << versions[0]; + qInfo() << "dc-firmware-version (NOT UPDATED)" << versions[1]; + } + } +#endif + res = updateBinaryRes; + } + } else if (fToWorkOn.contains("DC2C_print", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; +#if UPDATE_PRINTER_TEMPLATES == 1 + int i = fToWorkOn.indexOf("DC2C_print", Qt::CaseInsensitive); + int const templateIdx = fToWorkOn.mid(i).midRef(10, 2).toInt(); + if ((templateIdx < 1) || (templateIdx > 32)) { + qCritical() << "WRONG TEMPLATE INDEX" << templateIdx; + res = false; + } else { + if ((res = updatePrinterTemplate(templateIdx, fToWorkOn))) { + qInfo() << "downloaded printer template"<< fToWorkOn; + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } + } +#endif + } else if (fToWorkOn.contains("DC2C_cash", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; +#if UPDATE_CASH_TEMPLATE == 1 + if ((res = updateCashConf(fToWorkOn))) { + qInfo() << "downloaded cash template"<< fToWorkOn; + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } +#endif + } else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; +#if UPDATE_CONF_TEMPLATE == 1 + if ((res= updateConfig(fToWorkOn))) { + qInfo() << "downloaded config template"<< fToWorkOn; + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); + } +#endif + } else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive) + && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { + res = true; +#if UPDATE_DEVICE_TEMPLATE == 1 + if ((res = updateDeviceConf(fToWorkOn))) { + qInfo() << "downloaded device template"<< fToWorkOn; + ++displayIndex; + emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")") + + QString(" Update ") + QFileInfo(fToWorkOn).fileName(), + Worker::UPDATE_STEP_DONE); } #endif } else { - // TODO + qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn; + res = false; + } + m_worker->stopProgressLoop(); + m_worker->setProgress(100); + + if (res == false) { + break; } - char buf[128]; - memset(buf, 0x00, sizeof(buf)); - snprintf(buf, sizeof(buf)-1, "DONE, %*.*s, %*.*s, %*.*s\n", - 35, 35, name.toStdString().c_str(), - 20, 20, QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), - 10, 10, (res == true) ? "SUCCESS" : "ERROR"); - m_update_ctrl_file_copy.write(buf); } // for (it = openLines.cbegin(); it != openLines.end(); ++it) { - closeSerial(); - m_hw->dc_autoRequest(true); - qDebug() << "SET AUTO-REQUEST=TRUE"; + //m_hw->dc_autoRequest(true); // ALWAYS turn autoRequest ON + //qDebug() << "SET AUTO-REQUEST=TRUE"; - qInfo() << "current dc-hardware-version" << m_hw->dc_getHWversion(); - qInfo() << "current dc-firmware-version" << m_hw->dc_getSWversion(); - - return finishUpdate(linesToWorkOn.size() > 0); -} - -bool Update::finishUpdate(bool swapCtrlFiles) { - if (swapCtrlFiles) { - m_update_ctrl_file.close(); - m_update_ctrl_file_copy.close(); - - QString const &fn = m_update_ctrl_file.fileName(); - QString const &fn_tmp = m_update_ctrl_file.fileName() + ".tmp"; - QString const &fn_copy = m_update_ctrl_file_copy.fileName(); - QFile tmp(fn_tmp); - - if (tmp.exists()) { - tmp.remove(); - } - - if (m_update_ctrl_file.rename(fn_tmp)) { - if (m_update_ctrl_file_copy.rename(fn)) { - return m_update_ctrl_file.rename(fn_copy); - } - } - return false; + if (serialOpened) { + m_hw->dc_closeSerial(); } - return true; + + return res; } diff --git a/update.h b/update.h index 08b6478..f56cdc7 100644 --- a/update.h +++ b/update.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "plugins/interfaces.h" @@ -15,46 +17,55 @@ #define SERIAL_PORT "ttyUSB0" #endif -class Update; - -// TODO: check hardware compatibility -// TODO: opkg commandos - +class Worker; class Update : public QObject { Q_OBJECT hwinf *m_hw; + Worker *m_worker; char const *m_serialInterface; char const *m_baudrate; - QFile m_update_ctrl_file; - QFile m_update_ctrl_file_copy; + QString m_customerRepository; + QString m_customerNrStr; + QString m_branchName; + QString m_pluginName; QString m_workingDir; bool m_maintenanceMode; + bool m_dryRun; - bool m_init; - - bool finishUpdate(bool finish); - QStringList getLinesToWorkOn(); - QStringList split(QString line, QChar sep = ','); - - bool execUpdateScript(); + static QPluginLoader pluginLoader; public: enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6}; static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn); + static bool unloadDCPlugin(); + static QStringList split(QString line, QChar sep = ','); explicit Update(hwinf *hw, - QString update_ctrl_file, - QString workingDir = ".", - bool maintenanceMode = false, + Worker *worker, + QString customerRepository, + QString customerNrStr, + QString branchName, + QString pluginName, + QString workingDir, + bool dryRun = false, QObject *parent = nullptr, char const *serialInterface = SERIAL_PORT, char const *baudrate = "115200"); virtual ~Update() override; - bool doUpdate(); + bool doUpdate(int &displayIndex, QStringList const &linesToWorkOn); + + //QString customerId() { return m_customerId; } + //QString const customerId() const { return m_customerId; } + + QString branchName() { return m_branchName; } + QString const branchName() const { return m_branchName; } + + //QString repositoryPath() { return m_repositoryPath; } + //QString const repositoryPath() const { return m_repositoryPath; } private: static QString jsonType(enum FileTypeJson type); @@ -68,6 +79,7 @@ private: bool stopBootloader() const; bool openSerial(int br, QString baudrate, QString comPort) const; void closeSerial() const; + bool isSerialOpen() const; bool resetDeviceController() const; QByteArray loadBinaryDCFile(QString filename) const; bool downloadBinaryToDC(QString const &bFile) const; @@ -79,5 +91,12 @@ private: bool updateDeviceConf(QString jsFileToSendToDC); bool downloadJson(enum FileTypeJson type, int templateIdx, QString jsFileToSendToDC) const; + QStringList getDcSoftAndHardWareVersion(); + +private slots: + void readyReadStandardOutput(); + void readyReadStandardError(); + void finished(int exitCode, QProcess::ExitStatus exitStatus); + }; #endif // UPDATE_H_INCLUDED diff --git a/update_dc_event.cpp b/update_dc_event.cpp new file mode 100644 index 0000000..a3b8ec9 --- /dev/null +++ b/update_dc_event.cpp @@ -0,0 +1,25 @@ +#include "update_dc_event.h" + +QEvent::Type UpdateDcEvent::customEventType = QEvent::None; + +UpdateDcEvent::UpdateDcEvent(QObject const *sender, + UpdateStep updateStep, + int count, + QDateTime const &sendDateTime) + : QEvent(UpdateDcEvent::type()) + , m_sender(sender) + , m_updateStep(updateStep) + , m_count(count) + , m_sendDateTime(sendDateTime) { +} + +UpdateDcEvent::~UpdateDcEvent() { +} + +QEvent::Type UpdateDcEvent::type() { + if (customEventType == QEvent::None) { + int generatedType = QEvent::registerEventType(); + customEventType = static_cast(generatedType); + } + return customEventType; +} diff --git a/update_dc_event.h b/update_dc_event.h new file mode 100644 index 0000000..869375a --- /dev/null +++ b/update_dc_event.h @@ -0,0 +1,40 @@ +#ifndef UPDATE_DC_EVENT_H_INCLUDED +#define UPDATE_DC_EVENT_H_INCLUDED + +#include +#include + +class UpdateDcEvent : public QEvent { +public: + enum UpdateStep { NONE, DC_REBOOT, BL_START, BL_CHECK, BL_CHECK_AFTER_STOP, BL_IS_UP, BL_IS_DOWN, BL_STOP}; + +private: + QObject const *m_sender; + UpdateStep m_updateStep; + int m_count; + QDateTime m_sendDateTime; + +public: + explicit UpdateDcEvent(QObject const *sender, UpdateStep updateStep, + int count, + QDateTime const &sendDateTime = QDateTime::currentDateTime()); + virtual ~UpdateDcEvent(); + static QEvent::Type type(); + + QObject const *sender() { return m_sender; } + QObject const *sender() const { return m_sender; } + + void setUpdateStep(UpdateStep updateStep) { m_updateStep = updateStep; } + UpdateStep updateStep() { return m_updateStep; } + UpdateStep updateStep() const { return m_updateStep; } + int count() const { return m_count; } + void setCount(int count) { m_count = count; } + QDateTime &sendDateTime() { return m_sendDateTime; } + QDateTime const &sendDateTime() const { return m_sendDateTime; } + +private: + static QEvent::Type customEventType; +}; + + +#endif // PROGRESS_EVENT_H_INCLUDED diff --git a/utils.cpp b/utils.cpp index c91f809..284c95e 100644 --- a/utils.cpp +++ b/utils.cpp @@ -1,201 +1,231 @@ #include "utils.h" +#include "message_handler.h" +#include "git/git_client.h" + + +#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX) +#include "unistd.h" +#endif -#include -#include #include -#include -#include #include +#include +#include +#include +#include -#include "interfaces.h" -#include "DCPlugin/include/hwapi.h" +#include -//#include -#include -#include -#include -#include -#include - -#define COLUMN_STATUS (0) -#define COLUMN_NAME (1) -#define COLUMN_DATE_TIME (2) -#define COLUMN_RESULT (3) - -Utils::Utils(QString update_ctrl_file, - QObject *parent, - char const *serialInterface, - char const *baudrate) - : QObject(parent) - , m_hw(new hwapi()) - , m_serialInterface(serialInterface) - , m_baudrate(baudrate) - , m_update_ctrl_file(update_ctrl_file) - , m_update_ctrl_file_copy(update_ctrl_file + ".copy") - , m_in(&m_update_ctrl_file) - , m_out(&m_update_ctrl_file_copy) - , m_init(true) { - - if (!m_update_ctrl_file.exists()) { - qCritical() << "Update-file" << m_update_ctrl_file.fileName() - << "does not exist"; - m_init = false; - } - if (!m_update_ctrl_file.open(QIODevice::ReadOnly | QIODevice::Text)) { - qCritical() << "can not open " << m_update_ctrl_file.fileName() - << "for reading"; - m_init = false; - } - if (!m_update_ctrl_file_copy.open(QIODevice::WriteOnly | QIODevice::Text)) { - qCritical() << "can not open " << m_update_ctrl_file_copy.fileName() - << "for writing"; - m_init = false; - } -} - -Utils::~Utils() { - -} - -void Utils::updateBinary(char const *fileToSendToDC) { - qDebug() << "file to send to DC ..." << fileToSendToDC; - qDebug() << "baudrate ............." << m_baudrate; - qDebug() << "serial interface ....." << m_serialInterface; - m_hw->dc_updateDC(fileToSendToDC, m_baudrate, m_serialInterface); - std::this_thread::sleep_for(std::chrono::milliseconds(3000)); - QCoreApplication::quit(); -} - -void Utils::updatePrinterConf(int nrOfTemplate, char const *fileToSendToDC) { - QVector printTemplates{ nrOfTemplate }; - QVector filesToSend{ fileToSendToDC }; - - m_hw->dc_updatePrinterTemplate(hwapi::FileTypeJson::PRINTER, - printTemplates, filesToSend, - QString(m_baudrate), - QString(m_serialInterface)); - std::this_thread::sleep_for(std::chrono::milliseconds(3000)); - QCoreApplication::quit(); -} - -QStringList Utils::getOpenLines() { - QStringList openLines; - - while (!m_in.atEnd()) { - QString line = m_in.readLine().trimmed(); - // QString.split() is defined >= 5.14 - if (!line.startsWith("OPEN")) { - m_out << line; - } else { - openLines << line; +int Utils::read1stLineOfFile(QString fileName) { + QFile f(fileName); + if (f.exists()) { + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&f); + in.setCodec("UTF-8"); + while(!in.atEnd()) { + return in.readLine().toInt(); + } } } - return openLines; + return -1; } -bool Utils::doUpdate() { - /* - The file referred to by 'update_data' has the following structure for - each line: +QString Utils::zoneName(quint8 i) { + static constexpr char const *zName[] = { + "", + "purple", + "blue", + "yellow", + "green", + "yellow (mars)", + "green (mars)" + }; + if (i < (sizeof(zName)/sizeof(char const *))) { + return zName[i]; + } + return "N/A"; +} - # ====================================================================== - # STATUS | NAME | DATE | RESULT - # ====================================================================== - # where - # - # STATUS: OPEN or CLOSED - # NAME : If starting with 'opkg' it is an opkg-command to be executed. - # Otherwise its the name of a file which has to be updated. - # DATE : 0000-00-00T00:00:00 - # RESULT: SUCCESS or ERROR (possibly with description) - # - */ - if (!m_init) { +void Utils::printCriticalErrorMsg(QString const &errorMsg) { + qCritical() << QString(80, '!'); + qCritical() << errorMsg; + qCritical() << QString(80, '!'); +} + +void Utils::printInfoMsg(QString const &infoMsg) { + qCritical() << QString(80, '='); + qCritical() << infoMsg; + qCritical() << QString(80, '='); + +} + +void Utils::printLineEditInfo(QStringList const &lines) { + if (getDebugLevel() == LOG_DEBUG) { + for (int i=0; i= 0; --n) { + if (!str.at(n).isSpace()) { + return str.left(n + 1); + } + } + return ""; +} + +bool Utils::sameFilesInDirs(QDir const &dir1, QDir const &dir2, + QStringList const &nameFilters) { + if (!dir1.exists()) { + printCriticalErrorMsg(dir1.dirName() + " DOES NOT EXIST"); + return false; + } + if (!dir2.exists()) { + printCriticalErrorMsg(dir2.dirName() + " DOES NOT EXIST"); + return false; + } + if (dir1.absolutePath() == dir2.absolutePath()) { + printCriticalErrorMsg(dir1.dirName() + " AND "+ dir2.dirName() + " HAVE SAME PATH"); return false; } - QStringList openLines = getOpenLines(); + // files, sorted by name + QFileInfoList const &lst1 = dir1.entryInfoList(nameFilters, QDir::Files, QDir::Name); + QFileInfoList const &lst2 = dir2.entryInfoList(nameFilters, QDir::Files, QDir::Name); - bool res = false; - QList::const_iterator it; - for (it = openLines.cbegin(); it != openLines.cend(); ++it) { - int start = 0, end; - int column = 0; - QString status, name, datetime, result; - QString line = *it; - while ((end = line.indexOf(QChar(','), start)) != -1) { - QString next = line.mid(start, end).trimmed(); - switch (column) { - case COLUMN_STATUS: - status = next; - break; - case COLUMN_NAME: - name = next; - break; - case COLUMN_DATE_TIME: - datetime = next; - break; - case COLUMN_RESULT: - result = next; - break; + QStringList fileNameLst1{}; + QStringList fileNameLst2{}; + QListIterator i1(lst1); + while (i1.hasNext()) { + fileNameLst1 << i1.next().fileName(); + } + QListIterator i2(lst2); + while (i2.hasNext()) { + fileNameLst2 << i2.next().fileName(); + } + + if (fileNameLst1.isEmpty()) { + qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES"; + return false; + } + if (fileNameLst2.isEmpty()) { + qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES"; + return false; + } + if (fileNameLst1 != fileNameLst2) { + printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName() + + " DIFFER: [" + fileNameLst1.join(',') + "],[" + + fileNameLst2.join(',') + "]"); + return false; + } else { + printInfoMsg(dir1.dirName() + " AND " + dir2.dirName() + + " ARE EQUAL: [" + fileNameLst1.join(',') + "]"); + } + + QStringList gitBlobLst1{}; + QStringList gitBlobLst2{}; + QListIterator i3(lst1); + while (i3.hasNext()) { + gitBlobLst1 << GitClient::gitBlob(i3.next().fileName()); + } + QListIterator i4(lst2); + while (i4.hasNext()) { + gitBlobLst2 << GitClient::gitBlob(i4.next().fileName()); + } + + if (gitBlobLst1.isEmpty()) { + qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES"; + return false; + } + if (gitBlobLst2.isEmpty()) { + qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES"; + return false; + } + + if (gitBlobLst1 != gitBlobLst2) { + printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName() + + " DIFFER: [" + gitBlobLst1.join(',') + "],[" + + gitBlobLst2.join(',') + "]"); + return false; + } else { + printInfoMsg(dir1.dirName() + " AND " + dir2.dirName() + + " CONTAIN SAME GIT-BLOBS FOR FILES: [" + fileNameLst1.join(',') + "]"); + + } + + return true; +} + + +QString Utils::getParentName() { // get name of parent process + QString ppid = QString("/proc/%1/status").arg(getppid()); + std::ifstream f(ppid.toStdString().c_str()); + if (f.is_open()) { + std::string next; + while (std::getline(f, next)) { + QString line = QString(next.c_str()).simplified(); + if (line.startsWith("Name")) { + int const idx = line.indexOf(QChar(':')); + if (idx != -1) { + return line.mid(idx+1).trimmed(); + } } - ++column; - start = end + 1; } + } + return ""; +} - if (!status.contains("OPEN")) { - qCritical() << "Parsing error for" << m_update_ctrl_file.fileName(); - return false; - } - if (name.contains("dc2c") && name.endsWith(".bin")) { - updateBinary(name.toStdString().c_str()); - res = true; - } else - if (name.contains("DC2C_print") && name.endsWith(".json")) { - int i = name.indexOf("DC2C_print"); - int templateIdx = name.mid(i).midRef(10, 2).toInt(); - updatePrinterConf(templateIdx, name.toStdString().c_str()); - res = true; - } else - if (name.contains("opkg")) { - int i = name.indexOf("opkg "); - QString rest = name.mid(i).trimmed(); - QScopedPointer p(new QProcess(this)); - p->setProcessChannelMode(QProcess::MergedChannels); - p->start("opkg", QStringList() << rest); - if (p->waitForStarted(1000)) { - if (p->state() == QProcess::ProcessState::Running) { - if (p->waitForFinished(10000)) { - QByteArray output = p->readAllStandardOutput(); - qCritical() << output; - res = true; +bool Utils::isATBQTRunning() { + QDirIterator it("/proc", + QStringList() << "status", + QDir::Files, + QDirIterator::Subdirectories); + while (it.hasNext()) { + QString const &nextStatusFile = it.next(); + static const QRegularExpression re("^/proc/[0-9]{1,}/status"); + QRegularExpressionMatch match = re.match(nextStatusFile); + if (match.hasMatch()) { + std::ifstream f(nextStatusFile.toStdString().c_str()); + if (f.is_open()) { + std::string next; + while (std::getline(f, next)) { + QString line = QString(next.c_str()).simplified(); + if (line.startsWith("Name")) { + int const idx = line.indexOf(QChar(':')); + if (idx != -1) { + QString const binary = line.mid(idx+1).trimmed(); + if (binary == "ATBQT") { + return true; + } + } } } } - } else { - // TODO - } - QString resultLine = "CLOSED"; - resultLine += ", " + name; - resultLine += ", " + QDateTime::currentDateTime().toString(Qt::ISODate); - resultLine += ", " + (res == true) ? "SUCCESS" : "ERROR"; - m_out << resultLine; - } // for (it = openLines.cbegin(); it != openLines.end(); ++it) { - - return finishUpdate(openLines.size() > 0); -} - -bool Utils::finishUpdate(bool replaceCtrlFile) { - if (replaceCtrlFile) { - if (!m_update_ctrl_file_copy.exists()) { - return false; - } - if (!m_update_ctrl_file.remove()) { - return false; - } - if (!m_update_ctrl_file_copy.rename(m_update_ctrl_file.fileName())) { - return false; } } - return true; + return false; } diff --git a/utils.h b/utils.h index 4e42753..6dd7dd7 100644 --- a/utils.h +++ b/utils.h @@ -3,44 +3,26 @@ #include #include +#include #include +#include +#include +#include -#include +namespace Utils { + int read1stLineOfFile(QString fileName); + QString zoneName(quint8 i); + void printCriticalErrorMsg(QString const &errorMsg); + void printInfoMsg(QString const &infoMsg); + void printLineEditInfo(QStringList const &lines); + QString getTariffLoadTime(QString fileName); + QString rstrip(QString const &str); + bool sameFilesInDirs(QDir const &dir1, QDir const &dir2, + QStringList const &nameFilters = {"*.json"}); -#include "interfaces.h" -#include "DCPlugin/include/hwapi.h" -#ifdef PTU5 -#define SERIAL_PORT "ttymxc2" -#else -#define SERIAL_PORT "ttyUSB0" -#endif + QString getParentName(); + bool isATBQTRunning(); +} -class Utils : public QObject { - Q_OBJECT - - std::unique_ptr m_hw; - char const *m_serialInterface; - char const *m_baudrate; - QFile m_update_ctrl_file; - QFile m_update_ctrl_file_copy; - QTextStream m_in; - QTextStream m_out; - - bool m_init; - - void updateBinary(char const *fileToSendToDC); - void updatePrinterConf(int nrOfTemplate, char const *fileToSendToDC); - bool finishUpdate(bool finish); - QStringList getOpenLines(); - static constexpr QChar SEPARATOR = QChar(','); - -public: - explicit Utils(QString update_ctrl_file, - QObject *parent = nullptr, - char const *serialInterface = SERIAL_PORT, - char const *baudrate = "115200"); - virtual ~Utils() override; - bool doUpdate(); -}; #endif // UTILS_H_INCLUDED diff --git a/worker.cpp b/worker.cpp index 73c0c16..c787c05 100644 --- a/worker.cpp +++ b/worker.cpp @@ -7,34 +7,113 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include + +#include #include "message_handler.h" #include "plugins/interfaces.h" +#include "ismas/ismas_client.h" +#include "progress_event.h" +#include "mainwindow.h" +#include "utils.h" +QString const Worker::UPDATE_STEP_OK(" [ ok]"); +QString const Worker::UPDATE_STEP_DONE(" [done]"); +QString const Worker::UPDATE_STEP_FAIL(" [FAIL]"); +QString const Worker::UPDATE_STEP_SUCCESS(" [SUCCESS]"); -Worker::Worker(QString update_ctrl_file, QString workingDir) - : m_update_ctrl_file(update_ctrl_file) - , m_workingDir(workingDir) - , m_workerThread("workerThread") { +Worker::Worker(int customerNr, + int machineNr, + int zoneNr, + QString branchName, + QString pluginName, + QString workingDirectory, + bool dryRun, + QObject *parent, + char const *serialInterface, + char const *baudrate) + : m_workerThread("workerThread") + , m_customerNr(customerNr) + , m_customerNrStr(QString("customer_") + QString::number(m_customerNr).rightJustified(3, '0')) + , m_machineNr(machineNr) + , m_zoneNr(zoneNr) + , m_pluginName(pluginName) + , m_workingDirectory(workingDirectory) + , m_branchName(branchName) + , m_customerRepositoryPath(QString("https://git.mimbach49.de/GerhardHoffmann/%1.git").arg(m_customerNrStr)) + , m_customerRepository(QDir::cleanPath(m_workingDirectory + QDir::separator() + m_customerNrStr)) + , m_dryRun(dryRun) + , m_parent(parent) + , m_serialInterface(serialInterface) + , m_baudrate(baudrate) + , m_gc(m_customerNrStr, m_customerRepository, m_workingDirectory, m_branchName, this) + , m_osVersion(getOsVersion()) + , m_atbqtVersion(getATBQTVersion()) + , m_cpuSerial(getCPUSerial()) + , m_pluginVersionATBDeciceController(getPluginVersion("/opt/app/ATBAPP/plugins/libATBDeviceControllerPlugin.so")) + , m_pluginVersionIngenicoISelf(getPluginVersion("/opt/app/ATBAPP/plugins/libIngenicoISelf_CCPlugin.so")) + , m_pluginVersionMobilisisCalc(getPluginVersion("/opt/app/ATBAPP/plugins/libMOBILISIS_CalculatePricePlugin.so")) + , m_pluginVersionMobilisisCalcConfig(getPluginVersion("/opt/app/ATBAPP/plugins/libMOBILISIS_CalculatePricePlugin_ConfigUi.so")) + , m_pluginVersionPrmCalc(getPluginVersion("/opt/app/ATBAPP/plugins/libPRM_CalculatePricePlugin.so")) + , m_pluginVersionPrmCalcConfig(getPluginVersion("/opt/app/ATBAPP/plugins/libPRM_CalculatePricePlugin_ConfigUi.so")) + , m_pluginVersionTcpZvt(getPluginVersion("/opt/app/ATBAPP/plugins/libTCP_ZVT_CCPlugin.so")) + , m_ismasUpdateRequests(ISMAS_UPDATE_REQUESTS) + , m_waitForNewUpdates(this) + , m_filesToUpdate() + , m_updateProcessRunning(true) + , m_returnCode(0) + , m_progressValue(0) + //, m_withoutIsmasDirectPort(true) /* useful for testing */ { + , m_withoutIsmasDirectPort(false) /* useful for testing */ { + + this->setObjectName("worker-object"); + QDir::setCurrent(m_workingDirectory); + + // restart apism to make sure it is running ? + // Command c("systemctl restart apism"); + // if (c.execute("/tmp")) { + // QThread::sleep(10); // give APISM some time to reconnect + // } + + if (std::optional v = getApismVersion()) { + m_apismVersion = v.value(); + } + + qInfo() << "CURRENT TIME ..............." << QDateTime::currentDateTime().toString(Qt::ISODate); + qInfo() << "OS VERSION ................." << m_osVersion; + qInfo() << "ATBQT VERSION .............." << m_atbqtVersion; + qInfo() << "CPU SERIAL ................." << m_cpuSerial; + qInfo() << "CUSTOMER_NR ................" << m_customerNr; + qInfo() << "CUSTOMER_NR_STR ............" << m_customerNrStr; + qInfo() << "CUSTOMER_REPOSITORY_PATH ..." << m_customerRepositoryPath; + qInfo() << "CUSTOMER_REPOSITORY ........" << m_customerRepository; + qInfo() << "MACHINE_NR ................." << m_machineNr; + qInfo() << "ZONE_NR ...................." << m_zoneNr; + qInfo() << "BRANCH_NAME ................" << m_branchName; + qInfo() << "PLUGIN_NAME ................" << m_pluginName; + qInfo() << "WORKING_DIRECTORY .........." << m_workingDirectory; + qInfo() << "APISM VERSION .............." << m_apismVersion; this->moveToThread(&m_workerThread); m_workerThread.start(); - QThread::usleep(100000); int cnt = 0; while (!m_workerThread.isRunning()) { if (++cnt > 5) { - qCritical() << "starting worker thread FAILED"; + Utils::printCriticalErrorMsg("starting worker thread FAILED"); return; } QThread::sleep(1); } - - connect(this, SIGNAL(workNow()), this, SLOT(work()), Qt::QueuedConnection); - connect(&m_timer, SIGNAL(timeout()), this, SLOT(update())); - m_timer.setSingleShot(true); - m_timer.start(1000); } Worker::~Worker() { @@ -43,24 +122,1303 @@ Worker::~Worker() { while (!m_workerThread.isFinished()) { if (!m_workerThread.wait(1000)) { if (++cnt > 5) { - qCritical() << "stopping worker thread FAILED"; + Utils::printCriticalErrorMsg("stopping worker thread FAILED"); return; } } } } -void Worker::update() { - qCritical() << __func__ << ":" << __LINE__; - emit workNow(); +void Worker::setProgress(int progress) { + if (m_mainWindow) { + m_progressValue = progress; + QApplication::postEvent(m_mainWindow, new ProgressEvent(this, progress)); + } } -void Worker::work() { - qCritical() << __func__ << ":" << __LINE__; - //Update m_update(m_update_ctrl_file, m_workingDir); - QThread::sleep(3); - //if (m_update.doUpdate()) { - //} - m_workerThread.quit(); - QApplication::quit(); +void Worker::startProgressLoop() { + QApplication::postEvent(m_mainWindow, new ProgressEvent(this, MainWindow::START_PROGRESS_LOOP)); +} + +void Worker::stopProgressLoop() { + QApplication::postEvent(m_mainWindow, new ProgressEvent(this, MainWindow::STOP_PROGRESS_LOOP)); +} + +static std::once_flag once; +void Worker::update() { + // user should not start the update process several times + std::call_once(once, &Worker::privateUpdate, this); +} + +void Worker::privateUpdate() { + if (!m_mainWindow) { + Utils::printCriticalErrorMsg("m_mainWindow NOT SET"); + return; + } + + m_updateProcessRunning = true; + bool sentIsmasLastVersionNotification = false; + + emit disableExit(); + + m_returnCode = -1; + QDir customerRepository(m_customerRepository); + if (!customerRepository.exists()) { + emit appendText("\nInitializing customer environment ..."); + startProgressLoop(); + if (m_gc.gitCloneAndCheckoutBranch()) { + stopProgressLoop(); + emit replaceLast("Initializing customer environment", UPDATE_STEP_DONE); + + int progress = (m_mainWindow->progressValue()/10) + 10; + setProgress(progress); + + m_updateStatus = UpdateStatus(UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_SUCCESS, + QString("CLONED AND CHECKED OUT: ") + m_customerRepository); + + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.cloneAndCheckoutCustomerRepository( + m_updateStatus.m_statusDescription)); + + setProgress(progress + 10); + + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSASucceeded("")); + + setProgress(100); + m_ismasClient.setProgressInPercent(100); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + m_ismasClient.updateOfPSAActivated()); + + m_returnCode = 0; + } else { + stopProgressLoop(); + + int progress = (m_mainWindow->progressValue()/10) + 10; + setProgress(progress); + + emit replaceLast("Initializing customer environment", UPDATE_STEP_FAIL); + + m_updateStatus = UpdateStatus(UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_FAILURE, + QString("CLONE OR CHECKOUT FAILED: ") + m_customerRepository); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.errorGitClone(100, m_updateStatus.m_statusDescription)); + + m_returnCode = -3; + } + } else { + m_ismasClient.setProgressInPercent(10); + if (updateTriggerSet()) { + m_ismasClient.setProgressInPercent(20); + if (customerEnvironment()) { + m_ismasClient.setProgressInPercent(30); + if (filesToUpdate()) { + // send message to ISMAS about files which have been + // checked in into git repository + m_updateStatus = UpdateStatus(UPDATE_STATUS::GIT_CHECK_FILES_TO_UPDATE_SUCCESS, + QString("Files to update: ") + m_filesToUpdate.join(',')); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAContinues("CHECK-FILES-TO-UPDATE", + m_updateStatus.m_statusDescription)); + m_ismasClient.setProgressInPercent(40); + if (updateFiles(50)) { + m_ismasClient.setProgressInPercent(50); + if (syncCustomerRepositoryAndFS()) { + m_ismasClient.setProgressInPercent(60); + if (sendIsmasLastVersionNotification()) { + m_ismasClient.setProgressInPercent(70); + sentIsmasLastVersionNotification = true; + if (saveLogFile()) { + m_ismasClient.setProgressInPercent(80); + + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSASucceeded("")); + + // mark update as activated -> this resets the WAIT button + m_ismasClient.setProgressInPercent(95); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAActivated()); + + m_returnCode = 0; + } else { + m_updateStatus = UpdateStatus(UPDATE_STATUS::SAVE_LOG_FILES_FAILED, + QString("Saving log files failed")); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + "SAVE-LOG-FILES", + m_updateStatus.m_statusDescription)); + m_returnCode = -11; + } + } else { + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_SEND_LAST_VERSION_FAILED, + QString("Sending ISMAS last version failed")); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + "ISMAS-SEND-LAST-VERSION", + m_updateStatus.m_statusDescription)); + m_returnCode = -10; + } + } else { + m_updateStatus = UpdateStatus(UPDATE_STATUS::RSYNC_UPDATES_FAILURE, + QString("Syncing files to update failed")); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + "RSYNC-UPDATE-FILES", + m_updateStatus.m_statusDescription)); + m_returnCode = -9; + } + } else { + m_updateStatus = UpdateStatus(UPDATE_STATUS::PSA_UPDATE_FILES_FAILED, + QString("Updating files failed")); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + "UPDATE-FILES", + m_updateStatus.m_statusDescription)); + m_returnCode = -8; + } + } else { + m_updateStatus = UpdateStatus(UPDATE_STATUS::GIT_FETCH_UPDATES_REQUEST_FAILURE, + QString("No files to update")); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + "FETCH-FILES-TO-UPDATE", + m_updateStatus.m_statusDescription)); + m_returnCode = -7; + } + } else { + m_updateStatus = UpdateStatus(UPDATE_STATUS::GIT_CHECKOUT_BRANCH_FAILURE, + QString("Configuring customer environment failed")); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + "GIT-CHECKOUT-BRANCH", + m_updateStatus.m_statusDescription)); + m_returnCode = -6; + } + } else { + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_SET_FAILURE, + QString("ISMAS update trigger wrong")); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + "CHECK-UPDATE-TRIGGER", + m_updateStatus.m_statusDescription)); + m_returnCode = -5; + } + } + + m_ismasClient.setProgressInPercent(100); + setProgress(100); + + if (m_returnCode != 0) { + stopProgressLoop(); + emit appendText(QString("UPDATE "), UPDATE_STEP_FAIL); + + m_updateStatus = UpdateStatus(UPDATE_STATUS::UPDATE_PROCESS_FAILURE, + QString("Update process failed")); + if (std::optional s = m_ismasClient.finalResult(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)) { + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + s.value()); + } + } else { + emit appendText(QString("UPDATE "), UPDATE_STEP_SUCCESS); + + m_updateStatus = UpdateStatus(UPDATE_STATUS::UPDATE_PROCESS_SUCCESS, + QString("Update process succeeded. Reset WAIT.")); + if (std::optional s = m_ismasClient.finalResult(IsmasClient::RESULT_CODE::SUCCESS, + m_updateStatus.m_statusDescription)) { + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + s.value()); + } + } + + if (!sentIsmasLastVersionNotification) { + // try even if the backend is not connected + sendIsmasLastVersionNotification(); + } + + m_updateProcessRunning = false; + emit enableExit(); + emit restartExitTimer(); +} + +std::optional Worker::getApismVersion() { + for (int repeat = 0; repeat < 10; ++repeat) { + qInfo() << "REPEAT" << repeat << "In getApismVersion() -> #M=APISM#C=REQ_SELF#J={}"; + std::optional result + = IsmasClient::sendRequestReceiveResponse( + IsmasClient::APISM::DIRECT_PORT, "#M=APISM#C=REQ_SELF#J={}"); + if (result) { + QString msg = result.value(); + qInfo() << "In getApismVersion() -> APISM response" << msg; + QJsonParseError parseError; + QJsonDocument document(QJsonDocument::fromJson(msg.toUtf8(), &parseError)); + if (parseError.error != QJsonParseError::NoError) { + qCritical() << "(1) INVALID JSON MSG: PARSING FAILED (msg=" << msg << "):" + << parseError.error << parseError.errorString(); + m_updateStatus = UpdateStatus(UPDATE_STATUS::JSON_PARSE_FAILURE, + QString("(2) INVALID JSON %1 %2 %3") + .arg(msg) + .arg(parseError.error) + .arg(parseError.errorString())); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.jsonParseFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + return std::nullopt; + } + if (!document.isObject()) { + qCritical() << "FILE IS NOT A JSON OBJECT!"; + m_updateStatus = UpdateStatus(UPDATE_STATUS::JSON_PARSE_FAILURE, + QString("NOT A JSON-OBJECT %1").arg(msg)); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.jsonParseFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + return std::nullopt; + } + QJsonObject obj = document.object(); + QStringList keys = obj.keys().filter("CMD_GET_APISMSTATUS_RESPONSE"); + if (keys.size() != 1) { + m_updateStatus = UpdateStatus(UPDATE_STATUS::BACKEND_CHECK_FAILURE, + "CMD_GET_APISMSTATUS_RESPONSE KEY NOT AVAILABLE"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("apism response", m_updateStatus.m_statusDescription); + return std::nullopt; + } else { + QString const key = keys.at(0); + QJsonValue v = obj.value(key); + return v.toObject().value("Version").toString(); + } + } else { + QThread::sleep(1); + } + } + return std::nullopt; +} + +#define CHECK_UPDATE_TRIGGER_SET "Check update trigger ..." + +bool Worker::updateTriggerSet() { + if (m_withoutIsmasDirectPort) { // useful for testing + return true; + } + + emit appendText("\n" CHECK_UPDATE_TRIGGER_SET); + QString triggerValue(""); + int const startMs = QTime::currentTime().msecsSinceStartOfDay(); + + for (int repeat = 1; repeat <= 100; ++repeat) { + + qInfo() << "UPDATE TRIGGER SET -> REPEAT" << repeat; + + if (repeat > 1) { + int const durationMs = QTime::currentTime().msecsSinceStartOfDay() - startMs; + QString const &msg = QString("elapsed: %1.%2s").arg(durationMs / 1000).arg(durationMs % 1000); + qInfo() << "REPEAT" << msg; + emit showErrorMessage("check update trigger", msg); + } else { + emit showErrorMessage("check update trigger", ""); + } + + if ((repeat % 10) == 0) { + qInfo() << "CHECK UPDATE TRIGGER. RESTART APISM ..."; + Command c("systemctl restart apism"); + if (c.execute("/tmp")) { + QThread::sleep(20); // give APISM some time to reconnect + qInfo() << "CHECK UPDATE TRIGGER. RESTARTING APISM DONE"; + } + } + + startProgressLoop(); + if (std::optional result + = IsmasClient::sendRequestReceiveResponse( + IsmasClient::APISM::DIRECT_PORT, "#M=APISM#C=REQ_ISMASPARAMETER#J={}")) { + stopProgressLoop(); + int progress = (m_mainWindow->progressValue()/10) + 10; + setProgress(progress); + + QString msg = result.value(); + + qInfo() << "REPEAT" << repeat << "APISM RESPONSE (" << msg << ")"; + + QJsonParseError parseError; + QJsonDocument document(QJsonDocument::fromJson(msg.toUtf8(), &parseError)); + if (parseError.error != QJsonParseError::NoError) { + qCritical() << "(2) INVALID JSON MSG: PARSING FAILED (msg=" << msg << "):" + << parseError.error << parseError.errorString(); + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::JSON_PARSE_FAILURE, + QString("(2) INVALID JSON %1 %2 %3") + .arg(msg) + .arg(parseError.error) + .arg(parseError.errorString())); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.jsonParseFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("check update trigger", + QString("invalid json ") + msg.mid(0, 20)); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; + } + if (!document.isObject()) { + qCritical() << "FILE IS NOT A JSON OBJECT!"; + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::JSON_PARSE_FAILURE, + QString("NOT A JSON-OBJECT %1").arg(msg)); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.jsonParseFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("check update trigger", QString("not a json object") + msg); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; + } + progress += 1; + setProgress(progress); + + QJsonObject obj = document.object(); + + // always look for an 'error' first + if (obj.contains("error")) { + progress += 1; + setProgress(progress); + QString value = obj.value("error").toString(); + emit showErrorMessage("check update trigger", QString("REPEAT %1 error=<").arg(repeat) + value + ">"); + qInfo() << "REPEAT" << repeat << "In updateTriggerSet() error=<" + << value << ">"; + QThread::sleep(6); + continue; + } + // sanity check: cust_nr and machine_nr of PSA correct ? + // note: this check has to be done here, as the cust_nr and the machine_nr + // of the PSA are sent by ISMAS. + if (obj.contains("Dev_ID")) { + QJsonValue v = obj.value("Dev_ID"); + if (v.isObject()) { + QJsonObject obj = v.toObject(); + if (obj.contains("Custom_ID") && obj.contains("Device_ID")) { + QJsonValue const c = obj.value("Custom_ID"); + QJsonValue const m = obj.value("Device_ID"); + int customerNr = c.toInt(-1); + int machineNr = m.toInt(-1); + if (customerNr != m_customerNr) { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + QString("CUSTOMER-NR (%1) != LOCAL CUSTOMER-NR (%2)") + .arg(customerNr).arg(m_customerNr)); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; + } + if (machineNr != m_machineNr) { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + QString("MACHINE-NR (%1) != LOCAL MACHINE-NR (%2)") + .arg(machineNr).arg(m_machineNr)); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; + } + + qInfo() << "MACHINE-AND-CUSTOMER-CHECK" << m_updateStatus.m_statusDescription; + + } else { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + "Dev_ID DOES NOT CONTAIN Custom_ID AND/OR Device_ID"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; + } + } else { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + "Dev_ID KEY NOT A JSON-OBJECT"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; + } + } else { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + "Dev_ID KEY NOT AVAILABLE"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; + } + progress += 1; + setProgress(progress); + + if (obj.contains("Fileupload")) { + QJsonValue v = obj.value("Fileupload"); + if (v.isObject()) { + obj = v.toObject(); + if (obj.contains("TRG")) { + triggerValue = obj.value("TRG").toString(); + + qInfo() << "REPEAT" << repeat + << "In updateTriggerSet() TRG value=<" + << triggerValue << ">"; + + if (triggerValue == "WAIT") { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_SANITY_CHECK_OK, + QString("MACHINE-NR (%1) AND CUST-NR (%2) OK") + .arg(m_machineNr).arg(m_customerNr)); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateOfPSAContinues("MACHINE-AND-CUSTOMER-CHECK", + m_updateStatus.m_statusDescription)); + + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_SET, + QString("UPDATE TRIGGER SET. CONTINUE. ")); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.updateTriggerSet(m_updateStatus.m_statusDescription, "")); + + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_DONE); + return true; + } else + if (QRegExp("\\s*").exactMatch(triggerValue)) { // check for whitespace + stopProgressLoop(); + int progress = (m_mainWindow->progressValue()/10) + 10; + progress += 1; + setProgress(progress); + emit showErrorMessage("check update trigger", "empty update-trigger"); + QThread::sleep(6); + continue; + } else { + // if the download-button once has the wrong value, it will never recover + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + QString("TRIGGER-VALUE=<") + triggerValue + "> NOT 'WAIT'"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; + } + } else { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + "TRG KEY NOT AVAILABLE"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + return false; + } + } else { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + "Fileupload NOT A JSON-OBJECT"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + return false; + } + } else { + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE, + "Fileupload KEY NOT AVAILABLE"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.sanityCheckFailed(IsmasClient::RESULT_CODE::INSTALL_ERROR, + m_updateStatus.m_statusDescription)); + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + emit showErrorMessage("check update trigger", m_updateStatus.m_statusDescription); + return false; + } + } else { + stopProgressLoop(); + int progress = (m_mainWindow->progressValue()/10) + 10; + progress += 1; + setProgress(progress); + emit showErrorMessage("check update trigger", "no ISMAS response"); + QThread::sleep(6); + } + } + + setProgress(100); + m_updateStatus = UpdateStatus(UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_NOT_SET_OR_WRONG, + QString("ISMAS_UPDATE-TRIGGER-NOT-SET-OR-WRONG: VALUE=(") + + triggerValue + ")"); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.errorUpdateTrigger(m_updateStatus.m_statusDescription, "")); + + emit replaceLast(CHECK_UPDATE_TRIGGER_SET, UPDATE_STEP_FAIL); + return false; +} + +bool Worker::customerEnvironment() { + emit appendText("\nPrepare customer environment ..."); + if (QDir(m_customerRepository).exists()) { + startProgressLoop(); + if (m_gc.gitCheckoutBranch()) { + stopProgressLoop(); + int progress = (m_mainWindow->progressValue()/10) + 10; + setProgress(progress); + + m_updateStatus = UpdateStatus(UPDATE_STATUS::GIT_CHECKOUT_BRANCH, + QString("CHECKED-OUT BRANCH ") + m_gc.branchName()); + + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.checkoutBranch(m_updateStatus.m_statusDescription, "")); + + setProgress(100); + emit replaceLast("Prepare customer environment ...", UPDATE_STEP_DONE); + qInfo() << "PREPARE CUSTOMER ENVIRONMENT DONE"; + return true; + } else { + stopProgressLoop(); + int progress = (m_mainWindow->progressValue()/10) + 10; + setProgress(progress); + emit showErrorMessage("cust-env", + QString("Checkout ") + m_customerRepository + " failed"); + Utils::printCriticalErrorMsg(QString("CHECKOUT OF " + m_customerRepository + "FAILED")); + } + } else { + emit showErrorMessage("cust-env", m_customerRepository + " does not exist"); + Utils::printCriticalErrorMsg(m_customerRepository + " DOES NOT EXIST"); + } + + setProgress(100); + emit replaceLast("Prepare customer environment ...", UPDATE_STEP_FAIL); + return false; +} + +bool Worker::filesToUpdate() { + emit appendText("\nFetch changes files ..."); + startProgressLoop(); + if (std::optional changes = m_gc.gitFetch()) { + stopProgressLoop(); + int progress = (m_mainWindow->progressValue()/10) + 10; + setProgress(progress); + + m_updateStatus = UpdateStatus(UPDATE_STATUS::GIT_FETCH_UPDATES, + QString("FETCHING OF ") + m_customerRepositoryPath + + QString(" INTO ") + m_customerRepository); + + setProgress(progress + 10); + if (std::optional changedFileNames = m_gc.gitDiff(changes.value())) { + setProgress(progress + 20); + if (m_gc.gitPull()) { + emit replaceLast(QString("Fetch changes files ..."), UPDATE_STEP_DONE); + m_filesToUpdate = changedFileNames.value(); + + Utils::printInfoMsg("FILES-TO-UPDATE " + m_filesToUpdate.join(',')); + + int const size = m_filesToUpdate.size(); + if (size > 1) { + emit appendText(QString("Found %1 files to update :").arg(size), UPDATE_STEP_DONE); + for (int i = 0; i < size; ++i) { + emit appendText(QString("\n ") + m_filesToUpdate.at(i)); + } + } else { + emit appendText("Found 1 file to update :", UPDATE_STEP_DONE); + emit appendText(QString("\n ") + m_filesToUpdate.at(0)); + } + return true; + } + emit showErrorMessage("files to update", "pulling files failed"); + Utils::printCriticalErrorMsg("PULLING FILES FAILED"); + + } else { + emit showErrorMessage("files to update", "no files to update (checked-in any files?)"); + Utils::printCriticalErrorMsg("NO FILES TO UPDATE (CHECKED IN ANY FILES?)"); + } + setProgress(progress + 30); + } else { + stopProgressLoop(); + int progress = (m_mainWindow->progressValue()/10) + 10; + setProgress(progress + 30); + + emit showErrorMessage("files to update", + QString("no changes in ") + m_customerRepository + + " (checked-in any files?)"); + + Utils::printCriticalErrorMsg("NO CHANGES IN " + + m_customerRepository + + " (CHECKED IN ANY FILES?)"); + } + + emit replaceLast(QString("Fetch changes files ..."), UPDATE_STEP_FAIL); + setProgress(100); + return false; +} + +bool Worker::updateFiles(quint8 percent) { + QStringList filesToDownload; + m_displayIndex = 0; + startProgressLoop(); + for (int i = 0; i < m_filesToUpdate.size(); ++i) { + QString const fName = m_filesToUpdate.at(i); + Utils::printInfoMsg(QString("FNAME ") + fName); + + if (fName.contains("opkg_commands", Qt::CaseInsensitive)) { + emit appendText("\n( ) Update opkg pakets ..."); + // execute opkg commands + if (QDir::setCurrent(m_customerRepository)) { + QFile f(fName); + if (f.exists()) { + if (f.open(QIODevice::ReadOnly)) { + QTextStream in(&f); + int cmdCount = 0; + QStringList opkgCommands; + while (!in.atEnd()) { + QString line = in.readLine(); + static const QRegularExpression comment("^\\s*#.*$"); + if (line.indexOf(comment, 0) == -1) { + // found opkg command + QString opkgCommand = line.trimmed(); + ++cmdCount; + executeOpkgCommand(opkgCommand); + QString cmd = "\n " + opkgCommand; + emit appendText(cmd); + opkgCommands << cmd; + + m_ismasClient.setProgressInPercent(++percent); + m_updateStatus = UpdateStatus(UPDATE_STATUS::EXEC_OPKG_COMMAND, + QString("EXEC OPKG-COMMAND ") + opkgCommand); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.execOpkgCommand(m_updateStatus.m_statusDescription, "")); + } + } + f.close(); + if (cmdCount > 0) { + m_displayIndex = 1; + QString prepend = QString("(") + QString("%1").arg(m_displayIndex).rightJustified(2, ' ') + QString(")") + + QString(" Update opkg pakets ... "); + opkgCommands.prepend(prepend); + emit replaceLast(opkgCommands, UPDATE_STEP_DONE); + } else { + m_displayIndex = 1; + emit replaceLast(QString("(") + QString("%1").arg(m_displayIndex).rightJustified(2, ' ') + QString(")") + + QString(" Update opkg pakets ... "), UPDATE_STEP_FAIL); + } + } + } + } + } else + if (fName.contains("print", Qt::CaseInsensitive)) { + filesToDownload << fName; // download printer-config-files + } else { + static const QRegularExpression version("^.*dc2c[.][0-9]{1,2}[.][0-9]{1,2}[.]bin.*$"); + if (fName.contains(version)) { + filesToDownload << fName; // download device controller + } + } + } + + stopProgressLoop(); + setProgress(100); + + if (filesToDownload.size() > 0) { + Utils::printInfoMsg(QString("FILES_TO_DOWNLOAD_TO_PSA_HW ") + filesToDownload.join(',')); + + Update *update = m_mainWindow->getUpdate(); + if (update) { + return update->doUpdate(m_displayIndex, filesToDownload); + } + } else { + Utils::printCriticalErrorMsg("NO FILES_TO_DOWNLOAD_TO_PSA_HW"); + } + + return true; +} + +bool Worker::syncCustomerRepositoryAndFS() { + // this step is currently needed only for updating tariff-files + setProgress(0); + emit appendText("\nSync customer environment with filesystem ..."); + if (QDir(m_customerRepository).exists()) { + if (QDir::setCurrent(m_customerRepository)) { + Command md("bash"); + if (!md.execute(m_customerRepository, + QStringList() << "-c" << "mkdir -p /etc/psa_config /etc/psa_update /etc/dc /etc/psa_tariff")) { + qCritical() << "COULD NOT EXECUTE '" << md.command() << "' exitCode=(" << md.exitCode() << ")"; + } + int progress = 10; + setProgress(progress); + QString const params("-vvv " + "--recursive " + "--progress " + "--checksum " + "--exclude=.* " + "--include=*.bin " + "--include=*.json " + "--include=opkg_commands " + "--include=*.ini"); + QStringList cmds; + cmds << QString("rsync ") + params.simplified() + " etc/ /etc"; + cmds << QString("rsync ") + params.simplified() + " opt/ /opt"; + + QString cmd; + bool error = false; + foreach (cmd, cmds) { + progress += 5; + setProgress(progress); + if (!error) { + Command c("bash"); + qInfo() << "EXECUTING CMD..." << cmd; + if (c.execute(m_customerRepository, QStringList() << "-c" << cmd)) { + QStringList result = c.getCommandResult().split('\n'); + QString const &p1 = "send_files mapped "; + QString const &p2 = "of size"; + for (int i = 0; i < result.size(); ++i) { + QString line = result.at(i); + qInfo() << line; + + // "send_files mapped etc/psa_tariff/tariff01.json of size 19339" + int sendFilesAtPos = line.indexOf(p1); + int ofSizeAtPos = line.indexOf(p2); + if (sendFilesAtPos != -1 && ofSizeAtPos != -1) { + sendFilesAtPos += p1.length(); + QString const &s = line.mid(sendFilesAtPos, ofSizeAtPos - sendFilesAtPos).trimmed(); + m_updateStatus = UpdateStatus(UPDATE_STATUS::RSYNC_FILE_SUCCESS, + QString("RSYNC FILE ") + s.split("/").last() + + " LAST-COMMIT: " + m_gc.gitLastCommit(s)); + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_EVENT#J=") + + m_ismasClient.rsyncFile(m_updateStatus.m_statusDescription, "")); + } + } + } else { + Utils::printCriticalErrorMsg(QString("CMD ") + cmd + " FAILED: " + + c.getCommandResult() + QString(" EXIT_CODE=(%1)").arg(c.exitCode())); + error = true; + } + } + } + progress += 5; + setProgress(progress); + if (!error) { + // now check tariff-files in etc and /etc/psa_tariff + QDir dir1(QDir::cleanPath(m_customerRepository + QDir::separator() + "etc/psa_tariff")); + QDir dir2("/etc/psa_tariff"); + if (Utils::sameFilesInDirs(dir1, dir2)) { + setProgress(100); + emit replaceLast(QString("Sync customer environment with filesystem ..."), UPDATE_STEP_DONE); + return true; + } else { + // TODO: send message to ISMAS + } + } + } + } + setProgress(100); + emit replaceLast(QString("Sync customer environment with filesystem ..."), UPDATE_STEP_FAIL); + return false; +} + +bool Worker::sendIsmasLastVersionNotification() { + IsmasClient::sendRequestReceiveResponse(IsmasClient::APISM::DB_PORT, + QString("#M=APISM#C=CMD_SENDVERSION#J=") + + m_ismasClient.updateOfPSASendVersion(getPSAInstalled())); + emit appendText(QString("Send last version info "), UPDATE_STEP_DONE); + return true; +} + +bool Worker::saveLogFile() { + return true; +} +QString Worker::getOsVersion() const { + QString const cmd = QString("echo -n $(cat /etc/os-release | head -n 1 | cut -d'\"' -f2 | tr -d '\"')"); + Command c("bash"); + if (c.execute(m_workingDirectory, QStringList() << "-c" << cmd)) { + return c.getCommandResult(); + } + return "N/A"; +} + +QString Worker::getATBQTVersion() const { + QString const cmd = QString("echo -n $(/opt/app/ATBAPP/ATBQT -v | head -n 2 | cut -d':' -f2)"); + Command c("bash"); + if (c.execute(m_workingDirectory, QStringList() << "-c" << cmd)) { + return c.getCommandResult(); + } + return "N/A"; +} + +QString Worker::getCPUSerial() const { + QString const cmd = QString("echo -n $(cat /proc/cpuinfo | grep -i Serial | cut -d':' -f2)"); + Command c("bash"); + if (c.execute(m_workingDirectory, QStringList() << "-c" << cmd)) { + return c.getCommandResult(); + } + return "N/A"; +} + +QString Worker::getRaucVersion() const { + QString const cmd = QString("echo -n $(rauc --version)"); + Command c("bash"); + if (c.execute(m_workingDirectory, QStringList() << "-c" << cmd)) { + return c.getCommandResult(); + } + return "N/A"; +} + +QString Worker::getOpkgVersion() const { + QString const cmd = QString("echo -n $(opkg --version)"); + Command c("bash"); + if (c.execute(m_workingDirectory, QStringList() << "-c" << cmd)) { + return c.getCommandResult(); + } + return "N/A"; +} + +QString Worker::getPluginVersion(QString const &pluginFileName) const { + QString const cmd = QString("echo -n $(strings %1 | grep \\\"Version\\\" | cut -d':' -f2 | tr -d '\"' | tr -d ',')").arg(pluginFileName); + Command c("bash"); + if (c.execute(m_workingDirectory, QStringList() << "-c" << cmd)) { + return c.getCommandResult(); + } + return "N/A"; +} + +QStringList Worker::getDCVersion() const { + QStringList lst = (QStringList() << "N/A" << "N/A"); + hwinf *hwi = m_mainWindow->getPlugin(); + if (hwi) { + hwi->dc_autoRequest(true); // turn auto-request setting on + + QByteArray const cmp(8, char(0)); + QByteArray hw(""), sw(""); + for (int i=0; i<5; ++i) { + hw = hwi->dc_getHWversion().toUtf8(); + sw = hwi->dc_getSWversion().toUtf8(); + if (!hw.startsWith(cmp)) { + lst.clear(); + qInfo() << hw << sw; + lst << hw << sw; + break; + } + QThread::sleep(1); + } + } + return lst; +} + +qint64 Worker::getFileSize(QString const &fileName) const { + // fileName has to be an absolute path + QFileInfo fInfo(fileName); + return fInfo.exists() ? fInfo.size() : -1; +} + +bool Worker::executeOpkgCommand(QString opkgCommand) { + Command c(opkgCommand); + if (c.execute(m_workingDirectory)) { + QString const r = c.getCommandResult(); + Utils::printInfoMsg(QString("EXECUTE OPKG COMMAND %1 OK: %2") + .arg(opkgCommand) + .arg(c.getCommandResult())); + return true; + } else { + Utils::printCriticalErrorMsg(QString("EXECUTE OPKG COMMAND %1 FAILED") + .arg(opkgCommand)); + } + return false; +} + +PSAInstalled Worker::getPSAInstalled() { + QStringList const dcVersion = getDCVersion(); + QString const deviceControllerVersionHW = dcVersion.first(); + QString const deviceControllerVersionSW = dcVersion.last(); + + qInfo() << "CURRENT DC-HW-VERSION: " << deviceControllerVersionHW; + qInfo() << "CURRENT DC-SW-VERSION: " << deviceControllerVersionSW; + + QString const deviceControllerGitBlob = "N/A"; + QString const deviceControllerGitLastCommit = "N/A"; + + PSAInstalled psaInstalled; + QString printSysDir("/etc/psa_config"); + QString tariffSysDir("/etc/psa_tariff"); + QString tariffRepoDir("etc/psa_tariff"); + QString opkgSysDir("/etc/psa_update"); + QString opkgRepoDir("etc/psa_update"); + QString const &absPathNameOpkg = QDir::cleanPath(opkgSysDir + QDir::separator() + "opkg_commands"); + QString const &absPathNameRepositoryOpkg = QDir::cleanPath(opkgRepoDir + QDir::separator() + "opkg_commands"); + QString absPathName; + QString absPathNameRepository; + + psaInstalled.versionInfo.lastCommit = ""; + psaInstalled.versionInfo.reason = ""; + psaInstalled.versionInfo.created = ""; + + QStringList versionInfo = m_gc.gitShowReason(); + if (versionInfo.size() == 3) { + psaInstalled.versionInfo.lastCommit = versionInfo.at(0); + psaInstalled.versionInfo.reason = versionInfo.at(1); + psaInstalled.versionInfo.created = versionInfo.at(2); + } + + if (m_zoneNr != 0) { + QString const &n = QString("%1").arg(m_zoneNr).rightJustified(2, '0'); + psaInstalled.tariff.name = QString("tariff%1.json").arg(n); + absPathName = QDir::cleanPath(tariffSysDir + QDir::separator() + psaInstalled.tariff.name); + psaInstalled.tariff.blob = m_gc.gitBlob(absPathName); + absPathNameRepository = QDir::cleanPath(tariffRepoDir + QDir::separator() + psaInstalled.tariff.name); + psaInstalled.tariff.lastCommit = m_gc.gitLastCommit(absPathNameRepository); + psaInstalled.tariff.size = getFileSize(absPathName); + psaInstalled.tariff.zone = m_zoneNr; + psaInstalled.tariff.loadTime = Utils::getTariffLoadTime(absPathName); + } + psaInstalled.tariff.project = "Szeged"; + psaInstalled.tariff.info = "N/A"; + psaInstalled.tariff.version = "N/A"; + + psaInstalled.hw.linuxVersion = m_osVersion; + psaInstalled.hw.cpuSerial = m_cpuSerial; + + psaInstalled.opkg.blob = m_gc.gitBlob(absPathNameOpkg); + psaInstalled.opkg.size = getFileSize(absPathNameOpkg); + psaInstalled.opkg.loadTime = Utils::getTariffLoadTime(absPathNameOpkg); + psaInstalled.opkg.lastCommit = m_gc.gitLastCommit(absPathNameRepositoryOpkg); + + psaInstalled.dc.versionHW = deviceControllerVersionHW; + psaInstalled.dc.versionSW = deviceControllerVersionSW; + psaInstalled.dc.gitBlob = "N/A"; + psaInstalled.dc.gitLastCommit = "N/A"; + psaInstalled.dc.size = -1; + + psaInstalled.sw.apismVersion = m_apismVersion; + psaInstalled.sw.atbQTVersion = m_atbqtVersion; + + psaInstalled.pluginVersion.deviceController = m_pluginVersionATBDeciceController; + psaInstalled.pluginVersion.ingenicoISelfCC = m_pluginVersionIngenicoISelf; + psaInstalled.pluginVersion.mobilisisCalculatePrice = m_pluginVersionMobilisisCalc; + psaInstalled.pluginVersion.mobilisisCalculatePriceConfigUi = m_pluginVersionMobilisisCalcConfig; + psaInstalled.pluginVersion.prmCalculatePrice = m_pluginVersionPrmCalc; + psaInstalled.pluginVersion.prmCalculatePriceConfigUi = m_pluginVersionPrmCalcConfig; + psaInstalled.pluginVersion.tcpZVT = m_pluginVersionTcpZvt; + + psaInstalled.cash.name = "DC2C_cash.json"; + absPathName = QDir::cleanPath(printSysDir + QDir::separator() + psaInstalled.cash.name); + psaInstalled.cash.blob = m_gc.gitBlob(absPathName); + psaInstalled.cash.size = getFileSize(absPathName); + + psaInstalled.conf.name = "DC2C_conf.json"; + absPathName = QDir::cleanPath(printSysDir + QDir::separator() + psaInstalled.conf.name); + psaInstalled.conf.blob = m_gc.gitBlob(absPathName); + psaInstalled.conf.size = getFileSize(absPathName); + + psaInstalled.device.name = "DC2C_device.json"; + absPathName = QDir::cleanPath(printSysDir + QDir::separator() + psaInstalled.device.name); + psaInstalled.device.blob = m_gc.gitBlob(absPathName); + psaInstalled.device.size = getFileSize(absPathName); + + for (int i=0; i < 32; ++i) { + QString const &n = QString("%1").arg(i+1).rightJustified(2, '0'); + psaInstalled.print[i].name = QString("DC2C_print%1.json").arg(n); + absPathName = QDir::cleanPath(printSysDir + QDir::separator() + psaInstalled.print[i].name); + psaInstalled.print[i].blob = m_gc.gitBlob(absPathName); + psaInstalled.print[i].size = getFileSize(absPathName); + } + + return psaInstalled; +} + +hwinf *Worker::getPlugin() { + return m_mainWindow ? m_mainWindow->getPlugin() : nullptr; +} + +hwinf const *Worker::getPlugin() const { + return m_mainWindow ? m_mainWindow->getPlugin() : nullptr; +} + +/************************************************************************************************ + * operators + */ +QDebug operator<< (QDebug debug, UpdateStatus status) { + switch(status.m_updateStatus) { + case UPDATE_STATUS::ISMAS_SEND_LAST_VERSION_FAILED: + debug << QString("UPDATE_STATUS::ISMAS_SEND_LAST_VERSION_FAILED: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::SAVE_LOG_FILES_FAILED: + debug << QString("UPDATE_STATUS::SAVE_LOG_FILES_FAILED: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CHECK_FILES_TO_UPDATE_SUCCESS: + debug << QString("UPDATE_STATUS::GIT_CHECK_FILES_TO_UPDATE_SUCCESS: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::PSA_UPDATE_FILES_FAILED: + debug << QString("UPDATE_STATUS::PSA_UPDATE_FILES_FAILED: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::RSYNC_UPDATES_SUCCESS: + debug << QString("UPDATE_STATUS::RSYNC_UPDATES_SUCCESS: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::RSYNC_UPDATES_FAILURE: + debug << QString("UPDATE_STATUS::RSYNC_UPDATES_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::EXEC_OPKG_COMMAND: + debug << QString("UPDATE_STATUS::EXEC_OPKG_COMMAND: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_SUCCESS: + debug << QString("UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_SUCCESS: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_FAILURE: + debug << QString("UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::NOT_DEFINED: + debug << QString("UPDATE_STATUS::NOT_DEFINED: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::UPDATE_PROCESS_FAILURE: + debug << QString("UPDATE_STATUS::UPDATE_PROCESS_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_SET_FAILURE: + debug << QString("UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CHECKOUT_BRANCH_FAILURE: + debug << QString("UPDATE_STATUS::GIT_CHECKOUT_BRANCH_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CHECKOUT_BRANCH: + debug << QString("UPDATE_STATUS::GIT_CHECKOUT_BRANCH: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_NOT_SET_OR_WRONG: + debug << QString("UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_NOT_SET_OR_WRONG: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_SET: + debug << QString("UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_SET: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_SANITY_CHECK_OK: + debug << QString("UPDATE_STATUS::ISMAS_SANITY_CHECK_OK: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::JSON_PARSE_FAILURE: + debug << QString("UPDATE_STATUS::JSON_PARSE_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::BACKEND_CHECK: + debug << QString("UPDATE_STATUS::BACKEND_CHECK: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::BACKEND_CHECK_FAILURE: + debug << QString("UPDATE_STATUS::BACKEND_CHECK_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::BACKEND_NOT_CONNECTED: + debug << QString("UPDATE_STATUS::BACKEND_NOT_CONNECTED: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::UPDATE_PROCESS_SUCCESS: + debug << QString("UPDATE_STATUS::UPDATE_PROCESS_SUCCESS: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_PENDING: + debug << QString("UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_PENDING: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE: + debug << QString("UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_FETCH_UPDATES: + debug << QString("UPDATE_STATUS::GIT_FETCH_UPDATES: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_FETCH_UPDATES_REQUEST_FAILURE: + debug << QString("UPDATE_STATUS::GIT_FETCH_UPDATES_REQUEST_FAILURE: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_RESPONSE_RECEIVED: + debug << QString("UPDATE_STATUS::ISMAS_RESPONSE_RECEIVED: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::RSYNC_FILE_SUCCESS: + debug << QString("UPDATE_STATUS::RSYNC_FILE_SUCCESS: ") + << status.m_statusDescription; + break; + case UPDATE_STATUS::EXEC_OPKG_COMMANDS: + debug << QString("UPDATE_STATUS::EXEC_OPKG_COMMANDS: ") + << status.m_statusDescription; + break; + // default:; + } + return debug; +} + +QString& operator<< (QString& str, UpdateStatus status) { + switch(status.m_updateStatus) { + case UPDATE_STATUS::ISMAS_SEND_LAST_VERSION_FAILED: + str = QString("UPDATE_STATUS::ISMAS_SEND_LAST_VERSION_FAILED: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::SAVE_LOG_FILES_FAILED: + str = QString("UPDATE_STATUS::SAVE_LOG_FILES_FAILED: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CHECK_FILES_TO_UPDATE_SUCCESS: + str = QString("UPDATE_STATUS::GIT_CHECK_FILES_TO_UPDATE_SUCCESS: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::PSA_UPDATE_FILES_FAILED: + str = QString("UPDATE_STATUS::PSA_UPDATE_FILES_FAILED: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::RSYNC_UPDATES_SUCCESS: + str = QString("UPDATE_STATUS::RSYNC_UPDATES_SUCCESS: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::RSYNC_UPDATES_FAILURE: + str = QString("UPDATE_STATUS::RSYNC_UPDATES_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::EXEC_OPKG_COMMAND: + str = QString("UPDATE_STATUS::EXEC_OPKG_COMMAND: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_SUCCESS: + str = QString("UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_SUCCESS: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_FAILURE: + str = QString("UPDATE_STATUS::GIT_CLONE_AND_CHECKOUT_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::NOT_DEFINED: + str = QString("UPDATE_STATUS::NOT_DEFINED: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::UPDATE_PROCESS_FAILURE: + str = QString("UPDATE_STATUS::UPDATE_PROCESS_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_SET_FAILURE: + str = QString("UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CHECKOUT_BRANCH_FAILURE: + str = QString("UPDATE_STATUS::GIT_CHECKOUT_BRANCH_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_CHECKOUT_BRANCH: + str = QString("UPDATE_STATUS::GIT_CHECKOUT_BRANCH: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_NOT_SET_OR_WRONG: + str = QString("UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_NOT_SET_OR_WRONG: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE: + str = QString("UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_SET: + str = QString("UPDATE_STATUS::ISMAS_UPDATE_TRIGGER_SET: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_SANITY_CHECK_OK: + str = QString("UPDATE_STATUS::ISMAS_SANITY_CHECK_OK: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::JSON_PARSE_FAILURE: + str = QString("UPDATE_STATUS::JSON_PARSE_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::BACKEND_CHECK: + str = QString("UPDATE_STATUS::BACKEND_CHECK: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::BACKEND_CHECK_FAILURE: + str = QString("UPDATE_STATUS::BACKEND_CHECK_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::BACKEND_NOT_CONNECTED: + str = QString("UPDATE_STATUS::BACKEND_NOT_CONNECTED: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::UPDATE_PROCESS_SUCCESS: + str = QString("UPDATE_STATUS::UPDATE_PROCESS_SUCCESS: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_PENDING: + str = QString("UPDATE_STATUS::ISMAS_WAIT_STATE_CHECK_PENDING: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_FETCH_UPDATES: + str = QString("UPDATE_STATUS::GIT_FETCH_UPDATES: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::GIT_FETCH_UPDATES_REQUEST_FAILURE: + str = QString("UPDATE_STATUS::GIT_FETCH_UPDATES_REQUEST_FAILURE: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::ISMAS_RESPONSE_RECEIVED: + str = QString("UPDATE_STATUS::ISMAS_RESPONSE_RECEIVED: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::EXEC_OPKG_COMMANDS: + str = QString("UPDATE_STATUS::EXEC_OPKG_COMMANDS: "); + str += status.m_statusDescription; + break; + case UPDATE_STATUS::RSYNC_FILE_SUCCESS: + str = QString("UPDATE_STATUS::RSYNC_FILE_SUCCESS: "); + str += status.m_statusDescription; + break; + //default:; + } + return str; } diff --git a/worker.h b/worker.h index ced641e..2ecadfb 100644 --- a/worker.h +++ b/worker.h @@ -3,28 +3,205 @@ #include #include +#include #include +#include +#include +#include + +#include #include "worker_thread.h" +#include "update.h" +#include "git/git_client.h" +#include "ismas/ismas_client.h" +#ifdef PTU5 +#define SERIAL_PORT "ttymxc2" +#else +#define SERIAL_PORT "ttyUSB0" +#endif + + +enum class UPDATE_STATUS : quint8 { + NOT_DEFINED, + ISMAS_WAIT_STATE_CHECK_PENDING, + ISMAS_WAIT_STATE_CHECK_FAILURE, + ISMAS_RESPONSE_RECEIVED, + BACKEND_CHECK, + BACKEND_CHECK_FAILURE, + BACKEND_NOT_CONNECTED, + ISMAS_UPDATE_TRIGGER_SET, + ISMAS_UPDATE_TRIGGER_NOT_SET_OR_WRONG, + GIT_CLONE_AND_CHECKOUT_SUCCESS, + GIT_CLONE_AND_CHECKOUT_FAILURE, + GIT_CHECKOUT_BRANCH, + GIT_CHECKOUT_BRANCH_FAILURE, + GIT_FETCH_UPDATES, + GIT_FETCH_UPDATES_REQUEST_FAILURE, + EXEC_OPKG_COMMAND, + EXEC_OPKG_COMMANDS, + RSYNC_UPDATES_FAILURE, + RSYNC_UPDATES_SUCCESS, + RSYNC_FILE_SUCCESS, + JSON_PARSE_FAILURE, + UPDATE_PROCESS_SUCCESS, + UPDATE_PROCESS_FAILURE, + ISMAS_SANITY_CHECK_OK, + ISMAS_UPDATE_TRIGGER_SET_FAILURE, + PSA_UPDATE_FILES_FAILED, + GIT_CHECK_FILES_TO_UPDATE_SUCCESS, + ISMAS_SEND_LAST_VERSION_FAILED, + SAVE_LOG_FILES_FAILED +}; + +struct UpdateStatus { + UPDATE_STATUS m_updateStatus; + QString m_statusDescription; + + explicit UpdateStatus(UPDATE_STATUS s = UPDATE_STATUS::NOT_DEFINED, + QString const &d = QString("")) + : m_updateStatus(s), m_statusDescription(d) {} +}; + +QDebug operator<<(QDebug debug, UpdateStatus status); +QString& operator<<(QString &str, UpdateStatus status); + +#define ISMAS_UPDATE_REQUESTS (10) + +class MainWindow; +class hwinf; class Worker : public QObject { Q_OBJECT - QString m_update_ctrl_file; - QString m_workingDir; WorkerThread m_workerThread; - QTimer m_timer; + int const m_customerNr; + QString const m_customerNrStr; + int const m_machineNr; + int const m_zoneNr; + QString const m_pluginName; + QString const m_workingDirectory; + QString const m_branchName; + QString const m_customerRepositoryPath; + QString const m_customerRepository; + bool const m_dryRun; + QObject *m_parent; + QString const m_serialInterface; + QString const m_baudrate; + IsmasClient m_ismasClient; + GitClient m_gc; + QString const m_osVersion; + QString const m_atbqtVersion; + QString const m_cpuSerial; + QString const m_pluginVersionATBDeciceController; + QString const m_pluginVersionIngenicoISelf; + QString const m_pluginVersionMobilisisCalc; + QString const m_pluginVersionMobilisisCalcConfig; + QString const m_pluginVersionPrmCalc; + QString const m_pluginVersionPrmCalcConfig; + QString const m_pluginVersionTcpZvt; + + int m_ismasUpdateRequests; + QTimer m_waitForNewUpdates; + + UpdateStatus m_updateStatus; + + QStringList m_filesToUpdate; + bool m_updateProcessRunning; + int m_displayIndex; + int m_returnCode; + + MainWindow *m_mainWindow; + int m_progressValue; + bool m_withoutIsmasDirectPort; + QString m_apismVersion; + + bool executeOpkgCommand(QString opkgCommand); + QString getOsVersion() const; + QString getATBQTVersion() const; + QString getCPUSerial() const; + QString getRaucVersion() const; + QString getOpkgVersion() const; + QString getPluginVersion(QString const &pluginFileName) const; + QStringList getDCVersion() const; + + qint64 getFileSize(QString const &fileName) const; + public: - explicit Worker(QString update_ctrl_file, QString workingDir); + static const QString UPDATE_STEP_OK; + static const QString UPDATE_STEP_DONE; + static const QString UPDATE_STEP_FAIL; + static const QString UPDATE_STEP_SUCCESS; + + explicit Worker(int customerNr, // 281 + int machineNr, + int zoneNr, + QString branchName, + QString pluginName, + QString workingDir = ".", + bool dryRun = false, + QObject *parent = nullptr, + char const *serialInterface = SERIAL_PORT, + char const *baudrate = "115200"); ~Worker(); - void quit() { return m_workerThread.quit(); } + + void setMainWindow(MainWindow *mainWindow) { m_mainWindow = mainWindow; } + hwinf *getPlugin(); + hwinf const *getPlugin() const; + void setProgress(int progress); + void startProgressLoop(); + void stopProgressLoop(); + + IsmasClient &getIsmasClient() { return m_ismasClient; } + IsmasClient const &getIsmasClient() const { return m_ismasClient; } + + bool updateProcessRunning() const { return m_updateProcessRunning; } + int returnCode() const { return m_returnCode; } + + int machineNr() const { return m_machineNr; } + int customerNr() const { return m_customerNr; } + int zoneNr() const { return m_zoneNr; } + QString apismVersion() const { return m_apismVersion; } + + MainWindow *mainWindow() { return m_mainWindow; } + MainWindow const *mainWindow() const { return m_mainWindow; } + + //friend QDebug operator<<(QDebug debug, Worker const &w) { + // Q_UNUSED(w); + // return debug; + //} + //friend QString& operator<<(QString &str, Worker const &w) { + // Q_UNUSED(w); + // return str; + //} signals: - void workNow(); + void appendText(QString, QString suffix = ""); + void replaceLast(QString, QString); + void replaceLast(QStringList, QString); + void showErrorMessage(QString title, QString description); + void showStatusMessage(QString title, QString description); + void stopStartTimer(); + void restartExitTimer(); + void enableExit(); + void disableExit(); public slots: - void work(); void update(); + +private slots: + bool updateTriggerSet(); + bool customerEnvironment(); + bool filesToUpdate(); + bool updateFiles(quint8 percent); + bool syncCustomerRepositoryAndFS(); + bool sendIsmasLastVersionNotification(); + bool saveLogFile(); + +private: + PSAInstalled getPSAInstalled(); + void privateUpdate(); + std::optional getApismVersion(); }; #endif // WORKER_H_INCLUDED