#include "calculator_functions.h" #include "payment_method.h" #include "utilities.h" #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, char const* start_datetime, // given in local time 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"; #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"; } // 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); LOG_DEBUG("Ticket is valid until ", buf); total_duration_min = 0.0f; return std::string(buf); } /////////////////////////////////////// /// double Calculator::GetCostFromDuration(Configuration* cfg, uint8_t payment_option, const char* start_datetime, double durationMin, bool nextDay, bool prepaid) { // Get input date QDateTime inputDate = QDateTime::fromString(start_datetime,Qt::ISODate); // Get day of week int weekdayId = 0; weekdayId = Utilities::ZellersAlgorithm(inputDate.date().day(),inputDate.date().month(),inputDate.date().year()); //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 (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; } // Check overtime if (!overtime) { if (durationMin > maxMin) { LOG_WARNING("Total duration is greater or equal to max_min"); return maxMin; } 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); // 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); total_duration_min = durationMin; LOG_DEBUG("Total min:", total_duration_min); 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 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 (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 GetCostFromDuration(cfg, payment_option, inputDate.toString(Qt::ISODate).toStdString().c_str(), durationMin, true, prepaid); } // 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 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); } } while(true) { if(total_duration_min <= 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 0.0f; } // 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; 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); } // Increment input date minutes for each monetary unit inputDate = inputDate.addSecs(60); total_duration_min -=1; total_cost += price_per_unit; } qDebug() << "Valid until:" << inputDate.toString(Qt::ISODate).toStdString().c_str(); double ret_val = total_cost; total_cost = 0.0f; return ceil(ret_val); }