85 Commits

Author SHA1 Message Date
effe41bac9 Use finalResult() to sent a final message to ISMAS about the result of the update process. 2023-08-11 12:20:09 +02:00
4968942cc2 Added finalResult() -> send final message to ISMAS in any case. 2023-08-11 12:19:10 +02:00
769626581f Use head -n 1 in gitLastCommit(). 2023-08-11 11:12:59 +02:00
0e97ce7dc2 Take the pipe symbol into account in output of git getch command. 2023-08-11 11:12:09 +02:00
34334676fc Use print-utis from utils.h. 2023-08-11 11:11:14 +02:00
853787cd4b Added lastCommit to tariff-struct. Added jsonParseFailed(). 2023-08-11 11:09:51 +02:00
a932ed5471 Hide button on error message box. 2023-08-11 11:07:16 +02:00
c338884fc7 Use print-util in utils.h. 2023-08-11 11:06:38 +02:00
ed6df98653 Set progress bar timer to 500ms.
Yield thread to give worker thread its share of CPU time.
2023-08-11 11:05:33 +02:00
9eb458c4c5 Start Application after 1 second delay. 2023-08-11 11:05:02 +02:00
202e83268b Added utilities
void printCriticalErrorMsg(QString const &errorMsg);
    void printInfoMsg(QString const &infoMsg);
    void printLineEditInfo(QStringList const &lines);
    QString getTariffLoadTime(QString fileName);
2023-08-11 11:03:21 +02:00
8f26bfee0f Get last commit date and loadtime of tariff-file. 2023-08-11 11:02:15 +02:00
1af136e39d Set delay when trying to fetch value of update-trigger. 2023-08-11 11:01:26 +02:00
a550d55004 Get triggerValue directly from JSON. 2023-08-11 11:00:52 +02:00
a37a22d3f9 Show message boxes when running UpdateTool manually. 2023-08-11 11:00:02 +02:00
a8941f4ef4 Set progress values. 2023-08-11 10:59:26 +02:00
746565dbe0 Add messages sent to ISMAS. 2023-08-11 10:58:29 +02:00
79906df12e Add some qt debug aoutput. 2023-08-11 10:56:36 +02:00
edf1d105e7 Deactivate backendConnected(). 2023-08-11 10:55:53 +02:00
6c4b02cb56 Use print-utils to print some debug messages. 2023-08-11 10:52:31 +02:00
04d5061d79 Added some constants. 2023-08-11 10:49:34 +02:00
042e6dfa38 Try to establish a connection to backend 50x. 2023-08-09 16:17:28 +02:00
e523d3cc2c Added/chenged some debug output. 2023-08-09 16:16:36 +02:00
927197d0d1 Removed restart of APISM. 2023-08-09 16:14:59 +02:00
72cb738af5 Removed handling of pipe symbol (if available in git output). 2023-08-09 16:12:56 +02:00
0fb38013f7 Work with device controller file directly, not via link.
Add startProgress/stopProgress().
2023-08-09 15:09:44 +02:00
5f1376cf1e Removed buttons reserved for future use.
Start application automatically, not via Start-Button.
2023-08-09 15:08:22 +02:00
5db7b4224e Made start/stopProgress() public. 2023-08-09 15:06:20 +02:00
7c7adc94e6 Removed Start-button, because it is not needed.
Removed buttons reserved for future use.
2023-08-09 15:05:04 +02:00
89c2d3a8ae Added some debug output. 2023-08-09 15:03:14 +02:00
c2b52aa91d Only check for keyword RECORD to be more general. 2023-08-09 15:01:43 +02:00
329c770aa0 Use a message box with color red and a timer to click on ok after 5 secs. 2023-08-07 14:01:51 +02:00
cdb045b72b Fixed onAppendText() and onReplaceLast():
Watch out for special suffixes.
2023-08-07 13:59:44 +02:00
6d43cf4c9f Send custom events to the progress bar according to the state of the update process.
Changed the handling of messages for the text edit.
2023-08-07 13:56:51 +02:00
4caa0c0d83 Removed obsolete out-commented lines. 2023-08-07 13:55:52 +02:00
4d2d38e45c Default parameter for onReplaceLast() 2023-08-07 13:53:48 +02:00
9a55ce18e4 Chawnged font to Misc Fixed 2023-08-07 13:53:04 +02:00
223c7a8f8d Added errorGitClone(). 2023-08-07 13:51:47 +02:00
ce72d3d14d For the error EWOULDBLOCK try again 10 times until quitting. 2023-08-07 13:50:59 +02:00
8b66c47e49 Added errorGitClone() 2023-08-07 13:50:14 +02:00
4ff3b0efdf Advance the progress bar in the foreground when a long running task
in the background (e.g. git clone).
2023-08-06 20:44:26 +02:00
1fd2269753 onAppendText() only appends text.
onReplaceLast() replaces the last line in the text edit window.
2023-08-06 14:14:47 +02:00
a8994423f4 Change font size for full screen window. 2023-08-06 14:11:41 +02:00
4594c913e0 Using syslog for debugging. 2023-08-06 07:34:17 +02:00
cf9033e898 Send custom event from worker(-thread) to MainWindow in order to update
progress bar.
2023-08-05 18:50:50 +02:00
b09ccfd4f5 Add custom ProgressEvent class for future use. 2023-08-04 14:10:47 +02:00
6b4c486549 Add m_ismasClient.updateOfPSASucceeded() for a successful run. 2023-08-04 13:53:55 +02:00
9c44656104 Add parameter -vv fot the rsync command to see more debugoutput. 2023-08-04 13:52:57 +02:00
d57914957d Add text-edit entry if git pull is successful. 2023-08-04 13:50:19 +02:00
c4f12ce75a Added terminal-debug-output. 2023-08-04 13:49:32 +02:00
4ad370ea46 Extended displayed messages in text-edit. 2023-08-04 13:48:40 +02:00
44ad3caf2b Stop exit timer in onQuit.
Activate error message boxes.
2023-08-04 13:45:56 +02:00
427a272f8f Connect exit-button with clicked(). 2023-08-04 13:45:19 +02:00
96fb50e68d Moved init. of text-edit upwards.
Hide reserved buttons.
2023-08-04 13:44:16 +02:00
19274546c9 set main window fullscreen 2023-08-04 13:39:17 +02:00
d2d730589b changed return type of returnCode() ti 'int' 2023-08-04 13:38:33 +02:00
f88b0edb2a Fix for the case when several branches are edited: 'git fetch' will
display several lines then, not only one.
2023-08-04 13:35:42 +02:00
c62299aa72 Read from /mnt/system_data/ instead of /etc. 2023-08-04 13:33:25 +02:00
c054668eac Ask for the reurn code of the process, not only for process exit status. 2023-08-04 13:31:12 +02:00
82352713f1 Added 6 more buttons for future use and for the fullscreen layout. 2023-08-04 13:30:18 +02:00
48073ab1f0 update to next version 2023-08-03 10:14:23 +02:00
c6e98f50c2 call onQuit() directly whem timer runs out 2023-08-03 10:14:09 +02:00
ef88fdc9a4 Fix: config for deployment 2023-08-03 09:50:26 +02:00
8889aaca2a Fix: path for deployment 2023-08-03 09:44:08 +02:00
9b08420ac1 Use exit() instead of quit() to be able to add a returnCode in case of failure. 2023-08-03 09:06:50 +02:00
0ee92f0181 disbale exit-button as long as update process is running 2023-08-02 17:51:35 +02:00
a995cae000 set exit timer to 10 secs 2023-08-02 16:54:47 +02:00
0b7d504a7a Fixed missing git pull command.
Fixed missing update for text-edit when only clone the customer repository.
2023-08-02 16:53:19 +02:00
6a4a852fd6 Updated for new device controller lib (CAmaster) 2023-08-02 15:52:01 +02:00
81a9304438 Worker is the work-horse of the update process.
Using ismas-client it sends requests to APISM and gets results in a synchronous fashion.
Add previous emits to git client and apism client have been removed.
2023-08-02 15:50:04 +02:00
332d689b8c Add worker-object to be able to update for the worker. 2023-08-02 15:47:19 +02:00
b508c0517d Reset.
Add helper functions read1stLineOfFile() and zoneNr().
2023-08-02 15:45:20 +02:00
d2c0fdf820 Minor change: remove header 2023-08-02 15:44:45 +02:00
7293afd203 Minor change: include header 2023-08-02 15:44:16 +02:00
2fd1053bf9 Removed maintenance mode 2023-08-02 15:40:44 +02:00
e0a68a35f4 raised output-buffer length to 1024*8 2023-08-02 15:39:52 +02:00
4e277b4ca6 Added text edit 2023-08-02 15:39:08 +02:00
b3f4a4086b Populate text-edit with run-time info from update-process. 2023-08-02 15:29:10 +02:00
eb7d77692b Removed obsolete apism/tcp-client files. 2023-08-02 15:20:07 +02:00
ce1b1859df Removed apism-client and tcp-client. All done inside of ismas client now. 2023-08-02 15:18:39 +02:00
371cc1a187 Removed communication with ISMAS from git client. 2023-08-02 15:17:10 +02:00
a94606ca89 Added sendRequestReceiveResponse(): static member for communication with
APISM, on ports 7777 and 7778.
Each request is immediately handled in a synchronous fashion.
Added several helper functions to format messages to be sent to APISM/ISMAS.
2023-08-02 15:13:05 +02:00
5c9c9dc917 add mainwindow and utils 2023-07-31 16:56:16 +02:00
ab8acfc7d1 use gui-interface for ATBUpdateTool 2023-07-31 16:55:36 +02:00
1ddd0074b3 Set c++17 for PTU5-YOCTO 2023-07-20 09:06:07 +02:00
25 changed files with 2370 additions and 2086 deletions

View File

@@ -1,10 +1,20 @@
# QT -= gui QT += core gui
QT += core
QT += widgets serialport network QT += widgets serialport network
# QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = ATBUpdateTool 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.0.0 VERSION=1.0.0
INCLUDEPATH += plugins INCLUDEPATH += plugins
@@ -44,6 +54,8 @@ contains( CONFIG, PTU5 ) {
} }
contains( CONFIG, PTU5_YOCTO ) { contains( CONFIG, PTU5_YOCTO ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport 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 PTU5BASEPATH = /opt/devel/ptu5
ARCH = PTU5 ARCH = PTU5
DEFINES+=PTU5 DEFINES+=PTU5
@@ -65,10 +77,11 @@ contains( CONFIG, DesktopLinux ) {
SOURCES += \ SOURCES += \
main.cpp \ main.cpp \
progress_event.cpp \
mainwindow.cpp \
utils.cpp \
update.cpp \ update.cpp \
git/git_client.cpp \ git/git_client.cpp \
apism/apism_client.cpp \
apism/apism_tcp_client.cpp \
ismas/ismas_client.cpp \ ismas/ismas_client.cpp \
process/command.cpp \ process/command.cpp \
message_handler.cpp \ message_handler.cpp \
@@ -77,9 +90,10 @@ SOURCES += \
HEADERS += \ HEADERS += \
update.h \ update.h \
progress_event.h \
utils.h \
mainwindow.h \
git/git_client.h \ git/git_client.h \
apism/apism_client.h \
apism/apism_tcp_client.h \
apism/ismas_data.h \ apism/ismas_data.h \
ismas/ismas_client.h \ ismas/ismas_client.h \
process/command.h \ process/command.h \
@@ -88,6 +102,9 @@ HEADERS += \
worker_thread.h \ worker_thread.h \
plugins/interfaces.h plugins/interfaces.h
FORMS += \
mainwindow.ui
OTHER_FILES += \ OTHER_FILES += \
/opt/app/tools/atbupdate/update_log.csv \ /opt/app/tools/atbupdate/update_log.csv \
main.cpp.bck \ main.cpp.bck \
@@ -98,3 +115,13 @@ OTHER_FILES += \
# git subtree add --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash # 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 # git subtree pull --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash
# include(./DCPlugin/DCPlugin.pri) # 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
}

View File

@@ -1,816 +0,0 @@
#include "apism_client.h"
//#include "support/VendingData.h"
//#include "support/PersistentData.h"
//#include "support/utils.h"
//#include "ATBHMIconfig.h"
#include <QDebug>
#include <QByteArray>
#include <QHostAddress>
#include <QString>
#include <QList>
#include <QListIterator>
#include <QScopedPointer>
#include <QUuid>
#include <QProcess>
#include <QRegularExpression>
// Q_DECLARE_METATYPE(QAbstractSocket::SocketError)
ApismClient::ApismClient(QObject *eventReceiver, ATBHMIconfig *config, PersistentData *persistentData, QObject *parent)
: QObject(parent)
, healthEventReceiver(eventReceiver)
, m_config(config)
, persistentData(persistentData)
, lastError(0)
, lastErrorDescription("")
, currentRequestUid("")
, currentRequest(ISMAS::REQUEST::NO_REQUEST)
{
this->apismTcpSendClient = new ApismTcpClient("127.0.0.1", "7777", this);
this->apismTcpRequestResponseClient = new ApismTcpClient("127.0.0.1", "7778", this);
connect(apismTcpRequestResponseClient, &ApismTcpClient::receivedData,
this, &ApismClient::onReceivedResponse);
connect(apismTcpRequestResponseClient, &ApismTcpClient::responseTimeout,
this, &ApismClient::onRequestResponseClientResponseTimeout);
connect(apismTcpSendClient, &ApismTcpClient::responseTimeout,
this, &ApismClient::onSendClientResponseTimeout);
connect(this, SIGNAL(sendCmdSendVersionToIsmas(QString)),
this, SLOT(onSendCmdSendVersionToIsmas(QString)));
// not needed as APISM closes the socket after we send data, so readyRead()
// might not even fire
// connect(&m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
// defined for Qt >= 5.15, we have 5.12
// qRegisterMetaType<QAbstractSocket::SocketError>();
// connect(&m_socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
// this, SLOT(onSocketError(&QAbstractSocket::SocketError)));
}
ApismClient::~ApismClient() {
}
void ApismClient::restartApism() {
QProcess::startDetached("/bin/systemctl", {"restart", "apism"});
}
//void ApismClient::onReadyRead() { // parse APISM response
// QByteArray data = m_socket.readAll();
// qCritical() << "APISM-RESPONSE = (" << endl << data << endl << ")";
//}
void ApismClient::sendTransaction(const VendingData *vendingData) {
Q_UNUSED(vendingData);
#if 0
QScopedPointer<ISMAS::TransferData> transferData(new ISMAS::TransferData());
PAYMENT_VARIANTS::TYPE paymentType = vendingData->getParameter("PaymentType").value<PAYMENT_VARIANTS::TYPE>();
////////////////////// DEVICE //////////////////////////////////
bool deviceSet = false;
//QJsonValue tariffId("TariffInfo");
//transferData->device.insert("TARIFID", tariffId);
//QJsonValue group("group");
//transferData->device.insert("GROUP", group);
//QJsonValue zone("zone");
//transferData->device.insert("ZONE", zone);
if (deviceSet) {
transferData->insert("DEVICE", transferData->device);
}
////////////////////// TRANSACTION /////////////////////////////
bool transactionSet = false;
if (vendingData->hasParameter("TRANSACTION_STATE")) {
QVariant tstate = vendingData->getParameter("TRANSACTION_STATE");
if (tstate.isValid() && tstate.type() == QVariant::Int) {
transferData->transaction.state = tstate.toInt();
transferData->transaction.insert("STATE", transferData->transaction.state);
transactionSet = true;
}
}
if (vendingData->hasParameter("TRANSACTION_UID")) {
QVariant tuid = vendingData->getParameter("TRANSACTION_UID");
if (tuid.isValid() && tuid.type() == QVariant::String) {
transferData->transaction.uid = tuid.toString();
transferData->transaction.insert("UID", transferData->transaction.uid);
this->persistentData->setLastTransactionUID(tuid.toString());
transactionSet = true;
}
}
if (vendingData->hasParameter("TRANSACTION_TIMESTAMP")) {
QVariant tstamp = vendingData->getParameter("TRANSACTION_TIMESTAMP");
if (tstamp.isValid() && tstamp.type() == QVariant::String) {
transferData->transaction.timestamp = tstamp.toString();
transferData->transaction.insert("TIMESTAMP", transferData->transaction.timestamp);
transactionSet = true;
}
}
if (vendingData->hasParameter("TRANSACTION_TICKET_SEQUENCE_NUMBER")) {
QVariant tsn = vendingData->getParameter("TRANSACTION_TICKET_SEQUENCE_NUMBER");
if (tsn.isValid() && tsn.type() == QVariant::Int) {
transferData->transaction.seq_tick_number = tsn.toInt();
transferData->transaction.insert("TICKETNU", transferData->transaction.seq_tick_number);
transactionSet = true;
}
}
if (vendingData->hasParameter("LICENSEPLATE")) {
QVariant lp = vendingData->getParameter("LICENSEPLATE");
transferData->transaction.userText = QJsonValue::fromVariant(lp);
transferData->transaction.insert("USERTEXT", transferData->transaction.userText);
transferData->transaction.userTextType = "license plate";
transferData->transaction.insert("USERTEXTTYPE", transferData->transaction.userTextType);
transactionSet = true;
}
if (transactionSet) {
transferData->insert("TRANSACTION", transferData->transaction);
}
////////////////////// ITEM //////////////////////////////////
bool itemSet = false;
if (vendingData->hasParameter("PermitType")) {
QVariant idVariant = vendingData->getParameter("PermitType");
transferData->item.id = idVariant.toString();
transferData->item.insert("ID", transferData->item.id);
itemSet = true;
}
if (vendingData->hasParameter("Product")) {
QVariant nameVariant = vendingData->getParameter("Product");
transferData->item.name = nameVariant.toString();
transferData->item.insert("NAME", transferData->item.name);
itemSet = true;
}
if (vendingData->hasParameter("PRICE_INFO_GROSS")) {
int priceUint = vendingData->getUintParameter("PRICE_INFO_GROSS");
transferData->item.price = priceUint;
transferData->item.insert("PRICE", transferData->item.price);
itemSet = true;
}
if (vendingData->hasParameter("PERIOD_START")) {
QVariant startTimeVariant = vendingData->getParameter("PERIOD_START");
transferData->item.startTime = utils::getISODateTimeWithMsAndOffset(startTimeVariant);
transferData->item.insert("STARTTIME", transferData->item.startTime);
itemSet = true;
}
if (vendingData->hasParameter("PERIOD_END")) {
QVariant endTimeVariant = vendingData->getParameter("PERIOD_END");
transferData->item.endTime = utils::getISODateTimeWithMsAndOffset(endTimeVariant);
transferData->item.insert("ENDTIME", transferData->item.endTime);
itemSet = true;
}
if (vendingData->hasParameter("ITEM_PRINT_TEXT")) {
QVariant textVariant = vendingData->getParameter("ITEM_PRINT_TEXT");
transferData->item.printText = textVariant.toString();
transferData->item.insert("PRINTTEXT", transferData->item.printText);
itemSet = true;
}
// set static data:
// currency
if (itemSet) {
transferData->item.currency = this->m_config->getPaymentCurrencyISOCode();
transferData->item.insert("CURRENCY", transferData->item.currency);
}
if (itemSet) {
transferData->insert("ITEM", transferData->item);
}
////////////////////////////////////////////////////////////////////////
////////////////////// PAYMENT //////////////////////////////
bool paymentSet = false;
/////////////////////////// PAYMENT.CASH /////////////////////////////
if (paymentType == PAYMENT_VARIANTS::TYPE::CASH) {
bool cashSet = false;
if (vendingData->hasParameter("PaymentCashCoins")) {
QVariant coins = vendingData->getParameter("PaymentCashCoins");
transferData->payment.cash.coins = coins.toInt();
transferData->payment.cash.insert("COINS", transferData->payment.cash.coins);
cashSet = true;
}
if (vendingData->hasParameter("PaymentCashChange")) {
QVariant change = vendingData->getParameter("PaymentCashChange");
transferData->payment.cash.change = change.toInt();
transferData->payment.cash.insert("CHANGE", transferData->payment.cash.change);
cashSet = true;
}
if (vendingData->hasParameter("PaymentCashOverpaid")) {
QVariant overpaid = vendingData->getParameter("PaymentCashOverpaid");
transferData->payment.cash.overpaid = overpaid.toInt();
transferData->payment.cash.insert("OVERPAID", transferData->payment.cash.overpaid);
cashSet = true;
}
// currency
if (cashSet) {
transferData->payment.cash.currency = this->m_config->getPaymentCurrencyISOCode();
transferData->payment.cash.insert("CURRENCY", transferData->payment.cash.currency);
}
if (cashSet) {
transferData->payment.insert("CASH", transferData->payment.cash);
paymentSet = true;
}
}
/////////////////////////// PAYMENT.CARD /////////////////////////////
if (paymentType == PAYMENT_VARIANTS::TYPE::CARD) {
paymentSet = true;
bool cardSet = true;
transferData->payment.card.insert("CARDNU", "unknown");
transferData->payment.card.insert("VALUE", transferData->item.price);
transferData->payment.card.insert("CARDTYPE", "unknown");
transferData->payment.card.currency = this->m_config->getPaymentCurrencyISOCode();
transferData->payment.card.insert("CURRENCY", transferData->payment.card.currency);
// transferData->payment.card.insert("TERMINALID", tid);
//transferData->payment.card.insert("TERMINALRESULT", tresult);
if (cardSet) {
transferData->payment.insert("CARD", transferData->payment.card);
paymentSet = true;
}
}
if (paymentSet) {
transferData->insert("PAYMENT", transferData->payment);
}
////////////////////////////////////////////////////////////////////////
///////////////////////////// RESULT /////////////////////////////////
bool resultSet = false;
if (vendingData->hasParameter("RESULT_DELIVERY")) {
QVariant delVariant = vendingData->getParameter("RESULT_DELIVERY");
transferData->result.delivery = delVariant.toJsonValue();
transferData->result.insert("DELIVERY", transferData->result.delivery);
resultSet = true;
}
if (vendingData->hasParameter("RESULT_RESULT")) {
QVariant resVariant = vendingData->getParameter("RESULT_RESULT");
transferData->result.result = resVariant.toJsonValue();
transferData->result.insert("RESULT", transferData->result.result);
resultSet = true;
}
if (vendingData->hasParameter("RESULT_ERROR_CODE")) {
QVariant ecVariant = vendingData->getParameter("RESULT_ERROR_CODE");
transferData->result.errorCode = ecVariant.toJsonValue();
transferData->result.insert("ERRORCODE", transferData->result.errorCode);
resultSet = true;
}
if (vendingData->hasParameter("RESULT_ERROR_MESSAGE")) {
QVariant emsgVariant = vendingData->getParameter("RESULT_ERROR_MESSAGE");
transferData->result.errorMsg = emsgVariant.toJsonValue();
transferData->result.insert("ERRORMSG", transferData->result.errorMsg);
resultSet = true;
}
if (resultSet) {
transferData->insert("RESULT", transferData->result);
}
////////////////////////////////////////////////////////////////////////
QJsonDocument jsonDoc(*transferData);
QByteArray data = "#M=APISM#C=CMD_TRANSACTION#J=";
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpSendClient->sendData(data);
}
void ApismClient::sendAccount(const QHash<QString, QVariant> & accountDataHash)
{
QScopedPointer<ISMAS::AccountData> accountData(new ISMAS::AccountData());
accountData->coinBox.UID = QUuid::createUuid().toString(QUuid::WithoutBraces); // .mid(0, 8)
accountData->insert("UID", accountData->coinBox.UID);
accountData->coinBox.ChangeNumber = QJsonValue(static_cast<qint32>(this->persistentData->getNewCoinboxChangeNumber()));
accountData->insert("COINBOX_CHANGE_NUMBER", accountData->coinBox.ChangeNumber);
accountData->coinBox.Process = "COINBOX_CHANGE";
accountData->insert("PROCESS", accountData->coinBox.Process);
accountData->insert("StartTime", utils::getISODateTimeWithMsAndOffset(persistentData->getAccountStartTime()));
accountData->insert("EndTime", utils::getCurrentISODateTimeWithMsAndOffset());
accountData->insert("StartHash", persistentData->getFirstTransactionUID());
accountData->insert("EndHash", persistentData->getLastTransactionUID());
// coins
int numberOfCoinVariants = accountDataHash["NumberOfCoinVariants"].toInt();
for (int i=0; i < numberOfCoinVariants;++i) {
accountData->coinBox.coin.value = accountDataHash["COIN_" + QString::number(i) + "_Value"].toInt();
accountData->coinBox.coin.numberOfCoins = accountDataHash["COIN_" + QString::number(i) + "_Quantity"].toInt();
accountData->coinBox.coin.insert("VALUE", accountData->coinBox.coin.value);
accountData->coinBox.coin.insert("QUANTITY", accountData->coinBox.coin.numberOfCoins);
if (accountDataHash.contains("COIN_" + QString::number(i) + "_Currency")) {
accountData->coinBox.coin.currency = accountDataHash["COIN_" + QString::number(i) + "_Currency"].toString();
accountData->coinBox.coin.insert("CURRENCY", accountData->coinBox.coin.numberOfCoins);
}
accountData->insert("COIN_" + QString::number(i), accountData->coinBox.coin);
}
QJsonDocument jsonDoc(*accountData);
QByteArray data = "#M=APISM#C=CMD_CashboxChange#J=";
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpSendClient->sendData(data);
this->persistentData->clearForNewAccount();
persistentData->serializeToFile();
}
void ApismClient::sendEvent(const ATBMachineEvent* machineEvent)
{
QScopedPointer<ISMAS::EventData> eventData(new ISMAS::EventData());
eventData->machineEvent.eventID = machineEvent->eventId;
eventData->insert("EVENT_ID", eventData->machineEvent.eventID);
eventData->machineEvent.deviceName = machineEvent->deviceName;
eventData->insert("DeviceName", eventData->machineEvent.deviceName);
eventData->machineEvent.reason = ATBMachineEvent::getEventClassString(machineEvent->machineEventClass);
eventData->insert("Reason", eventData->machineEvent.reason);
eventData->machineEvent.event = machineEvent->eventName;
eventData->insert("Event", eventData->machineEvent.event);
eventData->machineEvent.eventState = machineEvent->eventState;
eventData->insert("EventState", eventData->machineEvent.eventState);
eventData->machineEvent.timeStamp = machineEvent->timestamString;
eventData->insert("Timestamp", eventData->machineEvent.timeStamp);
eventData->machineEvent.parameter = machineEvent->parameterString;
eventData->insert("Parameter", eventData->machineEvent.parameter);
eventData->machineEvent.secondLevelInfo = machineEvent->secondLevelInfoString;
eventData->insert("SecondLevelInfo", eventData->machineEvent.secondLevelInfo);
QJsonDocument jsonDoc(*eventData);
QByteArray data = "#M=APISM#C=CMD_EVENT#J=";
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpSendClient->sendData(data);
#endif
}
void ApismClient::requestAvailableIsmasUpdates() {
QByteArray data = "#M=APISM #C=REQ_ISMASParameter #J={}";
this->currentRequest = ISMAS::REQUEST::ISMAS_PARAMETER;
this->apismTcpRequestResponseClient->sendData(data);
}
void ApismClient::onSendCmdEventToIsmas(QString msg) {
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(msg.toUtf8(), &parseError));
if (parseError.error != QJsonParseError::NoError) {
qCritical() << "INVALID JSON MSG: PARSING FAILED:"
<< parseError.error << parseError.errorString();
return;
}
if (!document.isObject()) {
qCritical() << "FILE IS NOT A JSON OBJECT!";
return;
}
QByteArray data = "#M=APISM#C=CMD_EVENT#J=";
data += document.toJson(QJsonDocument::Compact);
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
this->apismTcpSendClient->sendData(data);
}
void ApismClient::onSendCmdSendVersionToIsmas(QString msg) {
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(msg.toUtf8(), &parseError));
if (parseError.error != QJsonParseError::NoError) {
qCritical() << "INVALID JSON MSG: PARSING FAILED:"
<< parseError.error << parseError.errorString();
return;
}
if (!document.isObject()) {
qCritical() << "FILE IS NOT A JSON OBJECT!";
return;
}
QByteArray data = "#M=APISM#C=CMD_SENDVERSION#J=";
data += document.toJson(QJsonDocument::Compact);
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
this->apismTcpSendClient->sendData(data);
}
void ApismClient::emulateUpdatesAvailable(QString const &msg) {
qDebug() << "EMULATE UPDATES AVAILABLE. MSG=" << msg;
onSendCmdEventToIsmas(msg);
}
void ApismClient::sendState(const QString & state, const QString & msg)
{
qCritical() << "ApismClient::sendState(): ";
qCritical() << " state: " << state;
qCritical() << " msg: " << msg;
QJsonParseError parseError;
QJsonDocument document(QJsonDocument::fromJson(msg.toUtf8(), &parseError));
if (parseError.error != QJsonParseError::NoError) {
qCritical() << "ApismClient::sendState() invalid json msg: Parsing failed:" << parseError.error << parseError.errorString();
return;
}
if (!document.isObject()) {
qCritical() << "File is not JSON object!";
return;
}
QScopedPointer<ISMAS::StateData> stateData(new ISMAS::StateData());
QJsonObject stateObject = document.object();
QJsonArray statesArray;
statesArray.append(stateObject);
// stateData->insert("TIMESTAMP", utils::getCurrentISODateTimeWithMsAndOffset());
// stateData->insert("HW_States", statesArray);
QJsonDocument jsonDoc(*stateData);
QByteArray data = "#M=APISM#C=CMD_HW_STATUS#J=";
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpSendClient->sendData(data);
}
#if 0
void ApismClient::sendMininformStartRequest(const VendingData* vendingData)
{
this->currentRequest = ISMAS::REQUEST::START;
struct MininFormTransferData : public QJsonObject {
struct : public QJsonObject {
QJsonValue uid; // MUST: uuid -> vendorId
QJsonValue posid; // terminal-id
QJsonValue authCode; // approval-code
QJsonValue stan; // MUST
QJsonValue lpn;
QJsonValue transactionTime; // MUST: Zeitstempel Verkauf
QJsonValue parkingStartTime; // MUST: Startzeit
QJsonValue preAuthAmount;
QJsonValue hourlyRate;
QJsonValue vehicleCategory;
QJsonValue langCode;
QJsonValue zoneCode;
} startAction;
};
QScopedPointer<MininFormTransferData> transferData(new MininFormTransferData());
transferData->startAction.uid = vendingData->getParameter("START_UID").toJsonValue();
this->currentRequestUid = vendingData->getParameter("START_UID").toString();
transferData->startAction.insert("UID", transferData->startAction.uid);
transferData->startAction.insert("POSID", vendingData->getParameter("START_POSID").toString());
transferData->startAction.insert("AUTHCODE", vendingData->getParameter("START_AuthCode").toString());
transferData->startAction.insert("STAN", vendingData->getParameter("START_STAN").toString());
transferData->startAction.insert("LPN", vendingData->getParameter("LICENSEPLATE").toString());
transferData->startAction.insert("TRANSACTIONTIME", utils::getISODateTimeWithMsAndOffset(vendingData->getParameter("START_TRANSACTIONTIME").toString()));
transferData->startAction.insert("STARTTIME", utils::getISODateTimeWithMsAndOffset(vendingData->getParameter("START_STARTTIME").toString()));
transferData->startAction.insert("AMOUNT", static_cast<int>(vendingData->getUintParameter("PRICE_INFO_GROSS")));
transferData->startAction.insert("RATE_H", static_cast<int>(vendingData->getUintParameter("PRICE_INFO_RATE_H")));
transferData->startAction.insert("VEHICLETYPE", "01"); // Fixed value
transferData->startAction.insert("LANGUAGE", "HUN"); // Fixed value
transferData->startAction.insert("ZONE", vendingData->getParameter("MININFORM_ZONE").toString());
transferData->insert("STARTACTION", transferData->startAction);
QJsonDocument jsonDoc(*transferData);
QByteArray data = "#M=APISM#C=REQ_START#J="; // REQ_... -> use port 7778
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpRequestResponseClient->sendData(data);
}
void ApismClient::sendMininformStopRequest(const VendingData* vendingData)
{
this->currentRequest = ISMAS::REQUEST::STOP;
struct MininFormTransferData : public QJsonObject {
struct : public QJsonObject {
QJsonObject uid; // MUST: uuid
QJsonObject posid; // terminal-id
QJsonObject stan; // MUST
QJsonObject lpn; // MUST
QJsonObject stopTime; // MUST: Stop-Zeit
QJsonObject langCode;
QJsonObject deviceId;
} stopAction;
};
QScopedPointer<MininFormTransferData> transferData(new MininFormTransferData());
this->currentRequestUid = QUuid::createUuid().toString(QUuid::WithoutBraces).mid(0, 8);
transferData->stopAction.insert("UID", this->currentRequestUid);
transferData->stopAction.insert("POSID", vendingData->getParameter("STOP_POSID").toString());
transferData->stopAction.insert("STAN", vendingData->getParameter("STOP_STAN").toString());
transferData->stopAction.insert("LPN", vendingData->getParameter("LICENSEPLATE").toString());
transferData->stopAction.insert("STOPTIME", utils::getISODateTimeWithMsAndOffset(vendingData->getParameter("STOP_STOPTIME")));
transferData->stopAction.insert("LANGUAGE", "HUN"); // Fixed value
transferData->stopAction.insert("DEVICE_ID", this->m_config->getMachineNr());
transferData->insert("STOPACTION", transferData->stopAction);
QJsonDocument jsonDoc(*transferData);
QByteArray data = "#M=APISM#C=REQ_STOP#J="; // REQ_ -> use port 7778
data += jsonDoc.toJson(QJsonDocument::Compact);
this->apismTcpRequestResponseClient->sendData(data);
}
#endif
void ApismClient::sendSelfTest() {
qDebug() << "SENDING APISM-SELF-TEST";
this->currentRequest = ISMAS::REQUEST::SELF;
QByteArray data = "#M=APISM#C=REQ_SELF#J={}";
this->apismTcpRequestResponseClient->sendData(data);
}
#if 0
void ApismClient::sendMininformPingRequest()
{
this->currentRequest = ISMAS::REQUEST::PING;
QByteArray data = "#M=APISM#C=REQ_Ping#J={\"281\":\"PING\"}";
this->apismTcpRequestResponseClient->sendData(data);
}
#endif
void ApismClient::onReceivedResponse(QByteArray response) {
qCritical() << "RECEIVED ------> " << response;
if (this->currentRequest == ISMAS::REQUEST::NO_REQUEST &&
response == "RECORD SAVED") { // sent by APISM to indicate that record
return; // has been saved in DB
}
// get the root object
QJsonParseError jsonParseError;
QJsonDocument responseDoc = QJsonDocument::fromJson(response, &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError) {
qCritical() << "ApismClient::onReceivedResponse() response is no json data:";
qCritical() << " Error: " << jsonParseError.errorString();
this->handleISMASResponseError();
return;
}
QJsonObject rootObject = responseDoc.object();
QStringList rootObjectKeys = rootObject.keys();
// DEBUG
qDebug() << "ApismClient::onReceivedResponse(): objects: " << rootObjectKeys;
// results to:
// ApismClient::onReceivedResponse(): objects: ("REQ_START#60044_Response", "Response")
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_START.*")) >= 0) {
this->private_handleMininformStartResponse(rootObject["Response"].toObject());
}
else
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_STOP.*")) >= 0) {
this->private_handleMininformStopResponse(rootObject["Response"].toObject());
}
else
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_SELF.*")) >= 0) {
this->private_handleReqSelfResponse(rootObject["Response"].toObject());
}
else
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_PING.*")) >= 0) {
this->private_handleReqPingResponse(rootObject["PING"].toObject());
} else
if(rootObjectKeys.indexOf(QRegularExpression("^REQ_ISMASPARAMETER.*")) >= 0) {
this->private_handleIsmasParameterResponse(rootObject);
}
else {
qCritical() << "ApismClient::onReceivedResponse() for unknown Request: ";
qCritical() << " currentRequestName: " << currentRequest;
qCritical() << " rootObject.keys(): " << rootObjectKeys;
this->handleISMASResponseError();
return;
}
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
}
void ApismClient::handleISMASResponseError()
{
switch (this->currentRequest) {
case ISMAS::REQUEST::NO_REQUEST:
qCritical() << "ApismClient::onReceivedResponse() for unknown Request: " << currentRequest;
break;
case ISMAS::REQUEST::START:
emit this->sendMininformStartResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
break;
case ISMAS::REQUEST::STOP:
emit this->sendMininformStopResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
break;
case ISMAS::REQUEST::PING:
emit this->sendMininformPingResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
break;
case ISMAS::REQUEST::SELF:
emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
break;
case ISMAS::REQUEST::ISMAS_PARAMETER:
// TODO
// emit
break;
}
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
}
/*
{\r\n \"REQ_START#53365_Response\": {\r\n \"Aknoledge\": \"OK\"\r\n },
\r\n \"Response\": {\r\n \"Result\": \"ERR:Ung<6E>ltiger Datums-String: \"\r\n }\r\n}
*/
/*
: ISMAS received: "{\r\n \"REQ_PING#30844_Response\": {\r\n \"Aknoledge\": \"OK\"\r\n },
"PING": { "IsAviable": "TRUE" }
}"
*/
void ApismClient::onSendClientResponseTimeout()
{
}
void ApismClient::onRequestResponseClientResponseTimeout()
{
switch (this->currentRequest) {
case ISMAS::REQUEST::NO_REQUEST:
qCritical() << "ApismClient::onRequestResponseClientResponseTimeout() for unknown Request: " << currentRequest;
break;
case ISMAS::REQUEST::START:
emit this->sendMininformStartResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
break;
case ISMAS::REQUEST::STOP:
emit this->sendMininformStopResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
break;
case ISMAS::REQUEST::PING:
emit this->sendMininformPingResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
break;
case ISMAS::REQUEST::SELF:
emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
break;
case ISMAS::REQUEST::ISMAS_PARAMETER:
break;
}
this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
}
void ApismClient::private_handleMininformStartResponse(QJsonObject response)
{
emit this->sendMininformStartResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
}
void ApismClient::private_handleMininformStopResponse(QJsonObject response)
{
emit this->sendMininformStopResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
}
void ApismClient::private_handleReqSelfResponse(QJsonObject response)
{
emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
}
void ApismClient::private_handleReqPingResponse(QJsonObject response)
{
emit this->sendMininformPingResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
}
void ApismClient::private_handleIsmasParameterResponse(QJsonObject response) {
emit this->ismasResponseAvailable(response);
}
/************************************************************************************************
* operators
*/
QDebug operator<< (QDebug debug, ISMAS::REQUEST request)
{
switch(request) {
case ISMAS::REQUEST::NO_REQUEST:
debug << QString("ISMAS::REQUEST::NO_REQUEST");
break;
case ISMAS::REQUEST::START:
debug << QString("ISMAS::REQUEST::START");
break;
case ISMAS::REQUEST::STOP:
debug << QString("ISMAS::REQUEST::STOP");
break;
case ISMAS::REQUEST::PING:
debug << QString("ISMAS::REQUEST::PING");
break;
case ISMAS::REQUEST::SELF:
debug << QString("ISMAS::REQUEST::SELF");
break;
case ISMAS::REQUEST::ISMAS_PARAMETER:
debug << QString("ISMAS::REQUEST::ISMASPARAMETER");
break;
}
return debug;
}
QString& operator<< (QString& str, ISMAS::REQUEST request)
{
switch(request) {
case ISMAS::REQUEST::NO_REQUEST:
str = QString("ISMAS::REQUEST::NO_REQUEST");
break;
case ISMAS::REQUEST::START:
str = QString("ISMAS::REQUEST::START");
break;
case ISMAS::REQUEST::STOP:
str = QString("ISMAS::REQUEST::STOP");
break;
case ISMAS::REQUEST::PING:
str = QString("ISMAS::REQUEST::PING");
break;
case ISMAS::REQUEST::SELF:
str = QString("ISMAS::REQUEST::SELF");
break;
case ISMAS::REQUEST::ISMAS_PARAMETER:
str = QString("ISMAS::REQUEST::ISMASPARAMETER");
break;
}
return str;
}

View File

@@ -1,127 +0,0 @@
#ifndef APISM_CLIENT_H_INCLUDED
#define APISM_CLIENT_H_INCLUDED
#include "ismas_data.h"
#include "apism_tcp_client.h"
#include <QObject>
#include <QAbstractSocket>
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonParseError>
#include <QJsonValue>
QDebug operator<<(QDebug debug, ISMAS::REQUEST request);
QString& operator<<(QString& str, ISMAS::REQUEST request);
namespace nsApismInterface {
enum class RESULT_STATE : quint8 {
SUCCESS = 1,
ERROR_BACKEND = 2, // error from backand (e.g. backend replies with error)
ERROR_NETWORK = 3, // error from network (e.g. host not available)
ERROR_TIMEOUT = 4, // the operation timed out
ERROR_PROCESS = 5, // internal plugin error (e.g. bug in implementation)
ERROR_RETRY = 6, // retry operation
INFO = 7
};
}
class VendingData;
class ATBHMIconfig;
class PersistentData;
class ATBMachineEvent;
class ApismClient : public QObject {
Q_OBJECT
public:
explicit ApismClient(QObject *eventReceiver, ATBHMIconfig *config, PersistentData *persistentData, QObject *parent = 0);
~ApismClient();
quint32 getLastError();
const QString & getLastErrorDescription();
ApismTcpClient* getApismTcpSendClient() { return apismTcpSendClient; }
public slots:
void sendSelfTest();
void sendTransaction(const VendingData* vendingData);
// void sendAccount(const QHash<QString, QVariant> &accountDataHash);
//void sendEvent(const ATBMachineEvent* machineEvent);
void sendState(const QString & state, const QString & msg);
void emulateUpdatesAvailable(QString const &msg);
void onSendCmdSendVersionToIsmas(QString);
void onSendCmdEventToIsmas(QString);
void requestAvailableIsmasUpdates();
//void sendMininformStartRequest(const VendingData* vendingData);
//void sendMininformStopRequest(const VendingData* vendingData);
//void sendMininformPingRequest();
void restartApism();
signals:
// public signals:
void sendTransactionRespones(nsApismInterface::RESULT_STATE result);
void sendAccountResponse(nsApismInterface::RESULT_STATE result);
void sendMininformStartResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
void sendMininformStopResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
void sendMininformPingResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
void sendReqSelfResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
void ismasResponseAvailable(QJsonObject ismasResponse);
void sendCmdSendVersionToIsmas(QString);
void sendCmdEventToIsmas(QString);
private slots:
// void onSocketError(QAbstractSocket::SocketError socketError);
void onReceivedResponse(QByteArray response);
void onSendClientResponseTimeout();
void onRequestResponseClientResponseTimeout();
private:
QObject *healthEventReceiver;
ATBHMIconfig *m_config;
PersistentData *persistentData;
ApismTcpClient* apismTcpSendClient;
ApismTcpClient* apismTcpRequestResponseClient;
quint32 lastError;
QString lastErrorDescription;
QString currentRequestUid;
ISMAS::REQUEST currentRequest;
void private_handleMininformStartResponse(QJsonObject response);
void private_handleMininformStopResponse(QJsonObject response);
void private_handlePingResponse(QJsonObject response);
void private_handleReqSelfResponse(QJsonObject response);
void private_handleReqPingResponse(QJsonObject response);
void private_handleIsmasParameterResponse(QJsonObject response);
void handleISMASResponseError();
};
// Q_DECLARE_METATYPE(QAbstractSocket::SocketError)
#endif // APISM_CLIENT_H_INCLUDED

View File

@@ -1,172 +0,0 @@
#include "apism_tcp_client.h"
#include <QHostAddress>
#include <QTimer>
#include <QCoreApplication>
ApismTcpClient::ApismTcpClient(const QString & hostname,
const QString & port,
QObject *parent)
: QObject(parent)
, hostname(hostname)
, port(port)
, responseTimerTimeoutCounter(0)
{
this->responseTimeoutTimer = new QTimer(this);
this->responseTimeoutTimer->setInterval(10000);
this->responseTimeoutTimer->setSingleShot(true);
connect(this->responseTimeoutTimer, SIGNAL(timeout()), this, SLOT(onResponseTimeoutTimerTimeout()));
socket = new QTcpSocket(this);
connect(socket, SIGNAL(connected()), this, SLOT(onSocketConnected()));
connect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
connect(socket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(onSocketBytesWritten(qint64)));
}
void ApismTcpClient::connectToHost() {
qCritical() << "ApismTcpClient::connectToHost(this->" << hostname << ", " << port << ")";
int portNumber = this->port.toInt();
this->socket->connectToHost(QHostAddress(this->hostname), portNumber);
if (!socket->waitForConnected(10000)) {
qCritical() << "ERROR IN WAIT FOR CONNECTED" << socket->errorString();
} else {
qDebug() << "connected to" << hostname << ", " << port << ")";
}
}
void ApismTcpClient::connectToHost(const QString & hostname, const QString & port)
{
qCritical() << "ApismTcpClient::connectToHost(" << hostname << ", " << port << ")";
int portNumber = port.toInt();
socket->connectToHost(hostname, portNumber);
}
void ApismTcpClient::closeConnection()
{
socket->close();
}
bool ApismTcpClient::isConnected()
{
bool result = false;
QAbstractSocket::SocketState socketState = socket->state();
switch (socketState) {
case QAbstractSocket::UnconnectedState:
/* FALLTHRU */
case QAbstractSocket::HostLookupState:
/* FALLTHRU */
case QAbstractSocket::ConnectingState:
result = false;
break;
case QAbstractSocket::ConnectedState:
/* FALLTHRU */
case QAbstractSocket::BoundState:
result = true;
break;
case QAbstractSocket::ClosingState:
/* FALLTHRU */
case QAbstractSocket::ListeningState:
result = false;
break;
}
return result;
}
void ApismTcpClient::sendData(const QByteArray & message) {
qDebug() << "ApismTcpClient::send: " << message;
this->sendQueue.enqueue(message);
if (this->isConnected()) {
qCritical() << "ApismTcpClient::send: connected, send" << message;
this->private_sendData();
} else {
qCritical() << "ApismTcpClient::send: not connected, connect";
this->connectToHost();
}
}
/**
* @brief ApismTcpClient::private_sendData
*
* Precondition is that queue is not empty.
*/
void ApismTcpClient::private_sendData()
{
// take message from queue
QByteArray ba = this->sendQueue.dequeue();
qDebug() << "ApismTcpClient::send: " << QString(ba);
socket->write(ba);
socket->flush();
// start timeoutTimer
this->responseTimeoutTimer->start();
}
void ApismTcpClient::onSocketConnected()
{
qInfo() << "ApismTcpClient: Connected!";
if (this->sendQueue.size() > 0) {
this->private_sendData();
}
}
void ApismTcpClient::onSocketDisconnected()
{
qDebug() << "ApismTcpClient: Disconnected!";
qDebug() << " -> SocketErrorString: " << socket->errorString();
if (this->sendQueue.size() > 0) {
this->connectToHost();
}
}
void ApismTcpClient::onSocketBytesWritten(qint64 bytes)
{
Q_UNUSED(bytes)
}
void ApismTcpClient::onSocketReadyRead()
{
QByteArray readData;
// stop timeoutTimer
this->responseTimeoutTimer->stop();
readData = socket->readAll();
qDebug() << "ISMAS received: " << QString(readData);
emit this->receivedData(readData);
//QCoreApplication::processEvents();
this->socket->close();
}
void ApismTcpClient::onResponseTimeoutTimerTimeout()
{
if (this->sendQueue.size() == 0) {
return;
}
emit this->responseTimeout();
qCritical() << "ApismTcpClient::onResponseTimeoutTimerTimeout() --> skip this message, send next command, if available.";
// Try next command
this->sendQueue.removeFirst();
if (this->sendQueue.size() > 0) this->private_sendData();
}

View File

@@ -1,62 +0,0 @@
#ifndef APISMTCPCLIENT_H
#define APISMTCPCLIENT_H
#include <QObject>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QByteArray>
#include <QQueue>
class QTimer;
class ApismTcpClient : public QObject
{
Q_OBJECT
public:
explicit ApismTcpClient(const QString & hostname, const QString & port, QObject *parent = nullptr);
bool isConnected();
void connectToHost();
void connectToHost(const QString & hostname, const QString & port);
// socket is implicitely closed by APISM
void closeConnection();
void sendData(const QByteArray & message);
public slots:
// socket interface
void onSocketConnected();
void onSocketDisconnected();
void onSocketBytesWritten(qint64 bytes);
void onSocketReadyRead();
signals:
void receivedData(QByteArray response);
void responseTimeout();
private:
QTcpSocket *socket;
QQueue<QByteArray> sendQueue;
QString hostname;
QString port;
QTimer *responseTimeoutTimer;
quint8 responseTimerTimeoutCounter;
void private_sendData();
public slots:
void onResponseTimeoutTimerTimeout();
};
#endif // APISMTCPCLIENT_H

View File

@@ -1,6 +1,7 @@
#include "git_client.h" #include "git_client.h"
#include "update.h" #include "update.h"
#include "worker.h" #include "worker.h"
#include "utils.h"
#include <QRegularExpression> #include <QRegularExpression>
#include <QDebug> #include <QDebug>
@@ -22,67 +23,6 @@ GitClient::GitClient(QString const &customerNrStr,
if (!m_worker) { if (!m_worker) {
qCritical() << "ERROR CASTING PARENT TO WORKER FAILED"; qCritical() << "ERROR CASTING PARENT TO WORKER FAILED";
} }
connect(this, SIGNAL(ismasUpdatesAvailable()),
this, SLOT(onIsmasUpdatesAvailable()), Qt::QueuedConnection);
}
void GitClient::onIsmasUpdatesAvailable() {
if (QDir(m_customerRepository).exists()) {
qInfo() << UpdateStatus(UPDATE_STATUS::GIT_FETCH_UPDATES_REQUEST,
QString("FETCHING OF ") + m_repositoryPath +
QString(" INTO ") + m_customerRepository);
std::optional<QString> changes = gitFetch();
if (changes) {
std::optional<QStringList> changedFileNames = gitDiff(changes.value());
if (changedFileNames) {
qInfo() << UpdateStatus(UPDATE_STATUS::GIT_FETCH_UPDATES_REQUEST_SUCCESS,
QString("FETCHED NEW FILES ") +
changedFileNames.value().join(",") +
QString(" INTO ") + m_customerRepository);
if (gitPull()) {
qInfo() << UpdateStatus(UPDATE_STATUS::GIT_PULL_UPDATES_SUCCESS,
QString("PULL NEW FILES ") +
changedFileNames.value().join(",") +
QString(" INTO ") + m_customerRepository);
emit m_worker->handleChangedFiles(changedFileNames.value());
} else {
qCritical() << UpdateStatus(UPDATE_STATUS::GIT_PULL_UPDATES_FAILURE,
QString("PULLING NEW FILES ") +
changedFileNames.value().join(",") +
QString(" INTO ") + m_customerRepository + " FAILED");
emit m_worker->terminateUpdateProcess();
}
} else {
qCritical() << "NO CHANGES IN" << m_repositoryPath
<< "(" << m_customerRepository << ")";
emit m_worker->finishUpdateProcess(false);
}
} else {
qCritical() << UpdateStatus(UPDATE_STATUS::GIT_FETCH_UPDATES_REQUEST_FAILURE,
QString("NO CHANGES IN ") + m_repositoryPath +
QString(" (%1)").arg(m_customerRepository));
emit m_worker->finishUpdateProcess(false);
}
} else {
if (gitCloneAndCheckoutBranch()) {
qInfo() << "CLONED" << m_repositoryPath
<< "AND CHECKED OUT INTO" << m_customerRepository;
} else {
qCritical() << "ERROR CLONING " << m_repositoryPath
<< "AND/OR CHECKING OUT INTO" << m_customerRepository;
emit m_worker->terminateUpdateProcess();
}
}
} }
bool GitClient::gitCloneCustomerRepository() { bool GitClient::gitCloneCustomerRepository() {
@@ -108,8 +48,7 @@ bool GitClient::gitCloneCustomerRepository() {
} }
} }
} }
qCritical() << "ERROR CLONE RESULT HAS WRONG FORMAT. CLONE_RESULT=" Utils::printCriticalErrorMsg(QString("ERROR CLONE RESULT HAS WRONG FORMAT. CLONE_RESULT=") + result);
<< result;
} }
return false; return false;
} }
@@ -128,14 +67,35 @@ bool GitClient::copyGitConfigFromMaster() { // only allowed when called in
return false; return false;
} }
bool GitClient::gitCheckoutBranch() { QStringList GitClient::gitBranchNames() {
// git config --global pager.branch false
QStringList bNames;
if (QDir(m_customerRepository).exists()) { 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 "); QString gitCommand("git checkout ");
gitCommand += m_branchName; gitCommand += m_branchName;
Command c(gitCommand); Command c(gitCommand);
return c.execute(m_customerRepository); // execute command in customerRepo return c.execute(m_customerRepository); // execute command in customerRepo
} }
qCritical() << "ERROR" << m_customerRepository << "DOES NOT EXIST"; Utils::printCriticalErrorMsg(QString("ERROR ") + m_customerRepository + " DOES NOT EXIST");
return false; return false;
} }
@@ -146,12 +106,11 @@ bool GitClient::gitCloneAndCheckoutBranch() {
if (gitCheckoutBranch()) { if (gitCheckoutBranch()) {
return true; return true;
} else { } else {
m_worker->terminateUpdateProcess(); // TODO
} }
//} //}
} else {
m_worker->terminateUpdateProcess();
} }
Utils::printCriticalErrorMsg(QString("CLONE ") + m_repositoryPath + " AND CHECKOUT FAILED");
return false; return false;
} }
@@ -168,24 +127,37 @@ std::optional<QStringList> GitClient::gitDiff(QString const &commits) {
Command c(gitCommand); Command c(gitCommand);
if (c.execute(m_customerRepository)) { // execute command in local customerRepo if (c.execute(m_customerRepository)) { // execute command in local customerRepo
QString s = c.getCommandResult().trimmed(); QString s = c.getCommandResult().trimmed();
Utils::printInfoMsg("GIT DIFF RESULT " + s);
QStringList lines = Update::split(s, '\n'); QStringList lines = Update::split(s, '\n');
QStringList fileNames; QStringList fileNames;
// each line has the format "etc/psa_config/DC2C_print01.json | 1 + // each line has the format "etc/psa_config/DC2C_print01.json | 1 +
// or the format "etc/psa_config/DC2C_print01.json (new) | 1 + // or the format "etc/psa_config/DC2C_print01.json (new) | 1 +
// the filenames are relativ to the repository // the filenames are relativ to the repository
for (int i = 0; i < lines.size(); ++i) { for (int i = 0; i < lines.size(); ++i) {
// TODO: koennte auch (delete) kommen ? QString const &line = lines.at(i);
int newIndex = lines.at(i).indexOf("(new)"); // for new files int newIndex = line.indexOf("(new"); // for new files
// int goneIndex = lines.at(i).indexOf("(gone)"); // for removed files int goneIndex = line.indexOf("(gone"); // for removed files
int modeIndex = line.indexOf("(mode");
int pipeIndex = line.indexOf('|');
if (newIndex != -1) { if (newIndex != -1) {
QString fileName = lines.at(i).mid(0, newIndex).trimmed(); QString file = line.left(newIndex).trimmed();
fileNames << fileName; qInfo() << "FILE (NEW)" << file;
} else { fileNames << file;
int pipeIndex = lines.at(i).indexOf('|'); } 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) { if (pipeIndex != -1) {
QString fileName = lines.at(i).mid(0, pipeIndex).trimmed(); QString const file = line.left(pipeIndex).trimmed();
fileNames << fileName; qInfo() << "FILE (PIPE)" << file;
} fileNames << file;
} }
} }
if (!fileNames.isEmpty()) { if (!fileNames.isEmpty()) {
@@ -201,36 +173,62 @@ std::optional<QStringList> GitClient::gitDiff(QString const &commits) {
*/ */
std::optional<QString> GitClient::gitFetch() { std::optional<QString> GitClient::gitFetch() {
if (QDir(m_customerRepository).exists()) { if (QDir(m_customerRepository).exists()) {
qInfo() << "BRANCH NAME" << m_branchName;
Command c("git fetch"); Command c("git fetch");
if (c.execute(m_customerRepository)) { if (c.execute(m_customerRepository)) {
QString const s = c.getCommandResult().trimmed(); QString const s = c.getCommandResult().trimmed();
qCritical() << "GIT RESULT" << s;
if (!s.isEmpty()) { if (!s.isEmpty()) {
QStringList lines = Update::split(s, '\n'); QStringList lines = Update::split(s, '\n');
if (!lines.empty()) { 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 // 409f198..6c22726 zg1/zone1 -> origin/zg1/zone1
static QRegularExpression re("(^\\s*)([0-9A-Fa-f]+..[0-9A-Fa-f]+)(.*$)"); static QRegularExpression re("(^\\s*)([0-9A-Fa-f]+..[0-9A-Fa-f]+)(.*$)");
QRegularExpressionMatch match = re.match(lines.last()); QRegularExpressionMatch match = re.match(lines.at(i));
if (match.hasMatch()) { if (match.hasMatch()) {
if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches
return match.captured(2); return match.captured(2);
} else { } else {
qCritical() << "ERROR WRONG CAPTURE COUNT FOR 'GIT FETCH'" << re.captureCount(); emit m_worker->showErrorMessage("git fetch",
QString("(wrong cap-count (%1)").arg(re.captureCount()));
} }
} else { } else {
qCritical() << "ERROR NO MATCH OF COMMITS FOR 'GIT FETCH'"; 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 { } else {
qCritical() << "ERROR WRONG FORMAT FOR RESULT FOR 'GIT FETCH'" << s; 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 { } else {
qCritical() << "ERROR EMPTY RESULT FROM 'GIT FETCH'"; emit m_worker->showErrorMessage("git fetch", "empty result for 'git fetch'");
Utils::printCriticalErrorMsg("EMPTY RESULT FOR 'GIT FETCH'");
} }
} }
} else { } else {
qCritical() << "ERROR" << m_customerRepository << "DOES NOT EXIST"; 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; return std::nullopt;
} }
@@ -251,7 +249,7 @@ bool GitClient::gitPull() {
qInfo() << "PULLED INTO" << m_customerRepository; qInfo() << "PULLED INTO" << m_customerRepository;
return true; return true;
} }
qCritical() << "PULL INTO" << m_customerRepository << "FAILED"; Utils::printCriticalErrorMsg(QString("PULL INTO " + m_customerRepository + " FAILED"));
} }
return false; return false;
} }
@@ -270,6 +268,7 @@ QString GitClient::gitLastCommit(QString fileName) {
if (QDir(m_customerRepository).exists()) { if (QDir(m_customerRepository).exists()) {
QString const filePath QString const filePath
= QDir::cleanPath(m_customerRepository + QDir::separator() + fileName); = QDir::cleanPath(m_customerRepository + QDir::separator() + fileName);
if (QFile(filePath).exists()) {
QString const gitCommand = QString("git log %1 | head -n 1").arg(fileName); QString const gitCommand = QString("git log %1 | head -n 1").arg(fileName);
Command c("bash"); Command c("bash");
if (c.execute(m_customerRepository, QStringList() << "-c" << gitCommand)) { if (c.execute(m_customerRepository, QStringList() << "-c" << gitCommand)) {
@@ -280,6 +279,7 @@ QString GitClient::gitLastCommit(QString fileName) {
} }
} }
} }
}
return ""; return "";
} }

View File

@@ -2,9 +2,11 @@
#define GIT_CLIENT_H_INCLUDED #define GIT_CLIENT_H_INCLUDED
#include <QObject> #include <QObject>
#include <QStringList>
#include <optional> #include <optional>
#include "process/command.h" #include "process/command.h"
#include "ismas/ismas_client.h"
class Worker; class Worker;
class GitClient : public QObject { class GitClient : public QObject {
@@ -14,7 +16,7 @@ class GitClient : public QObject {
QString const m_repositoryPath; QString const m_repositoryPath;
QString const m_customerNr; QString const m_customerNr;
QString const m_workingDirectory; QString const m_workingDirectory;
QString const m_branchName; QString m_branchName;
QString const m_customerRepository; QString const m_customerRepository;
bool copyGitConfigFromMaster(); bool copyGitConfigFromMaster();
@@ -28,6 +30,7 @@ class GitClient : public QObject {
bool gitCloneCustomerRepository(); bool gitCloneCustomerRepository();
bool gitCheckoutBranch(); bool gitCheckoutBranch();
QStringList gitBranchNames();
QString const workingDirectory() const { return m_workingDirectory; } QString const workingDirectory() const { return m_workingDirectory; }
QString workingDirectory() { return m_workingDirectory; } QString workingDirectory() { return m_workingDirectory; }
@@ -50,13 +53,6 @@ class GitClient : public QObject {
QString gitBlob(QString fileName); QString gitBlob(QString fileName);
QString gitCommitForBlob(QString blob); QString gitCommitForBlob(QString blob);
bool gitIsFileTracked(QString file2name); bool gitIsFileTracked(QString file2name);
signals:
void ismasUpdatesAvailable();
public slots:
void onIsmasUpdatesAvailable();
}; };
#endif // GIT_CLIENT_H_INCLUDED #endif // GIT_CLIENT_H_INCLUDED

View File

@@ -3,6 +3,24 @@
#include <cstring> #include <cstring>
#include <cstdio> #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 #if 0
# $1: EVENT: U0001 update finished: 100% # $1: EVENT: U0001 update finished: 100%
# U0002 reset TRG # U0002 reset TRG
@@ -24,6 +42,218 @@
#include <QDateTime> #include <QDateTime>
#include <QDebug> #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;
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
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) {
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;
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) + ")");
::close(sockfd);
return std::nullopt;
} else
if (n < 0) {
if (errno == EWOULDBLOCK) {
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, QString IsmasClient::updateNewsToIsmas(char const *event,
int percent, int percent,
int resultCode, int resultCode,
@@ -53,6 +283,63 @@ QString IsmasClient::updateNewsToIsmas(char const *event,
return buf; 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::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) { QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) {
//static int const constexpr SIZE = 4096*8; //static int const constexpr SIZE = 4096*8;
static char buf[4096*2]; static char buf[4096*2];
@@ -376,39 +663,132 @@ QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) {
psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(), psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(),
psa.pluginVersion.tcpZVT.toStdString().c_str()); psa.pluginVersion.tcpZVT.toStdString().c_str());
printf("buf=%s\n", buf); qInfo() << buf;
return buf; return buf;
} }
QString IsmasClient::updateOfPSAContinues(QString currentStage,
QString IsmasClient::updateOfPSAActivated() { QString currentStageInfo,
QString const &version) {
return updateNewsToIsmas("U0010", return updateNewsToIsmas("U0010",
1, m_progressInPercent,
0, RESULT_CODE::SUCCESS,
"activated", currentStage.toStdString().c_str(),
"detected WAIT state", currentStageInfo.toStdString().c_str(),
"1.0.0"); version.toStdString().c_str());
} }
QString IsmasClient::updateOfPSASucceeded() { 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 = 0;
return updateNewsToIsmas("U0001", return updateNewsToIsmas("U0001",
100, m_progressInPercent,
0, RESULT_CODE::SUCCESS,
"update_succeeded", "UPDATE SUCCESS",
"", "update process succeeded",
"1.0.0"); version.toStdString().c_str());
} }
QString IsmasClient::setUpdatesAvailable() { QString IsmasClient::sanityCheckFailed(int resultCode, QString reason, QString const &version) {
return updateNewsToIsmas("U0099", return updateNewsToIsmas("U0003",
10, m_progressInPercent,
0, resultCode,
"set_updates_available", "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 = 0;
if (resultCode == RESULT_CODE::SUCCESS) {
return updateNewsToIsmas("U0002",
m_progressInPercent,
RESULT_CODE::SUCCESS,
"FINAL-UPDATE-RESULT",
"(re-)set WAIT state",
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;
}
bool checkForAvailableUpdates(); 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());
}

View File

@@ -4,6 +4,8 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <optional>
struct PSAInstalled { struct PSAInstalled {
struct Tariff { struct Tariff {
QString name; QString name;
@@ -12,6 +14,7 @@ struct PSAInstalled {
int zone; int zone;
int size; int size;
QString blob; QString blob;
QString lastCommit;
QString info; QString info;
QString loadTime; QString loadTime;
} tariff; } tariff;
@@ -111,7 +114,28 @@ struct PSAInstalled {
class IsmasClient : public QObject { class IsmasClient : public QObject {
Q_OBJECT Q_OBJECT
int m_progressInPercent;
public: 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};
static std::optional<QString>
sendRequestReceiveResponse(int port, QString const &request);
int getProgressInPercent() const {return m_progressInPercent; }
void setProgressInPercent(int procent) { m_progressInPercent = procent; }
QString updateNewsToIsmas(char const *event, QString updateNewsToIsmas(char const *event,
int percent, int percent,
int resultCode, int resultCode,
@@ -119,12 +143,35 @@ public:
char const *step_result, char const *step_result,
char const *version); char const *version);
QString updateOfPSAActivated(); QString updateOfPSAStarted(QString const &version = QString()); // start of update process
QString updateOfPSASucceeded(); 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 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); QString updateOfPSASendVersion(PSAInstalled const &psa);
QString setUpdatesAvailable(); private:
bool checkForAvailableUpdates(); 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 #endif // ISMAS_CLIENT_H_INCLUDED

View File

@@ -20,13 +20,15 @@
#include <QProcess> #include <QProcess>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QStandardPaths> #include <QStandardPaths>
#include <QMainWindow>
#include "update.h" #include "update.h"
#include "git/git_client.h" #include "git/git_client.h"
#include "ismas/ismas_client.h" #include "ismas/ismas_client.h"
#include "apism/apism_client.h"
#include "worker_thread.h" #include "worker_thread.h"
#include "worker.h" #include "worker.h"
#include "mainwindow.h"
#include "utils.h"
#include <QThread> #include <QThread>
@@ -44,14 +46,15 @@ int main(int argc, char *argv[]) {
} }
// qputenv("XDG_RUNTIME_DIR", "/run/user/0"); // qputenv("XDG_RUNTIME_DIR", "/run/user/0");
openlog("ATB-UPDATE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QApplication a(argc, argv); QApplication a(argc, argv);
QApplication::setApplicationName("ATBUpdateTool"); QApplication::setApplicationName("ATBUpdateTool");
QApplication::setApplicationVersion(APP_VERSION); QApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput); atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(QtMsgType::QtInfoMsg); setDebugLevel(LOG_NOTICE);
//setDebugLevel(QtMsgType::QtDebugMsg);
} }
QCommandLineParser parser; QCommandLineParser parser;
@@ -80,10 +83,6 @@ int main(int argc, char *argv[]) {
workingDirectoryOption.setDefaultValue(workingDirectoryDefault); workingDirectoryOption.setDefaultValue(workingDirectoryDefault);
parser.addOption(workingDirectoryOption); parser.addOption(workingDirectoryOption);
QCommandLineOption maintenanceOption(QStringList() << "m" << "maintenance",
QCoreApplication::translate("main", "Maintenance mode for underlying script"));
parser.addOption(maintenanceOption);
QCommandLineOption dryRunOption(QStringList() << "d" << "dry-run", QCommandLineOption dryRunOption(QStringList() << "d" << "dry-run",
QCoreApplication::translate("main", "Start ATBUpdateTool in dry-run-mode. No actual actions.")); QCoreApplication::translate("main", "Start ATBUpdateTool in dry-run-mode. No actual actions."));
parser.addOption(dryRunOption); parser.addOption(dryRunOption);
@@ -101,7 +100,6 @@ int main(int argc, char *argv[]) {
QString plugInDir = parser.value(pluginDirectoryOption); QString plugInDir = parser.value(pluginDirectoryOption);
QString plugInName = parser.value(pluginNameOption); QString plugInName = parser.value(pluginNameOption);
QString workingDir = parser.value(workingDirectoryOption); QString workingDir = parser.value(workingDirectoryOption);
bool maintenanceMode = parser.isSet(maintenanceOption);
bool dryRun = parser.isSet(dryRunOption); bool dryRun = parser.isSet(dryRunOption);
QString const rtPath = QCoreApplication::applicationDirPath(); QString const rtPath = QCoreApplication::applicationDirPath();
@@ -118,7 +116,6 @@ int main(int argc, char *argv[]) {
qInfo() << "plugInDir ........." << plugInDir; qInfo() << "plugInDir ........." << plugInDir;
qInfo() << "plugInName ........" << plugInName; qInfo() << "plugInName ........" << plugInName;
qInfo() << "workingDir ........" << workingDir; qInfo() << "workingDir ........" << workingDir;
qInfo() << "maintenanceMode ..." << maintenanceMode;
qInfo() << "dryRun ............" << dryRun; qInfo() << "dryRun ............" << dryRun;
// before loading the library, delete all possible shared memory segments // before loading the library, delete all possible shared memory segments
@@ -131,11 +128,9 @@ int main(int argc, char *argv[]) {
hwinf *hw = Update::loadDCPlugin(QDir(plugInDir), plugInName); hwinf *hw = Update::loadDCPlugin(QDir(plugInDir), plugInName);
// hw->dc_autoRequest(false); // hw->dc_autoRequest(false);
QString const update_ctrl_file = "/opt/app/tools/atbupdate/update_log.csv"; int machineNr = Utils::read1stLineOfFile("/etc/machine_nr");
int customerNr = Utils::read1stLineOfFile("/etc/cust_nr");
int machineNr = Worker::read1stLineOfFile("/etc/machine_nr"); int zoneNr = Utils::read1stLineOfFile("/etc/zone_nr");
int customerNr = Worker::read1stLineOfFile("/etc/cust_nr");
int zoneNr = Worker::read1stLineOfFile("/etc/zone_nr");
QString const branchName = (zoneNr != 0) QString const branchName = (zoneNr != 0)
? QString("zg1/zone%1").arg(zoneNr) : "master"; ? QString("zg1/zone%1").arg(zoneNr) : "master";
@@ -148,8 +143,14 @@ int main(int argc, char *argv[]) {
zoneNr, zoneNr,
branchName, branchName,
workingDir, workingDir,
maintenanceMode,
dryRun); dryRun);
MainWindow mw(&worker);
worker.setMainWindow(&mw);
mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
mw.setWindowState(Qt::WindowFullScreen);
mw.show();
return a.exec(); return a.exec();
} }

197
mainwindow.cpp Normal file
View File

@@ -0,0 +1,197 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "worker.h"
#include "utils.h"
#include "progress_event.h"
#include <QDateTime>
#include <QMessageBox>
#include <QDebug>
MainWindow::MainWindow(Worker *worker, QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_worker(worker)
, m_width(70)
, m_progressRunning(false)
, m_progressValue(0) {
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("").leftJustified(m_width-3, '=');
ui->updateStatus->setText(lst.join('\n'));
ui->updateStatus->setEnabled(true);
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(replaceLast(QString, QString)), this, SLOT(onReplaceLast(QString,QString)));
ui->updateStatus->setText(lst.join('\n'));
ui->updateStatus->setEnabled(true);
}
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 !!!";
}
}
QThread::yieldCurrentThread();
}
void MainWindow::onStopStartTimer() {
m_startTimer->stop();
}
void MainWindow::onDisableExit() {
ui->exit->setEnabled(false);
}
void MainWindow::onEnableExit() {
ui->exit->setEnabled(true);
}
void MainWindow::onRestartExitTimer() {
m_exitTimer->stop();
m_exitTimer->start(60 * 1000);
}
void MainWindow::onQuit() {
m_exitTimer->stop();
qCritical() << QString("ON QUIT: EXIT CODE %1").arg(m_worker->returnCode());
qApp->exit(m_worker->returnCode());
}
void MainWindow::onAppendText(QString text, QString suffix) {
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) {
editText += QString("\n").leftJustified(m_width-3, '=');
editText += " ";
}
editText += (QString("\n") + text).leftJustified(m_width - (2 + suffix.size()) ) + suffix;
} else {
editText += text.leftJustified(m_width-9);
}
Utils::printLineEditInfo(editText.split('\n'));
ui->updateStatus->setPlainText(editText.trimmed());
ui->updateStatus->setEnabled(true);
}
void MainWindow::onReplaceLast(QString text, QString suffix) {
qInfo() << "REPL TEXT" << text << "SUFFIX" << suffix;
QString editText = ui->updateStatus->toPlainText();
QStringList lines = editText.split('\n');
if (lines.size() > 0) {
lines.removeLast();
if (!suffix.isNull() && suffix.size() > 0 && suffix != "\n") {
lines += text.leftJustified(m_width-10) + suffix;
} else {
lines += text.leftJustified(m_width-10);
}
}
Utils::printLineEditInfo(lines);
ui->updateStatus->setText(lines.join('\n').trimmed());
ui->updateStatus->setEnabled(true);
}
void MainWindow::onShowErrorMessage(QString title, QString text) {
text = text.leftJustified(50, ' ');
QMessageBox msgBox(QMessageBox::NoIcon, title,
text, QMessageBox::Ok,
nullptr, Qt::FramelessWindowHint);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.defaultButton()->setVisible(false);
QTimer *t = new QTimer(this);
connect(t, SIGNAL(timeout()), msgBox.defaultButton(), SLOT(click()));
t->setSingleShot(true);
t->start(5 * 1000);
if(msgBox.exec() == QMessageBox::Ok) {
// do something
} else {
// do something else
}
}

49
mainwindow.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
#include "worker.h"
class MainWindow : public QMainWindow {
Q_OBJECT
protected:
void customEvent(QEvent *event) override;
public:
MainWindow(Worker *worker, QWidget *parent = nullptr);
~MainWindow();
static const int START_PROGRESS_LOOP = -1;
static const int STOP_PROGRESS_LOOP = -2;
int progressValue() const { return m_progressValue; }
public slots:
void onAppendText(QString, QString suffix = "");
void onReplaceLast(QString, QString suffix = "");
void onShowErrorMessage(QString, QString);
void onStopStartTimer();
void onRestartExitTimer();
void onEnableExit();
void onDisableExit();
private slots:
void onQuit();
private:
Ui::MainWindow *ui;
Worker *m_worker;
int m_width;
QTimer *m_startTimer;
QTimer *m_exitTimer;
bool m_progressRunning;
int m_progressValue;
};
#endif // MAINWINDOW_H

66
mainwindow.ui Normal file
View File

@@ -0,0 +1,66 @@
<?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>461</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>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -6,13 +6,13 @@
#include <QFileInfo> #include <QFileInfo>
#include <QMessageLogContext> #include <QMessageLogContext>
#define OUTPUT_LEN (1024)
static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " };
static bool installedMsgHandler = false; static bool installedMsgHandler = false;
static QtMsgType debugLevel = QtInfoMsg; static int debugLevel = LOG_NOTICE;
QtMsgType getDebugLevel() { return debugLevel; } int getDebugLevel() { return debugLevel; }
void setDebugLevel(QtMsgType newDebugLevel) { void setDebugLevel(int newDebugLevel) {
debugLevel = newDebugLevel; debugLevel = newDebugLevel;
} }
@@ -45,75 +45,51 @@ QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
/// ///
#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #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) { void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
// static constexpr const char *format = "dd.MM.yyyy hh:mm:ss"; Q_UNUSED(context);
QByteArray localMsg = msg.toLocal8Bit(); QString const localMsg = QString(DBG_NAME[type]) + msg.toLocal8Bit();
QString fileName(context.file ? context.file : "N/A");
QString function(context.function ? context.function : "N/A"); switch (debugLevel) {
char buf[OUTPUT_LEN]{}; case LOG_DEBUG: { // debug-level message
memset(buf, 0x00, sizeof(buf)); syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
QString const datetime = QDateTime::currentDateTime().toString(Qt::ISODateWithMs); } break;
switch (type) { case LOG_INFO: { // informational message
case QtDebugMsg: { if (type != QtDebugMsg) {
if (debugLevel == QtDebugMsg) { syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
} }
} break; } break;
case QtInfoMsg: { case LOG_NOTICE: { // normal, but significant, condition
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg) { if (type != QtDebugMsg) {
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s", syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
} }
} break; } break;
case QtWarningMsg: { case LOG_WARNING: { // warning conditions
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg || debugLevel == QtWarningMsg) { if (type != QtInfoMsg && type != QtDebugMsg) {
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s", syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
} }
} break; } break;
case QtCriticalMsg: { case LOG_ERR: { // error conditions
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
|| debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg) { syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s",
datetime.toStdString().c_str(),
function.toStdString().c_str(),
fileName.toStdString().c_str(),
context.line,
localMsg.constData());
fprintf(stderr, "%s\n", buf);
} }
} break; } break;
case QtFatalMsg: { case LOG_CRIT: { // critical conditions
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
|| debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
|| debugLevel == QtFatalMsg) { }
snprintf(buf, sizeof(buf)-1, "%s DEBG [%s:%s:%04u] %s", } break;
datetime.toStdString().c_str(), case LOG_ALERT: { // action must be taken immediately
function.toStdString().c_str(), if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
fileName.toStdString().c_str(), syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
context.line, }
localMsg.constData()); } break;
fprintf(stderr, "%s\n", buf); case LOG_EMERG: { // system is unusable
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
} }
} break; } break;
default: { default: {
fprintf(stderr, "%s No ErrorLevel defined! %s\n", //fprintf(stderr, "%s No ErrorLevel defined! %s\n",
datetime.toStdString().c_str(), msg.toStdString().c_str()); // datetime.toStdString().c_str(), msg.toStdString().c_str());
} }
} }
} }

View File

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

View File

@@ -3,7 +3,7 @@
#include <QtPlugin> #include <QtPlugin>
#define THIS_IS_CA_MASTER
struct T_emp struct T_emp
{ {
@@ -128,28 +128,24 @@ struct T_vaultRecord
uint32_t AbsReserve; uint32_t AbsReserve;
uint32_t AbsNrOfCuts; uint32_t AbsNrOfCuts;
//16
char label3buffer[4]; // mw > char label3buffer[4]; // mw >
// Verkauf, T<EFBFBD>r zu: // Verkauf, Tuer zu:
uint32_t VKcoinsInserted[16]; // nur f<EFBFBD>r Wechsler, soviel wurde eingeworfen uint32_t VKcoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen
uint32_t VKcoinsReturned[6]; // nur f<EFBFBD>r Wechsler, Anzahl M<EFBFBD>nzen pro Typ, soviel wurde zur<EFBFBD>ckgegeben uint32_t VKcoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben
//88
// Service, T<EFBFBD>r offen: // Service, Tuer offen:
uint16_t ServCoinsInserted[16]; // nur f<EFBFBD>r Wechsler, soviel wurde eingeworfen uint16_t ServCoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen
uint16_t ServCoinsReturned[6]; // nur f<EFBFBD>r Wechsler, Anzahl M<EFBFBD>nzen pro Typ, soviel wurde zur<EFBFBD>ckgegeben uint16_t ServCoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben
uint16_t resint3; uint16_t resint3;
uint16_t resint4; uint16_t resint4;
uint16_t currentTubeContent[6]; // nur f<EFBFBD>r Wechsler, aktueller F<EFBFBD>llstand uint16_t currentTubeContent[6]; // nur fuer Wechsler, aktueller Fuellstand
uint16_t resint5; uint16_t resint5;
uint16_t resint6; uint16_t resint6;
// 56
char label4buffer[4]; // box> char label4buffer[4]; // box>
uint16_t coinsInVault[16]; uint16_t coinsInVault[16];
uint16_t billsInStacker[8]; uint16_t billsInStacker[8];
// 48
char label5buffer[4]; // val> char label5buffer[4]; // val>
// actually constant unless exchange rate is changed // actually constant unless exchange rate is changed
@@ -159,11 +155,17 @@ struct T_vaultRecord
uint16_t exchangeRate; uint16_t exchangeRate;
uint16_t resint9; 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 temper;
uint8_t poweronTest; 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 doorWasOpened; // 1: all doors are closed 200: any door was just opened
uint8_t changer; // can only be tested by usage 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 // set by master, used(1) or notused (0) or type 2....20
UCHAR kindOfPrinter; // 0:off 1:Gebe UCHAR kindOfPrinter; // 0:off 1:Gebe
UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza c<EFBFBD> (MW) UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza Csquare (MW)
UCHAR kindOfMifareReader; // by now only stronglink SL025 =1 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 kindOfModem; // 0:off 1:Sunlink
UCHAR kindOfCreditcard; // 0:off 1:Feig NFC UCHAR kindOfCreditcard; // 0:off 1:Feig NFC
@@ -336,6 +339,8 @@ struct T_devices
UINT VaultFullWarnLevel; UINT VaultFullWarnLevel;
UINT VaultFullErrorLevel; UINT VaultFullErrorLevel;
UINT BattEmptyWarnLevel;
UINT BattEmptyErrorLevel;
}; };
@@ -357,7 +362,7 @@ public:
// Furthermore the Cashagent-Library answers with status strings about sending and reading result // 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; virtual bool dc_openSerial(int BaudNr, QString BaudStr, QString ComName, uint8_t connect) const =0;
// Command: open serial interface // Command: open serial interface
// BaudNr: 0:1200 1:9600 2:19200 3:38400 4:57600 5:115200 // 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; virtual void dc_closeSerial(void) const =0;
// Command: close serial interface in order to save power while power down // Command: close serial interface in order to save power while power down
// or if another port must be used // or if another port must be used
#endif
virtual bool dc_isPortOpen(void) const =0; virtual bool dc_isPortOpen(void) const =0;
// returns true if port open (don't send unless open. Sending to closed port will crash program) // 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 // get data back in "payLoad", max 64 byte, can be used for diagnosis
// retval = nr of bytes received. If host buffer too small then // retval = nr of bytes received. If host buffer too small then
// only plBufSiz bytes are copied to "payLoad" // only plBufSiz bytes are copied to "payLoad"
// plBufSiz<EFBFBD>z=size of host buffer // plBufSiz = size of host buffer
virtual void dc_setWakeFrequency(uint8_t period) const =0; virtual void dc_setWakeFrequency(uint8_t period) const =0;
// RTC wakes DC2 (and PTU) by hardware signal every 32seconds // RTC wakes DC2 (and PTU) by hardware signal every 32seconds
@@ -403,7 +408,7 @@ public:
virtual void dc_OrderToReset(void) const =0; virtual void dc_OrderToReset(void) const =0;
// want DC2 to reset (in order to start Bootloader) // want DC2 to reset (in order to start Bootloader)
#ifdef THIS_IS_CA_MASTER
virtual QString dc_getSerialState(void) const =0; virtual QString dc_getSerialState(void) const =0;
// get result of opening-command like "ttyS0 opened with 115200 8N1! // get result of opening-command like "ttyS0 opened with 115200 8N1!
// or error messages like "comport not available..." // or error messages like "comport not available..."
@@ -411,7 +416,7 @@ public:
virtual void dc_clrSerialStateText(void) const =0; virtual void dc_clrSerialStateText(void) const =0;
// clear above text to avoid multiple repetive displaying // clear above text to avoid multiple repetive displaying
#endif
virtual void bl_sendDataDirectly(uint8_t length, uint8_t *buf) const =0; virtual void bl_sendDataDirectly(uint8_t length, uint8_t *buf) const =0;
// send without protocol frame, needed for the DC bootloader // send without protocol frame, needed for the DC bootloader
@@ -496,10 +501,10 @@ public:
// Analog values: // Analog values:
virtual uint32_t dc_getTemperature(void) const =0; virtual uint32_t dc_getTemperature(void) const =0;
// in Sax-Format 0...400 (0=-50,0<EFBFBD>C 100=0,0<EFBFBD>C 141=20,5<EFBFBD>C 400=150,0<EFBFBD>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; virtual QString dc_getTemperaturStr(void) const =0;
// as string like "-12,5<EFBFBD>C" // as string like "-12,5degC"
virtual uint32_t dc_getVoltage(void) const =0; virtual uint32_t dc_getVoltage(void) const =0;
// as value in mV, 0...65,535V // as value in mV, 0...65,535V
@@ -760,7 +765,7 @@ public:
uint8_t kindOfModem, uint8_t kindOfCredit ) const =0; uint8_t kindOfModem, uint8_t kindOfCredit ) const =0;
// enable hardware in device controller: // enable hardware in device controller:
// kindOfPrinter: 0:off 1: GPT4672 (only this one implemented) // kindOfPrinter: 0:off 1: GPT4672 (only this one implemented)
// kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: C<EFBFBD>_changer // kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: Csquare_changer
// kindOfMifareReader: 0:off 1: SL025 (only this one implemented) // kindOfMifareReader: 0:off 1: SL025 (only this one implemented)
// suppressSleep: 0:sleep allowed 1: sleep surpressed for special reason // suppressSleep: 0:sleep allowed 1: sleep surpressed for special reason
// kindOfModem: 0:off 1: ATB_Sunlink_LTE (not yet implemented) // 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 // send 5 byte: byte 0,1: speed 5...250 mm/s
// byte2: density 0....(25)....50 // byte2: density 0....(25)....50
// byte3: alignment 'l', 'c', 'r' = left, center, right // byte3: alignment 'l', 'c', 'r' = left, center, right
// byte4: orientation 0, 90, 180 = 0<EFBFBD>, 90<EFBFBD>, 180<EFBFBD> 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 // not batched! don't use twice within 100ms
virtual void prn_movePaper(uint8_t wayInMm, uint8_t direction) const =0; virtual void prn_movePaper(uint8_t wayInMm, uint8_t direction) const =0;
@@ -1018,7 +1023,7 @@ public:
// use for changer // use for changer
#ifdef THIS_IS_CA_MASTER
virtual QString dc_getTxt4RsDiagWin(void) const =0; virtual QString dc_getTxt4RsDiagWin(void) const =0;
virtual void dc_clrTxt4RsDiagWin(void) const =0; virtual void dc_clrTxt4RsDiagWin(void) const =0;
virtual QString dc_get2ndTxt4RsDiagWin(void) const =0; virtual QString dc_get2ndTxt4RsDiagWin(void) const =0;
@@ -1033,7 +1038,6 @@ public:
virtual void dc_clrTxt4dataStateLine(void) const =0; virtual void dc_clrTxt4dataStateLine(void) const =0;
virtual QString dc_getdatifLine(void) const =0; virtual QString dc_getdatifLine(void) const =0;
virtual void dc_clrTxt4datifLine(void) const =0; virtual void dc_clrTxt4datifLine(void) const =0;
#endif
@@ -1152,14 +1156,13 @@ public:
// to be forwarded to Ismas // to be forwarded to Ismas
virtual bool prn_printAccountReceipt(void) const =0; 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 // return true if sending to DC OK, false if cmd-stack is full
virtual bool prn_printTestTicket(void) const =0; virtual bool prn_printTestTicket(void) const =0;
// return true if sending to DC OK, false if cmd-stack is full // return true if sending to DC OK, false if cmd-stack is full
virtual bool cash_startPayment(uint32_t amount) const =0; virtual bool cash_startPayment(uint32_t amount) const =0;
// 17.4.23TS: extended to 32bit // 17.4.23TS: extended to 32bit
@@ -1178,7 +1181,7 @@ public:
virtual uint16_t getLastInsertedCoin(void) const =0; virtual uint16_t getLastInsertedCoin(void) const =0;
virtual bool getAllInsertedCoins(uint16_t *types, uint16_t *values) const =0; virtual bool getAllInsertedCoins(uint16_t *types, uint16_t *values) const =0;
// alle bei diesem Verkauf eingeworfenen M<EFBFBD>nzen sind gespeichert, max 64 // alle bei diesem Verkauf eingeworfenen Muenzen sind gespeichert, max 64
virtual bool cash_cancelPayment(void) const =0; virtual bool cash_cancelPayment(void) const =0;
@@ -1232,6 +1235,8 @@ public:
virtual uint8_t prn_getPrintResult() const =0; virtual uint8_t prn_getPrintResult() const =0;
virtual uint8_t prn_getCurrentPrinterState() const =0; virtual uint8_t prn_getCurrentPrinterState() const =0;
// 0: printer OK // 0: printer OK
// bit0: near paper end bit1: no paper // 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_sendDeviceParameter(struct T_devices *deviceSettings) const =0;
virtual void sys_restoreDeviceParameter(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; virtual bool sys_areDCdataValid(void) const =0;
@@ -1268,6 +1275,71 @@ public:
virtual bool dc_isAutoRequestOn(void) const =0; 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 // 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 // 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.Psa2020.software.HWapi/3.1"
//#define HWINF_iid "Atb.Psa1256ptu5.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.0"
//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.1" //#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.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"

View File

@@ -62,10 +62,11 @@ bool Command::execute(QString workingDirectory, QStringList args) {
if (p->waitForFinished(m_waitForFinishTimeout)) { if (p->waitForFinished(m_waitForFinishTimeout)) {
//qDebug() << "PROCESS" << m_command << "FINISHED"; //qDebug() << "PROCESS" << m_command << "FINISHED";
if (p->exitStatus() == QProcess::NormalExit) { if (p->exitStatus() == QProcess::NormalExit) {
//qInfo() << "EXECUTED" << m_command if (p->exitCode() == 0) {
// << "with code" << p->exitCode();
// qInfo() << "RESULT" << m_commandResult;
return true; return true;
} else {
qCritical() << "EXECUTED" << m_command << "with code" << p->exitCode();
}
} else { } else {
qCritical() << "PROCESS" << m_command << "CRASHED with code" qCritical() << "PROCESS" << m_command << "CRASHED with code"
<< p->exitCode(); << p->exitCode();

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

View File

@@ -1,4 +1,5 @@
#include "update.h" #include "update.h"
#include "worker.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QApplication> #include <QApplication>
@@ -7,6 +8,7 @@
#include <QDebug> #include <QDebug>
#include <QTextStream> #include <QTextStream>
#include <QRegularExpression> #include <QRegularExpression>
#include <QRegExp>
//#include <iostream> //#include <iostream>
//#include <fstream> //#include <fstream>
@@ -25,9 +27,9 @@
#define UPDATE_OPKG (1) #define UPDATE_OPKG (1)
#define UPDATE_DC (1) #define UPDATE_DC (1)
#define UPDATE_PRINTER_TEMPLATES (1) #define UPDATE_PRINTER_TEMPLATES (1)
#define UPDATE_CASH_TEMPLATE (0) #define UPDATE_CASH_TEMPLATE (1)
#define UPDATE_CONF_TEMPLATE (0) #define UPDATE_CONF_TEMPLATE (1)
#define UPDATE_DEVICE_TEMPLATE (0) #define UPDATE_DEVICE_TEMPLATE (1)
static const QMap<QString, int> baudrateMap = { static const QMap<QString, int> baudrateMap = {
{"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3}, {"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3},
@@ -75,6 +77,7 @@ hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) {
} }
Update::Update(hwinf *hw, Update::Update(hwinf *hw,
Worker *worker,
QString customerRepository, QString customerRepository,
QString customerNrStr, QString customerNrStr,
QString branchName, QString branchName,
@@ -85,6 +88,7 @@ Update::Update(hwinf *hw,
char const *baudrate) char const *baudrate)
: QObject(parent) : QObject(parent)
, m_hw(hw) , m_hw(hw)
, m_worker(worker)
, m_serialInterface(serialInterface) , m_serialInterface(serialInterface)
, m_baudrate(baudrate) , m_baudrate(baudrate)
, m_customerRepository(customerRepository) , m_customerRepository(customerRepository)
@@ -347,21 +351,25 @@ bool Update::downloadBinaryToDC(QString const &bFile) const {
10 : bl_stopBL() // leave bl and start (the new) application 10 : bl_stopBL() // leave bl and start (the new) application
*/ */
bool Update::updateBinary(char const *fileToSendToDC) { bool Update::updateBinary(char const *fileToSendToDC) {
qInfo() << "updating device controller binary" << fileToSendToDC; qInfo() << "UPDATING DEVICE CONTROLLER BINARY" << fileToSendToDC;
QFile fn(fileToSendToDC); QFile fn(fileToSendToDC);
bool r; bool r;
if ((r = fn.exists()) == true) { if ((r = fn.exists()) == true) {
QString const linkTarget = fn.symLinkTarget(); QFileInfo fi(fn);
QFileInfo fi(linkTarget); qInfo() << " UPDATING BINARY" << fi.fileName() << "(size=" << fi.size() << ")";
qInfo() << " updating binary (size=" << linkTarget << fi.size() << ")"; if ((r = updateDC(fileToSendToDC)) == true) {
if ((r = updateDC(linkTarget)) == true) { qCritical() << QString(80, '*');
qInfo() << " updating binary (size=" << linkTarget << fi.size() << ") done"; qInfo() << " UPDATING BINARY" << fi.fileName() << "(size=" << fi.size() << ") DONE";
qCritical() << QString(80, '*');
} else { } else {
qCritical() << "updating binary (size=" << linkTarget << fi.size() << ")... FAILED"; qCritical() << QString(80, '*');
qCritical() << " UPDATING BINARY" << fi.fileName() << "(size=" << fi.size() << ") FAILED";
qCritical() << QString(80, '*');
} }
} else { } else {
qCritical() << "symlink" << fileToSendToDC qCritical() << QString(80, '*');
<< "does not exist -> NO UPDATE OF DC FIRMWARE"; qCritical() << fileToSendToDC << "does not exist -> NO UPDATE OF DC FIRMWARE";
qCritical() << QString(80, '*');
} }
return r; return r;
} }
@@ -510,8 +518,7 @@ void Update::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError())); disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
} }
bool Update::doUpdate(QStringList const &filesToWorkOn) { bool Update::doUpdate(int &displayIndex, QStringList const &filesToWorkOn) {
// //
// ACHTUNG !!! // ACHTUNG !!!
// //
@@ -520,11 +527,6 @@ bool Update::doUpdate(QStringList const &filesToWorkOn) {
bool serialOpened = false; bool serialOpened = false;
bool serialOpen = false; bool serialOpen = false;
if (filesToWorkOn.size() == 0) {
qCritical() << "NOTHING TO UPDATE";
return true;
}
if (!serialOpen) { if (!serialOpen) {
if (!isSerialOpen()) { // open serial only if not already open if (!isSerialOpen()) { // open serial only if not already open
if ((serialOpened = openSerial(baudrateMap.value(m_baudrate), m_baudrate, m_serialInterface)) == false) { if ((serialOpened = openSerial(baudrateMap.value(m_baudrate), m_baudrate, m_serialInterface)) == false) {
@@ -534,19 +536,23 @@ bool Update::doUpdate(QStringList const &filesToWorkOn) {
} }
} }
serialOpen = true; serialOpen = true;
qCritical() << "SERIAL OPEN" << m_serialInterface << "(BAUDRATE=" << m_baudrate << ")"; qInfo() << "SERIAL OPEN" << m_serialInterface << "(BAUDRATE=" << m_baudrate << ")";
} }
bool res = false;
QList<QString>::const_iterator it; QList<QString>::const_iterator it;
for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) { for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) {
bool res = false; m_worker->startProgressLoop();
QString fToWorkOn = (*it).trimmed(); QString fToWorkOn = (*it).trimmed();
fToWorkOn = QDir::cleanPath(m_customerRepository + QDir::separator() + fToWorkOn);
static const QRegularExpression version("^.*dc2c[.][0-9][0-9][.][0-9][0-9][.]bin.*$");
if (fToWorkOn.contains(version)) {
if (fToWorkOn.contains("dc2c", Qt::CaseInsensitive) && qInfo() << QString(80, '*');
fToWorkOn.endsWith(".bin", Qt::CaseInsensitive)) { qInfo() << "DO-UPDATE FILE-TO-WORK-ON" << fToWorkOn;
qInfo() << QString(80, '*');
qDebug() << "sending sw/hw-requests...";
for (int i=0; i < 3; ++i) { // send explicit reuests to get for (int i=0; i < 3; ++i) { // send explicit reuests to get
// current SW/HW-versions // current SW/HW-versions
m_hw->request_DC2_SWversion(); m_hw->request_DC2_SWversion();
@@ -556,12 +562,16 @@ bool Update::doUpdate(QStringList const &filesToWorkOn) {
QString const hwVersion = m_hw->dc_getHWversion().toLower(); QString const hwVersion = m_hw->dc_getHWversion().toLower();
QString const fwVersion = m_hw->dc_getSWversion().toLower(); QString const fwVersion = m_hw->dc_getSWversion().toLower();
qInfo() << "current dc-hardware-version" << hwVersion; qInfo() << "current dc-hardware-version" << hwVersion;
qInfo() << "current dc-firmware-version" << fwVersion; qInfo() << "current dc-firmware-version" << fwVersion;
QFile fn(fToWorkOn); QFile fn(fToWorkOn);
QFileInfo linkTarget(fn.symLinkTarget()); QFileInfo finfo(fn);
if (!linkTarget.exists()) { // check for broken link if (!fn.exists()) { // check for broken link
qCritical() << QString(80, '*');
qCritical() << "FILE-TO-WORK-ON" << fn << "DOES NOT EXIST";
qCritical() << QString(80, '*');
res = false; res = false;
} else { } else {
if (false) { if (false) {
@@ -572,26 +582,29 @@ bool Update::doUpdate(QStringList const &filesToWorkOn) {
} else { } else {
res = true; res = true;
qCritical() << "downloading" << fToWorkOn.trimmed() << "->" qInfo() << "DOWNLOADING" << finfo.completeBaseName() << "TO DC";
<< linkTarget.completeBaseName() << "to DC";
#if UPDATE_DC == 1 #if UPDATE_DC == 1
m_hw->dc_autoRequest(false);// default: turn auto-request setting off m_hw->dc_autoRequest(false);// default: turn auto-request setting off
QThread::sleep(1); // wait to be sure that there are no more QThread::sleep(1); // wait to be sure that there are no more
// commands sent to dc-hardware // commands sent to dc-hardware
qDebug() << "SET AUTO-REQUEST=FALSE"; qInfo() << "SET AUTO-REQUEST=FALSE";
if ((res = updateBinary(fToWorkOn.toStdString().c_str())) == true) { if ((res = updateBinary(fToWorkOn.toStdString().c_str())) == true) {
qCritical() << "downloaded binary" << fToWorkOn; qCritical() << "downloaded binary" << fToWorkOn;
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
} }
m_hw->dc_autoRequest(true); // turn auto-request setting on m_hw->dc_autoRequest(true); // turn auto-request setting on
qDebug() << "SET AUTO-REQUEST=TRUE"; qInfo() << "SET AUTO-REQUEST=TRUE";
qDebug() << "WAIT 10 SECS TO RECEIVE RESPONSES..."; qInfo() << "WAIT 10 SECS TO RECEIVE RESPONSES...";
QThread::sleep(10); // wait to be sure that responses QThread::sleep(10); // wait to be sure that responses
// have been received // have been received
qCritical() << "updated dc-hardware-version" << m_hw->dc_getHWversion(); qInfo() << "updated dc-hardware-version" << m_hw->dc_getHWversion();
qCritical() << "updated dc-firmware-version" << m_hw->dc_getSWversion(); qInfo() << "updated dc-firmware-version" << m_hw->dc_getSWversion();
#endif #endif
} }
} }
@@ -607,6 +620,10 @@ bool Update::doUpdate(QStringList const &filesToWorkOn) {
} else { } else {
if ((res = updatePrinterTemplate(templateIdx, fToWorkOn))) { if ((res = updatePrinterTemplate(templateIdx, fToWorkOn))) {
qInfo() << "downloaded printer template"<< fToWorkOn; qInfo() << "downloaded printer template"<< fToWorkOn;
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
} }
} }
#endif #endif
@@ -614,34 +631,52 @@ bool Update::doUpdate(QStringList const &filesToWorkOn) {
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true; res = true;
#if UPDATE_CASH_TEMPLATE == 1 #if UPDATE_CASH_TEMPLATE == 1
if ((res = updateCashConf(name))) { if ((res = updateCashConf(fToWorkOn))) {
qInfo() << "downloaded cash template"<< name; qInfo() << "downloaded cash template"<< fToWorkOn;
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
} }
#endif #endif
} else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive) } else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true; res = true;
#if UPDATE_CONF_TEMPLATE == 1 #if UPDATE_CONF_TEMPLATE == 1
if ((res= updateConfig(name))) { if ((res= updateConfig(fToWorkOn))) {
qInfo() << "downloaded config template"<< name; qInfo() << "downloaded config template"<< fToWorkOn;
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
} }
#endif #endif
} else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive) } else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) { && fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true; res = true;
#if UPDATE_DEVICE_TEMPLATE == 1 #if UPDATE_DEVICE_TEMPLATE == 1
if ((res = updateDeviceConf(name))) { if ((res = updateDeviceConf(fToWorkOn))) {
qInfo() << "downloaded device template"<< name; qInfo() << "downloaded device template"<< fToWorkOn;
++displayIndex;
emit m_worker->appendText(QString("\n(") + QString("%1").arg(displayIndex).rightJustified(2, ' ') + QString(")")
+ QString(" Update ") + QFileInfo(fToWorkOn).fileName(),
Worker::UPDATE_STEP_DONE);
} }
#endif #endif
} else { } else {
qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn; qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn;
res = false; res = false;
} }
m_worker->stopProgressLoop();
m_worker->setProgress(100);
if (res == false) {
break;
}
} // for (it = openLines.cbegin(); it != openLines.end(); ++it) { } // for (it = openLines.cbegin(); it != openLines.end(); ++it) {
m_hw->dc_autoRequest(true); // ALWAYS turn autoRequest ON m_hw->dc_autoRequest(true); // ALWAYS turn autoRequest ON
qDebug() << "SET AUTO-REQUEST=TRUE"; qDebug() << "SET AUTO-REQUEST=TRUE";
return true; return res;
} }

View File

@@ -9,7 +9,6 @@
#include <QProcess> #include <QProcess>
#include "plugins/interfaces.h" #include "plugins/interfaces.h"
#include "apism/apism_client.h"
#ifdef PTU5 #ifdef PTU5
#define SERIAL_PORT "ttymxc2" #define SERIAL_PORT "ttymxc2"
@@ -17,15 +16,12 @@
#define SERIAL_PORT "ttyUSB0" #define SERIAL_PORT "ttyUSB0"
#endif #endif
class Update; class Worker;
// TODO: check hardware compatibility
// TODO: opkg commandos
class Update : public QObject { class Update : public QObject {
Q_OBJECT Q_OBJECT
hwinf *m_hw; hwinf *m_hw;
Worker *m_worker;
char const *m_serialInterface; char const *m_serialInterface;
char const *m_baudrate; char const *m_baudrate;
QString m_customerRepository; QString m_customerRepository;
@@ -44,6 +40,7 @@ public:
explicit Update(hwinf *hw, explicit Update(hwinf *hw,
Worker *worker,
QString customerRepository, QString customerRepository,
QString customerNrStr, QString customerNrStr,
QString branchName, QString branchName,
@@ -53,7 +50,7 @@ public:
char const *serialInterface = SERIAL_PORT, char const *serialInterface = SERIAL_PORT,
char const *baudrate = "115200"); char const *baudrate = "115200");
virtual ~Update() override; virtual ~Update() override;
bool doUpdate(QStringList const &linesToWorkOn); bool doUpdate(int &displayIndex, QStringList const &linesToWorkOn);
//QString customerId() { return m_customerId; } //QString customerId() { return m_customerId; }
//QString const customerId() const { return m_customerId; } //QString const customerId() const { return m_customerId; }

246
utils.cpp
View File

@@ -1,201 +1,79 @@
#include "utils.h" #include "utils.h"
#include "message_handler.h"
#include <QCoreApplication>
#include <QApplication>
#include <QFile> #include <QFile>
#include <QTemporaryFile>
#include <QDebug>
#include <QTextStream> #include <QTextStream>
#include <QDebug>
#include "interfaces.h" int Utils::read1stLineOfFile(QString fileName) {
#include "DCPlugin/include/hwapi.h" QFile f(fileName);
if (f.exists()) {
//#include <unistd.h> if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
#include <thread> QTextStream in(&f);
#include <memory> in.setCodec("UTF-8");
#include <QSharedMemory> while(!in.atEnd()) {
#include <QScopedPointer> return in.readLine().toInt();
#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() return -1;
<< "for writing"; }
m_init = false;
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";
}
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() << "";
} }
} }
Utils::~Utils() { QString Utils::getTariffLoadTime(QString fileName) {
QFileInfo fInfo(fileName);
} if (fInfo.exists()) {
QDateTime lastModifiedTime = fInfo.lastModified();
void Utils::updateBinary(char const *fileToSendToDC) { if (lastModifiedTime.isValid()) {
qDebug() << "file to send to DC ..." << fileToSendToDC; return lastModifiedTime.toString(Qt::ISODateWithMs);
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 { } else {
openLines << line; printCriticalErrorMsg(fileName + " HAS INVALID MODIFIED-TIME");
} QDateTime birthTime = fInfo.birthTime();
} if (birthTime.isValid()) {
return openLines; return birthTime.toString(Qt::ISODateWithMs);
} } else {
printCriticalErrorMsg(fileName + " HAS INVALID BIRTH-TIME");
bool Utils::doUpdate() {
/*
The file referred to by 'update_data' has the following structure for
each line:
# ======================================================================
# 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) {
return false;
}
QStringList openLines = getOpenLines();
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;
}
++column;
start = end + 1;
}
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;
}
} }
} }
} else { } else {
// TODO printCriticalErrorMsg(fileName + " DOES NOT EXIST");
} }
QString resultLine = "CLOSED"; return "N/A";
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;
}

48
utils.h
View File

@@ -3,44 +3,18 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QStringList>
#include <QFile> #include <QFile>
#include <QFileInfo>
#include <QDateTime>
#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);
}
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
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 #endif // UTILS_H_INCLUDED

1321
worker.cpp

File diff suppressed because it is too large Load Diff

122
worker.h
View File

@@ -13,7 +13,6 @@
#include "update.h" #include "update.h"
#include "git/git_client.h" #include "git/git_client.h"
#include "ismas/ismas_client.h" #include "ismas/ismas_client.h"
#include "apism/apism_client.h"
#ifdef PTU5 #ifdef PTU5
#define SERIAL_PORT "ttymxc2" #define SERIAL_PORT "ttymxc2"
@@ -23,21 +22,34 @@
enum class UPDATE_STATUS : quint8 { enum class UPDATE_STATUS : quint8 {
ISMAS_EMULATE_DATA_AVAILABLE, NOT_DEFINED,
ISMAS_UPDATE_REQUEST_PENDING, STEP_OK,
ISMAS_UPDATE_REQUEST_FAILURE, STEP_DONE,
ISMAS_UPDATE_REQUEST_TIMEOUT, STEP_FAIL,
ISMAS_UPDATE_REQUEST_SUCCESS, ISMAS_WAIT_STATE_CHECK_PENDING,
ISMAS_WAIT_STATE_CHECK_FAILURE,
ISMAS_WAIT_STATE_CHECK_TIMEOUT,
ISMAS_WAIT_STATE_CHECK_SUCCESS,
ISMAS_RESPONSE_RECEIVED, ISMAS_RESPONSE_RECEIVED,
GIT_CHECKOUT_BRANCH_REQUEST, BACKEND_CONNECTED,
BACKEND_CHECK,
BACKEND_CHECK_FAILURE,
ISMAS_BACKEND_CHECK_FAILURE,
BACKEND_NOT_CONNECTED,
UPDATE_TRIGGER_SET,
UPDATE_TRIGGER_NOT_SET_OR_WRONG,
GIT_CLONE_AND_CHECKOUT_SUCCESS,
GIT_CLONE_AND_CHECKOUT_FAILURE,
GIT_CHECKOUT_BRANCH,
GIT_CHECKOUT_BRANCH_REQUEST_FAILURE, GIT_CHECKOUT_BRANCH_REQUEST_FAILURE,
GIT_CHECKOUT_BRANCH_NOT_EXISTS, GIT_CHECKOUT_BRANCH_NOT_EXISTS,
GIT_CHECKOUT_BRANCH_CHECKOUT_ERROR, GIT_CHECKOUT_BRANCH_CHECKOUT_ERROR,
GIT_FETCH_UPDATES_REQUEST, GIT_FETCH_UPDATES,
GIT_FETCH_UPDATES_REQUEST_FAILURE, GIT_FETCH_UPDATES_REQUEST_FAILURE,
GIT_FETCH_UPDATES_REQUEST_SUCCESS, GIT_FETCH_UPDATES_REQUEST_SUCCESS,
GIT_PULL_UPDATES_SUCCESS, GIT_PULL_UPDATES_SUCCESS,
GIT_PULL_UPDATES_FAILURE, GIT_PULL_UPDATES_FAILURE,
EXEC_OPKG_COMMAND,
EXEC_OPKG_COMMANDS, EXEC_OPKG_COMMANDS,
EXEC_OPKG_COMMAND_FAILURE, EXEC_OPKG_COMMAND_FAILURE,
EXEC_OPKG_COMMAND_SUCCESS, EXEC_OPKG_COMMAND_SUCCESS,
@@ -50,20 +62,30 @@ enum class UPDATE_STATUS : quint8 {
DEVICE_CONTROLLER_UPDATE_SUCCESS, DEVICE_CONTROLLER_UPDATE_SUCCESS,
JSON_UPDATE, JSON_UPDATE,
JSON_UPDATE_FAILURE, JSON_UPDATE_FAILURE,
JSON_PARSE_FAILURE,
JSON_UPDATE_SUCCESS, JSON_UPDATE_SUCCESS,
UPDATE_PROCESS_SUCCESS,
UPDATE_PROCESS_FAILURE,
ISMAS_UPDATE_INFO_CONFIRM, ISMAS_UPDATE_INFO_CONFIRM,
ISMAS_UPDATE_INFO_CONFIRM_FAILURE, ISMAS_UPDATE_INFO_CONFIRM_FAILURE,
ISMAS_UPDATE_INFO_CONFIRM_SUCCESS, ISMAS_UPDATE_INFO_CONFIRM_SUCCESS,
ISMAS_CURRENT_PSA_STATUS_CONFIRM, ISMAS_CURRENT_PSA_STATUS_CONFIRM,
ISMAS_CURRENT_PSA_STATUS_CONFIRM_FAILURE, ISMAS_CURRENT_PSA_STATUS_CONFIRM_FAILURE,
ISMAS_CURRENT_PSA_STATUS_CONFIRM_SUCCESS ISMAS_CURRENT_PSA_STATUS_CONFIRM_SUCCESS,
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 { struct UpdateStatus {
UPDATE_STATUS m_updateStatus; UPDATE_STATUS m_updateStatus;
QString m_statusDescription; QString m_statusDescription;
explicit UpdateStatus(UPDATE_STATUS s, QString const &d) explicit UpdateStatus(UPDATE_STATUS s = UPDATE_STATUS::NOT_DEFINED,
QString const &d = QString(""))
: m_updateStatus(s), m_statusDescription(d) {} : m_updateStatus(s), m_statusDescription(d) {}
}; };
@@ -72,16 +94,13 @@ QString& operator<<(QString &str, UpdateStatus status);
#define ISMAS_UPDATE_REQUESTS (10) #define ISMAS_UPDATE_REQUESTS (10)
class MainWindow;
class hwinf; class hwinf;
class Worker : public QObject { class Worker : public QObject {
Q_OBJECT Q_OBJECT
hwinf *m_hw; hwinf *m_hw;
WorkerThread m_workerThread; WorkerThread m_workerThread;
QTimer m_startUpdateProcess;
QTimer m_emergencyTimer;
Update *m_update;
ApismClient m_apismClient;
int const m_customerNr; int const m_customerNr;
QString const m_customerNrStr; QString const m_customerNrStr;
int const m_machineNr; int const m_machineNr;
@@ -90,8 +109,9 @@ class Worker : public QObject {
QString const m_branchName; QString const m_branchName;
QString const m_customerRepositoryPath; QString const m_customerRepositoryPath;
QString const m_customerRepository; QString const m_customerRepository;
Update *m_update;
IsmasClient m_ismasClient;
GitClient m_gc; GitClient m_gc;
bool m_maintenanceMode;
QString const m_osVersion; QString const m_osVersion;
QString const m_atbqtVersion; QString const m_atbqtVersion;
QString const m_cpuSerial; QString const m_cpuSerial;
@@ -107,12 +127,19 @@ class Worker : public QObject {
int m_ismasUpdateRequests; int m_ismasUpdateRequests;
QTimer m_waitForNewUpdates; QTimer m_waitForNewUpdates;
IsmasClient m_ismasClient;
UPDATE_STATUS m_updateStatus; UpdateStatus m_updateStatus;
QString m_statusDescription;
void executeOpkgCommand(QString opkgCommand); QStringList m_filesToUpdate;
bool m_updateProcessRunning;
int m_displayIndex;
int m_returnCode;
MainWindow *m_mainWindow;
int m_progressValue;
bool m_withoutIsmasDirectPort;
bool executeOpkgCommand(QString opkgCommand);
QString getOsVersion() const; QString getOsVersion() const;
QString getATBQTVersion() const; QString getATBQTVersion() const;
QString getCPUSerial() const; QString getCPUSerial() const;
@@ -124,21 +151,37 @@ class Worker : public QObject {
qint64 getFileSize(QString const &fileName) const; qint64 getFileSize(QString const &fileName) const;
public: public:
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(hwinf *hw, explicit Worker(hwinf *hw,
int customerNr, // 281 int customerNr, // 281
int machineNr, int machineNr,
int zoneNr, int zoneNr,
QString branchName, QString branchName,
QString workingDir = ".", QString workingDir = ".",
bool maintenanceMode = false,
bool dryRun = false, bool dryRun = false,
QObject *parent = nullptr, QObject *parent = nullptr,
char const *serialInterface = SERIAL_PORT, char const *serialInterface = SERIAL_PORT,
char const *baudrate = "115200"); char const *baudrate = "115200");
~Worker(); ~Worker();
void quit() { return m_workerThread.quit(); }
static int read1stLineOfFile(QString fileName); void setMainWindow(MainWindow *mainWindow) { m_mainWindow = mainWindow; }
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; }
//friend QDebug operator<<(QDebug debug, Worker const &w) { //friend QDebug operator<<(QDebug debug, Worker const &w) {
// Q_UNUSED(w); // Q_UNUSED(w);
@@ -150,24 +193,31 @@ public:
//} //}
signals: signals:
void handleChangedFiles(QStringList); void appendText(QString, QString suffix = "");
void summarizeUpload(QStringList); void replaceLast(QString, QString);
void summarizeRepositoryStatus(); void showErrorMessage(QString title, QString description);
void sendCmdSendVersionToIsmas(); void stopStartTimer();
void finishUpdateProcess(bool changes); void restartExitTimer();
void terminateUpdateProcess(); void enableExit();
void disableExit();
public slots: public slots:
void onIsmasResponseReceived(QJsonObject ismasResponse); void update();
private slots: private slots:
void askIsmasForNewData(); bool backendConnected();
void onSendCmdSendVersionToIsmas(); bool updateTriggerSet();
void onSummarizeRepositoryStatus(); bool customerEnvironment();
void onFinishUpdateProcess(bool changes); bool filesToUpdate();
void onTerminateUpdateProcess(); bool updateFiles(quint8 percent);
void onSummarizeUpload(QStringList); bool syncCustomerRepositoryAndFS();
void onHandleChangedFiles(QStringList); bool sendIsmasLastVersionNotification();
bool saveLogFile();
private:
PSAInstalled getPSAInstalled();
QString sendCmdSendVersionToIsmas();
void privateUpdate();
}; };
#endif // WORKER_H_INCLUDED #endif // WORKER_H_INCLUDED