410 lines
16 KiB
C++
410 lines
16 KiB
C++
#include "calculate_parking_tickets_algorithms.h"
|
|
#include "calculate_parking_tickets_global.h"
|
|
#include "calculate_parking_tickets_tariff_step.h"
|
|
#include "calculate_parking_tickets_utils.h"
|
|
#include "calculate_parking_tickets_tariff.h"
|
|
#include "calculate_parking_tickets_day.h"
|
|
|
|
#include <QDebug>
|
|
#include <QDateTime>
|
|
#include <cmath>
|
|
|
|
|
|
/* BEGRIFFE
|
|
bezahltes Parken am Ende der Parkdauer vor Einsteigen ins Auto:
|
|
pay on foot parking
|
|
bezahltes Parken am Anfang der Parkdauer und Quittung an der Windschutzscheibe
|
|
pay and display parking
|
|
Parkdauer
|
|
parking time
|
|
Parkschein
|
|
parking voucher
|
|
Parkgutschein
|
|
parking voucher
|
|
Maximale Parkdauer
|
|
maximal parking time
|
|
*/
|
|
|
|
#if 0
|
|
bool get_price(parking_tariff_t const *tariff, QDateTime const &qdt,
|
|
uint32_t &price_units) {
|
|
QDate const &qdate = qdt.date();
|
|
QTime const &qtime = qdt.time();
|
|
qDebug() << qdate << qtime;
|
|
if (!TUtils::isHoliday(tariff, qdate)) {
|
|
int const dayOfWeek = qdate.dayOfWeek();
|
|
switch (dayOfWeek) {
|
|
case Qt::Monday:
|
|
return Monday::price(tariff, qtime, &price_units);
|
|
case Qt::Tuesday:
|
|
return Tuesday::price(tariff, qtime, &price_units);
|
|
case Qt::Wednesday:
|
|
return Wednesday::price(tariff, qtime, &price_units);
|
|
case Qt::Thursday:
|
|
return Thursday::price(tariff, qtime, &price_units);
|
|
case Qt::Friday:
|
|
return Friday::price(tariff, qtime, &price_units);
|
|
case Qt::Saturday:
|
|
return Saturday::price(tariff, qtime, &price_units);
|
|
case Qt::Sunday:
|
|
return Sunday::price(tariff, qtime, &price_units);
|
|
}
|
|
}
|
|
return false;
|
|
// return Holiday::price(tariff, qtime, &price_units);
|
|
}
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
static minute_t computeEffectiveStartMinute(parking_tariff_t const *tariff,
|
|
minute_t startMinute) {
|
|
QDateTime const start = TUtils::fromMinutes(startMinute);
|
|
minute_t free_of_charge = 0;
|
|
minute_t startMinuteDay = start.time().hour()*60 +start.time().minute();
|
|
if (startMinuteDay >= tariff->day_tariff_start &&
|
|
startMinuteDay < tariff->day_tariff_end) {
|
|
free_of_charge = tariff->free_of_charge_day_tariff;
|
|
} else
|
|
if ((startMinuteDay >= tariff->night_tariff_start && startMinuteDay < MINUTES_PER_DAY) ||
|
|
(startMinuteDay >= 0 && startMinuteDay < tariff->night_tariff_end)) {
|
|
free_of_charge = tariff->free_of_charge_night_tariff;
|
|
}
|
|
|
|
return (startMinute + free_of_charge);
|
|
}
|
|
|
|
static bool checkCalculatePreConditions(parking_tariff_t const* tariff,
|
|
minute_t startMinute, // CET
|
|
minute_t endMinute, // CET
|
|
struct price_t *price) {
|
|
|
|
if (startMinute > endMinute) {
|
|
qCritical() << "startMinute > endMinute ("
|
|
<< startMinute << endMinute << ")";
|
|
return false;
|
|
}
|
|
|
|
minute_t const parkingTime = endMinute - startMinute;
|
|
|
|
if (parkingTime == 0) {
|
|
qCritical() << "startMinute == endMinute => no parking time";
|
|
return false;
|
|
}
|
|
|
|
if (tariff->parking_time_max != -1) {
|
|
if (parkingTime > tariff->parking_time_max) {
|
|
qCritical() << "parkingTime" << parkingTime
|
|
<< ">" << tariff->parking_time_max;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
QDateTime const start = TUtils::fromMinutes(startMinute);
|
|
//minute_t free_of_charge = 0;
|
|
//minute_t startMinuteDay = start.time().hour()*60 +start.time().minute();
|
|
//if (startMinuteDay >= tariff->day_tariff_start &&
|
|
// startMinuteDay < tariff->day_tariff_end) {
|
|
// free_of_charge = tariff->free_of_charge_day_tariff;
|
|
//} else
|
|
//if ((startMinuteDay >= tariff->night_tariff_start && startMinuteDay < MINUTES_PER_DAY) ||
|
|
// (startMinuteDay >= 0 && startMinuteDay < tariff->night_tariff_end)) {
|
|
// free_of_charge = tariff->free_of_charge_night_tariff;
|
|
//}
|
|
|
|
minute_t const effectiveStartMinute = computeEffectiveStartMinute(tariff, startMinute);
|
|
QDateTime const end = TUtils::fromMinutes(endMinute);
|
|
|
|
if (effectiveStartMinute > endMinute) {
|
|
fprintf(stderr,
|
|
"[start:%02d:%02d, ende:%02d:%02d]: %ld free minutes. no charge.\n",
|
|
start.time().hour(), start.time().minute(),
|
|
end.time().hour(), end.time().minute(), tariff->free_of_charge_day_tariff);
|
|
memset(price, 0x00, sizeof(*price));
|
|
return false;
|
|
}
|
|
|
|
// start algorithm
|
|
return true;
|
|
}
|
|
|
|
#define DEBUG_INTERNAL 0
|
|
|
|
TariffStep const *
|
|
compute_price_for_parking_ticket_internal(parking_tariff_t const *tariff,
|
|
minute_t startMinute, // CET
|
|
minute_t endMinute, // CET
|
|
struct price_t *price) {
|
|
TariffStep const *stepDbg = nullptr;
|
|
|
|
QDateTime const start = TUtils::fromMinutes(startMinute);
|
|
QDateTime const end = TUtils::fromMinutes(endMinute);
|
|
|
|
int const minute = start.time().minute();
|
|
int const hour = start.time().hour();
|
|
int const day = start.date().dayOfWeek()-1;
|
|
int const week = 0;
|
|
|
|
// printf("%d%d%04d\n", week, day, hour*60+minute);
|
|
|
|
TariffStep const *step = &tariff->m_tariffSteps[week][day][hour*60+minute];
|
|
|
|
// TODO: tageslimit muss beruecksichtigt werden
|
|
stepDbg = step;
|
|
|
|
|
|
uint32_t price_units = 0;
|
|
//if (tariff->max_price_for_24_hours == (uint32_t)(-1)) {
|
|
if (tariff->basic_tariff) {
|
|
while (step->dateTime() < end) {
|
|
price_units += step->price();
|
|
step = step->next();
|
|
}
|
|
}
|
|
if (tariff->max_price_24h_after_arrival){
|
|
QDateTime stepDateTime = step->dateTime();
|
|
QDateTime nextStepDateTime;
|
|
// QDateTime prevStepDateTime;
|
|
QDateTime _24hDateTime = stepDateTime;
|
|
uint32_t _24h_price_units = 0;
|
|
long t0 = _24hDateTime.toSecsSinceEpoch();
|
|
bool dbg = true;
|
|
while (stepDateTime < end) {
|
|
long const t1 = stepDateTime.toSecsSinceEpoch();
|
|
uint32_t const price_units_tmp = step->price();
|
|
step = step->next();
|
|
nextStepDateTime = step->dateTime();
|
|
long const diff = t1 - t0;
|
|
if (diff < SECS_PER_DAY) {
|
|
_24h_price_units += price_units_tmp;
|
|
if (_24h_price_units > tariff->max_price_for_24_hours) {
|
|
_24h_price_units = tariff->max_price_for_24_hours;
|
|
if (dbg) {
|
|
#if DEBUG_INTERNAL
|
|
fprintf(stderr,
|
|
"[%s:%02d:%02d %d]\n",
|
|
stepDateTime.date().toString().toStdString().c_str(),
|
|
stepDateTime.time().hour(), stepDateTime.time().minute(),
|
|
_24h_price_units);
|
|
dbg = false;
|
|
#endif
|
|
}
|
|
} else {
|
|
#if DEBUG_INTERNAL
|
|
dbg = true;
|
|
fprintf(stderr,
|
|
"[%s:%02d:%02d %d]\n",
|
|
stepDateTime.date().toString().toStdString().c_str(),
|
|
stepDateTime.time().hour(), stepDateTime.time().minute(),
|
|
_24h_price_units);
|
|
#endif
|
|
}
|
|
} else {
|
|
price_units += _24h_price_units;
|
|
_24h_price_units = price_units_tmp;
|
|
#if DEBUG_INTERNAL
|
|
fprintf(stderr,
|
|
"[%s:%02d:%02d 24h max. %d]\n",
|
|
stepDateTime.date().toString().toStdString().c_str(),
|
|
stepDateTime.time().hour(), stepDateTime.time().minute(),
|
|
price_units);
|
|
fprintf(stderr,
|
|
"[%s:%02d:%02d %d]\n",
|
|
stepDateTime.date().toString().toStdString().c_str(),
|
|
stepDateTime.time().hour(), stepDateTime.time().minute(),
|
|
_24h_price_units);
|
|
#endif
|
|
|
|
_24hDateTime = stepDateTime;
|
|
t0 = _24hDateTime.toSecsSinceEpoch();
|
|
}
|
|
// prevStepDateTime = stepDateTime;
|
|
stepDateTime = nextStepDateTime;
|
|
}
|
|
price_units += _24h_price_units;
|
|
}
|
|
if (tariff->max_price_at_midnight) {
|
|
// restart pricing at midnight if current price is max_price_at_midnight
|
|
QDateTime stepDateTime = step->dateTime();
|
|
QDateTime nextStepDateTime;
|
|
QDateTime midnight(stepDateTime.date().addDays(1), QTime(0,0));
|
|
uint32_t day_price_units = 0;
|
|
bool dbg = true;
|
|
while (stepDateTime < end) {
|
|
uint32_t const price_units_tmp = step->price();
|
|
step = step->next();
|
|
nextStepDateTime = step->dateTime();
|
|
if (stepDateTime < midnight) {
|
|
day_price_units += price_units_tmp;
|
|
if (day_price_units > tariff->max_price_at_midnight) {
|
|
day_price_units = tariff->max_price_at_midnight;
|
|
if (dbg) {
|
|
#if DEBUG_INTERNAL
|
|
fprintf(stderr,
|
|
"[%s:%02d:%02d price_so_far=%d day_price=%d]\n",
|
|
stepDateTime.date().toString().toStdString().c_str(),
|
|
stepDateTime.time().hour(), stepDateTime.time().minute(),
|
|
price_units, day_price_units);
|
|
//dbg = false;
|
|
#endif
|
|
}
|
|
} else {
|
|
#if DEBUG_INTERNAL
|
|
dbg = true;
|
|
fprintf(stderr,
|
|
"[%s:%02d:%02d price_so_far=%d day_price=%d]\n",
|
|
stepDateTime.date().toString().toStdString().c_str(),
|
|
stepDateTime.time().hour(), stepDateTime.time().minute(),
|
|
price_units, day_price_units);
|
|
#endif
|
|
}
|
|
} else {
|
|
if (day_price_units == tariff->max_price_at_midnight) {
|
|
price_units += day_price_units;
|
|
day_price_units = 0;
|
|
} else {
|
|
day_price_units += price_units_tmp;
|
|
}
|
|
#if DEBUG_INTERNAL
|
|
fprintf(stderr,
|
|
"[%s:%02d:%02d 24h max. price_so_far=%d day_price=%d]\n",
|
|
stepDateTime.date().toString().toStdString().c_str(),
|
|
stepDateTime.time().hour(), stepDateTime.time().minute(),
|
|
price_units, day_price_units);
|
|
#endif
|
|
midnight = stepDateTime;
|
|
midnight.setDate(midnight.date().addDays(1));
|
|
midnight.setTime(QTime(0, 0));
|
|
}
|
|
stepDateTime = nextStepDateTime;
|
|
}
|
|
price_units += day_price_units;
|
|
}
|
|
|
|
price->units = price_units;
|
|
price->netto = roundf((price->units * tariff->unit_scale_factor) / tariff->unit_definition);
|
|
price->vat_percentage = tariff->vat;
|
|
price->vat = price->netto * price->vat_percentage;
|
|
price->brutto = price->netto + price->vat;
|
|
|
|
return stepDbg;
|
|
}
|
|
|
|
bool compute_price_for_parking_ticket(parking_tariff_t const *tariff,
|
|
minute_t startMinute, // CET
|
|
minute_t endMinute, // CET
|
|
struct price_t *price) {
|
|
|
|
if (!checkCalculatePreConditions(tariff, startMinute, endMinute, price)) { // CET
|
|
return false;
|
|
}
|
|
|
|
// minute_t const parkingTime = endMinute - startMinute;
|
|
memset(price, 0x00, sizeof(*price));
|
|
|
|
minute_t const effectiveStartMinute = computeEffectiveStartMinute(tariff, startMinute);
|
|
// QDateTime const start = TUtils::fromMinutes(startMinute);
|
|
// QDateTime const effStart = TUtils::fromMinutes(effectiveStartMinute);
|
|
// QDateTime const end = TUtils::fromMinutes(endMinute);
|
|
|
|
//qDebug() << effStart;
|
|
//qDebug() << end;
|
|
|
|
TariffStep const *stepDbg =
|
|
compute_price_for_parking_ticket_internal(tariff,
|
|
effectiveStartMinute,
|
|
endMinute, price);
|
|
if (stepDbg) {
|
|
#if 0
|
|
if (tariff->free_of_charge == 0) {
|
|
fprintf(stderr,
|
|
"[EINFAHRT: %s:%02d:%02d, AUSFAHRT: %s:%02d:%02d]\n\t",
|
|
effStart.date().toString().toStdString().c_str(),
|
|
effStart.time().hour(), effStart.time().minute(),
|
|
end.date().toString().toStdString().c_str(),
|
|
end.time().hour(), end.time().minute());
|
|
} else {
|
|
fprintf(stderr,
|
|
"[EINFAHRT: %s:%02d:%02d, %ld Minuten frei -> "
|
|
"EFF. EINFAHRT: %s:%02d:%02d, AUSFAHRT: %s:%02d:%02d]\n\t",
|
|
start.date().toString().toStdString().c_str(),
|
|
start.time().hour(), start.time().minute(),
|
|
tariff->free_of_charge,
|
|
effStart.date().toString().toStdString().c_str(),
|
|
effStart.time().hour(), effStart.time().minute(),
|
|
end.date().toString().toStdString().c_str(),
|
|
end.time().hour(), end.time().minute());
|
|
}
|
|
int cnt = -1;
|
|
while (stepDbg->dateTime() < end) {
|
|
// qDebug() << price_units << step->price();
|
|
fprintf(stderr, "%s %02d:%02d +%u |",
|
|
(++cnt == 0) ? "|" : "",
|
|
stepDbg->dateTime().time().hour(),
|
|
stepDbg->dateTime().time().minute(),
|
|
stepDbg->price());
|
|
//price_units += stepDbg->price();
|
|
//qDebug() << price_units << step->price();
|
|
stepDbg = stepDbg->next();
|
|
}
|
|
#endif
|
|
|
|
if (price->units > tariff->max_parking_price_units) {
|
|
qCritical() << "gesamt" << price->units
|
|
<< ">" << tariff->max_parking_price_units;
|
|
return false;
|
|
}
|
|
|
|
fprintf(stderr, " : gesamt %.2f (netto: %.2f)\n", price->brutto/100, price->netto/100);
|
|
return true;
|
|
|
|
} else {
|
|
qCritical() << "compute_price_for_parking_ticket";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool compute_duration_for_parking_ticket(parking_tariff_t const *tariff,
|
|
minute_t start_parking_time,
|
|
minute_t *end_parking_time,
|
|
struct price_t const *price) {
|
|
price_t p;
|
|
for (int i = 0; i < tariff->parking_time_max; ++i) {
|
|
minute_t end_time = start_parking_time + i;
|
|
if (compute_price_for_parking_ticket(tariff, start_parking_time,
|
|
end_time, &p)) {
|
|
if (p.netto >= price->netto) {
|
|
*end_parking_time = end_time;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool compute_price_for_tages_ticket(parking_tariff_t *tariff,
|
|
time_t parking_time_start, // minutes
|
|
struct price_t *price) {
|
|
QDateTime dateTime = TUtils::fromMinutes(parking_time_start).addDays(1);
|
|
dateTime.setTime(QTime(0, 0));
|
|
time_t parking_time_end = dateTime.toSecsSinceEpoch() / 60;
|
|
return compute_price_for_parking_ticket(tariff, parking_time_start,
|
|
parking_time_end, price);
|
|
}
|
|
|
|
bool compute_price_for_24h_ticket(parking_tariff_t *tariff,
|
|
time_t parking_time_start, // minutes
|
|
struct price_t *price) {
|
|
QDateTime dateTime = TUtils::fromMinutes(parking_time_start).addDays(1);
|
|
time_t parking_time_end = dateTime.toSecsSinceEpoch() / 60;
|
|
return compute_price_for_parking_ticket(tariff, parking_time_start,
|
|
parking_time_end, price);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|