#include "AppControl.h" #include "ATBHMIconfig.h" #include "VMC/vmc.h" #include "atb_system.h" #include "support/VendingData.h" #include #include #include #include #include #include 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(, 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)), this, SLOT(onProcessed_QRCode(QHash))); 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(this->pluginManager->getInstance("CCPlugin")); if (this->cc == nullptr) { qCritical() << "ERROR on loading Plugin CCInterface!"; return; } if ( ! static_cast(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(cc), SIGNAL(sendStartTransactionResult(nsCCInterface::RESULT_STATE, QString&)), this, SLOT(onCCStartTransactionResult(nsCCInterface::RESULT_STATE, QString&))); connect(dynamic_cast(cc), SIGNAL(sendConfirmTransactionResult(nsCCInterface::RESULT_STATE, QString&)), this, SLOT(onCCConfirmTransactionResult(nsCCInterface::RESULT_STATE, QString&))); connect(dynamic_cast(cc), SIGNAL(sendCancelTransactionResult(nsCCInterface::RESULT_STATE, QString&)), this, SLOT(onCCCancelTransactionResult(nsCCInterface::RESULT_STATE, QString&))); connect(dynamic_cast(cc), SIGNAL(sendRevertTransactionResult(nsCCInterface::RESULT_STATE, QString&)), this, SLOT(onCCRevertTransactionResult(nsCCInterface::RESULT_STATE, QString&))); //connect(dynamic_cast(cc), SIGNAL(sendRevertTransactionResult(nsCCInterface::RESULT_STATE, QString&)), vmc, SLOT())); //connect(dynamic_cast(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(cc), SIGNAL(sendPreAuthTransactionResult(nsCCInterface::RESULT_STATE, QString&)), this, SLOT(onCCPreAuthTransactionResult(nsCCInterface::RESULT_STATE, QString&))); connect(dynamic_cast(cc), SIGNAL(sendBookTotalTransactionResult(nsCCInterface::RESULT_STATE, QString&)), this, SLOT(onCCBookTotalTransactionResult(nsCCInterface::RESULT_STATE, QString&))); connect(dynamic_cast(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(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(cc), SLOT(wakeupCC())); connect(vmc, SIGNAL(ccSleep()), dynamic_cast(cc), SLOT(sleepCC())); #else connect(this, SIGNAL(changedModeToSELL()), dynamic_cast(cc), SLOT(wakeupCC())); connect(this, SIGNAL(changedModeToIDLE()), dynamic_cast(cc), SLOT(sleepCC())); #endif connect(this, SIGNAL(changedModeToSERVICE()), dynamic_cast(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(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(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(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(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(this->pluginManager->getInstance("SimulationPlugin")); if (this->simulation == nullptr) { qCritical() << "plugin Simulation ist not available..."; } else { if ( ! static_cast(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(); 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::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(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(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(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(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(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(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(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