#include "calculator_functions.h" #include "payment_method.h" #include "utilities.h" #include "tariff_log.h" #include double total_duration_min = 0.0f; double total_cost = 0.0f; bool overtime = false; /// std::string Calculator::GetDurationFromCost(Configuration* cfg, uint8_t payment_option, char const* start_datetime, double price, bool nextDay, bool prepaid) { // Get current date time from input struct tm current_datetime = Utilities::DateTimeToStructTm(start_datetime); time_t current_datetime_t; // Get day of week DayOfWeek weekdayId = DayOfWeek::UndefinedDay; weekdayId = Utilities::GetDayOfWeek(¤t_datetime); //std::stringstream ss; // ss << "*** Input date is: " << start_datetime << " [weekday id = " << weekdayId << "]" << endl; LOG_DEBUG("*** Input date is: ", start_datetime, " [weekday id = ", weekdayId, "]"); 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"; struct tm valid_until_datetime = *localtime(&valid_until_datetime_t); LOG_DEBUG("Ticket is valid until ", asctime(&valid_until_datetime)); total_duration_min = 0.0f; return asctime(&valid_until_datetime); } /////////////////////////////////////// /// 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 day of week DayOfWeek weekdayId = DayOfWeek::UndefinedDay; weekdayId = Utilities::GetDayOfWeek(¤t_datetime); LOG_DEBUG("*** Input date is: ", start_datetime, " [weekday id = ", weekdayId, "]"); 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 0.0f; } if (maxMin <= minMin) { LOG_ERROR("Error: max_min cannot be lower or equal than min_min"); return 0.0f; } 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; } } 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); total_duration_min = durationMin; LOG_DEBUG("Total min:", total_duration_min); 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 floor(GetCostFromDuration(cfg, payment_option, start_datetime, durationMin, true, prepaid)); } 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); // 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 0.0f; } } 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 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; // return ceil(ret_val); return floor(ret_val); }