From ef66c1f0c04bc0a14fd17430cd29d78013aeadc3 Mon Sep 17 00:00:00 2001 From: Moran Rod Date: Fri, 12 May 2023 11:55:35 +0200 Subject: [PATCH] Fixed GetDurationFromCost() --- library/src/calculator_functions.cpp | 484 +++++++++++---------------- main/main.cpp | 14 +- 2 files changed, 211 insertions(+), 287 deletions(-) diff --git a/library/src/calculator_functions.cpp b/library/src/calculator_functions.cpp index d3ed4ed..39c4994 100644 --- a/library/src/calculator_functions.cpp +++ b/library/src/calculator_functions.cpp @@ -12,11 +12,11 @@ double total_cost = 0.0f; bool overtime = false; #ifdef _WIN32 - inline struct tm* localtime_r(const time_t *clock, struct tm* result){ +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 /// @@ -27,292 +27,216 @@ 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); + } + + 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.addSecs(-(int)ceil(calc_price) * 60); + } + ret_val = total_duration_min; + + if(price >= min_price && total_duration_min >= minMin) + qDebug() << "Valid until: " << inputDate.toString(Qt::ISODate); + else qDebug() << "Parking not allowed"; + + 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(); } /////////////////////////////////////// diff --git a/main/main.cpp b/main/main.cpp index c9e2e5f..db78f5e 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -36,20 +36,20 @@ int main() { memset(&price, 0x00, sizeof(price)); 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 + 1230; + time_t end_parking_time = start_parking_time + 1236; if (compute_price_for_parking_ticket(tariff, start_parking_time, end_parking_time, &price)) { - qDebug() << "price=" << price.netto; + qDebug() << "GetCostFromDuration() => price=" << price.netto; } -// QString duration; -// if(compute_duration_for_parking_ticket(tariff,start_parking_time,3090,duration)) -// { -// qDebug() << "duration=" << duration; -// } + QString duration; + if(compute_duration_for_parking_ticket(tariff,start_parking_time,3090,duration)) + { + qDebug() << "GetDurationFromCost() => duration=" << duration; + } // // tests // struct tm now;