Add update functionality (again using ATBUpdateTool as template)

This commit is contained in:
Gerhard Hoffmann 2024-04-23 16:25:36 +02:00
parent 2aa676f18c
commit 0933274c82
2 changed files with 437 additions and 0 deletions

View File

@ -0,0 +1,332 @@
#include "update.h"
#include <QCoreApplication>
#include <QFile>
#include <QTemporaryFile>
#include <QDebug>
#include <QTextStream>
#include <QRegularExpression>
#include <QRegExp>
#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include "unistd.h"
#endif
#include <DeviceController/interfaces.h>
#include <QSharedMemory>
#include <QScopedPointer>
#include <QDir>
#include <QThread>
#include <QDateTime>
#include <QPluginLoader>
#include <QMap>
#define UPDATE_OPKG (1)
#define UPDATE_DC (0)
static const QMap<QString, int> baudrateMap = {
{"1200" , 0}, {"9600" , 1}, {"19200" , 2}, {"38400" , 3},
{"57600" , 4}, {"115200" , 5}
};
QPluginLoader Update::pluginLoader;
hwinf *Update::loadDCPlugin(QDir const &plugInDir, QString const &fname) {
hwinf *hw = nullptr;
if (plugInDir.exists()) {
QString pluginLibName(fname);
pluginLibName = plugInDir.absoluteFilePath(pluginLibName);
QFileInfo info(pluginLibName);
if (info.exists()) {
pluginLibName = plugInDir.absoluteFilePath(pluginLibName);
pluginLoader.setFileName(pluginLibName);
// static QPluginLoader pluginLoader(pluginLibName);
if (!pluginLoader.load()) {
qCritical() << "in directory" << plugInDir.absolutePath();
qCritical() << "cannot load plugin" << pluginLoader.fileName();
qCritical() << pluginLoader.errorString();
exit(-1);
}
qCritical() << "loadDCPlugin() plugin directory:" << plugInDir.absolutePath();
qCritical() << "loadDCPlugin() plugin file name:" << pluginLoader.fileName();
if (!pluginLoader.isLoaded()) {
qCritical() << pluginLoader.errorString();
exit(-2);
}
QObject *plugin = pluginLoader.instance();
if (!plugin) {
qCritical() << "cannot start instance";
exit(-3);
}
if (! (hw = qobject_cast<hwinf *>(plugin))) {
qCritical() << "cannot cast plugin" << plugin << "to hwinf";
exit(-4);
}
} else {
qCritical() << pluginLibName << "does not exist";
exit(-5);
}
} else {
qCritical() << "plugins directory" << plugInDir.absolutePath()
<< "does not exist";
exit(-6);
}
return hw;
}
bool Update::unloadDCPlugin() {
if (pluginLoader.unload()) {
qCritical() << "unloaded plugin" << pluginLoader.fileName();
// Note: will re-instantiate the library !
// QObject *rootObject = pluginLoader.instance();
// if (rootObject) {
// qCritical() << "reloaded plugin: root object again available";
// return false;
// }
// qCritical()unloaded plugin: root object gone";
return true;
}
return false;
}
class hwapi;
Update::Update(QString customerRepository,
QString customerNrStr,
QString branchName,
QString plugInDir,
QString pluginName,
QString workingDir,
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;
#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
}
}
Update::~Update() {
// unloadDCPlugin();
}
bool Update::doUpdate(QStringList const &filesToWorkOn) {
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-SLAVE-PLUGIN NOT CONNECTED";
return false;
}
qCritical() << "ERROR!!! DC DATA NOT VALID -> CA-SLAVE-PLUGIN NOT CONNECTED (" << tries << ")";
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 = 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)) {
res = true;
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)) {
res = true;
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)) {
res = true;
if ((res = updateDeviceConf(fToWorkOn))) {
qCritical() << QString("DOWNLOADED DEVICE TEMPLATE %1").arg(fToWorkOn);
}
} else {
qCritical() << "UNKNOWN JSON FILE NAME" << fToWorkOn;
res = false;
}
}
return res;
}
bool Update::downloadJson(enum FileTypeJson type,
int templateIdx,
QString jsFileToSendToDC) const {
m_hw->dc_autoRequest(true); // downloading Json needs the AutoEmission flag
qDebug() << "SET AUTO-REQUEST=TRUE";
QThread::sleep(1); // make sure the auto-request flag is acknowledged
QStringList lst;
bool ready = false;
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;
QString msg;
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())) {
/*
* Note: the machine id is contained in DC2C_conf.json.
* The idea was to use this to check if the download of
* the json-file was correct. It did not work, as the
* 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
m_hw->request_ReadbackMachineID();
QThread::msleep(500);
uint8_t data[64];
memset(data, 0x00, sizeof(data));
uint8_t length = 0;
m_hw->readback_machineIDdata(&length, data);
QThread::msleep(500);
QByteArray ba((const char*)data, length);
qCritical() << length << "MACHINE ID =" << ba.toHex(':');
*/
ret = true;
} else {
qCritical() << QString("ERROR SEND JSON-FILE %1 TO DC").arg(file.fileName());
}
} 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_hw->dc_autoRequest(false);
qDebug() << "SET AUTO-REQUEST=FALSE";
QThread::sleep(1); // make sure the auto-request flag is acknowledged
return ret;
}
bool Update::updatePrinterTemplate(int templateIdx, QString jsFile) const {
return downloadJson(FileTypeJson::PRINTER, templateIdx, jsFile);
}
bool Update::updateConfig(QString jsFile) {
return downloadJson(FileTypeJson::CONFIG, 0, jsFile);
}
bool Update::updateCashConf(QString jsFile) {
return downloadJson(FileTypeJson::CASH, 0, jsFile);
}
bool Update::updateDeviceConf(QString jsFile) {
return downloadJson(FileTypeJson::DEVICE, 0, jsFile);
}

View File

@ -0,0 +1,105 @@
#ifndef UPDATE_H_INCLUDED
#define UPDATE_H_INCLUDED
#include <QObject>
#include <QString>
#include <QFile>
#include <QDir>
#include <QByteArray>
#include <QProcess>
#include <QPluginLoader>
#include <DeviceController/interfaces.h>
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
class Update : public QObject {
Q_OBJECT
hwinf *m_hw = nullptr;
char const *m_serialInterface;
char const *m_baudrate;
QString m_customerRepository;
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;
public:
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
enum class FileTypeJson {CONFIG=1, DEVICE=2, CASH=3, SERIAL=4, TIME=5, PRINTER=6};
static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn);
static bool unloadDCPlugin();
static QStringList split(QString line, QChar sep = ',');
explicit Update(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;
bool doUpdate(QStringList const &jsonFilesToDownload);
bool updatePrinterTemplate(int templateIdx, QString fname) const;
bool updateConfig(QString jsFileToSendToDC);
bool updateCashConf(QString jsFileToSendToDC);
bool updateDeviceConf(QString jsFileToSendToDC);
bool downloadJson(enum FileTypeJson type, int templateIdx,
QString jsFileToSendToDC) const;
/*
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:
static QString jsonType(enum FileTypeJson type);
bool openSerial(int br, QString baudrate, QString comPort) const;
void closeSerial() const;
bool isSerialOpen() const;
bool resetDeviceController() const;
QByteArray loadBinaryDCFile(QString filename) const;
bool downloadBinaryToDC(QString const &bFile) const;
bool updateBinary(QString const &fileToSendToDC);
QStringList getDcSoftAndHardWareVersion();
QString getFileVersion(QString const& jsonFile);
private slots:
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
void onReportDCDownloadStatus(QString const &status);
void onReportDCDownloadSuccess(QString const &msg);
void onReportDCDownloadFailure(QString const &errorMsg);
*/
};
#endif // UPDATE_H_INCLUDED