Compare commits

..

8 Commits

Author SHA1 Message Date
9d5ddfc328 GetCostFromDuration(): fixed moving from time range 8:00-12:00 -> 14:00-18:00. 2024-02-21 14:20:51 +01:00
102607b71f GetTimeSteps(): adapt for progressive tariff (e.g. neuhauser/kirchdorf).
Compute timesteps according to tariff01.json file.
2024-02-21 11:16:44 +01:00
81d515eb7f GetCostFromDuration(): set endtime to start in case cost==0. 2024-02-21 11:13:53 +01:00
ce61e5d3b2 compute_next_timestep(): use fpr PaymentMenthod::Progressive
same code as for PaymentMethod::Steps, as GetTimeSteps() inside
the Calculator class has been adapted accordingly.
2024-02-21 11:11:09 +01:00
b6a0f5e8af tests for neuhauser/kirchdorf 2024-02-21 11:08:49 +01:00
e0223b50f9 Added helper getTimeRangeStep() (useful for progressive tariff as for neuhauser/kirchdorf (743) 2024-02-21 09:31:13 +01:00
690267c388 get_maximal_parkingprice(): in case of progressive tariff, read maximal parking time
directly form tariff-file as there is not price per hour.
2024-02-20 15:47:02 +01:00
9c19414e5a GetDurationFromCost(): add some sanity checks for cost. Check if cost < minParkingPrice.
Cut cost internally to maxParkingPrice if cost > maxParkingPrice.
2024-02-20 13:47:45 +01:00
5 changed files with 139 additions and 86 deletions

View File

@@ -90,6 +90,7 @@ namespace Utilities {
uint32_t getMinimalParkingPrice(Configuration const *cfg, PaymentMethod methodId);
uint32_t getMaximalParkingPrice(Configuration const *cfg, PaymentMethod methodId);
uint32_t getFirstDurationStep(Configuration const *cfg, PaymentMethod methodId);
uint32_t getTimeRangeStep(Configuration const *cfg, int step, PaymentMethod methodId);
BusinessHours getBusinessHours(Configuration const *cfg, PaymentMethod methodId);
uint32_t computeWeekDaysPrice(Configuration const *cfg, PaymentMethod id);
double computeWeekDaysDurationUnit(Configuration const *cfg, PaymentMethod id);

View File

@@ -78,17 +78,22 @@ int CALCULATE_LIBRARY_API get_minimal_parkingprice(Configuration *cfg, PERMIT_TY
int CALCULATE_LIBRARY_API get_maximal_parkingprice(Configuration *cfg, PERMIT_TYPE permitType) {
int maxPrice = -1;
static const PaymentMethod paymentMethodId = Utilities::getPaymentMethodId(cfg);
switch(permitType) {
case PERMIT_TYPE::SHORT_TERM_PARKING: { // e.g. szeged (customer_281)
int const key = cfg->getPaymentOptions().pop_id;
int const maxTime = cfg->getPaymentOptions().pop_max_time; // maxTime is given in minutes
std::optional<QVector<ATBPaymentRate>> const &pv = cfg->getPaymentRateForKey(key);
if (pv) {
QVector<ATBPaymentRate> const &paymentRate = pv.value();
if (paymentRate.size() > 0) {
int const price = paymentRate.at(0).pra_price; // price is given per hour
maxPrice = qRound((maxTime * price) / 60.0f);
if (paymentMethodId == PaymentMethod::Progressive) {
maxPrice = Utilities::getMaximalParkingPrice(cfg, paymentMethodId);
} else { // PaymentMethod::Linear -> e.g. szeged
int const key = cfg->getPaymentOptions().pop_id;
int const maxTime = cfg->getPaymentOptions().pop_max_time; // maxTime is given in minutes
std::optional<QVector<ATBPaymentRate>> const &pv = cfg->getPaymentRateForKey(key);
if (pv) {
QVector<ATBPaymentRate> const &paymentRate = pv.value();
if (paymentRate.size() > 0) {
int const price = paymentRate.at(0).pra_price; // price is given per hour
maxPrice = qRound((maxTime * price) / 60.0f);
}
}
}
} break;
@@ -217,7 +222,9 @@ int CALCULATE_LIBRARY_API compute_next_timestep(parking_tariff_t *tariff, int cu
// use tariff with structure as for instance Schnau, Koenigsee:
// without given YearPeriod, SpecialDays and SpecialDaysWorktime
if (paymentMethodId == PaymentMethod::Steps)
if ((paymentMethodId == PaymentMethod::Steps) ||
// progressive tariff: e.g. Neuhauser, Kirchdorf (743)
(paymentMethodId == PaymentMethod::Progressive))
{
const QList<int> stepList = Calculator::GetInstance().GetTimeSteps(tariff);
qCritical() << " compute_next_timestep() timeSteps:" << stepList;

View File

@@ -157,6 +157,17 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg,
static const bool carryOverNotSet = Utilities::isCarryOverNotSet(cfg, paymentMethodId);
static const uint minParkingPrice = Utilities::getMinimalParkingPrice(cfg, paymentMethodId);
static const uint maxParkingPrice = Utilities::getMaximalParkingPrice(cfg, paymentMethodId);
if (cost < minParkingPrice) {
qCritical() << QString("ERROR: COST < MIN_PARKING_PRICE (%1 < %2)").arg(cost).arg(minParkingPrice);
return QDateTime().toString(Qt::ISODate).toStdString();
}
if (cost > maxParkingPrice) {
qCritical() << QString("WARN: COST > MAX_PARKING_PRICE (%1 > %2)").arg(cost).arg(maxParkingPrice);
cost = maxParkingPrice;
}
Q_ASSERT_X(carryOverNotSet, __func__, "CARRYOVER SET (FOR KIRCHDORF)");
Q_ASSERT_X(prepaid, __func__, "PREPAID NOT SET (FOR KIRCHDORF)");
@@ -279,6 +290,10 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg,
price += (uint)rate.pra_price;
if (price >= maxParkingPrice) {
price = maxParkingPrice;
}
durationMinutes -= duration;
durationMinutesNetto += duration;
durationMinutesBrutto += duration;
@@ -305,25 +320,31 @@ std::string Calculator::GetDurationFromCost(Configuration* cfg,
lastCurrent = current;
current.setTime(to);
int const minsLeft = lastCurrent.secsTo(current) / 60;
int const minsUsed = lastCurrent.secsTo(current) / 60;
// mod duration: possibly discard some minutes in
// the next time-range
minsToCarryOver = (durationMinutes - minsLeft) % duration;
minsToCarryOver = (durationMinutes - minsUsed) % duration;
durationMinutes -= minsLeft;
durationMinutesNetto += minsLeft;
durationMinutesBrutto += minsLeft;
durationMinutes -= minsUsed;
durationMinutesNetto += minsUsed;
durationMinutesBrutto += minsUsed;
if (minsLeft > 0) {
if (minsUsed > 0) {
for(const auto &x: cfg->PaymentRate) {
ATBPaymentRate const rate = x.second;
if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) {
price += (uint)rate.pra_price;
if (price >= maxParkingPrice) {
price = maxParkingPrice;
}
if (price >= cost) {
end_datetime = current;
// return end_datetime.toString(Qt::ISODate).toStdString();
}
break;
}
}
@@ -527,6 +548,7 @@ double Calculator::GetCostFromDuration(Configuration* cfg,
if (current.time() >= to) {
if (carryOverNotSet) {
end_datetime = start;
return 0;
} else {
QDateTime const dt = start;
@@ -547,6 +569,7 @@ double Calculator::GetCostFromDuration(Configuration* cfg,
if (carryOverNotSet) {
int range = 0;
int minsToCarryOver = 0; // from one work-time to the other on the same day
int minsUsed = 0;
QDateTime lastCurrent = QDateTime();
auto timeRangeIt = cfg->TimeRange.cbegin();
@@ -562,7 +585,6 @@ double Calculator::GetCostFromDuration(Configuration* cfg,
Q_ASSERT_X(from < to, __func__, "MISCONFIGURED WORK-TIMES");
if (current.time() >= to) {
continue; // try to use next available work-time
} else
@@ -588,18 +610,23 @@ double Calculator::GetCostFromDuration(Configuration* cfg,
int duration = timeRange.time_range_to_in_minutes_from_start -
timeRange.time_range_from_in_minutes_from_start;
if (current.addSecs(duration * 60).time() <= to) {
if (minsUsed > 0) {
duration -= minsUsed;
minsUsed = 0;
}
for(const auto &x: cfg->PaymentRate) {
ATBPaymentRate const rate = x.second;
if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) {
if (minsToCarryOver > 0) {
durationMinutes -= minsToCarryOver;
durationMinutesNetto += minsToCarryOver;
durationMinutesBrutto += minsToCarryOver;
current = current.addSecs(minsToCarryOver*60);
minsToCarryOver = 0;
} else {
if (current.addSecs(duration * 60).time() <= to) {
if (minsToCarryOver > 0) { // the price for this time range
// has been is paid already
durationMinutes -= duration;
durationMinutesNetto += duration;
durationMinutesBrutto += duration;
current = current.addSecs(duration*60);
minsToCarryOver = 0;
} else {
for(const auto &x: cfg->PaymentRate) {
ATBPaymentRate const rate = x.second;
if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) {
price += (uint)rate.pra_price;
durationMinutes -= duration;
@@ -607,8 +634,8 @@ double Calculator::GetCostFromDuration(Configuration* cfg,
durationMinutesBrutto += duration;
current = current.addSecs(duration * 60);
break;
}
break;
}
}
@@ -623,17 +650,19 @@ double Calculator::GetCostFromDuration(Configuration* cfg,
lastCurrent = current;
current.setTime(to);
int const minsLeft = lastCurrent.secsTo(current) / 60;
minsUsed = lastCurrent.secsTo(current) / 60;
// mod duration: possibly discard some minutes in
// the next time-range
minsToCarryOver = (durationMinutes - minsLeft) % duration;
if (durationMinutes >= minsUsed) {
minsToCarryOver = durationMinutes - minsUsed;
}
durationMinutes -= minsLeft;
durationMinutesNetto += minsLeft;
durationMinutesBrutto += minsLeft;
durationMinutes -= minsUsed;
durationMinutesNetto += minsUsed;
durationMinutesBrutto += minsUsed;
if (minsLeft > 0) {
if (minsUsed > 0) {
for(const auto &x: cfg->PaymentRate) {
ATBPaymentRate const rate = x.second;
if (rate.pra_payment_unit_id == timeRange.time_range_payment_type_id) {
@@ -642,7 +671,6 @@ double Calculator::GetCostFromDuration(Configuration* cfg,
}
}
}
break;
}
}
@@ -1145,6 +1173,8 @@ QList<int> Calculator::GetTimeSteps(Configuration *cfg) const {
int const pop_carry_over = cfg->getPaymentOptions().pop_carry_over;
int const pop_time_step_config = cfg->getPaymentOptions().pop_time_step_config;
static PaymentMethod const paymentMethodId = Utilities::getPaymentMethodId(cfg);
qCritical() << __func__ << ":" << __LINE__ << " start parking time:" << start.toString(Qt::ISODate);
qCritical() << __func__ << ":" << __LINE__ << " payment option id:" << pop_id;
qCritical() << __func__ << ":" << __LINE__ << "payment option carry over:" << pop_carry_over;
@@ -1152,63 +1182,71 @@ QList<int> Calculator::GetTimeSteps(Configuration *cfg) const {
if (pop_time_step_config == (int)ATBTimeStepConfig::TimeStepConfig::DYNAMIC) {
//qCritical() << __PRETTY_FUNCTION__ << "payment option time step config:" << "TimeStepConfig::DYNAMIC";
uint16_t timeStepCompensation = 0;
if (paymentMethodId == PaymentMethod::Progressive) { // e.g. neuhauser kirchdorf (743)
std::size_t const s = cfg->TimeRange.size();
for (std::size_t id = 1; id <= s; ++id) {
int const step = Utilities::getTimeRangeStep(cfg, id, paymentMethodId);
m_timeSteps.append(step);
}
} else {
uint16_t timeStepCompensation = 0;
if (pop_carry_over) {
int const pop_carry_over_time_range_id = cfg->getPaymentOptions().pop_carry_over_time_range_id;
QTime const carryOverTimeRangeFrom = cfg->TimeRange.find(pop_carry_over_time_range_id)->second.time_range_from;
QTime const carryOverTimeRangeTo = cfg->TimeRange.find(pop_carry_over_time_range_id)->second.time_range_to;
if (pop_carry_over) {
int const pop_carry_over_time_range_id = cfg->getPaymentOptions().pop_carry_over_time_range_id;
QTime const carryOverTimeRangeFrom = cfg->TimeRange.find(pop_carry_over_time_range_id)->second.time_range_from;
QTime const carryOverTimeRangeTo = cfg->TimeRange.find(pop_carry_over_time_range_id)->second.time_range_to;
if (carryOverTimeRangeFrom.secsTo(carryOverTimeRangeTo) <= 60) { // carry over time point, usually 00:00:00
if (carryOverTimeRangeFrom == QTime(0, 0, 0)) {
for (auto[itr, rangeEnd] = cfg->PaymentRate.equal_range(pop_id); itr != rangeEnd; ++itr) {
int const durationId = itr->second.pra_payment_unit_id;
auto search = cfg->Duration.find(durationId);
if (search != cfg->Duration.end()) {
ATBDuration duration = search->second;
if (durationId == 1) {
QDateTime carryOver = start;
carryOver = carryOver.addDays(1);
carryOver.setTime(QTime(0, 0, 0));
if (carryOverTimeRangeFrom.secsTo(carryOverTimeRangeTo) <= 60) { // carry over time point, usually 00:00:00
if (carryOverTimeRangeFrom == QTime(0, 0, 0)) {
for (auto[itr, rangeEnd] = cfg->PaymentRate.equal_range(pop_id); itr != rangeEnd; ++itr) {
int const durationId = itr->second.pra_payment_unit_id;
auto search = cfg->Duration.find(durationId);
if (search != cfg->Duration.end()) {
ATBDuration duration = search->second;
if (durationId == 1) {
QDateTime carryOver = start;
carryOver = carryOver.addDays(1);
carryOver.setTime(QTime(0, 0, 0));
int const timeStep = std::ceil(start.secsTo(carryOver) / 60.0);
if (timeStep < duration.pun_duration_min || timeStep > duration.pun_duration_max) {
qCritical()
<< QString("ERROR timeStep (%1) < durationMin (%2) || timeStep (%3)) > durationMax (%4)")
.arg(timeStep).arg(duration.pun_duration_min)
.arg(timeStep).arg(duration.pun_duration_max);
break;
int const timeStep = std::ceil(start.secsTo(carryOver) / 60.0);
if (timeStep < duration.pun_duration_min || timeStep > duration.pun_duration_max) {
qCritical()
<< QString("ERROR timeStep (%1) < durationMin (%2) || timeStep (%3)) > durationMax (%4)")
.arg(timeStep).arg(duration.pun_duration_min)
.arg(timeStep).arg(duration.pun_duration_max);
break;
}
qCritical() << __PRETTY_FUNCTION__ << "configured minimal parking time:" << cfg->getPaymentOptions().pop_min_time;
// set dynamic minimal parking time
cfg->getPaymentOptions().pop_min_time = timeStep;
qCritical() << __PRETTY_FUNCTION__ << " computed minimal parking time:" << cfg->getPaymentOptions().pop_min_time;
duration.pun_duration = timeStep;
timeStepCompensation = duration.pun_duration_max - duration.pun_duration;
m_timeSteps << duration.pun_duration;
} else {
duration.pun_duration = duration.pun_duration_max - timeStepCompensation;
m_timeSteps << duration.pun_duration;;
}
qCritical() << __PRETTY_FUNCTION__ << "configured minimal parking time:" << cfg->getPaymentOptions().pop_min_time;
// set dynamic minimal parking time
cfg->getPaymentOptions().pop_min_time = timeStep;
cfg->Duration.erase(search);
cfg->Duration.insert(pair<int, ATBDuration>(duration.pun_id, duration));
qCritical() << __PRETTY_FUNCTION__ << " computed minimal parking time:" << cfg->getPaymentOptions().pop_min_time;
duration.pun_duration = timeStep;
timeStepCompensation = duration.pun_duration_max - duration.pun_duration;
m_timeSteps << duration.pun_duration;
} else {
duration.pun_duration = duration.pun_duration_max - timeStepCompensation;
m_timeSteps << duration.pun_duration;;
} else { // if (search != cfg->Duration.end()) {
// TODO
}
cfg->Duration.erase(search);
cfg->Duration.insert(pair<int, ATBDuration>(duration.pun_id, duration));
} else { // if (search != cfg->Duration.end()) {
// TODO
}
} else { // if (carryOverTimeRangeFrom == QTime(0, 0, 0)) {
// TODO
}
} else { // if (carryOverTimeRangeFrom == QTime(0, 0, 0)) {
} else { // if (carryOverTimeRangeFrom == carryOverTimeRangeTo) {
// TODO
}
} else { // if (carryOverTimeRangeFrom == carryOverTimeRangeTo) {
} else { // if (pop_carry_over) {
// TODO
}
} else { // if (pop_carry_over) {
// TODO
}
} else {
qCritical() << __PRETTY_FUNCTION__ << "payment option time step config:" << "TimeStepConfig::STATIC";

View File

@@ -397,6 +397,14 @@ uint32_t Utilities::getMaximalParkingPrice(Configuration const *cfg, PaymentMeth
return std::max((int)cfg->PaymentOption.find(methodId)->second.pop_max_price, 0);
}
uint32_t Utilities::getTimeRangeStep(Configuration const *cfg, int step, PaymentMethod methodId) {
if (methodId == PaymentMethod::Progressive) {
return std::max((int)cfg->TimeRange.find(step)->second.time_range_to_in_minutes_from_start, 0);
}
return 0;
}
uint32_t Utilities::getFirstDurationStep(Configuration const *cfg, PaymentMethod methodId) {
int const popId = cfg->PaymentOption.find(methodId)->second.pop_id;

View File

@@ -115,12 +115,12 @@ int main() {
bool prePaid = true;
// bool carryOver = false;
QDateTime s(QDate(2023, 11, 30), QTime());
// QDateTime s(QDate(2023, 11, 26), QTime());
QDateTime s(QDate(2024, 2, 21), QTime());
QDateTime end;
for (int duration = 30; duration <= 90; duration += 5) {
// for (int duration = 30; duration <= maxParkingTime; duration += 5) {
qCritical() << "";
//for (int offset = 420; offset <= 1080; ++offset) {
for (int offset = 420; offset <= 1080; ++offset) {
//for (int offset = 420; offset <= 1080; ++offset) {
//if (offset > 720 && offset < 840) {
@@ -140,7 +140,6 @@ int main() {
duration_ist = duration_ist - 120;
}
qCritical() << "****" << offset << duration << "****";
qCritical() << " firstStart :" << firstStart.toString(Qt::ISODate);
qCritical() << " start :" << start.toString(Qt::ISODate);
@@ -150,16 +149,16 @@ int main() {
qCritical() << " cost (soll):" << cost_soll;
qCritical() << " cost (ist) :" << cost;
if (cost_soll != cost) {
// if (cost_soll != cost) {
//qCritical() << "ERROR" << __func__ << ":" << __LINE__
// << "cost_soll" << cost_soll << "cost_ist" << cost;
//break;
}
if (duration != duration_ist) {
// }
// if (duration != duration_ist) {
//qCritical() << "ERROR" << __func__ << ":" << __LINE__
// << "duration_soll" << duration << "duration_ist" << duration_ist;
//break;
}
// }
//#else
start = s.addSecs(offset * 60);