diff --git a/library/include/mobilisis/calculate_price.h b/library/include/mobilisis/calculate_price.h index 277d9d5..2fa53a2 100644 --- a/library/include/mobilisis/calculate_price.h +++ b/library/include/mobilisis/calculate_price.h @@ -107,7 +107,7 @@ struct CALCULATE_LIBRARY_API CalcState { CalcState CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff, char const *config_file); void CALCULATE_LIBRARY_API free_tariff(parking_tariff_t *tariff); -int CALCULATE_LIBRARY_API get_zone_nr(); +int CALCULATE_LIBRARY_API get_zone_nr(int zone = -1); CalcState CALCULATE_LIBRARY_API compute_price_for_parking_ticket( parking_tariff_t *tariff, diff --git a/library/src/calculate_price.cpp b/library/src/calculate_price.cpp index d4bde75..69730ca 100644 --- a/library/src/calculate_price.cpp +++ b/library/src/calculate_price.cpp @@ -10,18 +10,23 @@ static Calculator calculator; -int CALCULATE_LIBRARY_API get_zone_nr() { - QFile zone("/etc/zone_nr"); - if (zone.exists()) { - QFileInfo finfo(zone); - if (finfo.size() <= 4) { // decimal 000\n - if (zone.open(QIODevice::ReadOnly | QIODevice::Text)) { - QTextStream in(&zone); - return in.readLine(100).toInt(); +int CALCULATE_LIBRARY_API get_zone_nr(int zone) +{ + if(zone > -1) return zone; + else + { + QFile zone("/etc/zone_nr"); + if (zone.exists()) { + QFileInfo finfo(zone); + if (finfo.size() <= 4) { // decimal 000\n + if (zone.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&zone); + return in.readLine(100).toInt(); + } } } - } - return -1; + return -1; + } } CalcState CALCULATE_LIBRARY_API init_tariff(parking_tariff_t **tariff, char const *config_file) { @@ -228,7 +233,7 @@ CalcState CALCULATE_LIBRARY_API compute_duration_for_parking_ticket( duration = calculator.GetDurationFromCost(tariff, PaymentOption::Option1, cs.toLocal8Bit().constData(), price, false, true).c_str(); - QDateTime d = QDateTime::fromString(duration); + QDateTime d = QDateTime::fromString(duration, Qt::ISODate); if (!d.isValid()) { calcState.setDesc(QString("ticketEndTime=%1").arg(duration)); return calcState.set(CalcState::State::WRONG_ISO_TIME_FORMAT); diff --git a/library/src/calculator_functions.cpp b/library/src/calculator_functions.cpp index 6c45218..a97c02d 100644 --- a/library/src/calculator_functions.cpp +++ b/library/src/calculator_functions.cpp @@ -4,11 +4,21 @@ #include "tariff_log.h" #include +#include +#include double total_duration_min = 0.0f; double total_cost = 0.0f; bool overtime = false; +#ifdef _WIN32 +inline struct tm* localtime_r(const time_t *clock, struct tm* result){ + if(!clock || !result) return NULL; + memcpy(result,localtime(clock),sizeof(*result)); + return result; +} +#endif + /// std::string Calculator::GetDurationFromCost(Configuration* cfg, uint8_t payment_option, @@ -17,292 +27,222 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, bool nextDay, bool prepaid) { - // Get current date time from input - struct tm current_datetime = Utilities::DateTimeToStructTm(start_datetime); - time_t current_datetime_t; + // Get input date + QDateTime inputDate = QDateTime::fromString(start_datetime,Qt::ISODate); - // Get day of week - DayOfWeek weekdayId = DayOfWeek::UndefinedDay; - weekdayId = Utilities::GetDayOfWeek(¤t_datetime); + // Get day of week + int weekdayId = 0; + weekdayId = Utilities::ZellersAlgorithm(inputDate.date().day(),inputDate.date().month(),inputDate.date().year()); - //std::stringstream ss; + //Get min and max time defined in JSON + double minMin = 0; + minMin = cfg->PaymentOption.find(payment_option)->second.pop_min_time; - // ss << "*** Input date is: " << start_datetime << " [weekday id = " << weekdayId << "]" << endl; - LOG_DEBUG("*** Input date is: ", start_datetime, " [weekday id = ", weekdayId, "]"); + double maxMin = 0; + maxMin = cfg->PaymentOption.find(payment_option)->second.pop_max_time; - double minMin = 0; - minMin = cfg->PaymentOption.find(payment_option)->second.pop_min_time; - if (minMin < 0) minMin = 0; - - double maxMin = 0; - maxMin = cfg->PaymentOption.find(payment_option)->second.pop_max_time; - if (maxMin <= 0) maxMin = 60; - - if (minMin >= maxMin) - { - LOG_ERROR("Error: min_min cannot be greater or equal to max_min"); - return "PARKING NOT ALLOWED"; - } - - if (maxMin <= minMin) - { - LOG_ERROR("Error: max_min cannot be lower or equal than min_min"); - return "PARKING NOT ALLOWED"; - } - - uint8_t p_method = PaymentMethod::Undefined; - p_method = payment_option; - LOG_DEBUG("Payment method id: ", (unsigned)p_method); - - double day_price = 0.0f; - int current_special_day_id = -1; - bool is_special_day = Utilities::CheckSpecialDay(cfg, start_datetime, ¤t_special_day_id, &day_price); - LOG_DEBUG("Special day: ", is_special_day); - - double money_left = price; - LOG_DEBUG("Total money:", money_left); - - double price_per_unit = 0.0f; - - string worktime_from = ""; - string worktime_to = ""; - - if (is_special_day) - { - // Set special day price - price_per_unit = Utilities::CalculatePricePerUnit(day_price); - worktime_from = cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from; - worktime_to = cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to; - } - else - { - // Set new price for the normal day - day_price = cfg->PaymentRate.find(payment_option)->second.pra_price; - price_per_unit = Utilities::CalculatePricePerUnit(day_price); - - // If no working day found, skip it (recursively call method again) - size_t found = 0; - found = cfg->WeekDaysWorktime.count(weekdayId); - - if (found <= 0) - { - LOG_DEBUG("- No workday found, trying to find next available day"); - current_datetime_t = mktime(¤t_datetime); - current_datetime_t += 86400; - current_datetime = *localtime(¤t_datetime_t); - - char buffer_datetime[80]; - strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime); - - // Make new datetime string and call function again recursively - start_datetime = buffer_datetime; - return GetDurationFromCost(cfg, payment_option, start_datetime, price, true); - } - - worktime_from = cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from; - worktime_to = cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to; - } - - if (price_per_unit < 0) price_per_unit = 1.0f; - LOG_DEBUG("Calculated price per minute: ", price_per_unit); - - LOG_DEBUG("Worktime from: ", worktime_from); - LOG_DEBUG("Worktime to: ", worktime_to); - - struct tm from_tm; - struct tm to_tm; - - from_tm = Utilities::TimeToStructTm(worktime_from.c_str(), current_datetime.tm_year, current_datetime.tm_mon, current_datetime.tm_mday, current_datetime.tm_wday); - from_tm.tm_year = current_datetime.tm_year; - from_tm.tm_mon = current_datetime.tm_mon; - from_tm.tm_wday = current_datetime.tm_wday; - from_tm.tm_mday = current_datetime.tm_mday; - - to_tm = Utilities::TimeToStructTm(worktime_to.c_str(), current_datetime.tm_year, current_datetime.tm_mon, current_datetime.tm_mday, current_datetime.tm_wday); - to_tm.tm_year = current_datetime.tm_year; - to_tm.tm_mon = current_datetime.tm_mon; - to_tm.tm_wday = current_datetime.tm_wday; - to_tm.tm_mday = current_datetime.tm_mday; - - // Convert tm structures to time_t - current_datetime_t = mktime(¤t_datetime); - time_t from_datetime_t = mktime(&from_tm); - time_t to_datetime_t = mktime(&to_tm); - - /*Newly added */ - //current_datetime.tm_hour = from_tm.tm_hour; - //current_datetime.tm_min = from_tm.tm_min; - //current_datetime.tm_sec = from_tm.tm_sec; - //current_datetime_t = mktime(¤t_datetime); - - // If overtime flag is set - if (overtime || nextDay) - { - current_datetime.tm_hour = from_tm.tm_hour; - current_datetime.tm_min = from_tm.tm_min; - current_datetime.tm_sec = from_tm.tm_sec; - current_datetime_t = mktime(¤t_datetime); - LOG_DEBUG(" *** New input date set according to worktime: ", asctime(¤t_datetime)); - overtime = false; - } - - // Validate ticket - if (!prepaid) - { - if ((current_datetime_t < from_datetime_t) || (current_datetime_t > to_datetime_t)) - { - LOG_DEBUG("[STOP] * Ticket is not valid * "); - return "PARKING NOT ALLOWED"; - } - } - else - { - LOG_DEBUG("* PREPAID MODE ACTIVE *"); - - if (current_datetime_t < from_datetime_t) - { - current_datetime.tm_hour = from_tm.tm_hour; - current_datetime.tm_min = from_tm.tm_min; - current_datetime.tm_sec = from_tm.tm_sec; - current_datetime_t = mktime(¤t_datetime); - LOG_DEBUG(" *** PREPAID *** Current time is before the time range start, adjusting time to: ", asctime(¤t_datetime)); - } - else if (current_datetime_t > to_datetime_t) - { - LOG_DEBUG(" *** PREPAID *** Current time is past the time range end, searching for next available day"); - current_datetime_t = mktime(¤t_datetime); - current_datetime_t += 86400; - current_datetime = *localtime(¤t_datetime_t); - - char buffer_datetime[80]; - strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime); - - //Make new datetime string and call function again recursively - start_datetime = buffer_datetime; - return GetDurationFromCost(cfg, payment_option, start_datetime, price, true, prepaid); - } - } - - while (true) - { - if (!Utilities::IsYearPeriodActive(cfg, ¤t_datetime)) - { - LOG_DEBUG("Year period is not valid"); - return "PARKING NOT ALLOWED"; - } - - // Increment by 1 minute - current_datetime_t = mktime(¤t_datetime); - current_datetime_t += 60; - current_datetime = *localtime(¤t_datetime_t); - total_duration_min += 1.0f; - money_left -= price_per_unit; - - - // If no money left (e.g. spent all of the money before reaching end of worktime) - if (money_left <= 0) - { - LOG_DEBUG("No money left "); - break; - } - else - { - if (total_duration_min >= maxMin) - { - LOG_DEBUG("Total duration is greater or equal to max_min"); - - current_datetime_t = mktime(¤t_datetime); - current_datetime_t -= 60; - current_datetime = *localtime(¤t_datetime_t); - total_duration_min = maxMin; - break; - } - - // If money has left but the end of worktime has been reached (go to next day) - if (current_datetime_t >= to_datetime_t) - { - total_duration_min -= 1.0f; - - int carry_over_status = 0; - carry_over_status = cfg->PaymentOption.find(payment_option)->second.pop_carry_over; - LOG_DEBUG("Carry over status: ", carry_over_status); - if (carry_over_status < 1) break; - - LOG_DEBUG("Reached end of worktime"); - LOG_DEBUG("Trying to find next available day, money left = ", money_left); - current_datetime_t = mktime(¤t_datetime); - current_datetime_t += 86400; - current_datetime = *localtime(¤t_datetime_t); - - char buffer_datetime[80]; - strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime); - - // Make new datetime string and call function again recursively - start_datetime = buffer_datetime; - overtime = true; - return GetDurationFromCost(cfg, payment_option, start_datetime, price); - } - } - } - - time_t valid_until_datetime_t = current_datetime_t; - - if ((total_duration_min < minMin) || (price / price_per_unit) < minMin) - { - LOG_DEBUG("Total duration is lower than min_min"); - //valid_until_datetime_t += (minMin - total_duration_min) * 60; - //total_duration_min = minMin; - //return "PARKING NOT ALLOWED"; - - valid_until_datetime_t = from_datetime_t; - total_duration_min = 0; - } - - double ret_val = 0; - double calc_price = (int)total_duration_min - (int)price / price_per_unit; - - if (calc_price > 0 && total_duration_min > 0) - { - valid_until_datetime_t -= (int)ceil(calc_price) * 60; - ret_val = total_duration_min - calc_price; - } - else ret_val = total_duration_min; - - cout << "Total minutes: " << (int)ret_val << endl; - if (ret_val <= 0) return "PARKING NOT ALLOWED"; - -#ifdef __linux__ - #if !defined(_SVID_SOURCE) && !defined(_XOPEN_SOURCE) - // needed for timezone-correction - #error "!defined(_SVID_SOURCE) && !defined(_XOPEN_SOURCE)" - #else - // timezone correction: localtime() needs argument in UTC-timezone - // The global variable 'timezone' is set by tzset(), see - // https://linux.die.net/man/3/tzset, so I change of the timezone will - // also change the value of the variable 'timezone'. - valid_until_datetime_t += timezone; - #endif -#else // windows, only for testing - static const long timezone = -3600; - valid_until_datetime_t += timezone; -#endif - - struct tm valid_until_datetime; - memset(&valid_until_datetime, 0x00, sizeof(valid_until_datetime)); - - if (!localtime_r(&valid_until_datetime_t, &valid_until_datetime)) { - return "LOCALTIME_R() ERROR"; + double min_price = 0; + min_price = cfg->PaymentOption.find(payment_option)->second.pop_min_price; + if(price < min_price) + { + return "PARKING NOT ALLOWED"; } - // return in ISO-format: "%Y-%m-%dT%H:%M:%S" - char buf[128]; - memset(buf, 0x00, sizeof(buf)); - strftime(buf, sizeof(buf)-1, "%Y-%m-%dT%H:%M:%S", &valid_until_datetime); + if (minMin < 0) minMin = 0; + if (maxMin < 0) maxMin = 0; + if (minMin >= maxMin) + { + LOG_ERROR("Error: min_min cannot be greater or equal to max_min"); + return "PARKING NOT ALLOWED"; + } - LOG_DEBUG("Ticket is valid until ", buf); + if (maxMin <= minMin) + { + LOG_ERROR("Error: max_min cannot be lower or equal than min_min"); + return "PARKING NOT ALLOWED"; + } - total_duration_min = 0.0f; - return std::string(buf); + + // Get payment method + uint8_t p_method = PaymentMethod::Undefined; + p_method = payment_option; + LOG_DEBUG("Payment method id: ", (unsigned)p_method); + + // Check special day + double day_price = 0.0f; + int current_special_day_id = -1; + bool is_special_day = Utilities::CheckSpecialDay(cfg, inputDate.toString(Qt::ISODate).toStdString().c_str(), ¤t_special_day_id, &day_price); + LOG_DEBUG("Special day: ", is_special_day); + + double money_left = price; + double price_per_unit = 0.0f; + + QTime worktime_from; + QTime worktime_to; + + if(is_special_day) + { + // Set special day price + price_per_unit = Utilities::CalculatePricePerUnit(day_price); + worktime_from = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from.c_str()); + worktime_to = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to.c_str()); + } + else + { + // Set new price for the normal day + day_price = cfg->PaymentRate.find(payment_option)->second.pra_price; + price_per_unit = Utilities::CalculatePricePerUnit(day_price); + + // If no working day found, skip it (recursively call method again) + size_t found = 0; + found = cfg->WeekDaysWorktime.count(weekdayId); + + // When no workday found, go to next available day + if(found <=0) + { + LOG_DEBUG("- No workday found, trying to find next available day"); + inputDate = inputDate.addDays(1); + return GetDurationFromCost(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), money_left,true,prepaid); + } + worktime_from = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from.c_str()); + worktime_to = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to.c_str()); + } + + if (price_per_unit < 0) price_per_unit = 1.0f; + LOG_DEBUG("Calculated price per minute: ", price_per_unit); + + if (price_per_unit < 0) + { + inputDate = inputDate.addDays(1); + inputDate.setTime(worktime_from); + return GetDurationFromCost(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), money_left, true); + } + + // If overtime flag is set + if (overtime || nextDay) + { + inputDate.setTime(worktime_from); + overtime = false; + } + + // Check prepaid + if (!prepaid) + { + if ((inputDate.time() < worktime_from) || (inputDate.time() > worktime_to)) + { + LOG_DEBUG("[STOP] * Ticket is not valid * "); + return "PARKING NOT ALLOWED"; + } + } + else + { + LOG_DEBUG("* PREPAID MODE ACTIVE *"); + if (inputDate.time() < worktime_from) + { + inputDate.setTime(worktime_from); + } + else if(inputDate.time() > worktime_to) + { + LOG_DEBUG(" *** PREPAID *** Current time is past the time range end, searching for next available day"); + inputDate = inputDate.addDays(1); + return GetDurationFromCost(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), money_left, true); + } + } + + while(true) + { + if(money_left <= 0) break; + + // Check year period + bool isYearPeriodActive = false; + + //// Parse input date + int dayCurrent = inputDate.date().day(); + int monthCurrent = inputDate.date().month(); + + // Current date time + int cdt = (monthCurrent * 100) + dayCurrent; + + multimap::iterator year_period_itr; + for (year_period_itr = cfg->YearPeriod.begin(); year_period_itr != cfg->YearPeriod.end(); ++year_period_itr) + { + int dStart = year_period_itr->second.pye_start_day; + int dEnd = year_period_itr->second.pye_end_day; + + int mStart = year_period_itr->second.pye_start_month; + int mEnd = year_period_itr->second.pye_end_month; + + int start = (mStart * 100) + dStart; + int end = (mEnd * 100) + dEnd; + + if (cdt >= start && cdt <= end) { + isYearPeriodActive = true; + break; + } + } + if (!isYearPeriodActive) + { + LOG_DEBUG("Year period is not valid"); + return "PARKING NOT ALLOWED"; + } + + if(total_duration_min > maxMin) + { + total_duration_min = maxMin; + break; + } + + // If reached end of worktime go to next day + if(inputDate.time() >= worktime_to) + { + int carry_over_status = 0; + carry_over_status = cfg->PaymentOption.find(payment_option)->second.pop_carry_over; + if (carry_over_status < 1) break; + + inputDate = inputDate.addDays(1); + overtime = true; + return GetDurationFromCost(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), money_left ,true, prepaid); + } + + if(money_left > 1) + inputDate = inputDate.addSecs(60); + + if(price_per_unit > 0) total_duration_min +=1; + money_left -= price_per_unit; + + //qDebug() <<"Timestamp:" << inputDate << ", total duration min: " << total_duration_min << ", money left = " << money_left; + } + + // if ((total_duration_min < minMin) || (price / price_per_unit) < minMin) + // { + // LOG_DEBUG("Total duration is lower than min_min"); + // inputDate.time() = worktime_from; + // total_duration_min = 0; + // } + + double ret_val = 0; + double calc_price = (int)total_duration_min - (int)price / price_per_unit; + + if (calc_price > 0 && total_duration_min > 0) + { + inputDate = inputDate.addSecs(-(int)ceil(calc_price) * 60); + } + + if(price >= min_price && total_duration_min >= minMin) + qDebug() << "Valid until: " << inputDate.toString(Qt::ISODate); + else + { + qDebug() << "Parking not allowed"; + total_duration_min = 0; + } + + ret_val = total_duration_min; + if(ret_val < 0) ret_val = 0; + qDebug() << "Duration: " << ret_val; + if (ret_val <= 0) return "PARKING NOT ALLOWED"; + + total_duration_min = 0; + return inputDate.toString(Qt::ISODate).toStdString(); } /////////////////////////////////////// @@ -310,237 +250,194 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg, /// double Calculator::GetCostFromDuration(Configuration* cfg, uint8_t payment_option, const char* start_datetime, double durationMin, bool nextDay, bool prepaid) { - // Get current date time from input - struct tm current_datetime = Utilities::DateTimeToStructTm(start_datetime); - time_t current_datetime_t; + // Get input date + QDateTime inputDate = QDateTime::fromString(start_datetime,Qt::ISODate); - // Get day of week - DayOfWeek weekdayId = DayOfWeek::UndefinedDay; - weekdayId = Utilities::GetDayOfWeek(¤t_datetime); - LOG_DEBUG("*** Input date is: ", start_datetime, " [weekday id = ", weekdayId, "]"); + // Get day of week + int weekdayId = 0; + weekdayId = Utilities::ZellersAlgorithm(inputDate.date().day(),inputDate.date().month(),inputDate.date().year()); - double minMin = 0; - minMin = cfg->PaymentOption.find(payment_option)->second.pop_min_time; - if (minMin < 0) minMin = 0; + //Get min and max time defined in JSON + double minMin = 0; + minMin = cfg->PaymentOption.find(payment_option)->second.pop_min_time; - double maxMin = 0; - maxMin = cfg->PaymentOption.find(payment_option)->second.pop_max_time; - if (maxMin <= 0) maxMin = 60; + double maxMin = 0; + maxMin = cfg->PaymentOption.find(payment_option)->second.pop_max_time; - if (minMin >= maxMin) - { - LOG_ERROR("Error: min_min cannot be greater or equal to max_min"); - return 0.0f; - } + if (minMin < 0) minMin = 0; + if (maxMin < 0) maxMin = 0; + if (minMin >= maxMin) + { + LOG_ERROR("Error: min_min cannot be greater or equal to max_min"); + return 0.0f; + } + if (maxMin <= minMin) + { + LOG_ERROR("Error: max_min cannot be lower or equal than min_min"); + return 0.0f; + } - if (maxMin <= minMin) - { - LOG_ERROR("Error: max_min cannot be lower or equal than min_min"); - return 0.0f; - } + // Check overtime + if (!overtime) + { + if (durationMin > maxMin) + { + LOG_WARNING("Total duration is greater or equal to max_min"); + return maxMin; + } - if (!overtime) - { - if (durationMin > maxMin) - { - LOG_WARNING("Total duration is greater or equal to max_min"); - return 0.0f; - } + if (durationMin < minMin) + { + LOG_WARNING("Total duration is lower or equal to min_min"); + return 0.0f; + } + } - if (durationMin < minMin) - { - LOG_WARNING("Total duration is lower or equal to min_min"); - return 0.0f; - } - } + // Get payment method + uint8_t p_method = PaymentMethod::Undefined; + p_method = payment_option; + LOG_DEBUG("Payment method id: ", (unsigned)p_method); - uint8_t p_method = PaymentMethod::Undefined; - p_method = payment_option; - LOG_DEBUG("Payment method id: ", (unsigned)p_method); + // Check special day + double day_price = 0.0f; + int current_special_day_id = -1; + bool is_special_day = Utilities::CheckSpecialDay(cfg, inputDate.toString(Qt::ISODate).toStdString().c_str(), ¤t_special_day_id, &day_price); + LOG_DEBUG("Special day: ", is_special_day); - double day_price = 0.0f; - int current_special_day_id = -1; - bool is_special_day = Utilities::CheckSpecialDay(cfg, start_datetime, ¤t_special_day_id, &day_price); - LOG_DEBUG("Special day: ", is_special_day); + total_duration_min = durationMin; + LOG_DEBUG("Total min:", total_duration_min); - total_duration_min = durationMin; - LOG_DEBUG("Total min:", total_duration_min); + double price_per_unit = 0.0f; + QTime worktime_from; + QTime worktime_to; - double price_per_unit = 0.0f; + if(is_special_day) + { + // Set special day price + price_per_unit = Utilities::CalculatePricePerUnit(day_price); + worktime_from = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from.c_str()); + worktime_to = QTime::fromString(cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to.c_str()); + } + else + { + // Set new price for the normal day + day_price = cfg->PaymentRate.find(payment_option)->second.pra_price; + price_per_unit = Utilities::CalculatePricePerUnit(day_price); - string worktime_from = ""; - string worktime_to = ""; + // If no working day found, skip it (recursively call method again) + size_t found = 0; + found = cfg->WeekDaysWorktime.count(weekdayId); - if (is_special_day) - { - // Set special day price - price_per_unit = Utilities::CalculatePricePerUnit(day_price); - worktime_from = cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_from; - worktime_to = cfg->SpecialDaysWorktime.find(current_special_day_id)->second.pedwt_time_to; - } - else - { - // Set new price for the normal day - day_price = cfg->PaymentRate.find(payment_option)->second.pra_price; - price_per_unit = Utilities::CalculatePricePerUnit(day_price); + // When no workday found, go to next available day + if(found <=0) + { + LOG_DEBUG("- No workday found, trying to find next available day"); + inputDate = inputDate.addDays(1); + return floor(GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), durationMin, true, prepaid)); + } + worktime_from = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from.c_str()); + worktime_to = QTime::fromString(cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to.c_str()); + } - // If no working day found, skip it (recursively call method again) - size_t found = 0; - found = cfg->WeekDaysWorktime.count(weekdayId); + if (price_per_unit < 0) price_per_unit = 1.0f; + LOG_DEBUG("Calculated price per minute: ", price_per_unit); - if (found <= 0) - { - LOG_DEBUG("- No workday found, trying to find next available day"); - current_datetime_t = mktime(¤t_datetime); - current_datetime_t += 86400; - current_datetime = *localtime(¤t_datetime_t); + if (price_per_unit == 0) + { + inputDate = inputDate.addDays(1); + inputDate.setTime(worktime_from); + return GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), durationMin, true, prepaid); + } - char buffer_datetime[80]; - strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime); + // If overtime flag is set + if (overtime || nextDay) + { + inputDate.setTime(worktime_from); + overtime = false; + } - //Make new datetime string and call function again recursively - start_datetime = buffer_datetime; - return floor(GetCostFromDuration(cfg, payment_option, start_datetime, durationMin, true, prepaid)); - } + // Check prepaid + if (!prepaid) + { + if ((inputDate.time() < worktime_from) || (inputDate.time() > worktime_to)) + { + LOG_DEBUG("[STOP] * Ticket is not valid * "); + return 0.0f; + } + } + else + { + LOG_DEBUG("* PREPAID MODE ACTIVE *"); + if (inputDate.time() < worktime_from) + { + inputDate.setTime(worktime_from); + } + else if(inputDate.time() > worktime_to) + { + LOG_DEBUG(" *** PREPAID *** Current time is past the time range end, searching for next available day"); + inputDate = inputDate.addDays(1); + return GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), durationMin, true, prepaid); + } + } - worktime_from = cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_from; - worktime_to = cfg->WeekDaysWorktime.find(weekdayId)->second.pwd_time_to; - } + while(true) + { + if(total_duration_min <= 0) break; - if (price_per_unit < 0) price_per_unit = 1.0f; - LOG_DEBUG("Calculated price per minute: ", price_per_unit); + // Check year period + bool isYearPeriodActive = false; - LOG_DEBUG("Worktime from: ", worktime_from); - LOG_DEBUG("Worktime to: ", worktime_to); + //// Parse input date + int dayCurrent = inputDate.date().day(); + int monthCurrent = inputDate.date().month(); - struct tm from_tm; - struct tm to_tm; + // Current date time + int cdt = (monthCurrent * 100) + dayCurrent; - from_tm = Utilities::TimeToStructTm(worktime_from.c_str(), current_datetime.tm_year, current_datetime.tm_mon, current_datetime.tm_mday, current_datetime.tm_wday); - from_tm.tm_year = current_datetime.tm_year; - from_tm.tm_mon = current_datetime.tm_mon; - from_tm.tm_wday = current_datetime.tm_wday; - from_tm.tm_mday = current_datetime.tm_mday; + multimap::iterator year_period_itr; + for (year_period_itr = cfg->YearPeriod.begin(); year_period_itr != cfg->YearPeriod.end(); ++year_period_itr) + { + int dStart = year_period_itr->second.pye_start_day; + int dEnd = year_period_itr->second.pye_end_day; - to_tm = Utilities::TimeToStructTm(worktime_to.c_str(), current_datetime.tm_year, current_datetime.tm_mon, current_datetime.tm_mday, current_datetime.tm_wday); - to_tm.tm_year = current_datetime.tm_year; - to_tm.tm_mon = current_datetime.tm_mon; - to_tm.tm_wday = current_datetime.tm_wday; - to_tm.tm_mday = current_datetime.tm_mday; + int mStart = year_period_itr->second.pye_start_month; + int mEnd = year_period_itr->second.pye_end_month; - // Convert tm structures to time_t - current_datetime_t = mktime(¤t_datetime); + int start = (mStart * 100) + dStart; + int end = (mEnd * 100) + dEnd; - time_t from_datetime_t = mktime(&from_tm); - time_t to_datetime_t = mktime(&to_tm); + if (cdt >= start && cdt <= end) { + isYearPeriodActive = true; + break; + } + } + if (!isYearPeriodActive) + { + LOG_DEBUG("Year period is not valid"); + return 0.0f; + } - // If overtime flag is set - if (overtime || nextDay) - { - current_datetime.tm_hour = from_tm.tm_hour; - current_datetime.tm_min = from_tm.tm_min; - current_datetime.tm_sec = from_tm.tm_sec; - current_datetime_t = mktime(¤t_datetime); - LOG_DEBUG(" *** New input date set according to worktime: ", asctime(¤t_datetime)); - overtime = false; - } + // Go to next day if minutes not spent + if(inputDate.time() >= worktime_to) + { + int carry_over_status = 0; + carry_over_status = cfg->PaymentOption.find(payment_option)->second.pop_carry_over; + if (carry_over_status < 1) break; - // Validate ticket - if (!prepaid) - { - if ((current_datetime_t < from_datetime_t) || (current_datetime_t > to_datetime_t)) - { - LOG_DEBUG("[STOP] * Ticket is not valid * "); - return 0.0f; - } - } - else - { - LOG_DEBUG("* PREPAID MODE ACTIVE *"); + LOG_DEBUG("Reached end of worktime, searching for the next working day"); + inputDate = inputDate.addDays(1); + overtime = true; + return GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), total_duration_min); + } - if (current_datetime_t < from_datetime_t) - { - current_datetime.tm_hour = from_tm.tm_hour; - current_datetime.tm_min = from_tm.tm_min; - current_datetime.tm_sec = from_tm.tm_sec; - current_datetime_t = mktime(¤t_datetime); - LOG_DEBUG(" *** PREPAID *** Current time is before the time range start, adjusting time to: ", asctime(¤t_datetime)); - } - else if (current_datetime_t > to_datetime_t) - { - LOG_DEBUG(" *** PREPAID *** Current time is past the time range end, searching for next available day"); - current_datetime_t = mktime(¤t_datetime); - current_datetime_t += 86400; - current_datetime = *localtime(¤t_datetime_t); + // Increment input date minutes for each monetary unit + inputDate = inputDate.addSecs(60); + total_duration_min -=1; + total_cost += price_per_unit; - char buffer_datetime[80]; - strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime); - - //Make new datetime string and call function again recursively - start_datetime = buffer_datetime; - return floor(GetCostFromDuration(cfg, payment_option, start_datetime, durationMin, true, prepaid)); - } - } - - while (true) - { - if (!Utilities::IsYearPeriodActive(cfg, ¤t_datetime)) - { - LOG_DEBUG("Year period is not valid"); - return 0.0f; - } - - // Decrement by 1 minute - current_datetime_t = mktime(¤t_datetime); - current_datetime_t += 60; - current_datetime = *localtime(¤t_datetime_t); - - total_duration_min -= 1.0f; - total_cost += price_per_unit; - - // If no minutes left (e.g. spent all of the money before reaching end of worktime) - if (total_duration_min <= 0) - { - //total_duration_min -= 1.0f; - LOG_DEBUG("No minutes left "); - break; - } - else - { - // If minutes has left but the end of worktime has been reached (go to next day) - if (current_datetime_t >= to_datetime_t) - { - int carry_over_status = 0; - carry_over_status = cfg->PaymentOption.find(payment_option)->second.pop_carry_over; - total_duration_min += 1.0f; - total_cost -= price_per_unit; - - LOG_DEBUG("Carry over status: ", carry_over_status); - if (carry_over_status < 1) break; - - LOG_DEBUG("Reached end of worktime"); - LOG_DEBUG("Trying to find next available day, min left = ", total_duration_min); - current_datetime_t = mktime(¤t_datetime); - current_datetime_t += 86400; - current_datetime = *localtime(¤t_datetime_t); - - char buffer_datetime[80]; - strftime(buffer_datetime, 80, "%Y-%m-%dT%H:%M:%S", ¤t_datetime); - - // Make new datetime string and call function again recursively - start_datetime = buffer_datetime; - overtime = true; - return floor(GetCostFromDuration(cfg, payment_option, start_datetime, total_duration_min)); - } - } - } - - time_t valid_until_datetime_t = current_datetime_t; - struct tm valid_until_datetime = *localtime(&valid_until_datetime_t); - LOG_DEBUG("Ticket is valid until ", asctime(&valid_until_datetime)); - - double ret_val = total_cost; - total_cost = 0.0f; + } + qDebug() << "Valid until:" << inputDate.toString(Qt::ISODate).toStdString().c_str(); + double ret_val = total_cost; + total_cost = 0.0f; return ceil(ret_val); - // return floor(ret_val); } diff --git a/library/src/utilities.cpp b/library/src/utilities.cpp index 19a40bf..48cf880 100644 --- a/library/src/utilities.cpp +++ b/library/src/utilities.cpp @@ -33,7 +33,7 @@ time_t Utilities::GetCurrentLocalTime() memset(&tm_curr_time, '\0', sizeof(struct tm)); tm_curr_time = *localtime(&curr_time); - curr_time = mktime(&tm_curr_time) - timezone; + curr_time = mktime(&tm_curr_time); //- timezone; return curr_time; } catch (...) diff --git a/main/main.cpp b/main/main.cpp index ffd6e8c..f5582e1 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -68,81 +68,87 @@ int main() { if (init_tariff(&tariff, "/etc/psa_tariff/")) { struct price_t price; memset(&price, 0x00, sizeof(price)); - QDateTime start = QDateTime::currentDateTime(); + QDateTime start = QDateTime::fromString("2023-05-11T07:50:00",Qt::ISODate); //QDateTime::currentDateTime(); time_t start_parking_time = start.toSecsSinceEpoch() / 60; - time_t end_parking_time = start_parking_time + 60; + time_t end_parking_time = start_parking_time + 610; if (compute_price_for_parking_ticket(tariff, start_parking_time, end_parking_time, &price)) { - qDebug() << "price=" << price.netto; + qDebug() << "GetCostFromDuration() => price=" << price.netto; } - // tests - struct tm now; - memset(&now, 0, sizeof(now)); - - // 3.Jan 2023 -> Tuesday - strptime("2023-01-03T14:00:00", "%Y-%m-%dT%H:%M:%S", &now); - for (int i = 0; i < 600; ++i) { - start_parking_time = mktime(&now); - end_parking_time = start_parking_time + 240; // duration == 240 - - if (compute_price_for_parking_ticket(tariff, - start_parking_time, - end_parking_time, - &price)) { - int const zone = get_zone_nr(); - switch (zone) { - case 1: - assert(price.netto == 879); // expected value: 880 - break; - case 2: - /* fall through */ - case 3: - assert(price.netto == 1920); - break; - } - } - time_t t = start_parking_time + 60; - now = *localtime(&t); + QString duration; + if(compute_duration_for_parking_ticket(tariff,start_parking_time,1525,duration)) + { + qDebug() << "GetDurationFromCost() => duration=" << duration; } - // - // test May 1st 2023 - // - memset(&now, 0, sizeof(now)); - strptime("2023-04-30T06:00:00", "%Y-%m-%dT%H:%M:%S", &now); - now.tm_hour -= 1; // for ctime - // for (int i=0; i<6*24; ++i) { - for (int i=0; i<1; ++i) { - int const duration = 120; - time_t t = mktime(&now); - start_parking_time = t / 60; - end_parking_time = start_parking_time + duration; - if (compute_price_for_parking_ticket(tariff, - start_parking_time, - end_parking_time, - &price)) { - int const zone = get_zone_nr(); - switch (zone) { - case 1: - qDebug() << i << zone << ctime(&t) << price.netto << " FT"; - assert(price.netto == 440); - break; - case 2: - /* fall through */ - case 3: - qDebug() << i << zone << ctime(&t) << price.netto << " FT"; - assert(price.netto == 960); - break; - } - } +// // tests +// struct tm now; +// memset(&now, 0, sizeof(now)); - t = (start_parking_time + 60)*60; - now = *localtime(&t); - } +// // 3.Jan 2023 -> Tuesday +// strptime("2023-01-03T14:00:00", "%Y-%m-%dT%H:%M:%S", &now); +// for (int i = 0; i < 600; ++i) { +// start_parking_time = mktime(&now); +// end_parking_time = start_parking_time + 240; // duration == 240 + +// if (compute_price_for_parking_ticket(tariff, +// start_parking_time, +// end_parking_time, +// &price)) { +// int const zone = get_zone_nr(1); +// switch (zone) { +// case 1: +// assert(price.netto == 879); // expected value: 880 +// break; +// case 2: +// /* fall through */ +// case 3: +// assert(price.netto == 1920); +// break; +// } +// } +// time_t t = start_parking_time + 60; +// now = *localtime(&t); +// } +// // +// // test May 1st 2023 +// // +// memset(&now, 0, sizeof(now)); +// strptime("2023-04-30T06:00:00", "%Y-%m-%dT%H:%M:%S", &now); +// now.tm_hour -= 1; // for ctime +// // for (int i=0; i<6*24; ++i) { +// for (int i=0; i<1; ++i) { +// int const duration = 120; +// time_t t = mktime(&now); +// start_parking_time = t / 60; +// end_parking_time = start_parking_time + duration; + +// if (compute_price_for_parking_ticket(tariff, +// start_parking_time, +// end_parking_time, +// &price)) { +// int const zone = get_zone_nr(); +// switch (zone) { +// case 1: +// qDebug() << i << zone << ctime(&t) << price.netto << " FT"; +// assert(price.netto == 440); +// break; +// case 2: +// /* fall through */ +// case 3: +// qDebug() << i << zone << ctime(&t) << price.netto << " FT"; +// assert(price.netto == 960); +// break; +// } +// } + +// t = (start_parking_time + 60)*60; +// now = *localtime(&t); +// } free_tariff(tariff); }