Merge branch 'pu/accountRequest' into pu/integration

This commit is contained in:
Siegfried Siegert 2023-06-22 14:56:28 +02:00
commit 7d722e2b2c
Signed by: SiegfriedSiegert
GPG Key ID: 68371E015E8F0B03
8 changed files with 615 additions and 17 deletions

View File

@ -71,14 +71,18 @@ DEFINES += QT_DEPRECATED_WARNINGS
HEADERS += \
include/interfaces.h \
src/ATBAPP/ATBAPPplugin.h \
src/ATBAPP/DeviceControllerDiag.h \
src/ATBAPP/DeviceControllerInterface.h \
src/ATBAPP/ATBHealthEvent.h \
src/ATBAPP/ATBMachineEvent.h \
src/ATBAPP/ATBDeviceControllerPlugin.h \
src/ATBAPP/Utils.h
SOURCES += \
src/ATBAPP/ATBHealthEvent.cpp \
src/ATBAPP/ATBMachineEvent.cpp \
src/ATBAPP/ATBDeviceControllerPlugin.cpp \
src/ATBAPP/DeviceControllerDiag.cpp \
src/ATBAPP/Utils.cpp
DISTFILES += \

View File

@ -1,5 +1,6 @@
#include "src/ATBAPP/ATBDeviceControllerPlugin.h"
#include "src/ATBAPP/ATBHealthEvent.h"
#include "src/ATBAPP/ATBMachineEvent.h"
#include "src/ATBAPP/Utils.h"
#include <QTimer>
@ -9,13 +10,16 @@
#include <QPluginLoader>
#include <QDateTime>
#include <QFileInfo>
#include <QCoreApplication>
#include <QUuid>
#include <cstdlib>
ATBDeviceControllerPlugin::ATBDeviceControllerPlugin(QObject *parent) :
pluginState(PLUGIN_STATE::NOT_INITIALIZED)
ATBDeviceControllerPlugin::ATBDeviceControllerPlugin(QObject *parent)
: pluginState(PLUGIN_STATE::NOT_INITIALIZED)
, eventReceiver(nullptr)
{
this->setParent(parent);
@ -25,9 +29,6 @@ ATBDeviceControllerPlugin::ATBDeviceControllerPlugin(QObject *parent) :
return;
}
//connect(dynamic_cast<QObject*>(hw), SIGNAL(hwapi_templatePrintFinished_OK()), this, SLOT(onPrintFinishedOK()), Qt::QueuedConnection);
//connect(dynamic_cast<QObject*>(hw), SIGNAL(hwapi_templatePrintFinished_Err()), this, SLOT(onPrintFinishedERR()), Qt::QueuedConnection);
@ -45,15 +46,18 @@ ATBDeviceControllerPlugin::ATBDeviceControllerPlugin(QObject *parent) :
connect(dynamic_cast<QObject*>(hw), SIGNAL(hwapi_doorAllDoorsClosed()), this, SLOT(onAllDoorsClosed()), Qt::QueuedConnection); // check for errors, switch to mode IDLE
this->diag = new DeviceControllerDiag(this);
this->currentSelectedTicketType = 0;
this->currentCashState = CASH_STATE::CACHE_EMPTY;
}
ATBDeviceControllerPlugin::~ATBDeviceControllerPlugin() {}
PLUGIN_STATE ATBDeviceControllerPlugin::initDCPlugin(QObject *healthEventReceiver, const QSettings & settings)
PLUGIN_STATE ATBDeviceControllerPlugin::initDCPlugin(QObject *eventReceiver, const QSettings & settings)
{
this->healthEventReceiver = healthEventReceiver;
this->eventReceiver = eventReceiver;
// read variables from setting
QString serialPort = settings.value("DEVICE_CONTROLLER/serialPort", "ttymxc2").toString();
@ -73,7 +77,7 @@ PLUGIN_STATE ATBDeviceControllerPlugin::initDCPlugin(QObject *healthEventReceive
// text encoding for printer
this->codec = QTextCodec::codecForName(printerEncoding);
this->diag->init(this->hw, this->eventReceiver);
this->pluginState = PLUGIN_STATE::INITIALIZED;
@ -96,6 +100,9 @@ void ATBDeviceControllerPlugin::onChangedProgramModeToSERVICE()
void ATBDeviceControllerPlugin::onChangedProgramModeToIDLE()
{
//hw->dc_autoRequest(false); // <-- TODO: ???
this->diag->diagRequest();
}
void ATBDeviceControllerPlugin::onChangedProgramModeToOOO()
@ -192,6 +199,10 @@ void ATBDeviceControllerPlugin::private_checkAccountData()
// cannot get accountData within ~10*500ms
qCritical() << "checkAccountData() failed";
// simulate:
this->private_getAccountData();
// TODO: create and send an HealthEvent...
}
}
@ -213,6 +224,9 @@ void ATBDeviceControllerPlugin::private_getAccountData()
int numberOfCoinVariants = sizeof(retVR.coinsInVault);
// DEBUG
qCritical() << " NumberOfCoinVariants = " << numberOfCoinVariants;
// limit numberOfCoinVariants:
if (numberOfCoinVariants > 16) { numberOfCoinVariants = 16; }
@ -270,6 +284,8 @@ void ATBDeviceControllerPlugin::onCBinAndAllDoorsClosed()
{
qCritical() << "ATBDeviceControllerPlugin::onCBinAndAllDoorsClosed()";
this->diag->diagRequest();
// TODO: Stop background task "ACCOUNT"
QTimer::singleShot(2000, this, SIGNAL(requestModeIDLE()));
@ -401,15 +417,38 @@ void ATBDeviceControllerPlugin::onPrinterDataPrepared()
{
this->hw->prn_printKombiticket(this->currentSelectedTicketType);
// FAKE SIGNAL:
QTimer::singleShot(4000, this, SLOT(onPrintFinishedOK()));
// note: calling prn_getPrintResult() immediately may result in wrong answer!
// We have to wait "about some seconds" until calling this function!
QTimer::singleShot(4000, this, SLOT(onPrinterWaitForPrinting()));
// old: use printer templates:
// this->currentTemplate = 1;
// this->onPrinterPrintNextTemplate();
}
void ATBDeviceControllerPlugin::onPrinterWaitForPrinting()
{
quint8 printerResult = this->hw->prn_getPrintResult();
switch (printerResult) {
case 0: // still printing
qCritical() << "--> printer: WaitForPrinting";
QTimer::singleShot(2000, this, SLOT(onPrinterWaitForPrinting()));
break;
case 1: // printing finished, Ok
this->onPrintFinishedOK();
break;
case 2: // printing finished, Error
this->onPrintFinishedERR();
break;
default:
qCritical() << "DC Error: wait for printing";
this->onPrintFinishedERR();
break;
}
}
void ATBDeviceControllerPlugin::onPrinterPrintNextTemplate()
{
@ -694,6 +733,8 @@ const QString ATBDeviceControllerPlugin::getString(nsDeviceControllerInterface::
}
/************************************************************************************************
* ... end
*/

View File

@ -5,6 +5,8 @@
#include "src/ATBAPP/DeviceControllerInterface.h"
#include "src/ATBAPP/ATBAPPplugin.h"
#include "src/ATBAPP/DeviceControllerDiag.h"
#include "version.h"
@ -12,9 +14,6 @@
#include "interfaces.h"
#include <unistd.h>
#include <thread>
#include <memory>
#include <QSharedMemory>
class QTextCodec;
@ -40,7 +39,7 @@ public:
// ----------------------------------------------------------------------------
// interface:
PLUGIN_STATE initDCPlugin(QObject *healthEventReceiver, const QSettings & settings);
PLUGIN_STATE initDCPlugin(QObject *eventReceiver, const QSettings & settings);
// TASKS: Cash handling -------------------------------------------------------
void requestStartCashInput(const QString & amount);
@ -90,10 +89,13 @@ private:
PLUGIN_STATE pluginState;
QObject* healthEventReceiver;
QObject* eventReceiver;
hwinf* hw;
DeviceControllerDiag* diag;
QTextCodec *codec;
bool private_loadCashAgentLib(QString pluginName);
@ -111,6 +113,7 @@ private slots:
void onPrinterDataPrepared();
void onPrinterPrintNextTemplate();
void onPrinterWaitForPrinting();
void onPrintFinishedOK();
void onPrintFinishedERR();

View File

@ -0,0 +1,66 @@
/* Machine Event
*
* Used e.g. to send events to ISMAS
*
* Note: It's an Event, not a State!
* -> An Event may cause a transition to a different state, depending on the current state.
* -> Compare to edge/level trigger: Event is an "edge", State is a "level"
* => Do not mix both
*
*/
#include <QDateTime>
#include "ATBMachineEvent.h"
ATBMachineEvent::ATBMachineEvent(
const QString & id,
const QString & deviceName, // PTU/PRINTER/DC/...
EVENT_CLASS eventClass, // reason of event: Error/Warning/Alarm
const QString & name, // 'Event': "E001", "W003"
const int state,
const QString & parameter,
const QString & secondLevelInfo)
: QEvent(ATB_MACHINE_EVENT)
, eventId(id)
, deviceName(deviceName)
, machineEventClass(eventClass)
, eventName(name)
, eventState(state)
// timestamp including timezone offset
, timestamString(QDateTime::currentDateTime().toOffsetFromUtc(
QDateTime::currentDateTime().offsetFromUtc()).toString(Qt::ISODate)
)
, parameterString(parameter)
, secondLevelInfoString(secondLevelInfo)
{
}
QString ATBMachineEvent::getEventClassString(EVENT_CLASS eventClass)
{
switch (eventClass) {
case EVENT_CLASS::WARNING:
return "WARNING";
break;
case EVENT_CLASS::ERROR:
return "ERROR";
break;
case EVENT_CLASS::ALARM:
return "ALARM";
break;
case EVENT_CLASS::DEBUG:
return "DEBUG";
break;
case EVENT_CLASS::STATE:
return "STATE";
break;
case EVENT_CLASS::NOT_DEFINED:
return "NOT_DEFINED";
break;
}
return "NOT_DEFINED";
}

View File

@ -0,0 +1,47 @@
#ifndef ATBMACHINECONDITIONEVENT_H
#define ATBMACHINECONDITIONEVENT_H
#include <QEvent>
#include <QString>
enum class EVENT_CLASS : quint8;
const QEvent::Type ATB_MACHINE_EVENT = static_cast<QEvent::Type>(QEvent::User + 2);
class ATBMachineEvent : public QEvent
{
public:
explicit ATBMachineEvent(const QString & id,
const QString & deviceName, // PTU/PRINTER/DC/...
EVENT_CLASS eventClass, // reason of event: Error/Warning/Alarm
const QString & name, // 'Event': "E001", "W003"
const int state,
const QString & parameter,
const QString & secondLevelInfo
);
QString eventId;
QString deviceName;
EVENT_CLASS machineEventClass;
QString eventName;
int eventState;
QString timestamString;
QString parameterString;
QString secondLevelInfoString;
static QString getEventClassString(EVENT_CLASS eventClass);
};
enum class EVENT_CLASS : quint8 {
WARNING,
ERROR,
ALARM,
DEBUG,
STATE,
NOT_DEFINED
};
#endif // ATBMACHINEEVENT_H

View File

@ -0,0 +1,382 @@
#include "DeviceControllerDiag.h"
#include <QCoreApplication>
#include <QUuid>
#include <QDebug>
DeviceControllerDiag::DeviceControllerDiag(QObject *parent)
: QObject(parent)
, eventReceiver(nullptr)
, isRequestRunning(false)
, flagInterruptDiag(false)
{
diagRequestTimeoutTimer = new QTimer(this);
diagRequestTimeoutTimer->setInterval(1000*20); // 20s
diagRequestTimeoutTimer->setSingleShot(true);
connect(diagRequestTimeoutTimer, &QTimer::timeout, this, &DeviceControllerDiag::onDiagRequestTimeoutTimerTimeout);
}
void DeviceControllerDiag::init(hwinf *hw, QObject* eventReceiver)
{
this->hw = hw;
this->eventReceiver = eventReceiver;
// make a system check on startup:
QTimer::singleShot(2000, this, &DeviceControllerDiag::diagRequest);
}
void DeviceControllerDiag::diagRequest()
{
qCritical() << "DeviceControllerDiag::diagRequest()";
if (this->isRequestRunning) {
qCritical() << "DeviceControllerDiag::diagRequest() is already running";
return;
}
this->isRequestRunning = true;
this->diagRequestTimeoutTimer->start();
this->private_startDiag();
}
void DeviceControllerDiag::onDiagRequestTimeoutTimerTimeout()
{
qCritical() << "DeviceControllerDiag::onDiagRequestTimeoutTimerTimeout()";
this->flagInterruptDiag = true;
}
void DeviceControllerDiag::private_startDiag()
{
// check for DiagRequestTimeoutTimerTimeout:
if (this->flagInterruptDiag) {
qCritical() << "DeviceControllerDiag::private_startDiag() interrupted!";
this->private_finishedDiag(0xff);
return;
}
bool result;
result = hw->sys_areDCdataValid();
if (result) {
qCritical() << "DeviceControllerDiag::private_startDiag() DCdata is valid";
QTimer::singleShot(200, this, &DeviceControllerDiag::sys_superviseSystem);
}
else {
qCritical() << "DeviceControllerDiag::private_startDiag() DCdata is +++not+++ valid";
// try it again
QTimer::singleShot(200, this, &DeviceControllerDiag::private_startDiag);
}
}
void DeviceControllerDiag::sys_superviseSystem()
{ // this function proofs if vending is possible depending of doors state
struct T_dynamicCondition dynMaCond;
struct T_moduleCondition modCond;
qCritical() << " sys_superviseSystem()";
// check for DiagRequestTimeoutTimerTimeout:
if (this->flagInterruptDiag) {
qCritical() << "DeviceControllerDiag::sys_superviseSystem() interrupted!";
this->private_finishedDiag(0xff);
return;
}
if (!hw->sys_areDCdataValid())
{
// es gibt keinerlei gültige Daten vom DC
qCritical() << "DeviceControllerDiag::sys_superviseSystem() no valid data!";
this->private_finishedDiag(0xfe);
return;
}
// jetzt sind die DC-Daten aktuell, also reinholen:
hw->sys_getDynMachineConditions(&dynMaCond);
hw->sys_getDeviceConditions(&modCond);
qCritical() << "DeviceControllerDiag::sys_superviseSystem() get condition data";
if (!modCond.allModulesChecked)
{
// noch keine Testergebnisse
if (dynMaCond.startupTestIsRunning) {
// TODO?
}
qCritical() << " allModulesChecked is false --> call again";
QTimer::singleShot(200, this, &DeviceControllerDiag::sys_superviseSystem);
return;
}
// all doors: 99: undefined 0:closed 1:open
if (dynMaCond.lowerDoor || dynMaCond.upperDoor) {
// Service or battery door is open, goto INTRUSION MODE
qCritical() << "DeviceControllerDiag::sys_superviseSystem() Service or battery door is open, goto INTRUSION MODE";
this->private_finishedDiag(0xFD);
return;
}
if (dynMaCond.middleDoor) {
// vault door is open, goto INTRUSION MODE
qCritical() << "DeviceControllerDiag::sys_superviseSystem() vault door is open, goto INTRUSION MODE";
this->private_finishedDiag(0xFC);
return;
}
qCritical() << " --> call sub_componentAssessment()";
uint8_t proposedError = sub_componentAssessment();
if (proposedError) {
// All doors are closed but errors found, goto OOO MODE (out-of-order)
qCritical() << "DeviceControllerDiag::sys_superviseSystem() All doors are closed but errors found, goto OOO MODE (out-of-order)";
this->private_finishedDiag(proposedError);
return;
}
// everything fine
qCritical() << "DeviceControllerDiag::sys_superviseSystem() everything fine";
this->private_finishedDiag(0x00);
}
uint8_t DeviceControllerDiag::sub_componentAssessment()
{
// this function decides if vending mode is possible, independant from door
// return >0 in case of error
struct T_moduleCondition modCond;
hw->sys_getDeviceConditions(&modCond);
struct T_dynamicCondition dynMaCond;
hw->sys_getDynMachineConditions(&dynMaCond);
struct T_devices devPara;
hw->sys_restoreDeviceParameter(&devPara);
if (modCond.rtc>=200)
return 1;
if (modCond.printer==200 || modCond.printer==201) // 200: not connected 201: printer-HW-error 202: no paper
return 2;
if (modCond.printer==202)
return 3;
if (modCond.coinBlocker>=200)
return 4;
if (modCond.mdbBus>=200)
return 5;
if (modCond.intEe>=200)
return 6;
if (devPara.kindOfCoinChecker==1 || devPara.kindOfCoinChecker==2) // 0: without 1=EMP820 2=EMP900 3=currenza c² (MW)
{
if (modCond.coinChecker>=200 || modCond.coinEscrow>=200)
{
// Fehler Münzver.
return 7;
}
if (modCond.coinSafe>200) // 200: kasse fehlt 201: voll 100:fast voll 1:ok
{
return 8;
}
} else
if (devPara.kindOfCoinChecker==3)
{
if (modCond.changer>=200)
{
// Fehler Münzver.
return 7;
}
if (modCond.coinSafe>200) // 200: kasse fehlt 201: voll 100:fast voll 1:ok
{
return 8;
}
}
if ( modCond.billReader>=200 && devPara.BillAcceptor>0)
{
// Fehler BNA
return 9;
}
if (dynMaCond.onAlarm>0)
return 10;
if (dynMaCond.modeAbrech>0)
return 11;
if (dynMaCond.nowCardTest>0)
return 12;
if (dynMaCond.startupTestIsRunning>0)
return 13;
if (modCond.voltage>=200)
return 14;
if (modCond.temper>=200)
return 15;
return 0;
}
uint8_t DeviceControllerDiag::sys_getSystemErrors()
{
// 0: everything fine 1..15: errors
/* 1: real time clock error
2: printer error
3: no paper
4: coin blocker
5: mdb error
6: mem error int.ee.
7: error coin validator
8: coin safe missed or full
9: bill acceptor error
10: alarm / intrusion
11: cash box change is ongoing
12: card test running
13: startup-test is running
14: voltage error
15: temperature error
*/
return this->sub_componentAssessment();
}
/**
* @brief DeviceControllerDiag::private_finishedDiag
* @param result - result value from 'sub_componentAssessment()',
* - 0xFF on timer interrupt
* - 0xFE no valid data from DeviceController
* - 0xFD Service or battery door is open
* - 0xFE vault door is open
*/
void DeviceControllerDiag::private_finishedDiag(uint8_t result)
{
this->diagRequestTimeoutTimer->stop();
this->isRequestRunning = false;
this->flagInterruptDiag = false;
if (result == 0) return;
qCritical() << "DeviceControllerDiag::private_finishedDiag() result: " << result;
if (this->eventReceiver == nullptr) {
qCritical() << "DeviceControllerDiag: no eventReceiver";
return;
}
if (result > 15 && result != 0xFE) return;
// Errors are in this range 1...15:
QString eventId = QUuid::createUuid().toString(QUuid::WithoutBraces).mid(0, 8);
QString eventName;
EVENT_CLASS eventClass = EVENT_CLASS::STATE;
QString parameter;
switch (result) {
case 1: // real time clock error
eventName = "E001";
eventClass = EVENT_CLASS::ERROR;
parameter = "real time clock error";
break;
case 2: // printer error
eventName = "E002";
eventClass = EVENT_CLASS::ERROR;
parameter = "printer error";
break;
case 3: // no paper
eventName = "E003";
eventClass = EVENT_CLASS::ERROR;
parameter = "no paper";
break;
case 4: // coin blocker
eventName = "E004";
eventClass = EVENT_CLASS::ERROR;
parameter = "coin blocker";
break;
case 5: // mdb error
eventName = "E005";
eventClass = EVENT_CLASS::ERROR;
parameter = "mdb error";
break;
case 6: // mem error int.ee.
eventName = "E006";
eventClass = EVENT_CLASS::ERROR;
parameter = "mem error int.ee.";
break;
case 7: // error coin validator
eventName = "E007";
eventClass = EVENT_CLASS::ERROR;
parameter = "error coin validator";
break;
case 8: // coin safe missed or full
eventName = "E008";
eventClass = EVENT_CLASS::ERROR;
parameter = "coin safe missed or full";
break;
case 9: // bill acceptor error
eventName = "E009";
eventClass = EVENT_CLASS::ERROR;
parameter = "bill acceptor error";
break;
case 10: // alarm / intrusion
eventName = "E010";
eventClass = EVENT_CLASS::ERROR;
parameter = "alarm / intrusion";
break;
case 11: // cash box change is ongoing
eventName = "E011";
eventClass = EVENT_CLASS::STATE;
parameter = "cash box change is ongoing";
break;
case 12: // card test running
eventName = "E012";
eventClass = EVENT_CLASS::STATE;
parameter = "card test running";
break;
case 13: // startup-test is running
eventName = "E013";
eventClass = EVENT_CLASS::STATE;
parameter = "startup-test is running";
break;
case 14: // voltage error
eventName = "E014";
eventClass = EVENT_CLASS::ERROR;
parameter = "voltage error";
break;
case 15: // temperature error
eventName = "E015";
eventClass = EVENT_CLASS::STATE;
parameter = "temperature error";
break;
case 0xFE: // no valid data from DeviceController
eventName = "E254";
eventClass = EVENT_CLASS::STATE;
parameter = "no valid data from DeviceController";
break;
}
ATBMachineEvent *machineEvent = new ATBMachineEvent(
eventId,
"DC",
eventClass,
eventName,
1,
parameter,
"" // second level info
);
//emit diagResponse(machineEvent);
QCoreApplication::postEvent(eventReceiver, machineEvent);
}

View File

@ -0,0 +1,49 @@
#ifndef DEVICECONTROLLERDIAG_H
#define DEVICECONTROLLERDIAG_H
#include <QObject>
#include <QTimer>
#include "ATBMachineEvent.h"
#include "interfaces.h"
class DeviceControllerDiag : public QObject
{
Q_OBJECT
public:
DeviceControllerDiag(QObject *parent = nullptr);
void init(hwinf* hw, QObject* eventReceiver);
public slots:
void diagRequest();
signals:
void diagResponse(ATBMachineEvent* machineEvent);
private:
QObject *eventReceiver;
hwinf* hw;
bool isRequestRunning;
bool flagInterruptDiag;
QTimer *diagRequestTimeoutTimer;
uint8_t sub_componentAssessment();
uint8_t sys_getSystemErrors();
private slots:
void onDiagRequestTimeoutTimerTimeout();
void private_startDiag(); // diag entry method
void private_finishedDiag(uint8_t result); // diag exit method
void sys_superviseSystem();
};
#endif // DEVICECONTROLLERDIAG_H

View File

@ -24,7 +24,13 @@ class DeviceControllerInterface : public QObject
public:
virtual ~DeviceControllerInterface() {}
virtual nsDeviceControllerInterface::PLUGIN_STATE initDCPlugin(QObject *healthEventReceiver,
/**
* @brief initDCPlugin
* @param eventReceiver - QObject to receive ATBMachineEvents or HealthEvents
* @param settings
* @return
*/
virtual nsDeviceControllerInterface::PLUGIN_STATE initDCPlugin(QObject *eventReceiver,
const QSettings & settings) = 0;
// TASKS: Cash handling -------------------------------------------------------