Merge branch 'master' of git.mimbach49.de:GerhardHoffmann/UpdatePTUDevCtrl

This commit is contained in:
Siegfried Siegert 2023-09-21 15:50:19 +02:00
commit a8dd9d7e24
Signed by: SiegfriedSiegert
GPG Key ID: 68371E015E8F0B03
27 changed files with 5322 additions and 766 deletions

View File

@ -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
}

126
allgemein.txt Normal file
View File

@ -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

137
apism/ismas_data.h Normal file
View File

@ -0,0 +1,137 @@
#ifndef ISMASDATA_H
#define ISMASDATA_H
#include <QJsonObject>
#include <QJsonArray>
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

376
git/git_client.cpp Normal file
View File

@ -0,0 +1,376 @@
#include "git_client.h"
#include "update.h"
#include "worker.h"
#include "utils.h"
#include <QRegularExpression>
#include <QDebug>
#include <QDir>
GitClient::GitClient(QString const &customerNrStr,
QString const &customerRepository,
QString const &workingDirectory,
QString const &branchName,
QObject *parent)
: QObject(parent)
, m_worker(qobject_cast<Worker *>(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<QStringList> 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<QString> 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<QStringList> 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")
//}

59
git/git_client.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef GIT_CLIENT_H_INCLUDED
#define GIT_CLIENT_H_INCLUDED
#include <QObject>
#include <QStringList>
#include <optional>
#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<QString> gitFetch();
bool gitFetchAndDiff();
bool gitPull();
std::optional<QStringList> gitDiff(QString const &commit);
std::optional<QStringList> 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

904
ismas/ismas_client.cpp Normal file
View File

@ -0,0 +1,904 @@
#include "ismas/ismas_client.h"
#include "utils.h"
#include <cstring>
#include <cstdio>
#include <errno.h>
#include <arpa/inet.h> // inet_addr()
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <strings.h> // bzero()
#include <sys/socket.h>
#include <unistd.h> // read(), write(), close()
#include <fcntl.h>
#include <QThread>
#include <QJsonDocument>
#include <QJsonObject>
#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 <QDateTime>
#include <QDebug>
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<QString>
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<QString> 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];
}

206
ismas/ismas_client.h Normal file
View File

@ -0,0 +1,206 @@
#ifndef ISMAS_CLIENT_H_INCLUDED
#define ISMAS_CLIENT_H_INCLUDED
#include <QObject>
#include <QString>
#include <optional>
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<QString>
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<QString> 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

140
main.cpp
View File

@ -12,7 +12,6 @@
#include "plugins/interfaces.h"
#include <unistd.h>
#include <thread>
#include <memory>
#include <QSharedMemory>
#include <QRunnable>
@ -21,12 +20,24 @@
#include <QProcess>
#include <QCommandLineParser>
#include <QStandardPaths>
#include <QMainWindow>
#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 <thread>
#include <QThread>
#include <QtWidgets>
#include <QScopedPointer>
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include <unistd.h>
#include <errno.h>
#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> 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();
}

445
mainwindow.cpp Normal file
View File

@ -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 <QDateTime>
#include <QMessageBox>
#include <QDebug>
#include <QScrollBar>
#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);
}

83
mainwindow.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QStatusBar>
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

75
mainwindow.ui Normal file
View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="font">
<font>
<family>Source Code Pro</family>
</font>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>781</width>
<height>441</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="2">
<widget class="QPushButton" name="exit">
<property name="text">
<string>Exit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QProgressBar" name="updateProgress">
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3" colspan="3">
<widget class="QTextEdit" name="updateStatus">
<property name="enabled">
<bool>true</bool>
</property>
<property name="font">
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<bold>true</bold>
</font>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -2,14 +2,17 @@
#include <QDateTime>
#include <cstring>
#include <QString>
#include <QFileInfo>
#include <QMessageLogContext>
#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());
}
}
}

View File

@ -2,9 +2,12 @@
#define MESSAGE_HANDLER_H_INCLUDED
#include <QtGlobal>
#ifdef __linux__
#include <syslog.h>
#endif
QtMsgType getDebugLevel();
void setDebugLevel(QtMsgType newDebugLevel);
int getDebugLevel();
void setDebugLevel(int newDebugLevel);
bool messageHandlerInstalled();
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler);

View File

@ -3,7 +3,7 @@
#include <QtPlugin>
#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 (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"

BIN
plugins/libCAmaster.so Normal file → Executable file

Binary file not shown.

104
process/command.cpp Normal file
View File

@ -0,0 +1,104 @@
#include "command.h"
#include <QProcess>
#include <QDebug>
#include <QDir>
#include <QRegularExpression>
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<QProcess> 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;
}

35
process/command.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef COMMAND_H_INCLUDED
#define COMMAND_H_INCLUDED
#endif // COMMAND_H_INCLUDED
#include <QObject>
#include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QProcess>
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);
};

20
progress_event.cpp Normal file
View File

@ -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<QEvent::Type>(generatedType);
}
return customEventType;
}

26
progress_event.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef PROGRESS_EVENT_H_INCLUDED
#define PROGRESS_EVENT_H_INCLUDED
#include <QEvent>
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

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@
#include <QFile>
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#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

25
update_dc_event.cpp Normal file
View File

@ -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<QEvent::Type>(generatedType);
}
return customEventType;
}

40
update_dc_event.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef UPDATE_DC_EVENT_H_INCLUDED
#define UPDATE_DC_EVENT_H_INCLUDED
#include <QEvent>
#include <QDateTime>
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

384
utils.cpp
View File

@ -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 <QCoreApplication>
#include <QApplication>
#include <QFile>
#include <QTemporaryFile>
#include <QDebug>
#include <QTextStream>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QRegularExpression>
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include <fstream>
//#include <unistd.h>
#include <thread>
#include <memory>
#include <QSharedMemory>
#include <QScopedPointer>
#include <QProcess>
#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<int> printTemplates{ nrOfTemplate };
QVector<QString> 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<lines.size(); ++i) {
qInfo() << lines.at(i);
} qInfo() << ""; qInfo() << "";
}
}
QString Utils::getTariffLoadTime(QString fileName) {
QFileInfo fInfo(fileName);
if (fInfo.exists()) {
QDateTime lastModifiedTime = fInfo.lastModified();
if (lastModifiedTime.isValid()) {
return lastModifiedTime.toString(Qt::ISODateWithMs);
} else {
printCriticalErrorMsg(fileName + " HAS INVALID MODIFIED-TIME");
QDateTime birthTime = fInfo.birthTime();
if (birthTime.isValid()) {
return birthTime.toString(Qt::ISODateWithMs);
} else {
printCriticalErrorMsg(fileName + " HAS INVALID BIRTH-TIME");
}
}
} else {
printCriticalErrorMsg(fileName + " DOES NOT EXIST");
}
return "N/A";
}
QString Utils::rstrip(QString const &str) {
int n = str.size() - 1;
for (; n >= 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<QString>::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<QFileInfo> i1(lst1);
while (i1.hasNext()) {
fileNameLst1 << i1.next().fileName();
}
QListIterator<QFileInfo> 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<QFileInfo> i3(lst1);
while (i3.hasNext()) {
gitBlobLst1 << GitClient::gitBlob(i3.next().fileName());
}
QListIterator<QFileInfo> 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<QProcess> 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;
}

52
utils.h
View File

@ -3,44 +3,26 @@
#include <QObject>
#include <QString>
#include <QStringList>
#include <QFile>
#include <QFileInfo>
#include <QDateTime>
#include <QDir>
#include <memory>
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<hwinf> 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

1404
worker.cpp

File diff suppressed because it is too large Load Diff

191
worker.h
View File

@ -3,28 +3,205 @@
#include <QObject>
#include <QString>
#include <QStringList>
#include <QTimer>
#include <QFile>
#include <QJsonObject>
#include <QHash>
#include <optional>
#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<QString> getApismVersion();
};
#endif // WORKER_H_INCLUDED