VMCPlugin/AppControl.cpp

2746 lines
102 KiB
C++

#include "AppControl.h"
#include "ATBHMIconfig.h"
#include "VMC/vmc.h"
#include "atb_system.h"
#include "support/VendingData.h"
#include <QTimer>
#include <QTranslator>
#include <QLocale>
#include <QDebug>
#include <QCoreApplication>
#include <QDateTime>
AppControl::AppControl(QObject *parent) :
QObject(parent),
programmode(PROGRAM_MODE::IDLE)
{
this->config = new ATBHMIconfig();
this->system = new ATB_system(config, this);
this->vmc = new VMC(this, config, this);
this->prepareInternalOOO = 0;
// ------------------- -----------------------------------------------
this->vendingData = new VendingData(this);
// ------------------- connect HMI ---------------------------------------------
// -------------------- connect vmc --------------------------------------------
connect(vmc, SIGNAL(VMCFormatedString(FormatedStringList)), this, SLOT(onVMCFormatedString(FormatedStringList)), Qt::QueuedConnection);
connect(vmc, SIGNAL(sysCommand(quint16, QByteArray)), system, SLOT(executeSystemCommand(quint16, QByteArray)), Qt::QueuedConnection);
connect(vmc, SIGNAL(newDateTime(QString)), system, SLOT(setDateTime(QString)), Qt::QueuedConnection);
connect(vmc, SIGNAL(setCustNr(QString)), config, SLOT(setCustNr(QString)), Qt::QueuedConnection);
connect(vmc, SIGNAL(setGroupNr(QString)), config, SLOT(setGroupNr(QString)), Qt::QueuedConnection);
connect(vmc, SIGNAL(setZoneNr(QString)), config, SLOT(setZoneNr(QString)), Qt::QueuedConnection);
connect(vmc, SIGNAL(setMachineNr(QString)), config, SLOT(setMachineNr(QString)), Qt::QueuedConnection);
connect(vmc, SIGNAL(setDefaultLanguage(quint8)), config, SLOT(setDefaultLanguage(quint8)), Qt::QueuedConnection);
connect(vmc, SIGNAL(displayData(quint8,QString)), this, SLOT(onVMCSellData(quint8,QString)), Qt::QueuedConnection);
connect(vmc, SIGNAL(retriggerModeSell()), this, SLOT(restartSellModeTimeoutTimer()), Qt::QueuedConnection);
connect(vmc, SIGNAL(wakeVMC()), system, SLOT(onWakeVMC()));
connect(vmc, SIGNAL(setVendingData(QString,QByteArray)), this, SLOT(onSetVendingData(QString,QByteArray)), Qt::QueuedConnection);
// -------------------- connect system errors ---------------------------------------
/*
connect(<object>, SIGNAL(signalStateError(QString, QString)),
this,
SLOT(onSignalSystemErrors(QString, QString)));
*/
/* ----------------------------- load style -------------------------------------- */
// Note: style should be loaded after all Widgets are created. This is partialy
// done in 'initDefered()'; so loading and applying StyleSheet is done here.
//QString styleSheet = "style.qss";
//styleSheet.prepend('/').prepend(QApplication::applicationDirPath());
QString styleSheet = ":/style/resources/style/style.qss";
QFile File(styleSheet);
File.open(QFile::ReadOnly);
QString StyleSheet = QLatin1String(File.readAll());
// ------------------- TIMERS -----------------------------------------------
// this timer resets to mode idle, if in sell mode.
sellModeTimeoutTimer = new QTimer(this);
sellModeTimeoutTimer->setInterval(1000*(config->getSellTimeoutTime().toInt()));
sellModeTimeoutTimer->setSingleShot(false);
connect(sellModeTimeoutTimer, SIGNAL(timeout()), this, SLOT(onSellModeTimerTimeout()));
/* ----------------------------- load plugins ------------------------------------ */
#ifdef USE_PLUGINS
//QString ApplicationPluginPath = QCoreApplication::applicationDirPath() + "/plugins";
//QCoreApplication::addLibraryPath(ApplicationPluginPath);
// -> 20190728: path is set (see debug output below)...
// but plugin is not loaded with QPluginLoader!!!
this->pluginManager = new PluginManager(config, this);
#ifdef USE_CC_PLUGIN
/* ----------------------------- CC plugin -------------------------------------- */
this->private_initCCPlugin();
#endif
#ifdef USE_CALCULATEPRICE_PLUGIN
/* ----------------------------- Calculate Price -------------------------------- */
this->private_initCalculatePricePlugin();
#endif
#ifdef USE_SIMULATION_PLUGIN
/* ----------------------------- Simulation Plugin ------------------------------- */
this->private_initSimulationPlugin();
#endif
#endif // USE_PLUGINS
/* ----------------------------- setup hardware --------------------------------- */
#ifdef USE_BARCODESCANNER
// create and init barcode scanner
if (config->getUseBarcodeScanner()) {
this->barcodeScanner = new BarcodeScanner(this->config->getBarcodeScannerInputDevice(), this);
connect(this->barcodeScanner, SIGNAL(readHID(QString&)), this, SLOT(onReadBarcode(QString&)));
}
else { this->barcodeScanner = nullptr; }
#endif
/* ----------------------------- ISMAS ------------------------------------------ */
// used for PIN-generation and transaction
#ifdef USE_ISMAS
this->ismas = new ATB_ISMAS(this->config, this);
connect(ismas, SIGNAL(received_ATBQRCODE_RESULT_response(QHash<QString,QByteArray>)), this, SLOT(onProcessed_QRCode(QHash<QString,QByteArray>)));
connect(ismas, SIGNAL(requestOfflineProcessing()), this, SLOT(onRequestOfflineProcessing()), Qt::QueuedConnection);
connect(this, SIGNAL(doOfflineProcessing()), ismas, SLOT(onDoOfflineProcessing()), Qt::QueuedConnection);
connect(this, SIGNAL(changedModeToSELL()), ismas, SLOT(onChangedModeToSELL()));
#endif
/* ----------------------------- dim high on program start ---------------------- */
if (config->hasFeatureDBusDisplayControl()) {
system->dbus_DimControlStart();
}
/* -------------------- install event filter for global error handling ---------- */
QCoreApplication *app = QCoreApplication::instance();
app->installEventFilter(this);
connect(system, SIGNAL(wakeUp()), this, SLOT(onWakeUp()));
system->switchBlinkButtonOff();
/* ----------------------------- start first run -------------------------------- */
QTimer::singleShot(500, this, SLOT(firstRun()));
/* ----------------------------- TESTS ------------------------------------------ */
#if defined (ARCH_DesktopLinux)
/************************************************************************
* test utils:
*
QString amount = "122";
QString amountTax = "144";
QString amountNet = "56";
QString amount1 = "2";
QString amount2 = "99";
QString amount3 = "899";
QString amount4 = "7809";
qDebug() << "utils::getPriceString(" << amount << ") = " << utils::getPriceString(amount);
qDebug() << "utils::getPriceString(" << amountTax << "," << amountNet << ") = " << utils::getPriceString(amountTax, amountNet);
qDebug() << "utils::getPriceString(" << amountNet << ") = " << utils::getPriceString(amountNet);
qDebug() << "utils::getPriceString(" << amountNet << ") = " << utils::getPriceString(amount1);
qDebug() << "utils::getPriceString(" << amountNet << ") = " << utils::getPriceString(amount2);
qDebug() << "utils::getPriceString(" << amountNet << ") = " << utils::getPriceString(amount3);
qDebug() << "utils::getPriceString(" << amountNet << ") = " << utils::getPriceString(amount4);
qDebug() << " ";
*/
/************************************************************************
* test QR-Code receipt:
*/
/*
#if defined (USE_QRCODE_RECEIPT)
// Test: Beleg als QR-Code
QString tmpReceipt;
tmpReceipt.append(" ** Kundenbeleg ** \n");
tmpReceipt.append(" Bezahlung Mastercard \n");
tmpReceipt.append("Karte: xxxxxxxxxxxx9404 0\n");
tmpReceipt.append("01.12.2021 09:57:12\n");
tmpReceipt.append("Term.-ID:60213845 TA-Nr.:000069\n");
tmpReceipt.append("Vorgangs- \n");
tmpReceipt.append("Nr.:0064 Beleg:0009\n");
tmpReceipt.append("App-ID: A0000000041010\n");
tmpReceipt.append("Erfassungsart: Kontaktlos\n");
tmpReceipt.append("Autor.:555058 -\n");
tmpReceipt.append("VU-Nummer: 455600764906\n");
tmpReceipt.append("Betrag: EUR 1,00\n");
tmpReceipt.append(" Zahlung erfolgt \n");
tmpReceipt.append(" \n");
// Test: Link als QR-Code
tmpReceipt.clear();
tmpReceipt.append("https://www.betterpark.de/receipt?licencePlate=ABCD123&ticketNumber=123456");
this->vendingData->setParameter("RECEIPT", QVariant(tmpReceipt));
QRCodeGenerator::generateQRCode(this->vendingData->getParameter("RECEIPT").toString());
#endif
*/
#endif
}
/**************************************************************************
* program mode management
*
*/
bool AppControl::requestProgramMode(PROGRAM_MODE newProgramMode)
{
bool result = false;
if (this->programmode == newProgramMode) {
return result;
}
switch (newProgramMode) {
case PROGRAM_MODE::IDLE:
if (this->prepareInternalOOO) {
vmc->blockScreenSwitch(1);
this->requestProgramMode(PROGRAM_MODE::OOO);
return false;
}
else {
this->private_setupProgramModeIDLE();
this->programmode = PROGRAM_MODE::IDLE;
result = true;
}
break;
case PROGRAM_MODE::SELL_ENABLE:
this->private_setupProgramModeSELL_ENABLE();
this->programmode = PROGRAM_MODE::SELL_ENABLE;
result = true;
break;
case PROGRAM_MODE::SELL:
this->private_setupProgramModeSELL();
this->programmode = PROGRAM_MODE::SELL;
result = true;
break;
case PROGRAM_MODE::SERVICE:
this->private_setupProgramModeSERVICE();
this->programmode = PROGRAM_MODE::SERVICE;
result = true;
break;
case PROGRAM_MODE::OOO:
this->private_setupProgramModeOOO();
this->programmode = PROGRAM_MODE::OOO;
result = true;
break;
}
qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate)
<< "changed programmode to " << this->programmode;
switch (this->programmode) {
case PROGRAM_MODE::IDLE:
emit this->changedModeToIDLE();
break;
case PROGRAM_MODE::SELL_ENABLE:
break;
case PROGRAM_MODE::SELL:
emit this->changedModeToSELL();
break;
case PROGRAM_MODE::SERVICE:
emit this->changedModeToSERVICE();
break;
case PROGRAM_MODE::OOO:
emit this->changedModeToOOO();
break;
}
emit this->changedProgramMode(this->programmode);
return result;
}
PROGRAM_MODE AppControl::getCurrentProgramMode()
{
return this->programmode;
}
bool AppControl::isProgramModeSELL()
{
return this->programmode == PROGRAM_MODE::SELL ? true : false;
}
bool AppControl::isProgramModeSELL_ENABLE()
{
return this->programmode == PROGRAM_MODE::SELL_ENABLE ? true : false;
}
bool AppControl::isProgramModeSERVICE()
{
return this->programmode == PROGRAM_MODE::SERVICE ? true : false;
}
bool AppControl::isProgramModeIDLE()
{
return this->programmode == PROGRAM_MODE::IDLE ? true : false;
}
bool AppControl::isProgramModeOOO()
{
return this->programmode == PROGRAM_MODE::OOO ? true : false;
}
/* private mode setup routines */
void AppControl::private_setupProgramModeSELL()
{
if (config->hasFeatureDBusSuspendControl()) {
system->dbus_preventSuspend();
}
if (config->hasFeatureDBusDisplayControl()) {
system->dbus_DimHighPermanent();
}
// start SellModeTimeoutTimer
this->sellModeTimeoutTimer->start();
bool changed = false;
if (this->programmode != PROGRAM_MODE::SELL) {
changed = true;
}
// do the following only once (only on mode change)
if (changed) {
// clear (old) vending data
vendingData->clear();
}
}
void AppControl::private_setupProgramModeSELL_ENABLE()
{
}
void AppControl::private_setupProgramModeSERVICE()
{
this->sellModeTimeoutTimer->stop();
if (config->hasFeatureDBusSuspendControl()) {
system->dbus_preventSuspend();
}
if (config->hasFeatureDBusDisplayControl()) {
system->dbus_DimHighPermanent();
}
#ifdef USE_BARCODESCANNER
// reinit barcode scanner
if (config->getUseBarcodeScanner()) {
this->barcodeScanner->reopen(this->config->getBarcodeScannerInputDevice());
}
#endif
#ifdef USE_RFIDREADER
// reinit rfid reader
if (config->getUseRFIDReader()) {
this->rfidReader->reopen(this->config->getRFIDReaderInputDevice());
}
#endif
}
void AppControl::private_setupProgramModeIDLE()
{
// DEBUG Transactions:
if (vendingData->hasParameter("MISSINGTRANSACTION")) {
qCritical() << "setProgramModeIDLE: unsubmitted transaction: ";
qCritical() << " ACCESSINFORMATION = " << vendingData->getParameter("ACCESSINFORMATION").toString();
}
this->sellModeTimeoutTimer->stop();
if (this->programmode == PROGRAM_MODE::SERVICE) {
this->private_unsetProgramModeSERVICE();
}
if (this->programmode == PROGRAM_MODE::OOO) {
this->private_unsetProgramModeOOO();
}
if (config->hasFeatureDBusSuspendControl()) {
system->dbus_permitSuspend();
}
if (config->hasFeatureDBusDisplayControl()) {
// do the following only if there is a mode transition from SELL,OOO,SERVICE
// If we are already in idle, this would cause the backlight to be switched on.
if (!this->isProgramModeIDLE()) {
system->dbus_DimControlStart();
}
}
}
void AppControl::private_setupProgramModeOOO()
{
this->sellModeTimeoutTimer->stop();
this->prepareInternalOOO = 0;
if (this->programmode == PROGRAM_MODE::SERVICE) {
this->private_unsetProgramModeSERVICE();
}
this->LEDs_ooo();
if (config->hasFeatureDBusSuspendControl()) {
system->dbus_permitSuspend();
}
if (config->hasFeatureDBusDisplayControl()) {
system->dbus_DimControlStart();
}
}
/*****************************************************************************
* for cleanup from SERVICE-Mode:
* close service menu, show stackedWidget, ...
*/
void AppControl::private_unsetProgramModeSERVICE()
{
this->LEDs_default();
#ifdef USE_BARCODESCANNER
// reinit barcode scanner
if (config->getUseBarcodeScanner()) {
this->barcodeScanner->reopen(this->config->getBarcodeScannerInputDevice());
}
#endif
#ifdef USE_RFIDREADER
// reinit rfid reader
if (config->getUseRFIDReader()) {
this->rfidReader->reopen(this->config->getRFIDReaderInputDevice());
}
#endif
}
/*****************************************************************************
* for cleanup from OOO-Mode:
*/
void AppControl::private_unsetProgramModeOOO()
{
this->LEDs_default();
}
void AppControl::onRequestOfflineProcessing()
{
if (this->programmode == PROGRAM_MODE::IDLE)
emit this->doOfflineProcessing();
}
void AppControl::restartSellModeTimeoutTimer()
{
if (this->isProgramModeSELL()) { this->sellModeTimeoutTimer->start(); }
}
/**************************************************************************
* process VMC commands
*
*/
/*****************************************************************************
* VMC: received formated string
*
* Precondition:
* VMC sends this message only in case of a successful and finished selling process
* (i.e. a ticket was printed).
* In case of e.g. a printing error, VMC must not send this message.
*
*/
void AppControl::onVMCFormatedString(FormatedStringList data)
{
// DEBUG
qCritical() << "AppControl::onVMCFormatedString(): received " << data.size() << " list elements";
foreach ( const QByteArray &dataElement, data)
{
qCritical() << " " << dataElement;
}
#ifdef USE_CALCULATEPRICE_PLUGIN
/* for ProcessTransaction:
* - 7 Parameters:
* - 1 - "TRANS"
* - 2 - licenseplate (accessInformation)
* - 3 - accessInformationType
* - 4 - timestamp
* - 5 - amount
* - 6 - TransactionNumber
* - 7 - ReceiptNumber
*
*/
if ( (data.size() >= 7) && (data.at(0).at(0) == 'T') ) {
QStringList tmpList;
for (int i = 0; i < data.size(); i++) {
tmpList << QString(data.at(i));
}
this->calcPriceBackend->requestProcessTransaction(tmpList.at(1),
"LICENSEPLATE",
tmpList.at(4),
false,
"", // receipt data
tmpList.at(5), // TransactionId
this->vendingData->getParameter("PermitType").toString(),
this->vendingData->getParameter("PaymentType").toString());
// clear vending data parameter to indicate, that transaction is done:
vendingData->clearParameter("TRANS");
vendingData->clearParameter("MISSINGTRANSACTION");
}
#else
qCritical() << "AppControl::onVMCFormatedString(): calculate price backend is not in this part of this program";
#endif
}
/**************************************************************************
* private initializer
*
* note: init is done in constructor
*/
void AppControl::private_initCCPlugin()
{
#ifdef USE_CC_PLUGIN
// load CCPlugin:
this->cc = qobject_cast<CCInterface*>(this->pluginManager->getInstance("CCPlugin"));
if (this->cc == nullptr) {
qCritical() << "ERROR on loading Plugin CCInterface!";
return;
}
if ( ! static_cast<quint32>(cc->initCCInterfacePlugin(this, config->getSettings()))) {
qCritical() << "ERROR on init Plugin CCInterface:";
qCritical() << " " << cc->getLastErrorDescription();
return;
}
qCritical() << "pluginInfo: " << cc->getPluginInfo();
// control cc by VMC (deprecated):
connect(vmc, SIGNAL(ccStartTransaction()), this, SLOT(onVMC_ccStartTransaction()));
connect(vmc, SIGNAL(ccCancelTransaction()), this, SLOT(onVMC_ccCancelTransaction()));
connect(vmc, SIGNAL(ccConfirmTransaction()), this, SLOT(onVMC_ccConfirmTransaction()));
connect(dynamic_cast<QObject*>(cc), SIGNAL(sendStartTransactionResult(nsCCInterface::RESULT_STATE, QString&)),
this, SLOT(onCCStartTransactionResult(nsCCInterface::RESULT_STATE, QString&)));
connect(dynamic_cast<QObject*>(cc), SIGNAL(sendConfirmTransactionResult(nsCCInterface::RESULT_STATE, QString&)),
this, SLOT(onCCConfirmTransactionResult(nsCCInterface::RESULT_STATE, QString&)));
connect(dynamic_cast<QObject*>(cc), SIGNAL(sendCancelTransactionResult(nsCCInterface::RESULT_STATE, QString&)),
this, SLOT(onCCCancelTransactionResult(nsCCInterface::RESULT_STATE, QString&)));
connect(dynamic_cast<QObject*>(cc), SIGNAL(sendRevertTransactionResult(nsCCInterface::RESULT_STATE, QString&)),
this, SLOT(onCCRevertTransactionResult(nsCCInterface::RESULT_STATE, QString&)));
//connect(dynamic_cast<QObject*>(cc), SIGNAL(sendRevertTransactionResult(nsCCInterface::RESULT_STATE, QString&)), vmc, SLOT()));
//connect(dynamic_cast<QObject*>(cc), SIGNAL(sendDayCloseResult(nsCCInterface::RESULT_STATE, QString&)), vmc, SLOT(QString)));
#ifdef USE_CC_PREAUTHORISATION
// control cc by VMC (deprecated):
connect(vmc, SIGNAL(ccStartPreauthorisation()), this, SLOT(onVMC_ccStartPreauthorisation()));
connect(vmc, SIGNAL(ccCancelPreauthorisation()), this, SLOT(onVMC_ccCancelPreauthorisation()));
connect(vmc, SIGNAL(ccConfirmPreauthorisation()), this, SLOT(onVMC_ccConfirmPreauthorisation()));
connect(dynamic_cast<QObject*>(cc), SIGNAL(sendPreAuthTransactionResult(nsCCInterface::RESULT_STATE, QString&)),
this, SLOT(onCCPreAuthTransactionResult(nsCCInterface::RESULT_STATE, QString&)));
connect(dynamic_cast<QObject*>(cc), SIGNAL(sendBookTotalTransactionResult(nsCCInterface::RESULT_STATE, QString&)),
this, SLOT(onCCBookTotalTransactionResult(nsCCInterface::RESULT_STATE, QString&)));
connect(dynamic_cast<QObject*>(cc), SIGNAL(sendCancelPreAuthTransactionResult(nsCCInterface::RESULT_STATE, QString&)),
this, SLOT(onCCCancelPreAuthTransactionResult(nsCCInterface::RESULT_STATE, QString&)));
#endif
#ifdef USE_CC_READCARD
connect(vmc, SIGNAL(ccStartReadCard()), this, SLOT(onVMC_ccStartReadCard()));
connect(dynamic_cast<QObject*>(cc), SIGNAL(sendCardInfoResult(nsCCInterface::RESULT_STATE, QString&)),
this, SLOT(onCCRequestCardInfoResult(nsCCInterface::RESULT_STATE, QString&)));
#endif
// this is customer dependent
#if defined (CUST00308)
// Altmuehlsee
connect(vmc, SIGNAL(ccWakup()), dynamic_cast<QObject*>(cc), SLOT(wakeupCC()));
connect(vmc, SIGNAL(ccSleep()), dynamic_cast<QObject*>(cc), SLOT(sleepCC()));
#else
connect(this, SIGNAL(changedModeToSELL()), dynamic_cast<QObject*>(cc), SLOT(wakeupCC()));
connect(this, SIGNAL(changedModeToIDLE()), dynamic_cast<QObject*>(cc), SLOT(sleepCC()));
#endif
connect(this, SIGNAL(changedModeToSERVICE()), dynamic_cast<QObject*>(cc), SLOT(wakeupCC()));
// extract plugin name form pluginInfo:
bool ok;
QVariantMap jsonCommand = JSON::parse(cc->getPluginInfo(), ok).toMap();
QString ccPluginName = jsonCommand["PluginName"].toString();
// configure HMI according to CC plugin:
// note: there could be also or addtional a customer specific config in the defined screen.
// also, there could be a specific text variant for a certain used terminal variant
if (ccPluginName == "TCP_ZVT_CCPlugin") {
hmi->setCCVariant(CC_VARIANT::TERMINAL::FEIG_CCTOPP);
}
else
if (ccPluginName == "IngenicoZVT_CCPlugin") {
hmi->setCCVariant(CC_VARIANT::TERMINAL::INGENICO_CCTOPP);
}
else
if (ccPluginName == "IngenicoISelf_CCPlugin") {
hmi->setCCVariant(CC_VARIANT::TERMINAL::INGENICO_CHIP_AND_PIN);
}
else {
hmi->setCCVariant(CC_VARIANT::TERMINAL::INGENICO_CCTOPP);
}
#endif // USE_CC_PLUGIN
}
void AppControl::private_initCalculatePricePlugin()
{
#ifdef USE_CALCULATEPRICE_PLUGIN
// load CalculatePricePlugin:
this->calcPriceBackend = qobject_cast<CalculatePriceInterface*>(this->pluginManager->getInstance("CalculatePricePlugin"));
if (this->calcPriceBackend == nullptr) {
qCritical() << "plugin CalculatePrice ist not used, instantiate default...";
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, "", "Can not instantiate CalculatePricePlugin!");
this->calcPriceBackend = new CalculatePriceDefault(this);
return;
}
if ( ! static_cast<quint32>(calcPriceBackend->initCalculatePricePlugin(this, config->getSettings()))) {
qCritical() << "ERROR init Plugin Calculate Price:";
qCritical() << " " << calcPriceBackend->getLastErrorDescription();
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, "", "Can not init CalculatePricePlugin!");
return;
}
qCritical() << "pluginInfo: " << calcPriceBackend->getPluginInfo();
connect(dynamic_cast<QObject*>(calcPriceBackend),
SIGNAL(requestCalculatePriceResult(nsCalculatePriceInterface::RESULT_STATE,QString,QString,QString,QString,PriceInfo,QString,QString)),
this,
SLOT(onCalculatedPrice(nsCalculatePriceInterface::RESULT_STATE,QString,QString,QString,QString,PriceInfo,QString,QString)));
connect(dynamic_cast<QObject*>(calcPriceBackend),
SIGNAL(requestProcessTransactionResult(nsCalculatePriceInterface::RESULT_STATE,QString,QString)),
this,
SLOT(onProcessTransaction(nsCalculatePriceInterface::RESULT_STATE,QString,QString)));
#endif // USE_CALCULATEPRICE_PLUGIN
}
void AppControl::private_initSimulationPlugin()
{
#ifdef USE_SIMULATION_PLUGIN
// load SimulationPlugin:
this->simulation = qobject_cast<SimulationInterface*>(this->pluginManager->getInstance("SimulationPlugin"));
if (this->simulation == nullptr) {
qCritical() << "plugin Simulation ist not available...";
}
else {
if ( ! static_cast<quint32>(simulation->initSimulationInterfacePlugin(this, config->getSettings()))) {
qCritical() << "ERROR io init Plugin SimulationInterface:";
qCritical() << " " << simulation->getLastErrorDescription();
}
qCritical() << "pluginInfo: " << simulation->getPluginInfo();
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, "", "Touch Simulation!");
}
#endif // USE_SIMULATION_PLUGIN
}
/**************************************************************************
* first run initialisation
*
* This methode is called, by a timer, 500ms after program start.
*/
void AppControl::firstRun()
{
this->LEDs_default();
}
/**************************************************************************
* set a value in vending data
*/
void AppControl::onSetVendingData(QString key, QByteArray value)
{
this->vendingData->setParameter(key, QVariant(value));
}
/**************************************************************************
* CC slots
*
*/
// public interface for vmc (deprecated)
void AppControl::onVMC_ccStartTransaction()
{
this->onCCStartTransaction();
}
void AppControl::onVMC_ccCancelTransaction()
{
this->onCCCancelTransaction();
}
void AppControl::onVMC_ccConfirmTransaction()
{
this->onCCStartConfirmTransaction();
}
void AppControl::onVMC_ccStartPreauthorisation()
{
this->onCCStartPreauthorisation();
}
void AppControl::onVMC_ccCancelPreauthorisation()
{
this->onCCCancelPreauthorisationRequest();
}
void AppControl::onVMC_ccConfirmPreauthorisation()
{
this->onCCConfirmPreauthorisation();
}
void AppControl::onVMC_ccStartReadCard()
{
this->onCCStartReadCard();
}
void AppControl::onCCStartTransaction()
{
#ifdef USE_CC_PLUGIN
cc->requestStartTransaction(this->vendingData->getUintParameter("AMOUNT"));
#endif
}
void AppControl::onCCStartTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result)
{
#ifdef USE_CC_PLUGIN
qCritical() << "AppControl::onCCStartTransactionResult() result = " << result;
// restart sell mode timeout timer on every message from CC.
if (this->isProgramModeSELL()) { this->sellModeTimeoutTimer->start(); }
QString vmcResult; // 'String', sent to vmc
switch (resultState) {
case nsCCInterface::RESULT_STATE::INFO:
qCritical() << "AppControl::onCCStartTransactionResult() resultState is INFO: " << result;
if (result.contains("PAYMENT PREPARED")) {
hmi->setCCBusy(false);
hmi->enableCCAbort(true);
}
else
if (result.contains("PAYMENT DISABLE ABORT")) {
hmi->enableCCAbort(false);
}
else
if (result.startsWith("04FF#")) {
hmi->displayCCMessage(result);
}
break;
case nsCCInterface::RESULT_STATE::ERROR_BUSY:
qCritical() << "AppControl::onCCStartTransactionResult() resultState is ERROR_BUSY";
if (this->isProgramModeSELL()) {
// set mode busy
hmi->setCCBusy(true);
QTimer::singleShot(2000, this, SLOT(onCCStartTransaction()));
}
break;
case nsCCInterface::RESULT_STATE::SUCCESS:
qCritical() << "AppControl::onCCStartTransactionResult() resultState is SUCCESS";
qCritical() << " result is: " << result;
vendingData->setParameter("PaymentType", QVariant(1)); // '1' - CardPayment
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x00;
vmc->SendLongFormatedStringReference(vmcResult);
break;
case nsCCInterface::RESULT_STATE::ERROR_STATE:
qCritical() << "AppControl::onCCStartTransactionResult() resultState is ERROR_STATE";
qCritical() << " result is: " << result;
if (result.contains("DAYCLOSE RUNNING")) {
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
vmc->SendLongFormatedStringReference(vmcResult);
}
else
if (result.contains("INIT RUNNING")) {
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
vmc->SendLongFormatedStringReference(vmcResult);
}
else
if (result.contains("AMOUNT ZERO")) {
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
vmc->SendLongFormatedStringReference(vmcResult);
}
else {
qCritical() << "ASSERT: unhandled RESULT_STATE::ERROR_STATE";
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
// ignore this result message
}
break;
case nsCCInterface::RESULT_STATE::ERROR_BACKEND:
qCritical() << "AppControl::onCCStartTransactionResult() resultState is ERROR_BACKEND";
this->onCCErrorBackend(result);
break;
case nsCCInterface::RESULT_STATE::ERROR_TIMEOUT:
qCritical() << "AppControl::onCCStartTransactionResult() resultState is ERROR_TIMEOUT";
vmcResult[0] = result.at(0);
vmcResult[1] = result.at(1);
if (result.at(0) == 0x45 && result.at(1) == 0x00) {
qCritical() << "ASSERT: RESULT_STATE::ERROR_TIMEOUT: sending 0x45 00 to VMC";
}
else {
vmc->SendLongFormatedStringReference(vmcResult);
}
break;
default:
if(result.size()>0) {
qCritical() << "ASSERT: unhandled RESULT_STATE";
qCritical() << " result is: " << result;
if (result.at(0) == 0x45 && result.at(1) == 0x00) {
qCritical() << "ASSERT: 'default' sending 0x45 00 to VMC";
}
else {
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
vmc->SendLongFormatedStringReference(vmcResult);
}
}
break;
}
#else
Q_UNUSED(resultState);
Q_UNUSED(result);
#endif
}
void AppControl::onCCCancelTransaction()
{
#ifdef USE_CC_PLUGIN
cc->requestCancelTransaction();
#endif
}
/**
* check, if we do have already a receipt in this selling process...
* this is to prevent for multiple screen calls.
* It has to be ensured, that 'RECEIPT' is cleared/removed from vending data after selling process.
*/
void AppControl::onCCStartConfirmTransaction()
{
#ifdef USE_CC_PLUGIN
if (this->vendingData->hasParameter("RECEIPT")) {
qCritical() << "AppControl::onCCStartConfirmTransaction() hasParameter(\"RECEIPT\") => omit repeated transaction";
}
else {
qCritical() << "AppControl::onCCStartConfirmTransaction(): start confirm transaction";
cc->requestConfirmTransaction();
}
#endif
}
void AppControl::onCCConfirmTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result)
{
qCritical() << "AppControl::onCCConfirmTransactionResult() result = " << result;
switch (resultState) {
case nsCCInterface::RESULT_STATE::SUCCESS:
this->vendingData->setParameter("RECEIPT", QVariant(result));
QTimer::singleShot(1000, this, SLOT(onCCPrintReceipt()));
break;
case nsCCInterface::RESULT_STATE::ERROR_BUSY:
qCritical() << "AppControl::onCCConfirmTransactionResult() resultState is ERROR_BUSY";
break;
default:
break;
}
}
void AppControl::onCCCancelTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result)
{
qCritical() << "AppControl::onCCCancelTransactionResult() result = " << result;
switch (resultState) {
case nsCCInterface::RESULT_STATE::SUCCESS:
vmc->SendLongFormatedStringReference(result);
break;
case nsCCInterface::RESULT_STATE::ERROR_BUSY:
qCritical() << "AppControl::onCCCancelTransactionResult() resultState is ERROR_BUSY";
break;
default:
break;
}
}
// send receipt for printing
void AppControl::onCCPrintReceipt()
{
if (this->vendingData->hasParameter("RECEIPT")) {
qCritical() << "DEBUG: onCCPrintReceipt() hasParameter(\"RECEIPT\")";
vmc->ccPrintReceipt(this->vendingData->getParameter("RECEIPT").toString());
#if defined (USE_QRCODE_RECEIPT)
if (config->qrCodeReceiptIsUsed()) {
QRCodeGenerator::generateQRCode(this->vendingData->getParameter("RECEIPT").toString());
}
#endif
}
else {
qCritical() << "onCCPrintReceipt() vendingData Parameter(\"RECEIPT\") not available";
}
}
void AppControl::onCCStartPreauthorisation()
{
#ifdef USE_CC_PLUGIN
#ifdef USE_CC_PREAUTHORISATION
cc->requestPreAuthTransaction(this->vendingData->getUintParameter("AMOUNT"));
#endif
#endif
}
void AppControl::onCCPreAuthTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result)
{
#ifdef USE_CC_PLUGIN
#ifdef USE_CC_PREAUTHORISATION
qCritical() << "AppControl::onCCPreAuthTransactionResult() result = " << result;
QString vmcResult; // 'String', sent to vmc
bool ok;
QVariantMap jsonCardInfo;
QString cardInfo;
QString cardTransactionInfo;
switch (resultState) {
case nsCCInterface::RESULT_STATE::INFO:
qCritical() << "AppControl::onCCPreAuthTransactionResult() resultState is INFO: " << result;
if (result.contains("PAYMENT PREPARED")) {
hmi->setCCBusy(false);
hmi->enableCCAbort(true);
}
else
if (result.contains("PAYMENT DISABLE ABORT")) {
hmi->enableCCAbort(false);
}
else
if (result.startsWith("04FF#")) {
hmi->displayCCMessage(result);
}
break;
case nsCCInterface::RESULT_STATE::ERROR_BUSY:
qCritical() << "AppControl::onCCPreAuthTransactionResult() resultState is ERROR_BUSY";
if (this->isProgramModeSELL()) {
// set mode busy
hmi->setCCBusy(true);
QTimer::singleShot(2000, this, SLOT(onCCStartPreauthorisation()));
}
break;
case nsCCInterface::RESULT_STATE::SUCCESS:
qCritical() << "AppControl::onCCPreAuthTransactionResult() resultState is SUCCESS";
jsonCardInfo = JSON::parse(result, ok).toMap();
if (ok) {
// TODO: set correct parameter names...
cardInfo = jsonCardInfo["INSTITUTSNUMMER"].toString(); // Institutsnummer
cardTransactionInfo = jsonCardInfo["BELEGNUMMER"].toString(); // ZVT-Belegnummer
this->vendingData->setParameter("INSTITUTSNUMMER", QVariant(cardInfo));
this->vendingData->setParameter("ZVT_BELEGNUMMER", QVariant(cardTransactionInfo));
this->onCalculatePrice(cardInfo, cardTransactionInfo);
}
else {
// result is no json: abort
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
vmc->SendLongFormatedStringReference(vmcResult);
}
break;
case nsCCInterface::RESULT_STATE::ERROR_STATE:
qCritical() << "AppControl::onCCPreAuthTransactionResult() resultState is ERROR_STATE";
qCritical() << " result is: " << result;
if (result.contains("DAYCLOSE RUNNING")) {
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
vmc->SendLongFormatedStringReference(vmcResult);
}
else
if (result.contains("INIT RUNNING")) {
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
vmc->SendLongFormatedStringReference(vmcResult);
}
else {
qCritical() << "ASSERT: unhandled RESULT_STATE::ERROR_STATE";
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
// ignore this result message
}
break;
case nsCCInterface::RESULT_STATE::ERROR_BACKEND:
qCritical() << "AppControl::onCCPreAuthTransactionResult() resultState is ERROR_BACKEND";
this->onCCErrorBackend(result);
break;
case nsCCInterface::RESULT_STATE::ERROR_TIMEOUT:
qCritical() << "AppControl::onCCPreAuthTransactionResult() resultState is ERROR_TIMEOUT";
vmcResult[0] = result.at(0);
vmcResult[1] = result.at(1);
if (result.at(0) == 0x45 && result.at(1) == 0x00) {
qCritical() << "ASSERT: RESULT_STATE::ERROR_TIMEOUT: sending 0x45 00 to VMC";
}
else {
vmc->SendLongFormatedStringReference(vmcResult);
}
break;
default:
if(result.size()>0) {
qCritical() << "ASSERT: unhandled RESULT_STATE";
qCritical() << " result is: " << result;
if (result.at(0) == 0x45 && result.at(1) == 0x00) {
qCritical() << "ASSERT: 'default' sending 0x45 00 to VMC";
}
else {
vmcResult[0] = 0x45; // 'E'
vmcResult[1] = 0x65; // 'e'
vmc->SendLongFormatedStringReference(vmcResult);
}
}
break;
}
#endif
#else
Q_UNUSED(resultState);
Q_UNUSED(result);
#endif
}
void AppControl::onCCConfirmPreauthorisation()
{
#ifdef USE_CC_PLUGIN
#ifdef USE_CC_PREAUTHORISATION
if (!this->vendingData->hasParameter("ZVT_BELEGNUMMER")) {
qCritical() << "onCCConfirmPreauthorisation(): ERROR: missing vendingData \"ZVT_BELEGNUMMER\"";
return;
}
QString zvt_belegnummer = this->vendingData->getParameter("ZVT_BELEGNUMMER").toString();
cc->requestBookTotalTransaction(this->vendingData->getUintParameter("AMOUNT"),
zvt_belegnummer);
#else
qCritical() << "CC Preauthorisation is not supported, please define USE_CC_PREAUTHORISATION";
#endif
#endif
}
void AppControl::onCCCancelPreauthorisationRequest()
{
#ifdef USE_CC_PLUGIN
#ifdef USE_CC_PREAUTHORISATION
QString zvt_belegnummer;
if (this->vendingData->hasParameter("ZVT_BELEGNUMMER")) {
zvt_belegnummer = this->vendingData->getParameter("ZVT_BELEGNUMMER").toString();
qCritical() << "onCCCancelPreauthorisationRequest() with receipt number: " << zvt_belegnummer;
cc->requestCancelPreAuthTransaction(zvt_belegnummer);
}
else {
qCritical() << "onCCCancelPreauthorisationRequest(): ERROR: missing vendingData \"ZVT_BELEGNUMMER\"";
}
#else
qCritical() << "CC Preauthorisation is not supported, please define USE_CC_PREAUTHORISATION";
#endif
#endif
}
void AppControl::onCCCancelPreAuthTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result)
{
#ifdef USE_CC_PLUGIN
#ifdef USE_CC_PREAUTHORISATION
qCritical() << "AppControl::onCCCancelPreAuthTransactionResult() result = " << result;
switch (resultState) {
case nsCCInterface::RESULT_STATE::SUCCESS:
vmc->SendLongFormatedStringReference(result);
break;
case nsCCInterface::RESULT_STATE::ERROR_BUSY:
qCritical() << "AppControl::onCCCancelPreAuthTransactionResult() resultState is ERROR_BUSY";
break;
default:
break;
}
#endif
#else
Q_UNUSED(resultState);
Q_UNUSED(result);
#endif // USE_CC_PLUGIN
}
void AppControl::onCCBookTotalTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result)
{
qCritical() << "AppControl::onCCBookTotalTransactionResult() result = " << result;
switch (resultState) {
case nsCCInterface::RESULT_STATE::SUCCESS:
this->vendingData->setParameter("RECEIPT", QVariant(result));
QTimer::singleShot(1000, this, SLOT(ccSendReceipt()));
break;
case nsCCInterface::RESULT_STATE::ERROR_BUSY:
qCritical() << "AppControl::onCCBookTotalTransactionResult() resultState is ERROR_BUSY";
break;
default:
qCritical() << "AppControl::onCCBookTotalTransactionResult() ERROR:";
qCritical() << " errorDescription = " << cc->getLastErrorDescription();
break;
}
}
void AppControl::onCCRevertTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result)
{
#ifdef USE_CC_PLUGIN
qCritical() << "AppControl::onCRevertTransactionResult() result = " << result;
if (resultState == nsCCInterface::RESULT_STATE::ERROR_BUSY) {
qCritical() << "AppControl::onCCRevertTransactionResult() resultState is ERROR_BUSY";
}
vmc->SendLongFormatedStringReference(result);
#else
Q_UNUSED(resultState);
Q_UNUSED(result);
#endif
}
/**
"{
"06": {
"1F0b": "000000010000",
"1F14": "ee910b469d5f40bcba9a4c71c062769e", <-- TOKEN
"1F45": "0c788074038031c073d631c0",
"1F4c": "01",
"1F4d": "fe04",
"1F4f": "0400",
"1F50": "20",
"4c": "0000000000000880afaa", <-- UID
"62": {
"60[0]": {
"41": "0005",
"43": "a0000003591010028001"
}
}
},
"23": "6725904400002036478d22122012845018450f",
"27": "00"
}"
*/
void AppControl::onCCRequestCardInfoResult(nsCCInterface::RESULT_STATE resultState, QString & result)
{
#ifdef USE_CC_PLUGIN
#ifdef USE_CC_READCARD
qDebug() << "AppControl::onCCRequestCardInfoResult(): ";
bool ok;
QVariantMap jsonCardInfo;
QVariantMap TLVContrainer;
QByteArray tokenBA;
QString cardInfo;
QString cardTransactionInfo;
JSON::setPrettySerialize(true);
JSON::JsonObject json;
switch (resultState) {
case nsCCInterface::RESULT_STATE::SUCCESS:
qDebug() << " resultState = SUCCESS";
jsonCardInfo = JSON::parse(result, ok).toMap();
if (ok) {
JSON::JsonObject
TLVContrainer = jsonCardInfo["06"].toMap();
tokenBA = TLVContrainer["1F14"].toString().toLatin1();
cardInfo = jsonCardInfo["23"].toString().mid(3,5);
this->vendingData->setParameter("INSTITUTSNUMMER", QVariant(cardInfo));
}
else {
qCritical() << "AppControl::onCCRequestCardInfoResult() resultState is SUCCESS but no valid JSON";
}
// note: cardTransactionInfo = "" -> empty sting
this->onCalculatePrice(cardInfo, cardTransactionInfo);
break;
case nsCCInterface::RESULT_STATE::ERROR_BUSY:
qCritical() << "AppControl::onCCRequestCardInfoResult() resultState is ERROR_BUSY";
if (this->isProgramModeSELL()) {
// set mode busy
hmi->setCCBusy(true);
QTimer::singleShot(2000, this, SLOT(onCCStartReadCard()));
}
break;
case nsCCInterface::RESULT_STATE::INFO:
qCritical() << "AppControl::onCCRequestCardInfoResult() resultState is INFO: " << result;
if (result.contains("STARTING CARD INFO READ ON CC")) {
hmi->setCCBusy(false);
hmi->enableCCAbort(true);
}
else
if (result.startsWith("04FF#")) {
hmi->displayCCMessage(result);
}
break;
case nsCCInterface::RESULT_STATE::ERROR_TIMEOUT:
qCritical() << "AppControl::onCCRequestCardInfoResult() resultState is ERROR_TIMEOUT: " << result;
/* FALLTHRU */
case nsCCInterface::RESULT_STATE::ERROR_BACKEND:
qCritical() << "AppControl::onCCRequestCardInfoResult() resultState is ERROR_BACKEND: " << result;
/* FALLTHRU */
case nsCCInterface::RESULT_STATE::ERROR_NETWORK:
qCritical() << "AppControl::onCCRequestCardInfoResult() resultState is ERROR_NETWORK: " << result;
/* FALLTHRU */
case nsCCInterface::RESULT_STATE::ERROR_PROCESS:
qCritical() << "AppControl::onCCRequestCardInfoResult() resultState is ERROR_PROCESS: " << result;
/* FALLTHRU */
case nsCCInterface::RESULT_STATE::ERROR_STATE:
qCritical() << "AppControl::onCCRequestCardInfoResult() resultState is ERROR_STATE: " << result;
qCritical() << " -> send ButtonCancel to vmc";
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x39); // "System error"
break;
}
qDebug() << " JSON-String = " << result;
#else
Q_UNUSED(resultState)
Q_UNUSED(result)
#endif // USE_CC_READCARD
#else
Q_UNUSED(resultState);
Q_UNUSED(result);
#endif // USE_CC_PLUGIN
}
void AppControl::onCCStartReadCard()
{
#ifdef USE_CC_PLUGIN
#ifdef USE_CC_READCARD
cc->requestCardInfo();
#endif
#endif
}
void AppControl::onCCErrorBackend(QString & result)
{
#ifdef USE_CC_PLUGIN
QString vmcResult; // 'String', sent to vmc
QString errorcode;
QString errortext;
QString protocol;
bool ok;
QVariantMap jsonResult = JSON::parse(result, ok).toMap();
if (ok) {
// new, JSON
errorcode = jsonResult["errorcode"].toString();
errortext = jsonResult["errortext"].toString();
protocol = jsonResult["protocol"].toString();
vmcResult[0] = 0x45;
vmcResult[1] = errorcode.toUInt(&ok, 16);
}
else {
// legacy
vmcResult[0] = result.at(0);
vmcResult[1] = result.at(1);
}
if (result.at(0) == 0x45 && result.at(1) == 0x00) {
qCritical() << "ASSERT: RESULT_STATE::ERROR_BACKEND: prevent sending 0x45 00 to VMC";
vmcResult[1] = 0xFF;
}
#if defined (CUST00318)
// hack for 00318/Nexobility
if (result.at(1) == 0x13)
this->private_CUST00318_handle_CC_ERROR_0x13();
else {
vmc->SendLongFormatedStringReference(vmcResult);
}
#else
vmc->SendLongFormatedStringReference(vmcResult);
#endif
#else
Q_UNUSED(result);
#endif
}
#ifdef USE_CALCULATEPRICE_PLUGIN
/*****************************************************************************
* CalculatePricePlugin
*/
void AppControl::onCalculatePrice(QString & licensePlate)
{
Q_UNUSED(licensePlate);
this->onCalculatePrice();
}
void AppControl::onCalculatePrice()
{
#if defined (CUST00300) || defined (CUST00324)
// PRM license plate validation:
QString licensePlateString = this->vendingData->getParameter("LICENSEPLATE").toString();
// cr20201118/2: switch off lp validation
/*
if (!LicencePlateValidator::validateLicensePlate(licensePlateString)) {
this->onShowMessageBox(MessageBoxType::NOBUTTON, 0x34, QStringList() << licensePlateString);
this->onCloseBusy();
return;
}
*/
// cr20201118/3: switch off lp re-entry
/*
int lpcount = vendingData->getParameter("LPCOUNT").toInt();
if (lpcount == 0) { // -> the first try to insert licenseplate
this->onShowMessageBox(MessageBoxType::NOBUTTON, 0x35, QStringList() << licensePlateString);
this->onCloseBusy();
vendingData->setParameter("LP1", QVariant(licensePlateString));
vendingData->setParameter("LPCOUNT", QVariant(1));
screenLicPlate->onClearUserInputData();
return;
}
else
if (lpcount == 1) { // -> the second try to insert licenseplate
vendingData->setParameter("LP2", QVariant(licensePlateString));
vendingData->setParameter("LPCOUNT", QVariant(2));
// compare to LPs:
if (vendingData->getParameter("LP1").toString() != vendingData->getParameter("LP2").toString()) {
// TODO: -> Headline Text, Screen 0x31: "Bitte Kennzeichen erneut eingeben"
this->onShowMessageBox(MessageBoxType::NOBUTTON, 0x36,
QStringList() << vendingData->getParameter("LP1").toString()
<< vendingData->getParameter("LP2").toString() );
this->onCloseBusy();
vendingData->setParameter("LPCOUNT", QVariant(0));
screenLicPlate->onClearUserInputData();
return;
}
// ... jetzt passts, jetzt wird die Preisabfrage gestellt.
}
else
if (lpcount == 2) {
qCritical() << "HMI::onCalculatePrice() PRM: invalid 'lpcount'";
vendingData->setParameter("LPCOUNT", QVariant(0));
this->onCloseBusy();
return;
}
else {
// ASSERT / LOG:
qCritical() << "HMI::onCalculatePrice() PRM: invalid state'";
vendingData->setParameter("LPCOUNT", QVariant(0));
this->onCloseBusy();
return;
}
*/
// cr20201118/4: re-entry if calculate price failed
int lpcount = vendingData->getParameter("LPCOUNT").toInt();
if (lpcount == 0) { // -> the first try to insert licenseplate
vendingData->setParameter("LPCOUNT", QVariant(1));
hmi->onClearUserInputFields();
}
else
if (lpcount == 1) { // -> the second try to insert licenseplate
vendingData->setParameter("LPCOUNT", QVariant(2));
}
else {
// ASSERT / LOG:
qCritical() << "HMI::onCalculatePrice() PRM: invalid state'";
vendingData->setParameter("LPCOUNT", QVariant(0));
hmi->onCloseBusy();
return;
}
#endif
#if defined (CUST00318)
// Nexobility
// CalculatePrice State is licenseplate only:
this->vendingData->setParameter("CP_STATE", QVariant().fromValue(CP_STATE::LICENSEPLATE_INPUT));
#endif
#if defined (CUST00328)
// ParkAndControl
QString licensePlateString = this->vendingData->getParameter("LICENSEPLATE").toString();
if (licensePlateString.length() == 0) return;
// re-entry if calculate price failed
int lpcount = vendingData->getParameter("LPCOUNT").toInt();
if (lpcount == 0) { // -> the first try to insert licenseplate
vendingData->setParameter("LPCOUNT", QVariant(1));
hmi->onClearUserInputFields();
}
else
if (lpcount == 1) { // -> the second try to insert licenseplate
vendingData->setParameter("LPCOUNT", QVariant(2));
}
else {
// ASSERT / LOG:
qCritical() << "HMI::onCalculatePrice() ParkAndControl: invalid state'";
vendingData->setParameter("LPCOUNT", QVariant(0));
hmi->onCloseBusy();
return;
}
#endif
#if defined (USE_CALCULATEPRICE_PLUGIN)
this->calcPriceBackend->requestCalculatePrice(
this->vendingData->getParameter("LICENSEPLATE").toString(),
"LICENSEPLATE",
this->vendingData->getParameter("PermitType").toString());
#endif
}
void AppControl::onCalculatePrice(uint parktime)
{
this->vendingData->setParameter("PARKTIME", QVariant(parktime));
// CalculatePrice State is licenseplate and parking time:
this->vendingData->setParameter("CP_STATE", QVariant().fromValue(CP_STATE::PARKINGTIME_INPUT));
#if defined (USE_CALCULATEPRICE_PLUGIN)
this->calcPriceBackend->requestCalculatePrice(
this->vendingData->getParameter("LICENSEPLATE").toString(),
"LICENSEPLATE",
this->vendingData->getParameter("PermitType").toString(),
this->vendingData->getParameter("PARKTIME").toString());
#endif
}
void AppControl::onCalculatePrice(QString & cardInfo, QString & cardTransactionInfo)
{
this->vendingData->setParameter("CARDINFO", QVariant(cardInfo));
this->vendingData->setParameter("CARDTRANSACTIONINFO", QVariant(cardTransactionInfo));
this->vendingData->setParameter("CP_STATE", QVariant().fromValue(CP_STATE::CARDINFO_INPUT));
#if defined (USE_CALCULATEPRICE_PLUGIN)
this->calcPriceBackend->requestCalculatePrice(
this->vendingData->getParameter("LICENSEPLATE").toString(),
"LICENSEPLATE",
this->vendingData->getParameter("PermitType").toString(),
this->vendingData->getParameter("PARKTIME").toString(),
this->vendingData->getParameter("CARDINFO").toString(),
this->vendingData->getParameter("CARDTRANSACTIONINFO").toString());
#endif
}
/*
* handle CalculatePrice response data
*
*/
void AppControl::onCalculatedPrice(nsCalculatePriceInterface::RESULT_STATE resultState,
const QString & accessInformation,
const QString & accessInformationType,
const QString & amountDuePeriodStart,
const QString & amountDuePeriodEnd,
const PriceInfo & priceInfo,
const QString & errorCode,
const QString & errorDescription)
{
// DEBUG ------------------------------------------------------------------------------------
qCritical() << "onCalculatedPrice() resultState = " << this->calcPriceBackend->getString(resultState) << "\n"
<< " accessInformation = " << accessInformation << "\n"
<< " accessInformationType = " << accessInformationType << "\n"
<< " amountDuePeriodStart = " << amountDuePeriodStart << "\n"
<< " amountDuePeriodEnd = " << amountDuePeriodEnd << "\n"
<< " priceInfo.priceNet = " << QString::number(priceInfo.priceNet) << "\n"
<< " priceInfo.priceTax = " << QString::number(priceInfo.priceTax) << "\n"
<< " priceInfo.priceGross = " << QString::number(priceInfo.priceGross) << "\n"
<< " priceInfo.taxRate = " << QString::number(priceInfo.taxRate) << "\n"
<< " errorCode = " << errorCode << "\n"
<< " errorDescription = " << errorDescription;
nsCalculatePriceInterface::RESULT_STATE resultState_priv = resultState;
#if defined (ARCH_DesktopLinux)
// TEST / SIMULATION
// To call parktime input screen
/*
int simulation_run = vendingData->getParameter("SIMULATION_RUN").toInt();
if (simulation_run == 0) {
qDebug() << "HMI::onCalculatedPrice() Simulation run 1: ERROR_BACKEND";
resultState_priv = nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND;
vendingData->setParameter("SIMULATION_RUN", QVariant(1));
}
else {
qDebug() << "HMI::onCalculatedPrice() Simulation run 2";
}
*/
#endif
QString resultMessage;
switch(resultState_priv) {
case nsCalculatePriceInterface::RESULT_STATE::SUCCESS:
resultMessage = "CalculatePrice success";
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND:
// in this case we print only the message from backend
resultMessage = errorDescription;
//resultMessage = "CalculatePrice error: BACKEND\n";
//resultMessage.append(" errorCode: ").append(errorCode);
//resultMessage.append(" errorDescription: ").append(errorDescription);
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_NETWORK:
resultMessage = "CalculatePrice error: NETWORK\n";
resultMessage.append(" errorCode: ").append(errorCode).append("\n");
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_TIMEOUT:
resultMessage = "CalculatePrice error: TIMEOUT\n";
resultMessage.append(" errorCode: ").append(errorCode).append("\n");
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_PROCESS:
resultMessage = "CalculatePrice error: PROCESS\n";
resultMessage.append(" errorCode: ").append(errorCode).append("\n");
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_RETRY:
resultMessage = "CalculatePrice error: RETRY\n";
resultMessage.append(" errorCode: ").append(errorCode).append("\n");
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
default:
resultMessage = "CalculatePrice error: Unknown Error\n";
resultMessage.append(" errorCode: ").append(errorCode).append("\n");
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
}
qCritical() << resultMessage;
// call project specific handler
QString VMCCalcString;
VMCCalcString = private_calculatedPrice(resultState,
accessInformation,
accessInformationType,
amountDuePeriodStart,
amountDuePeriodEnd,
priceInfo,
errorCode,
errorDescription);
if (VMCCalcString.size() > 0) {
// Send D0-CALC to VMC ----------------------------------------------
// -> expect a screen change / response from VMC
// DEBUG
qCritical() << "onCalculatedPrice() VMCCalcString: " << VMCCalcString;
hmi->onShowBusy(1);
vmc->SendFormatedString(VMCCalcString);
}
else {
// do not send something to VMC -------------------------------------
// -> no response / action from VMC is expected
// -> this is e.g. in case of a new tariff step
hmi->onCloseBusy();
}
/*
vcl->SendAmountDueNet(QString::number(priceInfo.priceNet));
vcl->SendAmountDueTax(QString::number(priceInfo.priceTax);
vcl->SendAmountDuePeriodStart(amountDuePeriodStart);
vcl->SendAmountDuePeriodEnd(amountDuePeriodEnd);
*/
// store values in vendingData: to detect a possible missing transaction
vendingData->setParameter("TRANS", QVariant(1)); // marker, that transaction is pending
vendingData->setParameter("ACCESSINFORMATION", QVariant(accessInformation));
}
/**
* @brief AppControl::private_getAccessInformationField
* @param accessInformationType
* @return QChar
*
* Transform accessInformationType-String to a QChar for usage in VMC formated String
*/
QChar AppControl::private_getAccessInformationField(const QString & accessInformationType)
{
QChar accessInformationTypeField;
if (accessInformationType == "LICENSEPLATE") {
accessInformationTypeField = '1';
} else
if (accessInformationType == "BARCODE") {
accessInformationTypeField = '2';
} else
if (accessInformationType == "RFID") {
accessInformationTypeField = '3';
} else
if (accessInformationType == "QRCODE") {
accessInformationTypeField = '4';
} else
if (accessInformationType == "PIN") {
accessInformationTypeField = '5';
} else
if (accessInformationType == "UID") {
accessInformationTypeField = '6';
} else {
accessInformationTypeField = '\0';
qCritical() << "onCalculatedPrice() accessInformationType " << accessInformationType
<< " is currently not processed!";
}
return accessInformationTypeField;
}
#if defined (CUST00297) || defined (CUST00271) || defined (CUST00218)
// Lithunia (Kaunas, KaunasMuseum, Panevezys):
QString AppControl::private_calculatedPrice(nsCalculatePriceInterface::RESULT_STATE resultState,
const QString & accessInformation,
const QString & accessInformationType,
const QString & amountDuePeriodStart,
const QString & amountDuePeriodEnd,
const PriceInfo & priceInfo,
const QString & errorCode,
const QString & errorDescription)
{
Q_UNUSED(errorCode)
QString formatedString;
QChar accessInformationTypeField = this->private_getAccessInformationField(accessInformationType);
switch(resultState) {
case nsCalculatePriceInterface::RESULT_STATE::SUCCESS:
// on SUCCESS: send SUCCESS-formatedString
formatedString.append("#CALC#");
formatedString.append(accessInformation).append('#');
formatedString.append(accessInformationTypeField).append('#');
formatedString.append(amountDuePeriodStart).append('#');
formatedString.append(amountDuePeriodEnd).append('#');
formatedString.append(QString::number(priceInfo.priceNet)).append('#');
formatedString.append(QString::number(priceInfo.priceTax)).append('#');
formatedString.append("").append('#'); // <- grace period, - deprecated
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_NETWORK:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_TIMEOUT:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_PROCESS:
// show message box
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, "", errorDescription);
// send abort to vmc ... is sent by message box
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_RETRY:
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x35);
break;
}
return formatedString;
}
// END: Lithunia (Kaunas, KaunasMuseum, Panevezys)
#elif defined (CUST02100)
// Goldbeck -> for fallback tariff
QString AppControl::private_calculatedPrice(nsCalculatePriceInterface::RESULT_STATE resultState,
const QString & accessInformation,
const QString & accessInformationType,
const QString & amountDuePeriodStart,
const QString & amountDuePeriodEnd,
const PriceInfo & priceInfo,
const QString & errorCode,
const QString & errorDescription)
{
Q_UNUSED(errorCode)
Q_UNUSED(errorDescription)
QString formatedString;
QChar accessInformationTypeField = this->private_getAccessInformationField(accessInformationType);
switch(resultState) {
case nsCalculatePriceInterface::RESULT_STATE::SUCCESS:
// on SUCCESS: send SUCCESS-formatedString
formatedString.append("#CALC#");
formatedString.append(accessInformation).append('#');
formatedString.append(accessInformationTypeField).append('#');
formatedString.append(amountDuePeriodStart).append('#');
formatedString.append(amountDuePeriodEnd).append('#');
formatedString.append(QString::number(priceInfo.priceNet)).append('#');
formatedString.append(QString::number(priceInfo.priceTax)).append('#');
formatedString.append("").append('#'); // <- grace period, - deprecated
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_NETWORK:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_TIMEOUT:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_PROCESS:
formatedString.append("#CALC#");
formatedString.append(accessInformation).append('#');
formatedString.append("0").append('#');
formatedString.append("ERROR").append('#');
// VMC can not handle ISO8859 encoded error texts!!!!
// -> formatedString.append(errorText).append('#');
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_RETRY:
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x35);
break;
}
return formatedString;
}
// END: Goldbeck
#elif defined (CUST00300) || defined (CUST00324)
// PRM / ARIVO: "honest parking"
QString AppControl::private_calculatedPrice(nsCalculatePriceInterface::RESULT_STATE resultState,
const QString & accessInformation,
const QString & accessInformationType,
const QString & amountDuePeriodStart,
const QString & amountDuePeriodEnd,
const PriceInfo & priceInfo,
const QString & errorCode,
const QString & errorDescription)
{
Q_UNUSED(errorCode)
Q_UNUSED(errorDescription)
QString formatedString;
int lpcount;
QChar accessInformationTypeField = this->private_getAccessInformationField(accessInformationType);
switch(resultState) {
case nsCalculatePriceInterface::RESULT_STATE::SUCCESS:
// on SUCCESS: send SUCCESS-formatedString
formatedString.append("#CALC#");
formatedString.append(accessInformation).append('#');
formatedString.append(accessInformationTypeField).append('#');
formatedString.append(amountDuePeriodStart).append('#');
formatedString.append(amountDuePeriodEnd).append('#');
formatedString.append(QString::number(priceInfo.priceNet)).append('#');
formatedString.append(QString::number(priceInfo.priceTax)).append('#');
formatedString.append("").append('#'); // <- grace period, - deprecated
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND:
lpcount = vendingData->getParameter("LPCOUNT").toInt();
if (lpcount == 1) { // -> re-enter lp
// -> Msg-Box: "Kennzeichen erneut eingeben":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x35, QStringList() << this->vendingData->getParameter("LICENSEPLATE").toString());
}
else
if (lpcount == 2) { // -> Input Parking time (honest payment)
if (config->useHonestPayment()) {
// note: in case of an error, 'accessInformation' is possibly empty so we use vendingData:
hmi->onDisplayData(0x48, this->vendingData->getParameter("LICENSEPLATE").toString());
hmi->showScreen(Ui::SCREEN_PARKTIME1);
// -> Msg-Box: "Parkdauer eingeben":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x37, QStringList() << this->vendingData->getParameter("LICENSEPLATE").toString());
}
else {
// -> Msg-Box: "Kennzeichen wurde nicht gefunden":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_SEND_CANCEL, 0x40);
}
vendingData->setParameter("LPCOUNT", QVariant(0));
}
else {
// note: this could happen, e.g. if calculatePrice returns an error on an request with parking time parameter.
qCritical() << "onCalculatedPrice(): invalid lpcount!";
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x38); // Network Error
vendingData->setParameter("LPCOUNT", QVariant(0));
}
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_NETWORK:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_TIMEOUT:
// Network Error
qCritical() << "onCalculatedPrice(): Network error!";
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x38); // Network Error
vendingData->setParameter("LPCOUNT", QVariant(0));
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_PROCESS:
qCritical() << "onCalculatedPrice(): System error!";
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x39); // System Error
vendingData->setParameter("LPCOUNT", QVariant(0));
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_RETRY:
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x35);
break;
}
return formatedString;
}
// END: PRM / ARIVO
#elif defined (CUST00318)
// Nexobility: "honest parking"
QString AppControl::private_calculatedPrice(nsCalculatePriceInterface::RESULT_STATE resultState,
const QString & accessInformation,
const QString & accessInformationType,
const QString & amountDuePeriodStart,
const QString & amountDuePeriodEnd,
const PriceInfo & priceInfo,
const QString & errorCode,
const QString & errorDescription)
{
Q_UNUSED(errorCode)
Q_UNUSED(errorDescription)
QString formatedString;
CP_STATE cp_state = vendingData->getParameter("CP_STATE").value<CP_STATE>();
QChar accessInformationTypeField = this->private_getAccessInformationField(accessInformationType);
switch(resultState) {
case nsCalculatePriceInterface::RESULT_STATE::SUCCESS:
// on SUCCESS: send SUCCESS-formatedString
formatedString.append("#CALC#");
formatedString.append(accessInformation).append('#');
formatedString.append(accessInformationTypeField).append('#');
formatedString.append(amountDuePeriodStart).append('#');
formatedString.append(amountDuePeriodEnd).append('#');
formatedString.append(QString::number(priceInfo.priceNet)).append('#');
formatedString.append(QString::number(priceInfo.priceTax)).append('#');
formatedString.append("").append('#'); // <- grace period, - deprecated
// Nexobility: Discount
if (vendingData->getParameter("CP_STATE").value<CP_STATE>() == CP_STATE::CARDINFO_INPUT) {
// DISCOUNT:
int oldAmountNet = this->vendingData->getParameter("PRICE").toString().toInt();
int newAmountNet = priceInfo.priceNet;
if (newAmountNet < oldAmountNet) {
// discount is valid - show a message box with new price:
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_SUCCESS, 0x34,
QStringList() << utils::getPriceString(priceInfo.priceNet, priceInfo.priceTax));
}
else {
// discount is not valid - show a message box with error:
hmi->onShowMessageBox(MessageBoxType::OK_FAILURE, 0x3a,
QStringList() << utils::getPriceString(priceInfo.priceNet, priceInfo.priceTax));
}
// Trigger VMC on CloseButton sent from message box
vendingData->setParameter("CloseAction", QVariant::fromValue(APP_ACTION::SEND_D0_CALC));
vendingData->setParameter("D0_CALC", QVariant(formatedString));
formatedString.clear();
}
else {
this->vendingData->setParameter("PRICE", QVariant(utils::getPriceString(priceInfo.priceNet)));
}
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND:
// action depends on CP_STATE:
switch (cp_state) {
case CP_STATE::LICENSEPLATE_INPUT:
// 1st request: only with license plate
// Ask if licence plate is correct
hmi->onShowMessageBox(MessageBoxType::YESNO, 0x35, QStringList() << this->vendingData->getParameter("LICENSEPLATE").toString());
vendingData->setParameter("YesAction", QVariant::fromValue(APP_ACTION::PARKINGTIME_INPUT));
vendingData->setParameter("NoAction", QVariant::fromValue(APP_ACTION::LICENSEPLATE_INPUT));
qCritical() << "-> CP: ERROR_BACKEND - CP_STATE = LICENSEPLATE_INPUT";
break;
case CP_STATE::PARKINGTIME_INPUT:
// - possibly additional request: with parking time
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x39); // "System error"
qCritical() << "-> CP: ERROR_BACKEND - CP_STATE = PARKINGTIME_INPUT";
break;
case CP_STATE::CARDINFO_INPUT:
// - 2nd request: with license plate and cardInfo
// Note: this should be a very rare case (2nd request fails!)
// It is easier to cancel vending process here. Normally we should resend the
// formated string again to vmc.
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x36); // "Keine Rabattierung möglich"
qCritical() << "-> CP: ERROR_BACKEND - CP_STATE = CARDINFO_INPUT";
break;
case CP_STATE::NO_STATE:
qCritical() << "onCalculatedPrice(): CP_STATE::NO_STATE";
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x39); // System Error
break;
}
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_NETWORK:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_TIMEOUT:
// Network Error
qCritical() << "onCalculatedPrice(): Network error!";
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x38); // Network Error
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_PROCESS:
// System Error
qCritical() << "onCalculatedPrice(): System error!";
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x39); // System Error
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_RETRY:
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x35);
break;
}
return formatedString;
}
// END: Nexobility
#elif defined (CUST00328)
// ParkAndControl
QString AppControl::private_calculatedPrice(nsCalculatePriceInterface::RESULT_STATE resultState,
const QString & accessInformation,
const QString & accessInformationType,
const QString & amountDuePeriodStart,
const QString & amountDuePeriodEnd,
const PriceInfo & priceInfo,
const QString & errorCode,
const QString & errorDescription)
{
Q_UNUSED(errorCode)
Q_UNUSED(errorDescription)
// this is sent to VMC:
QString formatedString;
int lpcount = vendingData->getParameter("LPCOUNT").toInt();
// reference time to setup dateTime input:
QDateTime referenceDateTime = QDateTime::currentDateTime();
QChar accessInformationTypeField = this->private_getAccessInformationField(accessInformationType);
// currently tarif calculation depends on certain screens
Ui::SCREEN currentScreen = this->hmi->getCurrentScreen();
// Note for APCOA: this plugin does always provide valid sales data. Even e.g. if lookup
// fails for some reason (e.g. no network is available).
// So we have TODO:
// - set HMI-Data (onDisplayData) in any case / or even on certain Errors (ERROR_NETWORK)
// - send formated string to VMC if necessary if we need a screen change (e.g. switch to
// parking-time input.
switch(resultState) {
case nsCalculatePriceInterface::RESULT_STATE::SUCCESS:
// on SUCCESS: send SUCCESS-formatedString
formatedString.append("#CALC#");
formatedString.append(accessInformation).append('#');
formatedString.append(accessInformationTypeField).append('#');
formatedString.append(amountDuePeriodStart).append('#');
formatedString.append(amountDuePeriodEnd).append('#');
formatedString.append(QString::number(priceInfo.priceNet)).append('#');
formatedString.append(QString::number(priceInfo.priceTax)).append('#');
formatedString.append("").append('#'); // <- grace period, - deprecated
// set HMI-Data
this->hmi->onDisplayData(0x48, accessInformation);
this->hmi->onDisplayData(0x51, utils::getPriceStringFormated(priceInfo.priceGross,this->config->getPaymentCurrencySymbol()));
switch (config->getPaymentPointInTime()) {
case PAYMENT_POINTINTIME::PAY_ON_ARRIVAL:
this->hmi->onDisplayData(0x50, utils::getTimeStringFormated(amountDuePeriodEnd)); // Displayed ParktimeEnd
this->hmi->onDisplayData(0x54, utils::getTimeStringFormated(amountDuePeriodStart)); // Displayed EntryTime
break;
case PAYMENT_POINTINTIME::PAY_ON_EXIT:
this->hmi->onDisplayData(0x50, utils::getTimeStringFormated(amountDuePeriodStart)); // Displayed EntryTime, EndTime is currentTime
this->hmi->onDisplayData(0x54, utils::getTimeStringFormated(amountDuePeriodStart)); // Displayed EntryTime, EndTime is currentTime
break;
case PAYMENT_POINTINTIME::IGNORE:
this->hmi->onDisplayData(0x50, utils::getTimeStringFormated(amountDuePeriodStart)); // Displayed EntryTime, EndTime is not relevant
this->hmi->onDisplayData(0x54, utils::getTimeStringFormated(amountDuePeriodStart)); // Displayed EntryTime, EndTime is not relevant
break;
}
// set vendingData
this->vendingData->setParameter("AMOUNT", utils::getPriceByteArray(priceInfo.priceGross));
this->vendingData->setParameter("DATETIME_PARKEND", amountDuePeriodEnd);
// ensure, that "D0_CALC"-string is always initialized and prepared to send to VMC
vendingData->setParameter("D0_CALC", QVariant(formatedString));
switch (currentScreen) {
case Ui::SCREEN_LICPLT:
case Ui::SCREEN_DATETIME1: // honest payment: with entry date/time
case Ui::SCREEN_PARKTIME1: // honest payment: with parkingtime in minutes
// Trigger VMC immediately
break;
case Ui::SCREEN_PARKEND: // on selecting parking endtime
// Trigger VMC on NextButton, do not send formatedString immediately
formatedString.clear();
break;
default:
qCritical() << "AppControl::private_calculatedPrice() called with invalid screen.";
formatedString.clear(); // do not send formatedString
break;
}
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_TIMEOUT:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_NETWORK:
case nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND:
qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate)
<< "onCalculatedPrice(): ERROR_TIMEOUT || ERROR_NETWORK || ERROR_BACKEND:" << endl
<< " honest payment = " << config->useHonestPayment() << endl
<< " current screen = " << "0x" + QString(static_cast<quint8>(currentScreen)).toLatin1().toHex() << endl
<< " paymentTime = " << config->getPaymentPointInTime();
if (config->useHonestPayment()) {
// ------------------------------ honest payment ------------------------------------
switch (currentScreen) {
case Ui::SCREEN_LICPLT:
// cause honest payment
// => #CALC empty price
if (lpcount == 1) { // -> re-enter lp
// -> Msg-Box: "Kennzeichen erneut eingeben":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x35);
}
else {
formatedString.append("#CALC#");
formatedString.append(accessInformation).append('#');
formatedString.append(accessInformationTypeField).append('#');
formatedString.append(amountDuePeriodStart).append('#');
formatedString.append(amountDuePeriodEnd).append('#');
formatedString.append("").append('#'); // empty price
formatedString.append("").append('#'); // empty price
formatedString.append("").append('#'); // <- grace period, - deprecated
// ... and set HMI-Data (price is still not valid)
this->hmi->onDisplayData(0x48, accessInformation);
// setup HMI screen DateTime --------------------------
hmi->setupDateTimeInput(referenceDateTime,
Ui::DateTimeInputMode::Down,
referenceDateTime,
referenceDateTime.addDays(-10));
// prepare "D0_CALC"-string to send to VMC
vendingData->setParameter("D0_CALC", QVariant(formatedString));
// Trigger VMC on CloseButton sent from message box
vendingData->setParameter("CloseAction", QVariant::fromValue(APP_ACTION::SEND_D0_CALC));
formatedString.clear();
// -> Msg-Box: "Parkdauer eingeben":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x37, QStringList() << this->vendingData->getParameter("LICENSEPLATE").toString());
vendingData->setParameter("LPCOUNT", QVariant(0));
}
break; // Ui::SCREEN_LICPLT
default:
// should not occur!
// => log and ignore:
qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate)
<< "AppControl::private_calculatedPrice() ERROR_TIMEOUT || ERROR_NETWORK || ERROR_BACKEND: invalid screen.";
qCritical() << " currentScreen = " << "0x" + QString(static_cast<quint8>(currentScreen)).toLatin1().toHex();
formatedString.clear(); // do not send formatedString
break;
}
}
else {
// ------------------------------ *no* honest payment ------------------------------------
switch (currentScreen) {
case Ui::SCREEN_LICPLT:
// repeat endlessly
// until „Abort“ or match
// -> Msg-Box: "Kennzeichen XY wurde nicht gefunden, Bitte erneut eingeben":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x3A, QStringList() << this->vendingData->getParameter("LICENSEPLATE").toString());
formatedString.clear(); // do not send formatedString
hmi->onClearUserInputFields(); // clear input fields in hmi
break;
default:
// should not occur!
// => log and ignore:
qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate)
<< "AppControl::private_calculatedPrice() ERROR_TIMEOUT || ERROR_NETWORK || ERROR_BACKEND: invalid screen.";
qCritical() << " currentScreen = " << QString(static_cast<quint8>(currentScreen)).toLatin1().toHex();
formatedString.clear(); // do not send formatedString
break;
}
}
break; // ERROR_TIMEOUT || ERROR_NETWORK || ERROR_BACKEND
case nsCalculatePriceInterface::RESULT_STATE::ERROR_PROCESS:
// System Error
qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate)
<< "onCalculatedPrice(): ERROR_PROCESS: System error!";
switch (currentScreen) {
case Ui::SCREEN_LICPLT:
hmi->onShowMessageBox(MessageBoxType::NOBUTTON_FAILURE, 0x39); // System Error
break;
default:
break;
}
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_RETRY:
qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate)
<< "onCalculatedPrice(): ERROR_RETRY:" << endl
<< " honest payment = " << config->useHonestPayment() << endl
<< " current screen = " << "0x" + QString(static_cast<quint8>(currentScreen)).toLatin1().toHex() << endl
<< " paymentTime = " << config->getPaymentPointInTime();
if (config->useHonestPayment()) {
// ------------------------------ honest payment ------------------------------------
switch (currentScreen) {
case Ui::SCREEN_LICPLT:
if (lpcount == 1) { // -> re-enter lp
// -> Msg-Box: "Kennzeichen erneut eingeben":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x35);
}
else {
switch (config->getPaymentPointInTime()) {
case PAYMENT_POINTINTIME::PAY_ON_ARRIVAL:
case PAYMENT_POINTINTIME::PAY_ON_EXIT:
// cause honest payment
// => #CALC empty price
formatedString.append("#CALC#");
formatedString.append(accessInformation).append('#');
formatedString.append(accessInformationTypeField).append('#');
formatedString.append(amountDuePeriodStart).append('#');
formatedString.append(amountDuePeriodEnd).append('#');
formatedString.append("").append('#'); // empty price
formatedString.append("").append('#'); // empty price
formatedString.append("").append('#'); // <- grace period, - deprecated
// ... and set HMI-Data (price is still not valid)
this->hmi->onDisplayData(0x48, accessInformation);
// setup HMI screen DateTime --------------------------
hmi->setupDateTimeInput(referenceDateTime,
Ui::DateTimeInputMode::Down,
referenceDateTime,
referenceDateTime.addDays(-10));
// prepare "D0_CALC"-string to send to VMC
vendingData->setParameter("D0_CALC", QVariant(formatedString));
// Trigger VMC on CloseButton sent from message box
vendingData->setParameter("CloseAction", QVariant::fromValue(APP_ACTION::SEND_D0_CALC));
// -> Msg-Box: "Parkdauer eingeben":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x37, QStringList() << this->vendingData->getParameter("LICENSEPLATE").toString());
break;
case PAYMENT_POINTINTIME::IGNORE:
// Calculate price without match
// => #CALC with price to VMC
#if defined (USE_CALCULATEPRICE_PLUGIN)
this->calcPriceBackend->requestCalculatePrice(
this->vendingData->getParameter("LICENSEPLATE").toString(),
"LICENSEPLATE",
this->vendingData->getParameter("PermitType").toString(),
"60"); // default parking time: 60min
#endif
break;
}
formatedString.clear(); // do not send formatedString
vendingData->setParameter("LPCOUNT", QVariant(0));
}
break; // Ui::SCREEN_LICPLT
default:
// should not occur!
// => log and ignore:
qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate)
<< "AppControl::private_calculatedPrice() ERROR_RETRY: invalid screen.";
qCritical() << " currentScreen = " << "0x" + QString(static_cast<quint8>(currentScreen)).toLatin1().toHex();
formatedString.clear(); // do not send formatedString
break;
}
}
else {
// ------------------------------ *no* honest payment ------------------------------------
switch (currentScreen) {
case Ui::SCREEN_LICPLT:
// repeat endlessly
// until „Abort“ or match
// -> Msg-Box: "Kennzeichen XY wurde nicht gefunden, Bitte erneut eingeben":
hmi->onShowMessageBox(MessageBoxType::NOBUTTON, 0x3A, QStringList() << this->vendingData->getParameter("LICENSEPLATE").toString());
formatedString.clear(); // do not send formatedString
hmi->onClearUserInputFields(); // clear input fields in hmi
break;
default:
// should not occur!
// => log and ignore:
qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate)
<< "AppControl::private_calculatedPrice() ERROR_RETRY: invalid screen.";
qCritical() << " currentScreen = " << QString(static_cast<quint8>(currentScreen)).toLatin1().toHex();
formatedString.clear(); // do not send formatedString
break;
}
}
break;
case nsCalculatePriceInterface::RESULT_STATE::INFO:
break;
}
// send formatedString (aka "#CALC#.." to VMC only if size > 0;
return formatedString;
}
// END: ParkAndControl
#else
// dummy for all other projects
QString AppControl::private_calculatedPrice(nsCalculatePriceInterface::RESULT_STATE resultState,
const QString & accessInformation,
const QString & accessInformationType,
const QString & amountDuePeriodStart,
const QString & amountDuePeriodEnd,
const PriceInfo & priceInfo,
const QString & errorCode,
const QString & errorDescription)
{
Q_UNUSED(resultState)
Q_UNUSED(accessInformation)
Q_UNUSED(accessInformationType)
Q_UNUSED(amountDuePeriodStart)
Q_UNUSED(amountDuePeriodEnd)
Q_UNUSED(priceInfo)
Q_UNUSED(errorCode)
Q_UNUSED(errorDescription)
qCritical() << "AppControl::private_calculatedPrice() dummy method called";
return "";
}
#endif
void AppControl::onProcessTransaction(nsCalculatePriceInterface::RESULT_STATE resultState,
const QString & errorCode,
const QString & errorDescription)
{
QString resultMessage;
switch(resultState) {
case nsCalculatePriceInterface::RESULT_STATE::SUCCESS:
resultMessage = "Transaction success";
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND:
resultMessage = "Transaction error: BACKEND\n";
resultMessage.append(" errorCode: ").append(errorCode);
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_NETWORK:
resultMessage = "Transaction error: NETWORK\n";
resultMessage.append(" errorCode: ").append(errorCode);
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_TIMEOUT:
resultMessage = "Transaction error: TIMEOUT\n";
resultMessage.append(" errorCode: ").append(errorCode);
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
case nsCalculatePriceInterface::RESULT_STATE::ERROR_PROCESS:
resultMessage = "Transaction error: PROCESS\n";
resultMessage.append(" errorCode: ").append(errorCode);
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
default:
resultMessage = "Transaction error: Unknown Error\n";
resultMessage.append(" errorCode: ").append(errorCode);
resultMessage.append(" errorDescription: ").append(errorDescription);
break;
}
qCritical() << resultMessage;
}
#endif // USE_CALCULATEPRICE_PLUGIN
void AppControl::onWakeUp()
{
}
/********************************************************************************
* LED configurations
*
* default: one LED, green, slow flash
* ooo: one LED, red, slow flash
*/
void AppControl::LEDs_default()
{
system->onConfigLED(ATB_system::ATB_LED1, ATB_system::LED_TRIGGER_TIMER, 200, 2000);
system->onConfigLED(ATB_system::ATB_LED2, ATB_system::LED_TRIGGER_NONE);
system->onConfigLED(ATB_system::ATB_LED3, ATB_system::LED_TRIGGER_NONE);
system->onConfigLED(ATB_system::ATB_LED4, ATB_system::LED_TRIGGER_NONE);
}
void AppControl::LEDs_ooo()
{
system->onConfigLED(ATB_system::ATB_LED1, ATB_system::LED_TRIGGER_NONE);
system->onConfigLED(ATB_system::ATB_LED2, ATB_system::LED_TRIGGER_NONE);
system->onConfigLED(ATB_system::ATB_LED3, ATB_system::LED_TRIGGER_NONE);
system->onConfigLED(ATB_system::ATB_LED4, ATB_system::LED_TRIGGER_TIMER, 500, 1000);
}
/*******************************************************************************
* timer timeout slots
*
*/
void AppControl::onSellModeTimerTimeout()
{
this->vmc->SendSystemMessageTimeout();
this->requestProgramMode(PROGRAM_MODE::IDLE);
}
/*******************************************************************************
* Signal System Errors
*
* System errors are errors that do not allow further execution of the program.
*/
void AppControl::onSignalSystemErrors(const QString & errorCode, const QString & errorDescription)
{
// TODO:
// -> send monitoring message
// -> go to ModeOOO
// ...
Q_UNUSED(errorCode);
Q_UNUSED(errorDescription);
}
/*******************************************************************************
* action handling
*/
quint8 AppControl::startAction(APP_ACTION action)
{
switch (action) {
case APP_ACTION::DEFAULT_ACTION:
qDebug() << "----> startAction: APP_ACTION::DEFAULT_ACTION";
break;
case APP_ACTION::LICENSEPLATE_INPUT:
qDebug() << "----> startAction: APP_ACTION::LICENSEPLATE_INPUT";
break;
case APP_ACTION::PARKINGTIME_INPUT:
qDebug() << "----> startAction: APP_ACTION::PARKINGTIME_INPUT";
break;
case APP_ACTION::CARD_PAYMENT:
qDebug() << "----> startAction: APP_ACTION::CARD_PAYMENT";
break;
case APP_ACTION::CHOOSE_PAYEMENT:
qDebug() << "----> startAction: APP_ACTION::CHOOSE_PAYEMENT";
break;
case APP_ACTION::COIN_PAYMENT_PAYUP:
qDebug() << "----> startAction: APP_ACTION::COIN_PAYMENT_PAYUP";
break;
case APP_ACTION::COIN_PAYMENT_PAYDOWN:
qDebug() << "----> startAction: APP_ACTION::COIN_PAYMENT_PAYDOWN";
break;
case APP_ACTION::SEND_D0_CALC:
qDebug() << "----> startAction: APP_ACTION::SEND_D0_CALC";
if (vendingData->hasParameter("D0_CALC")) {
// DEBUG
qCritical() << "startAction() APP_ACTION::SEND_D0_CALC: formatedString: " << vendingData->getParameter("D0_CALC").toString();
this->vmc->SendFormatedString(vendingData->getParameter("D0_CALC").toString());
}
break;
}
return 0;
}
/*****************************************************************************
* MOUSE handling
*/
#if defined (ARCH_PTU4)
/*****************************************************************************
* private USB Input Device handling methods
*
* This slot is called if something in the directory '/dev/input/' has changed (e.g.
* if a USB-mouse is plugged in/out or if a
* "hub 1-0:1.0: port 2 disabled by hub (EMI?), re-enabling..." occurs and re-inits
* the linux input devices.
*/
// Slot to receive a removed or added input device
void AppControl::onInputDeviceWatcher_directoryChanged(const QString& path)
{
Q_UNUSED(path)
QString mouseDeviceFile = system->getEventDeviceName(ATB_system::EVENT_DEVICE_USBMOUSE);
QString touchDeviceFile = system->getEventDeviceName(ATB_system::EVENT_DEVICE_TOUCHSCREEN);
qCritical() << "AppControl::onInputDeviceWatcher_directoryChanged() mouseDeviceFile: " << mouseDeviceFile;
if (!mousePresent && mouseDeviceFile.contains("mouse")) {
// mouse is now present:
setMouse(mouseDeviceFile, touchDeviceFile);
} else if (mousePresent && !mouseDeviceFile.contains("mouse")) {
// mouse is not present anymore:
closeMouse(mouseDeviceFile, touchDeviceFile);
} else if (!mousePresent) {
// mouse is not present (e.g. on startup, without mouse)
QWSServer::setCursorVisible( false );
}
// reinit USB-HID (qrcode scanner / RFID-reader)
#ifdef USE_BARCODESCANNER
// reinit barcode scanner
if (config->getUseBarcodeScanner()) {
this->barcodeScanner->reopen(this->config->getBarcodeScannerInputDevice());
}
#endif
#ifdef USE_RFIDREADER
// reinit rfid reader
if (config->getUseRFIDReader()) {
this->rfidReader->reopen(this->config->getRFIDReaderInputDevice());
}
#endif
}
void AppControl::setMouse(const QString & mouseDeviceFile, const QString &touchDeviceFile)
{
Q_UNUSED(touchDeviceFile)
QWSMouseHandler *pMouseHandler = QMouseDriverFactory::create("IntelliMouse", mouseDeviceFile);
QWSServer* pQwsServer = QWSServer::instance();
pQwsServer->setMouseHandler(pMouseHandler);
QWSServer::setCursorVisible( true );
mousePresent = true;
// DEBUG
qCritical() << "setMouse(): using device: " << mouseDeviceFile;
}
void AppControl::closeMouse(const QString & mouseDeviceFile, const QString &touchDeviceFile)
{
// TODO: only openMouse(), if a touchscreen is available; hide mouse pointer in this case
// test: setDefaultMouse()
//
QWSServer* pQwsServer = QWSServer::instance();
// DEBUG
qCritical() << "closeMouse(): touch device: " << touchDeviceFile;
if (touchDeviceFile.contains("event")) {
// touch device is available:
// open mouse device specified by QWS_MOUSE_PROTO -> this is normally the touchscreen...
pQwsServer->openMouse();
}
else {
// touch device is not available:
pQwsServer->closeMouse();
// -> close all pointer devices (defined by QWS_MOUSE_PROTO)
}
//pQwsServer->closeMouse(); -> close all pointer devices (also defined by QWS_MOUSE_PROTO => also the touch screen!!!)
QWSServer::setCursorVisible( false );
mousePresent = false;
// DEBUG
qCritical() << "closeMouse(): device: " << mouseDeviceFile;
}
#endif
#if defined (ARCH_DesktopLinux)
/*******************************************************************************
* Screenshot
*/
void AppControl::private_screenshot()
{
QString filename = QDir::homePath() + "/tmp/ATBQT_screenshot_0x"
+ QString(static_cast<quint8>(this->hmi->getCurrentScreen())).toLatin1().toHex()
+ ".png";
qDebug() << "HMI::private_screenshot(): save screenshot to: " << filename;
QWidget *w = QApplication::activeWindow();
if(w) {
QPixmap p = QPixmap::grabWidget(w);
p.save(QString(filename));
}
}
#endif
/**************************************************************************
* helpers:
* - operators
* - ...
*/
QDebug operator<<(QDebug debug, PROGRAM_MODE mode) {
switch (mode) {
case PROGRAM_MODE::IDLE:
debug << "PROGRAM_MODE::IDLE";
break;
case PROGRAM_MODE::SELL_ENABLE:
debug << "PROGRAM_MODE::SELL_ENABLE";
break;
case PROGRAM_MODE::SELL:
debug << "PROGRAM_MODE::SELL";
break;
case PROGRAM_MODE::SERVICE:
debug << "PROGRAM_MODE::SERVICE";
break;
case PROGRAM_MODE::OOO:
debug << "PROGRAM_MODE::OOO";
break;
}
return debug;
}
QDebug operator<<(QDebug debug, APP_ACTION action) {
switch (action) {
case APP_ACTION::DEFAULT_ACTION:
debug << "APP_ACTION::DEFAULT_ACTION";
break;
case APP_ACTION::LICENSEPLATE_INPUT:
debug << "APP_ACTION::LICENSEPLATE_INPUT";
break;
case APP_ACTION::PARKINGTIME_INPUT:
debug << "APP_ACTION::PARKINGTIME_INPUT";
break;
case APP_ACTION::CARD_PAYMENT:
debug << "APP_ACTION::CARD_PAYMENT";
break;
case APP_ACTION::CHOOSE_PAYEMENT:
debug << "APP_ACTION::CHOOSE_PAYEMENT";
break;
case APP_ACTION::COIN_PAYMENT_PAYUP:
debug << "APP_ACTION::COIN_PAYMENT_PAYUP";
break;
case APP_ACTION::COIN_PAYMENT_PAYDOWN:
debug << "APP_ACTION::COIN_PAYMENT_PAYDOWN";
break;
case APP_ACTION::SEND_D0_CALC:
debug << "APP_ACTION::SEND_D0_CALC";
break;
}
return debug;
}
/**************************************************************************
* customer specific things
*/
#if defined (CUST00318)
/*****************************************************************************
* hack for 00318/Nexobility
*/
void AppControl::private_CUST00318_handle_CC_ERROR_0x13()
{
qCritical() << "CUST00318_handle_CC_ERROR_0x13";
QString formatedString;
vendingData->setParameter("CC_ERROR_0x13", QVariant(1));
formatedString.append("#CALC#");
formatedString.append(this->vendingData->getParameter("LICENSEPLATE").toString()).append('#');
formatedString.append("1").append('#');
formatedString.append("2021-08-17T16:00:00").append('#');
formatedString.append("2021-08-17T17:00:00").append('#');
formatedString.append("0").append('#');
formatedString.append("0").append('#');
formatedString.append("0").append('#');
vmc->SendFormatedString(formatedString);
}
#endif