diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a9d35c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.user diff --git a/ATBAPP.h b/ATBAPP.h new file mode 100644 index 0000000..3d6eb6d --- /dev/null +++ b/ATBAPP.h @@ -0,0 +1,87 @@ +#ifndef ATBAPP_H +#define ATBAPP_H + +#include +#include + + +enum class PROGRAM_MODE : quint8 { + IDLE = 1, + SELL_ENABLE = 2, + SELL = 3, + SERVICE = 4, + OOO = 5 +}; +QDebug operator<<(QDebug debug, PROGRAM_MODE mode); + + + +namespace CC_VARIANT { + enum class TERMINAL : quint8 { + NO_TERMINAL, + INGENICO_CCTOPP, + INGENICO_CHIP_AND_PIN, + CCV, + FEIG_CCTOPP, + FEIG_CHIP_AND_PIN + }; +} + +namespace PAYMENT_VARIANTS { + enum class CASH_VARIANT : quint8 { + NO_CASH, + CoinsOnly, + CoinsAndNotes + }; +} + + +enum class PAYMENT_POINTINTIME : quint8 { + PAY_ON_ARRIVAL, + PAY_ON_EXIT, + IGNORE +}; + + + +/********************************************************************* + * APP_ACTION + * + */ + +/* QVariant.toString() returns empty string for custom types, even if + there is a .toString() method defined. + From QT5.2 onwards there are new methods in QMetaType for registering + comperator and converter methods: + QMetaType::registerComparators() + QMetaType::registerConverter() + see: https://doc.qt.io/qt-5/qmetatype.html#registerComparators + */ + + +enum class APP_ACTION : int { + DEFAULT_ACTION = 0x00, + LICENSEPLATE_INPUT = 0x01, // License Plate Input + PARKINGTIME_INPUT = 0x02, // Parking Time Input + CARD_PAYMENT = 0x03, // CardPayment + CHOOSE_PAYEMENT = 0x04, // Choose payment variant + COIN_PAYMENT_PAYUP = 0x05, + COIN_PAYMENT_PAYDOWN = 0x06, + SEND_D0_CALC = 0x07, +}; +Q_DECLARE_METATYPE(APP_ACTION) +QDebug operator<<(QDebug debug, APP_ACTION action); + + +enum class CP_STATE : quint8 { + NO_STATE = 0x00, + LICENSEPLATE_INPUT = 0x01, // with accessInformation only + PARKINGTIME_INPUT = 0x02, // with accessInformation and parkingtime + CARDINFO_INPUT = 0x03, // with accessInformation and cardInfo +}; +Q_DECLARE_METATYPE(CP_STATE) + + + + +#endif // ATBAPP_H diff --git a/ATBHMIconfig.cpp b/ATBHMIconfig.cpp new file mode 100644 index 0000000..c684275 --- /dev/null +++ b/ATBHMIconfig.cpp @@ -0,0 +1,862 @@ +#include "ATBHMIconfig.h" +#include "atb_system.h" +#include +#include +#include + + + +ATBHMIconfig::ATBHMIconfig(QObject *parent) : QObject(parent) { + this->initEarly(); + this->initDefered(); +} + +ATBHMIconfig::~ATBHMIconfig() { + delete settings; +} + +void ATBHMIconfig::initEarly() { + QString settings_file = QCoreApplication::applicationDirPath() + "/ATBQT.ini"; + settings = new QSettings(settings_file, QSettings::IniFormat); + settings->setIniCodec("UTF-8"); + configGroups = settings->childGroups(); + + // [COMMON] + + settings->beginGroup("COMMON"); + +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + ComPortName = settings->value("com_port_name", "/dev/ttyS0").toString(); +#else + ComPortName = settings->value("com_port_name", "/dev/pts/20").toString(); +#endif + ComBaudRate = settings->value("com_baud_rate", "115200").toInt(); + + if ((ComBaudRate != 115200) && + (ComBaudRate != 57600) && + (ComBaudRate != 9600)) { ComBaudRate = 115200; } + + + lang1 = settings->value("lang1", "de_DE").toString(); + lang2 = settings->value("lang2", "en_US").toString(); + lang3 = settings->value("lang3", "fr_FR").toString(); + lang4 = settings->value("lang4", "ja_JP").toString(); + lang5 = settings->value("lang5", "nl-NL").toString(); + + useLanguage = static_cast(settings->value("useLanguage", "255").toInt()); + + this->defaultLanguage = settings->value("defaultLanguage", "1").toInt(); + if ((this->defaultLanguage < 1) || (this->defaultLanguage > 5)) { + this->defaultLanguage = 1; + } + + usePayment = static_cast(settings->value("usePayment", "255").toInt()); + + useVehicle = static_cast(settings->value("useVehicle", "255").toInt()); + + useProduct = static_cast(settings->value("useProduct", "255").toInt()); + + + textfilename = settings->value("textfile", "HMI_Texte.xml").toString(); +#if defined (ARCH_DesktopLinux) + QString filename = textfilename.split("/").last(); + textfilename = qApp->applicationDirPath() + "/text/" + filename; +#endif + +#if defined (ARCH_DesktopLinux) + logfilename = QDir::homePath() + "/ATBAPP_VMC.log"; +#else + logfilename = settings->value("logfile", "/var/log/atbqt.log").toString(); +#endif + + oooServiceNumber = settings->value("ooo_serviceNumber", "").toString(); + + oooServiceEmail = settings->value("ooo_serviceEmail", "").toString(); + + oooAlternativeLocation = settings->value("ooo_alternativeLocation", "").toString(); + + alternativeLogoFilename = settings->value("alternativeLogoFile").toString(); + + this->features = 0; + + // deprecated: + qmPath = qApp->applicationDirPath() + "/translations"; + + settings->endGroup(); + + /* + * note: PSA configuration settings are not stored in ini-file + */ + cust_nr = "0"; + group_nr = "0"; + zone_nr = "0"; + machine_nr = "0"; + validPSAconfig = false; +} + + +void ATBHMIconfig::initDefered() { + bool ok = false; + + // [COMMON] + + settings->beginGroup("COMMON"); + + SuspendMethode = settings->value("suspend_methode", "sysfs").toString(); + SuspendProgram = settings->value("suspend_program", "").toString(); + SuspendTime = settings->value("suspend_time", "10").toString(); + + if (settings->value("feature_Log", "on").toString() == "on" || + settings->value("feature_Log", "on").toString() == "yes" ) + { this->setFeatureLog(); } + else { this->unsetFeatureLog(); } + + if (settings->value("feature_TouchFeedback", "on").toString() == "on" || + settings->value("feature_TouchFeedback", "on").toString() == "yes" ) + { this->setFeatureTF(); } + else { this->unsetFeatureTF(); } + + if (settings->value("feature_DisplayControl", "off").toString() == "on" || + settings->value("feature_DisplayControl", "off").toString() == "yes" ) + { this->setFeatureDC(); } + else { this->unsetFeatureDC(); } + + if (settings->value("feature_StartButton", "on").toString() == "on" || + settings->value("feature_StartButton", "on").toString() == "yes" ) + { this->setFeatureStartButton(); } + else { this->unsetFeatureStartButton(); } + + if (settings->value("feature_selfOOO", "on").toString() == "on" || + settings->value("feature_selfOOO", "on").toString() == "yes" ) + { this->setFeatureSelfOOO(); } + else { this->unsetFeatureSelfOOO(); } + + if (settings->value("feature_DBusDisplayControl", "on").toString() == "on" || + settings->value("feature_DBusDisplayControl", "on").toString() == "yes" ) + { this->setFeatureDBusDisplayControl(); } + else { this->unsetFeatureDBusDisplayControl(); } + + if (settings->value("feature_DBusSuspendControl", "on").toString() == "on" || + settings->value("feature_DBusSuspendControl", "on").toString() == "yes" ) + { this->setFeatureDBusSuspendControl(); } + else { this->unsetFeatureDBusSuspendControl(); } + + if (settings->value("feature_ShowParkingTime", "on").toString() == "on" || + settings->value("feature_ShowParkingTime", "on").toString() == "yes" ) + { this->setFeatureShowParkingTime(); } + else { this->unsetFeatureShowParkingTime(); } + + + touch_feedback_gpio = settings->value("touch_feedback_gpio", "121").toString(); + + touch_feedback_on_value = settings->value("touch_feedback_on_value", "0").toString(); + touch_feedback_off_value = settings->value("touch_feedback_off_value", "1").toString(); + + QString ooo_mode = settings->value("ooo_mode", "useDisk").toString(); + if (ooo_mode == "useDisk") + this->oooMode = OOO_MODE::USE_PARKDISK; + else if (ooo_mode == "otherMachine") + this->oooMode = OOO_MODE::USE_OTHER_MACHINE; + else + this->oooMode = OOO_MODE::DEFAULT; + + characterSet = settings->value("CharacterSet", "ISO 8859-15").toByteArray(); + + qrCodeLink = settings->value("QRCodeLink", "").toString(); + + SellTimeoutTime = settings->value("sellTimeout_time", "60").toString(); + + this->privateReadInputDevices(); + + this->lpLocalExample = settings->value("LicencePlateExampleString", "HOAB123").toString(); + + +#if defined (USE_BARCODESCANNER) + if (settings->value("useBarcode", "on").toString() == "on" || + settings->value("useBarcode", "on").toString() == "yes" ) + { this->useBarcode = true; } + else { this->useBarcode = false; } +#endif // USE_BARCODESCANNER + +#ifdef USE_RFIDREADER + if (settings->value("useRFID", "on").toString() == "on" || + settings->value("useRFID", "on").toString() == "yes" ) + { this->useRFID = true; } + else { this->useRFID = false; } +#endif + + // DEBUG + qDebug() << "ATBHMIconfig::initDefered() com_port_name = " << ComPortName; + qDebug() << "ATBHMIconfig::initDefered() touch_feedback_gpio = " << touch_feedback_gpio; + + // DEBUG: write settings to file: + /* + settings->setValue("com_port_name", this->ComPortName); + settings->setValue("lang1", "de_DE"); + settings->setValue("lang2", "en_US"); + settings->setValue("lang3", "fr_FR"); + settings->setValue("lang4", "ja_JP"); + settings->setValue("textfile", "HMI_Texte.xml"); + settings->setValue("logfile", "/var/log/atbqt.log"); + settings->setValue("feature_Log", false); + settings->setValue("feature_TouchFeedback", true); + settings->setValue("feature_DisplayControl", true); + settings->setValue("feature_SystemSuspend", true); + settings->setValue("suspend_methode", "sysfs"); + settings->setValue("suspend_program", ""); + settings->setValue("suspend_time", 10); + settings->setValue("touch_feedback_gpio", 121); + settings->setValue("touch_feedback_on_value", 0); + settings->setValue("touch_feedback_off_value", 1); + */ + + this->HelpButtonUsed = settings->value("showHelpButton", "0").toBool(); + this->InfoButtonUsed = settings->value("showInfoButton", "0").toBool(); + + this->ScreenChangeTimeoutTime = settings->value("screenChangeTimeoutTime", "10").toUInt(); + + settings->endGroup(); + + // [PAYMENT] + + settings->beginGroup("PAYMENT"); + + QString cashVariantString = settings->value("cashVariant", "CoinsOnly").toString(); + if (cashVariantString == "CoinsOnly") { + this->cashVariant = PAYMENT_VARIANTS::CASH_VARIANT::CoinsOnly; + } + else + if (cashVariantString == "CoinsAndNotes") { + this->cashVariant = PAYMENT_VARIANTS::CASH_VARIANT::CoinsAndNotes; + } + else { + // Default + this->cashVariant = PAYMENT_VARIANTS::CASH_VARIANT::CoinsOnly; + } + + this->currencySymbol = settings->value("currencySymbol", "€").toString(); + + + QString paymentPointInTimeString = settings->value("paymentPointInTime", "poe").toString(); + if (paymentPointInTimeString == "poe") { + this->paymentPointInTime = PAYMENT_POINTINTIME::PAY_ON_EXIT; + } + else + if (paymentPointInTimeString == "poa") { + this->paymentPointInTime = PAYMENT_POINTINTIME::PAY_ON_ARRIVAL; + } + else + if (paymentPointInTimeString == "ign") { + this->paymentPointInTime = PAYMENT_POINTINTIME::IGNORE; + } + else { + // Default + this->paymentPointInTime = PAYMENT_POINTINTIME::PAY_ON_EXIT; + } + + + if (settings->value("useHonestPayment", "on").toString() == "on" || + settings->value("useHonestPayment", "on").toString() == "yes" ) + { this->honestPayment = true; } + else { this->honestPayment = false; } + + + settings->endGroup(); + + // [CUSTOMER] + + settings->beginGroup("CUSTOMER"); + + this->DiscountUsed = settings->value("DiscountUsed", "1").toBool(); + this->QRCodeReceiptUsed = settings->value("QRCodeReceiptUsed", "1").toBool(); + + settings->endGroup(); + +#ifdef USE_EXTERNAL_TICKETMANAGER + // [BARCODE_VERIFICATION] + + settings->beginGroup("BARCODE_VERIFICATION"); + + this->endOfSellingTime = settings->value("EndOfSellingTime", "06:00").toString(); + this->blockingTimeMin = settings->value("BlockingTime_Min", "15").toInt(&ok); + if (!ok) this->blockingTimeMin = 15; + + settings->endGroup(); +#endif + + +#ifdef USE_ISMAS + // [ISMAS] + + settings->beginGroup("ISMAS"); + + this->ismasHost = settings->value("ISMAS_HOST", "87.139.50.222").toString(); + this->ismasPort = settings->value("ISMAS_PORT", "10000").toInt(&ok); + this->ismasID = settings->value("ISMAS_ID", "").toString(); + + if (settings->value("ISMAS_DEBUG", "on").toString() == "on" || + settings->value("ISMAS_DEBUG", "on").toString() == "yes") + { this->ismasDebug = true; } + else { this->ismasDebug = false; } + + if (settings->value("ISMAS_CheckEntries", "on").toString() == "on" || + settings->value("ISMAS_CheckEntries", "on").toString() == "yes") + { this->ismasCheckEntries = true; } + else { this->ismasCheckEntries = false; } + + this->ismasConnectionTimeout = settings->value("ISMAS_ConnectionTimeout", "2").toInt(&ok); + if (!ok) this->ismasConnectionTimeout = 2; + + if (settings->value("usePinGenerator", "off").toString() == "on" || + settings->value("usePinGenerator", "off").toString() == "yes" ) + { this->usePINgenerator = true; } + else { this->usePINgenerator = false; } + + settings->endGroup(); +#endif + + // [ERRORCODE_MAPPING] + + if (settings->childGroups().contains("ERRORCODE_MAPPING")) { + settings->beginGroup("ERRORCODE_MAPPING"); + + QStringList ErrorCodes = settings->childKeys(); + + for (int i = 0; i < ErrorCodes.size(); ++i) { + errorTextHash.insert(ErrorCodes.at(i), settings->value(ErrorCodes.at(i),"").toString()); + } + + settings->endGroup(); + } + + + qDebug() << ok; +} + + +QSettings * ATBHMIconfig::getSettingsPtr() { return this->settings; } + +const QSettings & ATBHMIconfig::getSettings() { return *(this->settings); } + +QString ATBHMIconfig::getComPortName() { return this->ComPortName; } + +QString ATBHMIconfig::getSuspendMethode() { return this->SuspendMethode; } + +QString ATBHMIconfig::getSuspendProgram() { return this->SuspendProgram; } + + +/*********************************************************************************************** + * get suspend time (-> time, after system goes to suspend) in seconds + */ +QString ATBHMIconfig::getSuspendTime() { return this->SuspendTime; } + + +/*********************************************************************************************** + * get sell timout time (-> time, after system switched back to mode idle, if we do not have + * any user inputs or vmc messages during sell mode) in seconds + */ +QString ATBHMIconfig::getSellTimeoutTime() { return this->SellTimeoutTime; } + +quint32 ATBHMIconfig::getHMIScreenChangeTimeoutTime() { return this->ScreenChangeTimeoutTime; } + +QString ATBHMIconfig::getQmPath() { return this->qmPath; } + +bool ATBHMIconfig::hasFeatureTF() { return ((this->features & this->featureMaskTF) != 0); } +bool ATBHMIconfig::hasFeatureLog() { return ((this->features & this->featureMaskLog) != 0); } +bool ATBHMIconfig::hasFeatureDC() { return ((this->features & this->featureMaskDC) != 0); } +bool ATBHMIconfig::hasFeatureStartButton() { return ((this->features & this->featureMaskStartButton) != 0); } +bool ATBHMIconfig::hasFeatureSelfOOO() { return ((this->features & this->featureMaskSelfOOO) != 0); } +bool ATBHMIconfig::hasFeatureDBusDisplayControl() { return ((this->features & this->featureMaskDBusDisplayControl) != 0); } +bool ATBHMIconfig::hasFeatureDBusSuspendControl() { return ((this->features & this->featureMaskDBusSuspendControl) != 0); } +bool ATBHMIconfig::hasFeatureShowParkingTime() { return ((this->features & this->featureMaskShowParkingTime) != 0); } + +void ATBHMIconfig::setFeatureTF() { this->features |= this->featureMaskTF; } +void ATBHMIconfig::unsetFeatureTF() { this->features &= ~(this->featureMaskTF); } +void ATBHMIconfig::setFeatureLog() { this->features |= this->featureMaskLog; } +void ATBHMIconfig::unsetFeatureLog() { this->features &= ~(this->featureMaskLog); } +void ATBHMIconfig::setFeatureStartButton() { this->features |= this->featureMaskStartButton; } +void ATBHMIconfig::unsetFeatureStartButton() { this->features &= ~(this->featureMaskStartButton); } +void ATBHMIconfig::setFeatureSelfOOO() { this->features |= this->featureMaskSelfOOO; } +void ATBHMIconfig::unsetFeatureSelfOOO() { this->features &= ~(this->featureMaskSelfOOO); } + + +void ATBHMIconfig::setFeatureDC() +{ + // this features are mutually exclusive + if (this->hasFeatureDBusDisplayControl()) { + this->unsetFeatureDBusDisplayControl(); + } + this->features |= this->featureMaskDC; +} + +void ATBHMIconfig::unsetFeatureDC() { this->features &= ~(this->featureMaskDC); } + + +void ATBHMIconfig::setFeatureDBusDisplayControl() +{ + // this features are mutually exclusive + if (this->hasFeatureDC()) { + this->unsetFeatureDC(); + } + this->features |= this->featureMaskDBusDisplayControl; +} + +void ATBHMIconfig::setFeatureDBusSuspendControl() +{ + this->features |= this->featureMaskDBusSuspendControl; +} + +void ATBHMIconfig::unsetFeatureDBusDisplayControl() { this->features &= ~(this->featureMaskDBusDisplayControl); } +void ATBHMIconfig::unsetFeatureDBusSuspendControl() { this->features &= ~(this->featureMaskDBusSuspendControl); } + +void ATBHMIconfig::setFeatureShowParkingTime() { this->features |= this->featureMaskShowParkingTime; } +void ATBHMIconfig::unsetFeatureShowParkingTime() { this->features &= ~(this->featureMaskShowParkingTime); } + + +QString ATBHMIconfig::getTouchFeedbackGPIO() { return this->touch_feedback_gpio; } +QString ATBHMIconfig::getTouchFeedbackOnValue() { return this->touch_feedback_on_value; } +QString ATBHMIconfig::getTouchFeedbackOffValue() { return this->touch_feedback_off_value; } + +QString ATBHMIconfig::getTextfileName() { return this->textfilename; } + +QString ATBHMIconfig::getAlternativeLogoFilename() { return this->alternativeLogoFilename; } + +#ifdef USE_BARCODESCANNER +bool ATBHMIconfig::getUseBarcodeScanner() { return this->useBarcode; } +QString & ATBHMIconfig::getBarcodeScannerInputDevice() { return this->barcodeScannerInputDeviceName; } +#endif + +#ifdef USE_RFIDREADER +bool ATBHMIconfig::getUseRFIDReader() { return this->useRFID; } +QString & ATBHMIconfig::getRFIDReaderInputDevice() { return this->rfidReaderInputDeviceName; } +#endif + +PAYMENT_VARIANTS::CASH_VARIANT ATBHMIconfig::getCashVariant() { return this->cashVariant; } + +bool ATBHMIconfig::discountIsUsed() { return this->DiscountUsed; } +bool ATBHMIconfig::qrCodeReceiptIsUsed() { return this->QRCodeReceiptUsed; } + +#ifdef USE_EXTERNAL_TICKETMANAGER +/********************************************************************** + * for barcode verification: + * + * EndOfSellingTime: time, when all daily data (e.g. blocking list) is reset + * BlockingTime: time, in minutes, how long a verified ticket is blocked for reentry + */ +QString ATBHMIconfig::getEndOfSellingTime() { return this->endOfSellingTime; } +int ATBHMIconfig::getBlockingTime() { return this->blockingTimeMin; } +#endif + +QString ATBHMIconfig::getLang(int lang) +{ + switch (lang) { + case 1: return this->lang1; + break; + case 2: return this->lang2; + break; + case 3: return this->lang3; + break; + case 4: return this->lang4; + break; + case 5: return this->lang5; + break; + default: return ""; + } +} + + +/** + * Returns the ordered list of configured languages. + * { , , ... , } + * This languages should be available in text file. + */ +QStringList ATBHMIconfig::getConfiguredLanguages() +{ + QStringList langs; + langs << lang1 << lang2 << lang3 << lang4 << lang5; + return langs; +} + + +QString ATBHMIconfig::getCountry(int country) +{ + switch (country) { + case 1: return this->country1; + break; + case 2: return this->country2; + break; + case 3: return this->country3; + break; + case 4: return this->country4; + break; + case 5: return this->country5; + break; + default: return ""; + } +} + +bool ATBHMIconfig::isLanguageUsed(int lang) +{ + switch (lang) { + case 1: return (this->useLanguage & 0x80) ? (true) : (false); + break; + case 2: return (this->useLanguage & 0x40) ? (true) : (false); + break; + case 3: return (this->useLanguage & 0x20) ? (true) : (false); + break; + case 4: return (this->useLanguage & 0x10) ? (true) : (false); + break; + case 5: return (this->useLanguage & 0x08) ? (true) : (false); + break; + default: return false; + } +} + +bool ATBHMIconfig::isInfoButtonUsed() { return this->InfoButtonUsed; } +bool ATBHMIconfig::isHelpButtonUsed() { return this->HelpButtonUsed; } + + +bool ATBHMIconfig::isPaymentUsed(int payment) +{ + switch (payment) { + case 1: return (this->usePayment & 0x80) ? (true) : (false); + break; + case 2: return (this->usePayment & 0x40) ? (true) : (false); + break; + case 3: return (this->usePayment & 0x20) ? (true) : (false); + break; + case 4: return (this->usePayment & 0x10) ? (true) : (false); + break; + default: return false; + } +} + + +bool ATBHMIconfig::isVehicleUsed(int vehicle) +{ + switch (vehicle) { + case 1: return (this->useVehicle & 0x80) ? (true) : (false); + break; + case 2: return (this->useVehicle & 0x40) ? (true) : (false); + break; + case 3: return (this->useVehicle & 0x20) ? (true) : (false); + break; + case 4: return (this->useVehicle & 0x10) ? (true) : (false); + break; + case 5: return (this->useVehicle & 0x08) ? (true) : (false); + break; + case 6: return (this->useVehicle & 0x04) ? (true) : (false); + break; + case 7: return (this->useVehicle & 0x02) ? (true) : (false); + break; + case 8: return (this->useVehicle & 0x01) ? (true) : (false); + break; + default: return false; + } +} + + +bool ATBHMIconfig::isProductUsed(int product) +{ + switch (product) { + case 1: return (this->useProduct & 0x80) ? (true) : (false); + break; + case 2: return (this->useProduct & 0x40) ? (true) : (false); + break; + case 3: return (this->useProduct & 0x20) ? (true) : (false); + break; + case 4: return (this->useProduct & 0x10) ? (true) : (false); + break; + case 5: return (this->useProduct & 0x08) ? (true) : (false); + break; + case 6: return (this->useProduct & 0x04) ? (true) : (false); + break; + case 7: return (this->useProduct & 0x02) ? (true) : (false); + break; + case 8: return (this->useProduct & 0x01) ? (true) : (false); + break; + default: return false; + } +} + + +QString ATBHMIconfig::getLogFileName() +{ + return this->logfilename; +} + +int ATBHMIconfig::getDefaultLanguage() +{ + return this->defaultLanguage; +} + +void ATBHMIconfig::setDefaultLanguage(quint8 lang) +{ + this->defaultLanguage = lang; +} + + +int ATBHMIconfig::getComBaudRate() +{ + return this->ComBaudRate; +} + + +/******************************************************************************** + * Funktions for storing some machine specific configuration data in filesystem. + * + * Note: this functions must reduce write operations to flash memory. + * Therefore, first the data values are read from memory and compared to the new ones. + * Only changed values should be written. + * Additionally, open files, read their values and compare this values is time consuming. + * So we store this values also in our config-object and do only a comparisson if values + * have changed. + * + * + */ +QString & ATBHMIconfig::getCustNr() +{ + if (!this->validPSAconfig) { this->readPSAConfig(); } + return this->cust_nr; +} + +QString & ATBHMIconfig::getGroupNr() +{ + if (!this->validPSAconfig) { this->readPSAConfig(); } + return this->group_nr; +} + +QString & ATBHMIconfig::getZoneNr() +{ + if (!this->validPSAconfig) { this->readPSAConfig(); } + return this->zone_nr; +} + +QString & ATBHMIconfig::getMachineNr() +{ + if (!this->validPSAconfig) { this->readPSAConfig(); } + return this->machine_nr; +} + +quint8 ATBHMIconfig::readPSAConfig() +{ + this->cust_nr = ATB_system::readPSAConfigString("/etc/cust_nr"); + this->group_nr = ATB_system::readPSAConfigString("/etc/group_nr"); + this->zone_nr = ATB_system::readPSAConfigString("/etc/zone_nr"); + this->machine_nr = ATB_system::readPSAConfigString("/etc/machine_nr"); + this->validPSAconfig = true; + return 1; +} + +void ATBHMIconfig::setCustNr(const QString& cust_nr) +{ + if (! validPSAconfig) { this->readPSAConfig(); } + + // compare to cached value: + + if (cust_nr != this->cust_nr) { // Customer number has changed + ATB_system::setPSAConfigString("/etc/cust_nr", cust_nr); + this->cust_nr = cust_nr; + } +} + + +void ATBHMIconfig::setGroupNr(const QString& group_nr) +{ + if (! validPSAconfig) { this->readPSAConfig(); } + + // compare to cached value: + + if (group_nr != this->group_nr) { + ATB_system::setPSAConfigString("/etc/group_nr", group_nr); + this->group_nr = group_nr; + } +} + + +void ATBHMIconfig::setZoneNr(const QString& zone_nr) +{ + if (! validPSAconfig) { this->readPSAConfig(); } + + // compare to cached value: + + if (zone_nr != this->zone_nr) { + ATB_system::setPSAConfigString("/etc/zone_nr", zone_nr); + this->zone_nr = zone_nr; + } +} + + +void ATBHMIconfig::setMachineNr(const QString &machine_nr) +{ + if (! validPSAconfig) { this->readPSAConfig(); } + + // compare to cached value: + + if (machine_nr != this->machine_nr) { + ATB_system::setPSAConfigString("/etc/machine_nr", machine_nr); + this->machine_nr = machine_nr; + } +} + + +OOO_MODE ATBHMIconfig::getOOOMode() +{ + return this->oooMode; +} + +QString ATBHMIconfig::getOOOModeString() +{ + switch (this->oooMode) { + case OOO_MODE::USE_OTHER_MACHINE: + return QString("otherMachine"); + break; + case OOO_MODE::USE_PARKDISK: + return QString("useDisk"); + break; + default: + return ""; + } + + return ""; +} + +QString ATBHMIconfig::getOOOServiceNumber() +{ + return this->oooServiceNumber; +} + +QString ATBHMIconfig::getOOOServiceEmail() +{ + return this->oooServiceEmail; +} + +QString ATBHMIconfig::getOOOAlternativeLocation() +{ + return this->oooAlternativeLocation; +} + +QString ATBHMIconfig::getLicensePlateExampleString() +{ + return this->lpLocalExample; +} + +QString ATBHMIconfig::getQrCodeLink() +{ + return this->qrCodeLink; +} + +const QByteArray & ATBHMIconfig::getDefaultCharacterSet() const +{ + return this->characterSet; +} + + +const QString & ATBHMIconfig::getPaymentCurrencySymbol() +{ + return this->currencySymbol; +} + +PAYMENT_POINTINTIME ATBHMIconfig::getPaymentPointInTime() +{ + return this->paymentPointInTime; +} + +bool ATBHMIconfig::useHonestPayment() +{ + return this->honestPayment; +} + +/*************************************************************************** + * public slot to re-read config + * + * This slot could be called e.g. on a mode switch from 'service' to 'sell' or vv. + */ +void ATBHMIconfig::reReadSettings() +{ + this->privateReadInputDevices(); +} + +/* private common function to set/read input device names. + */ +void ATBHMIconfig::privateReadInputDevices() +{ +#if defined (USE_BARCODESCANNER) + barcodeScannerInputDeviceName = ATB_system::getEventDeviceName(ATB_system::EVENT_DEVICE_BARCODEREADER); +#endif // USE_BARCODESCANNER + +#if defined (USE_RFIDREADER) + rfidReaderInputDeviceName = ATB_system::getEventDeviceName(ATB_system::EVENT_DEVICE_RFIDREADER); +#endif // USE_RFIDREADER +} + + +/*************************************************************************** + * ISMAS + */ +#ifdef USE_ISMAS +QString ATBHMIconfig::getISMASHost() { return this->ismasHost; } +quint16 ATBHMIconfig::getISMASPort() { return this->ismasPort; } +bool ATBHMIconfig::isIsmasDebug() { return this->ismasDebug; } +bool ATBHMIconfig::IsmasCheckEntries() { return this->ismasCheckEntries; } +quint16 ATBHMIconfig::getIsmasConnectionTimeout() { return this->ismasConnectionTimeout; } +bool ATBHMIconfig::UsePINgenerator() { return this->usePINgenerator; } +QString ATBHMIconfig::getISMASId() +{ + // if no ismasID is defined, we use customer Number (-> "ATB device Id" in Multipass) + if (this->ismasID == "") { + return this->getCustNr(); + } + return this->ismasID; +} +#endif + + +/*************************************************************************** + * public methods for ErrorCode + */ +QString ATBHMIconfig::getErrorText(const QString & errorCode) +{ + return errorTextHash.value(errorCode); +} + + +/*************************************************************************** + * get tools name/path + */ + +/***************************************************************************** + * check availability of network diag tool + * + * possible install path for networkDiag: + * 1) /usr/bin/atb_networkDiag + * 2) /opt/app/tools/networkDiag + * 3) /opt/app/atb_networkDiag/networkDiag + * + * Check, if file exists and if it is executable, + * Return path/name for binary. + */ +QString & ATBHMIconfig::getNetworkDiagTool() +{ + // search list for config file: + QStringList binFileList; + + // check, if file exists and if it is executable +#if defined (ARCH_PTU4) + binFileList << "/usr/bin/atbNetworkDiag" + << "/opt/app/tools/networkDiag" + << "/opt/app/atbNetworkDiag/networkDiag"; +#else + binFileList << QDir::homePath().append("/atbNetworkDiag"); +#endif + + // using C++11 range based loop: -> take first file, which is readable + for (const auto& filename : binFileList) { + if (QFileInfo(filename).isExecutable()) { + this->networkDiagToolName = filename; + break; + } + } + + return this->networkDiagToolName; +} diff --git a/ATBHMIconfig.h b/ATBHMIconfig.h new file mode 100644 index 0000000..f26a5b4 --- /dev/null +++ b/ATBHMIconfig.h @@ -0,0 +1,338 @@ +#ifndef ATBHMICONFIG_H +#define ATBHMICONFIG_H + +#include +#include +#include +#include "version.h" + + +/*************************************** + * Sample configuration: + * + * [General] + * com_port_name=/dev/pts/16 + * machine_nr=99 + * + * suspend_methode=sysfs + * suspend_program= + * suspend_time=10 + * + * touch_feedback=on + * touch_feedback_gpio=115 // 115 = beep; 121 = vibration motor + * touch_feedback_on_value=0 // 0 (buzzer) 1 (vibration) + * touch_feedback_off_value=1 // 1 (buzzer) 0 (vibration) + * + * log=on // loggin is on + * + * lang1=de_DE + * lang2=en_US + * lang3=fr_FR + * lang4=ja_JP + * + * textfile=HMI_Texte.xml + * + */ + + +enum class OOO_MODE : quint8; + + +class ATBHMIconfig : public QObject +{ + Q_OBJECT + + QString ComPortName; + + QString SuspendMethode; + QString SuspendProgram; + QString SuspendTime; + + QString SellTimeoutTime; + + QString qmPath; + + QString touch_feedback_gpio; + QString touch_feedback_on_value; + QString touch_feedback_off_value; + + + QString lang1; + QString lang2; + QString lang3; + QString lang4; + QString lang5; + + quint8 useLanguage; + quint8 useVehicle; + quint8 useProduct; + quint8 usePayment; + + QString country1; + QString country2; + QString country3; + QString country4; + QString country5; + + QSettings *settings; + + quint16 features; + static const quint16 featureMaskTF = 0x0001; + static const quint16 featureMaskLog = 0x0002; + static const quint16 featureMaskDC = 0x0004; // manuell display control, (in future deprecated) + static const quint16 featureMaskSusp = 0x0008; // manuell suspend control, (in future deprecated) + static const quint16 featureMaskStartButton = 0x0010; + static const quint16 featureMaskSelfOOO = 0x0020; + static const quint16 featureMaskDBusDisplayControl = 0x0040; + static const quint16 featureMaskDBusSuspendControl = 0x0080; + static const quint16 featureMaskShowParkingTime = 0x0100; + + + + QString logfilename; + + QString textfilename; + + QString alternativeLogoFilename; + + QString oooServiceNumber; + + QString oooServiceEmail; + + QString oooAlternativeLocation; + + QString qrCodeLink; + + QString lpLocalExample; + + QString currencySymbol; + +#ifdef USE_BARCODESCANNER + bool useBarcode; + QString barcodeScannerInputDeviceName; +#endif + +#ifdef USE_RFIDREADER + bool useRFID; + QString rfidReaderInputDeviceName; +#endif + +#ifdef USE_EXTERNAL_TICKETMANAGER + // for ticket management + QString endOfSellingTime; + int blockingTimeMin; +#endif + + quint8 defaultLanguage; + + + bool HelpButtonUsed; + bool InfoButtonUsed; + bool DiscountUsed; + bool QRCodeReceiptUsed; + + int ComBaudRate; + + QString cust_nr; + QString group_nr; + QString zone_nr; + QString machine_nr; + bool validPSAconfig; + + QByteArray characterSet; + +#ifdef USE_ISMAS + QString ismasHost; + quint16 ismasPort; + QString ismasID; + bool ismasDebug; + bool ismasCheckEntries; + quint16 ismasConnectionTimeout; + bool usePINgenerator; +#endif + + quint32 ScreenChangeTimeoutTime; + + PAYMENT_VARIANTS::CASH_VARIANT cashVariant; + + // ErrorCode-mapping + QHash errorTextHash; + +public: + explicit ATBHMIconfig(QObject *parent = nullptr); + ~ATBHMIconfig(); + + QSettings *getSettingsPtr(); + const QSettings & getSettings(); + + void initEarly(); + void initDefered(); + + QString getComPortName(); + + QString getSuspendMethode(); + QString getSuspendProgram(); + QString getSuspendTime(); + + QString getSellTimeoutTime(); + + quint32 getHMIScreenChangeTimeoutTime(); + + QString getQmPath(); + + QString getLang(int lang); + QString getCountry(int country); + bool isLanguageUsed(int lang); + + bool isInfoButtonUsed(); + bool isHelpButtonUsed(); + + bool isPaymentUsed(int payment); + bool isVehicleUsed(int vehicle); + bool isProductUsed(int product); + bool isProcductUsed(int product); + + QString getTouchFeedbackGPIO(); + QString getTouchFeedbackOnValue(); + QString getTouchFeedbackOffValue(); + + bool hasFeatureTF(); // touch feedback + bool hasFeatureLog(); // log serial communication + bool hasFeatureDC(); // display control + bool hasFeatureStartButton(); // StartButton + bool hasFeatureSelfOOO(); // self ooo capability + bool hasFeatureDBusDisplayControl(); + bool hasFeatureDBusSuspendControl(); + bool hasFeatureShowParkingTime(); // show parking time in addition to parking end time + + + QString getTextfileName(); + QString getAlternativeLogoFilename(); + + const QByteArray & getDefaultCharacterSet() const; + + QString getOOOServiceNumber(); + QString getOOOServiceEmail(); + QString getOOOAlternativeLocation(); + QString getLicensePlateExampleString(); + QString getQrCodeLink(); + + const QString & getPaymentCurrencySymbol(); + +#ifdef USE_BARCODESCANNER + bool getUseBarcodeScanner(); + QString & getBarcodeScannerInputDevice(); +#endif + +#ifdef USE_RFIDREADER + bool getUseRFIDReader(); + QString & getRFIDReaderInputDevice(); +#endif + +#ifdef USE_EXTERNAL_TICKETMANAGER + QString getEndOfSellingTime(); + int getBlockingTime(); +#endif + +#ifdef USE_ISMAS + QString getISMASHost(); + quint16 getISMASPort(); + QString getISMASId(); + bool isIsmasDebug(); + bool IsmasCheckEntries(); + bool UsePINgenerator(); + quint16 getIsmasConnectionTimeout(); +#endif + + void setFeatureTF(); + void unsetFeatureTF(); + + void setFeatureLog(); + void unsetFeatureLog(); + + void setFeatureDC(); + void unsetFeatureDC(); + + void setFeatureStartButton(); + void unsetFeatureStartButton(); + + void setFeatureSelfOOO(); + void unsetFeatureSelfOOO(); + + void setFeatureDBusDisplayControl(); + void unsetFeatureDBusDisplayControl(); + + void setFeatureDBusSuspendControl(); + void unsetFeatureDBusSuspendControl(); + + void setFeatureShowParkingTime(); + void unsetFeatureShowParkingTime(); + + QString getLogFileName(); + + int getDefaultLanguage(); + QStringList getConfiguredLanguages(); + + int getComBaudRate(); + + QString getErrorText(const QString & errorCode); + + // machine configuration + QString & getCustNr(); + QString & getGroupNr(); + QString & getZoneNr(); + QString & getMachineNr(); + quint8 readPSAConfig(); + + PAYMENT_VARIANTS::CASH_VARIANT getCashVariant(); + + bool discountIsUsed(); + bool qrCodeReceiptIsUsed(); + + // service programs + QString & getNetworkDiagTool(); + //QString & getPTUConfig(); + //QString & getPTUUpdate(); + + OOO_MODE getOOOMode(); + + QString getOOOModeString(); + + PAYMENT_POINTINTIME getPaymentPointInTime(); + bool useHonestPayment(); + + QStringList configGroups; + +private: + OOO_MODE oooMode; + + PAYMENT_POINTINTIME paymentPointInTime; + bool honestPayment; + + void privateReadInputDevices(); + + QString networkDiagToolName; + +signals: + +public slots: + void setDefaultLanguage(quint8 lang); + + void setCustNr(const QString& cust_nr); + void setGroupNr(const QString& group_nr); + void setZoneNr(const QString& zone_nr); + void setMachineNr(const QString& machine_nr); + + void reReadSettings(); +}; + + + + +enum class OOO_MODE : quint8 { + DEFAULT = 0, + USE_PARKDISK = 1, + USE_OTHER_MACHINE = 2 +}; + +#endif // ATBHMICONFIG_H diff --git a/AppControl.cpp b/AppControl.cpp new file mode 100644 index 0000000..62dc26b --- /dev/null +++ b/AppControl.cpp @@ -0,0 +1,2745 @@ +#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 + diff --git a/AppControl.h b/AppControl.h new file mode 100644 index 0000000..3f45781 --- /dev/null +++ b/AppControl.h @@ -0,0 +1,242 @@ +#ifndef APPCONTROL_H +#define APPCONTROL_H + +#include + +#include "version.h" +#include "atb_system.h" +#include "ATBAPP.h" +#include "plugins/PluginManager.h" +#include "plugins/CC/CCInterface.h" +#include "plugins/CalculatePrice/CalculatePriceInterface.h" + +#include "support/VendingData.h" +#include "CalculatePrice/CalculatePriceDefault.h" + +class VMC; +class ATBHMIconfig; +class VendingData; +class QTimer; + +class AppControl : public QObject { + Q_OBJECT +public: + explicit AppControl(QObject *parent = nullptr); + + PROGRAM_MODE getCurrentProgramMode(); + bool requestProgramMode(PROGRAM_MODE newProgramMode); + + bool isProgramModeSELL(); + bool isProgramModeSELL_ENABLE(); + bool isProgramModeSERVICE(); + bool isProgramModeIDLE(); + bool isProgramModeOOO(); + +public slots: + void onRequestOfflineProcessing(); + + // e.g. by button event, + // by vmc screen command, + // by vmc data display command + // ... + void restartSellModeTimeoutTimer(); + + // slots for LED configuration: + void LEDs_default(); + void LEDs_ooo(); + + // CC-Plugin + void onCCStartTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void onCCConfirmTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void onCCCancelTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void onCCRevertTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void onCCPreAuthTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void onCCBookTotalTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void onCCCancelPreAuthTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void onCCRequestCardInfoResult(nsCCInterface::RESULT_STATE resultState, QString & result); + + // CalculatePricePlugin + #ifdef USE_CALCULATEPRICE_PLUGIN + void onCalculatePrice(QString & licensePlate); + void onCalculatePrice(uint parktime); + void onCalculatePrice(QString & cardInfo, QString & cardTransactionInfo); + void onCalculatePrice(); + void 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); + void onProcessTransaction(nsCalculatePriceInterface::RESULT_STATE resultState, + const QString & errorCode, + const QString & errorDescription); + + QChar private_getAccessInformationField(const QString & accessInformationType); + QString 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); + #endif // USE_CALCULATEPRICE_PLUGIN + + + + // vmc control + void onVMCFormatedString(FormatedStringList data); + void onVMCSellData(quint8 nr,QString data); + + // deprecated vmc control for cc + // this allows cc control by screens + void onVMC_ccStartTransaction(); + void onVMC_ccCancelTransaction(); + void onVMC_ccConfirmTransaction(); + void onVMC_ccStartPreauthorisation(); + void onVMC_ccCancelPreauthorisation(); + void onVMC_ccConfirmPreauthorisation(); + void onVMC_ccStartReadCard(); + + // new control interface + void onControl_switchMode(quint16 mode); + void onControl_switchScreen(quint16 screen); + void onControl_setText(quint16 screenNr, quint16 textNr, quint16 lang, QString text); + void onShowCurrentIdle(); + void onShowCurrentOOO(); + + // vendingData + void onSetVendingData(QString key, QByteArray value); + + +signals: + // signals for signaling a mode change to sub-modules: + void changedProgramMode(PROGRAM_MODE newProgramMode); + void changedModeToSELL(); + void changedModeToSERVICE(); + void changedModeToIDLE(); + void changedModeToOOO(); + + void doOfflineProcessing(); + + // cc + + +protected: + bool eventFilter(QObject *obj, QEvent *ev); + +private: + PROGRAM_MODE programmode; + ATBHMIconfig *config; + ATB_system *system; + VMC *vmc; + VendingData *vendingData; + + PluginManager *pluginManager; + + void private_setupProgramModeSELL(); + void private_setupProgramModeSELL_ENABLE(); + void private_setupProgramModeSERVICE(); + void private_setupProgramModeIDLE(); + void private_setupProgramModeOOO(); + void private_unsetProgramModeSERVICE(); + void private_unsetProgramModeOOO(); + + // plugins + CCInterface *cc; + CalculatePriceInterface *calcPriceBackend; + + // init methods called from constructor + void private_initCCPlugin(); + void private_initCalculatePricePlugin(); + void private_initSimulationPlugin(); + + // timers + QTimer *sellModeTimeoutTimer; + + // flag to prepare for OOO in next idle + quint8 prepareInternalOOO; + + // action processing + quint8 startAction(APP_ACTION action); + +#if defined (ARCH_DesktopLinux) + void private_screenshot(); +#endif + +#if defined (CUST00318) + // hack for 00318/Nexobility + void private_CUST00318_handle_CC_ERROR_0x13(); +#endif + + // mouse detection +#if defined (ARCH_PTU4) + QFileSystemWatcher *pInputDeviceWatcher; + void setMouse(const QString & mouseDeviceFile, const QString & touchDeviceFile); + void closeMouse(const QString & mouseDeviceFile, const QString & touchDeviceFile); + bool mousePresent; +#endif + + +private slots: + + // firstRun -> things done with running event loop + void firstRun(); + + // timers + void onSellModeTimerTimeout(); + + + /******************************************** + * CC - Slots + */ + void onCCStartTransaction(); + void onCCStartConfirmTransaction(); + void onCCCancelTransaction(); + + void onCCStartPreauthorisation(); + void onCCConfirmPreauthorisation(); + void onCCCancelPreauthorisationRequest(); + + void onCCPrintReceipt(); + + void onCCErrorBackend(QString & result); + + void onCCStartReadCard(); + /*******************************************/ + + + // called, if system was woken up + void onWakeUp(); + + + /******************************************** + * Barcode / QR-Code - Slots + */ + #ifdef USE_BARCODESCANNER + // called, if a barcode was read from scanner + void onReadBarcode(QString & barcode); + + // called, if a new barcode should be created and sent to vmc + void onRequestNewBarcode(); + #endif + /*******************************************/ + +#if defined (Q_WS_QWS) + void onInputDeviceWatcher_directoryChanged(const QString& path); +#endif + + /******************************************** + * ISMAS + */ + void onProcessed_QRCode(QHash data); + + /******************************************** + * Signal System Errors + */ + void onSignalSystemErrors(const QString & errorCode, const QString & errorDescription); +}; + +#endif // APPCONTROL_H diff --git a/VMCPlugin.pro b/VMCPlugin.pro index e69de29..9735ac7 100644 --- a/VMCPlugin.pro +++ b/VMCPlugin.pro @@ -0,0 +1,103 @@ +TEMPLATE = lib +CONFIG += plugin +QT += gui +QT += serialport + + +INCLUDEPATH+=$$_PRO_FILE_PWD_ +INCLUDEPATH+=$$_PRO_FILE_PWD_/support +INCLUDEPATH+=$$_PRO_FILE_PWD_/plugins +INCLUDEPATH+=$$_PRO_FILE_PWD_/include +INCLUDEPATH+=$$_PRO_FILE_PWD_/HMI +INCLUDEPATH+=$$_PRO_FILE_PWD_/HMI/screens +INCLUDEPATH+=$$_PRO_FILE_PWD_/HMI/support +INCLUDEPATH+=$$_PRO_FILE_PWD_/src +INCLUDEPATH+=$$_PRO_FILE_PWD_/src/ATBAPP +INCLUDEPATH+=$$_PRO_FILE_PWD_/src/ATBAPP/support +INCLUDEPATH+=$$_PRO_FILE_PWD_/src/ATBAPP/support/VMC + +QMAKE_CXXFLAGS += -Wno-deprecated-copy -O + +# default +ARCH = PTU5 + +DEFINES+=ATB_QT_VERSION=\\\"5.11\\\" + +contains( CONFIG, DesktopLinux ) { + QMAKE_CC = ccache $$QMAKE_CC + QMAKE_CXX = ccache $$QMAKE_CXX + QMAKE_CXXFLAGS += -std=c++11 + # QMAKE_CXXFLAGS += -Wno-deprecated-ctor + linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments } + ARCH = DesktopLinux +} + +contains( CONFIG, PTU5 ) { + # QMAKE_CC = ccache $$QMAKE_CC + # QMAKE_CXX = ccache $$QMAKE_CXX + QMAKE_CXXFLAGS += -std=c++11 + linux-clang { QMAKE_CXXFLAGS += -Qunused-arguments } + CONFIG += link_pkgconfig + ARCH = PTU5 +} + +contains( CONFIG, PTU5_YOCTO ) { + greaterThan(QT_MAJOR_VERSION, 4): QT += serialport + PTU5BASEPATH = /opt/devel/ptu5 + ARCH = PTU5 + + # add qmqtt lib + #LIBS += -lQt5Qmqtt +} + +TARGET = ATBVMCPlugin +#DESTDIR = ../plugins +INTERFACE = VMCInterface +INTERFACE_DEFINITION = $${PWD}/src/ATBAPP/VMCInterface.h + +DEFINES += VMCPLUGIN_LIBRARY + +# Default rules for deployment. +#qnx: target.path = /tmp/$${TARGET}/bin +#else: unix:!android: target.path = /opt/$${TARGET}/bin +#!isEmpty(target.path): INSTALLS += target + +# ATBAPP interface +HEADERS += \ + AppControl.h \ + ATBHMIconfig.h \ + atb_system.h \ + include/interfaces.h \ + src/ATBAPP/VMCInterface.h \ + src/ATBAPP/ATBVMCPlugin.h \ + src/ATBAPP/UnifiedDCVMCInterface.h \ + src/ATBAPP/support/VMC/com_interface.h \ + src/ATBAPP/support/VMC/ReceiveBuffer.h \ + src/ATBAPP/support/VMC/SendBuffer.h \ + src/ATBAPP/support/VMC/vmc.h \ + src/ATBAPP/support/VendingData.h + +SOURCES += \ + atb_system.cpp \ + AppControl.cpp \ + ATBHMIconfig.cpp \ + src/ATBAPP/ATBVMCPlugin.cpp \ + src/ATBAPP/support/VMC/com_interface.cpp \ + src/ATBAPP/support/VMC/ReceiveBuffer.cpp \ + src/ATBAPP/support/VMC/SendBuffer.cpp \ + src/ATBAPP/support/VMC/vmc.cpp \ + src/ATBAPP/support/VendingData.cpp + +DISTFILES += \ + generate-version.sh + + +# Define how to create version.h +VERSION_H = $$PWD/include/version.h +version.output = $$PWD/include/version.h +version.commands = $$PWD/generate-version.sh $${ARCH} $${TARGET} $${INTERFACE} $${INTERFACE_DEFINITION} $${VERSION_H} +version.depends = FORCE +version.input = VERSION_H +version.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += version +QMAKE_CLEAN += $${PWD}/include/version.h diff --git a/atb_system.cpp b/atb_system.cpp new file mode 100644 index 0000000..d4207e2 --- /dev/null +++ b/atb_system.cpp @@ -0,0 +1,723 @@ +#include "atb_system.h" +#include "ATBHMIconfig.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +ATB_system::ATB_system(ATBHMIconfig *config, QObject *parent) : + QObject(parent) +{ + + this->config = config; + + this->tf_gpio = nullptr; + +#if defined (ARCH_PTU2) + if (config->hasFeatureTF()) init_touch_feedback(); +#elif defined (ARCH_PTU4) + if (config->hasFeatureTF()) init_touch_feedback(); +#endif + +} + + +ATB_system::~ATB_system() +{ + if (config->hasFeatureTF()) { + delete(this->tf_gpio_outstream); + delete(this->tf_gpio); + } +} + + + +void ATB_system::executeSystemCommand(quint16 cmd, QByteArray data) +{ + Q_UNUSED(data); + + switch (cmd) { + case SYS_COMMAND_SLEEP: + qDebug() << "ATB_system::executeSystemCommand(): " << cmd; + dbus_permitSuspend(); + break; + case SYS_COMMAND_DIMLOW: + if (config->hasFeatureDBusDisplayControl()) { + dbus_DimControlStart(); + } else { + this->DimLow(); + } + break; + case SYS_COMMAND_DIMHIGH: + if (config->hasFeatureDBusDisplayControl()) { + dbus_DimHighPermanent(); + } else { + this->DimHigh(); + } + break; + case SYS_COMMAND_BEEP: + if (config->hasFeatureTF()) this->Beep(); + break; + case SYS_COMMAND_MACHINE_NUMBER: // deprecated and not used + // setMachineNumber(data); + break; + case SYS_COMMAND_LED_CONTROL: + this->privateConfigLED(data); + break; + case SYS_COMMAND_HALT: + this->halt(); + break; + case SYS_COMMAND_REBOOT: + this->reboot(); + break; + default: + qDebug() << "ATB_system::executeSystemCommand(): unknown command: " << cmd; + break; + } + +} + + + +/***************************************************************************** + * stubs for display control + * + * this methodes should later be scriptable an call corresponding methodes + * in DisplayControl via DBus. + */ + +void ATB_system::DimLow() { return; } + +void ATB_system::DimHigh() +{ + //if (config->hasFeatureDC()) + // this->dc->DimHigh(); +} + +//void ATB_system::Dim(quint8 value) { if (config->hasFeatureDC()) this->dc->Dim((uchar)value); } + +void ATB_system::DimStart() { return; } +void ATB_system::DimStop() { return; } + + +/***************************************************************************** + */ + +void ATB_system::Beep() { + qDebug() << "ATB_system::Beep(): now i should make a beep!"; +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + QTimer::singleShot (BEEPTIMEOUT, this, SLOT(BeepStop()) ); + *(this->tf_gpio_outstream) << config->getTouchFeedbackOnValue(); + this->tf_gpio_outstream->flush(); +#endif +} + +void ATB_system::BeepStop() { +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + *(this->tf_gpio_outstream) << config->getTouchFeedbackOffValue(); + this->tf_gpio_outstream->flush(); +#endif +} + + +int ATB_system::init_touch_feedback() { +#if defined (ARCH_PTU2) + QString GPIODirName("/sys/class/gpio/"); + QString directionFileName; + QString valueFileName; + + // # echo 129 > /sys/class/gpio/export + QFile file("/sys/class/gpio/export"); + file.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&file); + out << config->getTouchFeedbackGPIO(); + file.close(); + + // check, if gpio-dir was created + GPIODirName.append("gpio"); + GPIODirName.append(config->getTouchFeedbackGPIO()); + QFileInfo fileinfo1(GPIODirName); + if (! fileinfo1.isDir() ) { + qDebug() << "ATB_system::init_touch_feedback(): \"" << GPIODirName << "\" is not a directory"; + config->unsetFeatureTF(); + return -1; + } + directionFileName = GPIODirName + "/direction"; + QFileInfo fileinfo2(directionFileName); + if (! fileinfo2.isWritable() ) { + qDebug() << "ATB_system::init_touch_feedback(): \"" << directionFileName << "\" is not writable"; + config->unsetFeatureTF(); + return -1; + } + + // # echo out > /sys/class/gpio/gpio129/direction + QFile directionfile(directionFileName); + directionfile.open(QIODevice::ReadWrite | QIODevice::Text); + QTextStream out2(&directionfile); + out2 << "out"; + out2.flush(); + + // re-read 'direction' for confirmation + // -> the following is disabled because this sometimes failed + //QTextStream in(&directionfile); + //QString line = in.readLine(); + //if (line != "out") { + // qDebug() << "ATB_system::init_touch_feedback(): re-read directionfile failed"; + // config->unsetFeatureTF(); + // return -1; + //} + directionfile.close(); + + // open gpio file + valueFileName = GPIODirName + "/value"; + this->tf_gpio = new QFile(valueFileName); + + this->tf_gpio->open(QIODevice::WriteOnly); + this->tf_gpio_outstream = new QTextStream(this->tf_gpio); + + qDebug() << "ATB_system::init_touch_feedback(): GPIODirName = \"" << GPIODirName << "\""; + qDebug() << "ATB_system::init_touch_feedback(): valueFileName = \"" << valueFileName << "\""; + +#elif defined (ARCH_PTU4) + QString sysfs_buzzer("/sys/class/leds/Buzzer/brightness"); + + this->tf_gpio = new QFile(sysfs_buzzer); + tf_gpio->open(QIODevice::WriteOnly); + + this->tf_gpio_outstream = new QTextStream(this->tf_gpio); +#endif + + return 1; +} + + +/******************************************************************************** + * set date/time + * + * Datestring format: YYYY-MM-DD hh:mm[:ss] + * This is a format that is accepted both by busybox-date (e.g. PTU2) and by + * coreutils-date. + * + * example: + * /bin/date -s "2015-08-05 17:55:00" + * + */ +void ATB_system::setDateTime(const QString & dateTimeString) +{ + QString datetimeCMD = "/bin/date -s " + dateTimeString; + +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + QProcess::startDetached(datetimeCMD); + + // the following does not work on PTU2: + //QProcess::startDetached ("/sbin/hwclock -w"); +#endif + + +} + + + +/******************************************************************************** + * static functions to write single values to files. + * + * Used e.g. to store some machine specific settings like customer number or machine + * number in filesystem. + * + * This data should be updated rarely and the calling function must keep this in mind! + * + * Note: QFile::open() creates a file, if it is not existing. + */ + +quint8 ATB_system::setPSAConfigInt(const QString &filename, quint16 iValue) +{ + return setPSAConfigString(filename, QString::number(iValue)); +} + + +quint8 ATB_system::setPSAConfigString(const QString & filename, const QString & sValue) +{ +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + QFile file(filename); +#else + QFile file(QDir::homePath() + filename); +#endif + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "ATB_system::setPSAConfigString() cannot open file: " << filename; + qDebug() << " file.errorString() = " << file.errorString(); + return 0; + } + + qDebug() << "ATB_system::setPSAConfigString() write file: " << filename; + + QTextStream out(&file); + out << sValue; + out.flush(); + + file.close(); + + return 1; +} + + +quint16 ATB_system::readPSAConfigInt(const QString & filename) +{ + bool ok; + quint16 result = (quint16)readPSAConfigString(filename).toInt(&ok); + + if (!ok) { + result = 0; + } + + return result; +} + + + +QString ATB_system::readPSAConfigString(const QString & filename) +{ +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + QFileInfo fileinfo(filename); +#else + QFileInfo fileinfo(QDir::homePath() + filename); +#endif + if (! fileinfo.isReadable() ) { + qDebug() << "ATB_system::readPSAConfigString(): \"" << filename << "\" is not readable"; + return 0; + } + +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + QFile file(filename); +#else + QFile file(QDir::homePath() + filename); +#endif + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "ATB_system::readPSAConfigString() cannot open file: " << filename; + return 0; + } + + QTextStream in(&file); + + QString stringValue = in.readLine(100); + qDebug() << "ATB_system::readPSAConfigString() stringValue = " << stringValue; + + file.close(); + + return stringValue; +} + + +/******************************************************************************** + * LED control interface + * + */ +void ATB_system::onConfigLED(LED_NAME LED, LED_TRIGGER trigger, quint16 delayOn, quint16 delayOff) +{ + QString LEDPath = "/sys/class/leds/"; + QString LEDName = ""; + QString LEDtrigger = ""; + + // DEBUG + + + + // //////// + + + switch (LED) { +#if defined (ARCH_PTU2) + case ATB_LED1: LEDName.append("atb_led1"); break; + case ATB_LED2: LEDName.append("atb_led2"); break; + case ATB_LED3: LEDName.append("atb_led3"); break; + case ATB_LED4: LEDName.append("atb_led4"); break; +#elif defined (ARCH_PTU4) + case ATB_LED1: LEDName.append("D504"); break; + case ATB_LED2: LEDName.append("D503"); break; + case ATB_LED3: LEDName.append("D502"); break; + case ATB_LED4: LEDName.append("D501"); break; +#endif + default: return; + } + + switch (trigger) { + case LED_TRIGGER_NONE: LEDtrigger.append("none"); break; + case LED_TRIGGER_TIMER: LEDtrigger.append("timer"); break; +#if defined (ARCH_PTU2) + case LED_TRIGGER_DEFAULT_ON: LEDtrigger.append("default-on"); break; +#elif defined (ARCH_PTU4) + /* note: ptu4 leds currently do not support 'default-on' trigger + * workaround is to set delay_off to '0' + */ + case LED_TRIGGER_DEFAULT_ON: LEDtrigger.append("timer"); + trigger = LED_TRIGGER_TIMER; + delayOn = 1; + delayOff = 0; + break; +#endif + default: return; + } + + QString filename = LEDPath + LEDName + "/trigger"; + + // set trigger + this->privateSetLEDTrigger(filename, LEDtrigger); + + + // set delays for trigger 'timer' + if (trigger == LED_TRIGGER_TIMER) { + + // set delay on + if (delayOn != 0) { + filename = LEDPath + LEDName + "/delay_on"; + this->privateSetLEDDelayOn(filename, QString::number(delayOn)); + } + + // set delay off + if (delayOff != 0) { + filename = LEDPath + LEDName + "/delay_off"; + this->privateSetLEDDelayOn(filename, QString::number(delayOff)); + } + + } + + return; +} + +/***************************************************************************** + * wrapper function for LED control. + * + * TODO: Test, check: plausibillity, cmd.lenght(), type casts to LED_NAME, LED_TRIGGER + */ +void ATB_system::privateConfigLED(const QByteArray & cmd) +{ + quint16 delayOn = (quint8)cmd.at(3) + ((quint16)(cmd.at(2) << 8)); + quint16 delayOff = (quint8)cmd.at(5) + ((quint16)(cmd.at(4) << 8)); + + this->onConfigLED((LED_NAME)cmd.at(0), (LED_TRIGGER)cmd.at(1), delayOn, delayOff); +} + +void ATB_system::privateSetLEDTrigger(const QString & led, const QString & trigger) +{ +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + QFile file(led); +#else + QFile file(QDir::homePath() + led); +#endif + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "ATB_system::onConfigLED() cannot open file: " << led; + qDebug() << " file.errorString() = " << file.errorString(); + return; + } + + qDebug() << "ATB_system::onConfigLED() write file: " << led; + + QTextStream out(&file); + out << trigger; + out.flush(); + + file.close(); +} + + +void ATB_system::privateSetLEDDelayOn(const QString & led, const QString & delayOn) +{ +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + QFile file(led); +#else + QFile file(QDir::homePath() + led); +#endif + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "ATB_system::onConfigLED() cannot open file: " << led; + qDebug() << " file.errorString() = " << file.errorString(); + return; + } + + qDebug() << "ATB_system::onConfigLED() write file: " << led; + + QTextStream out(&file); + out << delayOn; + out.flush(); + + file.close(); +} + + +void ATB_system::privateSetLEDDelayOff(const QString & led, const QString & delayOff) +{ +#if defined (ARCH_PTU2) || defined (ARCH_PTU4) + QFile file(led); +#else + QFile file(QDir::homePath() + led); +#endif + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "ATB_system::onConfigLED() cannot open file: " << led; + qDebug() << " file.errorString() = " << file.errorString(); + return; + } + + qDebug() << "ATB_system::onConfigLED() write file: " << led; + + QTextStream out(&file); + out << delayOff; + out.flush(); + + file.close(); +} + + +/***************************************************************************** + * functions for switching blink button on/off + * This is only available on PTU4 + */ +void ATB_system::switchBlinkButtonOn() +{ + this->privateSwitchBlinkButton(true); +} + +void ATB_system::switchBlinkButtonOff() +{ + this->privateSwitchBlinkButton(false); +} + +void ATB_system::privateSwitchBlinkButton(bool on) +{ +#if defined (ARCH_PTU2) + +#elif defined (ARCH_PTU4) + QString BlinkLED("/sys/class/leds/TIMER_RESET/brightness"); + + QFile file(BlinkLED); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qDebug() << "ATB_system::privateSwitchBlinkButton() cannot open file: " << BlinkLED; + qDebug() << " file.errorString() = " << file.errorString(); + return; + } + + QTextStream out(&file); + + if (on) { out << "0"; } + else { out << "1"; } + + out.flush(); + + file.close(); + + +#else + Q_UNUSED(on) +#endif +} + + +/***************************************************************************** + * halt system. + * + */ +void ATB_system::halt() +{ + QProcess::startDetached("/sbin/halt"); +} + +/***************************************************************************** + * reboot system. + * + */ +void ATB_system::reboot() +{ + QProcess::startDetached("/sbin/reboot"); +} + + +/***************************************************************************** + * functions for wake vmc + * This is only available on PTU4 + */ +void ATB_system::onWakeVMC() +{ +#if defined (ARCH_PTU2) + +#elif defined (ARCH_PTU4) + if (config->hasFeatureDBusSuspendControl()) { + this->dbus_wakeSystem(); + } +#endif +} + + + +/***************************************************************************** + * get event device name by a type: + * + * e.g. type is TOUCHSCREEN: + * getEventDeviceName(TOUCHSCREEN) returns "/dev/input/event0" + * + */ +QString ATB_system::getEventDeviceName(DEVICE_TYPE type) +{ + QString procDevices = "/proc/bus/input/devices"; + + QFile inFile(procDevices); + if (!inFile.open(QFile::ReadOnly)) { + qCritical() << "ATB_system::getEventDeviceName() ERROR open()" << procDevices; + return ""; + } + + QTextStream textInStream(&inFile); + QString line; + QString N_RegExp; + QString DeviceTypeName; + int lineCounter = 0; + bool typeFound = false; + QString H_RegExp = "^H: Handlers"; // H: Handlers=kbd event2 + QString eventDeviceName = ""; + QString eventPart; + QStringList devNames; + QString DeviceNamePattern; + + //PTU4 Touchscreen N: Name="atmel_mxt_ts T100 touchscreen" + //PTU4 GPIO kys N: Name="gpio_keys.6" + //RFID Reader, Stronglink SL040 N: Name="StrongLink USB CardReader" + //2D Scanner, Datalogig DSE0420 N: Name="Datalogic ADC Inc. Handheld Barcode Scanner" VID: 05f9, PID:4005 + //2D Scanner, Datalogig DSM0400 N: Name="Datalogic Handheld Barcode Scanner" VID: 05f9, PID:4005 + //2D Scanner, Honeywell N56000 N: Name="Honeywell Imaging & Mobility N5600" + //2D Scanner, Newland FM3080 N: Name="Newland Auto-ID NLS-FM3080V2-20 USB POS KBW" VID: 1eab, PID:0022 + //2D Scanner, Opticon MDI-4000 N: Name="Opticon Opticon USB Barcode Reader" + //USB Mouse N: Name="Logitech USB-PS/2 Optical Mouse" + + /* Datalogic DSE0420 + I: Bus=0003 Vendor=05f9 Product=4005 Version=0110 + N: Name="Datalogic ADC Inc. Handheld Barcode Scanner" + P: Phys=usb-700000.ehci-2.3/input2 + S: Sysfs=/devices/ahb.0/700000.ehci/usb1/1-2/1-2.3/1-2.3:1.2/input/input14 + U: Uniq=S/N G16C15795 + H: Handlers=kbd event3 + B: PROP=0 + B: EV=120013 + B: KEY=10000 7 ff9f207a c14057ff febeffdf ffefffff ffffffff fffffffe + B: MSC=10 + B: LED=1f + */ + /* Datalogic DSM04XX + I: Bus=0003 Vendor=05f9 Product=4005 Version=0110 + N: Name="Datalogic Handheld Barcode Scanner" + P: Phys=usb-700000.ehci-2.3/input0 + S: Sysfs=/devices/ahb.0/700000.ehci/usb1/1-2/1-2.3/1-2.3:1.0/input/input15 + U: Uniq=S/N G21FA0180 + H: Handlers=kbd event3 + B: PROP=0 + B: EV=120013 + B: KEY=e080ffdf 1cfffff ffffffff fffffffe + B: MSC=10 + B: LED=1f + */ + /* + I: Bus=0003 Vendor=1eab Product=0022 Version=0110 + N: Name="Newland Auto-ID NLS-FM3080V2-20 USB POS KBW" + P: Phys=usb-700000.ehci-2.2/input0 + S: Sysfs=/devices/ahb.0/700000.ehci/usb1/1-2/1-2.2/1-2.2:1.0/input/input6 + U: Uniq=FM3080V2-20-BH00017 + H: Handlers=kbd event2 + B: PROP=0 + B: EV=120013 + B: KEY=10000 7 ff9f207a c14057ff febeffdf ffefffff ffffffff fffffffe + B: MSC=10 + B: LED=1f + */ + + + switch (type) { + case EVENT_DEVICE_BARCODEREADER: + N_RegExp = "^N: Name=.+(Barcode|Honeywell Imaging|Newland Auto).+"; + DeviceTypeName = "1D/2D-Scanner"; + DeviceNamePattern = "event"; + break; + case EVENT_DEVICE_TOUCHSCREEN: + N_RegExp = "^N: Name=.+touch.+"; + DeviceTypeName = "Touchscreen"; + DeviceNamePattern = "event"; + break; + case EVENT_DEVICE_GPIO_KEYBOARD: + N_RegExp = "^N: Name=.+gpio.+"; + DeviceTypeName = "gpio-keys"; + DeviceNamePattern = "event"; + break; + case EVENT_DEVICE_KEYBOARD: + N_RegExp = "^N: Name=.+touch.+"; + DeviceTypeName = "keyboard"; + DeviceNamePattern = "event"; + break; + case EVENT_DEVICE_RFIDREADER: + N_RegExp = "^N: Name=.+Card.+"; + DeviceTypeName = "card reader"; + DeviceNamePattern = "event"; + break; + case EVENT_DEVICE_USBMOUSE: + N_RegExp = "^N: Name=.+USB.+Mouse.+"; + DeviceTypeName = "USB Mouse"; + DeviceNamePattern = "mouse"; + break; + } + + qDebug() << "ATB_system::getEventDeviceName() N_RegExp = " << N_RegExp; + + do { + line = textInStream.readLine(); + + // DEBUG + //qDebug() << " " << line; + + // find "N:"-line: + if (line.contains(QRegExp(N_RegExp))) { + typeFound = true; + lineCounter = 0; + + qDebug() << "ATB_system::getEventDeviceName() found N_RegExp = " << N_RegExp; + + } + + if (typeFound) { + // found "N:"-line, now search for "H:"-line; + // the corresponding "H:"-line should be within the next 7 following text lines. + + // example lines: + // - "H: Handlers=event0 mouse0" -> for touchscreen on PTU4 + // - "H: Handlers=event3 mouse1" -> for USB mouse on PTU4 + + lineCounter++; + if ( (lineCounter<=7) && (line.contains(QRegExp(H_RegExp))) ) { + eventPart = line.split("=", QString::SkipEmptyParts).takeLast(); + devNames = eventPart.split(" ", QString::SkipEmptyParts); + + for (int i=0; i +#include +#include +#include "version.h" + + +#define SYS_COMMAND_SLEEP 0x0001 +#define SYS_COMMAND_DIMLOW 0x0002 +#define SYS_COMMAND_DIMHIGH 0x0003 +#define SYS_COMMAND_BEEP 0x0004 +#define SYS_COMMAND_MACHINE_NUMBER 0x0005 +#define SYS_COMMAND_HALT 0x0006 +#define SYS_COMMAND_REBOOT 0x0007 +#define SYS_COMMAND_LED_CONTROL 0x3600 + + +#if defined (ARCH_PTU2) + #define BEEPTIMEOUT 200 +#elif defined (ARCH_PTU4) + #define BEEPTIMEOUT 100 +#else + #define BEEPTIMEOUT 100 +#endif + +class ATBHMIconfig; + +class ATB_system : public QObject { + Q_OBJECT + +private: + int init_touch_feedback(); + int init_sc_dbus(); + + ATBHMIconfig *config; + QFile *tf_gpio; + QTextStream *tf_gpio_outstream; + + quint8 current_dim_value; + + void privateConfigLED(const QByteArray & cmd); + void privateSetLEDTrigger(const QString & led, const QString & trigger); + void privateSetLEDDelayOn(const QString & led, const QString & delayOn); + void privateSetLEDDelayOff(const QString & led, const QString & delayOff); + void privateSwitchBlinkButton(bool on); + +public: + explicit ATB_system(ATBHMIconfig *config, QObject *parent = nullptr); + ~ATB_system(); + + + static quint8 setPSAConfigInt(const QString & filename, quint16 iValue); + static quint16 readPSAConfigInt(const QString &filename); + + static quint8 setPSAConfigString(const QString & filename, const QString & sValue); + static QString readPSAConfigString(const QString & filename); + + enum LED_NAME { + ATB_LED1 = 1, + ATB_LED2, + ATB_LED3, + ATB_LED4 + }; + + enum LED_TRIGGER { + LED_TRIGGER_NONE = 1, + LED_TRIGGER_TIMER, + LED_TRIGGER_DEFAULT_ON + }; + + enum DEVICE_TYPE { + EVENT_DEVICE_BARCODEREADER, + EVENT_DEVICE_TOUCHSCREEN, + EVENT_DEVICE_GPIO_KEYBOARD, + EVENT_DEVICE_KEYBOARD, + EVENT_DEVICE_RFIDREADER, + EVENT_DEVICE_USBMOUSE + }; + + static QString getEventDeviceName(DEVICE_TYPE type); + static quint8 setupInductionLoops(); + + + void dbus_permitSuspend(); + void dbus_preventSuspend(); + + void dbus_DimHighPermanent(); + void dbus_DimControlStart(); + + void dbus_wakeSystem(); + +signals: + /* emitted, after waking up from suspend... + */ + void wakeUp(); + +public slots: + void executeSystemCommand(quint16 cmd, QByteArray data); + void DimHigh(); + void DimLow(); + void Dim(quint8 value); + void DimStart(); + void DimStop(); + void Beep(); + void BeepStop(); + void setDateTime(const QString &dateTimeString); + + void onConfigLED(LED_NAME LED, LED_TRIGGER trigger, quint16 delayOn = 0, quint16 delayOff = 0); + + void switchBlinkButtonOn(); + void switchBlinkButtonOff(); + + void onWakeVMC(); + void halt(); + void reboot(); +}; + +#endif // ATB_SYSTEM_H diff --git a/generate-version.sh b/generate-version.sh new file mode 100755 index 0000000..e3e1736 --- /dev/null +++ b/generate-version.sh @@ -0,0 +1,157 @@ +#!/bin/bash + +VERSION_STRING="" + +#GIT='/cygdrive/c/Program Files \(x86\)/Git/bin/git' +GIT=git + + + + +parse_git_branch () { + $GIT branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/* \(.*\)/\1/" +} + + +ARCH=$1 +TARGET=$2 +INTERFACE=$3 +INTERFACE_DEFINITION=$4 +VERSION_H=$5 + +SCRIPT=$(readlink -f $0) +SCRIPTPATH=`dirname $SCRIPT` +OUTPUTDIR=$(pwd) +echo " current dir is : " $(pwd) +echo $SCRIPT +echo $SCRIPTPATH + +echo "changing dir to script path: " $SCRIPTPATH +cd $SCRIPTPATH + +# set version string ################################################################## +if [ -z $VERSION_STRING ] ; then + VERSION_STRING=$(date +%Y%m%d_%H%M) +fi +GIT_DESCRIBE=$($GIT describe --always) +GIT_BRANCH=$(parse_git_branch) + +# extract path from branchname: +IFS='_' read -ra TMP_ARRAY <<< "${GIT_BRANCH}" +BRANCH_PATH=${TMP_ARRAY[0]} + + + +# detect if we have a development version: +if [ ${#TMP_ARRAY[1]} -gt 0 ] ; then + DEV_SUFFIX="_dev" +else + DEV_SUFFIX="" +fi + +# detect if git status is dirty +GIT_DESCRIBE_DIRTY=$($GIT describe --dirty) +if [ "${GIT_DESCRIBE_DIRTY:(-6)}" == "-dirty" ] ; then + DIRTY_SUFFIX="_dirty" +else + DIRTY_SUFFIX="" +fi + + +if [ -n "$DIRTY_SUFFIX" ] || [ -n "$DEV_SUFFIX" ] ; then + DEVDIRTY=true +else + DEVDIRTY=false +fi + + +# extract interface definition + +# +#Q_DECLARE_INTERFACE(CCInterface, +# "eu.atb.ptu.plugin.CCInterface/2.9.0") +#Q_DECLARE_INTERFACE(DeviceControllerInterface, +# "eu.atb.ptu.plugin.DeviceControllerInterface/1.0") +# -> extract whole string within quotation marks +INTERFACE_VERSION=$(grep 'eu.atb.ptu.plugin.' ${INTERFACE_DEFINITION}) +# get string within quotes: +INTERFACE_VERSION=`echo ${INTERFACE_VERSION} | awk -F \" '{print $2}'` + + +# +# write version.h + + +echo " TARGET is: $TARGET" +echo " ARCH is: $ARCH" +echo " " +echo " PluginName: $TARGET" +echo " Interface: $INTERFACE" +echo " InterfaceVersion: $INTERFACE_VERSION" +echo " " +echo " new version is: $VERSION_STRING" +echo " git describe is: $GIT_DESCRIBE" +echo " git branch is: $GIT_BRANCH" +echo " branch-path is: $BRANCH_PATH" +echo " " + +echo " dev suffix: $DEV_SUFFIX" +echo " dirty suffix: $DIRTY_SUFFIX" + + +PLUGIN_VERSION=${VERSION_STRING} +#ATB_QT_GIT_DESCRIBE=${GIT_DESCRIBE}_${GIT_BRANCH} +PLUGIN_GIT_DESCRIBE=${GIT_DESCRIBE}_${BRANCH_PATH}${DEV_SUFFIX}${DIRTY_SUFFIX} + +#TARGET=IngenicoZVT_CCPlugin + + +# build version.h ##################################################################### + +echo " building new version info (version.h) ..." + + + + +echo "#ifndef VERSION_H" > ${VERSION_H} +echo "#define VERSION_H" >> ${VERSION_H} +echo "" >> ${VERSION_H} +echo "" >> ${VERSION_H} +echo "#define INTERFACE_VERSION \"${INTERFACE_VERSION}\"" >> ${VERSION_H} +echo "#define PLUGIN_VERSION \"${PLUGIN_VERSION}\"" >> ${VERSION_H} +echo "" >> ${VERSION_H} +echo "#define PLUGIN_GIT_DESCRIBE \"${PLUGIN_GIT_DESCRIBE}\"" >> ${VERSION_H} +echo "" >> ${VERSION_H} +echo "" >> ${VERSION_H} + +cat <> ${VERSION_H} + +const std::string pluginInfoString = R"( +{ + "Interface": "${INTERFACE}", + "InterfaceVersion": "${INTERFACE_VERSION}", + "PluginName": "${TARGET}", + "Version": "${PLUGIN_VERSION}", + "git-describe": "${PLUGIN_GIT_DESCRIBE}", +} +)"; + +EOT + +echo "" >> ${VERSION_H} +echo "" >> ${VERSION_H} +if [ ${DEVDIRTY} == "true" ] ; then +echo "#define DEVDIRTY" >> ${VERSION_H} +echo "" >> ${VERSION_H} +echo "" >> ${VERSION_H} +fi +echo "#define SYSTEM_ARCH \"${ARCH}\"" >> ${VERSION_H} +echo "#define ARCH_${ARCH}" >> ${VERSION_H} +echo "" >> ${VERSION_H} +echo "" >> ${VERSION_H} +echo "#endif //VERSION_H" >> ${VERSION_H} + + + + + diff --git a/include/interfaces.h b/include/interfaces.h new file mode 100755 index 0000000..15462a8 --- /dev/null +++ b/include/interfaces.h @@ -0,0 +1,1468 @@ +#ifndef INTERFACE_H +#define INTERFACE_H + +#include + + + +struct T_emp +{ + + // Fixdata from EMP: + uint8_t shaft; // = changer level + uint16_t countryCode; + uint8_t scale; + uint8_t decimals; + uint8_t coinValues[16]; + uint16_t routing; + + // Master specs: + uint8_t gotSetup; // 1: got specifications from master 0: no specs + uint16_t coinAccept; // bit 0 = coin1 bit H=accept + uint8_t tokenChannel; + uint16_t denomination[16]; + + // dynamic: + uint8_t state; // step counter of EMP (electronic coin checker) FSM (finite state machine): + // 0=start command + // 1=powered, do emp ini, send reset + // 2=delay + // 3=wait for response, requesting status after response + // 4,5 through, startup + // 6: wait for status + // 7: through, startup + // 8: IDLE state. EMP is up and ready, polling is running + // 9: polling on, payment not yet on + // 10: payment, check coins + // 11: through + // 12: wait 1s for last coin + // 90: stop all, 1s delay + // 99: off, all stopped + + + uint8_t pollingRunning; + uint8_t paymentRunning; + +}; + +struct Trtc_DateTime +{ + uint8_t rtc_hour; + uint8_t rtc_min; + uint8_t rtc_sec; + uint8_t rtc_dayOfMonth; + uint8_t rtc_month; + uint8_t rtc_year; + uint8_t rtc_dayOfWeek; +}; + +struct Tprn_hw_state +{ + // hardware (IO's) + bool powerRdBk; // prn pwr is on + bool rsSwOk; // serial switch (printer or modem) is set to printer + bool rsDrvOk; // RS232 converter for PTU, Printer and Modem in on + + bool ReadyLine; // HW signal from printer showing ready + bool inIdle; // powered and free from errors + bool paperNearEnd; // paper roll runs out + bool noPaper; + bool ErrorTemp; + bool HeadOpen; + bool cutterJam; + bool noResponse; // printer is not connected, cable broken, wrong baudrate + bool badResponse; +}; + + struct Tprn_currentSettings +{ + uint8_t currFont; + uint8_t currSize; + uint8_t currHeigth; + uint8_t currWidth; + bool nowBold; + bool nowInvers; + bool nowUnderlined; + uint8_t currDensity; + uint8_t currSpeed; + bool nowAligned; +}; + +// obsolete + struct T_dynDat +{ + uint8_t licensePlate[8]; + uint8_t vendingPrice[8]; + uint8_t parkingEnd[8]; + uint8_t currentTime[8]; + uint8_t currentDate[8]; + uint8_t dynDat5[8]; + uint8_t dynDat6[8]; + uint8_t dynDat7[8]; +}; + + +struct T_vaultRecord +{ + + // Kassenbeleg (Abrechnungsdatensatz = Kassenwechsel-Datensatz) + char startbuffer[4]; // Psa> // never move or change this 1st entry + uint16_t AccountingNumber; + uint16_t CUNU; + uint16_t MANU; + uint16_t resint1; + //uint16_t resint2; + + char label1buffer[4]; // tim> + uint8_t year; + uint8_t month; + uint8_t dom; + uint8_t hour; + uint8_t min; + uint8_t sec; + uint8_t DoW; + uint8_t reschar3; + + char label2buffer[4]; // abs> + uint32_t AbsIncome1; + uint32_t AbsReserve; + uint32_t AbsNrOfCuts; + + char label3buffer[4]; // mw > + + // Verkauf, Tuer zu: + uint32_t VKcoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen + uint32_t VKcoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben + + // Service, Tuer offen: + uint16_t ServCoinsInserted[16]; // nur fuer Wechsler, soviel wurde eingeworfen + uint16_t ServCoinsReturned[6]; // nur fuer Wechsler, Anzahl Muenzen pro Typ, soviel wurde zurueckgegeben + uint16_t resint3; + uint16_t resint4; + uint16_t currentTubeContent[6]; // nur fuer Wechsler, aktueller Fuellstand + uint16_t resint5; + uint16_t resint6; + + char label4buffer[4]; // box> + uint16_t coinsInVault[16]; + uint16_t billsInStacker[8]; + + char label5buffer[4]; // val> + // actually constant unless exchange rate is changed + uint16_t coinDenomination[16]; // 5..50000 (z.B. 2? sind in Ungarn 760Ft) + uint16_t billDenom[8]; + uint16_t tubeDenom[6]; + uint16_t exchangeRate; + uint16_t resint9; + + // new from 1.8.23 + uint32_t cutsSinceCBchange; + uint32_t CBcontent_cent; + uint32_t CBnrofCoins; + + char endofblock[4]; // end +// 332 bytes + + uint16_t CRC16; // Xmodem16 from startbuffer[0] to endofblock[3] + uint16_t resint11; + char endofRecord[4]; // ---- + +}; + +struct T_moduleCondition +{ + // store conditon of all system components, hold in RAM + // 0 means unknown, not yet tested/used + // 1 means OK + // 50..99 = HINT / Notification + // 100..150 = WARNING + // 200..250 = ERROR + + uint8_t ram; + uint8_t intEe; + uint8_t extEe; + + uint8_t rtc; // 1: time/date OK 100: time not plausible 200: hardware error + uint8_t boardHw; + uint8_t printer; + uint8_t modem; + + uint8_t signal; // 1...99 + uint8_t regist; // 100:not 1:reg 2:ping OK 3:gotTime + uint8_t mdbBus; + uint8_t coinChecker; // EMP, OMP or mei-cashflow + + uint8_t coinEscrow; + uint8_t mifareReader; // 0: unknown 1=OK 200=no response 201=wrong response 202: Reader reports HW-error + uint8_t creditTerm; + uint8_t coinReject; + + uint8_t coinSafe; + uint8_t billSafe; + uint8_t voltage; // 1:11..14V + uint8_t temper; + + uint8_t poweronTest; + uint8_t doorState; // 1: alles zu 200: tuer offen + bit1(S) +bit2(CB) + bit3(CB) + uint8_t doorWasOpened; // 1: all doors are closed 200: any door was just opened + uint8_t changer; // can only be tested by usage + + uint8_t coinBlocker; // can only be tested by usage + uint8_t billReader; // can only be tested by usage + uint8_t ResetReason; + uint8_t allModulesChecked; + + uint8_t alarmState; + uint8_t res11; + uint8_t res12; + uint8_t res13; +// 31 +}; + +struct T_dynamicCondition +{ + char allDoorsDebounced; + char openedAuthorized; + uint8_t CBinDebounced; // 0:fehlt 1:drin + char upperDoor; // 99: undefined 0:closed 1:open + char middleDoor; // 99: undefined 0:closed 1:open + char lowerDoor; // 99: undefined 0:closed 1:open + char reserve; + char billBox; + char modeAbrech; + char onAlarm; // 0:alarm aus 1:alarm 2:alarm mit Sirene 3: Sirenentest + char nowCardTest; + char nowPayment; // not used, always 0 + char lastMifCardType; + uint8_t lastSDoorState; + uint8_t lastVDoorState; + uint8_t lastCBstate; + char paymentInProgress; + // 0: stopped by timeout + // 1: running 2: wait4lastCoin + // 3: payment stopped manually, coins in Escrow + // 4: payment stopped autom, amount collected, coins in Escrow + // 5: payment stopped, escrow full, coins in Escrow + // 6: coins encashed 7:coins returned + // 8: CoinChecker or MDB on Error + + + char res1; + uint16_t U_Batt; + uint16_t Temperatur; + uint16_t nrCoinsInBox; + uint32_t amountInBox; + uint32_t totalTransVolume; + uint32_t totalNrOfVends; + char jsonValid_config; + char jsonValid_device; + char jsonValid_cash; + char jsonValid_print; + char jsonValid_serial; + char jsonValid_time; + char lastFileType; +// 44 + uint8_t MifCardHolder[8]; + uint8_t resultOfLastTemplPrint; + // 0: unknown or printing in progress + // 1: OK, doc was printed 2: error, doc was not printed + uint8_t lastPrinterStatus; + // 0: printer OK + // bit0: near paper end bit1: no paper + // bit2: temperature error bit3: error head open + // bit4: paper jam in cutter + // bit6: no response bit7: serial rec. error + // bit5: printer not ready + uint8_t startupTestIsRunning; + + //54 +}; + +struct T_extTime +{ + uint8_t Hours; + uint8_t Min; + uint8_t Sec; + uint8_t Year; + uint8_t Month; + uint8_t Day; + uint8_t DOW; + uint8_t res1; + uint16_t MinOfDay; + uint16_t res2; + uint32_t SecOfDay; + uint8_t isLeapYear; + uint8_t nxtLeapYear; + uint8_t lastLeapYear; + uint8_t hoursOfThisWeek; + uint16_t minutesOfThisWeek; + uint16_t hoursOfThisMonth; + uint16_t daysOfThisYear; + uint16_t GetHoursOfYear; + uint16_t res3; + uint32_t GetMinutesOfYear; + uint8_t getWakeIntvSec; + uint8_t res4; + uint16_t res5; + uint32_t MinutesOfMillenium; + +}; + + +typedef uint8_t UCHAR; +typedef uint16_t UINT; + +struct T_devices +{ + // set by master, used(1) or notused (0) or type 2....20 + + UCHAR kindOfPrinter; // 0:off 1:Gebe + UCHAR kindOfCoinChecker; // 0: without 1=EMP820 2=EMP900 3=currenza Csquare (MW) + UCHAR kindOfMifareReader; // by now only stronglink SL025 =1 + UCHAR solarPower; // 1:sleep allowed 0: no sleep + //UCHAR suppressSleepMode; // 0:sleep allowed 1: no sleep + + UCHAR kindOfModem; // 0:off 1:Sunlink + UCHAR kindOfCreditcard; // 0:off 1:Feig NFC + UCHAR CoinEscrow; + UCHAR CoinRejectUnit; + + UCHAR CoinShutter; + UCHAR BillAcceptor; + UCHAR usevaultLock; + UCHAR autoAlarm; // 1: switch on siren for 1min in doors opened unauthorized + + UCHAR autoOpen; // 1: open door covers after valid ATBcard + UCHAR printAccReceipt; // 0/1 + UCHAR printDoorReceipt; + UCHAR printTokenTicket; + + UINT VaultFullWarnLevel; + UINT VaultFullErrorLevel; + UINT BattEmptyWarnLevel; + UINT BattEmptyErrorLevel; + +}; + + + +class hwinf +{ + +public: + virtual ~hwinf() {} + + + + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + // Use serial interface and protocol stack in Cashagent-Library + // Sending Output data to DeviceController DC2b + // Sending input requests to DC2 (single or auto-batch) + // Getting input data as receiver payload + // Furthermore the Cashagent-Library answers with status strings about sending and reading result + // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + + + virtual bool dc_openSerial(int BaudNr, QString BaudStr, QString ComName, uint8_t connect) const =0; + // Command: open serial interface + // BaudNr: 0:1200 1:9600 2:19200 3:38400 4:57600 5:115200 + // BaudStr: for exapmle "19200" + // ComName: for example "COM48" + // connect: 0, 1 + + virtual void dc_closeSerial(void) const =0; + // Command: close serial interface in order to save power while power down + // or if another port must be used + + + virtual bool dc_isPortOpen(void) const =0; + // returns true if port open (don't send unless open. Sending to closed port will crash program) + + virtual void dc_autoRequest(bool on) const =0; + // on = true: select that all READ-Requests are sent automatically + // on = false: select that all READ-Requests are sent manually one by one + // Every input information from DC2 must be requested + // ( digital and analog sensors, get time/date, get status information ) + + virtual void dc_requTestResponse() const =0; + // tell DC2 to send a test-string, useful to see if cable and baudrate is OK + + virtual bool dc_readAnswTestResponse() const =0; + // retval: true: test was successful, got right response + + virtual uint8_t dc_isRequestDone(void) const =0; + // retval: 0: request is still in progress + // 1: answer from DC2 was OK + // 2: wrong answer from DC2 + + virtual uint16_t dc_getCompletePayLoad(uint16_t plBufSiz, uint8_t *payLoad) const =0; + // get data back in "payLoad", max 64 byte, can be used for diagnosis + // retval = nr of bytes received. If host buffer too small then + // only plBufSiz bytes are copied to "payLoad" + // plBufSiz = size of host buffer + + virtual void dc_setWakeFrequency(uint8_t period) const =0; + // RTC wakes DC2 (and PTU) by hardware signal every 32seconds + // change wake signal period to 1...64s + + virtual void dc_OrderToReset(void) const =0; + // want DC2 to reset (in order to start Bootloader) + + + virtual QString dc_getSerialState(void) const =0; + // get result of opening-command like "ttyS0 opened with 115200 8N1! + // or error messages like "comport not available..." + // was saved by last opening event, can be passed for 100ms + + virtual void dc_clrSerialStateText(void) const =0; + // clear above text to avoid multiple repetive displaying + + + virtual void bl_sendDataDirectly(uint8_t length, uint8_t *buf) const =0; + // send without protocol frame, needed for the DC bootloader + + virtual uint8_t getRawRecLength(void) const =0; + // only needed if protocol stack in Cashagent-Library is bypassed + + virtual uint8_t getRawReceivedData(uint8_t *receivedData) const =0; + // only needed if protocol stack in Cashagent-Library is bypassed + + virtual QString dc_getSerialParams(void) const =0; + // get DC2 serial settings (not very meaningful as they will not come if different from master settings) + + virtual QString dc_getHWversion(void) const =0; + // get DC2 hardware version + + virtual QString dc_getSWversion(void) const =0; + // get DC2 software version + + virtual QString dc_getState(void) const =0; + // get DC2 status (every OK or any error/warning? ) + + +// ---------------------------------------------------------------------------------------------------------- +// Date and Time +// ---------------------------------------------------------------------------------------------------------- + + virtual uint8_t rtc_getDateTime(struct Trtc_DateTime *rtc_DateTime) const =0; + + virtual uint8_t rtc_setDateTime(void) const =0; + // synch DC2 with PC or PTU system time and date + + virtual void rtc_getTime(uint8_t *hh, uint8_t *mm, uint8_t *ss) const =0; + // get time directly + + virtual void rtc_getDate(uint8_t *yy, uint8_t *mm, uint8_t *dd) const =0; + // get date directly + + virtual uint8_t rtc_getToday(uint8_t *dow, uint16_t *minOfToday, uint32_t *secOfToday) const =0; + // dow=day of week, 1=monday...7 + // minOfToday: 0=midnight...1439= 23:59 + // secOfToday: 0=midnight...86399= 23:59:59 + + virtual bool rtc_isLeapYear(uint8_t *lastLeapYear, uint8_t *NextLeapYear) const =0; + // retval true: this year is leap year + + virtual bool rtc_isLeapYear() const =0; + + virtual void rtc_getWeek(uint8_t *DayOfWeek, uint8_t *HoursOfWeek, uint16_t *MinutesOfWeek) const =0; + // DayOfWeek: 1=monday...7 + // HoursOfWeek: 0=Monday 0:00 o'clock...167=Sunday 23:00 + // MinutesOfWeek: 0=Monday 0:00 o'clock...10079=Sunday 23:59 + + virtual void rtc_getMonth(uint8_t *DayOfMonth, uint16_t *HoursOfMonth, uint16_t *MinutesOfMonth) const =0; + // DayOfMonth: 1...31 + // HoursOfMonth: 0 = 0:00o'clock of 1.day in month up to 743 + // MinutesOfMonth:0 = 0:00o'clock of 1.day in month up to 44639 + + virtual void rtc_getYear(uint16_t *DayOfYear, uint16_t *HoursOfYear, uint32_t *MinutesOfYear) const =0; + // DayOfYear: 1...366 1= 1.Jan of this current year + // HoursOfYear: 0=1.Jan 0:00o'clock ...8783=31.12 23 o'clock + // MinutesOfYear: 0=1.Jan 0:00o'clock ...527039=31.12 23:59 o'clock + + virtual QString rtc_getTimStr(void) const =0; + virtual QString rtc_getDatStr(void) const =0; + virtual QString rtc_getTimDatStr(void) const =0; + + + // UID - unique number, different in every DC + virtual void dc_getUID8byte(uint8_t *buf8byteUid) const =0; + // get 8 single bytes in buffer + + virtual QString dc_getUIDstr() const =0; + // get as string + + virtual uint64_t dc_getUIDnumber(void) const =0; + // get UID as one long number + +// ---------------------------------------------------------------------------------------------------------- +// analogs +// ---------------------------------------------------------------------------------------------------------- + + // Analog values: + virtual uint32_t dc_getTemperature(void) const =0; + // in Sax-Format 0...400 (0=-50,0degC 100=0,0degC 141=20,5degC 400=150,0degC) + + virtual QString dc_getTemperaturStr(void) const =0; + // as string like "-12,5degC" + + virtual uint32_t dc_getVoltage(void) const =0; + // as value in mV, 0...65,535V + + virtual QString dc_getVoltagStr(void) const =0; + // as string in mV + + virtual bool dc_mainFuseIsOk(void) const=0; + // true if 12V fuse is OK + // false: fuse blown, DC will continue working but no 12V device can be used! + +// ------------------------------------------------------------------------------ +// digital outputs +// ------------------------------------------------------------------------------ + + virtual uint8_t lock_switchUpperLock(uint8_t dir) const =0; + // dir 0=off 1=up 2=down + // move lock until stop cmd (0) + + virtual uint8_t lock_switchLowerLock(uint8_t dir) const =0; + // dir 0=off 1=up 2=down + // move lock until stop cmd (0) + + virtual void lock_switchVaultDoor(void) const =0; + + virtual void coin_switchRejectMotor(uint8_t dir) const =0; + + virtual void coin_rejectCoins(void) const =0; + + // LEDs + virtual void led_switchLedService(uint8_t on) const =0; + virtual void led_switchLedPaper(uint8_t on, uint8_t ton, uint8_t tof) const =0; + virtual void led_switchLedPinPad(uint8_t on, uint8_t ton, uint8_t tof) const =0; + virtual void led_switchLedStart(uint8_t on, uint8_t ton, uint8_t tof) const =0; + virtual void led_switchLedCoinbassin(uint8_t on, uint8_t ton, uint8_t tof) const =0; + + virtual void fan_switchFan(bool on) const =0; + virtual void alarm_switchSiren(bool on) const =0; + virtual void bar_OpenBarrier(bool open) const =0; + virtual void ptu_switchWake(bool WAKEACTIVE) const =0; + + virtual void aux_power(bool on) const =0; + virtual void aux_setUsage(uint8_t PinDirection) const =0; + virtual void aux_setOutputs(uint8_t PinIsHigh) const =0; + + virtual void lock_switchContactPower(bool on) const =0; + + virtual void prn_switchPower(bool on) const =0; + + virtual void mif_readerOn(bool on) const =0; + + virtual void mif_creatAtbCard(uint8_t cardType) const =0; + + virtual void mod_switchPower(bool on) const=0; + + virtual void mod_switchWake(bool WAKEACTIVE) const=0; + + virtual void mdb_switchPower(bool on) const =0; + + virtual void mdb_switchWake(bool WAKEACTIVE) const =0; + + virtual void credit_switchPower(bool on) const =0; + // the same as modem power + + virtual void credit_switchWake(bool WAKEACTIVE) const =0; + + virtual void shut_move(bool open) const =0; + + virtual void esc_moveFlaps(uint8_t flap ) const =0; + // 0: close both 1: open take-flap 2: open return + + +// ------------------------------------------------------------------------------ +// door, cashbox and lock switches +// ------------------------------------------------------------------------------ + + virtual uint8_t door_getSwitches(void) const =0; + // retval: bit0: upper door 1: low door 2:vault door + + virtual bool door_isUpperDoorOpen(void) const =0; + + virtual bool door_isLowerDoorOpen(void) const =0; + + virtual bool vault_isVaultDoorOpen(void) const =0; + + virtual uint8_t vault_getSwitches(void) const =0; + // retval bit0: cash box, bit 1: bill box + + virtual bool vault_isCoinVaultIn(void) const =0; + + virtual bool vault_isBillVaultIn(void) const =0; + + virtual uint8_t door_getLocks(void) const =0; + // retval bit0: upper lever is up (=open) + // bit1: upper lever is down (=locked) + // bit2: lower lever is up + // bit3: lower lever is down (=locked) + + virtual bool door_upperDoorIsLocked(void) const =0; + + virtual bool door_upperDoorIsUnlocked(void) const =0; + + virtual bool door_lowerDoorIsLocked(void) const =0; + + virtual bool door_lowerDoorIsUnlocked(void) const =0; + + virtual bool bar_optoIn1isOn(void) const =0; + + virtual bool bar_optoIn2isOn(void) const =0; + + virtual uint8_t aux_getAuxInputs(void) const =0; + + virtual bool ptu_WakeINisActive(void) const =0; + + virtual bool mdb_WakeINisActive(void) const =0; + + virtual bool prn_readyINisActive(void) const =0; + + virtual bool coid_isAttached(void) const =0; + + virtual bool coin_escrowIsOpen(void) const =0; + + virtual bool mif_cardIsAttached(void) const =0; + +//bool hwapi::mod_WakeINisActive(void); + + virtual bool door_isContactPowerOn(void) const =0; + + virtual bool mif_isMifarePowerOn(void) const =0; + + virtual bool mdb_testIsmdbTxDon(void) const =0; + + virtual bool aux_isAuxPowerOn(void) const =0; + + virtual bool mod_isGsmPowerOn(void) const =0; + + virtual bool cred_isCreditPowerOn(void) const =0; + + virtual bool prn_isPrinterPowerOn(void) const =0; + + virtual uint8_t prn_PrnFuseIsOk(void) const=0; + //retval: 0: fuse blown 1: fuse OK 2:unknown as printer power is off + + virtual bool mdb_isMdbPowerOn(void) const =0; + + virtual bool cash_getRejectMotorHomePos(void) const=0; + + virtual uint8_t cash_getLowPaperSensor(void) const=0; + // 0: Sensor sees paper 1: no paper 99: off + + + // ------------------------------------------------------------------------------ + // request commands + // ------------------------------------------------------------------------------ + + // all read-requests can be sent manually by the following functions + // or automatically in background by: void hwapi::dc_autoRequest(bool on) + + + virtual void request_DC2serialConfig() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + + virtual void request_DC2_HWversion() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_SWversion() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_condition() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_UID() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_TimeAndDate() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_analogues() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_digitalInputs() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + virtual void request_DC2_digitalOutputs() const =0; + // read-request can be sent manually by this function, not needed if auto-request is on + + // ------------------------------------------------------------------------------ + + // the following device state requests are deployed only if device is powered up: + virtual void request_PrinterHwState() const =0; + virtual void request_PrinterCurrentFonts() const =0; + virtual void request_PrinterStateComplete() const =0; + + virtual void request_MifareReaderState() const =0; + virtual void request_MifareCardType() const =0; + virtual void request_MifareAtbType() const =0; + virtual void request_MifareID() const =0; + virtual void request_MifareData(uint8_t dataBlockNumber) const =0; + //virtual void request_MifareData() const =0; + + virtual void request_MDB_Status() const =0; + virtual void request_MDB_lastResponse() const =0; + virtual void request_EMP_allParameters() const =0; + virtual void request_EMP_lastCoin() const =0; + + + // ------------------------------------------------------------------------------ + // readback digital outputs of connected devices + // these functions are not needed for normal operation + // but can be used to test and verify conditions + // ------------------------------------------------------------------------------ + + + virtual bool test_getDO_mdbRXtst(void) const =0; + // readback digital outputs of connected devices + // these functions are not needed for normal operation + // but can be used to test and verify conditions + + virtual uint8_t lock_getDO_motors(void) const =0; + // bit0: upper lock forward bit 1 backward + // bit2: lower lock forward bit 3 backward + + virtual uint8_t test_serialState(void) const =0; + // test on-board signals for the serials + // serial drv on/off, Serial mux1, Serial mux2 + + virtual bool test_serialIsOn(void) const =0; + + virtual bool test_serialMux1isSetToPrinter(void) const =0; + virtual bool test_serialMux1isSetToModem(void) const =0; + virtual bool test_serialMux2isSetToCredit(void) const =0; + virtual bool test_serialMux2isSetToMifare(void) const =0; + + virtual bool led_coinIsOn(void) const =0; + virtual bool led_frontIsOn(void) const =0; + virtual bool led_ticketIsOn(void) const =0; + virtual bool led_pinIsOn(void) const =0; + virtual bool led_StartIsOn(void) const =0; + virtual bool led_insideIsOn(void) const =0; + + virtual bool fan_isOn(void) const =0; + virtual bool siren_isOn(void) const =0; + virtual bool bar_relayIsOn(void) const =0; + virtual bool ptu_WakeOutIsOn(void) const =0; + + virtual bool aux_powerIsOn(void) const =0; + + virtual bool coin_shutterIsOpen(void) const =0; + virtual bool coin_shutterTestOutput(void) const =0; + + virtual uint8_t coin_escrowFlapOpened(void) const =0; + // retval: 1:return flap is open 2:take flap is open 0:closed + + + +// ------------------------------------------------------------------------------ +// devices are operated by DC +// processes with more then one devices +// timer controlled or long term processes +// ------------------------------------------------------------------------------ + + + virtual void sendDeviceSettings(uint8_t kindOfPrinter, uint8_t kindOfCoinChecker, + uint8_t kindOfMifareReader, uint8_t suppressSleep, + uint8_t kindOfModem, uint8_t kindOfCredit ) const =0; + // enable hardware in device controller: + // kindOfPrinter: 0:off 1: GPT4672 (only this one implemented) + // kindOfCoinChecker: 0:off 1:EMP820 2:EMP900 3: Csquare_changer + // kindOfMifareReader: 0:off 1: SL025 (only this one implemented) + // suppressSleep: 0:sleep allowed 1: sleep surpressed for special reason + // kindOfModem: 0:off 1: ATB_Sunlink_LTE (not yet implemented) + // kindOfCredit: 0:off 1: cVendTopp 2:cVendPin (not yet implemented) + + virtual void request_ReadbackDeviceSettings() const =0; + + virtual void readback_DeviceSettings(uint8_t *length, uint8_t *data) const =0; + // refer to DC2 manual for exact content + // state 5.5.21: byte[0]=kindOfPrinter byte[1]=kindOfCoinChecker + // byte[2]=kindOfMifarereadr byte[3]=suppress sleep mode + // byte[4]=kindOfModem byte[5]=kind of cc terminal + + virtual void sendMachineID(uint16_t customerNr, uint16_t machineNr, + uint16_t borough, uint16_t zone, + uint16_t alias, char *location) const =0; + + virtual void request_ReadbackMachineID() const =0; + + virtual void readback_machineIDdata(uint8_t *length, uint8_t *data) const =0; + // state 5.5.21: byte[0,1]=customer number byte[2,3]=machine number + // byte[4,5]=borough byte[6,7]=zone byte[8,9]=alias name + // byte[10...41]=location + + // Locks stops automatically at end switch or by timeout + virtual uint8_t lock_openUpperDoor(void) const =0; + virtual uint8_t lock_closeUpperDoor(void) const =0; + + virtual uint8_t lock_openLowerDoor(void) const =0; + virtual uint8_t lock_closeLowerDoor(void) const =0; + + virtual void shut_openOnce(void) const =0; + // and close automatic after shutter time + + virtual void shut_openForCoin(bool start) const =0; + // open flap if coin is attached + // once process is started it runs until stop command + + virtual void shut_sendOpeningTime(uint16_t timeIn_ms ) const =0; + // after this time without retrigger the flap is closed + + virtual void esc_takeMoney(void) const =0; + // and close automatically after escrow time (1s) + + virtual void esc_returnMoney(void) const =0; + // and close automatically after escrow time (1s) + + +// ---------------------------------------------------------------------------------------------------------- +// --------------------------------------------- MIFARE ----------------------------------------------------- +// ---------------------------------------------------------------------------------------------------------- + +// obsolete + virtual uint8_t mif_returnReaderStateAndCardType(uint8_t *buf, uint8_t maxBufferSize) const =0; + // retval 0=OK 1=error host buffer too small + /* data description, new fast version: + byte 0= still the same: current read state: + 0=power off 1=reader-fault 2=ready + 3=just reading 4=read complete + 5=read partial, removed too early + 6=state unknown + + byte 1: reader state 1=ok 0=nok + byte 2: card preent (0,1) + byte 3: card selected (0) + byte 4: card type: 0...5 + byte 5: card allowed (0=no 1=MifareClassic 1k or 4k) + byte 6: CardSize: 1 or 4 (kB) + byte 7: length of UID 4 or 7 (byte) + */ + + virtual bool mif_readerIsOK(void) const =0; + + virtual bool mif_cardAttached(void) const =0; + // not working! use mif_cardIsAttached() instead + + virtual uint8_t mif_readResult(void) const =0; + // result: 0: unknown or still in progress + // 1: card read successful + // 2: reading error + // not working! + + virtual QString mif_cardUID(void) const =0; + // not working + + virtual uint8_t mif_getCardDataDec(uint8_t blkNr, uint8_t *buf, uint8_t maxBufferSize) const =0; + + virtual QString mif_getCardDataStr(uint8_t blockNumber) const =0; + // with blockNumber=0...11 + + + + // ---------------------------------------------------------------------------------------------------------- + // --------------------------------------------- PRINTER ---------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------- + + // read printer condition and settings + + virtual uint8_t prn_getHwState(struct Tprn_hw_state *prn_hw_state) const =0; + // retval: status byte + // byte 0 = 0: prnter OK, >0: error + // bit0: paper low 1: no paper 2: temperature error + // 3: head open 4: paper jam in cutter + // 6: no response 7: bad response from printer + // and return struct "Tprn_hw_state" + + virtual bool prn_isUpAndReady(void) const =0; + // true: printer is powered, serial is ok, no error, printer is connected and resonding + + virtual void prn_getCurrentFontSetting(struct Tprn_currentSettings *prn_fonts) const =0; + + + // send Commands to printer: + + virtual void prn_sendText(QByteArray *buf) const =0; + // up to 1280 bytes + + virtual void prn_sendPrnSysCmd(uint8_t para1, uint8_t para2, uint32_t para3) const =0; + // send three byte through to printer, see printers manual + + virtual void prn_sendPrnEscCmd(uint8_t para1, uint8_t para2, uint8_t para3, uint8_t para4) const =0; + // send four byte through to printer, see printers manual + + + virtual void prn_sendPrnSetup(uint16_t paperSpeed, uint8_t density, uint8_t alignment, uint8_t orientation) const =0; + // send 5 byte: byte 0,1: speed 5...250 mm/s + // byte2: density 0....(25)....50 + // byte3: alignment 'l', 'c', 'r' = left, center, right + // byte4: orientation 0, 90, 180 = 0deg, 90deg, 180deg rotation (by now not supported!) + // not batched! don't use twice within 100ms + + virtual void prn_movePaper(uint8_t wayInMm, uint8_t direction) const =0; + //direction: 1=forward 2=backward + // + virtual void prn_setFonts(uint8_t font, uint8_t size, uint8_t width, uint8_t height) const =0; + // font = kind of font 5...11 (0..22) + // size = 6...20, 9..9: too tiny 10: small ...12 = normal size ...20=huge + // width: 0...4 0=1x 1=2x 2=4x (huge!) 3=8x 4=16x (3,4 make no sense) + // heigth: 0...7 = 1x...8x only 0,1,2,(3) make sense + + virtual void prn_setLetters(uint8_t bold, uint8_t invers, uint8_t underlined) const =0; + // bold: 0/1 + // invers: 0/1 + // underlined: 0/1 + + virtual void prn_cut(uint8_t kindof) const =0; + // kindof: 1=full cut 2=partial cut 3=eject (5xLF + full cut) + + virtual void prn_newLine(uint8_t nrOfLines) const =0; + + virtual void prn_printCompleteFontTable(void) const =0; + + + virtual void prn_printBarcode(uint8_t kindOf, uint8_t withText, uint8_t offset, uint8_t rotation, uint8_t dataLeng, uint8_t *data) const =0; + // kind of barcode: 0=Code39 1=Code128 2=EAN13 3= 2/5interleaved 4=UPC-A 5=EAN8 + // withText: print readable text below + // offset: move by pixel from left border + // rotation + // dataLeng in byte + + virtual void prn_sendQRdata(QByteArray *buf) const =0; + // maximal 150 alphanummeric bytes + + virtual void prn_printQRcode(void) const =0; + // QRcode may have 1...150 alphanummeric data, must be transfered in advance + + + virtual void prn_printLogo(uint8_t nrOfLogo, uint8_t offset ) const =0; + // nrOfLogo: 1..4 in flash 5...8 in Ram + // offset: in mm form left border + + + // ......................................................... + // Parking Ticket (print-out document) designer TD - obsolete + // ......................................................... + + + + // ---------------------------------------------------------------------------------------------------------- + // --------------------------------------------- MDB Bus ---------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------- + + virtual void mdb_sendBusReset(void) const =0; + + virtual void mdb_sendCommand(uint8_t toMdbDevice, uint8_t mdbCommand) const =0; + // send one bus command directly over mdb bus, refer to mdb manual for commands + // this command is not needed in normal operation, just for new or special things + + virtual void mdb_sendMessage(uint8_t toMdbDevice, uint8_t mdbCommand, uint8_t nrOfData, uint8_t *dataBuffer) const =0; + // nrOfData = sizeOf(dataBuffer) maximal 34 byte according mdb specs + // same as mdb_sendCommand, just with data + + virtual bool mdb_busIsReadyToWork() const =0; + + virtual bool mdb_deviceVoltageOK() const =0; + + virtual bool mdb_busVoltageOk() const =0; + + virtual uint8_t mdb_getLastDeviceResponse(uint8_t *fromDevice, uint8_t *lastRequest, + uint8_t *responseLength, uint8_t *responseBuffer) const =0; + // fromDevice: device nr from which data was requested 0,1,2,3 + // lastRequest: sent mdb command + // responseLength: nr of payload data (after mdb-ack) 0...34 + // responseBuffer holds payload data (answer from mdb device) + // return val: mdb result of this request: 1=got ACK 2=got 3xNAK 3=no or bad response 4:got Data (after ACK) + + + // ---------------------------------------------------------------------------------------------------------- + // ---------------------------------- Electronic Coin Validator EMP ----------------------------------------- + // ---------------------------------------------------------------------------------------------------------- + + + virtual void emp_sendSettings(uint16_t coinAcceptance, uint8_t tokenChannel, uint16_t *coinDenomination ) const =0; + // coinAcceptance: bit0=coin1 (lowest donomination) bit15=coin16 bitH=accept bit L = deny coin (no validation) + // tokenChannel 0...31: if this signal comes from emp then a token was inserted + // coinDenomination = array of 16 coin values (e.g. 5, 10, 20...) + + virtual void emp_pollingOnOff(uint8_t on) const =0; + + virtual void emp_startCoinAcceptance(void) const =0; + + virtual void emp_stopCoinAcceptance(void) const =0; + + virtual void emp_getAllParameters(struct T_emp *emp) const =0; + // see struct in hwapi.h + // usage example: + // hwapi *HWaccess const override; + // HWaccess = new hwapi() const override; + // struct T_emp myEmp const override; + // HWaccess->emp_getAllParameters(&myEmp) const override; + // readval=myEmp.pollingRunning const override; + + virtual uint8_t emp_chkIfCoinInserted(void) const =0; + // retval: 0...16 coins left in FIFO + + virtual void emp_getNewCoinRecord(uint8_t *valid, uint8_t *signal, uint8_t *error, uint16_t *value) const =0; + // with every call ONE coin is taken out of FIFO and pointer decremented + // valid: should be 1 + // signal: comes right from coin checker, 0...15 (0=first programmed coin type) 0xFF=no signal + // error: was reported from EMP as dynamic signal right after coin insertion (instead of + // coin signal), example: 3=unknown coin 4=coin is blocked by host. 0xFF=no error + // value: of the coin. Depends on parameter "coinDenomination" in function "emp_sendSettings" + // if coinDenomination[coin 0..15] = 0 then the value programmed in coin checker is taken + // if coinDenomination > 0 then this value is taken. + // Useful in case of two currencies (adapt to local currency) or for token. + + // function gives more details as "emp getLastCoin()" but "emp getLastCoin()" is easier to use + + // alternativ to emp_getNewCoinRecord( ): + virtual uint8_t emp_giveLastCoin(uint16_t *value, uint8_t *signal) const =0; + // retval: 0: NO coin stored 1: valid coin 2: got wrong coin or coin denied + // value: if retval1: value of the coin if reval=2: error number + // 0xFF means NO error or NO signal (as 0 is a valid error/signal) + // signal: channel nr reported from checker 0...15 + + + virtual uint8_t emp_returnLastCoin(uint16_t *value, uint8_t *signal) const =0; + // use for changer + + + + virtual QString dc_getTxt4RsDiagWin(void) const =0; + virtual void dc_clrTxt4RsDiagWin(void) const =0; + virtual QString dc_get2ndTxt4RsDiagWin(void) const =0; + virtual void dc_clr2ndTxt4RsDiagWin(void) const =0; + virtual QString dc_getTxt4HsStateLine(void) const =0; + virtual void dc_clrTxt4HsStateLine(void) const =0; + virtual QString dc_getTxt4masterStateLine(void) const =0; + virtual void dc_clrTxt4masterStateLine(void) const =0; + virtual QString dc_getTxt4resultStateLine(void) const =0; + virtual void dc_clrTxt4resultStateLine(void) const =0; + virtual QString dc_getdataStateLine(void) const =0; + virtual void dc_clrTxt4dataStateLine(void) const =0; + virtual QString dc_getdatifLine(void) const =0; + virtual void dc_clrTxt4datifLine(void) const =0; + + + +// ---------------------------------------------------------------------------------------------------------- +// -------- DC Bootloader 1.Version +// ---------------------------------------------------------------------------------------------------------- + + // using DC2 Bootloader + virtual void bl_iniChain(void) const =0; + virtual bool bl_importBinFile(QByteArray readBinFile, uint32_t fileSize, char withDispl) const =0; + virtual uint8_t bl_activatBootloader(uint8_t *sendData) const =0; + virtual uint8_t bl_startChain(void) const =0; + virtual uint8_t bl_readBLversion(uint8_t *sendData) const =0; + // minimum size of sendData-buffer: 5byte retval: length + virtual uint8_t bl_readFWversion(uint8_t *sendData) const =0; + // minimum size of sendData-buffer: 5byte retval: length + + virtual uint8_t bl_prepareDC_BLcmd(uint8_t Cmd, uint8_t SendDataLength, uint8_t *sendData, uint8_t *outBuf) const =0; + // make BL protocol, retval = outbuf length (5...133) + // bring data in correct form: start always with 0x02 finish with 0x03 and append checksum + // 0x02 Cmd < ...sendData ..> CRC CRC 0x03 + // Data length = 0...64 + // special conversion: if data contain 2 or 3 (STX, ETX) then write two bytes: 0x1B (=ESC) and data|0x80 + // so maxlength = 5 + 2 x 64 (if all data are 2 or 3) without 2,3: maxlength = 5 + 64 + + virtual uint8_t bl_exitBL(uint8_t *sendData) const =0; + // minimum size of sendData-buffer: 5byte retval: length + + virtual void led_switchLedIllumination(uint8_t on) const =0; + + + + +// ------------------------------------------------------------------------------------ +// 27.3.2023: Use Device-Controller's Bootloader to send hex-file +// ------------------------------------------------------------------------------------ + + virtual void bl_rebootDC(void) const =0; + + virtual void bl_startBL(void) const =0; + virtual void bl_checkBL(void) const =0; + virtual bool bl_isUp(void) const =0; + // return true is bl is up and running + // also initializes "sendFile" + + virtual void bl_sendAddress(uint16_t blockNumber) const =0; + // send start address, nr of 64byte-block, start with 0 + // will be sent only for folling block-numbers: + // 0, 1024, 2048, 3072 and 4096, so basically every 64kByte + + virtual uint8_t bl_wasSendingAddOK(void) const =0; + // return val: 0: no response by now 1:error 10: OK + + virtual void bl_openBinary(void) const =0; + + virtual void bl_sendDataBlock(uint8_t length, uint8_t *buffer) const =0; + // send 64 byte from bin file + + virtual void bl_sendLastBlock(void) const =0; + + virtual uint8_t bl_wasSendingDataOK(void) const =0; + // return val: 0: no response by now 1:error 10: OK + + virtual void bl_stopBL(void) const =0; + + +// ------------------------------------------------------------------------------------ +// 6.4.2023: new functions for coin collection and printing +// some new system functions +// ------------------------------------------------------------------------------------ + + + virtual bool rtc_setTimeDateDirect(struct Trtc_DateTime *DateTime) const =0; + // return true if successful. could fail if more the 8 commands are waiting + + virtual bool rtc_getExtendedTime(uint8_t *leng, uint8_t *data) const =0; + + virtual bool rtc_getExtendedTime(struct T_extTime *exTime) const =0; + + virtual bool sys_runCompleteTest(void) const =0; + // warning: lasts 20s in one pace + + virtual bool sys_ready4sending(void) const =0; + // return true if a Json-file can be sent + + virtual bool sys_sendJsonFileToDc(uint8_t kindOfFile, uint8_t nrOfTemplate, uint8_t *content ) const =0; + // kindOfFile: 1=config, 2=device, 3=cash, 4=serial, 5=time, 6=printer + // nrOfTemplate=1...32 if kindOfFile==6 + // content = content of the Json file, max 800byte ascii signs + // file is 0-terminated! + // return false if sending is not possible, wait a second + + virtual bool prn_sendDynamicPrnValues(uint8_t *dynPrnVal ) const =0; + // dynPrnVal = array of 8 Variables with 8 byte each, come as ascii string + // like: char prn_dynPrnVal[8][8]; + // return true if sending, false if cmd-stack is full + + virtual bool prn_printTemplate(uint8_t nrOftemplate) const =0; + // print one of the templates loaded by Json prior + // nr = 1..32 + // return true if sending, false if cmd-stack is full + + virtual void log_getHoldAccountNumbers(uint8_t *nrOfVals, uint16_t *accNr ) const =0; + // returns all acc nrs of the backuped vault records + // use: uint16_t backupedAccNumbers[8] + + virtual bool log_selectVaultRecord(uint16_t accountNr ) const =0; + // return true if sending, false if cmd-stack is full + // and trigger transfer + + virtual bool log_chkIfVaultRecordAvailable(void) const =0; + // return true if completly received + + virtual bool log_getVaultRecord(struct T_vaultRecord *retVR) const =0; + // which was selected by: log_selectVaultRecord() + // to be forwarded to Ismas + + virtual bool prn_printAccountReceipt(void) const =0; + // print all 8 backuped accounting receipts + // return true if sending to DC OK, false if cmd-stack is full + + virtual bool prn_printTestTicket(void) const =0; + // return true if sending to DC OK, false if cmd-stack is full + + + virtual bool cash_startPayment(uint32_t amount) const =0; + // 17.4.23TS: extended to 32bit + + virtual uint8_t cash_paymentProcessing(void) const =0; + // run this function periodically while coin payment process to generate necessary signals + // return value: + // 0: stopped 1: starting up 2: coin collection + // 3: finished by User (Push button) 4: finished, Max-Value collected + // 5: finished by escrow + // 10,11: error cannot start + // 12: timeout while payment, coins returned + // 13: stopped by unexpected error + + virtual uint32_t getInsertedAmount(void) const =0; + + virtual uint16_t getLastInsertedCoin(void) const =0; + + virtual bool getAllInsertedCoins(uint16_t *types, uint16_t *values) const =0; + // alle bei diesem Verkauf eingeworfenen Muenzen sind gespeichert, max 64 + + + virtual bool cash_cancelPayment(void) const =0; + // and return coins + + virtual bool cash_stopPayment(void) const =0; + // and keep coins in escrow + + + // after ticket/goods issue: + virtual bool vend_success(void) const =0; + // conclude payment process, encash all inserted coins to vault. Printing was successful + // if possible return change + + virtual bool vend_failed(void) const =0; + // conclude payment process and return all inserted coins + + + + + + virtual uint8_t mif_getCardType(QString *cardholder) const =0; + // return 1,2,3,4 = upper, lower access card, printer test, coin test + // cardholder: 7byte Name-String + + virtual uint64_t sys_getWakeSource(void) const =0; + // retval: 6 bytes, bit coded, 1=event keeps DC awake + + virtual uint8_t sys_getWakeReason(void) const =0; + // Master was woken by following reason: + // 1: MDB Event + // 2: Coin Event + // ( 3: Master Event) - will not set the wake line + // ( 4: 32s pulse) - will not set the wake line + // 5: Door Event + // ( 6: Diag Event) - will not set the wake line + // 7: 30min-Pulse for HB + + virtual void sys_getDeviceConditions(uint8_t *leng, uint8_t *data) const =0; + + virtual void sys_getDeviceConditions(struct T_moduleCondition *devCond) const =0; + + virtual void sys_getDynMachineConditions(uint8_t *leng, uint8_t *data) const =0; + + virtual void sys_getDynMachineConditions(struct T_dynamicCondition *dynMachCond) const =0; + + + virtual uint32_t cash_getAmountInVault(void) const =0; + + virtual uint16_t cash_getNrCoinsInVault(void) const =0; + + virtual uint8_t prn_getPrintResult() const =0; + + + + virtual uint8_t prn_getCurrentPrinterState() const =0; + // 0: printer OK + // bit0: near paper end bit1: no paper + // bit2: temperature error bit3: error head open + // bit4: paper jam in cutter + // bit6: no response bit7: serial rec. error + // bit5: printer not ready + + + virtual void sys_sendDeviceParameter(struct T_devices *deviceSettings) const =0; + + virtual void sys_restoreDeviceParameter(struct T_devices *deviceSettings) const =0; + // attention: only applies if function "sys_sendDeviceParameter()" was used to send this settings before + // cannot be used to see settings programmed by JsonFile + + virtual bool sys_areDCdataValid(void) const =0; + + virtual bool sys_sendingTest(void) const =0; + + virtual void prn_requestCurrentDynData(void) const =0; + + virtual bool prn_getCurrentDynamicPrnValuesFromDC(uint8_t *dynPrnVal ) const =0; + // which was sent with: bool prn_sendDynamicPrnValues(uint8_t *dynPrnVal ) const =0; + + virtual bool prn_dynDataAreEqual(uint8_t *buf) const =0; + + virtual bool prn_printKombiticket(uint8_t nrOfKombi) const =0; + // print four of the templates loaded by Json prior + // nr = 1..8 + + virtual void lock_triggerUpperSolenoid(void) const =0; + + virtual void lock_triggerLowerSolenoid(void) const =0; + + virtual bool doors_supervise(void) const =0; + + virtual bool dc_isAutoRequestOn(void) const =0; + + virtual uint16_t log_getLatestAccountNumber(void) const=0; + // new function 27.6.2023 + // latest = highest of the backup's + + virtual uint8_t log_getAvailableVaultBlocks(void) const=0; + // return 0x0011 1111 if all 6 blocks are loaded (one bit per block) + + virtual uint8_t log_getAnswerToLastSlaveRequest(void) const =0; + // use only for ONE request/command + // return: 0xFF: result unknown by now as sending is ongoing + // 0=OK + // 1= wrong length 2=wrong start sign 5= wrong crc + // 6= slave: master cmd was wrong 7: slave: could not write/read data + // 8=timeout, got no response from slave + // 0,8 work, 1..6 not yet tested. 8 comes immed. and stays 8 until reconnect + + // use for important and extended commands (print several templates, print ticket...) + virtual void log_startSupervision(void) const =0; + + virtual uint8_t log_getAnswerToLastCmdBatch(void) const =0; + // 0xFF: no command sent by now + // 0: started, in progress + // 1: done and OK + // 2: done and error + // not working properly, always 0 + + virtual bool log_getVaultData(uint8_t *data) const =0; + // get vault record in linear 8bit buffer with 384 byte + + + + // new from 1.8.23 + virtual bool prn_printOneAccountReceipt(uint16_t accountNr) const =0; + // print one out of eight stored last accounting receipts + // function log_getHoldAccountNumbers() gives a list of acc-Nr. of the stored receipts + + virtual bool prn_printAllAvailAccountReceipts(void) const =0; + // same as: prn_printAccountReceipt() from line 1153 + // return true if sending to DC OK, false if cmd-stack is full + + virtual bool log_verifyVaultRecordByCrc(void) const =0; + // return true if CRC16 is correct, data are 100% OK. Security level 1:65536 + // verification is strongly recommended before further processing + // in case of "false"-result please reload from DC + + + virtual uint16_t log_DC_getNextAccountNumber(void) const=0; + // the current cash box content will be backuped with this number on next cashbox-change + + virtual void log_DC_setNextAccountNumber(uint16_t newAccountingNumber) const=0; + // the current cash box content will be backuped with this number on next cashbox-change + // use only in case of hardware replacements or errors which derailed the number + + virtual void log_DC_deleteAllVaultrecordsInDc(void) const=0; + // use only in case of hardware replacements or errors which derailed the number + + virtual void log_DC_deleteAllTotalCounters(void) const=0; + // use only in case of hardware replacements or errors which derailed the number + + virtual void dc_setNewCustomerNumber(uint16_t newCustNr) const =0; + + virtual void dc_setNewMachineNumber(uint16_t newMachNr) const =0; + + virtual void dc_setNewBorough(uint16_t newBorough) const =0; + + virtual void dc_setNewZone(uint16_t newZone) const =0; + + + + // new functions from 8.9.23 + virtual QString mif_getReaderType(void) const =0; + // return "SL025" if correct reader is connected + + virtual void mif_getCardSize(uint8_t *cardSize, uint8_t *idLeng) const =0; + // cardSize=1k or 4kByte + // idLeng =4Byte or 7 byte + + virtual char mif_getAtbCardData(uint8_t *buf, uint8_t maxBuffSiz) const =0; + // return complete buffer binary, just for test purpose + + virtual bool mif_isValidAtbCard(void) const =0; + + virtual uint32_t mif_getAtbCardCuNu(void) const =0; + + virtual uint8_t mif_getAtbCardTyp(void) const =0; + // return 1=upper door card 1=lower door 3=printer-test 4=coin-test + // 0: not a valid atb2020 card + + virtual QString mif_getAtbCardPerso(void) const =0; + // e.g. "PNsax001" used for personal number, name shortcode, card number + // free to use, can be set in AtbMcw23.exe tool + + virtual void mif_getAtbCardExpire(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *hour, uint8_t *minute) const =0; + + + + + + + +signals: + virtual void hwapi_templatePrintFinished_OK(void) const=0; + virtual void hwapi_templatePrintFinished_Err(void) const=0; + + virtual void hwapi_coinCollectionJustStarted(void) const=0; + virtual void hwapi_coinCollectionAborted(void) const=0; + + virtual void hwapi_gotNewCoin(void) const=0; + virtual void hwapi_payStopByMax(void) const=0; + virtual void hwapi_payStopByPushbutton(void) const=0; + + virtual void hwapi_payStopByEscrow(void) const=0; + virtual void hwapi_payStopByError(void) const=0; + virtual void hwapi_payStopByTimeout(void) const=0; + virtual void hwapi_payCancelled(void) const=0; + virtual void hwapi_coinProcessJustStopped(void) const=0; + + + // new from 2023.06.12 + virtual void hwapi_doorServiceDoorOpened(void) const=0; + virtual void hwapi_doorVaultDoorOpened(void) const=0; + virtual void hwapi_doorCoinBoxRemoved(void) const=0; + virtual void hwapi_doorCoinBoxInserted(void) const=0; + virtual void hwapi_doorCBinAndAllDoorsClosed(void) const=0; + virtual void hwapi_doorAllDoorsClosed(void) const=0; + + + +}; + + +// History +// 11.10.2021: V1.0 222 functions +// 23.12.2021: V1.1 added block-parameter to function "read mifare data" +// 30.12.2021: V1.2 added function: mif_clearDataBuffer(), mif_isBlockAvailable(uint8_t blkNr) and mif_getAvailableDataBlocks() +// 1.1.2022: V1.3 Mifare extended. ( background: read 16 x 48byte from card to DC, read 12 x 64byte from DC to CA) +// new: read full card with 768bytes from HWapi without block borders +// added: mif_getNrOfAvailableDataBytes mif_getCardData768byteDec(uint8_t *buf, uint16_t bufferSize) +// mif_getCardDataDec(uint16_t fromAddr, uint16_t toAddr, uint8_t *buf, uint16_t bufferSize) +// mif_getCardDataStr(bool useHexFormat, char seperator) + +// 29.03.2023: V3.1 some extensions for PSA1256_ptu5, +// V3.2 Bootloader improvement +// 12.04.2023: V3.3 new features extended: loading and using Json-files, cash-collection, cash-data-logging +// 14.04.2023: V3.4 new features extended: sys_getDynMachineConditions, sys_getDeviceConditions and +// rtc_getExtendedTime return struct in addition. New function to select and get VaultRecord +// +// 19.04.2023: V3.5 new function: sys_getWakeReason(); +// 17.05.2023: V3.6 new function: cash_isCollectionActive(), cash_isPayProcessActive() +// new signals: hwapi_coinCollectionJustStopped, hwapi_coinCollectionJustStarted +// getAllInsertedCoins() fixed, also in datif and storeINdata + +// 15.06.2023 V4.2 bring into same order as hwapi in order to set the THIS_IS_CA_MASTER correct +// 19.06.2023 V4.3 added some qCriticals to see emits + +// 01.08.2023 V4.4 some new values at the end of struct T_vaultRecord +// two more values in struct T_devices +// 7 new functions at the end of the file + + +//#define HWINF_iid "Atb.Psa2020.software.HWapi/3.1" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.1" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.3" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.4" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.5" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/3.6" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.0" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.1" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.2" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.3" +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.4" + // 8.9.2023 two new functions (end of file) for mifare test +//#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.5" + // 18.9.2023 major improvements for DC data exchange + // verification of door and cash box signals + // intensive verification of Json-Programming Master-Slave (PTU to DC), 100% ok + +#define HWINF_iid "Atb.Psa1256ptu5.software.HWapi/4.6" + // 20.9.2023: speeding up door and cash box signals + + +Q_DECLARE_INTERFACE(hwinf, HWINF_iid) + + +#endif + + + diff --git a/plugins/ATBAPPplugin.h b/plugins/ATBAPPplugin.h new file mode 100644 index 0000000..ec017f5 --- /dev/null +++ b/plugins/ATBAPPplugin.h @@ -0,0 +1,22 @@ +#ifndef ATBAPPPLUGIN_H +#define ATBAPPPLUGIN_H + +/*********************************************************** + * a simple class with only one method for plugin info + */ +#include +#include + +class ATBAPPplugin +{ + +public: + virtual const QString & getPluginInfo() = 0; +}; + +Q_DECLARE_INTERFACE(ATBAPPplugin, + "eu.atb.ptu.plugin.ATBAPPplugin/0.9") + + + +#endif // ATBAPPPLUGIN_H diff --git a/plugins/CC/CCDummy.cpp b/plugins/CC/CCDummy.cpp new file mode 100644 index 0000000..861a671 --- /dev/null +++ b/plugins/CC/CCDummy.cpp @@ -0,0 +1,147 @@ +#include "CCDummy.h" +#include + + +using namespace nsCCInterface; + + +const std::string CCInterfacePluginInfoString = R"( +{ + "PluginName": "CCDummy", + "Version": "1.0", + "git-describe": "", +} +)"; + + + +CCDummy::CCDummy(QObject *parent) : + QObject(parent), + errorCode(1), + errorDescription(""), + pluginState(PLUGIN_STATE::NOT_INITIALIZED) +{ + this->pluginInfo = QString::fromUtf8(CCInterfacePluginInfoString.c_str()); +} + + + +CCDummy::~CCDummy() +{ + +} + + +PLUGIN_STATE CCDummy::initCCInterfacePlugin(QObject *healthEventReceiver, const QSettings & settings) +{ + Q_UNUSED(healthEventReceiver) + + qDebug() << "called CCDummy::initCalculatePricePlugin()"; + qDebug() << " pluginName from setting is: " << settings.value("PLUGINS/CCPlugin", "").toString(); + + this->pluginState = PLUGIN_STATE::INITIALIZED; + + return this->pluginState; +} + + + +void CCDummy::requestReset() +{ + +} + + +void CCDummy::requestStartTransaction(quint32 amount) +{ + Q_UNUSED(amount) +} + + +void CCDummy::requestCancelTransaction() +{ + +} + + +void CCDummy::requestRevertTransaction() +{ + +} + + +void CCDummy::requestConfirmTransaction() +{ + +} + + +void CCDummy::requestDayClose() +{ + +} + +void CCDummy::requestCardInfo() +{ + +} + +void CCDummy::requestPreAuthTransaction(quint32 amount) +{ + Q_UNUSED(amount); +} + +void CCDummy::requestCancelPreAuthTransaction(QString & receiptNumber) +{ + Q_UNUSED(receiptNumber); +} + +void CCDummy::requestBookTotalTransaction(quint32 amount, QString & receiptNumber) +{ + Q_UNUSED(amount) + Q_UNUSED(receiptNumber) +} + +void CCDummy::wakeupCC() +{ + +} + +void CCDummy::sleepCC() +{ + +} + + + + + +/************************************************************************************************ + * Mandatory plugin methods + * + */ + + +PLUGIN_STATE CCDummy::getState() +{ + return this->pluginState; +} + +quint32 CCDummy::getLastError() +{ + return this->errorCode; +} + +const QString & CCDummy::getLastErrorDescription() +{ + return this->errorDescription; +} + + +const QString & CCDummy::getPluginInfo() +{ + return pluginInfo; +} + + + diff --git a/plugins/CC/CCDummy.h b/plugins/CC/CCDummy.h new file mode 100644 index 0000000..e85051f --- /dev/null +++ b/plugins/CC/CCDummy.h @@ -0,0 +1,68 @@ +#ifndef CCDUMMY_H +#define CCDUMMY_H + + +#include +#include "CCInterface.h" +#include "ATBAPPplugin.h" + + +class CCDummy : public QObject, + public CCInterface +{ + Q_OBJECT + Q_INTERFACES(ATBAPPplugin) + Q_INTERFACES(CCInterface) + +public: + CCDummy(QObject *parent = 0); + ~CCDummy(); + + // interface: + nsCCInterface::PLUGIN_STATE initCCInterfacePlugin(QObject *healthEventReceiver, const QSettings & settings); + + nsCCInterface::PLUGIN_STATE getState(); + quint32 getLastError(); + const QString & getLastErrorDescription(); + + // return a plugin description in JSON or XML + const QString & getPluginInfo(); + + +public slots: + void requestReset(); + void requestStartTransaction(quint32 amount); + void requestCancelTransaction(); + void requestRevertTransaction(); + void requestConfirmTransaction(); + void requestDayClose(); + void requestCardInfo(); + void requestPreAuthTransaction(quint32 amount); + void requestCancelPreAuthTransaction(QString & receiptNumber); + void requestBookTotalTransaction(quint32 amount, QString & receiptNumber); + + void wakeupCC(); + void sleepCC(); + +signals: + void sendStartTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void sendCancelTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void sendRevertTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void sendConfirmTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void sendDayCloseResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void sendCardInfoResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void sendPreAuthTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void sendCancelPreAuthTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + void sendBookTotalTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result); + +private: + quint32 errorCode; + QString errorCodeString; + QString errorDescription; + QString pluginInfo; + + nsCCInterface::PLUGIN_STATE pluginState; + +}; + +#endif // CCDUMMY_H diff --git a/plugins/CC/CCInterface.h b/plugins/CC/CCInterface.h new file mode 100644 index 0000000..f15ea4f --- /dev/null +++ b/plugins/CC/CCInterface.h @@ -0,0 +1,86 @@ +#ifndef CCINTERFACE_H +#define CCINTERFACE_H + +#include + +#include +#include + +#include "ATBAPPplugin.h" + + +namespace nsCCInterface { + enum class PLUGIN_STATE : quint8; + enum class RESULT_STATE : quint8; +} + + +class CCInterface : public ATBAPPplugin +{ + Q_INTERFACES(ATBAPPplugin) + +public: + virtual ~CCInterface() {} + + virtual nsCCInterface::PLUGIN_STATE initCCInterfacePlugin(QObject *healthEventReceiver, const QSettings & settings) = 0; + + + virtual nsCCInterface::PLUGIN_STATE getState() = 0; + virtual quint32 getLastError() = 0; + virtual const QString & getLastErrorDescription() = 0; + + // return a plugin description in JSON or XML + // -> ATBAPPplugin::getPluginInfo() + +public slots: + virtual void requestReset() = 0; + virtual void requestStartTransaction(quint32 amount) = 0; + virtual void requestCancelTransaction() = 0; + virtual void requestRevertTransaction() = 0; + virtual void requestConfirmTransaction() = 0; + virtual void requestDayClose() = 0; + virtual void requestCardInfo() = 0; + virtual void requestPreAuthTransaction(quint32 amount) = 0; + virtual void requestCancelPreAuthTransaction(QString & receiptNumber) = 0; + virtual void requestBookTotalTransaction(quint32 amount, QString & receiptNumber) = 0; + virtual void wakeupCC() = 0; + virtual void sleepCC() = 0; + +signals: + virtual void sendStartTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + virtual void sendCancelTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + virtual void sendRevertTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + virtual void sendConfirmTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + virtual void sendDayCloseResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + virtual void sendCardInfoResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + virtual void sendPreAuthTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + virtual void sendCancelPreAuthTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + virtual void sendBookTotalTransactionResult(nsCCInterface::RESULT_STATE resultState, QString & result) = 0; + +}; + +Q_DECLARE_INTERFACE(CCInterface, + "eu.atb.ptu.plugin.CCInterface/2.9.1") + + +namespace nsCCInterface { + + enum class PLUGIN_STATE : quint8 { + NOT_INITIALIZED = 0, + INITIALIZED = 1 + }; + + enum class RESULT_STATE : quint8 { + SUCCESS = 1, + ERROR_BACKEND = 2, + ERROR_NETWORK = 3, + ERROR_TIMEOUT = 4, + ERROR_PROCESS = 5, + ERROR_BUSY = 6, + ERROR_STATE = 7, + INFO = 8 + }; + +} + +#endif // CCINTERFACE_H diff --git a/plugins/CalculatePrice/CalculatePriceDefault.cpp b/plugins/CalculatePrice/CalculatePriceDefault.cpp new file mode 100644 index 0000000..2cbebba --- /dev/null +++ b/plugins/CalculatePrice/CalculatePriceDefault.cpp @@ -0,0 +1,215 @@ +#include "CalculatePriceDefault.h" +#include + + +using namespace nsCalculatePriceInterface; + + + +const std::string CalculatePriceInterfacePluginInfoString = R"( +{ + "PluginName": "CalculatePriceDummy", + "Version": "1.0", + "git-describe": "", +} +)"; + + + +CalculatePriceDefault::CalculatePriceDefault(QObject *parent) : + QObject(parent), + errorCode(1), + errorDescription(""), + pluginState(PLUGIN_STATE::NOT_INITIALIZED) +{ + this->pluginInfo = QString::fromUtf8(CalculatePriceInterfacePluginInfoString.c_str()); +} + + + +CalculatePriceDefault::~CalculatePriceDefault() +{ + +} + + +PLUGIN_STATE CalculatePriceDefault::initCalculatePricePlugin(QObject *healthEventReceiver, const QSettings & settings) +{ + Q_UNUSED(healthEventReceiver) + + qDebug() << "called CalculatePriceDummy::initCalculatePricePlugin()"; + qDebug() << " pluginName from setting is: " << settings.value("PLUGINS/CalculatePricePlugin", "").toString(); + + this->pluginState = PLUGIN_STATE::INITIALIZED; + + return this->pluginState; +} + + +void CalculatePriceDefault::requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType) +{ + Q_UNUSED(PermitType) + + emit this->requestCalculatePriceResult(RESULT_STATE::SUCCESS, + AccessInformation, + AccessInformationType, + "", + "", + "", + "", + "", + "1", + "dummyPlugin" + ); +} + +void CalculatePriceDefault::requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + const QString & parktime) +{ + Q_UNUSED(PermitType) + Q_UNUSED(parktime) + + emit this->requestCalculatePriceResult(RESULT_STATE::SUCCESS, + AccessInformation, + AccessInformationType, + "", + "", + "", + "", + "", + "1", + "dummyPlugin" + ); +} + +void CalculatePriceDefault::requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + nsCalculatePriceInterface::STEP step) +{ + Q_UNUSED(AccessInformation) + Q_UNUSED(AccessInformationType) + Q_UNUSED(PermitType) + Q_UNUSED(step) +} + +void CalculatePriceDefault::requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + int coinValue) +{ + Q_UNUSED(AccessInformation) + Q_UNUSED(AccessInformationType) + Q_UNUSED(PermitType) + Q_UNUSED(coinValue) +} + +void CalculatePriceDefault::requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + const QString & parktime, + const QString & cardInfo, + const QString & transactionInfo) +{ + Q_UNUSED(PermitType) + Q_UNUSED(parktime) + Q_UNUSED(cardInfo) + Q_UNUSED(transactionInfo) + + emit this->requestCalculatePriceResult(RESULT_STATE::SUCCESS, + AccessInformation, + AccessInformationType, + "", + "", + "", + "", + "", + "1", + "dummyPlugin" + ); + +} + + +void CalculatePriceDefault::requestProcessTransaction(const QString & AccessInformation, + const QString & AccessInformationType, + const QString &amount, + bool isOffline, + const QString & TransactionId, + const QString & ReceiptData, + const QString & PermitType, const QString &PaymentType) +{ + Q_UNUSED(isOffline) + Q_UNUSED(AccessInformation) + Q_UNUSED(AccessInformationType) + Q_UNUSED(amount) + Q_UNUSED(TransactionId) + Q_UNUSED(ReceiptData) + Q_UNUSED(PermitType) + Q_UNUSED(PaymentType) +} + + + +/************************************************************************************************ + * Mandatory plugin methods + * + */ + + +PLUGIN_STATE CalculatePriceDefault::getState() +{ + return this->pluginState; +} + +quint32 CalculatePriceDefault::getLastError() +{ + return this->errorCode; +} + +const QString & CalculatePriceDefault::getLastErrorDescription() +{ + return this->errorDescription; +} + + +const QString & CalculatePriceDefault::getPluginInfo() +{ + return pluginInfo; +} + + +const QString CalculatePriceDefault::getString(nsCalculatePriceInterface::RESULT_STATE resultState) +{ + QString str; + + switch (resultState) { + case nsCalculatePriceInterface::RESULT_STATE::SUCCESS: + str = QString("RESULT_STATE::SUCCESS"); + break; + case nsCalculatePriceInterface::RESULT_STATE::ERROR_BACKEND: + str = QString("RESULT_STATE::ERROR_BACKEND"); + break; + case nsCalculatePriceInterface::RESULT_STATE::ERROR_NETWORK: + str = QString("RESULT_STATE::ERROR_NETWORK"); + break; + case nsCalculatePriceInterface::RESULT_STATE::ERROR_TIMEOUT: + str = QString("RESULT_STATE::ERROR_TIMEOUT"); + break; + case nsCalculatePriceInterface::RESULT_STATE::ERROR_PROCESS: + str = QString("RESULT_STATE::ERROR_PROCESS"); + break; + case nsCalculatePriceInterface::RESULT_STATE::ERROR_RETRY: + str = QString("RESULT_STATE::ERROR_RETRY"); + break; + case nsCalculatePriceInterface::RESULT_STATE::INFO: + str = QString("RESULT_STATE::INFO"); + break; + } + return str; +} + diff --git a/plugins/CalculatePrice/CalculatePriceDefault.h b/plugins/CalculatePrice/CalculatePriceDefault.h new file mode 100644 index 0000000..d44ef0d --- /dev/null +++ b/plugins/CalculatePrice/CalculatePriceDefault.h @@ -0,0 +1,95 @@ +#ifndef CALCULATEPRICEDUMMY_H +#define CALCULATEPRICEDUMMY_H + + +#include +#include "CalculatePriceInterface.h" +using namespace nsCalculatePriceInterface; + + +class CalculatePriceDefault : public QObject, + public CalculatePriceInterface +{ + Q_OBJECT + Q_INTERFACES(CalculatePriceInterface) + +public: + CalculatePriceDefault(QObject *parent = 0); + ~CalculatePriceDefault(); + + // interface: + PLUGIN_STATE initCalculatePricePlugin(QObject *healthEventReceiver, const QSettings & settings); + void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType); + void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + const QString & parktime); + void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + nsCalculatePriceInterface::STEP step); + void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + int coinValue); + void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + const QString & parktime, + const QString & cardInfo, + const QString & transactionInfo); + void requestProcessTransaction(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & amount, + bool isOffline, + const QString & TransactionId, + const QString & ReceiptData, + const QString & PermitType, + const QString & PaymentType); + + + PLUGIN_STATE getState(); + quint32 getLastError(); + const QString & getLastErrorDescription(); + + const QString & getPluginInfo(); + + const QString getString(nsCalculatePriceInterface::RESULT_STATE resultState); + +signals: + void requestCalculatePriceResult(RESULT_STATE resultState, + const QString & accessInformation, + const QString & accessInformationType, + const QString & amountDuePeriodStart, + const QString & amountDuePeriodEnd, + const QString & amountDueNet, + const QString & amountDueTax, + const QString & gracePeriod, + const QString & errorCode, + const QString & errorDescription); + void requestCalculatePriceResult(RESULT_STATE resultState, + const QString & accessInformation, + const QString & accessInformationType, + const QString & amountDuePeriodStart, + const QString & amountDuePeriodEnd, + const PriceInfo & priceInfo, + const QString & errorCode, + const QString & errorDescription); + void requestProcessTransactionResult(RESULT_STATE resultState, + const QString & errorCode, + const QString & errorDescription); + + +private: + quint32 errorCode; + QString errorCodeString; + QString errorDescription; + QString pluginInfo; + + PLUGIN_STATE pluginState; + +}; + +#endif // CALCULATEPRICEDUMMY_H diff --git a/plugins/CalculatePrice/CalculatePriceInterface.h b/plugins/CalculatePrice/CalculatePriceInterface.h new file mode 100644 index 0000000..32c7a28 --- /dev/null +++ b/plugins/CalculatePrice/CalculatePriceInterface.h @@ -0,0 +1,157 @@ +#ifndef CALCULATEPRICEINTERFACE_H +#define CALCULATEPRICEINTERFACE_H + +#include + +#include +#include + +#include "ATBAPPplugin.h" + +namespace nsCalculatePriceInterface { + enum class PLUGIN_STATE : quint8; + enum class RESULT_STATE : quint8; + enum class STEP : quint8; +} + +class PriceInfo +{ +public: + int priceNet; // price net in cent + int priceTax; // price tax in cent + int priceGross; // price gross in cent + int taxRate; // tax rate per mill +}; + + + +class CalculatePriceInterface : public ATBAPPplugin +{ + Q_INTERFACES(ATBAPPplugin) + +public: + virtual ~CalculatePriceInterface() {} + + virtual nsCalculatePriceInterface::PLUGIN_STATE initCalculatePricePlugin(QObject *healthEventReceiver, + const QSettings & settings) = 0; + + // for price calculation dependent on ID (AccessInformation) + virtual void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType) = 0; + + // for price calculation dependent on value (e.g. parking time) + virtual void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + const QString & parktime) = 0; + + // for price/time calculation on next step up/down + virtual void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + nsCalculatePriceInterface::STEP step) = 0; + + // for price/time calculation on next coin value + virtual void requestCalculatePrice(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & PermitType, + int coinValue) = 0; + + // for price calculation dependent on additonal values (e.g. parking time, card info, ...) + virtual void requestCalculatePrice(const QString & AccessInformation, // primary key e.g. a license plate, credit card number + const QString & AccessInformationType, // type of AccessInformation: LICENSEPLATE, CREDITCARD, PIN, ... + const QString & PermitType, // e.g. selected product / vehicle + const QString & parktime, // parktime in minutes + const QString & cardInfo, // additonal card info (e.g. PAN, Institusnummer, Token, ...) + const QString & transactionInfo) = 0; // kind of a transcation id, could be used for identification of transaction + + virtual void requestProcessTransaction(const QString & AccessInformation, + const QString & AccessInformationType, + const QString & amount, + bool isOffline, + const QString & TransactionId, + const QString & ReceiptData, + const QString & PermitType, + const QString & PaymentType) = 0; + + + virtual nsCalculatePriceInterface::PLUGIN_STATE getState() = 0; + virtual quint32 getLastError() = 0; + virtual const QString & getLastErrorDescription() = 0; + + // return a plugin description in JSON or XML + // -> ATBAPPplugin::getPluginInfo() + + // helpers e.g. for debug / log + virtual const QString getString(nsCalculatePriceInterface::RESULT_STATE resultState) = 0; + +signals: + virtual void requestCalculatePriceResult(nsCalculatePriceInterface::RESULT_STATE resultState, + const QString & accessInformation, + const QString & accessInformationType, + const QString & amountDuePeriodStart, + const QString & amountDuePeriodEnd, + const QString & amountDueNet, + const QString & amountDueTax, + const QString & gracePeriod, + const QString & errorCode, + const QString & errorDescription) = 0; + + virtual void requestCalculatePriceResult(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) = 0; + + virtual void requestProcessTransactionResult(nsCalculatePriceInterface::RESULT_STATE resultState, + const QString & errorCode, + const QString & errorDescription) = 0; + + // TODO: + /* APPLICATION_PROCESS_TYPE is a global ATB-Type: + * - BACKGROUND -> process can work in background without display + * - HMI -> display is needed + * - t.b.d + * + * PROCESS_ID is an identifier for e.g. debuging or log which process/module/library/plugin... + * caused the prevent + * + virtual void preventSuspend(APPLICATION_PROCESS_TYPE pt, PROCESS_ID pid) = 0; + virtual void allowSuspend(PROCESS_ID pid) = 0; + */ +}; + + +Q_DECLARE_INTERFACE(CalculatePriceInterface, + "eu.atb.ptu.plugin.CalculatePriceInterface/4.0") + + +namespace nsCalculatePriceInterface { + + enum class PLUGIN_STATE : quint8 { + NOT_INITIALIZED = 0, + INITIALIZED = 1 + }; + + enum class RESULT_STATE : quint8 { + SUCCESS = 1, // got price and time from remote + ERROR_BACKEND = 2, // error from backand (e.g. backend replies with error) + ERROR_NETWORK = 3, // error from network (e.g. host not available) + ERROR_TIMEOUT = 4, // the operation timed out + ERROR_PROCESS = 5, // internal plugin error (e.g. bug in implementation) + ERROR_RETRY = 6, // retry operation + INFO = 7 + }; + + enum class STEP : quint8 { + UP = 1, + DOWN = 2 + }; +} + + +#endif // CALCULATEPRICEINTERFACE_H diff --git a/plugins/PluginManager.cpp b/plugins/PluginManager.cpp new file mode 100644 index 0000000..adf7a0a --- /dev/null +++ b/plugins/PluginManager.cpp @@ -0,0 +1,134 @@ +#include +#include + +#include "PluginManager.h" +#include "ATBHMIconfig.h" + +#include "plugins/ATBAPPplugin.h" +#include "plugins/CC/CCInterface.h" +#include "plugins/CalculatePrice/CalculatePriceInterface.h" + + +PluginManager::PluginManager(ATBHMIconfig *config, QObject *parent) : + QObject(parent), + config(config) +{ + this->loadPlugins(); +} + + +void PluginManager::loadPlugins() +{ + /* NOTE: setting a search path via 'QCoreApplication::addLibraryPath' does not work for + * user specific plugins... + * This seems to work only for QT core plugins which are loaded before QApplication object is + * created. + * => we set the path for our plugins manually here. + */ + // DEBUG + /* qDebug() << "--------------------------- DEBUG -----------------------------------"; + * qDebug() << "QCoreApplication::applicationDirPath() = " << QCoreApplication::applicationDirPath(); + * qDebug() << " "; + * qDebug() << "QCoreApplication::libraryPaths()"; + * foreach (const QString &path, QCoreApplication::libraryPaths()) { + * qDebug() << " " << path; + * } + * qDebug() << "---------------------------------------------------------------------"; + */ + /* NOTE: qputenv() to set LD_LIBRARY_PATH does not work either! + * We tried this to set the path were libraries used by the plugin itself could be found + * (e.g. kdsoap-library, which is used by several plugins). + */ + + // load all available plugins in config group [PLUGINS] + QSettings * settings = this->config->getSettingsPtr(); + + if (!settings->childGroups().contains("PLUGINS")) { + qCritical() << "PluginManager: no plugins defined in config"; + return; + } + settings->beginGroup("PLUGINS"); + QStringList pluginStringList = settings->childKeys(); + + QStringListIterator PluginsIterator(pluginStringList); + QString pluginKey; + QString pluginName; + while(PluginsIterator.hasNext()) { + pluginKey = PluginsIterator.next(); + pluginName = settings->value(pluginKey, "").toString(); + + if (pluginName == "") { + qCritical() << "PluginManager: plugin " << pluginKey << " is not defined"; + } + else if (pluginKey.startsWith('#')) { + qCritical() << "PluginManager: skip comment " << pluginKey; + } + else if (QString::compare(pluginName, "notUsed", Qt::CaseInsensitive) == 0) { + qCritical() << "PluginManager: plugin " << pluginKey << " is not used"; + } + else { + if (!QLibrary::isLibrary(pluginName)) pluginName.append(".so"); + if (!pluginName.startsWith("lib")) pluginName.prepend("lib"); + if (!pluginName.startsWith("plugins")) pluginName.prepend("plugins/"); + + if (!pluginName.startsWith('/')) { + pluginName.prepend('/').prepend(QApplication::applicationDirPath()); + } + + qCritical() << "PluginManager::loadPlugins() load plugin: " + << pluginName; + + // TODO: check, if plugin file is available, readable etc... + + QPluginLoader* pluginLoader = new QPluginLoader(); + pluginLoader->setFileName(pluginName); + + plugins.insert(pluginKey, pluginLoader); + } + } + settings->endGroup(); +} + + + +QObject * PluginManager::getInstance(const QString & pluginname) +{ + QObject * result = nullptr; + + QPluginLoader* pluginLoader = plugins[pluginname]; + + if (pluginLoader != nullptr) { + + QObject* plugin = pluginLoader->instance(); + if (!pluginLoader->isLoaded()) { + qCritical() << "PluginManager::getInstance(" + << pluginname + << ") errorString: " + << pluginLoader->errorString(); + } + else { + result = plugin; + } + + } + + return result; +} + + + +const QList PluginManager::listAvailablePlugins() +{ + return plugins.keys(); +} + + +bool PluginManager::isPluginAvailable(const QString & pluginname) +{ + if (plugins[pluginname] != nullptr) { + return true; + } + else { + return false; + } +} diff --git a/plugins/PluginManager.h b/plugins/PluginManager.h new file mode 100644 index 0000000..e5cf3f2 --- /dev/null +++ b/plugins/PluginManager.h @@ -0,0 +1,42 @@ +#ifndef PLUGINMANAGER_H +#define PLUGINMANAGER_H + +#include +#include +#include + + +class ATBHMIconfig; +class QPluginLoader; + + + +class PluginManager : public QObject +{ + Q_OBJECT + +public: + explicit PluginManager(ATBHMIconfig *config, QObject *parent = nullptr); + + QObject * getInstance(const QString & pluginname); + + const QList listAvailablePlugins(); + bool isPluginAvailable(const QString & pluginname); + +signals: + +public slots: + +private: + ATBHMIconfig *config; + + QDir pluginsDir; + + void loadPlugins(); + + // Hash to store all plugins + QHash plugins; + +}; + +#endif // PLUGINMANAGER_H diff --git a/src/ATBAPP/ATBAPPplugin.h b/src/ATBAPP/ATBAPPplugin.h new file mode 100644 index 0000000..333fbd0 --- /dev/null +++ b/src/ATBAPP/ATBAPPplugin.h @@ -0,0 +1,17 @@ +#ifndef ATBAPPPLUGIN_H +#define ATBAPPPLUGIN_H + +/*********************************************************** + * a simple class with only one method for plugin info + */ +#include +#include + +class ATBAPPplugin { +public: + virtual const QString & getPluginInfo() = 0; +}; + +Q_DECLARE_INTERFACE(ATBAPPplugin, "eu.atb.ptu.plugin.ATBAPPplugin/0.9") + +#endif // ATBAPPPLUGIN_H diff --git a/src/ATBAPP/ATBVMCPlugin.cpp b/src/ATBAPP/ATBVMCPlugin.cpp new file mode 100644 index 0000000..8656d1c --- /dev/null +++ b/src/ATBAPP/ATBVMCPlugin.cpp @@ -0,0 +1,91 @@ +#include "src/ATBAPP/ATBVMCPlugin.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + +ATBVMCPlugin::ATBVMCPlugin(QObject *parent) + : m_errorCode("") + , m_errorDescription("") + , m_pluginInfo("") + , m_serialPortName("") + , m_useDebug(false) + , m_pluginState(PLUGIN_STATE::NOT_INITIALIZED) + , m_eventReceiver(nullptr) { + this->setParent(parent); +} + +ATBVMCPlugin::~ATBVMCPlugin() { +} + +PLUGIN_STATE ATBVMCPlugin::initVMCPlugin(QObject *eventReceiver, + QSettings const &settings) { + Q_UNUSED(eventReceiver); + Q_UNUSED(settings); + return PLUGIN_STATE::NOT_INITIALIZED; +} + +// mandantory ATBAPP plugin methods: ------------------------------------------ +PLUGIN_STATE ATBVMCPlugin::getState() { + return PLUGIN_STATE::NOT_INITIALIZED; +} + +QString ATBVMCPlugin::getLastError() { + return ""; +} + +QString ATBVMCPlugin::getLastErrorDescription() { + return ""; +} + +QString const &ATBVMCPlugin::getPluginInfo() { + static QString info; + return info; +} + +// helpers e.g. for debug / log +QString ATBVMCPlugin::getString(RESULT_STATE /*resultState*/) { + return ""; +} + +void ATBVMCPlugin::onChangedProgramModeToSELL() { + +} + +void ATBVMCPlugin::onChangedProgramModeToSERVICE() { + +} + +void ATBVMCPlugin::onChangedProgramModeToIDLE() { + +} + +void ATBVMCPlugin::onChangedProgramModeToOOO() { + +} + +void ATBVMCPlugin::startPhysicalLayer() { + +} + +void ATBVMCPlugin::stopPhysicalLayer() { + +} + +void ATBVMCPlugin::reboot() { + +} + +void ATBVMCPlugin::reset() { + +} diff --git a/src/ATBAPP/ATBVMCPlugin.h b/src/ATBAPP/ATBVMCPlugin.h new file mode 100644 index 0000000..c6aed0f --- /dev/null +++ b/src/ATBAPP/ATBVMCPlugin.h @@ -0,0 +1,61 @@ +#ifndef ATBVMCPLUGIN_H +#define ATBVMCPLUGIN_H + +#include + +#include "src/ATBAPP/VMCInterface.h" +#include "src/ATBAPP/ATBAPPplugin.h" + +#include "version.h" + +#include "include/interfaces.h" + + +class ATBVMCPlugin : public VMCInterface { + Q_OBJECT + Q_INTERFACES(ATBAPPplugin) + Q_INTERFACES(UnifiedDCVMCInterface) + #if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA( IID "ATBVMCPlugin" ) + #endif + +public: + explicit ATBVMCPlugin(QObject *parent = nullptr); + ~ATBVMCPlugin(); + + // ---------------------------------------------------------------------------- + // interface: + PLUGIN_STATE initVMCPlugin(QObject *eventReceiver, QSettings const &settings); + + // mandantory ATBAPP plugin methods: ------------------------------------------ + PLUGIN_STATE getState() override; + QString getLastError() override; + QString getLastErrorDescription() override; + + virtual const QString & getPluginInfo() override; + + // helpers e.g. for debug / log + virtual QString getString(RESULT_STATE resultState) override; + +public slots: + virtual void onChangedProgramModeToSELL() override; + virtual void onChangedProgramModeToSERVICE() override; + virtual void onChangedProgramModeToIDLE() override; + virtual void onChangedProgramModeToOOO() override; + + virtual void startPhysicalLayer() override; + virtual void stopPhysicalLayer() override; + virtual void reboot() override; + virtual void reset() override; + +private: + QString m_errorCode; + QString m_errorDescription; + QString m_pluginInfo; + QString m_serialPortName; + bool m_useDebug; + PLUGIN_STATE m_pluginState; + QObject* m_eventReceiver; +}; + +#endif // ATBDEVICECONTROLLERPLUGIN_H diff --git a/src/ATBAPP/UnifiedDCVMCInterface.h b/src/ATBAPP/UnifiedDCVMCInterface.h new file mode 100644 index 0000000..1fbedc3 --- /dev/null +++ b/src/ATBAPP/UnifiedDCVMCInterface.h @@ -0,0 +1,143 @@ +#ifndef UNIFIED_DCVMC_INTERFACE_H_INCLUDED +#define UNIFIED_DCVMC_INTERFACE_H_INCLUDED + +#include +#include + +#include + +#include +#include + +#include "ATBAPPplugin.h" + +class UnifiedDCVMCInterface : public QObject, public ATBAPPplugin { + + Q_OBJECT + Q_INTERFACES(ATBAPPplugin) + +public: + enum class PLUGIN_STATE : quint8 { + NOT_INITIALIZED = 0, + INITIALIZED = 1 + }; + enum class RESULT_STATE : quint8 { + SUCCESS = 1, // operation was successfull + ERROR_BACKEND, // error from backend (e.g. backend replies with error) + ERROR_TIMEOUT, // the operation timed out + ERROR_PROCESS, // internal plugin error, should not occur (this is a bug in implementation) + ERROR_RETRY, // retry operation + INFO // informational (e.g. display a message, log something etc.) + }; + enum class CASH_STATE : quint8 { + CACHE_EMPTY, // Cache still empty, default state + CACHE_INPUT, // Coins are in Cache + OVERPAYED, + /* t.b.d. */ + }; + enum class TICKET_VARIANT : quint8 { + PARKING_TICKET, + RECEIPT, + ERROR_RECEIPT, + START_RECEIPT, // e.g. Szeged Start + STOP_RECEIPT, // e.g. Szeged Stop + }; + + explicit UnifiedDCVMCInterface() = default; + virtual ~UnifiedDCVMCInterface() {} + + virtual PLUGIN_STATE initPlugin(QObject *eventReceiver, QSettings const &settings) = 0; + + // mandantory ATBAPP plugin methods: + virtual PLUGIN_STATE getState() = 0; + virtual QString getLastError() = 0; + virtual QString getLastErrorDescription() = 0; + + virtual QString getString(RESULT_STATE resultState) = 0; + +public slots: + virtual void onChangedProgramModeToSELL() = 0; + virtual void onChangedProgramModeToSERVICE() = 0; + virtual void onChangedProgramModeToIDLE() = 0; + virtual void onChangedProgramModeToOOO() = 0; + + virtual void startPhysicalLayer() = 0; + virtual void stopPhysicalLayer() = 0; + virtual void reboot() = 0; + virtual void reset() = 0; + +signals: + void printTicketFinished(RESULT_STATE resultState, + const QString & errorCode, + const QString & errorDescription); + void printReceiptFinished(RESULT_STATE resultState, + const QString & errorCode, + const QString & errorDescription); + + /** + * emitted on e.g. a coin input + */ + void cashInputEvent(RESULT_STATE resultState, + CASH_STATE cashState, + const QString & newCashValue, + /* additional variables? */ + const QString & errorCode, + const QString & errorDescription); + + /** + * emitted if cashInput has been stopped, e.g. in result to task requestStopCashInput(): + * -> shutter is blocked + * -> no cash input is possible + * -> coins are in cache + */ + void cashInputFinished(RESULT_STATE resultState, + const QString & newCashValue, + /* additional variables? */ + const QString & errorCode, + const QString & errorDescription); + + + /** + * emitted e.g. if service door is opened + */ + void requestModeSERVICE(); + + /** + * emitted e.g. if doors are closed + */ + void requestModeIDLE(); + + /** + * emitted e.g. on severe errors + */ + void requestModeOOO(); + + /** + * emitted e.g. if service door is opened + */ + void requestAccountResponse(const QHash & accountData); + + /** + * emitted on error + * depending on errorCode: + * -> interrupt selling process + * -> machine can go to state OOO + * -> send error event to ISMAS + * -> ... + */ + void Error( + /* additional variables? */ + const QString & errorCode, + const QString & errorDescription); + +}; + +Q_DECLARE_INTERFACE(UnifiedDCVMCInterface, + "eu.atb.ptu.plugin.UnifiedDCVMCInterface/1.0") + +using PLUGIN_STATE = UnifiedDCVMCInterface::PLUGIN_STATE; +using RESULT_STATE = UnifiedDCVMCInterface::RESULT_STATE; +using CASH_STATE = UnifiedDCVMCInterface::CASH_STATE; +using TICKET_VARIANT = UnifiedDCVMCInterface::TICKET_VARIANT; + +#endif // UNIFIED_DCVMC_INTERFACE_H_INCLUDED diff --git a/src/ATBAPP/VMCInterface.h b/src/ATBAPP/VMCInterface.h new file mode 100644 index 0000000..743ce95 --- /dev/null +++ b/src/ATBAPP/VMCInterface.h @@ -0,0 +1,42 @@ +#ifndef VMCINTERFACE_H +#define VMCINTERFACE_H + +#include + +#include +#include + +#include "ATBAPPplugin.h" +#include "UnifiedDCVMCInterface.h" + +namespace nsVMCInterface { + using PLUGIN_STATE = UnifiedDCVMCInterface::PLUGIN_STATE; + using RESULT_STATE = UnifiedDCVMCInterface::RESULT_STATE; + using CASH_STATE = UnifiedDCVMCInterface::CASH_STATE; + using TICKET_VARIANT = UnifiedDCVMCInterface::TICKET_VARIANT; +} + +class VMCInterface : public UnifiedDCVMCInterface { + + Q_OBJECT + Q_INTERFACES(ATBAPPplugin) + Q_INTERFACES(UnifiedDCVMCInterface) + +public: + + virtual PLUGIN_STATE initPlugin(QObject *eventReceiver, QSettings const &settings) override { + return initVMCPlugin(eventReceiver, settings); + } + + virtual ~VMCInterface() {} + + /** + * @brief initDCPlugin + * @param eventReceiver - QObject to receive ATBMachineEvents or HealthEvents + * @param settings + * @return + */ + virtual PLUGIN_STATE initVMCPlugin(QObject *eventReceiver, const QSettings & settings) = 0; +}; + +#endif // VMCINTERFACE_H diff --git a/src/ATBAPP/support/VMC/ReceiveBuffer.cpp b/src/ATBAPP/support/VMC/ReceiveBuffer.cpp new file mode 100644 index 0000000..9bf4e2b --- /dev/null +++ b/src/ATBAPP/support/VMC/ReceiveBuffer.cpp @@ -0,0 +1,244 @@ +#include +#include "ReceiveBuffer.h" +#include "vmc.h" +#include + +ReceiveBuffer::ReceiveBuffer(COM_interface *cinterface, QObject *parent) : + QObject(parent) +{ + qRegisterMetaType("RECEIVE_ERROR"); + this->vmc = (VMC*)parent; + this->interface = cinterface; + + connect(interface, SIGNAL(ReadData(QByteArray*)), this, SLOT(getData(QByteArray*))); + connect(vmc, SIGNAL(CommandProcessed()), this, SLOT(removeCommand())); + connect(this, SIGNAL(CommandAvailiable(QByteArray)), vmc, SLOT(CommandAvaliable(QByteArray))); + + currentMessage.clear(); + + receiveIdleTimer = new QTimer(this); + receiveIdleTimer->setInterval(2000); // clear possible wrong data in receive buffer after 2s + receiveIdleTimer->setSingleShot(false); + + connect(receiveIdleTimer, SIGNAL(timeout()), this, SLOT(onReceiveIdleTimerTimeout())); + +} + + +ReceiveBuffer::~ReceiveBuffer() +{ + delete(this->receiveIdleTimer); +} + + +int ReceiveBuffer::size() +{ + return messageQueue.size(); +} + + +void ReceiveBuffer::removeCommand() +{ + if (messageQueue.size() > 0) { + messageQueue.removeFirst(); + } + else { + qDebug() << "ReceiveBuffer::removeCommand(): no message to remove!"; + } +} + + +void ReceiveBuffer::onReceiveIdleTimerTimeout() +{ + this->currentMessage.clear(); + this->receiveIdleTimer->stop(); +} + + + +void ReceiveBuffer::getData(QByteArray *data) +{ + int CRCindex; + int COMMAND_IN_PROGRESS = 1; + + // do nothing, if there is no data + if (data->size() == 0) { return; } + + // DEBUG + /* + qDebug() << "ReceiveBuffer::getData(): messageQueue.size = " << messageQueue.size(); + qDebug() << "ReceiveBuffer::getData(): data = " << data; + qDebug() << "ReceiveBuffer::getData(): data->size() = " << data->size(); + qDebug() << "ReceiveBuffer::getData(): ¤tMessage = " << ¤tMessage; + qDebug() << "ReceiveBuffer::getData(): currentMessage.capacity = " << currentMessage.capacity(); + qDebug() << "ReceiveBuffer::getData(): currentMessage.size() = " << currentMessage.size(); + */ + + this->receiveIdleTimer->start(); + + // for safety: prevent currentMessage to grow infinitely in size. + if (currentMessage.size() > 0xFF) { + emit ReceiveError(RECEIVE_ERROR::INVALID_COMMAND); + currentMessage.clear(); + } + + + // we use append because data can be an interrupted command: + currentMessage.append(data->data(), data->size()); + + //qDebug() << "ReceiveBuffer::getData(): currentMessage = " << QString(currentMessage); + + + // process commands: + while (COMMAND_IN_PROGRESS) { + + int startindex = currentMessage.indexOf(0x3e); // find start of command + + // detect ACK/NACK response messages from vmc: + if (startindex != 0) { + int forEnd = -1; + if (startindex > 0) forEnd = startindex; // if ACK/NACK is before command ... + if (startindex == -1) forEnd = currentMessage.size(); // if ACK/NACK is the only data + + for (int i = 0; i != forEnd; i++) { + switch (currentMessage.at(i)) { + case ACK: emit ReceiveResponse(ACK); + break; + case NACK: emit ReceiveResponse(NACK); + break; + } + } + } + + if(startindex == -1) { + currentMessage.clear(); + COMMAND_IN_PROGRESS = 0; + return; + } + else { + currentMessage.remove(0,startindex); + } + + // now, we have at least a beginning message + CRCindex = checkMessageForCRC(); + + if (CRCindex == 0) { + // exit this methode and message part is still in currentMessage. + break; + } + if (CRCindex < 0) { + + //qCriticalBAMessage(currentMessage); + + emit ReceiveError(RECEIVE_ERROR::INVALID_COMMAND); + currentMessage.clear(); + COMMAND_IN_PROGRESS = 0; + break; + } + + //qDebug() << "ReceiveBuffer::getData(): startindex = " << startindex; + //qDebug() << "ReceiveBuffer::getData(): currentMessage = " << currentMessage; + //qDebug() << "ReceiveBuffer::getData(): CRCindex = " << CRCindex; + + if (CRCindex) { + // found a complete message + if (checkCRC(currentMessage.at(CRCindex), CRCindex)) { + messageQueue << currentMessage.left(CRCindex + 1); + emit CommandAvailiable(messageQueue.first()); + } + else { + emit ReceiveError(RECEIVE_ERROR::CRC); + } + + if (currentMessage.size() > (CRCindex + 1)) { + currentMessage.remove(0,(CRCindex + 1)); + } + else { + currentMessage.clear(); + COMMAND_IN_PROGRESS = 0; + } + } + + } + +} + + +int ReceiveBuffer::checkMessageForCRC() +{ + int CRCindex; + int DataLengthIndex; + + + // qDebug() << "ReceiveBuffer::checkMessageForCRC(): currentMessage.size = " << currentMessage.size(); + // for (int i = 0; i < currentMessage.size(); i++) + // qDebug() << " currentMessage.at(" << i << ") = " << currentMessage.at(i); + + + if (currentMessage.size() <= 1) + return 0; + + switch (currentMessage.at(1)) { + case 0x60: DataLengthIndex = 5; break; + case 0x61: DataLengthIndex = 6; break; + default: DataLengthIndex = 2; break; + } + + if (currentMessage.size() <= DataLengthIndex) + return 0; + + if (( (static_cast(currentMessage.at(DataLengthIndex)) - 0x30)) <= 0 ) + return -1; + + CRCindex = DataLengthIndex + ( static_cast(currentMessage.at(DataLengthIndex)) - 0x30) + 1; + + if (currentMessage.size() <= CRCindex) + return 0; + else + return CRCindex; +} + + + +bool ReceiveBuffer::checkCRC(quint8 CRC, int index) +{ + quint8 cCRC = 0; + + for (int i = 0; i < index; i++) { + cCRC += static_cast(currentMessage.at(i)); + //qDebug() << "ReceiveBuffer::checkCRC(): currentMessage.at(i) = " << currentMessage.at(i); + } + + //qDebug() << "ReceiveBuffer::checkCRC(): cCRC = " << cCRC; + //qDebug() << "ReceiveBuffer::checkCRC(): CRC = " << CRC; + + + if (cCRC == CRC) return true; + else return false; +} + + + +/** + * @brief ReceiveBuffer::qCriticalBAMessage + * @param message + * + * This is for test and debugging only. + */ +void ReceiveBuffer::qCriticalBAMessage(QByteArray message) +{ + QString hexString = "received: "; + qint64 i; + + for (i=0; i < message.size(); i++) { + hexString.append((QString(message.at(i))).toLatin1().toHex()); + hexString.append(' '); + } + hexString.chop(1); + + //qCritical() << "VMC::CommandAvaliable(): message.size(): " << message.size() << ""; + qCritical() << "VMC::qCriticalBAMessage(): message: " << hexString << ""; + +} + + diff --git a/src/ATBAPP/support/VMC/ReceiveBuffer.h b/src/ATBAPP/support/VMC/ReceiveBuffer.h new file mode 100644 index 0000000..db44a47 --- /dev/null +++ b/src/ATBAPP/support/VMC/ReceiveBuffer.h @@ -0,0 +1,72 @@ +#ifndef RECEIVEBUFFER_H +#define RECEIVEBUFFER_H + + +#include +#include +#include +#include "com_interface.h" + + + + +class QTimer; +class VMC; +enum class RECEIVE_ERROR : quint8; + + +class ReceiveBuffer : public QObject +{ + Q_OBJECT + + +private: + VMC *vmc; + COM_interface *interface; + + QList messageQueue; + QByteArray currentMessage; + + int checkMessageForCRC(); + bool checkCRC(quint8 CRC, int index); + + /* the receiveIdleTimer clears the receive buffer (currentMessag), if for + * a certain time no data was received. + * This is for improve speed for responding on vmc messages because possible + * wrong data is removed from queue. + */ + QTimer *receiveIdleTimer; + + // for debug: + void qCriticalBAMessage(QByteArray message); + +public: + explicit ReceiveBuffer(COM_interface *cinterface, QObject *parent = nullptr); + ~ReceiveBuffer(); + + int size(); + + +signals: + void CommandAvailiable(QByteArray command); + void ReceiveError(RECEIVE_ERROR error); + void ReceiveResponse(quint8 response); + + +private slots: + void removeCommand(); + void getData(QByteArray *data); + + void onReceiveIdleTimerTimeout(); + +}; + + +enum class RECEIVE_ERROR : quint8 { + NO_ERROR = 0, + CRC, + NO_COMMAND, + INVALID_COMMAND +}; + +#endif // RECEIVEBUFFER_H diff --git a/src/ATBAPP/support/VMC/SendBuffer.cpp b/src/ATBAPP/support/VMC/SendBuffer.cpp new file mode 100644 index 0000000..106b43b --- /dev/null +++ b/src/ATBAPP/support/VMC/SendBuffer.cpp @@ -0,0 +1,206 @@ +#include +#include "SendBuffer.h" +#include "vmc.h" +#include +#include "version.h" + +SendBuffer::SendBuffer(COM_interface *cinterface, QObject *parent) : + QObject(parent) +{ + qRegisterMetaType("SEND_ERROR"); + this->vmc = (VMC*)parent; + this->interface = cinterface; + + this->responseTimeoutTimer = new QTimer(this); + this->responseTimeoutTimer->setInterval(1000); // resend message to vmc after 1s without response + this->responseTimeoutTimer->setSingleShot(false); + + connect(this->responseTimeoutTimer, SIGNAL(timeout()), this, SLOT(onResponseTimeoutTimerTimeout())); + responseTimerTimeoutCounter = 0; + + resendCounter = 0; + +} + + +SendBuffer::~SendBuffer() +{ + delete(this->responseTimeoutTimer); +} + + + +/**************************************************************** + * public interface + * + ****************************************************************/ + +/** + * from vmc: sendMessage / insert Message in Message-Queue + * + * interactiveFlag: (default = true) + * Interactive commands are commands resulting from user input (e.g. button clicks). + * This commands are enqueued only if responseTimeoutTimer is not active. + * This ensures, that such messages are sent in sequence and only, if an answer + * from vmc was received. + * Non-interactive commands are data messages which are sent to vmc, caused e.g. by + * an internal status change. + * + */ +int SendBuffer::SendMessage(QByteArray & ba, bool enqueue) +{ + + // prevent queue for growing endlessly: + + // check size of queue + // if queue-size is greater than SB_MAX_SENDQUEUESIZE, remove oldest (first) element + + qDebug() << "SendBuffer::SendMessage() messageQueue.size() = " << this->messageQueue.size(); + + if (this->messageQueue.size() > SB_MAX_SENDQUEUESIZE) { + messageQueue.removeFirst(); + this->resendCounter = 0; + } + + // put message in queue + if (enqueue) + this->messageQueue.enqueue(ba); + + + /* interactive messages are sent only, if the response timer is not active. + * this should prevent successive sending of the same message during waiting + * for an answer from vmc. + */ + if (! this->responseTimeoutTimer->isActive()) { + + if(!enqueue) + this->messageQueue.enqueue(ba); + + // try to send message + this->PrivateSendMessage(); + } + + return 0; +} + + +/**************************************************************** + * private interface + * + */ + + +/**************************************************************** + * handle ACK/NACK response from vmc + * + */ +void SendBuffer::onReceiveResponse(quint8 response) +{ + if (this->messageQueue.size() == 0) return; + + // reset counter: + this->responseTimerTimeoutCounter = 0; + + //if (response == NACK) qDebug() << "onReceiveResponse() NACK"; + //if (response == ACK) qDebug() << "onReceiveResponse() ACK"; + qDebug() << "--- onReceiveResponse() messageQueue.size() = " << this->messageQueue.size(); + + + + /* resend command on NACK */ + if (response == NACK) { + + // stop timeoutTimer + this->responseTimeoutTimer->stop(); + + if (this->resendCounter > SB_MAX_RESEND) { + // command could not be processed on peer: remove it from message queue + this->messageQueue.removeFirst(); + this->resendCounter = 0; + } + else { + // else: resend command + if (this->messageQueue.size() != 0) this->PrivateSendMessage(); + } + + } + + /* send next command on ACK */ + if (response == ACK) { + + // stop timeoutTimer + this->responseTimeoutTimer->stop(); + + // remove old message from message-queue + this->messageQueue.removeFirst(); + this->resendCounter = 0; + + if (this->messageQueue.size() != 0) this->PrivateSendMessage(); + } + +} + +/* this timeout-handler is called, if no response (ACK/NACK) was received from vmc + */ +void SendBuffer::onResponseTimeoutTimerTimeout() +{ + + qDebug() << "onResponseTimeoutTimerTimeout() messageQueue.size() = " << this->messageQueue.size(); + + if (this->messageQueue.size() == 0) { + return; + } + + // wakeup vmc + emit this->sendError(SEND_ERROR::RESPONSE); + + if (this->resendCounter > SB_MAX_RESEND) { + this->messageQueue.removeFirst(); + this->resendCounter = 0; + this->responseTimerTimeoutCounter++; + this->responseTimeoutTimer->stop(); + emit this->sendError(SEND_ERROR::RESPONSE_TIMER_TIMEOUT); + if (this->responseTimerTimeoutCounter > 10) { + // switch to self out-of-order mode ... + emit this->sendError(SEND_ERROR::MULTIPLE_RESPONSE_TIMER_TIMEOUT); + } + // try to send an possible next message in order + // to empty queue: + if (!this->messageQueue.isEmpty()) { + PrivateSendMessage(); + } + } + + // send next message + else { + PrivateSendMessage(); + } +} + + +/* send first message in Message-Queue + * + * preconditon: at least one message is in queue! + */ +void SendBuffer::PrivateSendMessage() +{ + // take message from queue + QByteArray ba = this->messageQueue.head(); + + + // put bytes on serial line + for (int i = 0; i < ba.size(); i++) { + this->interface->putChar(ba.at(i)); + } + + // do logging here + emit this->writeLogSent(ba); + + + this->resendCounter++; + + // start timeoutTimer + this->responseTimeoutTimer->start(); + + +} diff --git a/src/ATBAPP/support/VMC/SendBuffer.h b/src/ATBAPP/support/VMC/SendBuffer.h new file mode 100644 index 0000000..9e7bb36 --- /dev/null +++ b/src/ATBAPP/support/VMC/SendBuffer.h @@ -0,0 +1,59 @@ +#ifndef SENDBUFFER_H +#define SENDBUFFER_H + +#include +#include +#include +#include "com_interface.h" + + +class QTimer; +class VMC; +enum class SEND_ERROR : quint8; + +#define SB_MAX_RESEND 3 +#define SB_MAX_SENDQUEUESIZE 10 + + +class SendBuffer : public QObject +{ + Q_OBJECT + +private: + VMC *vmc; + COM_interface *interface; + + QQueue messageQueue; + quint8 resendCounter; + + /* timer for resending messages to vmc, if no valid response is resent from vmc + */ + QTimer *responseTimeoutTimer; + quint8 responseTimerTimeoutCounter; + + void PrivateSendMessage(); + +public: + explicit SendBuffer(COM_interface *cinterface, QObject *parent = nullptr); + ~SendBuffer(); + + int SendMessage(QByteArray & ba, bool enqueue = false); + +signals: + void writeLogSent(QByteArray ba); + void sendError(SEND_ERROR error); + +public slots: + void onReceiveResponse(quint8 response); + void onResponseTimeoutTimerTimeout(); +}; + + +enum class SEND_ERROR : quint8 { + NO_ERROR = 0, + RESPONSE, + RESPONSE_TIMER_TIMEOUT, + MULTIPLE_RESPONSE_TIMER_TIMEOUT +}; + +#endif // SENDBUFFER_H diff --git a/src/ATBAPP/support/VMC/com_interface.cpp b/src/ATBAPP/support/VMC/com_interface.cpp new file mode 100644 index 0000000..7e3da7e --- /dev/null +++ b/src/ATBAPP/support/VMC/com_interface.cpp @@ -0,0 +1,303 @@ +#include "com_interface.h" +#include + +#if QT_VERSION >=0x050000 +#include +#else +#include "qextserialport.h" +#endif + +COM_interface::COM_interface(QObject *parent) : + QObject(parent) +{ +#if QT_VERSION >=0x050000 + port = new QSerialPort(this); +#else + port = new QextSerialPort(QextSerialPort::EventDriven, this); +#endif + + buffer = new QByteArray(); +} + + +COM_interface::~COM_interface() +{ + delete buffer; + delete port; +} + +/*************************************************************************** + * ************************************************************************* + * + * QT5 ( = QSerialPort) specific methodes + * + *************************************************************************** + ***************************************************************************/ +#if QT_VERSION >=0x050000 + +int COM_interface::open(const QString & portname, int baudrate) +{ + QSerialPort::BaudRate br = QSerialPort::Baud115200; + + switch (baudrate) { + case 9600: br = QSerialPort::Baud9600; + break; + case 57600: br = QSerialPort::Baud57600; + break; + default: br = QSerialPort::Baud115200; + break; + } + + qDebug() << "COM_interface::open(): baudrate = " << baudrate; + qDebug() << "COM_interface::open(): br = " << br; + + port->setPortName(portname); + port->setBaudRate(br); + port->setStopBits(QSerialPort::OneStop); + port->setParity(QSerialPort::NoParity); + port->setDataBits(QSerialPort::Data8); + + + if (port->open(QIODevice::ReadWrite) == true) { + connect(port, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + + qDebug() << "listening for data on" << port->portName(); + + emit connected(); + return 1; + } + else { + qDebug() << "device failed to open:"; + qDebug() << " device:" << portname; + qDebug() << " errorstring:" << port->errorString(); + return -1; + } +} + +int COM_interface::open(const QString & portname, int baudrate, int dataBits, int stopBits, int parity) +{ + QSerialPort::BaudRate br = QSerialPort::Baud115200; + QSerialPort::DataBits db = QSerialPort::Data8; + QSerialPort::StopBits sb = QSerialPort::OneStop; + QSerialPort::Parity par = QSerialPort::NoParity; + + switch (baudrate) { + case 9600: br = QSerialPort::Baud9600; + break; + case 57600: br = QSerialPort::Baud57600; + break; + default: br = QSerialPort::Baud115200; + break; + } + + //TODO Add if needed! + switch (dataBits) { + default: db = QSerialPort::Data8; + break; + } + + switch (stopBits) { + case 2: sb = QSerialPort::TwoStop; + break; + default: sb = QSerialPort::OneStop; + break; + } + + switch (parity) { + default: par = QSerialPort::NoParity; + break; + } + + qDebug() << "COM_interface::open(): baudrate = " << baudrate; + qDebug() << "COM_interface::open(): br = " << br; + qDebug() << "COM_interface::open(): databits = " << dataBits; + qDebug() << "COM_interface::open(): db = " << db; + qDebug() << "COM_interface::open(): stopbits = " << stopBits; + qDebug() << "COM_interface::open(): sb = " << sb; + qDebug() << "COM_interface::open(): parity = " << parity; + qDebug() << "COM_interface::open(): par = " << par; + + port->setPortName(portname); + port->setBaudRate(br); + port->setStopBits(sb); + //port->setFlowControl(FLOW_OFF); + port->setParity(par); + port->setDataBits(db); + + if (port->open(QIODevice::ReadWrite) == true) { + connect(port, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + + qDebug() << "listening for data on" << port->portName(); + + emit connected(); + return 1; + } + else { + qDebug() << "device failed to open:"; + qDebug() << " device:" << portname; + qDebug() << " errorstring:" << port->errorString(); + return -1; + } +} + + +/*************************************************************************** + * ************************************************************************* + * + * QT4 ( = QextSerialPort) specific methodes + * + *************************************************************************** + ***************************************************************************/ +#else + +int COM_interface::open(const QString & portname, int baudrate) +{ + BaudRateType br = BAUD115200; + switch (baudrate) { + case 9600: br = BAUD9600; + break; + case 57600: br = BAUD57600; + break; + default: br = BAUD115200; + break; + } + + qDebug() << "COM_interface::open(): baudrate = " << baudrate; + qDebug() << "COM_interface::open(): br = " << br; + + port->setPortName(portname); + port->setBaudRate(br); + port->setStopBits(STOP_1); + //port->setFlowControl(FLOW_OFF); + port->setParity(PAR_NONE); + port->setDataBits(DATA_8); + port->setTimeout(0); + + if (port->open(QIODevice::ReadWrite) == true) { + connect(port, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + connect(port, SIGNAL(dsrChanged(bool)), this, SLOT(onDsrChanged(bool))); + if (!(port->lineStatus() & LS_DSR)) + qDebug() << "warning: device is not turned on"; + qDebug() << "listening for data on" << port->portName(); + + emit connected(); + return 1; + } + else { + qDebug() << "device failed to open:"; + qDebug() << " device:" << portname; + qDebug() << " errorstring:" << port->errorString(); + return -1; + } +} + +int COM_interface::open(const QString & portname, int baudrate, int dataBits, int stopBits, int parity) +{ + BaudRateType br = BAUD115200; + DataBitsType db = DATA_8; + StopBitsType sb = STOP_1; + ParityType par = PAR_NONE; + + switch (baudrate) { + case 9600: br = BAUD9600; + break; + case 57600: br = BAUD57600; + break; + default: br = BAUD115200; + break; + } + + //TODO Add if needed! + switch (dataBits) { + default: db = DATA_8; + break; + } + + switch (stopBits) { + case 2: sb = STOP_2; + break; + default: sb = STOP_1; + break; + } + + switch (parity) { + default: par = PAR_NONE; + break; + } + + qDebug() << "COM_interface::open(): baudrate = " << baudrate; + qDebug() << "COM_interface::open(): br = " << br; + qDebug() << "COM_interface::open(): databits = " << dataBits; + qDebug() << "COM_interface::open(): db = " << db; + qDebug() << "COM_interface::open(): stopbits = " << stopBits; + qDebug() << "COM_interface::open(): sb = " << sb; + qDebug() << "COM_interface::open(): parity = " << parity; + qDebug() << "COM_interface::open(): par = " << par; + + port->setPortName(portname); + port->setBaudRate(br); + port->setStopBits(sb); + //port->setFlowControl(FLOW_OFF); + port->setParity(par); + port->setDataBits(db); + port->setTimeout(0); + + if (port->open(QIODevice::ReadWrite) == true) { + connect(port, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + connect(port, SIGNAL(dsrChanged(bool)), this, SLOT(onDsrChanged(bool))); + if (!(port->lineStatus() & LS_DSR)) + qDebug() << "warning: device is not turned on"; + qDebug() << "listening for data on" << port->portName(); + + emit connected(); + return 1; + } + else { + qDebug() << "device failed to open:"; + qDebug() << " device:" << portname; + qDebug() << " errorstring:" << port->errorString(); + return -1; + } +} +#endif + +int COM_interface::close(void) +{ + if (port->isOpen()) { + port->flush(); + //port->reset(); + port->close(); + port->reset(); + } + emit disconnected(); + + return 0; +} + +void COM_interface::onReadyRead(void) { + qint64 i; + i = 0; + buffer->clear(); + + while (1) { + char c; + if (port->read(& c, 1) <= 0) + break; + qDebug() << "com_interface::onReadyRead(): buffer[" << i << "]: '" << c << "'"; + buffer->insert(i++,c); + } + + emit ReadData(buffer); +} + + +void COM_interface::onDsrChanged(bool state) { + // DSR-line is not used with CLR200 reader + // so we do nothing on this signal. + qDebug() << "onDsrChanged(): state is " << state; +} + + +void COM_interface::putChar(const char c) { + port->putChar(c); +} diff --git a/src/ATBAPP/support/VMC/com_interface.h b/src/ATBAPP/support/VMC/com_interface.h new file mode 100644 index 0000000..814b93f --- /dev/null +++ b/src/ATBAPP/support/VMC/com_interface.h @@ -0,0 +1,53 @@ +#ifndef COM_INTERFACE_H +#define COM_INTERFACE_H + +#include + +#if QT_VERSION >=0x050000 +class QSerialPort; +#else +class QextSerialPort; +#endif + + +class COM_interface : public QObject +{ + Q_OBJECT + +private: +#if QT_VERSION >=0x050000 + QSerialPort *port; +#else + QextSerialPort *port; +#endif + QByteArray *buffer; + + +public: + explicit COM_interface(QObject *parent = nullptr); + ~COM_interface(); + + int open(const QString & portname, int baudrate); + int open(const QString & portname, int baudrate, int dataBits, int stopBits, int parity); + int close(void); + + void putChar(const char c); + + int isOpen(void); + + + +signals: + void ReadData(QByteArray *buffer); + void connected(void); + void disconnected(void); + + +public slots: + +private slots: + void onReadyRead(void); + void onDsrChanged(bool state); +}; + +#endif // COM_INTERFACE_H diff --git a/src/ATBAPP/support/VMC/vmc.cpp b/src/ATBAPP/support/VMC/vmc.cpp new file mode 100644 index 0000000..4aa69cb --- /dev/null +++ b/src/ATBAPP/support/VMC/vmc.cpp @@ -0,0 +1,1625 @@ +#include "vmc.h" +#include "AppControl.h" +#include "ATBHMIconfig.h" +#include "version.h" +#include +#include +#include +#include +#include + + +VMC::VMC(QObject *parent) : QObject(parent) { +} + + +VMC::VMC(AppControl *main, ATBHMIconfig *config, QObject *parent) : + QObject(parent) +{ + this->flag_blockVMCScreen = 0; + this->currentCachedScreen = VMC_CMD_SCREEN_START; + + this->main = main; + this->config = config; + + this->com_interface = new COM_interface(this); + this->receiveBuffer = new ReceiveBuffer(com_interface, this); + this->sendBuffer = new SendBuffer(com_interface, this); + + connect(receiveBuffer, SIGNAL(ReceiveResponse(quint8)), sendBuffer, SLOT(onReceiveResponse(quint8))); + + com_interface->open(config->getComPortName(), config->getComBaudRate()); + + connect(receiveBuffer, SIGNAL(ReceiveError(RECEIVE_ERROR)), this, SLOT(onReceiveError(RECEIVE_ERROR)), Qt::QueuedConnection); + connect(sendBuffer, SIGNAL(sendError(SEND_ERROR)), this, SLOT(onSendError(SEND_ERROR))); + + this->sendDelayTimer = new QTimer; + this->sendDelayTimer->setSingleShot(true); + connect(this->sendDelayTimer, SIGNAL(timeout()), this, SLOT(onDelayedMessageTimerTimeout())); + + // send start-command to vmc: + this->SendSystemMessageStart(); +} + + +VMC::~VMC() { + delete(this->sendBuffer); + delete(this->receiveBuffer); + delete(this->com_interface); +} + + +/******************************************************************************** + * S E N D M E S S A G E S t o V M C + * + * + */ + +int VMC::SendMessage(QString msg) +{ + QByteArray ba; + this->SendMessage(ba.append(msg)); + + return 0; +} + + +int VMC::SendMessage(QByteArray ba, bool enqueue) +{ + quint8 CRC = 0; + + + // calculate CRC + for (int i = 0; i < ba.size(); i++) { + CRC += ba.at(i); + } + ba.append(CRC); + + this->sendBuffer->SendMessage(ba, enqueue); + + return 0; +} + + +int VMC::SendACK() +{ + this->com_interface->putChar(ACK); + emit this->writeLogSent(QByteArray(1, ACK)); + return 0; +} + +int VMC::SendNACK() +{ + this->com_interface->putChar(NACK); + emit this->writeLogSent(QByteArray(1, NACK)); + return 0; +} + + +int VMC::sendButtonChar(QChar c) +{ + QByteArray msg; + + msg.append(QByteArray::fromHex("3E4531")); + msg.append((quint8)0x30 + (quint8)c.toLatin1()); + + return SendMessage(msg); +} + +int VMC::SendButton0() // 0 - 3E 45 31 30 E4 +{ + return SendMessage(QByteArray::fromHex("3E453130")); +} + +int VMC::SendButton1() // 1 - 3E 45 31 31 E5 +{ + return SendMessage(QByteArray::fromHex("3E453131")); +} + +int VMC::SendButton2() // 2 - 3E 45 31 32 E6 +{ + return SendMessage(QByteArray::fromHex("3E453132")); +} + +int VMC::SendButton3() // 3 - 3E 45 31 33 E7 +{ + return SendMessage(QByteArray::fromHex("3E453133")); +} + +int VMC::SendButton4() // 4 - 3E 45 31 34 E8 +{ + return SendMessage(QByteArray::fromHex("3E453134")); +} + +int VMC::SendButton5() // 5 - 3E 45 31 35 E9 +{ + return SendMessage(QByteArray::fromHex("3E453135")); +} + +int VMC::SendButton6() // 6 - 3E 45 31 36 EA +{ + return SendMessage(QByteArray::fromHex("3E453136")); +} + +int VMC::SendButton7() // 7 - 3E 45 31 37 EB +{ + return SendMessage(QByteArray::fromHex("3E453137")); +} + +int VMC::SendButton8() // 8 - 3E 45 31 38 EC +{ + return SendMessage(QByteArray::fromHex("3E453138")); +} + +int VMC::SendButton9() // 9 - 3E 45 31 39 ED +{ + return SendMessage(QByteArray::fromHex("3E453139")); +} + +int VMC::SendButtonNext() // 10 - 3E 45 31 3A EE +{ + return SendMessage(QByteArray::fromHex("3E45313A")); +} + +int VMC::SendButtonBack() // 11 - 3E 45 31 3B EF +{ + return SendMessage(QByteArray::fromHex("3E45313B")); +} + +int VMC::SendButtonCancel() // 12 - 3E 45 31 3C F0 +{ + return SendMessage(QByteArray::fromHex("3E45313C")); +} + +int VMC::SendButtonStart() // 13 - 3E 45 31 3D F1 +{ +#if defined (ARCH_PTU4) || defined(ARCH_PTU5) + // Start button wakes vmc: + emit wakeVMC(); +#endif + return SendMessage(QByteArray::fromHex("3E45313D"), true); +} + +int VMC::SendButtonStart2() // - 3E 45 31 A0 cs +{ +#if defined (ARCH_PTU4) || defined(ARCH_PTU5) + // Start button wakes vmc: + emit wakeVMC(); +#endif + return SendMessage(QByteArray::fromHex("3E4531A0")); +} + +int VMC::SendButtonStart3() // - 3E 45 31 A1 cs +{ +#if defined (ARCH_PTU4) || defined(ARCH_PTU5) + // Start button wakes vmc: + emit wakeVMC(); +#endif + return SendMessage(QByteArray::fromHex("3E4531A1")); +} + +int VMC::SendButtonStart4() // - 3E 45 31 A2 cs +{ +#if defined (ARCH_PTU4) || defined(ARCH_PTU5) + // Start button wakes vmc: + emit wakeVMC(); +#endif + return SendMessage(QByteArray::fromHex("3E4531A2")); +} + + +int VMC::SendButtonHelp() // 14 - 3E 45 31 3E F2 +{ + return SendMessage(QByteArray::fromHex("3E45313E")); +} + +int VMC::SendButtonTarif() // 15 - 3E 45 31 3F F3 +{ + return SendMessage(QByteArray::fromHex("3E45313F")); +} + +int VMC::SendButtonArrowLeft() // - 3E 45 31 83 cs +{ + return SendMessage(QByteArray::fromHex("3E453183")); +} + +int VMC::SendButtonArrowRight() // - 3E 45 31 84 cs +{ + return SendMessage(QByteArray::fromHex("3E453184")); +} + +int VMC::SendButtonArrowUp() // - 3E 45 31 85 cs +{ + return SendMessage(QByteArray::fromHex("3E453185")); +} + +int VMC::SendButtonArrowDown() // - 3E 45 31 86 cs +{ + return SendMessage(QByteArray::fromHex("3E453186")); +} + +int VMC::SendButtonCardTypeLeft() // - 3E 45 31 87 cs +{ + return SendMessage(QByteArray::fromHex("3E453187")); +} + +int VMC::SendButtonCardTypeRight() // - 3E 45 31 88 cs +{ + return SendMessage(QByteArray::fromHex("3E453188")); +} + +int VMC::SendButtonA() { // 'A' - 3E 45 31 41 F4 + return SendMessage(QByteArray::fromHex("3E453141")); +} + +int VMC::SendButtonB() { // 'B' - 3E 45 31 42 F5 + return SendMessage(QByteArray::fromHex("3E453142")); +} + +int VMC::SendButtonC() { // 'C' - 3E 45 31 43 F6 + return SendMessage(QByteArray::fromHex("3E453143")); +} + +int VMC::SendButtonD() { // 'D' - 3E 45 31 44 F7 + return SendMessage(QByteArray::fromHex("3E453144")); +} + +int VMC::SendButtonE() { // 'E' - 3E 45 31 45 F8 + return SendMessage(QByteArray::fromHex("3E453145")); +} + +int VMC::SendButtonF() { // 'F' - 3E 45 31 46 F9 + return SendMessage(QByteArray::fromHex("3E453146")); +} + +int VMC::SendButtonG() { // 'G' - 3E 45 31 47 FA + return SendMessage(QByteArray::fromHex("3E453147")); +} + +int VMC::SendButtonH() { // 'H' - 3E 45 31 48 FB + return SendMessage(QByteArray::fromHex("3E453148")); +} + +int VMC::SendButtonI() { // 'I' - 3E 45 31 49 FC + return SendMessage(QByteArray::fromHex("3E453149")); +} + +int VMC::SendButtonJ() { // 'J' - 3E 45 31 4A FD + return SendMessage(QByteArray::fromHex("3E45314A")); +} + +int VMC::SendButtonK() { // 'K' - 3E 45 31 4B FE + return SendMessage(QByteArray::fromHex("3E45314B")); +} + +int VMC::SendButtonL() { // 'L' - 3E 45 31 4C FF + return SendMessage(QByteArray::fromHex("3E45314C")); +} + +int VMC::SendButtonM() { // 'M' - 3E 45 31 4D 00 + return SendMessage(QByteArray::fromHex("3E45314D")); +} + +int VMC::SendButtonN() { // 'N' - 3E 45 31 4E 01 + return SendMessage(QByteArray::fromHex("3E45314E")); +} + +int VMC::SendButtonO() { // 'O' - 3E 45 31 4F 02 + return SendMessage(QByteArray::fromHex("3E45314F")); +} + +int VMC::SendButtonP() { // 'P' - 3E 45 31 50 03 + return SendMessage(QByteArray::fromHex("3E453150")); +} + +int VMC::SendButtonQ() { // 'Q' - 3E 45 31 51 04 + return SendMessage(QByteArray::fromHex("3E453151")); +} + +int VMC::SendButtonR() { // 'R' - 3E 45 31 52 05 + return SendMessage(QByteArray::fromHex("3E453152")); +} + +int VMC::SendButtonS() { // 'S' - 3E 45 31 53 06 + return SendMessage(QByteArray::fromHex("3E453153")); +} + +int VMC::SendButtonT() { // 'T' - 3E 45 31 54 07 + return SendMessage(QByteArray::fromHex("3E453154")); +} + +int VMC::SendButtonU() { // 'U' - 3E 45 31 55 08 + return SendMessage(QByteArray::fromHex("3E453155")); +} + +int VMC::SendButtonV() { // 'V' - 3E 45 31 56 09 + return SendMessage(QByteArray::fromHex("3E453156")); +} + +int VMC::SendButtonW() { // 'W' - 3E 45 31 57 0A + return SendMessage(QByteArray::fromHex("3E453157")); +} + +int VMC::SendButtonX() { // 'X' - 3E 45 31 58 0B + return SendMessage(QByteArray::fromHex("3E453158")); +} + +int VMC::SendButtonY() { // 'Y' - 3E 45 31 59 0C + return SendMessage(QByteArray::fromHex("3E453159")); +} + +int VMC::SendButtonZ() { // 'Z' - 3E 45 31 5A 0D + return SendMessage(QByteArray::fromHex("3E45315A")); +} + +int VMC::SendButtonUE() { // 'Ü' - 3E 45 31 DC 90 - ISO 8859-1 (Latin-1): 'Ü' + return SendMessage(QByteArray::fromHex("3E4531DC")); +} + +int VMC::SendButtonOE() { // 'Ö' - 3E 45 31 D6 8a - ISO 8859-1 (Latin-1): 'Ö' + return SendMessage(QByteArray::fromHex("3E4531D6")); +} + +int VMC::SendButtonAE() { // 'Ä' - 3E 45 31 C4 78 - ISO 8859-1 (Latin-1): 'Ä' + return SendMessage(QByteArray::fromHex("3E4531C4")); +} + + +int VMC::SendButtonDEL() { // DEL - 3E 45 31 7F 33 + return SendMessage(QByteArray::fromHex("3E45317F")); +} + +int VMC::SendButtonDash() { // '-' - 3E 45 31 2D E1 + return SendMessage(QByteArray::fromHex("3E45312D")); +} + +int VMC::SendButtonSpace() { // ' ' - 3E 45 31 20 D4 + return SendMessage(QByteArray::fromHex("3E453120")); +} + +int VMC::SendButtonClose() { // - 3E 45 31 80 cs + return SendMessage(QByteArray::fromHex("3E453180")); +} + +int VMC::SendButtonYes() { // - 3E 45 31 81 cs + return SendMessage(QByteArray::fromHex("3E453181")); +} + +int VMC::SendButtonNo() { // - 3E 45 31 82 cs + return SendMessage(QByteArray::fromHex("3E453182")); +} + +int VMC::SendButtonVehicleDefintion1() { // 0x90 - 3E 45 31 90 cs + return SendMessage(QByteArray::fromHex("3E453190")); +} + +int VMC::SendButtonVehicleDefintion2() { // 0x91 - 3E 45 31 91 cs + return SendMessage(QByteArray::fromHex("3E453191")); +} + +int VMC::SendButtonVehicleDefintion3() { // 0x92 - 3E 45 31 92 cs + return SendMessage(QByteArray::fromHex("3E453192")); +} + +int VMC::SendButtonVehicleDefintion4() { // 0x93 - 3E 45 31 93 cs + return SendMessage(QByteArray::fromHex("3E453193")); +} + +int VMC::SendButtonVehicleDefintion5() { // 0x94 - 3E 45 31 94 cs + return SendMessage(QByteArray::fromHex("3E453194")); +} + +int VMC::SendButtonVehicleDefintion6() { // 0x95 - 3E 45 31 95 cs + return SendMessage(QByteArray::fromHex("3E453195")); +} + +int VMC::SendButtonVehicleDefintion7() { // 0x96 - 3E 45 31 96 cs + return SendMessage(QByteArray::fromHex("3E453196")); +} + +int VMC::SendButtonVehicleDefintion8() { // 0x97 - 3E 45 31 97 cs + return SendMessage(QByteArray::fromHex("3E453197")); +} + +int VMC::SendButtonPaymethode1() { // 0x98 - 3E 45 31 98 cs + return SendMessage(QByteArray::fromHex("3E453198")); +} + +int VMC::SendButtonPaymethode2() { // 0x99 - 3E 45 31 99 cs + return SendMessage(QByteArray::fromHex("3E453199")); +} + +int VMC::SendButtonPaymethode3() { // 0x9a - 3E 45 31 9a cs + return SendMessage(QByteArray::fromHex("3E45319a")); +} + +int VMC::SendButtonPaymethode4() { // 0x9b - 3E 45 31 9b cs + return SendMessage(QByteArray::fromHex("3E45319b")); +} + + +int VMC::SendSystemMessage(quint8 nr) +{ + QByteArray msg; + + msg.append(QByteArray::fromHex("3EF031")); + msg.append((quint8)0x30 + (quint8)nr); + return SendMessage(msg); +} + + +int VMC::SendSystemMessageStart() +{ + return this->SendSystemMessage(1); +} + +int VMC::SendSystemMessageTimeout() +{ + return this->SendSystemMessage(2); +} + +int VMC::SendLangSwitch(quint8 nr) +{ + QByteArray msg; + + msg.append(QByteArray::fromHex("3E4631")); + msg.append((quint8)0x30 + (quint8)nr); + return SendMessage(msg); +} + + +/* Send last line of Service Menu to VMC + * we send 20 characters + * + * hmi -> vmc: TODO + */ +int VMC::SendButtonServiceOK(QString MessageString) +{ + QString msg = MessageString.leftJustified(20,' ',true); + + + QByteArray message(QByteArray::fromHex("3E1014")); // TODO + message.append(msg.toLatin1()); + SendMessage(message); + + return 1; +} + + +/* Send Version String to VMC + * currently only 20 characters are sent. + * + * vmc -> hmi: 3E 10 31 31 B0 + * hmi -> vmc: 3E 10 14 56 30 2E 30 32 2E 37 2F 32 30 31 31 30 37 30 34 20 20 20 20 1B + */ +int VMC::SendVersion(QString VersionString) { + VersionString.truncate(20); + QByteArray message(QByteArray::fromHex("3E1014")); + message.append(VersionString.toLatin1()); + SendMessage(message); + + return 1; +} + + +/* Send Current PTU state + * currently only 20 characters are sent. + * + * vmc -> hmi: 3E 11 31 31 B1 + * hmi -> vmc: 3E 11 32 CM CS cs + */ +int VMC::SendCurrentState() +{ + QByteArray message(QByteArray::fromHex("3E1132")); + message.append(static_cast(main->getCurrentProgramMode())); + SendMessage(message); + + return 1; +} + +/* Send lic plate String to VMC + */ +int VMC::SendLicPlate(QString licplate) { + QString msg = licplate.leftJustified(9,' ',true); + QByteArray message(QByteArray::fromHex("3E4739")); + message.append(msg.toLatin1()); + //message.append(msg.toUtf8()); -> alternative: send Utf8 + SendMessage(message); + + return 1; +} + +/* Send generated PIN string to VMC + */ +int VMC::SendGeneratedPinNumber(QString pin) +{ + // DEBUG: + qDebug() << "SendGeneratedPinNumber(): PIN = " << pin; + + QByteArray message(QByteArray::fromHex("3E4735")); + message.append(pin.toLatin1()); + SendMessage(message, true); + + return 1; +} + + +/* Send barcode String to VMC + * + * This usese the same interface command like licence plate. + * Currently, we just transmit more charracters, e.g. 13 chars for EAN-13 code. + * This can be advanced to configure code settings (e.g. EAN-8, ...) + * + * Note: on changing string lenght (e.g. from 13 to 8) one must also adapt + * the lenght code in message (byte #3 = 0x30 + lenght). + * E.g. EAN-13: length = 13 characters + * => lenght code = 0x30 + 13 = 0x3D + */ +int VMC::SendBarcode(QString barcode) { + QString msg = barcode.leftJustified(13,' ',true); + QByteArray message(QByteArray::fromHex("3E473D")); + message.append(msg.toLatin1()); + SendMessage(message); + //SendMessageDelayed(message, 300); + + return 1; +} + + + +/** + * @brief VMC::SendFormatedString + * just to send a Latin1-string to vmc. + * @param formatString + * @return 1 on success + */ +int VMC::SendFormatedString(QString formatString) +{ + if (formatString.length() == 0) return 0; + + QByteArray message(QByteArray::fromHex("3ED0")); + message.append((quint8)0x30 + (quint8)formatString.length()); + message.append(formatString.toLatin1()); + SendMessage(message, true); + + return 1; +} + +int VMC::SendLongFormatedStringReference(QString & formatedString) +{ + if (formatedString.length() == 0) return 0; + + QByteArray message(QByteArray::fromHex("3ED1")); + message.append((quint8)(formatedString.length() & 0x00FF)); + message.append((quint8)((formatedString.length() >> 8) & 0x00FF)); + message.append(formatedString.toLatin1()); + SendMessage(message, true); + + return 1; +} + +int VMC::SendLongFormatedString(QString formatString) { + if (formatString.length() == 0) return 0; + + QByteArray message(QByteArray::fromHex("3ED1")); + message.append((quint8)(formatString.length() & 0x00FF)); + message.append((quint8)((formatString.length() >> 8) & 0x00FF)); + message.append(formatString.toLatin1()); + SendMessage(message, true); + + return 1; +} + + +int VMC::SendFormatedStringError(QString errorString) +{ + if (errorString.length() == 0) return 0; + + QByteArray message(QByteArray::fromHex("3ED0")); + message.append((quint8)0x30 + (quint8)errorString.length()); + message.append(errorString.toLatin1()); + SendMessage(message, true); + + return 1; +} + + +/********************************************************************* + * send bewonerscode data to vmc. + * + */ +int VMC::SendId(QString id) +{ + QByteArray message(QByteArray::fromHex("3E56")); + message.append((quint8)0x30 + (quint8)id.length()); + message.append(id.toLatin1()); + SendMessage(message, true); + + return 1; +} + +int VMC::SendCode(QString code) +{ + // code is filled with ' ' to 20 chars: + QString msg = code.leftJustified(20,' ',true); + QByteArray message(QByteArray::fromHex("3E5744")); + message.append(msg.toLatin1()); + SendMessage(message); + + return 1; +} + +/********************************************************************* + * send ilogs selling data to vmc. + * + * TODO: Format of data (e.g. timestamps) + * * this methods are deprecated * + * * use SendFormatedString() instead. + * + */ +int VMC::SendAmountDueTax(QString amount) +{ + QByteArray message(QByteArray::fromHex("3E53")); + message.append((quint8)0x30 + (quint8)amount.length()); + message.append(amount.toLatin1()); + SendMessage(message, true); + + return 1; +} + +int VMC::SendAmountDueNet(QString amount) +{ + QByteArray message(QByteArray::fromHex("3E51")); + message.append((quint8)0x30 + (quint8)amount.length()); + message.append(amount.toLatin1()); + SendMessage(message, true); + + return 1; +} + +int VMC::SendAmountDuePeriodStart(QString startTime) +{ + QByteArray message(QByteArray::fromHex("3E54")); + message.append((quint8)0x30 + (quint8)startTime.length()); + message.append(startTime.toLatin1()); + SendMessage(message, true); + + return 1; +} + +int VMC::SendAmountDuePeriodEnd(QString endTime) +{ + QByteArray message(QByteArray::fromHex("3E50")); + message.append((quint8)0x30 + (quint8)endTime.length()); + message.append(endTime.toLatin1()); + SendMessage(message, true); + + return 1; +} + +int VMC::SendCracePeriod(QString minute) +{ + QByteArray message(QByteArray::fromHex("3E49")); + message.append((quint8)0x30 + (quint8)minute.length()); + message.append(minute.toLatin1()); + SendMessage(message, true); + + return 1; +} + + + +/********************************************************************* + * send delayed message + * + * note, consecutive calls on this would possibly overwrite an already + * message, so please use this methode rarely and with care. + */ +int VMC::SendMessageDelayed(QString msg, int delay) +{ + this->delayedMessage = msg; + this->sendDelayTimer->start(delay); + + return 1; +} +void VMC::onDelayedMessageTimerTimeout() +{ + this->SendMessage(this->delayedMessage); +} + + + + + + +/********************************************************************* + * get vmc state + */ +quint16 VMC::getCurrentCachedScreen() +{ + return this->currentCachedScreen; +} + + +/********************************************************************* + * H A N D L E M E S S A G E S f r o m V M C + * + */ + + +void VMC::onReceiveError(RECEIVE_ERROR error) +{ + switch (error) { + case RECEIVE_ERROR::CRC: + case RECEIVE_ERROR::INVALID_COMMAND: + SendNACK(); + break; + case RECEIVE_ERROR::NO_COMMAND: + default: return; + + } +} + + + +void VMC::CommandAvaliable(QByteArray message) +{ + // DEBUG ///////////////////////////////////////////////////// + /* + QString hexString = "received: "; + qint64 i; + + for (i=0; i < message.size(); i++) { + hexString.append((QString(message.at(i))).toLatin1().toHex()); + hexString.append(' '); + } + hexString.chop(1); + + qDebug() << "VMC::CommandAvaliable(): message.size(): " << message.size() << ""; + qDebug() << "VMC::CommandAvaliable(): message: " << hexString << ""; + */ + ////////////////////////////////////////////////////////////// + + emit writeLogReceived(message); + + // now, our message starts with '3e' and has a correct CRC on the end: + + switch ((quint8)message.at(1)) { + case VMC_CMD_SCREEN: + + // set current cached screen only for certain screens (potential idle screens) + // NOTE: this has to be checked carefully in customer projects + switch ((quint8)message.at(3)) { + case VMC_CMD_SCREEN_START: + case VMC_CMD_SCREEN_FREEPARK: + case VMC_CMD_SCREEN_DEFBUTTONS: + case VMC_CMD_SCREEN_DEFBUTTONS2: + case VMC_CMD_SCREEN_DEFBUTTONS3: + // cache current idle screen + this->currentCachedScreen = static_cast(message.at(3)); + break; + case VMC_CMD_SCREEN_DIAG: + // unblock if we go to service mode + this->flag_blockVMCScreen = 0; + break; + default: + break; + } + break; + case VMC_CMD_TEXT: + parseTextCommand(message); + SendACK(); + break; + case VMC_CMD_TEXT_ENC: + parseTextCommandEnc(message); + SendACK(); + break; + case VMC_CMD_USER_MESSAGE: + parseUserMessageCommand(message); + SendACK(); + break; + case VMC_CMD_SYSTEM: + parseSystemCommand(message); + SendACK(); + break; + case VMC_CMD_MACHINE_NR: + break; + case VMC_CMD_PSA_CONFIG: + parsePSAConfigCommand(message); + SendACK(); + break; + case VMC_CMD_LANGUAGE: + parseLanguageCommand(message); + SendACK(); + break; + case VMC_CMD_END_PRG: + break; + case VMC_CMD_SW_VERSION: + SendVersion(ATB_QT_VERSION); + break; + case VMC_CMD_STATUS_REQUEST: + SendCurrentState(); + break; + case VMC_CMD_DISP_LICENCE_PLATE: + parseDataDisplayCommand(message); + SendACK(); + break; + case VMC_CMD_DISP_PRODUCT: + parseDataDisplayCommand(message); + SendACK(); + break; + case VMC_CMD_DISP_PARKTIME_END: + parseDataDisplayCommand(message); + SendACK(); + break; + case VMC_CMD_DISP_PRICE: + parseDataDisplayCommand(message); + SendACK(); + break; + case VMC_CMD_DATE_TIME: + parseDateCommand(message); + SendACK(); + break; + case VMC_CMD_DISP_LINE1: + parseDataDisplayCommand(message); + SendACK(); + break; + case VMC_CMD_DISP_LINE2: + parseDataDisplayCommand(message); + SendACK(); + break; + case VMC_CMD_DISP_AMOUNT_TO_PAY: + parseDataDisplayCommand(message); + SendACK(); + break; + case VMC_CMD_BUSY: + parseBusyCommand(message); + SendACK(); + break; + case VMC_CMD_MESSAGE_BOX: + parseShowMessageBoxCommand(message); + SendACK(); + break; + case VMC_CMD_SCREEN_CFG: + parseScreenConfigCommand(message); + SendACK(); + break; + case VMC_CMD_VMC_SYSTEM: + parseVMCSystemMessage(message); + SendACK(); + break; + case VMC_CMD_FORMATED_STRING: + parseVMCFormatedString(message); + SendACK(); + break; + default: + SendNACK(); + break; + } + + emit CommandProcessed(); +} + + +/* System commands: + * vmc -> hmi: 3E 63 32 xx xx cr + * hmi -> vmc: ACK/NACK + * + * LED-control: + * vmc -> hmi: 3E 63 36 xx xx xx xx xx xx cr + * hmi -> vmc: ACK/NACK + */ +quint16 VMC::parseSystemCommand(QByteArray &cmd) +{ + quint16 iCommand = 0; + + if( (cmd.at(2) - 0x30) == 0x02) { + iCommand = (quint8)cmd.at(4) + ((quint16)(cmd.at(3) << 8)); + emit this->sysCommand(iCommand, QByteArray()); + } + else + if( (cmd.at(2) - 0x30) == 0x06) { + iCommand = (quint16)(cmd.at(2) << 8); + emit this->sysCommand(iCommand, cmd.mid(3,7)); + // note: there is no length check here because + // cmd is already checked (lenght and checksum) + } + + return 1; +} + + + +/* Text setting command: + * vmc -> hmi: 3E 60 sc tx la lg .. .. .. .... CRS + * sc = screen nr + * tx = text nr + * la = language + * lg = legth + * hmi -> vmc: identical message + */ +quint8 VMC::parseTextCommand(QByteArray &cmd) +{ + if (cmd.size() < 7) return 0; + + quint8 screennr = cmd.at(2); + quint8 textnr = cmd.at(3); + quint8 lang = cmd.at(4)-0x30+1; + + quint8 size = cmd.at(5)-0x30; + + if (size > cmd.size() - 7) { size = cmd.size() - 7; } + + QTextCodec *codec = QTextCodec::codecForName(config->getDefaultCharacterSet()); + QString text = codec->toUnicode(cmd.mid(6,size)); + + qDebug() << "VMC::parseTextCommand() screennr = " << QString::number(screennr, 16); + qDebug() << "VMC::parseTextCommand() textnr = " << QString::number(textnr, 16); + qDebug() << "VMC::parseTextCommand() lang = " << QString::number(lang, 16); + //qDebug() << "VMC::parseTextCommand() size = " << QString::number(size, 16); + //qDebug() << "VMC::parseTextCommand() text = " << text; + + // remove whitespace from text start/end except if the text is only one sign! + // -> this is e.g. for setting one space sign + + return 1; +} + + + + + + +/* Text setting command: + * vmc -> hmi: 3E 61 en sc tx la lg .. .. .. .... CRS + * en = encoding + * sc = screen nr + * tx = text nr + * la = language + * lg = legth + * hmi -> vmc: identical message + */ +quint8 VMC::parseTextCommandEnc(QByteArray &cmd) +{ + if (cmd.size() < 8) return 0; + + quint8 encoding = cmd.at(2); + quint8 screennr = cmd.at(3); + quint8 textnr = cmd.at(4); + quint8 lang = cmd.at(5)-0x30+1; + + quint8 size = cmd.at(6)-0x30; + + if (size > cmd.size() - 8) { size = cmd.size() - 8; } + + QByteArray codecName = "ISO 8859-15"; + + switch (encoding) { + case 0x01: codecName = "ISO 8859-1"; break; + case 0x02: codecName = "ISO 8859-2"; break; + case 0x03: codecName = "ISO 8859-3"; break; + case 0x04: codecName = "ISO 8859-4"; break; + case 0x05: codecName = "ISO 8859-5"; break; + case 0x06: codecName = "ISO 8859-6"; break; + case 0x07: codecName = "ISO 8859-7"; break; + case 0x08: codecName = "ISO 8859-8"; break; + case 0x09: codecName = "ISO 8859-9"; break; + case 0x0A: codecName = "ISO 8859-10"; break; + case 0x0D: codecName = "ISO 8859-13"; break; + case 0x0E: codecName = "ISO 8859-14"; break; + case 0x0F: codecName = "ISO 8859-15"; break; + + case 0x20: codecName = "Windows-1250"; break; + case 0x21: codecName = "Windows-1251"; break; + case 0x22: codecName = "Windows-1252"; break; + case 0x23: codecName = "Windows-1253"; break; + case 0x24: codecName = "Windows-1254"; break; + case 0x25: codecName = "Windows-1255"; break; + case 0x26: codecName = "Windows-1256"; break; + case 0x27: codecName = "Windows-1257"; break; + case 0x28: codecName = "Windows-1258"; break; + + case 0x30: codecName = "KOI8-R"; break; + case 0x31: codecName = "KOI8-U"; break; + + case 0xfe: codecName = "UTF-32"; break; + case 0xff: codecName = "UTF-8"; break; + + default: codecName = "ISO 8859-15"; break; + } + + QTextCodec *codec = QTextCodec::codecForName(codecName); + QString text = codec->toUnicode(cmd.mid(7,size)); + + qDebug() << "VMC::parseTextCommandEnc() encoding = " << QString(codecName); + qDebug() << "VMC::parseTextCommandEnc() screennr = " << QString::number(screennr, 16); + qDebug() << "VMC::parseTextCommandEnc() textnr = " << QString::number(textnr, 16); + qDebug() << "VMC::parseTextCommandEnc() lang = " << QString::number(lang, 16); + qDebug() << "VMC::parseTextCommandEnc() size = " << QString::number(size, 16); + qDebug() << "VMC::parseTextCommandEnc() text = " << text; + + // remove whitespace from text start/end except if the text is only one sign! + // -> this is e.g. for setting one space sign + + return 1; +} + + + + +/* status data to display: + * vmc -> hmi: 3E
.. .. .. .... CRS + * dd = info data = { 48 | 49 | 50 | 51 | 53 | 54 | 56 } -> note 52 is command date/time + * lg = lenth + * hmi -> vmc: identical message + */ +quint8 VMC::parseDataDisplayCommand(QByteArray &cmd) +{ + if (cmd.size() < 4) return 0; + + quint8 infoField = cmd.at(1); + quint8 size = cmd.at(2)-0x30; + + if (size > cmd.size() - 4) { size = cmd.size() - 4; } + + QTextCodec::ConverterState state; + QTextCodec *codec = QTextCodec::codecForName("UTF-8"); + //QString text = codec->toUnicode(cmd.mid(3,size)); + + // detect whether we got unicode string or VMC legacy format string + const QByteArray data(cmd.mid(3,size)); + QString text = codec->toUnicode(data.constData(), data.size(), &state); + if (state.invalidChars > 0) { + // Not a UTF-8 text + QTextCodec *defaultCodec = QTextCodec::codecForName(config->getDefaultCharacterSet()); + text = defaultCodec->toUnicode(data); + } + + #if defined (USE_PTU_TARIFF) + // ignore some vmc data commands + if ( (infoField == 0x51) || // amount + (infoField == 0x50) || // parking endtime + (infoField == 0x48) ) // license plate + { + return 0; + } + #endif + + if (infoField == 0x56) { // display amount to pay + this->privateHandleDataCommand_AmountToPay(infoField, text); + } + + // Amount --------------------------------------------------------------------------- + if (infoField == 0x51) { // display amount + this->privateHandleDataCommand_Amount(infoField, text); + + #if defined (CUST00309) + // Neuharlingersiel + + // Workarround for display correct amount in display! + // -> text does not contain a '.' or ',' + + if ( (text.contains('.')) or + (text.contains(',')) ) { + int separatorIndex = text.indexOf("00 "); + if (separatorIndex >= 0) { + text = text.insert(separatorIndex,','); + } + else { + text = text.insert(-2,','); + } + } + #endif + + } // END AMOUNT + + + #if defined (CUST00276) + // Brombachsee ------------------------------------------------------------------------- + if (infoField == 0x50) + { + // check format: "hh:mm dd.mm.YYYY" + QDateTime endTime = QDateTime::fromString(text, "hh:mm dd.MM.yyyy"); + if (endTime.isValid()) { + // set vendingData + emit setVendingData("DATETIME_PARKEND", endTime.toString("yyyy-MM-dd HH:mm:ss").toLatin1()); + + if (config->hasFeatureShowParkingTime()) { + + // calculate time difference to current time: + int secondsTo = QDateTime::currentDateTime().secsTo(endTime); + + int days = (int) secondsTo / 86400; + int restSeconds = secondsTo % 86400; + + int hours = (int) restSeconds / 3600; + restSeconds = restSeconds % 3600; + + int minutes = (int) restSeconds / 60; + + // round to full hour: (<- dirty hack!) + if (minutes > 56) { + hours++; + minutes = 0; + } + // round to full day: (<- dirty hack!) + if (hours == 23) { + days++; + hours = 0; + } + + // TODO: get format string from text file (e.g. screen 0x50 (common)) + //text = QString("%1 Tag(e) %2 Stunde(n) %3 min").arg(days).arg(hours).arg(minutes); + + QString dayText = ""; + if (days == 1) dayText = "1 Tag "; + if (days > 1) dayText = QString("%1 Tage ").arg(days); + + QString hourText = ""; + if (hours == 1) hourText = "1 Stunde "; + if (hours > 1) hourText = QString("%1 Stunden ").arg(hours); + + QString minuteText = ""; + if (minutes > 0) minuteText = QString("%1 min").arg(minutes); + + text = dayText + hourText + minuteText; + + if (secondsTo <= 0) { text = "date error"; } + } // END: if hasFeatureShowParkingTime + } // END: if endTime.isValid + else { + text.remove(QChar::Null); + text = text.trimmed(); + } + } + if (infoField == 0x51) { + text.remove(QChar::Null); + text = text.simplified(); + } + // ------------------------------------------------------------------------------------ + #else + + // check format: "hh:mm dd.mm.YYYY" + QDateTime endTime = QDateTime::fromString(text, "hh:mm dd.MM.yyyy"); + if (endTime.isValid()) { + // set vendingData + emit setVendingData("DATETIME_PARKEND", endTime.toString("yyyy-MM-dd HH:mm:ss").toLatin1()); + } + + #endif + + + + qDebug() << "VMC::parseDataDisplayCommand() infoField = " << QString::number(infoField, 16); + + emit displayData(infoField, text); + + emit retriggerModeSell(); + + return 1; +} + + +/** + * @brief VMC::privateHandleDataCommand_Amount + * @param data + * @param dataString + * @return + * + * Used to set global vendingData "AMOUNT" + * Note: this should not be used on systems with PTU price calculation because on this + * systems "AMOUNT" is calculated and managed internally. + * This method however will possibly orverwrite an internal generated "AMOUNT". + * Using this method for setting "AMOUNT" is for + * - testing / simulation + * - for legacy projects + */ +QString & VMC::privateHandleDataCommand_Amount(quint8 data, QString & dataString) +{ + Q_UNUSED(data) + + // check amount string: + // this is normaly a string: "xxx,yy ZZ" + // we need amount as a string in cent. + + // DEBUG / SIMULATE card payments - for testing live terminals + //text = QString::fromUtf8("0,01 \u20AC"); + + // trim string + QString internalAmount = dataString.trimmed(); + + // remove currency symbol from string: + internalAmount = internalAmount.split(' ').first(); + + // starts with one or more number chars (digit), + // followed by 0 or one of the chars ',' or '.' + // ending with 0,1 or 2 number chars (digits) + QString pattern = "^\\d+[,\\.]?\\d{0,2}$"; + + + if(!QRegExp(pattern).exactMatch(internalAmount)) { + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "VMC::parseDataDisplayCommand: amount format error: " + << internalAmount; + } + + // get separator '.' or ',' + if (internalAmount.contains(',')) { + if (internalAmount.split(',').last().size() == 1) internalAmount.append("0"); + internalAmount.remove(','); + } + else + if (internalAmount.contains('.')) { + if (internalAmount.split('.').last().size() == 1) internalAmount.append("0"); + internalAmount.remove('.'); + } + else { + // in that case, we assume always that amount is in hundreds of units: + qCritical() << " ... consider amount as hundreds of units: " << internalAmount; + } + + // set vendingData: + emit setVendingData("AMOUNT", internalAmount.toLatin1()); + + return dataString; +} + +/** + * @brief VMC::privateHandleDataCommand_AmountToPay + * @param data + * @param dataString + * @return + * + * Used to set global vendingData "PaymentType" + */ +QString & VMC::privateHandleDataCommand_AmountToPay(quint8 data, QString & dataString) +{ + Q_UNUSED(data) + + emit this->setVendingData("PaymentType", QByteArray("0")); // CoinPayment + + QString pattern1 = "^\\d+$"; // only digits -> hundredth + QString pattern2 = "^\\d+[,\\.]+\\d{0,2}$"; // digits, separated by ',' or '.' + QString pattern3 = "^\\d+[,\\.]+\\d{0,2}\\s+\\D+$"; // digits with currency symbol + + + if(QRegExp(pattern1).exactMatch(dataString)) { + //qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + // << "VMC::privateHandleDataCommand_AmountToPay: pattern1: " + // << dataString; + + // insert decimal separator ',' + dataString.insert(-2,','); + + // append currency symbol + dataString.append(" "); + dataString.append(this->config->getPaymentCurrencySymbol()); + } + else + if(QRegExp(pattern2).exactMatch(dataString)) { + //qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + // << "VMC::privateHandleDataCommand_AmountToPay: pattern2: " + // << dataString; + + // fill: two digits behind decimal separator + if (dataString.contains(',')) { + if (dataString.split(',').last().size() == 1) dataString.append("0"); + if (dataString.split(',').last().size() == 0) dataString.append("00"); + } + else + if (dataString.contains('.')) { + if (dataString.split('.').last().size() == 1) dataString.append("0"); + if (dataString.split(',').last().size() == 0) dataString.append("00"); + } + + // add currency symbol + dataString.append(" "); + dataString.append(this->config->getPaymentCurrencySymbol()); + } + else + if(QRegExp(pattern3).exactMatch(dataString)) { + //qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + // << "VMC::privateHandleDataCommand_AmountToPay: pattern3: " + // << dataString; + + + } + else { + qCritical() << QDateTime::currentDateTime().toString(Qt::ISODate) + << "VMC::privateHandleDataCommand_AmountToPay: ERROR pattern: " + << dataString; + dataString = "ErrorPattern"; + } + + return dataString; +} + + + + + + +/* date command from vmc: set date and time + * vmc -> hmi: 3E 52 36 yy mm dd hh mm ss CRS + * hmi -> vmc: 3E 53 36 yy mm dd hh mm ss CRS + * + * Datestring format: YYYY-MM-DD hh:mm[:ss] + */ +quint8 VMC::parseDateCommand(QByteArray &cmd) +{ + if (cmd.size() < 10) return 0; + if (cmd.at(2) != 0x36) return 0; + + QString yy = QString("20%1").arg((ushort)cmd.at(3),0,10); + QString mm = QString("%1").arg((ushort)cmd.at(4),0,10); + QString dd = QString("%1").arg((ushort)cmd.at(5),0,10); + QString hh = QString("%1").arg((ushort)cmd.at(6),0,10); + QString mi = QString("%1").arg((ushort)cmd.at(7),0,10); + QString ss = QString("%1").arg((ushort)cmd.at(8),0,10); + + QString dateTimeString = "\"" + yy + "-" + + mm + "-" + + dd + " " + + hh + ":" + + mi + ":" + + ss + "\""; + + + emit newDateTime(dateTimeString); + + qDebug() << "VMC::parseDateCommand() datetimeCMD: " << dateTimeString; + + return 1; +} + + +quint8 VMC::parseUserMessageCommand(QByteArray &cmd) +{ + if (cmd.size() < 5) return 0; + if (cmd.at(2) != 0x32) return 0; + + emit userMessage(cmd.at(3), cmd.at(4)); + + return 1; +} + + +quint8 VMC::parseLanguageCommand(QByteArray &cmd) +{ + if (cmd.size() < 5) return 0; + if (cmd.at(2) != 0x32) return 0; + + quint8 lang = cmd.at(3); + quint8 flag = cmd.at(4); + + if (lang > 0x04) return 0; + if (flag > 0x02) return 0; + + + switch (flag) { + case 0x00: emit setLanguage(lang); + break; + case 0x01: emit setLanguage(lang); + emit setDefaultLanguage(lang); + break; + case 0x02: emit setDefaultLanguage(lang); + break; + default: return 0; + } + + return 1; +} + + +quint8 VMC::parseBusyCommand(QByteArray & cmd) +{ + if (cmd.size() < 4) return 0; + if (cmd.at(2) != 0x31) return 0; + + quint8 flag = cmd.at(3); + + switch (flag) { + case 0x01: emit setBusy(); + break; + case 0x02: emit resetBusy(); + break; + default: return 0; + } + + return 1; +} + + +quint8 VMC::parseShowMessageBoxCommand(QByteArray & cmd) +{ + if (cmd.size() < 5) return 0; + if (cmd.at(2) != 0x32) return 0; + if (cmd.at(4) < 0x30) return 0; + + emit showMessageBox(cmd.at(3), cmd.at(4)); + + return 1; +} + + +quint8 VMC::parseVMCSystemMessage(QByteArray & cmd) +{ + switch (cmd.at(3)) { + case 0x31: emit this->VMCSystemSellingProcessStart(); break; + case 0x32: emit this->VMCSystemSellingProcessStop(); break; + case 0x33: emit this->VMCSystemTransactionSuccess(); break; + default: + return 0; + break; + } + + return 1; +} + +quint8 VMC::parseVMCFormatedString(QByteArray &cmd) +{ + FormatedStringList parameters = cmd.split('#'); + + if (parameters.isEmpty()) return 0; + + // remove header + parameters.takeFirst(); + // remove checksum + parameters.takeLast(); + + emit this->VMCFormatedString(parameters); + + return 1; +} + + +/************************************************************************* + * PSA config command has two variants: + * 1) transmit customer number, group number, zone number and machine number + * together in one command, each number represented by an 16-Byte integer. + * 2) transmit a specified number (= configuration parameter) as a character string. + * The transmitted data is identified by a special selector byte. + * This variant has some advantages: + * - it allows sending arbitrary characters with arbitrary length + * -> arbitrary strings can be transmitted + * - the command is simple extensible in order to transmit additional data + * + * note: the payload lenght is limited to e.g. 20 characters. + */ +quint8 VMC::parsePSAConfigCommand(QByteArray & cmd) +{ + + // check minimum command length + if (cmd.size() < 5) return 0; + + // payload is at least 1 char and max. 20 char + if ((cmd.at(2) < 0x32) && (cmd.at(2) > 0x44)) { + qDebug() << "VMC::parsePSAConfigCommand() - payload length error"; + return 0; + } + + quint8 payloadLength = cmd.at(2) - 0x30; + quint8 payloadType = cmd.at(3); + + // true length check + if (cmd.size() != payloadLength + 4) { + qDebug() << "VMC::parsePSAConfigCommand() - command size error"; + return 0; + } + + QString payload = cmd.mid(4,(payloadLength-1)); + + switch (payloadType) { + case 0x31: // customer number + emit setCustNr(payload); + break; + case 0x32: // group number + emit setGroupNr(payload); + break; + case 0x33: // zone number + emit setZoneNr(payload); + break; + case 0x34: // machine number + emit setMachineNr(payload); + break; + default: + qDebug() << "VMC::parsePSAConfigCommand() - unknown payloadType: " << payloadType; + return 0; + } + + return 1; +} + + + + +void VMC::blockScreenSwitch(quint8 mode) +{ + this->flag_blockVMCScreen = mode; +} + + + +/********************************************************************* + * H A N D L E C C R E Q U E S T S + * + */ +void VMC::ccStartTransactionRequest() +{ + #ifdef USE_CC_PLUGIN + #if defined USE_CC_TRANSACTION + emit ccStartTransaction(); + #endif + #endif +} + + + +void VMC::ccStartConfirmTransaction() +{ +#ifdef USE_CC_PLUGIN + emit ccConfirmTransaction(); +#endif +} + + +/** + * @brief VMC::ccSendReceipt + * + * This is used to make a delayed sending to VMC for receipt data + * + */ +void VMC::ccPrintReceipt(QString receipt) +{ +#ifdef USE_CC_PLUGIN + this->SendLongFormatedString(receipt); +#else + Q_UNUSED(receipt); +#endif +} + + +/********************************************************************* + * customer specific methods + * + */ + +/* for 00318/Nexobility + * -> just send button to VMC + */ +void VMC::skipDiscount() { this->SendButtonPaymethode2(); } + + + +/********************************************************************* + * H A N D L E E R R O R S + * + */ + +/********************************************************************* + * this is usualy called, if vmc does not send any response after sending + * a message. + * + */ +void VMC::onSendError(SEND_ERROR error) +{ + switch (error) { + case SEND_ERROR::RESPONSE: + emit wakeVMC(); + break; + case SEND_ERROR::RESPONSE_TIMER_TIMEOUT: + // close a possible open busy indicator ... + // the following does not work like expected: it lasts up to 5s until this methode is called ... + emit resetBusy(); + break; + case SEND_ERROR::MULTIPLE_RESPONSE_TIMER_TIMEOUT: + if ( config->hasFeatureSelfOOO() ) { + // Handling on ERROR_RESPONSE_TIMER_TIMEOUT: + // -> compare to receive a screen command from vmc to set the ooo-screen: + } + break; + case SEND_ERROR::NO_ERROR: + default: return; + + } +} diff --git a/src/ATBAPP/support/VMC/vmc.h b/src/ATBAPP/support/VMC/vmc.h new file mode 100644 index 0000000..702ee4b --- /dev/null +++ b/src/ATBAPP/support/VMC/vmc.h @@ -0,0 +1,394 @@ +#ifndef VMC_H +#define VMC_H + +#include "com_interface.h" +#include "VMC/ReceiveBuffer.h" +#include "SendBuffer.h" +#include "support/VendingData.h" +#include +#include +#include +#include +#include +#include +#include "version.h" + + +#define VMC_RECEIVE_TIMEOUT 1000 + + + + +#define VMC_CMD_SW_VERSION 0x10 +#define VMC_CMD_STATUS_REQUEST 0x11 + +#define VMC_CMD_SCREEN 0x44 +#define VMC_CMD_LANGUAGE 0x46 + +#define VMC_CMD_DISP_LICENCE_PLATE 0x48 +#define VMC_CMD_DISP_PRODUCT 0x49 +#define VMC_CMD_DISP_PARKTIME_END 0x50 +#define VMC_CMD_DISP_PRICE 0x51 +#define VMC_CMD_DATE_TIME 0x52 +#define VMC_CMD_DISP_LINE1 0x53 +#define VMC_CMD_DISP_LINE2 0x54 +#define VMC_CMD_USER_MESSAGE 0x55 +#define VMC_CMD_DISP_AMOUNT_TO_PAY 0x56 + +#define VMC_CMD_TEXT 0x60 +#define VMC_CMD_TEXT_ENC 0x61 + +#define VMC_CMD_MACHINE_NR 0x62 +#define VMC_CMD_SYSTEM 0x63 +#define VMC_CMD_PSA_CONFIG 0x64 + +#define VMC_CMD_BUSY 0x66 +#define VMC_CMD_MESSAGE_BOX 0x67 + +#define VMC_CMD_END_PRG 0x70 // kill ATBQT +#define VMC_CMD_SHUTDOWN_SYSTEM 0x71 // 'halt' +#define VMC_CMD_RESTART_PRG 0x72 // restart ATBQT +#define VMC_CMD_RESTART_SYSTEM 0x73 // reboot ptu + +#define VMC_CMD_SCREEN_CFG 0x90 + +#define VMC_CMD_FORMATED_STRING 0xD0 + +#define VMC_CMD_VMC_SYSTEM 0xE0 + +#define VMC_CMD_SCREEN_INVALID 0x00 +#define VMC_CMD_SCREEN_START 0x30 +#define VMC_CMD_SCREEN_LICPLT 0x31 +#define VMC_CMD_SCREEN_PRODUCT 0x32 +#define VMC_CMD_SCREEN_PARKEND 0x33 +#define VMC_CMD_SCREEN_PAYMETH 0x34 +#define VMC_CMD_SCREEN_CREDITCARD 0x35 +#define VMC_CMD_SCREEN_WAIT_FOR 0x36 +#define VMC_CMD_SCREEN_GOODBYE 0x37 +#define VMC_CMD_SCREEN_CASHPAY 0x38 +#define VMC_CMD_SCREEN_PIN 0x39 +#define VMC_CMD_SCREEN_RESIDENT 0x3A +#define VMC_CMD_SCREEN_GOODBYE2 0x3B +#define VMC_CMD_SCREEN_RECEIPT 0x3C +#define VMC_CMD_SCREEN_FREEPARK 0x3D +#define VMC_CMD_SCREEN_DEFBUTTONS 0x3E +#define VMC_CMD_SCREEN_PAYMETHODE1 0x3F +#define VMC_CMD_SCREEN_PAYMETHODE2 0x40 +#define VMC_CMD_SCREEN_PAYMETHODE3 0x41 +#define VMC_CMD_SCREEN_PAYMETHODE4 0x42 +#define VMC_CMD_SCREEN_CARDSTATUS 0x43 +#define VMC_CMD_SCREEN_PAYMETHODE5 0x44 +#define VMC_CMD_SCREEN_PAYMETHODE6 0x45 +#define VMC_CMD_SCREEN_PAYMETHODE7 0x46 +#define VMC_CMD_SCREEN_CREDITCARDABORT 0x47 +#define VMC_CMD_SCREEN_PAYMETHODE8 0x48 +#define VMC_CMD_SCREEN_REMOVECARD 0x49 +#define VMC_CMD_SCREEN_GOODBYE3 0x4A +#define VMC_CMD_SCREEN_GENERALABORT 0x4B +#define VMC_CMD_SCREEN_ABORT2 0x4C +#define VMC_CMD_SCREEN_ABORT3 0x4D +#define VMC_CMD_SCREEN_GOODBYE4 0x4E +#define VMC_CMD_SCREEN_GOODBYE5 0x4F +// note: SCREEN_COMMON is 0x50 +#define VMC_CMD_SCREEN_FINEPAYMENT 0x51 +#define VMC_CMD_SCREEN_CREDITCARD2 0x52 +#define VMC_CMD_SCREEN_CREDITCARD3 0x53 +#define VMC_CMD_SCREEN_RECEIPT2 0x54 +#define VMC_CMD_SCREEN_CREDITCARD4 0x55 +#define VMC_CMD_SCREEN_TWO_PRODUCTS1 0x56 +#define VMC_CMD_SCREEN_TWO_PRODUCTS2 0x57 +#define VMC_CMD_SCREEN_PARKTIME1 0x5a +#define VMC_CMD_SCREEN_PARKTIME2 0x5b +#define VMC_CMD_SCREEN_DATETIME1 0x5c +#define VMC_CMD_SCREEN_DATETIME2 0x5d +#define VMC_CMD_SCREEN_DIAG 0x60 +#define VMC_CMD_SCREEN_WELCOME1 0x61 +#define VMC_CMD_SCREEN_WELCOME2 0x62 +#define VMC_CMD_SCREEN_WELCOME3 0x63 +#define VMC_CMD_SCREEN_WELCOME4 0x64 +#define VMC_CMD_SCREEN_OOO 0x65 +#define VMC_CMD_SCREEN_WAIT1 0x6A +#define VMC_CMD_SCREEN_WAIT2 0x6B +#define VMC_CMD_SCREEN_WAIT3 0x6C +#define VMC_CMD_SCREEN_WAIT4 0x6D +#define VMC_CMD_SCREEN_TARIF 0x70 +#define VMC_CMD_SCREEN_HELP1 0x71 +#define VMC_CMD_SCREEN_HELP2 0x72 +#define VMC_CMD_SCREEN_HELP3 0x73 +#define VMC_CMD_SCREEN_HELP4 0x74 +#define VMC_CMD_SCREEN_HELP5 0x75 +#define VMC_CMD_SCREEN_ABORT4 0x80 +#define VMC_CMD_SCREEN_ABORT5 0x81 +#define VMC_CMD_SCREEN_BARCODE 0x82 +#define VMC_CMD_SCREEN_AGEVERIFICATION 0x83 +#define VMC_CMD_SCREEN_DEFBUTTONS2 0x84 +#define VMC_CMD_SCREEN_DEFBUTTONS3 0x85 +#define VMC_CMD_SCREEN_PRINT2 0x86 +#define VMC_CMD_SCREEN_PRINT3 0x87 +#define VMC_CMD_SCREEN_QRCODE_RECEIPT 0x88 +#define VMC_CMD_SCREEN_LICPLT2 0x90 +#define VMC_CMD_SCREEN_LICPLT3 0x91 +#define VMC_CMD_SCREEN_LICPLT4 0x92 +#define VMC_CMD_SCREEN_ABORT6 0x93 +#define VMC_CMD_SCREEN_ABORT7 0x94 +#define VMC_CMD_SCREEN_PARKEND2 0xA0 +#define VMC_CMD_SCREEN_PARKEND3 0xA1 + + +#define VMC_CMD_SCREEN_COMMON 0x50 +#define VMC_CMD_SCREEN_MESSAGE_BOX 0xF1 +// CARD_SERVICE_ERROR_TEXTS 0xFA + +#define ACK 0x06 +#define NACK 0x15 + +class AppControl; +class ReceiveBuffer; +class SendBuffer; +class HMI; +class ATBHMIconfig; + +class VMC : public QObject +{ + Q_OBJECT + + +private: + AppControl *main; + COM_interface *com_interface; + ReceiveBuffer *receiveBuffer; + SendBuffer *sendBuffer; + + ATBHMIconfig *config; + + // internal: write a ByteArray to com-port: + int SendMessage(QByteArray ba, bool enqueue = false); + + quint16 parseSystemCommand(QByteArray & cmd); + quint8 parseTextCommand(QByteArray & cmd); + quint8 parseTextCommandEnc(QByteArray & cmd); + quint8 parseDataDisplayCommand(QByteArray & cmd); + quint8 parseDateCommand(QByteArray & cmd); + quint8 parseUserMessageCommand(QByteArray & cmd); + quint8 parseLanguageCommand(QByteArray & cmd); + quint8 parseBusyCommand(QByteArray & cmd); + quint8 parseScreenConfigCommand(QByteArray & cmd); + quint8 parsePSAConfigCommand(QByteArray & cmd); + quint8 parseShowMessageBoxCommand(QByteArray & cmd); + quint8 parseVMCSystemMessage(QByteArray & cmd); + quint8 parseVMCFormatedString(QByteArray & cmd); + + QString & privateHandleDataCommand_Amount(quint8 data, QString & dataString); + QString & privateHandleDataCommand_AmountToPay(quint8 data, QString & dataString); + + QTimer *sendDelayTimer; + QString delayedMessage; + + quint8 flag_blockVMCScreen; + quint16 currentCachedScreen; + +private slots: + void onDelayedMessageTimerTimeout(); + + void skipDiscount(); + +public: + explicit VMC(QObject *parent = nullptr); + VMC(AppControl *main, ATBHMIconfig *config, QObject *parent = nullptr); + ~VMC(); + + int SendMessage(QString msg); + int SendMessageDelayed(QString msg, int delay); + + int SendACK(); + int SendNACK(); + + int sendButtonChar(QChar c); + + int SendButton0(); // 0 - 3E 45 31 30 E4 + int SendButton1(); // 1 - 3E 45 31 31 E5 + int SendButton2(); // 2 - 3E 45 31 32 E6 + int SendButton3(); // 3 - 3E 45 31 33 E7 + int SendButton4(); // 4 - 3E 45 31 34 E8 + int SendButton5(); // 5 - 3E 45 31 35 E9 + int SendButton6(); // 6 - 3E 45 31 36 EA + int SendButton7(); // 7 - 3E 45 31 37 EB + int SendButton8(); // 8 - 3E 45 31 38 EC + int SendButton9(); // 9 - 3E 45 31 39 ED + + int SendButtonNext(); // 10 - 3E 45 31 3A EE + int SendButtonBack(); // 11 - 3E 45 31 3B EF + int SendButtonCancel(); // 12 - 3E 45 31 3C F0 + + int SendButtonStart(); // 13 - 3E 45 31 3D F1 + int SendButtonHelp(); // 14 - 3E 45 31 3E F2 + int SendButtonTarif(); // 15 - 3E 45 31 3F F3 + + int SendButtonStart2(); // - 3E 45 31 A0 cs + int SendButtonStart3(); // - 3E 45 31 A1 cs + int SendButtonStart4(); // - 3E 45 31 A2 cs + + int SendButtonArrowLeft(); // 16 - 3E 45 31 83 cs + int SendButtonArrowRight(); // 17 - 3E 45 31 84 cs - + int SendButtonArrowUp(); // 18 - 3E 45 31 85 cs - + int SendButtonArrowDown(); // 19 - 3E 45 31 86 cs - + + int SendButtonCardTypeLeft(); // 20 - 3E 45 31 87 cs - + int SendButtonCardTypeRight(); // 21 - 3E 45 31 88 cs - + + int SendButtonA(); // 'A' - 3E 45 31 41 F5 + int SendButtonB(); // 'B' - 3E 45 31 42 F6 + int SendButtonC(); // 'C' - 3E 45 31 43 F7 + int SendButtonD(); // 'D' - 3E 45 31 44 F8 + int SendButtonE(); // 'E' - 3E 45 31 45 F9 + int SendButtonF(); // 'F' - 3E 45 31 46 FA + int SendButtonG(); // 'G' - 3E 45 31 47 FB + int SendButtonH(); // 'H' - 3E 45 31 48 FC + int SendButtonI(); // 'I' - 3E 45 31 49 FD + int SendButtonJ(); // 'J' - 3E 45 31 4A FE + int SendButtonK(); // 'K' - 3E 45 31 4B FF + int SendButtonL(); // 'L' - 3E 45 31 4C 00 + int SendButtonM(); // 'M' - 3E 45 31 4D 01 + int SendButtonN(); // 'N' - 3E 45 31 4E 02 + int SendButtonO(); // 'O' - 3E 45 31 4F 03 + int SendButtonP(); // 'P' - 3E 45 31 50 04 + int SendButtonQ(); // 'Q' - 3E 45 31 51 05 + int SendButtonR(); // 'R' - 3E 45 31 52 06 + int SendButtonS(); // 'S' - 3E 45 31 53 07 + int SendButtonT(); // 'T' - 3E 45 31 54 08 + int SendButtonU(); // 'U' - 3E 45 31 55 09 + int SendButtonV(); // 'V' - 3E 45 31 56 0A + int SendButtonW(); // 'W' - 3E 45 31 57 0B + int SendButtonX(); // 'X' - 3E 45 31 58 0C + int SendButtonY(); // 'Y' - 3E 45 31 59 0D + int SendButtonZ(); // 'Z' - 3E 45 31 5A 0E + int SendButtonUE(); // 'Ü' - 3E 45 31 DC - ISO 8859-1 (Latin-1): 'Ü' + int SendButtonOE(); // 'Ö' - 3E 45 31 D6 - ISO 8859-1 (Latin-1): 'Ö' + int SendButtonAE(); // 'Ä' - 3E 45 31 C4 - ISO 8859-1 (Latin-1): 'Ä' + + int SendButtonDEL(); // DELL - 3E 45 31 7F 33 + + int SendButtonDash(); // '-' - 3E 45 31 2D E1 + int SendButtonSpace(); // ' ' - 3E 45 31 20 D4 + + int SendButtonClose(); // - 3E 45 31 80 cs + int SendButtonYes(); // - 3E 45 31 81 cs + int SendButtonNo(); // - 3E 45 31 82 cs + + int SendButtonVehicleDefintion1(); // 0x90 - 3E 45 31 90 cs + int SendButtonVehicleDefintion2(); // 0x91 - 3E 45 31 91 cs + int SendButtonVehicleDefintion3(); // 0x92 - 3E 45 31 92 cs + int SendButtonVehicleDefintion4(); // 0x93 - 3E 45 31 93 cs + int SendButtonVehicleDefintion5(); // 0x94 - 3E 45 31 94 cs + int SendButtonVehicleDefintion6(); // 0x95 - 3E 45 31 95 cs + int SendButtonVehicleDefintion7(); // 0x96 - 3E 45 31 96 cs + int SendButtonVehicleDefintion8(); // 0x97 - 3E 45 31 97 cs + + int SendButtonPaymethode1(); // 0x98 - 3E 45 31 98 cs + int SendButtonPaymethode2(); // 0x99 - 3E 45 31 99 cs + int SendButtonPaymethode3(); // 0x9a - 3E 45 31 9a cs + int SendButtonPaymethode4(); // 0x9b - 3E 45 31 9b cs + + int SendSystemMessage(quint8 nr); // 0xF0 - 3E F0 31 nr cs + int SendSystemMessageStart(); // 0xF0 - 3E F0 31 31 cs + int SendSystemMessageTimeout(); // 0xF0 - 3E F0 31 32 cs + + + + int SendButtonServiceOK(QString MessageString); + + // deprecated: + int SendLicPlate(QString licplate); + int SendBarcode(QString barcode); + int SendAmountDueTax(QString amount); + int SendAmountDueNet(QString amount); + int SendAmountDuePeriodStart(QString startTime); + int SendAmountDuePeriodEnd(QString endTime); + int SendCracePeriod(QString minute); + int SendGeneratedPinNumber(QString pin); + + // from former "Bewonerscode": send code and id + int SendId(QString id); + int SendCode(QString code); + + + int SendFormatedString(QString formatString); + int SendFormatedStringError(QString errorString); + + int SendLangSwitch(quint8 nr); + + int SendVersion(QString VersionString); // 3E 10 14 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. CRC + + int SendCurrentState(); + + void blockScreenSwitch(quint8 mode); + quint16 getCurrentCachedScreen(); + +signals: + void CommandProcessed(); // sent to ReceiveBuffer if a command is processed + + void retriggerModeSell(); + + void setBusy(); + void resetBusy(); + + void showMessageBox(quint8 type, quint8 text); + + void sysCommand(quint16 cmd, QByteArray data); + //void PSAconfig(quint16 cust_nr, quint16 group_nr, quint16 zone_nr, quint16 machine_nr); + void setCustNr(const QString & cust_nr); + void setGroupNr(const QString & group_nr); + void setZoneNr(const QString & zone_nr); + void setMachineNr(const QString & machine_nr); + void displayData(quint8 infoFild, QString text); + void newDateTime(const QString & dateTimeString); + + void setLanguage(quint8 lang); + void setDefaultLanguage(quint8 lang); + + + void userMessage(quint8 messagenr, quint8 action); + + void writeLog(QString str); + void writeLogReceived(QByteArray ba); + void writeLogSent(QByteArray ba); + + void wakeVMC(); + + void ccStartTransaction(); + void ccConfirmTransaction(); + void ccCancelTransaction(); + void ccStartPreauthorisation(); + void ccConfirmPreauthorisation(); + void ccCancelPreauthorisation(); + void ccStartReadCard(); + + void ccWakup(); + void ccSleep(); + +#ifdef USE_BARCODESCANNER + void requestNewBarcode(); +#endif + + void VMCSystemSellingProcessStart(); + void VMCSystemSellingProcessStop(); + void VMCSystemTransactionSuccess(); + + void VMCFormatedString(FormatedStringList); + + void setVendingData(QString key, QByteArray value); + +public slots: + void CommandAvaliable(QByteArray message); // called when a message is in receive buffer + void onReceiveError(RECEIVE_ERROR error); // for response on wrong vmc commands + void onSendError(SEND_ERROR error); // for react on e.g. a non answering vmc + + int SendLongFormatedString(QString formatString); + int SendLongFormatedStringReference(QString & formatedString); + + void ccStartTransactionRequest(); // called to start/restart a CC transaction + void ccStartConfirmTransaction(); // called to start confirmation + void ccPrintReceipt(QString receipt); // called to send receipt to vmc +}; + +#endif // VMC_H diff --git a/src/ATBAPP/support/VendingData.cpp b/src/ATBAPP/support/VendingData.cpp new file mode 100644 index 0000000..e00db0b --- /dev/null +++ b/src/ATBAPP/support/VendingData.cpp @@ -0,0 +1,76 @@ +#include "VendingData.h" +#include +#include "ATBAPP.h" +#include "version.h" + +VendingData::VendingData(QObject *parent) : QObject(parent) +{ + qRegisterMetaType("FormatedStringList"); +} + + +QVariant VendingData::getParameter(const QString & key) +{ + + #if defined (ARCH_DesktopLinux) + // note: QVariant.toString() returns empty string for custom types + + if (QString::compare(hash.value(key).typeName(), "APP_ACTION") == 0) { + APP_ACTION action = hash.value(key).value(); + qDebug() << "VendingData::getParameter() key = " << key << " value = " << action; + } + else { + qDebug() << "VendingData::getParameter() key = " << key << " value = " << hash.value(key).toString(); + } + + #endif + + return hash.value(key); +} + +void VendingData::setParameter(const QString & key, QVariant value) +{ + + #if defined (ARCH_DesktopLinux) + // note: QVariant.toString() returns empty string for custom types + if (QString::compare(value.typeName(), "APP_ACTION") == 0) { + APP_ACTION action = value.value(); + qDebug() << "VendingData::setParameter() key = " << key << " value = " << action; + } + else { + qDebug() << "VendingData::setParameter() key = " << key << " value = " << value.toString(); + } + + #endif + + this->hash.insert(key, value); +} + +void VendingData::clearParameter(const QString & key) +{ + this->hash.remove(key); +} + +bool VendingData::hasParameter(const QString & key) +{ + return hash.contains(key); +} + +void VendingData::clear() +{ + this->hash.clear(); +} + + +uint VendingData::getUintParameter(const QString & key) +{ + qDebug() << "VendingData::getUintParameter() key = " << key << " value = " << hash.value(key).toString(); + + uint returnValue = 0; + bool ok; + + returnValue = hash.value(key).toString().toUInt(&ok); + if (!ok) returnValue = 0; + + return returnValue; +} diff --git a/src/ATBAPP/support/VendingData.h b/src/ATBAPP/support/VendingData.h new file mode 100644 index 0000000..a0b8f21 --- /dev/null +++ b/src/ATBAPP/support/VendingData.h @@ -0,0 +1,37 @@ +#ifndef VENDINGDATA_H +#define VENDINGDATA_H + + +#include +#include +#include + + +typedef QList FormatedStringList; + + +class VendingData : public QObject +{ + Q_OBJECT + +private: + QHash hash; + +public: + explicit VendingData(QObject *parent = nullptr); + + QVariant getParameter(const QString & key); + void setParameter(const QString & key, QVariant value); + void clearParameter(const QString & key); + bool hasParameter(const QString & key); + + uint getUintParameter(const QString & key); + + +signals: + +public slots: + void clear(); +}; + +#endif // VENDINGDATA_H