diff --git a/library/src/calculator_functions.cpp b/library/src/calculator_functions.cpp index abb985c..f3cdd26 100644 --- a/library/src/calculator_functions.cpp +++ b/library/src/calculator_functions.cpp @@ -427,6 +427,10 @@ int Calculator::getMaximalParkingTime(Configuration const *cfg, PaymentMethod me return std::max((int)cfg->PaymentOption.find(methodId)->second.pop_max_time, 0); } +uint32_t Calculator::getMinimalParkingPrice(Configuration const *cfg, PaymentMethod methodId) { + return std::max((int)cfg->PaymentOption.find(methodId)->second.pop_min_price, 0); +} + bool Calculator::checkDurationMinutes(int minParkingTime, int maxParkingTime, int durationMinutes) { @@ -666,6 +670,209 @@ Ticket Calculator::private_GetCostFromDuration(Configuration const* cfg, } +Ticket Calculator::private_GetDurationFromCost(Configuration *cfg, + QDateTime const &start, + uint32_t price, + bool prepaid) { + // Get input date + QDateTime current = start; + + // use tariff with structure as for instance Schnau, Koenigsee: + // without given YearPeriod, SpecialDays and SpecialDaysWorktime + if (cfg->YearPeriod.size() == 0 + && cfg->SpecialDays.size() == 0 + && cfg->SpecialDaysWorktime.size() == 0) + { + uint64_t const durationMinutes = GetDurationForPrice(cfg, price); + uint64_t const durationSeconds = durationMinutes * 60; + current = current.addSecs(durationSeconds); + + return + Ticket(start, current, durationMinutes, durationMinutes, + price, Ticket::s[VALID]); + } + + static const PaymentMethod paymentMethodId = Utilities::getPaymentMethodId(cfg); + static const bool carryOverNotSet = isCarryOverNotSet(cfg, paymentMethodId); + static const uint32_t minParkingTimeMinutes = std::max(getMinimalParkingTime(cfg, paymentMethodId), 0); + static const uint32_t maxParkingTimeMinutes = std::max(getMaximalParkingTime(cfg, paymentMethodId), 0); + static const uint32_t minParkingPrice = getMinimalParkingPrice(cfg, paymentMethodId); + static const bool checkMinMaxMinutes = (minParkingTimeMinutes < maxParkingTimeMinutes); + static const uint32_t weekDaysPrice = computeWeekDaysPrice(cfg, paymentMethodId); + static const double weekDaysDurationUnit = computeWeekDaysDurationUnit(cfg, paymentMethodId); + static const double specialDaysDurationUnit = 60.0; + + if(price < minParkingPrice) { + uint64_t const durationMinutes = GetDurationForPrice(cfg, price); + return Ticket(start, current, durationMinutes, durationMinutes, + price, Ticket::s[INVALID_PRICE]); + } + if (minParkingTimeMinutes >= maxParkingTimeMinutes) { + // TODO + return Ticket(); + } + if (maxParkingTimeMinutes <= minParkingTimeMinutes) { + // TODO + return Ticket(); + } + + uint32_t durationMinutesNetto = 0; + int moneyLeft = price; + //double durationUnit = 0.0; + int specialDayId = -1; + bool isSpecialDay = false; + QDateTime end = start; + int totalTimeRanges = 0; + + for (current = start; moneyLeft > 0; current = current.addDays(1)) { + int const weekdayId = current.date().dayOfWeek(); + + specialDayId = -1; + + // find worktime ranges for the current day + int const timeRanges = std::max((int)cfg->WeekDaysWorktime.count(weekdayId), 1); + QScopedArrayPointer worktime(new TariffTimeRange[timeRanges]); + int ranges = 0; + + if((isSpecialDay = Utilities::CheckSpecialDay(cfg, current, &specialDayId, &price))) { + // Set special day price: + // durationUnit = specialDaysDurationUnit; + worktime[ranges].setTimeRange(SpecialDaysWorkTimeFrom(cfg, specialDayId), + SpecialDaysWorkTimeUntil(cfg, specialDayId)); + ranges = 1; + } else { + // Set new price for the normal day: do not use a floating-point type + // for the price, rather compute with integers. Only at the very end of + // the computation the price is divided by durationUnit. + price = weekDaysPrice; + // durationUnit = weekDaysDurationUnit; + + // If no working day found, skip it (epsecially Sundays!) + if (cfg->WeekDaysWorktime.count(weekdayId) <= 0) { + qDebug() << "No workday found, trying to find next available day"; + end = current; + current.setTime(QTime()); // start at midnight on the next day + continue; + } + + using WTIterator = std::multimap::const_iterator; + std::pair p = cfg->WeekDaysWorktime.equal_range(weekdayId); + + for (WTIterator itr = p.first; itr != p.second; ++itr) { + worktime[ranges].setTimeRange(WeekDaysWorkTimeFrom(itr), + WeekDaysWorkTimeUntil(itr)); + ranges += 1; + } + } + + QTime const &lastWorktimeTo = worktime[ranges-1].getTimeUntil(); + + // find worktime range to start with + + int currentRange = 0; + if (!isSpecialDay) { + if (start != current) { // on next day + current.setTime(worktime[currentRange].getTimeFrom()); + } else { + // check if inputDate is located inside a valid worktime-range... + currentRange = findWorkTimeRange(current, worktime, ranges); + if (currentRange == -1) { // no... + if (!prepaid) { // parking is not allowed + return Ticket(start, QDateTime(), durationMinutesNetto, 0, + 0, Ticket::s[INVALID_FROM_DATETIME]); + } + // find the next worktime-range (on the same day), and start from there + currentRange = findNextWorkTimeRange(current, worktime, ranges); + current.setTime(worktime[currentRange].getTimeFrom()); + } + } + } + + for (int w = currentRange; w < ranges; ++w, ++totalTimeRanges) { + if (moneyLeft > 0) { + QTime const &worktime_from = worktime[w].getTimeFrom(); + QTime const &worktime_to = worktime[w].getTimeUntil(); + + if (totalTimeRanges) { + // durationMinutes are always meant as netto time and + // the time between worktime-ranges are free. + current.setTime(worktime_from); + } + + if (price == 0) { + // inputDate = inputDate.addDays(1); + // inputDate.setTime(worktime_from); + end = current; + current.setTime(QTime()); + continue; + } + + if (current.time() == worktime_to) { + end = current; + current.setTime(QTime()); + continue; + } + + // Check prepaid + if (!prepaid) { + if ((current.time() < worktime_from) || (current.time() > worktime_to)) { + qDebug() << "[STOP] * Ticket is not valid * "; + return Ticket(); + } + } else { + qDebug() << "* PREPAID MODE ACTIVE *"; + if (current.time() < worktime_from) { + current.setTime(worktime_from); + end = current; + } else if(current.time() > lastWorktimeTo) { + qDebug() << " *** PREPAID *** Current time is past the time range end, searching for next available day"; + end = current; + current.setTime(QTime()); + continue; + } + } + + while(moneyLeft > 0) { + // Check for active year period + if (!IsYearPeriodActive(cfg, current)) { + return Ticket(); + } + if(durationMinutesNetto > maxParkingTimeMinutes) { + durationMinutesNetto = maxParkingTimeMinutes; + break; + } + if(current.time() >= lastWorktimeTo) { + // Go to next day if minutes not spent + if (carryOverNotSet) { + // no carry_over, so stop computation + break; + } + current.setTime(QTime()); + break; // stop while, and continue in outer loop + } else { + if(current.time() < worktime_to) { + // Increment input date minutes for each monetary unit + if(moneyLeft > 1) { + current = current.addSecs(60); + } + end = current; + if(price > 0) { + durationMinutesNetto +=1; + } + moneyLeft -= price; + } else break; + } + } // while(durationMinutes > 0) { + } // if (durationMinutes > 0) { + } // for (int w = currentRange; w < ranges; ++w, ++totalTimeRanges) { + } // for (current = start; durationMinutes > 0; current = current.addDays(1)) { + + int durationMinutesBrutto = start.secsTo(end) / 60; + + return + Ticket(start, end, durationMinutesNetto, durationMinutesBrutto, + price, Ticket::s[VALID]); +} QList Calculator::GetTimeSteps(Configuration *cfg) const { QList timeSteps;