Compare commits

...

84 Commits

Author SHA1 Message Date
843ac23770 Build: remove lib dependency on Yocto build 2026-02-10 10:16:20 +01:00
c3b2db7797 Set version 1.5.99 2026-02-10 10:10:34 +01:00
51640b70e9 Merge branch '281/Szeged_without_LibDC' into 281/Szeged 2026-02-10 09:55:35 +01:00
be84c96953 Fix: remove test code 2026-02-10 09:53:58 +01:00
24bf190c90 281/Szeged: Skip updating json files ...
... because this is not supported in currently installed DC-FW version
and DC-lib version.
2026-02-09 15:57:22 +01:00
b656b5eab6 281/Szeged: compile only main program ATBUpdateTool
... because current installed DC-FW version or current installed DClib
in Szeged do not support updating DC-json-files and DC-FW.
2026-02-09 15:55:01 +01:00
6681e61f59 Szeged is still using libCAmaster 2026-02-09 15:21:45 +01:00
7ec6ab00b1 Use interfaces.h from version 1.3.5 2026-02-09 15:19:42 +01:00
057e4603ce ATBUpdateDC: use ATBUpdateTool.ini 2026-02-05 16:19:33 +01:00
7b0e17c478 common/utils_internal: use state from df4e384d9d 2026-02-05 15:14:09 +01:00
e7f78fe976 Fix: merge-conflict from 'save for christmas' ...
518bd87033 2024-12-20 13:01:34
2026-02-05 15:10:18 +01:00
2ac87e93e4 UpdateDC: use common/command 2026-02-05 13:59:41 +01:00
47e7e1e796 Add common/command 2026-02-05 13:59:13 +01:00
553782a2ea Add common/include/utils_internal.h 2026-02-05 13:22:05 +01:00
f2af48eee3 after dc-download, check if device-controller reboots successfully 2026-02-05 12:41:39 +01:00
a30f8334f1 Replace TEST_DC_DOWNLOAD with m_debug. 2026-02-05 12:37:34 +01:00
ea1abc68da Replace TEST_DC_DOWNLOAD with m_debug. 2026-02-05 12:37:19 +01:00
9c40321857 Minor: init. m_debug to false. 2026-02-05 12:36:39 +01:00
0c6b219d31 read ATBUpdateDCsettings in *.ini file 2026-02-05 12:36:13 +01:00
de9304f867 Add define for TEST_DC_DOWNLOAD and add corresponding code
to test the download without actual dc-controller on the PTU.
2026-02-05 12:29:44 +01:00
47ac11fb9a "after dc_download restart autorequest or else E255" 2026-02-05 12:29:40 +01:00
379ca35db2 "shortened time to sendnextblock." 2026-02-05 12:29:34 +01:00
fc41883e6a "increased BL_start Timer (500 -> 1000) " 2026-02-05 12:29:12 +01:00
ff5d0a891c startBootloader():
set wait time to 1000ms.
    set try counter to 10.
    a message to ask for general bl installation (in hardware)
2026-02-05 12:05:29 +01:00
4d91d1e5bb turn dc-download on 2026-02-05 12:04:14 +01:00
bef618a778 testing... 2026-02-05 12:02:52 +01:00
d8112800d5 cleaning up source. rename doUndate() to run(). 2026-02-05 11:46:57 +01:00
98996af2b2 read ini-file and determine device controller to install 2026-02-05 11:46:50 +01:00
eb3f05ff19 preparing dc-update 2026-02-05 11:46:07 +01:00
5b621298c4 take over some code from mainwindow 2026-02-05 11:45:00 +01:00
579997b318 to be removed 2026-02-05 11:44:45 +01:00
6a32c75754 use qcoreapplication -> no window or widgets 2026-02-05 11:44:34 +01:00
724be1568d remove main-window (not needed) 2026-02-05 11:44:08 +01:00
d85eabf3e6 save files with comment (was 8f822c5) 2026-02-05 11:05:22 +01:00
d2d0eb40aa Revert "Remove any reference to device controller as downloading jsons/dc-firmware"
This reverts commit 6b0a784fc8.
2026-02-05 10:26:56 +01:00
518bd87033 save for christmas 2026-02-05 09:18:41 +01:00
6b0a784fc8 Remove any reference to device controller as downloading jsons/dc-firmware
will be done by ATBDownloadDCJsonFiles and ATBDownloadDCFirmware binaries.
2026-02-05 08:42:42 +01:00
093d77ddd1 Update::updateBinary():
begin with starting "/opt/app/tools/atbupdate/ATBDownloadDCFirmware".
2026-02-05 08:38:49 +01:00
ec57f9ba20 Minor: remove unused code. 2026-02-05 08:37:42 +01:00
96828f6e25 getCommandResult():
reset result if necessary (for instance for showing current result
	in GUI.
2026-02-05 08:37:20 +01:00
e1e2008aa6 read dc-verion directly from binary file 2026-02-05 08:36:17 +01:00
e82b92b95b Mior: remove debugs 2026-02-05 08:34:59 +01:00
808f01e1af init some vars to defaults 2026-02-05 08:34:29 +01:00
cf85965946 start with downloading dc: parsing command arguments. started to implement the acrual download 2026-02-05 08:33:58 +01:00
e1e6d1613a Merge branch 'removed-apsim-restart' 2026-02-04 10:13:15 +01:00
5a2ced4a96 set version to 1.5.9 2025-06-16 13:39:03 +02:00
3b3456196f removed restart of apism in case ISMAS is unreachable 2025-06-16 13:34:51 +02:00
c63fa92ff7 script to be started if update-tool finished unexpectedly 2025-05-20 14:19:57 +02:00
42624409ba set version to 1.5.8 2025-05-16 08:42:50 +02:00
c49ff5045b set version to 1.5.7 2025-05-16 08:40:13 +02:00
358fd80c47 Use applicationPid() of update-tool as event-id. (used by christian) 2025-05-14 10:44:09 +02:00
8a2d710cf8 Use pid of update-tool as event-id. (used by christian) 2025-05-14 10:42:27 +02:00
1ea1cdc3e6 set version to 1.5.7 after merge of pu/portrait 2025-02-19 12:08:17 +01:00
348fb15508 Ui: support portrait mode 2025-02-19 11:39:08 +01:00
061c57ef51 set verion to 1.5.6 2024-11-22 12:59:13 +01:00
e82417dde7 MainWindow::MainWindow():
Add status timer, to show proceeding update, so user does not exit
	application / restart machine.
2024-11-22 12:55:27 +01:00
1e271201c5 Set version to 1.5.5. 2024-11-21 09:15:33 +01:00
da66d75a45 getPSAInstalled():
call ptuPackagesVersion. Convert returned JSON-array into JsonObject
	to be appended to CMD_SENDVERSION.
2024-11-21 09:14:05 +01:00
7accabfa53 Add variables for handling ptu-package-versions and append corresponding data
to CMD_SENDVERSION.
2024-11-21 09:12:35 +01:00
201a1cbab9 privateUpdate():
Make sure opkg-commands re executed aunder certain error conditions:

	Failure of customerEnvironment(), filesToUpdate() or
	syncCustomerRepositoryAndFS().
2024-10-23 13:23:13 +02:00
9a9cce126a Minor: extended comment 2024-10-23 13:22:42 +02:00
da0fa54d28 gitPull():
Add important comment for the case that an remote host key change:
	'git pull' handles the issue itself, and continues to pull the repository
	using the ssh-key provided in the .keys/ directory.
2024-10-23 12:58:48 +02:00
1433c107b3 Remove right-justification in customer_id-name. Version: 1.5.3 2024-10-15 16:48:40 +02:00
8e0732cf95 Set version to 1.5.2. 2024-10-08 15:04:08 +02:00
8f6e1fdae7 clean up: remove .gz and .ipk files in /var/cache/opkg 2024-10-08 15:03:43 +02:00
0d00faf493 Remove hard coded (szeged) zone names 2024-07-23 09:28:54 +02:00
5beb235d92 Chop a trailing / of the repository url -> no clean path necessary 2024-07-17 14:50:32 +02:00
a4bb993217 Set version to 1.5.1 2024-07-17 14:06:18 +02:00
50357997c2 Do not use cleanPath() on an url-path 2024-07-17 14:04:53 +02:00
c02f0b08ed Set version to 1.5.0. 2024-06-28 13:01:24 +02:00
5da1eff41a Use repository-url="gitea@ptu-config.atb-comm.de:ATB/" 2024-06-28 12:58:13 +02:00
af89c9fbc1 Add SSH_GIT_COMMAND environment variable for pyu-config.atb-comm.de repository server. 2024-06-28 12:52:48 +02:00
fa7d1ba879 Allow empty lines in opkg_commands. 2024-06-28 12:50:30 +02:00
1dc7578645 Clean repository path from multiple slashes. 2024-06-28 12:49:53 +02:00
cd30cc91f0 Set version to 1.4.11. 2024-06-28 10:06:35 +02:00
6683b925aa Fix: remove hard-coded path to gitea repository (mimbach). 2024-06-28 10:03:10 +02:00
336d208906 Fix: never set the autoRequest-flag to false. 2024-06-26 13:27:39 +02:00
2da3c34d84 Set version to 1.4.10. 2024-06-26 13:26:55 +02:00
8f0a9112d6 Fix: do not set the autoRequest flag back to false. 2024-06-26 13:15:48 +02:00
25bf905a79 Use now new git-server: https://ptu-config.atb-comm.de/ATB 2024-06-14 13:36:34 +02:00
5d141b7234 Merge branch 'master' of git.mimbach49.de:GerhardHoffmann/ATBUpdateTool 2024-06-05 17:33:54 +02:00
ebb170123e Provide proto-binary 2024-06-05 10:10:21 +02:00
96bb70cfe9 Merge branch 'release_1.4.9' 2024-05-23 12:31:27 +02:00
8c1da92ff4 Make compile with yocto/bitbake 2024-05-23 12:29:07 +02:00
37 changed files with 3440 additions and 602 deletions

View File

@@ -1,5 +1,5 @@
[REPOSITORY_URL] [REPOSITORY_URL]
repository-url="https://git.mimbach49.de/GerhardHoffmann" repository-url="gitea@ptu-config.atb-comm.de:ATB/"
[DIRECTORIES] [DIRECTORIES]
plugin-directory="/usr/lib/" plugin-directory="/usr/lib/"
@@ -9,7 +9,7 @@ psa-tariff-directory="etc/psa_tariff/"
[PLUGINS] [PLUGINS]
plugin-name="libCAslave.so" plugin-name="libCAmaster.so"
[FLAGS] [FLAGS]
no-psa-hardware-update=false no-psa-hardware-update=false

View File

@@ -1,3 +1,4 @@
TEMPLATE = subdirs TEMPLATE = subdirs
CONFIG += ordered CONFIG += ordered
SUBDIRS = DownloadDCFirmware DownloadDCJsonFiles UpdatePTUDevCtrl #SUBDIRS = DownloadDCFirmware DownloadDCJsonFiles UpdatePTUDevCtrl
SUBDIRS = UpdatePTUDevCtrl

View File

@@ -1,7 +1,9 @@
QT += core gui QT += core serialport
QT += widgets serialport network
TARGET = ATBUpdateDC
include(../common.pri)
TARGET = ATBDownloadDCFirmware
VERSION="0.1.0" VERSION="0.1.0"
win32 { win32 {
@@ -77,21 +79,21 @@ contains( CONFIG, DesktopLinux ) {
SOURCES += \ SOURCES += \
main.cpp \ main.cpp \
mainwindow.cpp \
../common/src/message_handler.cpp \ ../common/src/message_handler.cpp \
../UpdatePTUDevCtrl/commandline_parser.cpp \ ../UpdatePTUDevCtrl/commandline_parser.cpp \
update.cpp \ update.cpp \
dc_download.cpp \ ../common/src/System.cpp \
../common/src/System.cpp ../common/src/utils_internal.cpp \
../common/src/command.cpp
HEADERS += \ HEADERS += \
mainwindow.h \
../common/include/message_handler.h \ ../common/include/message_handler.h \
../UpdatePTUDevCtrl/commandline_parser.h \ ../UpdatePTUDevCtrl/commandline_parser.h \
update.h \ update.h \
dc_download.h \ ../common/include/System.h \
../common/include/System.h ../common/include/utils_internal.h \
../common/include/command.h
OTHER_FILES += \ OTHER_FILES += \

View File

@@ -1,6 +1,7 @@
#include "dc_download.h" #include "dc_download.h"
#include "hwapi.h" #include <DeviceController/interfaces.h>
#include <QFile>
#include <QDebug> #include <QDebug>
#include <QDateTime> #include <QDateTime>
#include <QThread> #include <QThread>

View File

@@ -1,7 +1,6 @@
#include <QtGlobal> #include <QtGlobal>
#include <QCoreApplication> #include <QCoreApplication>
#include <QApplication>
#include <QByteArray> #include <QByteArray>
#include <QProcess> #include <QProcess>
@@ -11,12 +10,13 @@
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#include <QThread> #include <QThread>
#include <QRegularExpression>
#include "message_handler.h" #include "message_handler.h"
#include "commandline_parser.h" #include "commandline_parser.h"
#include "utils.h" #include "utils.h"
#include "utils_internal.h"
#include "update.h" #include "update.h"
#include "mainwindow.h"
#include "System.h" #include "System.h"
#include <DeviceController/interfaces.h> #include <DeviceController/interfaces.h>
@@ -52,17 +52,22 @@ int main(int argc, char **argv) {
} }
// qputenv("XDG_RUNTIME_DIR", "/var/run/user/0"); // qputenv("XDG_RUNTIME_DIR", "/var/run/user/0");
openlog("ATB-UPDATE-DC-FIRMWARE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); openlog("DC", LOG_PERROR | LOG_CONS, LOG_USER);
QCoreApplication a(argc, argv);
QCoreApplication::setOrganizationName("ATB Automatentechnik Baumann GmBH");
QCoreApplication::setApplicationName("ATBUpdateDC");
QCoreApplication::setApplicationVersion(APP_VERSION);
QApplication a(argc, argv);
QApplication::setApplicationName("ATBUpdateTool");
QApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput); atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE); setDebugLevel(LOG_NOTICE);
} }
//return 0;
/*
CommandLineParser parser; CommandLineParser parser;
parser.process(a); parser.process(a);
parser.readSettings(); parser.readSettings();
@@ -73,6 +78,7 @@ int main(int argc, char **argv) {
QString workingDir = parser.workingDir(); QString workingDir = parser.workingDir();
QString psaConfigDir = parser.psaConfigDir(); QString psaConfigDir = parser.psaConfigDir();
QString psaTariffDir = parser.psaTariffDir(); QString psaTariffDir = parser.psaTariffDir();
QString psaDcDir = parser.dcDir();
QString iniFileName = parser.iniFileName(); QString iniFileName = parser.iniFileName();
bool const dryRun = parser.dryRun(); bool const dryRun = parser.dryRun();
bool const noUpdatePsaHardware = parser.noUpdatePsaHardware(); bool const noUpdatePsaHardware = parser.noUpdatePsaHardware();
@@ -81,6 +87,7 @@ int main(int argc, char **argv) {
bool const showExtendedVersion = parser.extendedVersion(); bool const showExtendedVersion = parser.extendedVersion();
bool const alwaysDownloadConfig = parser.alwaysDownloadConfig(); bool const alwaysDownloadConfig = parser.alwaysDownloadConfig();
bool const alwaysDownloadDC = parser.alwaysDownloadDC(); bool const alwaysDownloadDC = parser.alwaysDownloadDC();
bool const readDCVersion = parser.readDCVersion();
QString const rtPath = QCoreApplication::applicationDirPath(); QString const rtPath = QCoreApplication::applicationDirPath();
@@ -109,26 +116,87 @@ int main(int argc, char **argv) {
qInfo() << "machineNr ................" << machineNr; qInfo() << "machineNr ................" << machineNr;
qInfo() << "customerNr ..............." << customerNr; qInfo() << "customerNr ..............." << customerNr;
qInfo() << "zoneNr ..................." << zoneNr; qInfo() << "zoneNr ..................." << zoneNr;
qInfo() << "readDCVersion ............" << readDCVersion;
qInfo() << "dcDir ...................." << psaDcDir;
if (!QDir(plugInDir).exists()) {
qCritical() << plugInDir
<< "does not exists, but has to contain dc-library";
exit(-1);
}
if (showExtendedVersion) { if (showExtendedVersion) {
printf(APP_EXTENDED_VERSION"\n"); printf(APP_EXTENDED_VERSION"\n");
return 0; return 0;
} }
QString const &customerRepo = QDir::cleanPath(workingDir + QDir::separator() + QString("customer_%1").arg(customerNr)); QString const &customerRepo
QStringList filesToUpdate; = QDir::cleanPath(workingDir + QDir::separator() + QString("customer_%1").arg(customerNr));
*/
QThread::currentThread()->setObjectName("main thread"); QString const &psaDcDir = internal::customerRepoDcDir();
qInfo() << "Main thread" << QThread::currentThreadId(); QString const &psaRepoRootDir = internal::customerRepoRoot();
QString const &psaRepoDir = internal::customerRepoDir();
QString const &branchName = internal::branchName();
MainWindow mw; bool debug = false;
bool noaction = true;
QString workingDir;
QString libDir;
QString libca;
mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint); std::unique_ptr<QSettings> settings = internal::readSettings();
//mw.showFullScreen();
qCritical() << "SHOW"; if (settings) {
settings->beginGroup("DIRECTORIES");
workingDir = settings->value("workingdir", "/tmp").toString();
libDir = settings->value("plugin-directory", "/usr/lib/").toString();
settings->endGroup();
mw.show(); settings->beginGroup("FLAGS");
debug = settings->value("debug", false).toBool();
settings->endGroup();
return a.exec(); settings->beginGroup("PLUGINS");
libca = libDir + settings->value("plugin-name", "libCAslave.so").toString();
settings->endGroup();
}
// etc/dc: located under mount-path
std::optional<QString> mountPath = System::checkForUSBStick(psaDcDir);
QFileInfo fi;
if (mountPath.has_value()) {
fi.setFile(mountPath.value(), System::getDCFileOnUsbStick(mountPath.value()));
} else
if ((mountPath = System::checkForSDCard(psaDcDir)).has_value()) {
fi.setFile(mountPath.value(), System::getDCFileOnSDCard(mountPath.value()));
} else {
if (debug) {
qInfo() << "using customer repository" << psaRepoDir;
}
std::unique_ptr<QString> c = internal::dcCandidateToInstall("/etc/dc/");
if (c) {
fi.setFile(*c);
if (fi.exists() == false) {
qCritical() << "dc2c.bin candidate" << *c << "does not exist. STOP.";
return -1;
}
qInfo() << "dc2c.bin canditate" << fi.absoluteFilePath();
}
}
if (debug) {
qInfo() << "downloading dc-firmware" << fi.absoluteFilePath();
qInfo() << "dc-firmware size (bytes)" << fi.size();
qInfo() << "dc-version" << Update::dcVersion(fi.absoluteFilePath());
}
Update u(fi.absoluteFilePath(), libca, debug, noaction);
u.run();
qInfo() << "<DC-UPDATE-FINISH>";
return 0;
} }

View File

@@ -1,10 +1,7 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer> #include <QTimer>
#include <QStatusBar>
#include <QWidget>
#include <QSerialPort> #include <QSerialPort>
#include <QSerialPortInfo> #include <QSerialPortInfo>

View File

@@ -1,4 +1,5 @@
#include "update.h" #include "update.h"
#include "command.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QFile> #include <QFile>
@@ -21,10 +22,16 @@
#include <QDateTime> #include <QDateTime>
#include <QPluginLoader> #include <QPluginLoader>
#include <QMap> #include <QMap>
#include <QStringList>
#include <QString>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QSettings>
#define UPDATE_OPKG (1) #define UPDATE_OPKG (1)
#define UPDATE_DC (0) #define UPDATE_DC (0)
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},
{"57600" , 4}, {"115200" , 5} {"57600" , 4}, {"115200" , 5}
@@ -32,25 +39,19 @@ static const QMap<QString, int> baudrateMap = {
QPluginLoader Update::pluginLoader; QPluginLoader Update::pluginLoader;
hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { hwinf *Update::loadDCPlugin(QString const &libCA /* absolute file path */) {
hwinf *hw = nullptr; hwinf *hw = nullptr;
if (plugInDir.exists()) { QFileInfo libCAInfo(libCA);
QString pluginLibName(fname); if (libCAInfo.exists()) {
pluginLibName = plugInDir.absoluteFilePath(pluginLibName); pluginLoader.setFileName(libCA);
QFileInfo info(pluginLibName);
if (info.exists()) {
pluginLibName = plugInDir.absoluteFilePath(pluginLibName);
pluginLoader.setFileName(pluginLibName);
// static QPluginLoader pluginLoader(pluginLibName); // static QPluginLoader pluginLoader(pluginLibName);
if (!pluginLoader.load()) { if (!pluginLoader.load()) {
qCritical() << "in directory" << plugInDir.absolutePath();
qCritical() << "cannot load plugin" << pluginLoader.fileName(); qCritical() << "cannot load plugin" << pluginLoader.fileName();
qCritical() << pluginLoader.errorString(); qCritical() << pluginLoader.errorString();
return nullptr; return nullptr;
} }
qCritical() << "loadDCPlugin() plugin directory:" << plugInDir.absolutePath(); // qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName();
qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName();
if (!pluginLoader.isLoaded()) { if (!pluginLoader.isLoaded()) {
qCritical() << pluginLoader.errorString(); qCritical() << pluginLoader.errorString();
@@ -66,14 +67,10 @@ hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) {
return nullptr; return nullptr;
} }
} else { } else {
qCritical() << pluginLibName << "does not exist"; qCritical() << libCAInfo.absoluteFilePath() << "does not exist";
return nullptr;
}
} else {
qCritical() << "plugins directory" << plugInDir.absolutePath()
<< "does not exist";
return nullptr; return nullptr;
} }
return hw; return hw;
} }
@@ -92,347 +89,389 @@ bool Update::unloadDCPlugin() {
return false; return false;
} }
QString Update::dcVersion(QString const &dcBinFile) {
QProcess p;
QStringList params;
params << "-c" << QString(R"(strings %1 | grep DC2c.\[0-9\] | uniq)").arg(dcBinFile);
p.start("bash", params);
p.waitForFinished();
return QString(p.readAllStandardOutput()).trimmed().split(QRegularExpression("\\s")).first();
}
class hwapi; class hwapi;
Update::Update(QString customerRepository, Update::Update(QString const &dcFileName, QString const &libCA, bool debug, bool noaction)
QString customerNrStr, : m_dcFileName(dcFileName)
QString branchName, , m_hw(loadDCPlugin(libCA))
QString plugInDir, , m_sys_areDCdataValid(false)
QString pluginName, , m_debug(debug)
QString workingDir, , m_noaction(noaction) {
bool dryRun,
QObject *parent,
char const *serialInterface,
char const *baudrate)
: QObject(parent)
, m_hw(loadDCPlugin(QDir(plugInDir), pluginName))
, m_serialInterface(serialInterface)
, m_baudrate(baudrate)
, m_customerRepository(customerRepository)
, m_customerNrStr(customerNrStr)
, m_branchName(branchName)
, m_pluginName(pluginName)
, m_workingDir(workingDir)
, m_dryRun(dryRun)
, m_sys_areDCdataValid(false) {
if (!m_hw) {
qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin loaded ???";
} else {
int tries = 20;
while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) {
// must deliver 'true', only then are all data from hwapi valid
if (--tries < 0) {
qCritical() << "ERROR!!! DC DATA NOT VALID -> CA-MASTER-PLUGIN NOT CONNECTED";
break;
}
m_hw->dc_autoRequest(true);
QThread::msleep(500);
}
qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_sys_areDCDataValid ..."
<< m_sys_areDCdataValid;
}
} }
Update::~Update() { Update::~Update() {
unloadDCPlugin(); unloadDCPlugin();
} }
bool Update::doUpdate(QStringList const &filesToWorkOn, bool usbStickDetected) { Update::DownloadResult Update::sendStatus(int ret) const {
switch (ret) { // return values of dc are:
case 0: // 0: no answer by now
return DownloadResult::NOP; // 1: error
case 10: // 10: success
return DownloadResult::OK;
default:;
}
return DownloadResult::ERROR;
}
if (!m_hw) { Update::DownloadResult
qCritical() << "(" << __func__ << ":" << __LINE__ << "):" Update::sendNextAddress(int bNum) const {
<< "ERROR!!! m_hw == nullptr"; // sends address only if blockNumber is one of 0, 1024, 2048, 3072, 4096
return false; int noAnswerCount = 0;
} int errorCount = 0;
if ( bNum==0 || bNum==1024 || bNum==2048 || bNum==3072 || bNum==4096 ) {
int tries = 20; // qDebug() << "addr-block" << bNum << "...";
while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { while (noAnswerCount <= 250) {
// must deliver 'true', only then are all data from hwapi valid
if (--tries < 0) { DownloadResult res = DownloadResult::OK;
qCritical() << "(" << __func__ << ":" << __LINE__ << "):" if (!m_debug) {
<< "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED"; m_hw->bl_sendAddress(bNum);
return false;
} QThread::msleep(10); //from 100ms to 20ms
qCritical() << "(" << __func__ << ":" << __LINE__ << "):" //###################################################################################
<< "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")"; res = sendStatus(m_hw->bl_wasSendingAddOK());
m_hw->dc_autoRequest(true);
QThread::msleep(500);
}
bool res = false;
QList<QString>::const_iterator it;
for (it = filesToWorkOn.cbegin(); it != filesToWorkOn.cend(); ++it) {
QString const &fToWorkOn = usbStickDetected ? QDir::cleanPath(it->trimmed())
: QDir::cleanPath(m_customerRepository + QDir::separator() + it->trimmed());
if (fToWorkOn.contains("DC2C_print", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
res = true;
int i = fToWorkOn.indexOf("DC2C_print", Qt::CaseInsensitive);
int const templateIdx = fToWorkOn.mid(i).midRef(10, 2).toInt();
if ((templateIdx < 1) || (templateIdx > 32)) {
qCritical() << "WRONG TEMPLATE INDEX" << templateIdx;
res = false;
} else {
if ((res = updatePrinterTemplate(templateIdx, fToWorkOn))) {
qCritical() <<
QString("DOWNLOADED PRINTER TEMPLATE %1 WITH INDEX=%2")
.arg(fToWorkOn)
.arg(templateIdx);
}
}
} else if (fToWorkOn.contains("DC2C_cash", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
if ((res = updateCashConf(fToWorkOn))) {
qCritical() << QString("DOWNLOADED CASH TEMPLATE %1").arg(fToWorkOn);
}
} else if (fToWorkOn.contains("DC2C_conf", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
if ((res = updateConfig(fToWorkOn))) {
qCritical() << QString("DOWNLOADED CONFIG TEMPLATE %1").arg(fToWorkOn);
}
} else if (fToWorkOn.contains("DC2C_device", Qt::CaseInsensitive)
&& fToWorkOn.endsWith(".json", Qt::CaseInsensitive)) {
if ((res = updateDeviceConf(fToWorkOn))) {
qCritical() << QString("DOWNLOADED DEVICE TEMPLATE %1").arg(fToWorkOn);
}
} else {
qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn;
res = false;
}
} }
if (res != DownloadResult::NOP) {
if (res == DownloadResult::ERROR) {
if (++errorCount >= 10) {
qCritical() << "addr-block" << bNum << "...FAILED";
return res; return res;
}
} else { // res == DownloadResult::OK
qInfo() << nextTimePoint().toUtf8().constData() << "addr-block" << bNum << "...done";
return res;
}
} else {
noAnswerCount += 1; // no answer by now
}
} // while
// wait max. about 3 seconds
return DownloadResult::TIMEOUT;
}
// blockNumber is not one of 0, 1024, 2048, 3072, 4096 -> do nothing
return DownloadResult::NOP;
} }
bool Update::checkJsonVersions(QStringList const& jsonFileNames) { Update::DownloadResult
if (!m_hw) { Update::sendNextDataBlock(QByteArray const &binary, int bNum) const {
qCritical() << "(" << __func__ << ":" << __LINE__ << "):" uint8_t local[66];
<< "ERROR!!! m_hw == nullptr"; int const bAddr = bNum * 64;
return false; int noAnswerCount = 0;
int errorCount = 0;
memcpy(local, binary.constData() + bAddr, 64);
local[64] = local[65] = 0x00;
QString s = nextTimePoint();
s += " sending block ";
s += QString("%1/%2 ...done <DC-PROGRESS>").arg(bNum).arg(m_totalBlocks);
s += QString::number(ceil(((bNum * 100.0) / (double)m_totalBlocks)));
qInfo() << s.toUtf8().constData();
QThread::msleep(20); //reduce from 200 to 50 ms
//############################################################################
QByteArray b((const char *)(&local[0]), 64);
qCritical() << "SNDB" << bNum << b.size() << b.toHex();
while (noAnswerCount <= 250) {
DownloadResult res = DownloadResult::OK;
if (!m_debug) {
m_hw->bl_sendDataBlock(64, local);
res = sendStatus(m_hw->bl_wasSendingDataOK());
} }
int tries = 20; if (res != DownloadResult::NOP) {
while ((m_sys_areDCdataValid = m_hw->sys_areDCdataValid()) == false) { if (res == DownloadResult::ERROR) {
// must deliver 'true', only then are all data from hwapi valid if (++errorCount >= 10) {
if (--tries < 0) { qCritical() << "data for block" << bNum << "...FAILED";
qCritical() << "(" << __func__ << ":" << __LINE__ << "):" return res;
<< "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED";
return false;
} }
qCritical() << "(" << __func__ << ":" << __LINE__ << "):"
<< "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")";
m_hw->dc_autoRequest(true);
QThread::msleep(500);
}
for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) {
uint8_t jsonNr = 0;
QString const &fName = jsonFileNames[i];
// send one request for every single version
// jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res.
// 6=printer template 1 ..... 36= template 32
if (fName.endsWith("conf.json")) {
jsonNr = 1;
} else
if (fName.endsWith("device.json")) {
jsonNr = 2;
} else
if (fName.endsWith("cash.json")) {
jsonNr = 3;
} else { } else {
QRegularExpressionMatch match; qInfo() << nextTimePoint().toUtf8().constData() << "data for block"
static const QRegularExpression re("^(.*print)([0-3][0-9])\\.json\\s*$"); << QString("%1/%2").arg(bNum).arg(m_totalBlocks) << "done";
int idx = fName.indexOf(re, 0, &match); return res;
if (idx != -1) {
QString captured = match.captured(match.lastCapturedIndex());
bool ok = false;
int n = captured.toInt(&ok);
if (ok) {
// note: use 5 (instead of 4 -> index has been shifted)
jsonNr = n + 5;
} }
}
}
if (jsonNr != 0) {
// send one request for every single version
// jsonNr=1...36, 1=config file (cust.Nr) 2=devices 3=cash 4=res.
// 5=printer template 1 ..... 36= template 32
m_hw->sys_requestJsonVersions(jsonNr);
QThread::msleep(500);
char buf[64];
memset(buf, 0x00, sizeof(buf));
m_hw->sys_getJsonVersions(jsonNr, buf);
buf[16] = '\0'; // the DC only handles 16 bytes
static const QByteArray cb(16, (char)0xff);
QString const installedVersion(QString::fromStdString(buf));
QString const fileVersion = getFileVersion(jsonFileNames[i]);
QFileInfo fi(jsonFileNames[i]);
qCritical() << endl;
qCritical() << " json request nr:" << jsonNr;
if (installedVersion == fileVersion) {
qCritical() << " json file:" << fi.fileName();
qCritical() << " installed version in DC:" << installedVersion;
} else
if (cb == QByteArray(buf) && fileVersion == "") {
qCritical() << "unknown json file (repo and DC):" << fi.fileName();
} else { } else {
qCritical() << " json file:" << fi.fileName(); noAnswerCount += 1; // no answer by now
qCritical() << " installed version in DC:" << installedVersion;
qCritical() << " file version in repository:" << fileVersion;
} }
}
// wait max. about 3 seconds
return DownloadResult::TIMEOUT;
}
bool Update::startBootloader() const {
qDebug() << "starting bootloader...";
if (!m_debug) {
int nTry = 10;
while (--nTry >= 0) {
m_hw->bl_startBL();
QThread::msleep(1000);
m_hw->bl_checkBL();
if (m_hw->bl_isUp()) {
qInfo() << "starting bootloader...OK";
QThread::msleep(5000);
return true;
} else {
qCritical() << "bootloader not up (" << nTry << ")";
qCritical() << "IS BOOTLOADER INSTALLED ???";
}
}
qCritical() << "starting bootloader...FAILED";
return false;
} else {
QThread::msleep(1000);
qInfo() << "starting bootloader...OK";
}
return true;
}
bool Update::stopBootloader() const {
qDebug() << "stopping bootloader...";
if (!m_debug) {
int nTry = 5;
while (--nTry >= 0) {
m_hw->bl_stopBL();
QThread::msleep(1000);
if (!m_hw->bl_isUp()) {
qInfo() << "stopping bootloader...OK";
return true;
}
}
qCritical() << "stopping bootloader...FAILED";
return false;
} else { } else {
qCritical() << "CANNOT FIND JSON-NR FOR" << fName; QThread::msleep(1000);
} qInfo() << "stopping bootloader...OK";
} }
return false; return true;
}
bool Update::resetDeviceController() const {
qInfo() << nextTimePoint().toUtf8().constData() << "resetting device controller";
if (!m_debug) {
m_hw->bl_rebootDC();
}
// wait maximally 3 seconds, before starting bootloader
QThread::sleep(1);
qInfo() << nextTimePoint().toUtf8().constData()
<< "resetting device controller ...done";
return true;
} }
QString Update::getFileVersion(QString const& jsonFileName) { QByteArray Update::loadBinaryDCFile(QString const &filename) const {
// "version":"15.10.2023 14:55 02.00.06",
static const QRegularExpression re("^.*(\\\"[Vv]ersion\\\":)([\\s\\\"]{0,})([^,\\\"]{0,}).*$");
QString fileVersion(""); QFile file(filename); // closed in destructor call
QFile inputFile(QDir::cleanPath(m_customerRepository + QDir::separator() + jsonFileName)); if (!file.exists()) {
qCritical() << "(" << __func__ << ":" << __LINE__ << ")"
if (inputFile.exists()) { << file.fileName() << "does not exist";
if (inputFile.open(QIODevice::ReadOnly)) { return QByteArray{};
QTextStream in(&inputFile);
while (!in.atEnd()) {
QString line = in.readLine();
QRegularExpressionMatch match;
int idx = line.indexOf(re, 0, &match);
if (idx != -1) {
int const lastCaptured = match.lastCapturedIndex();
// the dc only sends 16 Byte
fileVersion = match.captured(lastCaptured);
fileVersion.truncate(16);
break;
} }
} if (!file.open(QIODevice::ReadOnly)) {
inputFile.close(); qCritical() << "(" << __func__ << ":" << __LINE__ << ")"
} << "cannot open file" << file.fileName();
} else { return QByteArray{};
// qCritical() << "ERROR" << inputFile.fileName() << "does not exist";
} }
return fileVersion; qInfo() << nextTimePoint().toUtf8().constData()
<< "loading dc binary to memory" << Update::dcVersion(filename) << "...done";
return file.readAll();
} }
/*
///////////////////////////////////////////////////////////////////////////////
//
// USING THE DC BOOTLOADER
//
///////////////////////////////////////////////////////////////////////////////
bool Update::downloadJson(enum FileTypeJson type, 1 : bl_reboot() // send to application, want DC2 to reset (in order to
int templateIdx, // start the bootloader)
QString jsFileToSendToDC) const { //
// NOTE: this function is NOT reliable !!! Sometimes it
// simply does not work, in which case bl_startBL,
// bl_checkBL and bl_isUp do not work as well.
// Alas, there is no feedback if bl_reboot worked!
//
// NOTE: this function can be called only once per
// minute, because once called again, the controller
// performs some self-checks consuming some time.
//
// NOTE: after a successful bl_reboot(), the device is
// waiting about 4 seconds in the bootloader. To stay in
// the bootloader, we have to send the command
// bl_startBL(), which is kind of a misnomer, as it
// should be bl_doNotLeaveBL().
//
2 : bl_startBL(): // send within 4s after DC power-on, otherwise
// bootloader is left.
//
// NOTE: a running bootloader is a MUST for the download
// process of a device controller firmware as it does
// the actual writing of the memory (the bl_reboot()
// from above erases the available memory).
//
3 : bl_check(): // send command to verify if bl is up
//
// NOTE: this command is kind of a request that we want
// to check if the bootloader is up. The device
// (actually the bootloader) responds with its version.
//
4 : bl_isUp(): // returns true if bl is up and running
//
// NOTE: we know what the bootloader version actually is
// as the bootloader does not change. By comparing the
// string received in the previous step with this known
// version string we know if the bootloader is up.
//
// NOTE FOR ALL PREVIOUS STEPS: execute them in their
// own slots each to be sure to receive any possible
// responds from the device.
//
5 : bl_sendAddress(blockNumber)
// send start address, nr of 64-byte block, start with 0
// will be sent only for following block-numbers:
// 0, 1024, 2048, 3072 and 4096, so basically every
// 64kByte.
// for other addresses nothing happens
m_hw->dc_autoRequest(true); // downloading Json needs the AutoEmission flag 6 : bl_wasSendingAddOK()
qDebug() << "SET AUTO-REQUEST=TRUE"; // return val: 0: no response by now
QThread::sleep(1); // make sure the auto-request flag is acknowledged // 1: error
// 10: OK
QStringList lst; 7 : bl_sendDataBlock()
bool ready = false; // send 64 byte from bin file
int nTry = 25;
while ((ready = m_hw->sys_ready4sending()) == false) {
QThread::msleep(200);
if (--nTry <= 0) {
qCritical() << "SYS NOT READY FOR SENDING AFTER 5 SECONDS";
break;
}
}
bool ret = false; 8 : bl_sendLastBlock()
QString msg; // send this command after all data are transferred
lst.clear();
if (ready) {
QFile file(jsFileToSendToDC);
QFileInfo fi(jsFileToSendToDC); // max. size of template file is 800 bytes
if (file.exists()) {
if (file.open(QIODevice::ReadOnly)) {
if (fi.size() > 0 && fi.size() <= 800) {
QByteArray ba = file.readAll();
// kindOfFile: 1=config, 2=device, 3=cash, 4=serial, 5=time, 6=printer
// nrOfTemplate=1...32 if kindOfFile==6
// content = content of the Json file, max 800byte ascii signs
if (m_hw->sys_sendJsonFileToDc((uint8_t)(type),
templateIdx,
(uint8_t *)ba.data())) {
/* 9 : bl_wasSendingDataOK()
* Note: the machine id is contained in DC2C_conf.json. // return val: 0: no response by now
* The idea was to use this to check if the download of // 1: error
* the json-file was correct. It did not work, as the // 10: OK
* update of the PSA (to reflect a change in the
* machine id) did not happen immediately.
*
m_hw->dc_autoRequest(true);
QThread::msleep(500);
// testing 10 : bl_stopBL() // leave bl and start (the new) application
m_hw->request_ReadbackMachineID(); //
QThread::msleep(500); // NOTE: this function MUST work under all conditions.
// Alas, there is no direct result for this command, so
uint8_t data[64]; // the only way of knowing it was successful is to ask
memset(data, 0x00, sizeof(data)); // the device if the bootloader is still running.
uint8_t length = 0; // There is no problem to repeat this command until the
// bootloader is really not running anymore.
m_hw->readback_machineIDdata(&length, data);
QThread::msleep(500);
QByteArray ba((const char*)data, length);
qCritical() << length << "MACHINE ID =" << ba.toHex(':');
*/ */
int Update::run() {
ret = true; if (!m_hw) {
} else { qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin loaded ???";
qCritical() << QString("ERROR SEND JSON-FILE %1 TO DC").arg(file.fileName()); return -(int)Result::PLUGIN_LOAD_ERROR;
}
} else {
qCritical() << QString("SIZE OF %1 TOO BIG (%2 BYTES)").arg(jsFileToSendToDC).arg(fi.size());
}
} else {
qCritical() << QString("CAN NOT OPEN ") + jsFileToSendToDC + " FOR READING";
}
} else {
qCritical() << (QString(jsFileToSendToDC) + " DOES NOT EXIST");
}
} }
m_start = QDateTime::currentDateTime();
if (m_debug) {
qInfo() << "start dc-update for" << m_dcFileName << "at" << m_start.toString(Qt::ISODate);
qInfo() << "<DC-VERSION>" << Update::dcVersion(m_dcFileName);
}
if (!m_debug) {
m_hw->dc_autoRequest(false); m_hw->dc_autoRequest(false);
qDebug() << "SET AUTO-REQUEST=FALSE"; }
QThread::sleep(1); // make sure the auto-request flag is acknowledged
return ret; qInfo() << "DC auto request OFF";
}
bool Update::updatePrinterTemplate(int templateIdx, QString jsFile) const { qCritical() << "start dc-update for" << m_dcFileName << "at" << m_start.toString(Qt::ISODate);
return downloadJson(FileTypeJson::PRINTER, templateIdx, jsFile);
}
bool Update::updateConfig(QString jsFile) { QByteArray ba = loadBinaryDCFile(m_dcFileName);
return downloadJson(FileTypeJson::CONFIG, 0, jsFile); if (ba.size() > 0) {
} m_totalBlocks = (((ba.size())%64)==0) ? (ba.size()/64) : (ba.size()/64)+1;
bool Update::updateCashConf(QString jsFile) { qInfo() << nextTimePoint().toUtf8().constData() << "blocks to send" << m_totalBlocks;
return downloadJson(FileTypeJson::CASH, 0, jsFile);
}
bool Update::updateDeviceConf(QString jsFile) { // fill last block of data to be sent with 0xFF
return downloadJson(FileTypeJson::DEVICE, 0, jsFile); ba = ba.leftJustified(m_totalBlocks*64, (char)(0xFF));
resetDeviceController();
if (startBootloader()) {
int currentBlock = 0;
DownloadResult res = DownloadResult::OK;
qInfo() << nextTimePoint().toUtf8().constData() << "64-byte block" << currentBlock;
while (res != DownloadResult::ERROR && currentBlock <= m_totalBlocks) {
if ((res = sendNextAddress(currentBlock)) != DownloadResult::ERROR) {
if ((res = sendNextDataBlock(ba, currentBlock)) != DownloadResult::ERROR) {
currentBlock += 1;
} else break;
}
}
#if 0
qCritical() << "DownloadThread::run(): last 64-byte block %04d" << currentBlock;
int const rest = ba.size() % 64;
int const offset = ba.size() - rest;
char const *startAddress = ba.constData() + offset;
if (rest > 0) {
// SHOULD NEVER HAPPEN !!!
uint8_t local[66];
memset(local, 0xFF, sizeof(local));
memcpy(local, startAddress, rest);
qCritical() << "DownloadThread::run(): ERROR SEND REMAINING" << rest << "BYTES";
m_hw->bl_sendDataBlock(64, local);
} else {
m_hw->bl_sendLastBlock();
m_hw->dcDownloadSetCurrentBlockNumber(currentBlock);
}
qCritical() << "DownloadThread::run(): last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK());
#endif
}
stopBootloader(); // there is no harm in stopping the bootloader even
// if starting the bootloader failed
// check if update was successful
if (!m_debug) {
m_hw->dc_autoRequest(true); //restart dc_autoRequest after download else E255!
}
for (int i = 0; i < 3; ++i) {
qInfo() << "waiting for device controller restart...(" << i << ")";
QThread::sleep(20);
resetDeviceController();
if (startBootloader()) {
qInfo() << nextTimePoint().toUtf8().constData() << "<DC-UPDATE-SUCCESS>";
stopBootloader();
return -(int)Result::SUCCESS;
}
}
}
qInfo() << nextTimePoint().toUtf8().constData() << "<DC-UPDATE-FAILURE>";
//To Do Error handling if Dc doesnt start after download
return false;
} }

View File

@@ -8,6 +8,8 @@
#include <QByteArray> #include <QByteArray>
#include <QProcess> #include <QProcess>
#include <QPluginLoader> #include <QPluginLoader>
#include <QDateTime>
#include <cmath>
#include <initializer_list> #include <initializer_list>
@@ -15,115 +17,57 @@
#ifdef PTU5 #ifdef PTU5
#define SERIAL_PORT "ttymxc2" #define SERIAL_PORT "ttymxc2"
#define BAUDRATE 115200
#else #else
#define SERIAL_PORT "ttyUSB0" #define SERIAL_PORT "ttyUSB0"
#define BAUDRATE 115200
#endif #endif
class QSerialPort;
class Update : public QObject { class Update : public QObject {
Q_OBJECT Q_OBJECT
QString m_dcFileName{};
hwinf *m_hw = nullptr; hwinf *m_hw = nullptr;
char const *m_serialInterface; bool m_sys_areDCdataValid{};
char const *m_baudrate; bool m_debug{false};
QString m_customerRepository; bool m_noaction;
QString m_customerNrStr;
QString m_branchName;
QString m_pluginName;
QString m_workingDir;
bool m_maintenanceMode;
bool m_dryRun;
bool m_sys_areDCdataValid;
static QPluginLoader pluginLoader; static QPluginLoader pluginLoader;
QDateTime m_start;
QString nextTimePoint() const {
float const secs = m_start.msecsTo(QDateTime::currentDateTime()) / 1000.0;
return QStringLiteral("+%1s").arg(secs, 7, 'f', 2, QChar('0'));
}
public: public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP}; enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6}; enum class Result {SUCCESS=0, PLUGIN_LOAD_ERROR};
static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn); static hwinf *loadDCPlugin(QString const &libCA = "/usr/lib/libCAslave.so");
static bool unloadDCPlugin(); static bool unloadDCPlugin();
static QStringList split(QString line, QChar sep = ','); static QStringList split(QString line, QChar sep = ',');
explicit Update(QString const &dcBinFile, QString const &libCA, bool debug, bool noaction);
explicit Update(QString customerRepository,
QString customerNrStr,
QString branchName,
QString plugInDir,
QString pluginName,
QString workingDir,
bool dryRun = false,
QObject *parent = nullptr,
char const *serialInterface = SERIAL_PORT,
char const *baudrate = "115200");
virtual ~Update() override; virtual ~Update() override;
bool doUpdate(QStringList const &jsonFilesToDownload, bool usbStickDetected = false); int run();
static QString dcVersion(QString const &dcBinFile);
bool updatePrinterTemplate(int templateIdx, QString fname) const; private:
bool updateConfig(QString jsFileToSendToDC); DownloadResult sendStatus(int ret) const;
bool updateCashConf(QString jsFileToSendToDC); DownloadResult sendNextAddress(int bNum) const;
bool updateDeviceConf(QString jsFileToSendToDC); DownloadResult sendNextDataBlock(QByteArray const &binary, int bNum) const;
bool startBootloader() const;
bool stopBootloader() const;
QByteArray loadBinaryDCFile(QString const &dcFilename) const;
bool resetDeviceController() const;
DownloadResult dcDownloadBinary(QByteArray const &b) const;
bool downloadJson(enum FileTypeJson type, int templateIdx, QString m_fileToDownload;
QString jsFileToSendToDC) const; uint16_t m_totalBlocks = 0;
QString getFileVersion(QString const& jsonFileName);
bool checkJsonVersions(QStringList const& jsonFileNames =
QStringList(
QList(
std::initializer_list<QString>{
QString("etc/psa_config/DC2C_conf.json"),
QString("etc/psa_config/DC2C_cash.json"),
QString("etc/psa_config/DC2C_device.json"),
QString("etc/psa_config/DC2C_print01.json"),
QString("etc/psa_config/DC2C_print02.json"),
QString("etc/psa_config/DC2C_print03.json"),
QString("etc/psa_config/DC2C_print04.json"),
QString("etc/psa_config/DC2C_print05.json"),
QString("etc/psa_config/DC2C_print06.json"),
QString("etc/psa_config/DC2C_print07.json"),
QString("etc/psa_config/DC2C_print08.json"),
QString("etc/psa_config/DC2C_print09.json"),
QString("etc/psa_config/DC2C_print10.json"),
QString("etc/psa_config/DC2C_print11.json"),
QString("etc/psa_config/DC2C_print12.json"),
QString("etc/psa_config/DC2C_print13.json"),
QString("etc/psa_config/DC2C_print14.json"),
QString("etc/psa_config/DC2C_print15.json"),
QString("etc/psa_config/DC2C_print16.json"),
QString("etc/psa_config/DC2C_print17.json"),
QString("etc/psa_config/DC2C_print18.json"),
QString("etc/psa_config/DC2C_print19.json"),
QString("etc/psa_config/DC2C_print20.json"),
QString("etc/psa_config/DC2C_print21.json"),
QString("etc/psa_config/DC2C_print22.json"),
QString("etc/psa_config/DC2C_print23.json"),
QString("etc/psa_config/DC2C_print24.json"),
QString("etc/psa_config/DC2C_print25.json"),
QString("etc/psa_config/DC2C_print26.json"),
QString("etc/psa_config/DC2C_print27.json"),
QString("etc/psa_config/DC2C_print28.json"),
QString("etc/psa_config/DC2C_print29.json"),
QString("etc/psa_config/DC2C_print30.json"),
QString("etc/psa_config/DC2C_print31.json"),
QString("etc/psa_config/DC2C_print32.json")})));
/* /*
bool checkDownloadedJsonVersions(QStringList const& jsonFileNames);
hwinf *hw() { return m_hw; }
hwinf const *hw() const { return m_hw; }
//QString customerId() { return m_customerId; }
//QString const customerId() const { return m_customerId; }
QString branchName() { return m_branchName; }
QString const branchName() const { return m_branchName; }
//QString repositoryPath() { return m_repositoryPath; }
//QString const repositoryPath() const { return m_repositoryPath; }
private: private:
static QString jsonType(enum FileTypeJson type); static QString jsonType(enum FileTypeJson type);
bool openSerial(int br, QString baudrate, QString comPort) const; bool openSerial(int br, QString baudrate, QString comPort) const;

View File

@@ -1,6 +1,8 @@
QT += core QT += core
QT += serialport network QT += serialport network
include(../common.pri)
TARGET = ATBDownloadDCJsonFiles TARGET = ATBDownloadDCJsonFiles
VERSION="0.1.0" VERSION="0.1.0"

View File

@@ -414,10 +414,6 @@ bool Update::downloadJson(enum FileTypeJson type,
} }
} }
m_hw->dc_autoRequest(false);
qDebug() << "SET AUTO-REQUEST=FALSE";
QThread::sleep(1); // make sure the auto-request flag is acknowledged
return ret; return ret;
} }

View File

@@ -1,6 +1,8 @@
QT += core gui QT += core gui
QT += widgets serialport network QT += widgets serialport network
include(../common.pri)
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = ATBUpdateTool TARGET = ATBUpdateTool
@@ -132,7 +134,28 @@ DEFINES += QT_DEPRECATED_WARNINGS
# update. # update.
# In case there is an automatic update (00:00-04:00) use M0100 instead # In case there is an automatic update (00:00-04:00) use M0100 instead
# of U0002. # of U0002.
VERSION="1.4.9" # 1.4.10 : Fix: for the time being, never set the autoRequest-flag to false.
# 1.4.11 : Fix: remove hard-coded path to git-repos of gitea in mimbach. Read
# from ATBUpdateTool.ini the url for the gitrepositories.
# 1.5.0 : Use ptu-config.atb-comm.de (given in ini-file) as new server for
# customer repositories.
# Fix: allow empty lines in opkg_commands.
# 1.5.1 : Fix: do not use cleanPath() on a url-address.
# 1.5.2 : Remove .ipk and .gz files in /var/cache/opkg.
# 1.5.3 : Build customer_(id) name without right justification.
# 1.5.4 : Try to run opkg-commands even under some error conditions (failure
# of customerEnvironment(), filesToUpdate() or
# syncCustomerRepositoryAndFS().
# 1.5.5 : Call into binary ptuPackageVersion to get installed package
# versions.
# 1.5.6 : Show additional update progress info in status bar.
# 1.5.7 : Add support for dynamic portrait / landscape.
# 1.5.8 : Use EVENT_ID=<pid of update-tool> for CMD_EVENT.
# 1.5.9 : Removed restart of Apism if ISMAS unreachable.
# 1.5.99 : Special version for 281/Szeged: remove all DC-library dependencies, as
# DC-firmware in Szeged does not support uploading json-files or uploading
# DC-firmware binaries.
VERSION="1.5.99"
# PLANNED TODOS: # PLANNED TODOS:
# 1: Das Repository wird repariert bwz. neu geklont. Unabhaengig vom WAIT. # 1: Das Repository wird repariert bwz. neu geklont. Unabhaengig vom WAIT.
# 2: Wenn der WAIT-Button aktiv ist, dann wird ein Repository repariert (neu # 2: Wenn der WAIT-Button aktiv ist, dann wird ein Repository repariert (neu
@@ -238,9 +261,6 @@ contains( CONFIG, PTU5_YOCTO ) {
PTU5BASEPATH = /opt/devel/ptu5 PTU5BASEPATH = /opt/devel/ptu5
ARCH = PTU5 ARCH = PTU5
DEFINES+=PTU5 DEFINES+=PTU5
LIBS += -lCAslave
LIBS += -lCAmaster
} }
contains( CONFIG, DesktopLinux ) { contains( CONFIG, DesktopLinux ) {
greaterThan(QT_MAJOR_VERSION, 4): QT += serialport greaterThan(QT_MAJOR_VERSION, 4): QT += serialport

View File

@@ -85,7 +85,17 @@ CommandLineParser::CommandLineParser()
, m_yoctoInstallStatusOption( , m_yoctoInstallStatusOption(
QCommandLineOption( QCommandLineOption(
QStringList() << "Y" << "yocto-install", QStringList() << "Y" << "yocto-install",
QCoreApplication::translate("main", "Show yocto install status of ATBUpdateTool."))) { QCoreApplication::translate("main", "Show yocto install status of ATBUpdateTool.")))
, m_dcDirectoryOption(
QCommandLineOption(
QStringList() << "dc-directory" << "dc-directory",
QCoreApplication::translate("main", "device controller directory."),
QCoreApplication::translate("main", "directory")))
, m_readDCVersionOption(
QCommandLineOption(
QStringList() << "D" << "read-dc-version",
QCoreApplication::translate("main", "Show version of device controller."),
QCoreApplication::translate("main", "Show version of device controller."))) {
configure(); configure();
} }
@@ -138,6 +148,12 @@ void CommandLineParser::configure() {
m_yoctoInstallStatusOption.setDefaultValue("false"); m_yoctoInstallStatusOption.setDefaultValue("false");
m_parser.addOption(m_yoctoInstallStatusOption); m_parser.addOption(m_yoctoInstallStatusOption);
m_dcDirectoryOption.setDefaultValue("etc/dc/");
m_parser.addOption(m_dcDirectoryOption);
m_readDCVersionOption.setDefaultValue("false");
m_parser.addOption(m_readDCVersionOption);
} }
void CommandLineParser::readSettings() { void CommandLineParser::readSettings() {
@@ -145,8 +161,8 @@ void CommandLineParser::readSettings() {
QString const iniFileName = m_parser.value(m_iniFileNameOption); QString const iniFileName = m_parser.value(m_iniFileNameOption);
m_iniFileName = QDir::cleanPath(iniFileDir + QDir::separator() + iniFileName); m_iniFileName = QDir::cleanPath(iniFileDir + QDir::separator() + iniFileName);
qCritical() << __PRETTY_FUNCTION__ << " iniFileDir" << iniFileDir; //qCritical() << __PRETTY_FUNCTION__ << " iniFileDir" << iniFileDir;
qCritical() << __PRETTY_FUNCTION__ << "iniFileName" << m_iniFileName; //qCritical() << __PRETTY_FUNCTION__ << "iniFileName" << m_iniFileName;
if (!m_iniFileName.isEmpty()) { if (!m_iniFileName.isEmpty()) {
if (QFile(m_iniFileName).exists()) { if (QFile(m_iniFileName).exists()) {
@@ -155,8 +171,8 @@ void CommandLineParser::readSettings() {
for (QString const &key: keys) { for (QString const &key: keys) {
QVariant v = settings.value(key); QVariant v = settings.value(key);
qCritical() << __PRETTY_FUNCTION__ //qCritical() << "(" << __func__ << ":" << __LINE__ << ")"
<< key << " -> " << v.toString(); // << key << " -> " << v.toString();
if (key.contains("repository-url")) { if (key.contains("repository-url")) {
m_repositoryUrl = v.toString(); m_repositoryUrl = v.toString();
@@ -196,6 +212,12 @@ void CommandLineParser::readSettings() {
} else } else
if (key.contains("plugin-name")) { if (key.contains("plugin-name")) {
m_plugInName = v.toString(); m_plugInName = v.toString();
} else
if (key.contains("dc-directory")) {
m_dcDir = v.toString();
} else
if (key.contains("read-dc-version")) {
m_readDCVersion = (v.toBool() ? "true" : "false");
} else { } else {
qCritical() << __PRETTY_FUNCTION__ qCritical() << __PRETTY_FUNCTION__
<< key << " -> (UNKNOWN) " << v.toString(); << key << " -> (UNKNOWN) " << v.toString();
@@ -243,6 +265,20 @@ QString CommandLineParser::psaTariffDir() {
return m_psaTariffDir; return m_psaTariffDir;
} }
QString CommandLineParser::dcDir() {
if (m_parser.isSet(m_dcDirectoryOption)) {
m_dcDir = m_parser.value(m_dcDirectoryOption);
}
return m_dcDir;
}
bool CommandLineParser::readDCVersion() {
if (m_parser.isSet(m_readDCVersionOption)) {
m_readDCVersion = m_parser.value(m_readDCVersionOption);
}
return m_readDCVersion == "false" ? false : true;
}
QString CommandLineParser::workingDir() { QString CommandLineParser::workingDir() {
if (m_parser.isSet(m_workingDirectoryOption)) { if (m_parser.isSet(m_workingDirectoryOption)) {
m_workingDir = m_parser.value(m_workingDirectoryOption); m_workingDir = m_parser.value(m_workingDirectoryOption);
@@ -288,9 +324,9 @@ bool CommandLineParser::extendedVersion() {
bool CommandLineParser::alwaysDownloadConfig() { bool CommandLineParser::alwaysDownloadConfig() {
if (m_parser.isSet(m_alwaysDownloadConfigOption)) { if (m_parser.isSet(m_alwaysDownloadConfigOption)) {
m_alwaysDownloadConfig = m_parser.value(m_alwaysDownloadConfigOption); m_alwaysDownloadConfig = m_parser.value(m_alwaysDownloadConfigOption);
qCritical() << "m_alwaysDownloadConfigOption IS SET" << m_alwaysDownloadConfig; // qCritical() << "m_alwaysDownloadConfigOption IS SET" << m_alwaysDownloadConfig;
} }
qCritical() << "m_alwaysDownloadConfig" << m_alwaysDownloadConfig; // qCritical() << "m_alwaysDownloadConfig" << m_alwaysDownloadConfig;
return m_alwaysDownloadConfig == "false" ? false : true; return m_alwaysDownloadConfig == "false" ? false : true;
} }

View File

@@ -1,4 +1,4 @@
#ifndef COMMAND_LINE_PARSER_H_INCLUDED #ifndef COMMAND_LINE_PARSER_H_INCLUDED
#define COMMAND_LINE_PARSER_H_INCLUDED #define COMMAND_LINE_PARSER_H_INCLUDED
#include <QCoreApplication> #include <QCoreApplication>
@@ -11,8 +11,8 @@ class CommandLineParser : public QCommandLineParser {
QString m_plugInDir; QString m_plugInDir;
QString m_plugInName; QString m_plugInName;
QString m_workingDir; QString m_workingDir;
QString m_psaConfigDir; QString m_psaConfigDir{"etc/psa_config"};
QString m_psaTariffDir; QString m_psaTariffDir{"etc/psa_tariff"};
QString m_dryRun; QString m_dryRun;
QString m_noUpdatePsaHardware; QString m_noUpdatePsaHardware;
QString m_showYoctoVersion; QString m_showYoctoVersion;
@@ -21,6 +21,8 @@ class CommandLineParser : public QCommandLineParser {
QString m_iniFileName; QString m_iniFileName;
QString m_alwaysDownloadConfig; QString m_alwaysDownloadConfig;
QString m_alwaysDownloadDC; QString m_alwaysDownloadDC;
QString m_readDCVersion{"false"};
QString m_dcDir{"etc/dc/"};
QCommandLineOption m_repositoryUrlOption; QCommandLineOption m_repositoryUrlOption;
QCommandLineOption m_iniFileDirectoryOption; QCommandLineOption m_iniFileDirectoryOption;
@@ -37,6 +39,8 @@ class CommandLineParser : public QCommandLineParser {
QCommandLineOption m_extendedVersionOption; QCommandLineOption m_extendedVersionOption;
QCommandLineOption m_yoctoVersionOption; QCommandLineOption m_yoctoVersionOption;
QCommandLineOption m_yoctoInstallStatusOption; QCommandLineOption m_yoctoInstallStatusOption;
QCommandLineOption m_dcDirectoryOption;
QCommandLineOption m_readDCVersionOption;
QCommandLineParser m_parser; QCommandLineParser m_parser;
@@ -65,5 +69,7 @@ public:
bool extendedVersion(); bool extendedVersion();
bool alwaysDownloadConfig(); bool alwaysDownloadConfig();
bool alwaysDownloadDC(); bool alwaysDownloadDC();
bool readDCVersion();
QString dcDir();
}; };
#endif // COMMAND_LINE_PARSER_H_INCLUDED #endif // COMMAND_LINE_PARSER_H_INCLUDED

View File

@@ -2,6 +2,7 @@
#include "update.h" #include "update.h"
#include "worker.h" #include "worker.h"
#include "utils.h" #include "utils.h"
#include "process/command.h"
#include <QRegularExpression> #include <QRegularExpression>
#include <QDebug> #include <QDebug>
@@ -9,14 +10,15 @@
#include <QStringList> #include <QStringList>
GitClient::GitClient(QString const &customerNrStr, GitClient::GitClient(QString const &customerRepositoryPath,
QString const &customerNrStr,
QString const &customerRepository, QString const &customerRepository,
QString const &workingDirectory, QString const &workingDirectory,
QString const &branchName, QString const &branchName,
QObject *parent) QObject *parent)
: QObject(parent) : QObject(parent)
, m_worker(qobject_cast<Worker *>(parent)) , m_worker(qobject_cast<Worker *>(parent))
, m_repositoryPath(QString("https://git.mimbach49.de/GerhardHoffmann/%1.git").arg(customerNrStr)) , m_repositoryPath(customerRepositoryPath)
, m_customerNr(customerNrStr) , m_customerNr(customerNrStr)
, m_workingDirectory(workingDirectory) , m_workingDirectory(workingDirectory)
, m_branchName(branchName) , m_branchName(branchName)
@@ -392,6 +394,45 @@ std::optional<QString> GitClient::gitPull() {
if (QDir(m_customerRepository).exists()) { if (QDir(m_customerRepository).exists()) {
qInfo() << "BRANCH NAME" << m_branchName; qInfo() << "BRANCH NAME" << m_branchName;
#if 0
IMPORTANT COMMENT:
If remote host keys are changed, then
export GIT_SSH_COMMAND="ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig"
git pull
leads to the following warning/error message:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:vOD5jF2hglGktqLhK9ABxfEjwEgIK68/v9erdT05NDQ.
Please contact your system administrator.
Add correct host key in /home/root/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/root/.ssh/known_hosts:1
Password authentication is disabled to avoid man-in-the-middle attacks.
Keyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.
Agent forwarding is disabled to avoid man-in-the-middle attacks.
X11 forwarding is disabled to avoid man-in-the-middle attacks.
Already up to date.
This first part is from ssh itself. Only the last line is the git message.
Here an output of running ATBUpdateTool with a corrupted known-hosts-file:
...
Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: ( branchExistsRemotely : 310 ) branch "zg1/zone1" EXISTS REMOTELY. ( "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @\r\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\nIT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\r\nSomeone could be eavesdropping on you right now (man-in-the-middle attack)!\r\nIt is also possible that a host key has just been changed.\r\nThe fingerprint for the ECDSA key sent by the remote host is\nSHA256:vOD5jF2hglGktqLhK9ABxfEjwEgIK68/v9erdT05NDQ.\r\nPlease contact your system administrator.\r\nAdd correct host key in /home/root/.ssh/known_hosts to get rid of this message.\r\nOffending ECDSA key in /home/root/.ssh/known_hosts:1\r\nPassword authentication is disabled to avoid man-in-the-middle attacks.\r\nKeyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.\r\nAgent forwarding is disabled to avoid man-in-the-middle attacks.\r\nX11 forwarding is disabled to avoid man-in-the-middle attacks.\r\nbd4e8da4780b1a7d6be3d3ce8419f43ccf7e706f\trefs/heads/zg1/zone1" )
Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: EXECUTED "git branch -l" "(runtime 16ms)" with code 0 IN "/opt/app/tools/atbupdate/customer_336"
Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: "UPDATE_STEP::PULL_NEW_BRANCH"
Oct 23 14:18:18 ATB_PTU5 ATBUpdateTool[2696]: "BRANCH-NAME zg1/zone1 CONTAINED IN RESULT master\n* zg1/zone1" .....
The download continues.
#endif
Command c("git pull"); Command c("git pull");
if (c.execute(m_customerRepository)) { if (c.execute(m_customerRepository)) {
QString const s = c.getCommandResult().trimmed(); QString const s = c.getCommandResult().trimmed();

View File

@@ -4,8 +4,8 @@
#include <QObject> #include <QObject>
#include <QStringList> #include <QStringList>
#include <optional> #include <optional>
#include <QCoreApplication>
#include "process/command.h"
#include "ismas/ismas_client.h" #include "ismas/ismas_client.h"
class Worker; class Worker;
@@ -22,7 +22,8 @@ class GitClient : public QObject {
bool copyGitConfigFromMaster(); bool copyGitConfigFromMaster();
public: public:
explicit GitClient(QString const &customerNrStr, explicit GitClient(QString const &customerRepositoryUrl,
QString const &customerNrStr,
QString const &repositoryPath, QString const &repositoryPath,
QString const &workingDirectory = QCoreApplication::applicationDirPath(), QString const &workingDirectory = QCoreApplication::applicationDirPath(),
QString const &branchName = "master", QString const &branchName = "master",

View File

@@ -21,6 +21,7 @@
#include <QThread> #include <QThread>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QCoreApplication>
#if 0 #if 0
######################## ########################
@@ -380,7 +381,7 @@ QString IsmasClient::updateNewsToIsmas(char const *event,
"{" "{"
"\"REASON\":\"SW_UP\"," "\"REASON\":\"SW_UP\","
"\"TIMESTAMP\":\"%s\"," "\"TIMESTAMP\":\"%s\","
"\"EVENT_ID\":\"0\"," "\"EVENT_ID\":\"%d\","
"\"EVENT\":\"%s\"," "\"EVENT\":\"%s\","
"\"EVENTSTATE\":1," "\"EVENTSTATE\":1,"
"\"PARAMETER\": {" "\"PARAMETER\": {"
@@ -390,7 +391,7 @@ QString IsmasClient::updateNewsToIsmas(char const *event,
"\"STEP_RESULT\" : \"%s\"," "\"STEP_RESULT\" : \"%s\","
"\"VERSION\" : \"%s\"" "\"VERSION\" : \"%s\""
"}" "}"
"}", ts.toStdString().c_str(), event, percent, resultCode, "}", ts.toStdString().c_str(), static_cast<int>(QCoreApplication::applicationPid()), event, percent, resultCode,
step, step_result, version); step, step_result, version);
return buf; return buf;
} }
@@ -690,7 +691,8 @@ QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) {
"\"libTCP_ZVT_CCPlugin.so\" : {" "\"libTCP_ZVT_CCPlugin.so\" : {"
"\"VERSION\" : \"%s\"" "\"VERSION\" : \"%s\""
"}" "}"
"}" "},"
"\"PTU-PACKAGE-VERSIONS\" : %s"
"}", "}",
psa.versionInfo.reason.toStdString().c_str(), psa.versionInfo.reason.toStdString().c_str(),
psa.versionInfo.created.toStdString().c_str(), psa.versionInfo.created.toStdString().c_str(),
@@ -801,7 +803,9 @@ QString IsmasClient::updateOfPSASendVersion(PSAInstalled const &psa) {
psa.pluginVersion.mobilisisCalculatePriceConfigUi.toStdString().c_str(), psa.pluginVersion.mobilisisCalculatePriceConfigUi.toStdString().c_str(),
psa.pluginVersion.prmCalculatePrice.toStdString().c_str(), psa.pluginVersion.prmCalculatePrice.toStdString().c_str(),
psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(), psa.pluginVersion.prmCalculatePriceConfigUi.toStdString().c_str(),
psa.pluginVersion.tcpZVT.toStdString().c_str()); psa.pluginVersion.tcpZVT.toStdString().c_str(),
psa.ptuPackageVersion.toStdString().c_str());
qInfo() << buf; qInfo() << buf;

View File

@@ -77,6 +77,8 @@ struct PSAInstalled {
DC2C print[32]; DC2C print[32];
QString ptuPackageVersion;
explicit PSAInstalled() { explicit PSAInstalled() {
tariff.name = "N/A"; tariff.name = "N/A";
tariff.version = "N/A"; tariff.version = "N/A";

View File

@@ -31,6 +31,7 @@
#include "worker.h" #include "worker.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "utils.h" #include "utils.h"
// #include "process/command.h"
#include <QThread> #include <QThread>
#include <QtWidgets> #include <QtWidgets>
@@ -50,9 +51,11 @@
// argv[1]: file to send to dc // argv[1]: file to send to dc
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
QByteArray const value = qgetenv("LC_ALL"); QByteArray const value = qgetenv("LC_ALL");
if (value != "C") { if (value.isEmpty() || value != "C") {
qputenv("LC_ALL", "C"); qputenv("LC_ALL", "C");
} }
// qputenv("XDG_RUNTIME_DIR", "/var/run/user/0"); // qputenv("XDG_RUNTIME_DIR", "/var/run/user/0");
openlog("ATB-UPDATE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER); openlog("ATB-UPDATE", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
@@ -72,6 +75,38 @@ int main(int argc, char *argv[]) {
parser.readSettings(); parser.readSettings();
QString repositoryUrl = parser.repositoryUrl(); QString repositoryUrl = parser.repositoryUrl();
if (repositoryUrl.endsWith('/')) {
repositoryUrl.chop(1);
}
QString gitSSHCommand("");
if (repositoryUrl.contains("ptu-config.atb-comm.de")) {
QByteArray const v = qgetenv("GIT_SSH_COMMAND");
if (v.isEmpty()) {
QString sshKeyFile("/opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig");
if (QFileInfo(sshKeyFile).exists()) {
gitSSHCommand = "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig";
if (!qputenv("GIT_SSH_COMMAND", QByteArray(gitSSHCommand.toStdString().c_str()))) {
qCritical() << "ERROR: GIT_SSH_COMMAND not put into env. Exiting...";
return -1;
}
} else {
qCritical() << "ERROR ssh-key-file" << sshKeyFile << "does not exists. Exiting...";
return -1;
}
} else {
gitSSHCommand = QString(v.toStdString().c_str());
qCritical() << "WARNING GIT_SSH_COMMAND already set in enviroment:"
<< gitSSHCommand;
if (gitSSHCommand != "ssh -i /opt/app/tools/atbupdate/.keys/id_ed25519_ptuConfig") {
qCritical() << "ERROR" << gitSSHCommand << "wrong. Exiting...";
return -1;
}
}
}
QString plugInDir = parser.plugInDir(); QString plugInDir = parser.plugInDir();
QString plugInName = parser.plugInName(); QString plugInName = parser.plugInName();
QString workingDir = parser.workingDir(); QString workingDir = parser.workingDir();
@@ -94,6 +129,9 @@ int main(int argc, char *argv[]) {
qInfo() << "pwd ......................" << rtPath; qInfo() << "pwd ......................" << rtPath;
qInfo() << "repositoryUrl ............" << repositoryUrl; qInfo() << "repositoryUrl ............" << repositoryUrl;
if (!gitSSHCommand.isEmpty()) {
qInfo() << "GIT_SSH_COMMAND .........." << gitSSHCommand;
}
qInfo() << "plugInDir ................" << plugInDir; qInfo() << "plugInDir ................" << plugInDir;
qInfo() << "plugInName ..............." << plugInName; qInfo() << "plugInName ..............." << plugInName;
qInfo() << "workingDir ..............." << workingDir; qInfo() << "workingDir ..............." << workingDir;
@@ -162,5 +200,9 @@ int main(int argc, char *argv[]) {
mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint); mw.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
mw.showFullScreen(); mw.showFullScreen();
// test
worker.dcUpdate();
// worker.summary();
return a.exec(); return a.exec();
} }

View File

@@ -11,6 +11,11 @@
#include <QDebug> #include <QDebug>
#include <QScrollBar> #include <QScrollBar>
#include <QEvent> #include <QEvent>
#include <QColor>
#include <QColorDialog>
#include <QScreen>
MainWindow::MainWindow(Worker *worker, QWidget *parent) MainWindow::MainWindow(Worker *worker, QWidget *parent)
@@ -21,16 +26,17 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent)
, m_progressRunning(false) , m_progressRunning(false)
, m_updateStep(UpdateDcEvent::UpdateStep::NONE) { , m_updateStep(UpdateDcEvent::UpdateStep::NONE) {
ui->setupUi(this);
checkOrientation();
this->setStatusBar(new QStatusBar(this)); this->setStatusBar(new QStatusBar(this));
QFont f; QFont f;
f.setStyleHint(QFont::Monospace); f.setStyleHint(QFont::Monospace);
f.setWeight(QFont::Bold); f.setWeight(QFont::Bold);
f.setFamily("Misc Fixed"); f.setFamily("Misc Fixed");
f.setPixelSize(12); f.setPointSize(11);
this->statusBar()->setFont(f); this->statusBar()->setFont(f);
ui->setupUi(this);
ui->updateProgress->setRange(0, 100); ui->updateProgress->setRange(0, 100);
ui->updateProgress->reset(); ui->updateProgress->reset();
@@ -38,15 +44,15 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent)
QString start = QDateTime::currentDateTime().toString(Qt::ISODate); QString start = QDateTime::currentDateTime().toString(Qt::ISODate);
lst << QString("Start: ") + start.leftJustified(m_width-10); lst << QString("Start: ") + start.leftJustified(m_width-10);
lst << QString("").leftJustified(m_width-3, '='); lst << QString("").leftJustified(m_width-3, '=');
lst << QString("Update tool version: %1 - %2 %3").arg(APP_VERSION).arg(APP_BUILD_DATE).arg(APP_BUILD_TIME).leftJustified(m_width-3); lst << QString("Update tool version : %1 - %2 %3").arg(APP_VERSION).arg(APP_BUILD_DATE).arg(APP_BUILD_TIME).leftJustified(m_width-3);
lst << QString("Machine number : %1 ").arg(m_worker->machineNr()).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("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("Zone number : %1 (%2)").arg(m_worker->zoneNr()).arg(Utils::zoneName(m_worker->zoneNr())).leftJustified(m_width-3);
lst << QString("APISM version : %1").arg(m_worker->apismVersion()).leftJustified(m_width-3); lst << QString("APISM version : %1").arg(m_worker->apismVersion()).leftJustified(m_width-3);
lst << QString("").leftJustified(m_width-3, '='); lst << QString("").leftJustified(m_width-3, '=');
ui->updateStatus->setText(lst.join('\n')); ui->updateLabel->setText(lst.join('\n'));
ui->updateStatus->setEnabled(true); ui->updateLabel->setEnabled(true);
// ui->updateStatus->installEventFilter(this); // ui->updateStatus->installEventFilter(this);
m_startTimer = new QTimer(this); m_startTimer = new QTimer(this);
@@ -59,12 +65,34 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent)
m_exitTimer->setSingleShot(true); m_exitTimer->setSingleShot(true);
m_exitTimer->start(1800 * 1000); m_exitTimer->start(1800 * 1000);
m_statusTimer = new QTimer(this);
if (m_statusTimer) {
connect(m_statusTimer, &QTimer::timeout, [this]() {
static QString p(".");
QTime const &t = QDateTime::currentDateTime().time();
QString s = t.toString(Qt::ISODate);
s += ": Update might take several minutes " + p;
if (p.length() >= 5) {
p = ".";
} else {
p += ".";
}
this->statusBar()->showMessage(s);
});
m_statusTimer->setSingleShot(false);
m_statusTimer->start(1000);
}
connect(ui->exit, SIGNAL(clicked()),this,SLOT(onQuit())); connect(ui->exit, SIGNAL(clicked()),this,SLOT(onQuit()));
connect(m_worker, SIGNAL(showSummary(QString)),this,SLOT(onShowSummary(QString)));
connect(m_worker, SIGNAL(disableExit()),this,SLOT(onDisableExit())); connect(m_worker, SIGNAL(disableExit()),this,SLOT(onDisableExit()));
connect(m_worker, SIGNAL(showDcDownload(QString)),this,SLOT(onShowDcDownload(QString)));
connect(m_worker, SIGNAL(setDcDownloadProgress(int)),this,SLOT(onSetDcDownloadProgress(int)));
connect(m_worker, SIGNAL(enableExit()),this,SLOT(onEnableExit())); connect(m_worker, SIGNAL(enableExit()),this,SLOT(onEnableExit()));
connect(m_worker, SIGNAL(stopStartTimer()),this,SLOT(onStopStartTimer())); connect(m_worker, SIGNAL(stopStartTimer()),this,SLOT(onStopStartTimer()));
connect(m_worker, SIGNAL(restartExitTimer()),this,SLOT(onRestartExitTimer())); connect(m_worker, SIGNAL(restartExitTimer()),this,SLOT(onRestartExitTimer()));
connect(m_worker, SIGNAL(appendText(QString,QString)),this,SLOT(onAppendText(QString,QString))); connect(m_worker, SIGNAL(appendText(QString,QString)),this,SLOT(onAppendText(QString,QString)));
connect(m_worker, SIGNAL(insertText(QString)),this,SLOT(onInsertText(QString)), Qt::DirectConnection);
connect(m_worker, SIGNAL(showErrorMessage(QString,QString)),this, SLOT(onShowErrorMessage(QString,QString))); connect(m_worker, SIGNAL(showErrorMessage(QString,QString)),this, SLOT(onShowErrorMessage(QString,QString)));
connect(m_worker, SIGNAL(showStatusMessage(QString,QString)),this, SLOT(onShowStatusMessage(QString,QString))); connect(m_worker, SIGNAL(showStatusMessage(QString,QString)),this, SLOT(onShowStatusMessage(QString,QString)));
connect(m_worker, SIGNAL(showErrorMessage(QStringList)),this, SLOT(onShowErrorMessage(QStringList))); connect(m_worker, SIGNAL(showErrorMessage(QStringList)),this, SLOT(onShowErrorMessage(QStringList)));
@@ -73,12 +101,86 @@ MainWindow::MainWindow(Worker *worker, QWidget *parent)
connect(m_worker, SIGNAL(replaceLast(QStringList,QString)),this, SLOT(onReplaceLast(QStringList,QString))); connect(m_worker, SIGNAL(replaceLast(QStringList,QString)),this, SLOT(onReplaceLast(QStringList,QString)));
} }
void MainWindow::onShowSummary(QString text) {
// QString s = ui->updateLabel->text();
QString s("\n");
ui->updateLabel->setText(s);
ui->updateLabel->hide();
ui->stepLabel->hide();
s += text;
ui->updateStatus->setText(s);
}
void MainWindow::onSetDcDownloadProgress(int v) {
ui->updateProgress->setValue(v);
}
void MainWindow::onShowDcDownload(QString version) {
m_targetDcVersion = version;
ui->exit->setEnabled(false);
ui->stepLabel->setText(QString("Device controller update. Target version: %1").arg(version).leftJustified(m_width-3));
}
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
delete m_startTimer; delete m_startTimer;
delete m_exitTimer; delete m_exitTimer;
delete m_statusTimer;
delete ui; delete ui;
} }
// ----------------------------- Ui::LAYOUT setting -------------------------------------
void MainWindow::checkOrientation()
{
QScreen *screen = QGuiApplication::primaryScreen();
Qt::ScreenOrientation orientation = screen->orientation();
switch (orientation) {
case Qt::PrimaryOrientation:
this->setLandscapeLayout();
break;
case Qt::LandscapeOrientation:
this->setLandscapeLayout();
break;
case Qt::PortraitOrientation:
this->setPortraitLayout();
break;
case Qt::InvertedLandscapeOrientation:
this->setLandscapeLayout();
break;
case Qt::InvertedPortraitOrientation:
this->setPortraitLayout();
break;
}
this->currentOrientation = orientation;
}
void MainWindow::setPortraitLayout()
{
// Adjust layout for portrait mode (480x800)
this->setFixedSize(480, 800);
ui->centralwidget->setFixedSize(480, 800);
}
void MainWindow::setLandscapeLayout()
{
// Adjust layout for landscape mode (800x480)
this->setFixedSize(800, 480);
ui->centralwidget->setFixedSize(800, 480);
}
void MainWindow::customEvent(QEvent *event) { void MainWindow::customEvent(QEvent *event) {
if (event->type() == ProgressEvent::type()) { if (event->type() == ProgressEvent::type()) {
ProgressEvent *pevent = (ProgressEvent *)event; ProgressEvent *pevent = (ProgressEvent *)event;
@@ -180,6 +282,12 @@ void MainWindow::scrollDownTextEdit() {
ui->updateStatus->ensureCursorVisible(); ui->updateStatus->ensureCursorVisible();
} }
void MainWindow::onInsertText(QString text) {
scrollDownTextEdit();
ui->updateStatus->textCursor().insertText(text);
}
void MainWindow::onAppendText(QString text, QString suffix) { void MainWindow::onAppendText(QString text, QString suffix) {
// Utils::printInfoMsg(QString("ON APPEND CALLED AT ") // Utils::printInfoMsg(QString("ON APPEND CALLED AT ")
// + QDateTime::currentDateTime().toString(Qt::ISODateWithMs)); // + QDateTime::currentDateTime().toString(Qt::ISODateWithMs));

View File

@@ -36,8 +36,11 @@ public:
UpdateDcEvent::UpdateStep updateStep() const { return m_updateStep; } UpdateDcEvent::UpdateStep updateStep() const { return m_updateStep; }
void setUpdateStep(UpdateDcEvent::UpdateStep updateStep) { m_updateStep = updateStep; } void setUpdateStep(UpdateDcEvent::UpdateStep updateStep) { m_updateStep = updateStep; }
QString targetDcVersion() {return m_targetDcVersion; }
public slots: public slots:
void onAppendText(QString, QString suffix = ""); void onAppendText(QString, QString suffix = "");
void onInsertText(QString);
void onReplaceLast(QStringList, QString suffix = ""); void onReplaceLast(QStringList, QString suffix = "");
void onReplaceLast(QString, QString suffix = ""); void onReplaceLast(QString, QString suffix = "");
void onShowErrorMessage(QString, QString); void onShowErrorMessage(QString, QString);
@@ -48,6 +51,9 @@ public slots:
void onRestartExitTimer(); void onRestartExitTimer();
void onEnableExit(); void onEnableExit();
void onDisableExit(); void onDisableExit();
void onShowDcDownload(QString);
void onSetDcDownloadProgress(int);
void onShowSummary(QString);
#if EMERGENCY_LEAVE_BL==1 #if EMERGENCY_LEAVE_BL==1
void emergencyLeaveBL(); void emergencyLeaveBL();
#endif #endif
@@ -66,6 +72,12 @@ private:
void onShowMessage(QString, QString); void onShowMessage(QString, QString);
Ui::MainWindow *ui; Ui::MainWindow *ui;
void checkOrientation();
void setPortraitLayout();
void setLandscapeLayout();
Qt::ScreenOrientation currentOrientation;
Worker *m_worker; Worker *m_worker;
int const m_width; int const m_width;
QTimer *m_startTimer; QTimer *m_startTimer;
@@ -73,5 +85,7 @@ private:
bool m_progressRunning; bool m_progressRunning;
//int m_progressValue; //int m_progressValue;
UpdateDcEvent::UpdateStep m_updateStep; UpdateDcEvent::UpdateStep m_updateStep;
QTimer *m_statusTimer;
QString m_targetDcVersion;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@@ -10,9 +10,18 @@
<height>480</height> <height>480</height>
</rect> </rect>
</property> </property>
<property name="minimumSize">
<size>
<width>480</width>
<height>480</height>
</size>
</property>
<property name="font"> <property name="font">
<font> <font>
<family>Source Code Pro</family> <family>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font> </font>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -29,21 +38,28 @@
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="3" column="2"> <item row="2" column="0" colspan="3">
<widget class="QPushButton" name="exit"> <widget class="QLabel" name="stepLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>18</height>
</size>
</property>
<property name="font">
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text"> <property name="text">
<string>Exit</string> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="0" colspan="3">
<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"> <widget class="QTextEdit" name="updateStatus">
<property name="enabled"> <property name="enabled">
<bool>true</bool> <bool>true</bool>
@@ -52,9 +68,16 @@
<font> <font>
<family>Misc Fixed</family> <family>Misc Fixed</family>
<pointsize>11</pointsize> <pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::WinPanel</enum>
</property>
<property name="verticalScrollBarPolicy"> <property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum> <enum>Qt::ScrollBarAsNeeded</enum>
</property> </property>
@@ -64,6 +87,44 @@
<property name="sizeAdjustPolicy"> <property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum> <enum>QAbstractScrollArea::AdjustToContents</enum>
</property> </property>
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2" colspan="3">
<widget class="QLabel" name="updateLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="font">
<font>
<family>Misc Fixed</family>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QProgressBar" name="updateProgress">
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QPushButton" name="exit">
<property name="text">
<string>Exit</string>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@@ -1,33 +1,86 @@
#include "command.h" #include "command.h"
#include "worker.h"
#include <QProcess> #include <QProcess>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QRegularExpression> #include <QRegularExpression>
#include <QDateTime> #include <QDateTime>
#include <QMutexLocker>
Command::Command(QString const &command, int start_timeout, int finish_timeout) Command::Command(QString const &command, int start_timeout, int finish_timeout)
: m_command(command.trimmed()) : m_command(command.trimmed())
, m_commandResult("") , m_commandResult("")
, m_waitForStartTimeout(start_timeout) , m_waitForStartTimeout(start_timeout)
, m_waitForFinishTimeout(finish_timeout) , m_waitForFinishTimeout(finish_timeout)
, m_exitCode(-1) { , m_exitCode(-1)
, m_p(nullptr)
, m_worker(nullptr) {
} }
QString Command::getCommandResult() const { QString Command::getCommandResult(bool reset) const {
QMutexLocker locker(&m_mtx);
if (reset == false) {
return m_commandResult; return m_commandResult;
}
QString commandResult = m_commandResult;
m_commandResult.clear();
return commandResult;
} }
void Command::readyReadStandardOutput() { void Command::readyReadStandardOutput() {
QMutexLocker locker(&m_mtx);
QProcess *p = (QProcess *)sender(); QProcess *p = (QProcess *)sender();
m_commandResult += p->readAllStandardOutput(); if (p) {
// qCritical() << m_commandResult; QString s = p->readAllStandardOutput();
if (m_worker) {
int i = -1;
if ((i = s.indexOf("<DC-VERSION>")) != -1) {
s = s.mid(i+12).trimmed();
if ((i = s.indexOf("\"")) != -1) {
s = s.mid(i+1);
if ((i = s.indexOf("\"")) != -1) {
s = s.mid(0, i).trimmed();
}
}
emit m_worker->showDcDownload(s);
} else
if ((i = s.indexOf("<PROGRESS>")) != -1) {
bool ok;
int v = s.mid(i+10).trimmed().toInt(&ok);
if (ok) {
emit m_worker->setDcDownloadProgress(v);
emit m_worker->insertText(s.mid(0,i) + "\n");
}
} else
if ((i = s.indexOf("<DC-UPDATE-FINISH>")) != -1) {
m_worker->summary();
// qApp->exit(0);
} else
if ((i = s.indexOf("<DC-UPDATE-SUCCESS>")) != -1) {
m_worker->summary();
} else
if ((i = s.indexOf("<DC-UPDATE-FAILURE>")) != -1) {
m_worker->summary();
//qApp->exit(-1);
} else {
emit m_worker->insertText(s);
}
}
m_commandResult += s;
}
} }
void Command::readyReadStandardError() { void Command::readyReadStandardError() {
QProcess *p = (QProcess *)sender(); QProcess *p = (QProcess *)sender();
if (p) {
QByteArray buf = p->readAllStandardError(); QByteArray buf = p->readAllStandardError();
qCritical() << buf; qCritical() << buf;
}
} }
void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) { void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
@@ -35,12 +88,38 @@ void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
// read all remaining data sent to the process, just in case // read all remaining data sent to the process, just in case
QString d = p->readAllStandardOutput(); QString d = p->readAllStandardOutput();
if (!d.isEmpty()) { if (!d.isEmpty()) {
QMutexLocker locker(&m_mtx);
m_commandResult += d; m_commandResult += d;
} }
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput())); disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError())); disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
} }
bool Command::start(QString workingDirectory, QStringList args) {
if (!QDir::setCurrent(workingDirectory)) {
qCritical() << "SET WORKING_DIRECTORY" << workingDirectory
<< "FAILED FOR" << m_command;
return false;
}
if (m_p != nullptr) {
delete m_p;
}
m_p = new QProcess(this);
m_p->setWorkingDirectory(workingDirectory);
m_p->setProcessChannelMode(QProcess::MergedChannels);
connect(m_p, SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput()));
connect(m_p, SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError()));
if (!args.isEmpty()) {
m_p->start(m_command, args);
} else {
m_p->start(m_command);
}
return m_p->waitForStarted(m_waitForStartTimeout);
}
bool Command::execute(QString workingDirectory, QStringList args) { bool Command::execute(QString workingDirectory, QStringList args) {
if (!QDir::setCurrent(workingDirectory)) { if (!QDir::setCurrent(workingDirectory)) {

View File

@@ -7,27 +7,34 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QProcess> #include <QProcess>
#include <QMutex>
class Worker;
class Command : public QObject { class Command : public QObject {
Q_OBJECT Q_OBJECT
QString m_command; QString m_command;
QString m_commandResult; mutable QString m_commandResult;
int m_waitForStartTimeout; int m_waitForStartTimeout;
int m_waitForFinishTimeout; int m_waitForFinishTimeout;
int m_exitCode; int m_exitCode;
mutable QMutex m_mtx;
QProcess *m_p;
Worker *m_worker;
public: public:
explicit Command(QString const &command, explicit Command(QString const &command,
int start_timeout = 100000, int start_timeout = 100000,
int finish_timeout = 100000); int finish_timeout = 100000);
QString getCommandResult() const; QString getCommandResult(bool reset = false) const;
QString command() const { return m_command; } QString command() const { return m_command; }
bool execute(QString workingDirectory, QStringList args = QStringList()); bool execute(QString workingDirectory, QStringList args = QStringList());
bool start(QString workingDirectory, QStringList args = QStringList());
int exitCode() const { return m_exitCode; } int exitCode() const { return m_exitCode; }
void setWorker(Worker *worker) {m_worker = worker; }
private slots: private slots:
void readyReadStandardOutput(); void readyReadStandardOutput();
void readyReadStandardError(); void readyReadStandardError();

View File

@@ -40,6 +40,7 @@ QPluginLoader Update::pluginLoader;
hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) { hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) {
hwinf *hw = nullptr; hwinf *hw = nullptr;
#if 0
if (plugInDir.exists()) { if (plugInDir.exists()) {
QString pluginLibName(fname); QString pluginLibName(fname);
pluginLibName = plugInDir.absoluteFilePath(pluginLibName); pluginLibName = plugInDir.absoluteFilePath(pluginLibName);
@@ -74,6 +75,7 @@ hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) {
qCritical() << "plugins directory" << plugInDir.absolutePath() qCritical() << "plugins directory" << plugInDir.absolutePath()
<< "does not exist"; << "does not exist";
} }
#endif
return hw; return hw;
} }
@@ -117,6 +119,7 @@ Update::Update(Worker *worker,
, m_dryRun(dryRun) , m_dryRun(dryRun)
, m_sys_areDCdataValid(false) { , m_sys_areDCdataValid(false) {
#if 0
if (!m_hw) { if (!m_hw) {
qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin not loaded"; qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_hw == nullptr -> ca-slave plugin not loaded";
} else { } else {
@@ -133,37 +136,8 @@ Update::Update(Worker *worker,
qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_sys_areDCDataValid ..." qCritical() << "(" << __func__ << ":" << __LINE__ << ") m_sys_areDCDataValid ..."
<< m_sys_areDCdataValid; << m_sys_areDCdataValid;
#if 0
QObject const *obj = m_hw->getAPI();
Q_ASSERT(obj != nullptr);
QDebug critical = qCritical();
critical << "connect() to onReportDCDownloadStatus() ...";
if (!connect(obj,
SIGNAL(hwapi_reportDCDownloadStatus(QString const&)),
this,
SLOT(onReportDCDownloadStatus(QString const &)))) {
critical << "FAILED";
} else critical << "DONE";
critical = qCritical();
critical << "connect() to onReportDCDownloadSuccess() ...";
if (!connect(obj,
SIGNAL(hwapi_reportDCDownloadSuccess(QString const&)), this,
SLOT(onReportDCDownloadSuccess(QString const &)))) {
critical << "FAILED";
} else critical << "DONE";
critical = qCritical();
critical << "connect() to onReportDCDownloadFailure() ...";
if (!connect(obj,
SIGNAL(hwapi_reportDCDownloadFailure(QString const &)), this,
SLOT(onReportDCDownloadFailure(QString const &)))) {
critical << "FAILED";
} else critical << "DONE";
#endif
} }
#endif
} }
Update::~Update() { Update::~Update() {
@@ -193,8 +167,8 @@ bool Update::openSerial(int br, QString baudrate, QString comPort) const {
+ " " + baudrate + " " + comPort + "...OK"); + " " + baudrate + " " + comPort + "...OK");
// m_hw->dc_autoRequest(true); // m_hw->dc_autoRequest(true);
m_hw->dc_autoRequest(false); // m_hw->dc_autoRequest(false);
QThread::sleep(1); // QThread::sleep(1);
Utils::printInfoMsg(QString("IS PORT OPEN %1").arg(m_hw->dc_isPortOpen())); Utils::printInfoMsg(QString("IS PORT OPEN %1").arg(m_hw->dc_isPortOpen()));
return true; return true;
@@ -302,7 +276,14 @@ bool Update::isSerialOpen() const {
// bootloader is really not running anymore. // bootloader is really not running anymore.
*/ */
bool Update::updateBinary(QString const &fileToSendToDC) { bool Update::updateBinary(QString const &fileToSendToDC) {
qInfo() << "UPDATING DEVICE CONTROLLER FIRMWARE BINARY" << fileToSendToDC;
QFile dc("/opt/app/tools/atbupdate/ATBDownloadDCFirmware");
if (dc.exists()) {
qCritical() << "ERROR: dc-binary does not exist" << fileToSendToDC;
return false;
}
qInfo() << "updating dc-binary" << fileToSendToDC << "...";
return false; return false;
@@ -507,9 +488,9 @@ bool Update::downloadJson(enum FileTypeJson type,
} }
} }
m_hw->dc_autoRequest(false); // m_hw->dc_autoRequest(false);
qDebug() << "SET AUTO-REQUEST=FALSE"; // qDebug() << "SET AUTO-REQUEST=FALSE";
QThread::sleep(1); // make sure the auto-request flag is acknowledged // QThread::sleep(1); // make sure the auto-request flag is acknowledged
} }
return true; return true;
@@ -584,8 +565,8 @@ QStringList Update::getDcSoftAndHardWareVersion() {
QString const &hwVersion = m_hw->dc_getHWversion().toLower().trimmed(); QString const &hwVersion = m_hw->dc_getHWversion().toLower().trimmed();
QString const &swVersion = m_hw->dc_getSWversion().toLower().trimmed(); QString const &swVersion = m_hw->dc_getSWversion().toLower().trimmed();
m_hw->dc_autoRequest(false); //m_hw->dc_autoRequest(false);
QThread::sleep(1); // make sure the timer-slots are inactive //QThread::sleep(1); // make sure the timer-slots are inactive
if (!hwVersion.isEmpty() && !swVersion.isEmpty()) { if (!hwVersion.isEmpty() && !swVersion.isEmpty()) {
return QStringList() << hwVersion << swVersion; return QStringList() << hwVersion << swVersion;
@@ -629,6 +610,8 @@ QString Update::getFileVersion(QString const& jsonFileName) {
} }
bool Update::checkDownloadedJsonVersions(QStringList const& jsonFileNames) { bool Update::checkDownloadedJsonVersions(QStringList const& jsonFileNames) {
// note: 2026-02-09: this method is not used!
#if 0
for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) { for (QStringList::size_type i=0; i < jsonFileNames.size(); ++i) {
@@ -702,14 +685,14 @@ bool Update::checkDownloadedJsonVersions(QStringList const& jsonFileNames) {
qCritical() << "CANNOT FIND JSON-NR FOR" << fName; qCritical() << "CANNOT FIND JSON-NR FOR" << fName;
} }
} }
#endif
return false; return false;
} }
QMap<QString, QString> QMap<QString, QString>
Update::getInstalledJsonVersions(QStringList const& jsonFileNames) { Update::getInstalledJsonVersions(QStringList const& jsonFileNames) {
QMap<QString, QString> map; QMap<QString, QString> map;
#if 0
if (!m_hw) { if (!m_hw) {
qCritical() << "(" << __func__ << ":" << __LINE__ << "):" qCritical() << "(" << __func__ << ":" << __LINE__ << "):"
<< "ERROR!!! m_hw == nullptr"; << "ERROR!!! m_hw == nullptr";
@@ -809,6 +792,7 @@ Update::getInstalledJsonVersions(QStringList const& jsonFileNames) {
} }
} }
#endif
return map; return map;
} }

View File

@@ -13,9 +13,65 @@
#include <QDir> #include <QDir>
#include <QDirIterator> #include <QDirIterator>
#include <QRegularExpression> #include <QRegularExpression>
#include <QProcess>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonObject>
#include <QJsonArray>
#include <fstream> #include <fstream>
QVector<QPair<QString, QString>> Utils::installedPackages() {
QVector<QPair<QString, QString>> vec;
if (QFile::exists("/usr/bin/ptuPackageVersions")) {
QProcess p;
QStringList params;
params << "-c" << R"(/usr/bin/ptuPackageVersions -i -o json)";
p.start("bash", params);
p.waitForFinished();
QString r = p.readAllStandardOutput();
// ptuPackageVersions returns a json-array
QJsonArray const &ja = QJsonDocument::fromJson(r.remove(QRegExp("\\n")).toUtf8()).array();
if (!ja.empty()) {
qCritical() << __LINE__;
// transform the array into an object, containing the objects
// of the array (christian needs it this way)
foreach (QJsonValue const &value, ja) {
if (value.isObject()) {
QJsonObject obj = value.toObject();
QStringList keys = obj.keys();
if (!keys.isEmpty()) {
QString const &k = keys.first();
QJsonValue const &v = obj.value(k);
if (v.isObject()) {
obj = v.toObject();
if (obj.keys().contains("Version")) {
QJsonValue const &w = obj.value("Version");
if (w.isString()) {
QString s = w.toString();
QPair<QString, QString> p(k, s);
vec.push_back(p);
}
}
}
}
}
}
} else {
qCritical() << __func__ << ":" << __LINE__
<< "ERROR array return by ptuPackageVersions empty";
}
} else {
qCritical() << __func__ << ":" << __LINE__
<< "ERROR executing ptuPackageVersions";
}
return vec;
}
int Utils::read1stLineOfFile(QString fileName) { int Utils::read1stLineOfFile(QString fileName) {
QFile f(fileName); QFile f(fileName);
if (f.exists()) { if (f.exists()) {
@@ -110,19 +166,19 @@ QString Utils::getTariffInfo(QString fileName) {
} }
QString Utils::zoneName(quint8 i) { QString Utils::zoneName(quint8 i) {
static constexpr char const *zName[] = { //static constexpr char const *zName[] = {
"", // "",
"purple", // "purple",
"blue", // "blue",
"yellow", // "yellow",
"green", // "green",
"yellow (mars)", // "yellow (mars)",
"green (mars)" // "green (mars)"
}; //};
if (i < (sizeof(zName)/sizeof(char const *))) { //if (i < (sizeof(zName)/sizeof(char const *))) {
return zName[i]; // return zName[i];
} //}
return "N/A"; return "---";
} }
void Utils::printCriticalErrorMsg(QString const &errorMsg, bool upper, bool lower) { void Utils::printCriticalErrorMsg(QString const &errorMsg, bool upper, bool lower) {

View File

@@ -9,6 +9,7 @@
#include <QDateTime> #include <QDateTime>
#include <QDir> #include <QDir>
#include <QDebug> #include <QDebug>
#include <QPair>
namespace Utils { namespace Utils {
int read1stLineOfFile(QString fileName); int read1stLineOfFile(QString fileName);
@@ -33,6 +34,8 @@ namespace Utils {
QString getParentName(); QString getParentName();
bool isATBQTRunning(); bool isATBQTRunning();
QVector<QPair<QString, QString>> installedPackages();
} }
#endif // UTILS_H_INCLUDED #endif // UTILS_H_INCLUDED

View File

@@ -18,6 +18,7 @@
#include <Qt> #include <Qt>
#include <QScopedPointer> #include <QScopedPointer>
#include <QRegularExpression> #include <QRegularExpression>
#include <QJsonArray>
#include "message_handler.h" #include "message_handler.h"
#include <DeviceController/interfaces.h> #include <DeviceController/interfaces.h>
@@ -25,6 +26,7 @@
#include "progress_event.h" #include "progress_event.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "utils.h" #include "utils.h"
#include "process/command.h"
QString const Worker::UPDATE_STEP_OK ( " [ ok]"); QString const Worker::UPDATE_STEP_OK ( " [ ok]");
QString const Worker::UPDATE_STEP_DONE ( " [done]"); QString const Worker::UPDATE_STEP_DONE ( " [done]");
@@ -141,7 +143,7 @@ Worker::Worker(int customerNr,
char const *serialInterface, char const *serialInterface,
char const *baudrate) char const *baudrate)
: m_customerNr(customerNr) : m_customerNr(customerNr)
, m_customerNrStr(QString("customer_") + QString::number(m_customerNr).rightJustified(3, '0')) , m_customerNrStr(QString("customer_") + QString::number(m_customerNr))
, m_machineNr(machineNr) , m_machineNr(machineNr)
, m_zoneNr(zoneNr) , m_zoneNr(zoneNr)
, m_pluginDir(pluginDir) , m_pluginDir(pluginDir)
@@ -157,7 +159,7 @@ Worker::Worker(int customerNr,
, m_parent(parent) , m_parent(parent)
, m_serialInterface(serialInterface) , m_serialInterface(serialInterface)
, m_baudrate(baudrate) , m_baudrate(baudrate)
, m_gc(m_customerNrStr, m_customerRepository, m_workingDirectory, m_branchName, this) , m_gc(m_customerRepositoryPath, m_customerNrStr, m_customerRepository, m_workingDirectory, m_branchName, this)
, m_versionInfo(QStringList()) , m_versionInfo(QStringList())
, m_osVersion(getOsVersion()) , m_osVersion(getOsVersion())
, m_atbqtVersion(getATBQTVersion()) , m_atbqtVersion(getATBQTVersion())
@@ -175,9 +177,14 @@ Worker::Worker(int customerNr,
, m_filesToUpdate() , m_filesToUpdate()
, m_updateProcessRunning(true) , m_updateProcessRunning(true)
, m_mainWindow(nullptr) /* contains plugin */ , m_mainWindow(nullptr) /* contains plugin */
, m_dcDownloadFirmware(new Command("/opt/app/tools/atbupdate/ATBDownloadDCFirmware --read-dc-version true"))
//, m_withoutIsmasDirectPort(true) /* useful for testing */ { //, m_withoutIsmasDirectPort(true) /* useful for testing */ {
, m_withoutIsmasDirectPort(false) /* useful for testing */ { , m_withoutIsmasDirectPort(false) /* useful for testing */ {
m_start = QDateTime::currentDateTime();
m_dcDownloadFirmware->setWorker(this);
// TODO: turn object into singleton // TODO: turn object into singleton
instance = this; instance = this;
m_lastFailedUpdateStep = UPDATE_STEP::NONE; m_lastFailedUpdateStep = UPDATE_STEP::NONE;
@@ -283,6 +290,8 @@ void Worker::privateUpdate() {
return; return;
} }
//return;
QString func(__PRETTY_FUNCTION__); QString func(__PRETTY_FUNCTION__);
GUI() << (ISMAS() << (CONSOLE() << UPDATE_STEP::STARTED)); GUI() << (ISMAS() << (CONSOLE() << UPDATE_STEP::STARTED));
@@ -456,6 +465,14 @@ void Worker::privateUpdate() {
// //
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
if ((continueUpdate = customerEnvironment()) == false) { if ((continueUpdate = customerEnvironment()) == false) {
// even if something goes wrong creating the environment, try to execute
// opkg_commands
if (QDir(m_customerRepository).exists()) {
// always execute contents of opkg_commands-file
m_filesToUpdate.clear();
m_filesToUpdate << "etc/psa_update/opkg_commands";
execOpkgCommands();
}
return; return;
} }
m_versionInfo = m_gc.gitShowReason(m_branchName); m_versionInfo = m_gc.gitShowReason(m_branchName);
@@ -470,6 +487,14 @@ void Worker::privateUpdate() {
// //
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
if ((continueUpdate = filesToUpdate()) == false) { if ((continueUpdate = filesToUpdate()) == false) {
// even if something goes wrong in filesToUpdate, try to execute
// opkg_commands
if (QDir(m_customerRepository).exists()) {
// always execute contents of opkg_commands-file
m_filesToUpdate.clear();
m_filesToUpdate << "etc/psa_update/opkg_commands";
execOpkgCommands();
}
return; return;
} }
m_versionInfo = m_gc.gitShowReason(m_branchName); m_versionInfo = m_gc.gitShowReason(m_branchName);
@@ -483,6 +508,14 @@ void Worker::privateUpdate() {
// //
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
if ((continueUpdate = syncCustomerRepositoryAndFS()) == false) { if ((continueUpdate = syncCustomerRepositoryAndFS()) == false) {
// even if something goes wrong with rsync, try to execute
// opkg_commands
if (QDir(m_customerRepository).exists()) {
// always execute contents of opkg_commands-file
m_filesToUpdate.clear();
m_filesToUpdate << "etc/psa_update/opkg_commands";
execOpkgCommands();
}
return; return;
} }
lst = QStringList(QString(smap[UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS])); lst = QStringList(QString(smap[UPDATE_STEP::SYNC_CUSTOMER_REPOSITORY_SUCCESS]));
@@ -508,9 +541,10 @@ void Worker::privateUpdate() {
// UPDATE THE PSA USING THE CHANGED FILES // UPDATE THE PSA USING THE CHANGED FILES
// //
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
if ((continueUpdate = downloadFilesToPSAHardware()) == false) { // for 281/Szeged: skip uploading files to DeviceController
return; //if ((continueUpdate = downloadFilesToPSAHardware()) == false) {
} // return;
//}
lst = QStringList(QString("DONE")); lst = QStringList(QString("DONE"));
ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE_SUCCESS)); ISMAS(lst) << (GUI(lst) << (CONSOLE(lst) << UPDATE_STEP::DOWNLOAD_FILES_TO_PSA_HARDWARE_SUCCESS));
@@ -559,16 +593,6 @@ bool Worker::updateTriggerSet() {
CONSOLE(lst) << UPDATE_STEP::DEBUG; CONSOLE(lst) << UPDATE_STEP::DEBUG;
} }
if ((repeat % 8) == 0) {
CONSOLE(QStringList(func) << "RESTART APISM") << UPDATE_STEP::DEBUG;
Command c("systemctl restart apism");
if (c.execute("/tmp")) {
QThread::sleep(20); // give APISM some time to reconnect
QStringList lst = (m_ismasTriggerStatusMessage = (QStringList(func) << "RESTART APISM DONE"));
CONSOLE(lst) << UPDATE_STEP::DEBUG;
}
}
if (std::optional<QString> result if (std::optional<QString> result
= IsmasClient::sendRequestReceiveResponse( = IsmasClient::sendRequestReceiveResponse(
IsmasClient::APISM::DIRECT_PORT, "#M=APISM#C=REQ_ISMASPARAMETER#J={}")) { IsmasClient::APISM::DIRECT_PORT, "#M=APISM#C=REQ_ISMASPARAMETER#J={}")) {
@@ -864,7 +888,24 @@ bool Worker::computeFilesToDownload() {
return (m_filesToDownload.size() > 0); return (m_filesToDownload.size() > 0);
} }
bool Worker::cleanUpOpkgCache() {
bool removedFiles = true;
QDir dir("/var/cache/opkg");
if (dir.exists()) {
dir.setNameFilters(QStringList() << ".gz" << ".ipk");
dir.setFilter(QDir::Files);
foreach(QString dirFile, dir.entryList()) {
removedFiles &= dir.remove(dirFile);
}
}
return removedFiles;
}
bool Worker::execOpkgCommands() { bool Worker::execOpkgCommands() {
if (!cleanUpOpkgCache()) {
CONSOLE() << "INFO: some cached opkg files not removed";
}
for (int i = 0; i < m_filesToUpdate.size(); ++i) { for (int i = 0; i < m_filesToUpdate.size(); ++i) {
QString const fName = m_filesToUpdate.at(i); QString const fName = m_filesToUpdate.at(i);
if (fName.contains("opkg_commands", Qt::CaseInsensitive)) { if (fName.contains("opkg_commands", Qt::CaseInsensitive)) {
@@ -880,10 +921,12 @@ bool Worker::execOpkgCommands() {
while (!in.atEnd()) { while (!in.atEnd()) {
QString line = in.readLine(); QString line = in.readLine();
// TODO: "^\\s*[#]{0,}$" : empty line or comment line starting with # // TODO: "^\\s*[#]{0,}$" : empty line or comment line starting with #
static const QRegularExpression comment("^\\s*#.*$"); static const QRegularExpression comment("^\\s*[#].*$");
if (line.indexOf(comment, 0) == -1) { static const QRegularExpression emptyLine("^\\s*$");
// found opkg command if (line.indexOf(emptyLine, 0) == -1 &&
line.indexOf(comment, 0) == -1) {
QString opkgCommand = line.trimmed(); QString opkgCommand = line.trimmed();
qCritical() << "Found opkg-command" << opkgCommand;
if (!executeOpkgCommand(opkgCommand)) { if (!executeOpkgCommand(opkgCommand)) {
opkgErrorLst << opkgCommand; opkgErrorLst << opkgCommand;
} else { } else {
@@ -1223,6 +1266,7 @@ QString Worker::getPluginVersion(QString const &pluginFileName) const {
QStringList Worker::getDCVersion() const { QStringList Worker::getDCVersion() const {
QStringList lst = (QStringList() << "N/A" << "N/A"); QStringList lst = (QStringList() << "N/A" << "N/A");
#if 0
Update const *up = update(); Update const *up = update();
if (up) { if (up) {
hwinf const *caPlugin = up->hw(); hwinf const *caPlugin = up->hw();
@@ -1244,6 +1288,7 @@ QStringList Worker::getDCVersion() const {
} }
} }
} }
#endif
return lst; return lst;
} }
@@ -1418,5 +1463,116 @@ PSAInstalled Worker::getPSAInstalled() {
} }
} }
psaInstalled.ptuPackageVersion = "{}";
if (QFile::exists("/usr/bin/ptuPackageVersions")) {
Command c("/usr/bin/ptuPackageVersions -i -o json");
if (c.execute(m_workingDirectory)) {
QString r = c.getCommandResult();
// ptuPackageVersions returns a json-array
QJsonArray const &ja = QJsonDocument::fromJson(r.remove(QRegExp("\\n")).toUtf8()).array();
if (!ja.empty()) {
// transform the array into an object, containing the objects
// of the array (christian needs it this way)
QJsonObject o;
foreach (QJsonValue const &value, ja) {
if (value.isObject()) {
QJsonObject obj = value.toObject();
QStringList keys = obj.keys();
if (!keys.isEmpty()) {
QString const &k = obj.keys().first();
QJsonValue const &v = obj.value(k);
o.insert(k, v);
}
}
}
psaInstalled.ptuPackageVersion =
QJsonDocument(o).toJson(QJsonDocument::Compact);
} else {
qCritical() << __func__ << ":" << __LINE__
<< "ERROR array return by ptuPackageVersions empty";
}
} else {
qCritical() << __func__ << ":" << __LINE__
<< "ERROR executing ptuPackageVersions";
}
}
return psaInstalled; return psaInstalled;
} }
bool Worker::dcUpdate() {
return m_dcDownloadFirmware->start("/opt/app/tools/atbupdate");
}
void Worker::summary() {
QString summary, first, second, line, tmp;
QVector<QPair<QString, QString>> vec = Utils::installedPackages();
int max_first = 0, max_second = 0;
for (int i = 0; i < vec.size(); ++i) {
max_first = std::max(max_first, vec[i].first.length());
max_second = std::max(max_second, vec[i].second.length());
}
max_first += 5;
summary = "UPDATE SUMMARY\n\n";
first = QString("%1").arg("start", max_first, QChar(' '));
tmp = QString("%1").arg(start().toString(Qt::ISODate));
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
first = QString("%1").arg("update tool version", max_first, QChar(' '));
tmp = QString("%1 - %2 %3").arg(APP_VERSION).arg(APP_BUILD_DATE).arg(APP_BUILD_TIME);
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
first = QString("%1").arg("machine number", max_first, QChar(' '));
tmp = QString("%1").arg(machineNr());
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
first = QString("%1").arg("customer number", max_first, QChar(' '));
tmp = QString("%1").arg(customerNr());
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
first = QString("%1").arg("zone number", max_first, QChar(' '));
tmp = QString("%1").arg(zoneNr());
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
if (m_mainWindow) {
tmp = m_mainWindow->targetDcVersion();
if (!tmp.isEmpty()) {
first = QString("%1").arg("target device controller", max_first, QChar(' '));
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
}
}
first = QString("%1").arg("apism", max_first, QChar(' '));
tmp = QString("%1").arg(apismVersion());
second = QString("%1").arg(tmp, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
for (int i = 0; i < vec.size(); ++i) {
first = QString("%1").arg(vec[i].first, max_first, QChar(' '));
second = QString("%1").arg(vec[i].second, -max_second, QChar(' '));
line = first + ": " + second;
summary += line + "\n";
}
emit showSummary(summary);
}

View File

@@ -15,7 +15,6 @@
#include <optional> #include <optional>
#include <initializer_list> #include <initializer_list>
#include "update.h"
#include "git/git_client.h" #include "git/git_client.h"
#include "ismas/ismas_client.h" #include "ismas/ismas_client.h"
#include "utils.h" #include "utils.h"
@@ -135,6 +134,8 @@
#define ISMAS_UPDATE_REQUESTS (10) #define ISMAS_UPDATE_REQUESTS (10)
#define CHECK_UPDATE_TRIGGER_SET "Check update trigger ..." #define CHECK_UPDATE_TRIGGER_SET "Check update trigger ..."
class Command;
class Update;
class MainWindow; class MainWindow;
class hwinf; class hwinf;
class Worker : public QThread{ class Worker : public QThread{
@@ -189,10 +190,12 @@ class Worker : public QThread{
QStringList m_ismasTriggerStatusMessage; QStringList m_ismasTriggerStatusMessage;
MainWindow *m_mainWindow; MainWindow *m_mainWindow;
Command *m_dcDownloadFirmware;
bool m_withoutIsmasDirectPort; bool m_withoutIsmasDirectPort;
QString m_apismVersion; QString m_apismVersion;
bool executeOpkgCommand(QString opkgCommand); bool executeOpkgCommand(QString opkgCommand);
bool cleanUpOpkgCache();
QString getOsVersion() const; QString getOsVersion() const;
QString getATBQTVersion() const; QString getATBQTVersion() const;
QString getATBUpdateToolVersion() const; QString getATBUpdateToolVersion() const;
@@ -457,8 +460,13 @@ public:
Update *update() { return m_update; } Update *update() { return m_update; }
Update const *update() const { return m_update; } Update const *update() const { return m_update; }
bool dcUpdate();
void summary();
QDateTime start() { return m_start; }
signals: signals:
void appendText(QString, QString suffix = ""); void appendText(QString, QString suffix = "");
void insertText(QString);
void replaceLast(QString, QString); void replaceLast(QString, QString);
void replaceLast(QStringList, QString); void replaceLast(QStringList, QString);
void showErrorMessage(QString title, QString description); void showErrorMessage(QString title, QString description);
@@ -469,6 +477,9 @@ signals:
void restartExitTimer(); void restartExitTimer();
void enableExit(); void enableExit();
void disableExit(); void disableExit();
void showDcDownload(QString);
void showSummary(QString);
void setDcDownloadProgress(int);
private slots: private slots:
bool updateTriggerSet(); bool updateTriggerSet();
@@ -486,6 +497,8 @@ private:
bool computeFilesToDownload(); bool computeFilesToDownload();
bool execOpkgCommands(); bool execOpkgCommands();
QDateTime m_start;
static const QMap<UPDATE_STEP, const char*> smap; static const QMap<UPDATE_STEP, const char*> smap;
// CONSOLE() // CONSOLE()

1
common.pri Normal file
View File

@@ -0,0 +1 @@
INCLUDEPATH += $$PWD/common/include

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,9 @@ public:
static bool umountSDCard(); static bool umountSDCard();
static std::optional<QString> checkForUSBStick(QString const &dirPathUnderMountPath = "."); static std::optional<QString> checkForUSBStick(QString const &dirPathUnderMountPath = ".");
static std::optional<QString> checkForSDCard(QString const &dirPathUnderMountPath = ".");
static QString getUSBMountPath(QString const &dirPathUnderMountPath = "."); static QString getUSBMountPath(QString const &dirPathUnderMountPath = ".");
static QString getSDMountPath(QString const &dirPathUnderMountPath = ".");
//static QString getUSBDeviceName(); //static QString getUSBDeviceName();
static bool umountUSBStick(); static bool umountUSBStick();
@@ -54,7 +56,8 @@ public:
static QString getPTU4MACAddress(); static QString getPTU4MACAddress();
static QStringList getJsonFilesOnUsbStick(QString const &mountPath); static QStringList getJsonFilesOnUsbStick(QString const &mountPath);
static QString getDCFileOnUsbStick(QString const &mountPath);
static QString getDCFileOnSDCard(QString const &mountPath);
}; };
#endif // SYSTEM_H #endif // SYSTEM_H

49
common/include/command.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef COMMAND_H_INCLUDED
#define COMMAND_H_INCLUDED
#include <QObject>
#include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QProcess>
#include <QScopedPointer>
class Command : public QObject {
Q_OBJECT
QString m_command;
QString m_commandResult;
int m_waitForStartTimeout;
int m_waitForFinishTimeout;
bool m_verbose;
int m_exitCode;
QString m_workingDirectory;
QScopedPointer<QProcess> m_p;
QStringList m_args;
public:
Command(QString command,
QStringList args,
QString workingDirectory,
bool verbose = true,
int start_timeout = 100000,
int finish_timeout = 100000);
void resetCommandResult() { m_commandResult.clear(); }
QString getCommandResult(bool reset = false);
QString const &command() const { return m_command; }
QString const &commandResult() const { return m_commandResult; }
QStringList const &args() const { return m_args; }
bool exec();
int exitCode() const { return m_exitCode; }
private slots:
virtual void readyReadStandardOutput();
virtual void readyReadStandardError();
};
#endif // COMMAND_H_INCLUDED

View File

@@ -0,0 +1,92 @@
#ifndef UTILS_INTERNAL_H_INCLUDED
#define UTILS_INTERNAL_H_INCLUDED
#include <QString>
#include <QSettings>
#include <memory>
namespace internal {
static constexpr const char *UPDATE_NOT_NECESSARY{"not necessary"};
static constexpr const char *UPDATE_NOT_REQUESTED{"not requested"};
static constexpr const char *UPDATE_INITIAL{"initial update"};
static constexpr const char *UPDATE_REQUESTED{"requested"};
static constexpr const char *NO_CUSTOMER_REPOSITORY{"no customer repository"};
static constexpr const int NO_CUSTOMER_REPOSITORY_CODE{-8};
static constexpr const char *NO_ETC_CUSTOMER_REPOSITORY{"no etc/ in customer repository"};
static constexpr const int NO_ETC_CUSTOMER_REPOSITORY_CODE{-9};
static constexpr const char *NO_OPT_CUSTOMER_REPOSITORY{"no opt/ in customer repository"};
static constexpr const int NO_OPT_CUSTOMER_REPOSITORY_CODE{-10};
static constexpr const char *ISMAS_CONNECTED{"connected"};
static constexpr const char *ISMAS_DISCONNECTED{"disconnected"};
static constexpr const char *ISMAS_DISCONNECTING{"disconnecting"};
static constexpr const char *ISMAS_NOT_CONNECTED{"not connected"};
static constexpr const char *ISMAS_CONNECTION_IN_PROGRESS{"connecting"};
static constexpr const char *BROKER_CONNECTED{"connected"};
static constexpr const char *BROKER_DISCONNECTED{"disconnected"};
static constexpr const char *BROKER_DISCONNECTING{"disconnecting"};
static constexpr const char *BROKER_NOT_CONNECTED{"not connected"};
static constexpr const char *BROKER_CONNECTION_IN_PROGRESS{"connecting"};
static constexpr const int GIT_CHECKOUT_ERROR_CODE{-2};
static constexpr const int GIT_PULL_ERROR_CODE{-4};
static constexpr const int GIT_NOT_NECESSARY_CODE{1};
static constexpr const int GIT_UPDATED_CODE{2};
static constexpr const int GIT_CLONED_CODE{3};
static constexpr const char *GIT_CUSTOMER_REPO_CHECKOUT_ERROR{"checkout error"};
static constexpr const char *GIT_CUSTOMER_REPO_PULL_ERROR{"pull error"};
static constexpr const char *GIT_CUSTOMER_REPO_UP_TO_DATE{"up to date"};
static constexpr const char *GIT_CUSTOMER_REPO_NO_UPDATE_NECESSARY{"no repository update necessary"};
static constexpr const char *GIT_CUSTOMER_REPO_NOT_NECESSARY{"not necessary"};
static constexpr const char *GIT_CUSTOMER_REPO_UPDATED{"repository updated"};
static constexpr const char *GIT_CUSTOMER_REPO_CLONED{"repository cloned"};
static constexpr const char *GIT_UPDATED{"updated"};
static constexpr const char *EXEC_OPKG_COMMANDS_SUCCESS{"success"};
static constexpr const char *EXEC_OPKG_COMMANDS_FAIL{"FAIL"};
static constexpr const char *EXEC_OPKG_COMMANDS_NOACTION_SUCCESS{"success"};
static constexpr const char *EXEC_OPKG_COMMANDS_NOACTION_FAIL{"FAIL"};
static constexpr const char *UPDATE_DC_JSON_FILES_SUCCESS{"success"};
static constexpr const char *SYNC_CUSTOMER_REPO_FILES_SUCCESS{"success"};
static constexpr const char *UPDATE_DC_FIRMARE_SUCCESS{"success"};
static constexpr const char *OPKG_MARKER{"<OPKG>"};
static constexpr const char *SYNC_MARKER{"<SYNC>"};
static constexpr const char *DC_MARKER{"<DC>"};
static constexpr const char *GIT_MARKER{"<GIT>"};
static constexpr const char *ISMAS_MARKER{"<ISMAS>"};
static constexpr const int PERCENT_CHECK_ISMAS_CONNECIVITY{10};
static constexpr const int PERCENT_CHECK_UPDATE_REQUEST{20};
static constexpr const int PERCENT_CHECK_CUSTOMER_REPOSITORY{30};
static constexpr const int PERCENT_INSTALL_SW_PACKETS_NOACTION{40};
static constexpr const int PERCENT_INSTALL_SW_PACKETS{50};
static constexpr const int PERCENT_INSTALL_DC_CONFIGURATION{60};
static constexpr const int PERCENT_SYNCHRONIZE_REPO_AND_FILESYS{70};
static constexpr const int PERCENT_UPDATE_DC{80};
static constexpr const int PERCENT_SHOW_FINAL_STATUS{90};
static constexpr const char *DEFAULT_INI_DIR{"/etc/tools/atbupdate/"};
static constexpr const char *DEFAULT_INSTALL_DIR{"/opt/app/tools/atbupdate/"};
int read1stLineOfFile(QString fileName);
QString customerRepoRoot();
QString customerRepoDir();
QString customerRepoDcDir();
QString customerRepoDirName();
QString repositoryUrl();
QString branchName();
bool customerRepoExists();
std::unique_ptr<QSettings> readSettings(QString const &optionalDirName = "");
std::unique_ptr<QString> dcCandidateToInstall(QString const &dcDirectory);
}
#endif // UTILS_INTERNAL_H_INCLUDED

View File

@@ -265,6 +265,20 @@ std::optional<QString> System::checkForUSBStick(QString const &dirPathUnderMount
return mountPath.isEmpty() ? std::nullopt : std::optional<QString>(mountPath); return mountPath.isEmpty() ? std::nullopt : std::optional<QString>(mountPath);
} }
std::optional<QString> System::checkForSDCard(QString const &dirPathUnderMountPath) {
#if defined (ARCH_DesktopLinux)
// DEBUG / TEST:
if (QFileInfo(getSDMountPath()).isDir())
return true;
else
return false;
#endif
QString const &mountPath = getSDMountPath(dirPathUnderMountPath);
// qCritical() << "MOUNT-PATH" << mountPath;
return mountPath.isEmpty() ? std::nullopt : std::optional<QString>(mountPath);
}
/** /**
@@ -315,7 +329,7 @@ QString System::getUSBMountPath(QString const &dirPathUnderMountPath) {
mountLine = line.split(' '); mountLine = line.split(' ');
if (mountLine.size() > 3) { if (mountLine.size() > 3) {
qCritical() << "System::getUSBMountPath(): " << mountLine.at(0) << " is mounted on " << mountLine.at(2); // qCritical() << "System::getUSBMountPath(): " << mountLine.at(0) << " is mounted on " << mountLine.at(2);
QDir d(QDir::cleanPath(mountLine.at(2) + QDir::separator() + dirPathUnderMountPath)); QDir d(QDir::cleanPath(mountLine.at(2) + QDir::separator() + dirPathUnderMountPath));
if (d.exists()) { if (d.exists()) {
return mountLine.at(2); return mountLine.at(2);
@@ -332,6 +346,64 @@ QString System::getUSBMountPath(QString const &dirPathUnderMountPath) {
return ""; return "";
} }
QString System::getSDMountPath(QString const &dirPathUnderMountPath) {
#if defined (ARCH_DesktopLinux)
// DEBUG / TEST:
return QDir::homePath().append("/APconfigTest/USB");
#endif
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
QStringList mountLine;
qDebug() << "System::getSDMountPath()";
QRegExp devRegExp = QRegExp("dev/mmc*", Qt::CaseSensitive, QRegExp::WildcardUnix);
QRegExp mountRegExp = QRegExp("media/mmc*", Qt::CaseSensitive, QRegExp::WildcardUnix);
QString commandString = "mount";
process.start(commandString);
if (!process.waitForStarted()) {
errorMsg = "System::getSDMountPath(): ERROR: waitForStarted()";
return "";
}
if (!process.waitForFinished(600000)) {
errorMsg = "System::getSDMountPath(): ERROR: " + process.errorString();
qDebug() << errorMsg;
return "";
}
else {
QByteArray bytes = process.readAll();
QStringList lines = QString(bytes).split("\n");
foreach (QString line, lines) {
qDebug() << "System::getSDMountPath() line: " << line;
if (line.contains(devRegExp) && line.contains(mountRegExp)) {
qDebug() << " -> this line is a usb storage device mount" << line;
mountLine = line.split(' ');
if (mountLine.size() > 3) {
// qCritical() << "System::getSDMountPath(): " << mountLine.at(0) << " is mounted on " << mountLine.at(2);
QDir d(QDir::cleanPath(mountLine.at(2) + QDir::separator() + dirPathUnderMountPath));
if (d.exists()) {
return mountLine.at(2);
} else {
qCritical() << "directory" << d.absolutePath() << "does not exist";
}
}
}
}
}
qDebug() << "System::getSDMountPath() no mounted usb device found!";
return "";
}
QStringList System::getJsonFilesOnUsbStick(QString const &mountPath) { QStringList System::getJsonFilesOnUsbStick(QString const &mountPath) {
QStringList jsonFiles; QStringList jsonFiles;
@@ -354,6 +426,38 @@ QStringList System::getJsonFilesOnUsbStick(QString const &mountPath) {
return jsonFiles; return jsonFiles;
} }
QString System::getDCFileOnUsbStick(QString const &mountPath) {
QString dcFile;
// /media/sda2/etc/dc
QString const &dirPath = QDir::cleanPath(mountPath + QDir::separator() + "etc" + QDir::separator() + "dc");
QDir d(dirPath);
if (d.exists()) {
QFileInfo fi(dirPath + QDir::separator() + "dc2c.bin");
if (fi.exists()) {
dcFile = fi.absoluteFilePath();
}
}
return dcFile;
}
QString System::getDCFileOnSDCard(QString const &mountPath) {
QString dcFile;
// /media/sda2/etc/dc
QString const &dirPath = QDir::cleanPath(mountPath + QDir::separator() + "etc" + QDir::separator() + "dc");
QDir d(dirPath);
if (d.exists()) {
QFileInfo fi(dirPath + QDir::separator() + "dc2c.bin");
if (fi.exists()) {
dcFile = fi.absoluteFilePath();
}
}
return dcFile;
}
/******************************************************************************** /********************************************************************************
* static function to check if a mounted sd-card is writable. * static function to check if a mounted sd-card is writable.
* *

128
common/src/command.cpp Normal file
View File

@@ -0,0 +1,128 @@
#include "command.h"
#include <QProcess>
#include <QDebug>
#include <QDir>
#include <QRegularExpression>
#include <QDateTime>
Command::Command(QString command, QStringList args, QString workingDirectory,
bool verbose, int start_timeout, int finish_timeout)
: m_command(command.trimmed())
, m_commandResult("")
, m_waitForStartTimeout(start_timeout)
, m_waitForFinishTimeout(finish_timeout)
, m_verbose(verbose)
, m_exitCode(-1)
, m_workingDirectory(workingDirectory)
, m_args(args) {
m_p.reset(new QProcess(this));
if (m_p) {
m_p->setWorkingDirectory(workingDirectory);
m_p->setProcessChannelMode(QProcess::MergedChannels);
connect(m_p.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(readyReadStandardOutput()));
connect(m_p.get(), SIGNAL(readyReadStandardError()), this, SLOT(readyReadStandardError()));
}
}
void Command::readyReadStandardOutput() {
QProcess *p = (QProcess *)sender();
if (p) {
QString s = p->readAllStandardOutput();
if (m_verbose) {
// qCritical().noquote() << s;
m_commandResult += s;
}
}
}
void Command::readyReadStandardError() {
QProcess *p = (QProcess *)sender();
if (p) {
QString s = p->readAllStandardError();
// qCritical().noquote() << s;
m_commandResult += s;
}
}
QString Command::getCommandResult(bool reset) {
if (reset == false) {
return m_commandResult;
}
QString commandResult = m_commandResult;
m_commandResult.clear();
return commandResult;
}
bool Command::exec() {
if (!m_args.isEmpty()) {
m_p->start(m_command, m_args);
} else {
m_p->start(m_command);
}
qint64 const start = QDateTime::currentDateTime().toMSecsSinceEpoch();
bool started = false;
if ((started = m_p->waitForStarted(m_waitForStartTimeout)) == true) {
// qCritical() << "PROCESS" << m_command << "STARTED IN" << m_p->workingDirectory();
if (m_p->state() == QProcess::ProcessState::Running) {
// qDebug() << "PROCESS" << m_command << "RUNNING IN" << p->workingDirectory();
// wait forever for git/opkg-commands to finish
int wait = m_waitForFinishTimeout;
if (m_command.trimmed().startsWith("git", Qt::CaseInsensitive) ||
m_command.trimmed().startsWith("opkg", Qt::CaseInsensitive)) {
wait = -1;
}
bool const no_timeout = m_p->waitForFinished(wait);
if (no_timeout) {
// qDebug() << "PROCESS" << m_command << "FINISHED IN" << p->workingDirectory();
if (m_p->exitStatus() == QProcess::NormalExit) {
if ((m_exitCode = m_p->exitCode()) == 0) {
qCritical().noquote() << m_p->readAllStandardOutput();
//qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
//qDebug() << "EXECUTED" << m_command
// << QString("(runtime %1ms)").arg(end-start)
// << "with code" << m_exitCode
// << "IN" << m_p->workingDirectory();
return true;
} else {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qCritical() << "EXECUTED" << m_command
<< QString("(runtime %1ms)").arg(end-start)
<< "with code" << m_exitCode
<< "IN" << m_p->workingDirectory();
}
} else {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qCritical() << "PROCESS" << m_command << "CRASHED with code"
<< m_p->exitCode()
<< QString("(after %1ms)").arg(end-start)
<< "IN" << m_p->workingDirectory();
}
} else {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qCritical() << "PROCESS" << m_command
<< "DID NOT FINISH WITH" << wait
<< "MS IN" << m_p->workingDirectory()
<< QString("(runtime %1ms)").arg(end-start);
}
} else {
qCritical() << "WRONG PROCESS STATE" << m_p->state()
<< "IN" << m_p->workingDirectory();
}
} else {
qint64 const end = QDateTime::currentDateTime().toMSecsSinceEpoch();
qCritical() << "PROCESS" << m_command << "TIMEOUT AT START"
<< QString("(runtime %1ms)").arg(end-start)
<< "IN" << m_p->workingDirectory() << m_waitForStartTimeout;
}
return false;
}

View File

@@ -0,0 +1,167 @@
#include "utils_internal.h"
#include <QFile>
#include <QDir>
#include <QTextStream>
#include <QSettings>
#include <QDebug>
#include <QCryptographicHash>
#include <QFileInfoList>
namespace internal {
int read1stLineOfFile(QString fileName) {
QFile f(fileName);
if (f.exists()) {
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&f);
in.setCodec("UTF-8");
while(!in.atEnd()) {
return in.readLine().toInt();
}
}
}
return -1;
}
QString customerRepoRoot() {
return "/opt/app/tools/atbupdate/";
}
QString customerRepoDirName() {
int const customerNr = read1stLineOfFile("/mnt/system_data/cust_nr");
return (customerNr != -1) ? QString("customer_%1").arg(customerNr) : "";
}
QString customerRepoDir() {
QString const &n = customerRepoDirName();
QString const &r = customerRepoRoot();
return !n.isEmpty() ? QDir::cleanPath(r + QDir::separator() + n) : "";
}
QString customerRepoDcDir() {
QString const &r = customerRepoDir();
return QDir::cleanPath(r + QDir::separator() + "etc/dc/");
}
bool customerRepoExists() {
QString const repoDir{customerRepoDir()};
return !repoDir.isEmpty() ? QDir(repoDir).exists() : false;
}
QString repositoryUrl() {
return "gitea@ptu-config.atb-comm.de:ATB/";
}
QString branchName() {
int const zoneNr = read1stLineOfFile("/mnt/system_data/zone_nr");
if (zoneNr != -1) {
return QString("zg1/zone%1").arg(zoneNr);
}
return "";
}
std::unique_ptr<QSettings> readSettings(QString const &optionalDirName) {
std::unique_ptr<QSettings> settings{std::make_unique<QSettings>()};
//QString const fileName{settings->applicationName() + ".ini"};
QString const fileName{"ATBUpdateTool.ini"};
QDir d;
if (!optionalDirName.isEmpty()) {
d = QDir{optionalDirName};
if (d.exists()) { // try to find ini-file under optionalDirname
QFileInfo fi{d, optionalDirName};
if (fi.exists()) {
settings.reset(new QSettings(fi.absoluteFilePath(), QSettings::IniFormat));
return settings;
} else {
qCritical() << fi.absoluteFilePath() << "not found."
<< "Try" << internal::DEFAULT_INI_DIR;
}
} else {
qCritical() << optionalDirName << "not found."
<< "Try" << internal::DEFAULT_INSTALL_DIR;
}
}
d = internal::DEFAULT_INI_DIR;
if (d.exists()) { // try to find ini-file under /etc/tools/atbupdate
QFileInfo fi{d, fileName};
if (fi.exists()) {
settings.reset(new QSettings(fi.absoluteFilePath(), QSettings::IniFormat));
return settings;
} else {
qCritical() << fi.absoluteFilePath() << "not found."
<< "Try" << internal::DEFAULT_INSTALL_DIR;
}
} else {
qCritical() << internal::DEFAULT_INI_DIR << "not found."
<< "Try" << internal::DEFAULT_INSTALL_DIR;
}
d = QDir{internal::DEFAULT_INSTALL_DIR};
if (d.exists()) { // try to find ini-file under /opt/app/tools/atbupdate
QFileInfo fi{d, fileName};
if (fi.exists()) {
settings.reset(new QSettings(fi.absoluteFilePath(), QSettings::IniFormat));
return settings;
} else {
qCritical() << fi.absoluteFilePath() << "not found.";
}
} else {
qCritical() << internal::DEFAULT_INSTALL_DIR << "not found.";
}
return settings;
}
std::unique_ptr<QString> dcCandidateToInstall(QString const &dcDirectory) {
std::unique_ptr<QString> dcCandidate{nullptr};
qCritical() << __func__ << __LINE__ << dcDirectory;
QDir dcDir{dcDirectory.isEmpty() ? customerRepoDcDir() : dcDirectory};
if (dcDir.exists()) {
QFileInfoList fileInfoList =
dcDir.entryInfoList(QStringList("*.bin"),
QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
QFileInfo dc2cbin{dcDir.absoluteFilePath("dc2c.bin")};
if (dc2cbin.exists()) {
QCryptographicHash md5gen(QCryptographicHash::Md5);
QByteArray ba_dc2cbin{};
{
QFile f{dc2cbin.absoluteFilePath()};
if (f.open(QIODevice::ReadOnly)) {
md5gen.addData(f.readAll());
ba_dc2cbin = md5gen.result();
md5gen.reset();
}
}
if (ba_dc2cbin.size() > 0) {
QFileInfoList::const_iterator it;
for (it = fileInfoList.cbegin(); it != fileInfoList.cend(); ++it) {
if (it->absoluteFilePath() != dc2cbin.absoluteFilePath()) {
QFile f{it->absoluteFilePath()};
if (f.open(QIODevice::ReadOnly)) {
md5gen.addData(f.readAll());
if (ba_dc2cbin == md5gen.result()) {
dcCandidate.reset(new QString(f.fileName()));
break;
}
md5gen.reset();
}
}
}
}
}
}
return dcCandidate;
}
} // namespace internal

184
on_update_failure.sh Executable file
View File

@@ -0,0 +1,184 @@
#!/bin/bash
# set -x
#
#############################################################################
# Check if the ATBUpdateTool(.service) did not send U0001 (sucess),
# U0002 (activated) or U0003 (error) to ISMAS, including the case that the
# tool did not crash or was not killed under other circumstances.
#
# This script will be called in the associated atbupdatetool.service (see
# /lib/systemd/system/atbupdatetool.service).
#
###############################################################################
readonly OUT_FILE=/tmp/out.txt
readonly SCRIPT_NAME=$(basename $0)
if [ -z "$START_DATE" ]
then
START_DATE=$(date +"%Y-%m-%d00:00:00")
fi
if [ -z "$STOP_DATE" ]
then
STOP_DATE=$(date +"%Y-%m-%d%H:%M:%S")
fi
echo "EXIT_CODE=$EXIT_CODE"
echo "SERVICE_RESULT=$SERVICE_RESULT"
echo "EXIT_STATUS=$EXIT_STATUS"
echo "START_DATE=$START_DATE"
echo "STOP_DATE=$STOP_DATE"
last_cmd_event () {
# output json; look for start of CMD_EVENT; look for }}; extract everything between; finally re-add }}.
# could also look for ">>>", but this might change in the future.
local json=$(\
journalctl -u atbupdatetool --since="$START_DATE" --until="$STOP_DATE" --output=json-pretty |\
grep "#M=APISM#C=CMD_EVENT#J=" |\
awk '{split($0, a, "#M=APISM#C=CMD_EVENT#J="); print a[2]}' |\
awk '{split($0, a, /\}[\s]*\}/); print a[1]}' |\
tr -d '\\' |\
tail -1)
json+="}}"
echo "$json"
}
last_percent_value () {
echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(\"PERCENT\"\\s*:\)\(\\s*[0-9]\+\)\(.*$\)/\3/p" | xargs | awk '{print $NF}')
}
last_step () {
echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(\"PERCENT\"\\s*:\)\(\\s*[0-9]\+\)\(.*$\)/\1\2\3\4/p" | tail -1 | awk '{split($0, a, ","); print a[8]}' | awk '{split($0, b, " : "); print b[2]}' | tr -d '"\\')
}
last_step_result () {
echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(\"PERCENT\"\\s*:\)\(\\s*[0-9]\+\)\(.*$\)/\1\2\3\4/p" | tail -1 | awk '{split($0, a, ","); print a[9]}' | awk '{split($0, b, " : "); print b[2]}' | tr -d '"\\')
}
pid_atbupdatetool () {
echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(ATBUpdateTool\[\)\(\\s*[0-9]\+\)\(\].*$\)/\3/p" | uniq)
}
last_event () {
echo $(cat $OUT_FILE | sed -n "s/\(^.*\)\(\"PERCENT\"\\s*:\)\(\\s*[0-9]\+\)\(.*$\)/\1\2\3\4/p" | tail -1 | awk '{split($0, a, ","); print a[4]}' | awk '{split($0, b, ":"); print b[2]}' | tr -d ' ' | tr -d '"\\')
}
sendU0003 () {
LAST_CMD_EVENT=$(last_cmd_event)
echo "LAST=$LAST_CMD_EVENT"
local json_detected=0
local temp
local x
if [[ ! -z "$LAST_CMD_EVENT" ]]
then
temp=$(mktemp -p /tmp)
echo "$LAST_CMD_EVENT" > "$temp"
x=$(jq .EVENT $temp)
if [ $? -eq 0 ]; then
json_detected=1
fi
fi
if [ $json_detected -eq 1 ]
then
# local pid=$(jq .EVENT_ID $temp)
# if json has been detected, use "jq".
x=$(jq .PARAMETER.PERCENT $temp | tr -d '"')
[[ -z "$x" ]] && PERCENT=0 || PERCENT=$x
x=$(jq .PARAMETER.STEP $temp | tr -d '"')
[[ -z "$x" ]] && STEP="unknown" || STEP="$x"
x=$(jq .PARAMETER.STEP_RESULT $temp | tr -d '"')
[[ -z "$x" ]] && STEP_RESULT="unknown" || STEP_RESULT="$x"
x=$(pid_atbupdatetool)
[[ -z "$x" ]] && PID=99999 || PID=$x
x=$(last_event)
[[ -z "$x" ]] && EVENT="unknown" || EVENT="$x"
else
# cannot parse valid json. try original journal text file.
x=$(last_percent_value)
[[ -z "$x" ]] && PERCENT=0 || PERCENT=$x
x=$(last_step)
[[ -z "$x" ]] && STEP="unknown" || STEP="$x"
x=$(last_step_result)
[[ -z "$x" ]] && STEP_RESULT="unknown" || STEP_RESULT="$x"
x=$(pid_atbupdatetool)
[[ -z "$x" ]] && PID=99999 || PID=$x
x=$(last_event)
[[ -z "$x" ]] && EVENT="unknown" || EVENT="$x"
fi
echo "PERCENT=$PERCENT"
echo "STEP=$STEP"
echo "STEP_RESULT=$STEP_RESULT"
echo "PID=$PID"
echo "EVENT=$EVENT"
# build json to be sent to ISMAS with "U0003"
CURRENT_DATE=$(date +"%Y-%m-%d %H:%M:%S.000%z")
U0003Msg="{\
\"REASON\":\"SW_UP\",\
\"TIMESTAMP\":\"$CURRENT_DATE\",\
\"EVENT_ID\":\"$PID\",\
\"EVENT\":\"U0003\",\
\"EVENTSTATE\":1,\
\"PARAMETER\": {\
\"PERCENT\" : $PERCENT,\
\"RESULTCODE\" : 99,\
\"STEP\" : \"$1 -> monitored last step: $STEP\",\
\"STEP_RESULT\" : \"$1 -> monitored last step result: $STEP_RESULT\",\
\"VERSION\" : \"\"\
}\
}"
echo -e "U0003Msg=$U0003Msg"
(echo "#M=APISM#C=CMD_EVENT#J=$U0003Msg"; sleep 5) | nc localhost 7777
}
# save journal for the last run of atbupdatetool in $OUT_FILE
echo $(journalctl -u atbupdatetool --since="$START_DATE" --until=$STOP_DATE) > $OUT_FILE
if [[ "$EXIT_CODE" = "exited" && "$SERVICE_RESULT" = "success" && "$EXIT_STATUS" = "0" ]]
then
# almost normal run; at least there was no crash.
if cat $OUT_FILE | grep -o "U0003" | wc -l; then
echo "$SCRIPT_NAME: Failure code U0003 already sent to ISMAS"
exit 0
fi
if cat $OUT_FILE | grep -o "U0002" | wc -l; then
echo "$SCRIPT_NAME: Success code U0002 already sent to ISMAS"
exit 0
else
sendU0003 "$SCRIPT_NAME: Success code U0002 not sent to ISMAS"
fi
if cat $OUT_FILE | grep -o "U0001" | wc -l; then
echo "$SCRIPT_NAME: Success code U0001 already sent to ISMAS"
exit 0
else
sendU0003 "$SCRIPT_NAME: Success code U0001 not sent to ISMAS"
fi
else
# something unexpected has happened.
sendU0003 "$SCRIPT_NAME: EXIT_CODE=$EXIT_CODE;SERVICE_RESULT=$SERVICE_RESULT;EXIT_STATUS=$EXIT_STATUS"
fi