#include "worker.h" #include "update.h" #include #include #include #include #include #include #include #include #include #include #include #include "message_handler.h" #include "plugins/interfaces.h" #include "ismas/ismas_client.h" #include "apism/apism_client.h" int Worker::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; } Worker::Worker(hwinf *hw, int customerNr, int machineNr, int zoneNr, QString branchName, QString workingDirectory, bool maintenanceMode, bool dryRun, QObject *parent, char const *serialInterface, char const *baudrate) : m_workerThread("workerThread") , m_apismClient(0, 0, 0, this) // TODO , m_customerNr(customerNr) , m_customerNrStr(QString("customer_") + QString::number(m_customerNr).rightJustified(3, '0')) , m_machineNr(machineNr) , m_zoneNr(zoneNr) , m_workingDirectory(workingDirectory) , m_branchName(branchName) , m_customerRepository(QDir::cleanPath(m_workingDirectory + QDir::separator() + m_customerNrStr)) , m_gc(m_customerNrStr, m_customerRepository, m_workingDirectory, m_branchName, this) , m_maintenanceMode(maintenanceMode) , m_osVersion(getOsVersion()) , m_ismasUpdateRequests(ISMAS_UPDATE_REQUESTS) , m_waitForNewUpdates(this) { QDir::setCurrent(m_workingDirectory); qInfo() << "CURRENT TIME ..............." << QDateTime::currentDateTime().toString(Qt::ISODate); qInfo() << "OS VERSION ................." << m_osVersion; qInfo() << "CUSTOMER_NR ................" << m_customerNr; qInfo() << "CUSTOMER_NR_STR ............" << m_customerNrStr; qInfo() << "CUSTOMER_REPOSITORY_PATH ..." << QString("https://git.mimbach49.de/GerhardHoffmann/%1.git").arg(m_customerNrStr); qInfo() << "CUSTOMER_REPOSITORY ........" << m_customerRepository; qInfo() << "MACHINE_NR ................." << m_machineNr; qInfo() << "ZONE_NR ...................." << m_zoneNr; qInfo() << "BRANCH_NAME ................" << m_branchName; qInfo() << "WORKING_DIRECTORY .........." << m_workingDirectory; QProcess p; p.start("/bin/systemctl", {"restart", "apism"}); if (!p.waitForStarted(5000) || !p.waitForFinished(5000)) { qCritical() << "APISM-RESTART-FAILURE"; return; } this->moveToThread(&m_workerThread); m_workerThread.start(); int cnt = 0; while (!m_workerThread.isRunning()) { if (++cnt > 5) { qCritical() << "starting worker thread FAILED"; return; } QThread::sleep(1); } connect(&m_apismClient, SIGNAL(ismasResponseAvailable(QJsonObject)), this, SLOT(onIsmasResponseReceived(QJsonObject))); connect(this, SIGNAL(summarizeRepositoryStatus()), this, SLOT(onSummarizeRepositoryStatus()), Qt::QueuedConnection); connect(this, SIGNAL(sendCmdSendVersionToIsmas()), this, SLOT(onSendCmdSendVersionToIsmas()), Qt::QueuedConnection); connect(this, SIGNAL(summarizeUpload(QStringList)), this, SLOT(onSummarizeUpload(QStringList)), Qt::QueuedConnection); connect(this, SIGNAL(handleChangedFiles(QStringList)), this, SLOT(onHandleChangedFiles(QStringList)), Qt::QueuedConnection); connect(this, SIGNAL(finishUpdateProcess(bool)), this, SLOT(onFinishUpdateProcess(bool)), Qt::QueuedConnection); connect(this, SIGNAL(terminateUpdateProcess()), this, SLOT(onTerminateUpdateProcess()), Qt::QueuedConnection); QDir customerRepository(m_customerRepository); if (!customerRepository.exists()) { if (m_gc.gitCloneAndCheckoutBranch()) { // do nothing else, not even executing opkg-commands onFinishUpdateProcess(false); } } else { m_update = new Update(hw, m_customerRepository, m_customerNrStr, m_branchName, m_workingDirectory, dryRun, parent, serialInterface, baudrate); connect(&m_startUpdateProcess, SIGNAL(timeout()), this, SLOT(askIsmasForNewData()), Qt::QueuedConnection); m_startUpdateProcess.setSingleShot(true); m_startUpdateProcess.start(1000); connect(&m_waitForNewUpdates, SIGNAL(timeout()), this, SLOT(askIsmasForNewData()), Qt::QueuedConnection); m_waitForNewUpdates.setSingleShot(false); } } Worker::~Worker() { int cnt = 0; m_workerThread.quit(); while (!m_workerThread.isFinished()) { if (!m_workerThread.wait(1000)) { if (++cnt > 5) { qCritical() << "stopping worker thread FAILED"; return; } } } if (m_update) { delete m_update; } } QString Worker::getOsVersion() const { QString const cmd = QString("echo -n $(cat /etc/os-release | head -n 1 | cut -d'\"' -f2 | tr -d '\"')"); Command c("bash"); if (c.execute(m_workingDirectory, QStringList() << "-c" << cmd)) { return c.getCommandResult(); } return "N/A"; } void Worker::onHandleChangedFiles(QStringList changedFiles) { qCritical() << QDir::currentPath() << "ON HANDLE CHANGED FILES" << changedFiles; QString opkg_commands; static const QRegularExpression re("^.*opkg_commands\\s*$"); static const QRegularExpression comment("^\\s*#.*$"); int idx = changedFiles.indexOf(re); if (idx != -1) { m_updateStatus = UPDATE_STATUS::EXEC_OPKG_COMMANDS_REQUEST; m_statusDescription = "EXECUTE OPKG COMMANDS"; opkg_commands = changedFiles.takeAt(idx); QFile f(opkg_commands); if (f.open(QIODevice::ReadOnly)) { QTextStream in(&f); while (!in.atEnd()) { QString line = in.readLine(); if (line.indexOf(comment, 0) == -1) { // found opkg command QString opkgCommand = line.trimmed(); executeOpkgCommand(opkgCommand); } } } // m_updateStatus = UPDATE_STATUS::EXEC_OPKG_COMMANDS_SUCCESS; // m_statusDescription = QString("EXECUTE OPKG COMMANDS %1 OK").arg(opkgCommands.join('\n')); f.close(); } if (m_update->doUpdate(changedFiles)) { // first update the hardware // then sync the file-system if (QDir(m_customerRepository).exists()) { if (QDir::setCurrent(m_customerRepository)) { QString const params("--recursive " "--progress " "--checksum " "--exclude=.* " "--include=*.bin " "--include=*.json " "--include=opkg_commands " "--include=*.ini"); QStringList cmds; cmds << QString("rsync ") + params.simplified() + " etc/ /etc"; cmds << QString("rsync ") + params.simplified() + " opt/ /opt"; QString cmd; bool error = false; foreach (cmd, cmds) { if (!error) { Command c("bash"); qInfo() << "EXCUTING CMD..." << cmd; if (c.execute(m_customerRepository, QStringList() << "-c" << cmd)) { qDebug() << c.getCommandResult(); } else { qCritical() << "CMD" << cmd << "FAILED"; error = true; } } } if (!error) { onFinishUpdateProcess(true); return; } } } } onTerminateUpdateProcess(); } void Worker::onSummarizeUpload(QStringList changedFiles) { QDateTime const c = QDateTime::currentDateTime(); QDate const d = c.date(); QTime const t = c.time(); QString uploadHistoryFile = QString("upload_history_%1%2%3T%4%5%6.txt") .arg(d.year()).arg(d.month()).arg(d.day()) .arg(t.hour()).arg(t.minute()).arg(t.second()); QFile f(uploadHistoryFile); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&f); QString fName; foreach (fName, changedFiles) { QString lastCommit = m_gc.gitLastCommit(fName); out << fName << ":" << lastCommit << "\n"; } } else { // TODO: error an ISMAS } } void Worker::onSummarizeRepositoryStatus() { // TODO QString dir("/opt/app/tools/atbupdate/customer_999"); QDirIterator it(dir, QStringList() << "*.jpg", QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { qDebug() << it.next(); if (m_gc.gitIsFileTracked(it.next())) { QString lastCommit = m_gc.gitLastCommit(it.next()); } } /* QString repoStatusHistoryFile = QString("repo_status_history_%1%2%3T%4%5%6.txt") .arg(d.year()).arg(d.month()).arg(d.day()) .arg(t.hour()).arg(t.minute()).arg(t.second()); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&f); QString fName; foreach (fName, changedFiles) { QString lastCommit = m_gc.gitLastCommit(fName); out << fName << ":" << lastCommit << "\n"; } } else { // TODO: error an ISMAS } */ } void Worker::executeOpkgCommand(QString opkgCommand) { Command c(opkgCommand); if (c.execute(m_workingDirectory)) { QString const r = c.getCommandResult(); m_updateStatus = UPDATE_STATUS::EXEC_OPKG_COMMAND_SUCCESS; m_statusDescription = QString("EXECUTE OPKG COMMAND %1 OK").arg(opkgCommand); } else { m_updateStatus = UPDATE_STATUS::EXEC_OPKG_COMMAND_FAILURE; m_statusDescription = QString("EXECUTE OPKG COMMAND %1 FAILED").arg(opkgCommand); onTerminateUpdateProcess(); return; } } // sollte ParameterResponse heissen void Worker::onIsmasResponseReceived(QJsonObject ismasResponse) { if (!ismasResponse.isEmpty()) { QStringList const keys = ismasResponse.keys(); static QRegularExpression re("^REQ_ISMASPARAMETER.*"); if(keys.indexOf(re) >= 0) { m_waitForNewUpdates.stop(); // stop asking ISMAS for updates // sanity check: cust_nr and machine_nr of PSA correct ? if (keys.contains("Dev_ID", Qt::CaseInsensitive)) { QJsonObject const devId = ismasResponse["Dev_ID"].toObject(); QStringList const keys = devId.keys(); if (keys.contains("Custom_ID") && keys.contains("Device_ID")) { QJsonValue const c = devId.value("Custom_ID"); QJsonValue const m = devId.value("Device_ID"); int customerNr = c.toInt(-1); int machineNr = m.toInt(-1); if (customerNr != m_customerNr) { m_updateStatus = UPDATE_STATUS::ISMAS_UPDATE_REQUEST_FAILURE; m_statusDescription = QString("CUSTOMER-NR (%1) != LOCAL CUSTOMER-NR (%2)") .arg(customerNr).arg(m_customerNr); return; } if (machineNr != m_machineNr) { m_statusDescription = QString("MACHINE-NR (%1) != LOCAL MACHINE-NR (%2)") .arg(machineNr).arg(m_machineNr); m_updateStatus = UPDATE_STATUS::ISMAS_UPDATE_REQUEST_FAILURE; return; } } } // TODO: check if zone_nr is correct if (keys.contains("Fileupload", Qt::CaseInsensitive)) { QJsonObject fileUpload = ismasResponse["Fileupload"].toObject(); QJsonValue v = fileUpload.value("TRG"); if (!v.isNull() && !v.isUndefined()) { QString const s = v.toString(""); if (s == "WAIT") { m_updateStatus = UPDATE_STATUS::ISMAS_UPDATE_REQUEST_SUCCESS; m_ismasUpdateRequests = ISMAS_UPDATE_REQUESTS; m_statusDescription = "ISMAS UPDATES AVAILABLE"; emit m_gc.ismasUpdatesAvailable(); } } } else { m_updateStatus = UPDATE_STATUS::ISMAS_UPDATE_REQUEST_FAILURE; m_statusDescription = "NO FILEUPLOAD KEY AVAILABLE"; return; } } } else { m_updateStatus = UPDATE_STATUS::ISMAS_UPDATE_REQUEST_FAILURE; m_statusDescription = "NO ISMAS RESPONSE AVAILABLE (EMPTY)"; } } void Worker::onFinishUpdateProcess(bool changes) { qCritical() << "ON FINISH UPDATE PROCESS. CHANGES=" << changes; m_workerThread.quit(); QApplication::quit(); } void Worker::onTerminateUpdateProcess() { qCritical() << "ON TERMINATE UPDATE PROCESS"; m_workerThread.quit(); QApplication::quit(); } void Worker::onSendCmdSendVersionToIsmas() { QString const tariffVersion = "0.0.1"; QString const tariffProject = "test_project"; int tariffZone = 1; QString const tariffInfo = "test_tariff_info"; QString const tariffLoadTime = QDateTime::currentDateTime().toString(Qt::ISODateWithMs); // QString const linuxVersion = "test_linux_version"; QString const cpuSerial = "test_cpu_serial"; QString const deviceControllerVersion = "test_dc_version"; QString const deviceControllerGitBlob = "test_dc_blob_2a3b4f50"; QString const deviceControllerGitLastCommit = "test_dc_commit_12345abc"; QString const raucVersion = "test_rauc_version"; QString const opkgVersion = "test_opkg_version"; QString const atbQTVersion = "test_qtbqt_version"; QString const atbQTGitDescribe = "test_atbqt_git_describe"; QString const deviceControllerPluginVersion = "test_CAmaster_version"; QString const ingenicoISelfCCPluginVersion = "test_ingenico_plugin_version"; QString const mobilisisCalculatePricePluginVersion = "test_mobilisis_plugin_version"; QString const mobilisisCalculatePriceConfigUiVersion = "test_mobilisis_config_ui_plugin"; QString const prmCalculatePricePluginVersion = "test_prm_calculate_price_plugin"; QString const prmCalculatePriceConfigUiPluginVersion = "test_prm_calculate_price_config_ui_plugin"; QString const tcpZVTPluginVersion = "test_tcp_zvt_plugin"; QString data = m_ismasClient.updateOfPSASendVersion(tariffVersion, tariffProject, tariffZone, tariffInfo, tariffLoadTime, m_osVersion, cpuSerial, deviceControllerVersion, deviceControllerGitBlob, deviceControllerGitLastCommit, raucVersion, opkgVersion, atbQTVersion, atbQTGitDescribe, deviceControllerPluginVersion, ingenicoISelfCCPluginVersion, mobilisisCalculatePricePluginVersion, mobilisisCalculatePriceConfigUiVersion, prmCalculatePricePluginVersion, prmCalculatePriceConfigUiPluginVersion, tcpZVTPluginVersion); m_apismClient.sendCmdSendVersionToIsmas(data); } void Worker::askIsmasForNewData() { if (m_maintenanceMode) { m_updateStatus = UPDATE_STATUS::ISMAS_EMULATE_DATA_AVAILABLE; QString data = m_ismasClient.setUpdatesAvailable(); m_apismClient.emulateUpdatesAvailable(data); } m_updateStatus = UPDATE_STATUS::ISMAS_UPDATE_REQUEST_PENDING; m_apismClient.requestAvailableIsmasUpdates(); if (--m_ismasUpdateRequests > 0) { // if the timer is already running, it will be stopped and restarted. m_waitForNewUpdates.start(10000); m_updateStatus = UPDATE_STATUS::ISMAS_UPDATE_REQUEST_PENDING; } else { m_updateStatus = UPDATE_STATUS::ISMAS_UPDATE_REQUEST_TIMEOUT; onTerminateUpdateProcess(); } }