Compare commits

..

10 Commits

16 changed files with 2191 additions and 1750 deletions

View File

@ -3,9 +3,11 @@ QT += core
QT += widgets serialport
QT += network
TARGET = up_dev_ctrl
TARGET = ATBUpdateDC
CONFIG += c++11
INCLUDEPATH += plugins
CONFIG += c++17
# CONFIG -= app_bundle
# DEFINES+=LinuxDesktop
@ -18,11 +20,11 @@ QMAKE_CXXFLAGS += -Wno-deprecated-copy
# subtree.depends =
# QMAKE_EXTRA_UNIX_TARGETS += subtree
! exists(DCPlugin) {
$$system("git subtree add --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash")
} else {
# ! exists(DCPlugin) {
# $$system("git subtree add --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash")
# } else {
# $$system("git subtree pull --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash")
}
# }
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
@ -52,20 +54,27 @@ contains( CONFIG, DesktopLinux ) {
SOURCES += \
main.cpp \
update.cpp \
message_handler.cpp
message_handler.cpp \
worker.cpp \
worker_thread.cpp
HEADERS += \
update.h \
message_handler.h \
DCPlugin/include/interfaces.h
worker.h \
worker_thread.h \
plugins/interfaces.h
OTHER_FILES += \
/opt/app/tools/atbupdate/update_log.csv
/opt/app/tools/atbupdate/update_log.csv \
main.cpp.bck \
main.cpp.bck2 \
main.cpp.bck3
# https://blog.developer.atlassian.com/the-power-of-git-subtree/?_ga=2-71978451-1385799339-1568044055-1068396449-1567112770
# git subtree add --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash
# git subtree pull --prefix DCPlugin https://git.mimbach49.de/GerhardHoffmann/DCPlugin.git master --squash
include(./DCPlugin/DCPlugin.pri)
# include(./DCPlugin/DCPlugin.pri)
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin

Binary file not shown.

View File

@ -2,6 +2,7 @@
\usepackage{euler}
\usepackage[english]{babel}
\usepackage{lipsum}
\usepackage{multirow}
\usepackage[colorlinks=true, urlcolor=blue, linkcolor=red]{hyperref}
\newcounter{Chapcounter}
\newcommand\showmycounter{\addtocounter{Chapcounter}{1}\themycounter}
@ -19,44 +20,58 @@
\begin{document}
\maketitle
\chapter{Introduction}
\section{Motivation}
The two main components of a PSA are
\begin{itemize}
\item PTU software.
\item Device controller (DC) firmware.
\end{itemize}
While the DC firmware is basically the same for each PSA (even for different
customers), the PTU software is highly dependent on customer requirements.\par
Hence, each customer is assigned an own git-repository, which will be loaded
("cloned") on the PSA when configuring the machine for the first time.\par
Two special tools, the {\bf UpdateController} (a \href{https://doc.qt.io/qt-5/}{Qt}
binary [{\bf \nameref{UpdateTool}}]) and the {\bf UpdateScript}
(a \href{https://www.gnu.org/software/bash/manual/bash.html}{bash}
script [{\bf \nameref{UpdateScript}}]), work together to finish a PSA installation.\par
% \chapter{Introduction}
\section{Motivation}
The two main components of a PSA are
\begin{itemize}
\item PTU software.
\item Device controller (DC) firmware.
\end{itemize}
While the DC firmware is basically the same for each PSA (even for different
customers), the PTU software is highly dependent on customer requirements.\par
Hence, each customer is assigned an own git-repository, which will be loaded
("cloned") on the PSA when configuring the machine for the first time.\par
Two special tools, the {\bf UpdateController} (a \href{https://doc.qt.io/qt-5/}{Qt}
binary [{\bf \nameref{UpdateTool}}]) and the {\bf UpdateScript}
(a \href{https://www.gnu.org/software/bash/manual/bash.html}{bash}
script [{\bf \nameref{UpdateScript}}]), work together to finish a PSA installation.\par
\section{PSA: Initial configuration}
For the initial configuration, a PSA loads a customer-specific git-repository,
which structure is detailed below [{\bf \nameref{repostructure}}].\par
The "git clone" for the repository is done by the UpdateScript [{\bf \nameref{UpdateScript}}].
It updates the file
\begin{center}
\fbox{
/opt/app/tools/atbupdate/update\_log.csv
}
\end{center}
which will be interpreted by the UpdateController [{\bf \nameref{UpdateTool}}].
The structure of {\bf update\_log.csv} is detailed below [{\bf \nameref{updatelogcsv}}].\par
Each line of {\bf update\_log.csv} represents a command for the UpdateController,
which will either download certain files to the DC or execute some
\href{https://openwrt.org/docs/guide-user/additional-software/opkg}{opkg}
commands [{\bf \nameref{opkg}}].
\section{PSA: Initial configuration}
For the initial configuration, a PSA loads a customer-specific git-repository,
which structure is detailed below [{\bf \nameref{repostructure}}].\par
The "git clone" for the repository is done by the UpdateScript [{\bf \nameref{UpdateScript}}].
It updates the file
\begin{center}
\fbox{
/opt/app/tools/atbupdate/update\_log.csv
}
\end{center}
which will be interpreted by the UpdateController [{\bf \nameref{UpdateTool}}].
The structure of [{\bf \nameref{updatelogcsv}}] is detailed below.\par
Each line of update\_log.csv represents a command for the UpdateController,
which will either download certain files to the DC or execute some
\href{https://openwrt.org/docs/guide-user/additional-software/opkg}{opkg}
commands [{\bf \nameref{opkg}}].
\section{PSA: Update}
\section{PSA: Update}
The update of a PSA is basically managed by \href{https://git-scm.com/}{git}, a
free and open source distributed version control system. Like many other Version
Control Systems, git has a way to fire off custom scripts when certain important
actions occur, so-called \href{https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks}{hooks}.
The hooks used for a PSA update are called {\bf post-checkout} and {\bf post-merge}.\par
The post-checkout hook is called when checking out some branch of the customer-repository
(for instance when cloning the customer repository for the first time). In contrast,
the post-merge hook is called when a \href{https://www.git-scm.com/docs/git-pull}{git pull}
operation has been issued (more precisely, a \href {https://www.git-scm.com/docs/git-fetch}{git-fetch}
followed by a \href{https://www.git-scm.com/docs/git-merge}{git-merge}).\par
Both hooks update the file [{\bf \nameref{updatelogcsv}}], which is interpreted
by the UpdateController in a second step.
\newpage
\chapter{Update-Tool "up\_dev\_ctrl"}
% \chapter{Update-Tool "up\_dev\_ctrl"}
\section{up\_dev\_ctrl}
\label{UpdateTool}
@ -79,7 +94,7 @@
\newpage
\chapter{Update-Script "update\_psa"}
% \chapter{Update-Script "update\_psa"}
\section{update\_psa}
\label{UpdateScript}
@ -98,13 +113,60 @@
\end{itemize}
\newpage
\chapter{Annex}
%\chapter{Annex}
\section{Structure of a customer git-repository}
\label{repostructure}
\subsection{The post-checkout hook}
TODO: checkout\_history
\subsection{The post-merge hook}
They are both located under the {\bf .githooks}-directory
\section{Structure of "update\_log.csv"}
\label{updatelogcsv}
\section{update\_log.csv}
\label{updatelogcsv}
\begin{table}[h!]
\begin{center}
\caption{Initial state of update\_log.csv}
\label{tab:initial_state}
\begin{tabular}{llll}
\textbf{Request} & \textbf{Name} & \textbf{Date} & \textbf{Status}\\
\hline
\multicolumn{4}{c}{}\\
DOWNLOAD & /etc/dc/dc2c4.21.bin & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print01.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print02.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print03.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print04.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print29.json & 2023-05-01T12:00:00 & N/A\\
DOWNLOAD & /etc/psa\_config/DC2C\_print32.json & 2023-05-01T12:00:00 & N/A\\
EXECUTE & opkg update & 2023-05-01T12:00:00 & N/A\\
\end{tabular}
\end{center}
\end{table}
\begin{table}[h!]
\begin{center}
\caption{State of update\_log.csv after updating}
\label{tab:initial_state}
\begin{tabular}{llll}
\textbf{Request} & \textbf{Name} & \textbf{Date} & \textbf{Status}\\
\hline
\multicolumn{4}{c}{}\\
DONE & /etc/dc/dc2c4.21.bin & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print01.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print02.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print03.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print04.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print29.json & 2023-05-01T12:00:00 & N/A\\
DONE & /etc/psa\_config/DC2C\_print32.json & 2023-05-01T12:00:00 & N/A\\
DONE & opkg update & 2023-05-01T12:00:00 & N/A\\
\end{tabular}
\end{center}
\end{table}
\newpage
\section{The package manager "opkg"}
\label{opkg}
\section{Known problems}
\end{document}

View File

@ -5,8 +5,7 @@
#include <QFileInfo>
#include "message_handler.h"
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include "plugins/interfaces.h"
#include <unistd.h>
#include <thread>
@ -17,6 +16,10 @@
#include <QDir>
#include "update.h"
#include "worker_thread.h"
#include "worker.h"
#include <thread>
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
@ -24,20 +27,13 @@
#define SERIAL_PORT "ttyUSB0"
#endif
class Work : public QRunnable {
QString m_update_ctrl_file;
QString m_workingDir;
public:
explicit Work(QString update_ctrl_file, QString workingDir)
: m_update_ctrl_file(update_ctrl_file)
, m_workingDir(workingDir) {
}
void run() {
Update m_update(m_update_ctrl_file, m_workingDir);
// if (m_update.doUpdate()) {
// }
}
};
static void doWork(QString update_ctrl_file, QString workingDir) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
//Update update(update_ctrl_file, workingDir);
//update.doUpdate();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
QCoreApplication::quit();
}
// argv[1]: file to send to dc
int main(int argc, char *argv[]) {
@ -54,15 +50,29 @@ int main(int argc, char *argv[]) {
qputenv("XDG_RUNTIME_DIR", "/run/user/0");
}
QString rtPath = QCoreApplication::applicationDirPath();
QString plugInDir(rtPath +(rtPath.endsWith("/") ? "" : "/") + "plugins");
if (!QDir(plugInDir).exists()) {
qCritical() << plugInDir
<< "does not exists, but has to contain dc-library";
exit(-1);
}
qInfo() << "pwd" << "=" << rtPath;
qInfo() << "plugInDir" << "=" << plugInDir;
hwinf *hw = Update::loadDCPlugin(QDir(plugInDir), "libCAmaster.so");
QString const update_ctrl_file = "/opt/app/tools/atbupdate/update_log.csv";
QString const workingDir = (argc == 2) ? argv[1] : ".";
Update update(hw, update_ctrl_file, workingDir);
Work work(update_ctrl_file, workingDir);
work.setAutoDelete(false);
QThreadPool *threadPool = QThreadPool::globalInstance();
threadPool->start(&work);
if (!threadPool->waitForDone()) {
return -1;
}
return 0;
std::thread t(doWork, update_ctrl_file, workingDir);
int ret = a.exec();
t.join();
return ret;
}

81
main.cpp.bck Normal file
View File

@ -0,0 +1,81 @@
#include <QCoreApplication>
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QFileInfo>
#include "message_handler.h"
#include "plugins/interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include <unistd.h>
#include <thread>
#include <memory>
static void updateBinary(std::unique_ptr<hwinf> hw, // update d2dc*.bin
char const *fileToSendToDC,
char const *baudrate,
char const *serialInterface) {
for (int i=0; i < 1;++i) {
hw->dc_updateDC(fileToSendToDC, baudrate, serialInterface);
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
}
QCoreApplication::quit();
}
static void updatePrinterConf(std::unique_ptr<hwinf> hw, // update printer-file
char const *fileToSendToDC,
char const *baudrate,
char const *serialInterface) {
for (int i=0; i < 1;++i) {
hw->dc_updateDC(fileToSendToDC, baudrate, serialInterface);
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
}
QCoreApplication::quit();
}
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(QtMsgType::QtDebugMsg);
//setDebugLevel(QtMsgType::QtDebugMsg);
}
if (argc > 2) {
qCritical() << "Usage: " << argv[0] << "<file to send to dc>";
return -1;
}
std::unique_ptr<hwinf> hw(new hwapi());
QFileInfo fileInfo(argv[1]);
QString fname(fileInfo.fileName());
int ret = 0;
if (fname.startsWith("dc") && fname.endsWith(".bin")) {
#ifdef PTU5
std::thread t(updateBinary, std::move(hw),
fname.toStdString().c_str(), "115200", "ttymxc2");
#else
std::thread t(updateBinary, std::move(hw),
fname.toStdString().c_str(), "115200", "ttyUSB0");
#endif
ret = a.exec();
t.join();
} else
if (fname.startsWith("DC") && fname.endsWith(".json")) {
#ifdef PTU5
std::thread t(updatePrinterConf, std::move(hw),
fname.toStdString().c_str(), "115200", "ttymxc2");
#else
std::thread t(updatePrinterConf, std::move(hw),
fname.toStdString().c_str(), "115200", "ttyUSB0");
#endif
ret = a.exec();
t.join();
}
return ret;
}

54
main.cpp.bck2 Normal file
View File

@ -0,0 +1,54 @@
#include <QCoreApplication>
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QFileInfo>
#include "message_handler.h"
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include <unistd.h>
#include <thread>
#include <memory>
#include <QSharedMemory>
#include <QRunnable>
#include <QThreadPool>
#include <QDir>
#include "update.h"
#include "worker_thread.h"
#include "worker.h"
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
// argv[1]: file to send to dc
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(QtMsgType::QtDebugMsg);
//setDebugLevel(QtMsgType::QtDebugMsg);
}
QByteArray const value = qgetenv("XDG_RUNTIME_DIR");
if (value.size() == 0) {
qputenv("XDG_RUNTIME_DIR", "/run/user/0");
}
QString const update_ctrl_file = "/opt/app/tools/atbupdate/update_log.csv";
QString const workingDir = (argc == 2) ? argv[1] : ".";
Worker worker(update_ctrl_file, workingDir);
qCritical() << "starting main event loop";
int ret = a.exec();
qCritical() << "stopping main event loop" << ret;
return ret;
}

62
main.cpp.bck3 Normal file
View File

@ -0,0 +1,62 @@
#include <QCoreApplication>
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QFileInfo>
#include "message_handler.h"
#include "plugins/interfaces.h"
#include <unistd.h>
#include <thread>
#include <memory>
#include <QSharedMemory>
#include <QRunnable>
#include <QThreadPool>
#include <QDir>
#include "update.h"
#include "worker_thread.h"
#include "worker.h"
#include <thread>
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif
static void doWork(QString update_ctrl_file, QString workingDir) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
Update update(update_ctrl_file, workingDir);
update.doUpdate();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
QCoreApplication::quit();
}
// argv[1]: file to send to dc
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(QtMsgType::QtDebugMsg);
//setDebugLevel(QtMsgType::QtDebugMsg);
}
QByteArray const value = qgetenv("XDG_RUNTIME_DIR");
if (value.size() == 0) {
qputenv("XDG_RUNTIME_DIR", "/run/user/0");
}
// QString const update_ctrl_file = "/opt/app/tools/atbupdate/update_log.csv";
// QString const workingDir = (argc == 2) ? argv[1] : ".";
// std::thread t(doWork, update_ctrl_file, workingDir);
int ret = a.exec();
// t.join();
return ret;
}

View File

@ -3,8 +3,7 @@
#include <QDateTime>
#include <cstring>
#define OUTPUT_LEN (20)
#define OUTPUT_LEN (512)
static bool installedMsgHandler = false;
static QtMsgType debugLevel = QtInfoMsg;
@ -41,49 +40,10 @@ QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
/// return app.exec();
/// }
///
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
void atbDebugOutput(QtMsgType type, const char *msg) {
switch (type) {
case QtDebugMsg: {
if (debugLevel <= QtDebugMsg) {
fprintf(stderr, "%*.*s Debug: %s\n", OUTPUT_LEN, OUTPUT_LEN,
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
}
} break;
case QtInfoMsg: {
if (debugLevel <= QtInfoMsg) {
fprintf(stderr, "%*.*s Info: %s\n", OUTPUT_LEN, OUTPUT_LEN,
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
}
} break;
case QtWarningMsg: {
if (debugLevel <= QtWarningMsg) {
fprintf(stderr, "%*.*s Warning: %s\n", OUTPUT_LEN, OUTPUT_LEN,
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
}
} break;
case QtCriticalMsg: {
if (debugLevel <= QtCriticalMsg) {
fprintf(stderr, "%*.*s Critical: %s\n", OUTPUT_LEN, OUTPUT_LEN,
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
}
} break;
case QtFatalMsg: {
if (debugLevel <= QtFatalMsg) {
fprintf(stderr, "%*.*s Fatal: %s\n", OUTPUT_LEN, OUTPUT_LEN,
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
}
// abort();
} break;
default: {
fprintf(stderr, "%*.*s No ErrorLevel defined! %s\n", OUTPUT_LEN, OUTPUT_LEN,
QDateTime::currentDateTime().toString(Qt::ISODate).toStdString().c_str(), msg);
}
}
}
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
static constexpr const char *format = "dd.MM.yyyy hh:mm:ss";
static constexpr const char *format = "hh:mm:ss";
// static constexpr const char *format = "dd.MM.yyyy hh:mm:ss";
QByteArray localMsg = msg.toLocal8Bit();
const char *file = context.file ? context.file : "";
const char *function = context.function ? context.function : "";
@ -97,54 +57,65 @@ void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QSt
}
qint64 const currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();
int const fractional_part = currentMSecsSinceEpoch % 1000;
char buf[OUTPUT_LEN]{};
memset(buf, 0x00, sizeof(buf));
QDateTime const datetime = QDateTime::fromMSecsSinceEpoch(currentMSecsSinceEpoch);
switch (type) {
case QtDebugMsg: {
if (debugLevel <= QtDebugMsg) {
// fprintf(stderr, "%*.*s CTX %s (%s:%u) ->\n", OUTPUT_LEN, OUTPUT_LEN,
// "", function, file, context.line);
//fprintf(stderr, "%*.*s.%03d DEBG %s\n", OUTPUT_LEN, OUTPUT_LEN,
// datetime.toString(format).toStdString().c_str(), fractional_part,
// localMsg.constData());
fprintf(stderr, "%*.*s.%03d DEBUG %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
datetime.toString(format).toStdString().c_str(), fractional_part,
localMsg.constData(), file, context.line);
if (debugLevel == QtDebugMsg) {
snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d DEBG %s\n",
function, file, context.line,
datetime.time().toString(format).toStdString().c_str(),
fractional_part,
localMsg.constData());
}
} break;
case QtInfoMsg: {
if (debugLevel <= QtInfoMsg) {
fprintf(stderr, "%*.*s.%03d INFO %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
datetime.toString(format).toStdString().c_str(), fractional_part,
localMsg.constData(), file, context.line);
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg) {
snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d INFO %s\n",
function, file, context.line,
datetime.time().toString(format).toStdString().c_str(),
fractional_part,
localMsg.constData());
}
} break;
case QtWarningMsg: {
if (debugLevel <= QtWarningMsg) {
fprintf(stderr, "%*.*s.%03d WARN %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
datetime.toString(format).toStdString().c_str(), fractional_part,
localMsg.constData(), file, context.line);
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg || debugLevel == QtWarningMsg) {
snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d WARN %s\n",
function, file, context.line,
datetime.time().toString(format).toStdString().c_str(),
fractional_part,
localMsg.constData());
}
} break;
case QtCriticalMsg: {
if (debugLevel <= QtCriticalMsg) {
fprintf(stderr, "%*.*s.%03d CRIT %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
datetime.toString(format).toStdString().c_str(), fractional_part,
localMsg.constData(), file, context.line);
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg
|| debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg) {
snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d CRIT %s\n",
function, file, context.line,
datetime.time().toString(format).toStdString().c_str(),
fractional_part,
localMsg.constData());
}
} break;
case QtFatalMsg: {
if (debugLevel <= QtFatalMsg) {
fprintf(stderr, "%*.*s.%03d FATAL %s (%s:%u)\n", OUTPUT_LEN, OUTPUT_LEN,
datetime.toString(format).toStdString().c_str(), fractional_part,
localMsg.constData(), file, context.line);
if (debugLevel == QtInfoMsg || debugLevel == QtDebugMsg
|| debugLevel == QtWarningMsg || debugLevel == QtCriticalMsg
|| debugLevel == QtFatalMsg) {
snprintf(buf, sizeof(buf)-1, "%30.30s (%20.20s:%04u) %s.%03d FATAL %s\n",
function, file, context.line,
datetime.time().toString(format).toStdString().c_str(),
fractional_part,
localMsg.constData());
}
} break;
default: {
fprintf(stderr, "%*.*s.%03d No ErrorLevel defined! %s\n", OUTPUT_LEN, OUTPUT_LEN,
datetime.toString(format).toStdString().c_str(), fractional_part,
datetime.time().toString(format).toStdString().c_str(), fractional_part,
msg.toStdString().c_str());
}
}
fprintf(stderr, "%s\n", buf);
}
#endif

File diff suppressed because it is too large Load Diff

BIN
plugins/libCAmaster.so Normal file

Binary file not shown.

View File

@ -8,43 +8,82 @@
#include <QTextStream>
#include <QRegularExpression>
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include "plugins/interfaces.h"
#include <QSharedMemory>
#include <QScopedPointer>
#include <QProcess>
#include <QDir>
#include <QThread>
#include <QDateTime>
#include <QPluginLoader>
#define COLUMN_REQUEST (0)
#define COLUMN_NAME (1)
#define COLUMN_DATE_TIME (2)
#define COLUMN_RESULT (3)
void ScopedPointerCustomDeleter::cleanup(Update *update) {
if (update->m_delete) {
delete update;
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);
static QPluginLoader pluginLoader(pluginLibName);
if (!pluginLoader.load()) {
qCritical() << "in directory" << plugInDir.absolutePath();
qCritical() << "cannot load plugin" << pluginLoader.fileName();
qCritical() << pluginLoader.errorString();
exit(-1);
}
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;
}
Update::Update(QString update_ctrl_file,
Update::Update(hwinf *hw,
QString update_ctrl_file,
QString workingDir,
QObject *parent,
hwinf *hw,
char const *serialInterface,
char const *baudrate)
: QObject(parent)
, m_hw(hw != nullptr ? hw : new hwapi())
, m_hw(hw)
, m_serialInterface(serialInterface)
, m_baudrate(baudrate)
, m_update_ctrl_file(update_ctrl_file)
, m_update_ctrl_file_copy(update_ctrl_file + ".copy")
, m_workingDir(workingDir)
, m_init(true)
, m_delete(hw == nullptr) {
, m_init(true) {
// qCritical() << "workingDir" << m_workingDir;
// m_hw->dc_autoRequest(false);
return;
execUpdateScript();
if (!m_update_ctrl_file.exists()) {
@ -67,6 +106,8 @@ Update::Update(QString update_ctrl_file,
m_init = false;
}
qDebug() << "Opened" << m_update_ctrl_file_copy.fileName();
//QApplication::processEvents();
}
Update::~Update() {
@ -78,6 +119,14 @@ bool Update::execUpdateScript() {
update_psa += m_workingDir;
qCritical() << "update_psa: " << update_psa;
//QApplication::processEvents();
//for (int i=0;i<10;++i) {
// QThread::sleep(1);
//QApplication::processEvents();
//}
// debug
return true;
//QStringList const params(QStringList() << "-c" << update_psa);
@ -102,19 +151,248 @@ bool Update::execUpdateScript() {
return false;
}
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;
}
Update::DownloadResult Update::sendNextAddress(int bNum) const {
// sends address only if blockNumber is one of 0, 1024, 2048, 3072, 4096
int noAnswerCount = 0;
int errorCount = 0;
if ( bNum==0 || bNum==1024 || bNum==2048 || bNum==3072 || bNum==4096 ) {
qDebug() << "addr-block" << bNum << "...";
while (noAnswerCount <= 250) {
m_hw->bl_sendAddress(bNum);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
DownloadResult const res = sendStatus(m_hw->bl_wasSendingAddOK());
if (res != DownloadResult::NOP) {
if (res == DownloadResult::ERROR) {
if (++errorCount >= 10) {
qCritical() << "addr-block" << bNum << "...FAILED";
return res;
}
} else { // res == DownloadResult::OK
qInfo() << "addr-block" << bNum << "...OK";
return res;
}
} else {
noAnswerCount += 1; // no answer by now
}
}
// wait max. about 3 seconds
return DownloadResult::TIMEOUT;
}
// blockNumber is not one of 0, 1024, 2048, 3072, 4096 -> do nothing
return DownloadResult::NOP;
}
Update::DownloadResult Update::sendNextDataBlock(QByteArray const &binary,
int bNum) const {
uint8_t local[66];
int const bAddr = bNum * 64;
int noAnswerCount = 0;
int errorCount = 0;
memcpy(local, binary.constData() + bAddr, 64);
local[64] = local[65] = 0x00;
//for (int i=0; i<4; ++i) {
// printf("%04d ", bNum);
// for (int j=0; j < 16; ++j) {
// printf("%02x ", local[i*16 + j]);
// } printf("\n");
//}
while (noAnswerCount <= 250) {
m_hw->bl_sendDataBlock(64, local);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
DownloadResult const res = sendStatus(m_hw->bl_wasSendingDataOK());
if (res != DownloadResult::NOP) {
if (res == DownloadResult::ERROR) {
if (++errorCount >= 10) {
qCritical() << "data for block" << bNum << "...FAILED";
return res;
}
} else {
qInfo() << "data for block" << bNum << "OK";
return res;
}
} else {
noAnswerCount += 1; // no answer by now
}
}
// wait max. about 3 seconds
return DownloadResult::TIMEOUT;
}
Update::DownloadResult Update::dc_downloadBinary(QByteArray const &b) const {
int const nBlocks = (((b.size())%64)==0) ? (b.size()/64) : (b.size()/64)+1;
qInfo() << "total number of bytes to send to dc" << b.size();
qInfo() << "total number of blocks to send to dc" << nBlocks;
int bNum = 0;
DownloadResult res = DownloadResult::OK;
while (res != DownloadResult::ERROR && bNum < nBlocks) {
if ((res = sendNextAddress(bNum)) != DownloadResult::ERROR) {
if ((res = sendNextDataBlock(b, bNum)) != DownloadResult::ERROR) {
bNum += 1;
}
}
}
qInfo() << "nBlocks" << nBlocks;
//if (res != DownloadResult::ERROR) {
// always send last block, even when there are no data !!!
int const rest = b.size() % 64;
int const offset = b.size() - rest;
char const *startAddress = b.constData() + offset;
uint8_t local[66];
memset(local, 0x00, sizeof(local));
if (rest > 0) {
memcpy(local, startAddress, rest);
}
//for (int i=0; i<4; ++i) {
// printf("*** %04d ", bNum);
// for (int j=0; j < 16; ++j) {
// printf("%02x ", local[i*16 + j]);
// } printf("\n");
//}
// bl_sendLastBlock(local);
m_hw->bl_sendLastBlock();
qInfo() << "last result" << (int)sendStatus(m_hw->bl_wasSendingDataOK());
return res;
}
bool Update::startBootloader() const {
qDebug() << "starting bootloader...";
int nTry = 5;
while (--nTry >= 0) {
m_hw->bl_startBL();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
m_hw->bl_checkBL();
if (m_hw->bl_isUp()) {
qInfo() << "starting bootloader...OK";
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return true;
}
}
qCritical() << "starting bootloader...FAILED";
return false;
}
bool Update::stopBootloader() const {
qDebug() << "stopping bootloader...";
int nTry = 5;
while (--nTry >= 0) {
m_hw->bl_stopBL();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
if (!m_hw->bl_isUp()) {
qInfo() << "stopping bootloader...OK";
return true;
}
}
qCritical() << "stopping bootloader...FAILED";
return false;
}
// br is a index into a table, used for historical reasons.
bool Update::openSerial(int br, QString baudrate, QString comPort) const {
qDebug() << "opening serial" << br << baudrate << comPort << "...";
if (m_hw->dc_openSerial(br, baudrate, comPort, 1)) { // 1 for connect
qInfo() << "opening serial" << br << baudrate << comPort << "...OK";
return true;
}
qCritical() << "opening serial" << br << baudrate << comPort << "...FAILED";
return false;
}
void Update::closeSerial() const {
m_hw->dc_closeSerial();
}
bool Update::resetDeviceController() const {
qDebug() << "resetting device controller...";
//if (stopBootloader()) { // first stop a (maybe) running bootloader
// std::this_thread::sleep_for(std::chrono::milliseconds(1000));
m_hw->bl_rebootDC();
// wait maximally 3 seconds, before starting bootloader
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
qInfo() << "resetting device controller...OK";
return true;
//}
//qCritical() << "stopping bootloader...FAILED";
//return false;
}
QByteArray Update::loadBinaryDCFile(QString filename) const {
qDebug() << "loading dc binary" << filename << "...";
QFile file(filename); // closed in destructor call
if (!file.exists()) {
qCritical() << file.fileName() << "does not exist";
return QByteArray();
}
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "cannot open file" << file.fileName();
return QByteArray();
}
qInfo() << "loading dc binary" << filename << "...OK";
return file.readAll();
}
bool Update::downloadBinaryToDC(QString const &bFile) const {
qDebug() << "sending" << bFile << "to dc...";
QByteArray const dcBinary = loadBinaryDCFile(bFile);
if (dcBinary.size() > 0) {
if (dc_downloadBinary(dcBinary) != DownloadResult::OK) {
qCritical() << "sending" << bFile << "to dc...FAILED";
return false;
} else {
qInfo() << "sending" << bFile << "to dc...OK";
}
} else {
qCritical() << "sending" << bFile << "to dc...FAILED";
qCritical() << "loading binary" << bFile << "FAILED";
return false;
}
return true;
}
bool Update::updateBinary(char const *fileToSendToDC) {
// return true; // debug
return m_hw->dc_updateDC(fileToSendToDC, m_baudrate, m_serialInterface);
QFile fn(fileToSendToDC);
bool r;
if ((r = fn.exists()) == true) {
QString const linkTarget = fn.symLinkTarget();
qCritical() << "updating binary (dc): link target" << linkTarget;
// debug
//r = m_hw->dc_updateDC(linkTarget, m_baudrate, m_serialInterface);
qCritical() << "updating binary (dc): "
<< linkTarget << ((r == true) ? "OK" : "ERROR");
} else {
qCritical() << "symlink" << fileToSendToDC << "does not exist";
}
return r;
}
bool Update::updatePrinterConf(int nrOfTemplate, char const *fileToSendToDC) {
// return true; // debug
qCritical() << "updating printer template: " << fileToSendToDC;
return true; // debug
QVector<int> printTemplates{ nrOfTemplate };
QVector<QString> filesToSend{ fileToSendToDC };
return m_hw->dc_updatePrinterTemplate(hwapi::FileTypeJson::PRINTER,
printTemplates, filesToSend,
QString(m_baudrate),
QString(m_serialInterface));
//return m_hw->dc_updatePrinterTemplate(hwapi::FileTypeJson::PRINTER,
// printTemplates, filesToSend,
// QString(m_baudrate),
// QString(m_serialInterface));
}
QStringList Update::getOpenLines() {
@ -167,7 +445,6 @@ bool Update::doUpdate() {
#
*/
// qCritical() << "Device Controller SW-version" << m_hw->dc_getSWversion();
if (!m_init) {
return false;
@ -193,19 +470,19 @@ bool Update::doUpdate() {
QString const &result = lst[COLUMN_RESULT];
if ((!request.contains("DOWNLOAD") && !request.contains("EXECUTE")) ||
!result.contains("N/A")) {
qCritical() << "Parsing error for" << m_update_ctrl_file.fileName();
qCritical() << "parsing error for" << m_update_ctrl_file.fileName();
return false;
}
if (name.contains("dc2c") && name.endsWith(".bin")) {
if ((res = updateBinary(name.toStdString().c_str())) == true) {
qInfo() << "Downloaded" << name;
qInfo() << "downloaded binary" << name;
}
} else
if (name.contains("DC2C_print") && name.endsWith(".json")) {
int i = name.indexOf("DC2C_print");
int templateIdx = name.mid(i).midRef(10, 2).toInt();
if ((res = updatePrinterConf(templateIdx, name.toStdString().c_str())) == true) {
qInfo() << "Downloaded" << name;
qInfo() << "downloaded printer template" << name;
}
} else
if (name.contains("opkg")) {

View File

@ -4,9 +4,10 @@
#include <QObject>
#include <QString>
#include <QFile>
#include <QDir>
#include <QByteArray>
#include "interfaces.h"
#include "DCPlugin/include/hwapi.h"
#include "plugins/interfaces.h"
#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
@ -15,9 +16,6 @@
#endif
class Update;
struct ScopedPointerCustomDeleter {
static void cleanup(Update *pointer);
};
// TODO: check hardware compatibility
// TODO: opkg commandos
@ -25,7 +23,7 @@ struct ScopedPointerCustomDeleter {
class Update : public QObject {
Q_OBJECT
QScopedPointer<hwinf> m_hw;
hwinf *m_hw;
char const *m_serialInterface;
char const *m_baudrate;
QFile m_update_ctrl_file;
@ -41,16 +39,34 @@ class Update : public QObject {
QStringList split(QString line, QChar sep = ',');
bool execUpdateScript();
public:
explicit Update(QString update_ctrl_file,
enum class DownloadResult {OK, ERROR, TIMEOUT, NOP};
static hwinf *loadDCPlugin(QDir const &plugInDir, QString const &fn);
explicit Update(hwinf *hw,
QString update_ctrl_file,
QString workingDir = ".",
QObject *parent = nullptr,
hwinf *hw = nullptr,
char const *serialInterface = SERIAL_PORT,
char const *baudrate = "115200");
virtual ~Update() override;
bool doUpdate();
bool const m_delete;
private:
DownloadResult sendStatus(int ret) const;
DownloadResult sendNextAddress(int bNum) const;
DownloadResult sendNextDataBlock(QByteArray const &b, int bNum) const;
DownloadResult dc_downloadBinary(QByteArray const &binary) const;
bool startBootloader() const;
bool stopBootloader() const;
bool openSerial(int br, QString baudrate, QString comPort) const;
void closeSerial() const;
bool resetDeviceController() const;
QByteArray loadBinaryDCFile(QString filename) const;
bool downloadBinaryToDC(QString const &bFile) const;
};
#endif // UPDATE_H_INCLUDED

66
worker.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "worker.h"
#include "update.h"
#include <QCoreApplication>
#include <QApplication>
#include <QDebug>
#include <QTimer>
#include <QFileInfo>
#include <QDir>
#include <QThread>
#include "message_handler.h"
#include "plugins/interfaces.h"
Worker::Worker(QString update_ctrl_file, QString workingDir)
: m_update_ctrl_file(update_ctrl_file)
, m_workingDir(workingDir)
, m_workerThread("workerThread") {
this->moveToThread(&m_workerThread);
m_workerThread.start();
QThread::usleep(100000);
int cnt = 0;
while (!m_workerThread.isRunning()) {
if (++cnt > 5) {
qCritical() << "starting worker thread FAILED";
return;
}
QThread::sleep(1);
}
connect(this, SIGNAL(workNow()), this, SLOT(work()), Qt::QueuedConnection);
connect(&m_timer, SIGNAL(timeout()), this, SLOT(update()));
m_timer.setSingleShot(true);
m_timer.start(1000);
}
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;
}
}
}
}
void Worker::update() {
qCritical() << __func__ << ":" << __LINE__;
emit workNow();
}
void Worker::work() {
qCritical() << __func__ << ":" << __LINE__;
//Update m_update(m_update_ctrl_file, m_workingDir);
QThread::sleep(3);
//if (m_update.doUpdate()) {
//}
m_workerThread.quit();
QApplication::quit();
}

30
worker.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef WORKER_H_INCLUDED
#define WORKER_H_INCLUDED
#include <QObject>
#include <QString>
#include <QTimer>
#include "worker_thread.h"
class Worker : public QObject {
Q_OBJECT
QString m_update_ctrl_file;
QString m_workingDir;
WorkerThread m_workerThread;
QTimer m_timer;
public:
explicit Worker(QString update_ctrl_file, QString workingDir);
~Worker();
void quit() { return m_workerThread.quit(); }
signals:
void workNow();
public slots:
void work();
void update();
};
#endif // WORKER_H_INCLUDED

13
worker_thread.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "worker_thread.h"
WorkerThread::WorkerThread(QString const &name,
QObject *parent) : QThread(parent) {
this->setObjectName(name);
}
WorkerThread::~WorkerThread() {
}
//void WorkerThread::run() {
//
//}

17
worker_thread.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef WORKER_THREAD_H_INCLUDED
#define WORKER_THREAD_H_INCLUDED
#include <QThread>
#include <QString>
class WorkerThread : public QThread {
Q_OBJECT
public:
WorkerThread(QString const &name, QObject *parent = nullptr);
virtual ~WorkerThread();
protected:
// virtual void run();
};
#endif // WORKER_THREAD_H_INCLUDED