247 lines
7.3 KiB
C++
247 lines
7.3 KiB
C++
#include "day_manager.h"
|
|
|
|
#include <QRandomGenerator>
|
|
#include <algorithm>
|
|
|
|
DayManager::DayManager(const QTime &begin, const QTime &end) : DayManager() {
|
|
addAvailableSlot(begin, end);
|
|
}
|
|
|
|
void DayManager::addAvailableSlot(const QTime &begin, const QTime &end) {
|
|
if (!begin.isValid() || !end.isValid() || begin >= end) {
|
|
qWarning() << "DayManager::addAvailableSlot: begin or end is invalid";
|
|
return;
|
|
}
|
|
if (begin.msecsTo(end) >= minDurationMs_) {
|
|
availableSlots_.insert(begin, end);
|
|
}
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserve(const QTime &duration) {
|
|
if (!duration.isValid()) {
|
|
qWarning() << "DayManager::reserve: duration is invalid";
|
|
return {};
|
|
}
|
|
|
|
const qint64 requiredMs = duration.msecsSinceStartOfDay();
|
|
if (requiredMs <= 0) {
|
|
qWarning() << "DayManager::reserve: duration is invalid";
|
|
return {};
|
|
}
|
|
|
|
QPair<QTime, QTime> result = reserveImpl(requiredMs);
|
|
if (!isAvailable(result.first, result.second)) {
|
|
return {};
|
|
}
|
|
|
|
++createdRecords_;
|
|
return result;
|
|
}
|
|
|
|
template <typename... Times>
|
|
bool DayManager::isAvailable(const QTime &first, const Times &...others) const {
|
|
static const auto isAvailable = [](const QTime &time) {
|
|
return time.isValid();
|
|
};
|
|
return isAvailable(first) && isAvailable(others...);
|
|
}
|
|
|
|
QList<std::reference_wrapper<const QTime>>
|
|
DayManager::availableSlots(qint64 requiredMs) const {
|
|
QList<std::reference_wrapper<const QTime>> result;
|
|
for (auto const &[begin, end] : availableSlots_.asKeyValueRange()) {
|
|
if (begin.msecsTo(end) >= requiredMs) {
|
|
result.append(begin);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveAvailableSlotLeft(QTime begin,
|
|
qint64 requiredMs) {
|
|
QTime end = availableSlots_.value(begin);
|
|
|
|
QTime reservedEnd = begin.addMSecs(requiredMs);
|
|
if (reservedEnd < end) {
|
|
addAvailableSlot(reservedEnd, end);
|
|
}
|
|
availableSlots_.remove(begin);
|
|
|
|
return {begin, reservedEnd};
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveAvailableSlotRight(QTime begin,
|
|
qint64 requiredMs) {
|
|
QTime end = availableSlots_.value(begin);
|
|
availableSlots_.remove(begin);
|
|
|
|
QTime reservedBegin = end.addMSecs(-requiredMs);
|
|
if (reservedBegin > begin) {
|
|
addAvailableSlot(begin, reservedBegin);
|
|
}
|
|
|
|
return {reservedBegin, end};
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveAvailableSlotRandom(QTime begin,
|
|
qint64 requiredMs) {
|
|
QTime end = availableSlots_.value(begin);
|
|
if (!end.isValid()) {
|
|
qCritical() << "DayManager::reserveAvailableSlotRandom: end is invalid";
|
|
}
|
|
|
|
qint64 beginMs = begin.msecsSinceStartOfDay();
|
|
qint64 endMs = end.msecsSinceStartOfDay();
|
|
|
|
// x <= bounded(x, y) < y
|
|
qint64 highestMs = endMs - requiredMs;
|
|
qint64 startTimeMs =
|
|
QRandomGenerator::global()->bounded(beginMs, highestMs + 1);
|
|
|
|
if (startTimeMs == beginMs) {
|
|
return reserveAvailableSlotLeft(begin, requiredMs);
|
|
}
|
|
if (startTimeMs == highestMs) {
|
|
return reserveAvailableSlotRight(begin, requiredMs);
|
|
}
|
|
|
|
availableSlots_.remove(begin);
|
|
|
|
QTime reservedBegin = QTime::fromMSecsSinceStartOfDay(startTimeMs);
|
|
QTime reservedEnd = QTime::fromMSecsSinceStartOfDay(startTimeMs + requiredMs);
|
|
|
|
addAvailableSlot(begin, reservedBegin);
|
|
addAvailableSlot(reservedEnd, end);
|
|
|
|
return {reservedBegin, reservedEnd};
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveAvailableSlot(QTime begin,
|
|
qint64 requiredMs) {
|
|
switch (startTimeMode_) {
|
|
case StartTimeMode::Left:
|
|
return reserveAvailableSlotLeft(begin, requiredMs);
|
|
case StartTimeMode::Random:
|
|
return reserveAvailableSlotRandom(begin, requiredMs);
|
|
case StartTimeMode::Right:
|
|
return reserveAvailableSlotRight(begin, requiredMs);
|
|
default:
|
|
qWarning() << "DayManager::reserve: unknown start time mode";
|
|
return {};
|
|
}
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveImplMaximal(qint64 requiredMs) {
|
|
auto keys = availableSlots(requiredMs);
|
|
if (keys.isEmpty()) {
|
|
return {};
|
|
}
|
|
|
|
QTime begin = *std::max_element(
|
|
keys.begin(), keys.end(), [this](const auto &largest, const auto &other) {
|
|
return duration(largest) < duration(other);
|
|
});
|
|
return reserveAvailableSlot(begin, requiredMs);
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveImplMinimal(qint64 requiredMs) {
|
|
auto keys = availableSlots(requiredMs);
|
|
if (keys.isEmpty()) {
|
|
return {};
|
|
}
|
|
|
|
QTime begin =
|
|
*std::min_element(keys.begin(), keys.end(),
|
|
[this](const QTime &smallest, const QTime &other) {
|
|
return duration(smallest) < duration(other);
|
|
});
|
|
return reserveAvailableSlot(begin, requiredMs);
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveImplQuick(qint64 requiredMs) {
|
|
for (auto const &[begin, end] : availableSlots_.asKeyValueRange()) {
|
|
if (begin.msecsTo(end) >= requiredMs) {
|
|
return reserveAvailableSlot(begin, requiredMs);
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveImplRandom(qint64 requiredMs) {
|
|
auto keys = availableSlots(requiredMs);
|
|
if (keys.isEmpty()) {
|
|
return {};
|
|
}
|
|
|
|
QRandomGenerator *g = QRandomGenerator::global();
|
|
QTime begin = keys.at(g->bounded(keys.size()));
|
|
return reserveAvailableSlot(begin, requiredMs);
|
|
}
|
|
|
|
QPair<QTime, QTime> DayManager::reserveImpl(qint64 requiredMs) {
|
|
switch (slotSelectionMode_) {
|
|
case SlotSelectionMode::Quick:
|
|
return DayManager::reserveImplQuick(requiredMs);
|
|
case SlotSelectionMode::Maximal:
|
|
return DayManager::reserveImplMaximal(requiredMs);
|
|
case SlotSelectionMode::Minimal:
|
|
return DayManager::reserveImplMinimal(requiredMs);
|
|
case SlotSelectionMode::Random:
|
|
return DayManager::reserveImplRandom(requiredMs);
|
|
default:
|
|
qWarning() << "DayManager::reserve: unknown reserve mode";
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void DayManager::setMinDuration(const QTime &duration) {
|
|
if (!duration.isValid()) {
|
|
qWarning() << "DayManager::setMinDuration: duration is invalid";
|
|
return;
|
|
}
|
|
|
|
minDurationMs_ = duration.msecsSinceStartOfDay();
|
|
}
|
|
|
|
qsizetype DayManager::calculatePossibleReservations(qint64 requiredMs) const {
|
|
auto keys = availableSlots(requiredMs);
|
|
return std::accumulate(keys.begin(), keys.end(), 0,
|
|
[this, requiredMs](qsizetype sum, const QTime &key) {
|
|
qint64 durationMs =
|
|
duration(key).msecsSinceStartOfDay();
|
|
return sum + std::floor(durationMs / requiredMs);
|
|
});
|
|
}
|
|
|
|
QTime DayManager::duration() const {
|
|
if (availableSlots_.isEmpty()) {
|
|
// qWarning() << "DayManager::duration: no available slots";
|
|
return {};
|
|
}
|
|
|
|
const auto &kvRange = availableSlots_.asKeyValueRange();
|
|
auto f = [this](qint64 sum, const auto &kv) -> qint64 {
|
|
auto const &[begin, end] = kv;
|
|
return sum + begin.msecsTo(end);
|
|
};
|
|
qint64 sum = std::accumulate(kvRange.begin(), kvRange.end(), 0, f);
|
|
|
|
return QTime::fromMSecsSinceStartOfDay(sum);
|
|
}
|
|
|
|
QTime DayManager::duration(const QTime &begin) const {
|
|
if (!begin.isValid()) {
|
|
qWarning() << "DayManager::duration: begin is invalid";
|
|
return {};
|
|
}
|
|
|
|
const QTime &end = availableSlots_.value(begin);
|
|
if (!end.isValid()) {
|
|
qWarning() << "DayManager::duration: end is invalid";
|
|
return {};
|
|
}
|
|
|
|
return end.addMSecs(-begin.msecsSinceStartOfDay());
|
|
}
|