diff --git a/libguh/loggingcategories.cpp b/libguh/loggingcategories.cpp index 781fb941..21315472 100644 --- a/libguh/loggingcategories.cpp +++ b/libguh/loggingcategories.cpp @@ -22,6 +22,7 @@ Q_LOGGING_CATEGORY(dcApplication, "Application") Q_LOGGING_CATEGORY(dcDeviceManager, "DeviceManager") +Q_LOGGING_CATEGORY(dcTimeManager, "TimeManager") Q_LOGGING_CATEGORY(dcRuleEngine, "RuleEngine") Q_LOGGING_CATEGORY(dcHardware, "Hardware") Q_LOGGING_CATEGORY(dcConnection, "Connection") diff --git a/libguh/loggingcategories.h b/libguh/loggingcategories.h index a846c180..007bd25d 100644 --- a/libguh/loggingcategories.h +++ b/libguh/loggingcategories.h @@ -29,6 +29,7 @@ // Core / libguh Q_DECLARE_LOGGING_CATEGORY(dcApplication) Q_DECLARE_LOGGING_CATEGORY(dcDeviceManager) +Q_DECLARE_LOGGING_CATEGORY(dcTimeManager) Q_DECLARE_LOGGING_CATEGORY(dcRuleEngine) Q_DECLARE_LOGGING_CATEGORY(dcHardware) Q_DECLARE_LOGGING_CATEGORY(dcConnection) diff --git a/server/guhcore.cpp b/server/guhcore.cpp index 9add12d0..505d8072 100644 --- a/server/guhcore.cpp +++ b/server/guhcore.cpp @@ -356,16 +356,18 @@ RuleEngine *GuhCore::ruleEngine() const return m_ruleEngine; } +TimeManager *GuhCore::timeManager() const +{ + return m_timeManager; +} + /*! Constructs GuhCore with the given \a parent. This is private. Use \l{GuhCore::instance()} to access the single instance.*/ GuhCore::GuhCore(QObject *parent) : QObject(parent) { - qCDebug(dcApplication) << "Creating centralized timer"; - m_guhTimer = new QTimer(this); - m_guhTimer->setInterval(1000); - m_guhTimer->setSingleShot(false); - m_currentDateTime = QDateTime::currentDateTime(); + qCDebug(dcApplication()) << "Creating Time Manager"; + m_timeManager = new TimeManager(QTimeZone::systemTimeZoneId(), this); qCDebug(dcApplication) << "Creating Log Engine"; m_logger = new LogEngine(this); @@ -394,10 +396,11 @@ GuhCore::GuhCore(QObject *parent) : connect(m_ruleEngine, &RuleEngine::ruleRemoved, this, &GuhCore::ruleRemoved); connect(m_ruleEngine, &RuleEngine::ruleConfigurationChanged, this, &GuhCore::ruleConfigurationChanged); - connect(m_guhTimer, &QTimer::timeout, this, &GuhCore::guhTimeout); + connect(m_timeManager, &TimeManager::timeChanged, this, &GuhCore::onTimeChanged); + connect(m_timeManager, &TimeManager::dateChanged, this, &GuhCore::onDateChanged); + connect(m_timeManager, &TimeManager::tick, m_deviceManager, &DeviceManager::timeTick); m_logger->logSystemEvent(true); - m_guhTimer->start(); } /*! Connected to the DeviceManager's emitEvent signal. Events received in @@ -458,21 +461,14 @@ void GuhCore::gotEvent(const Event &event) executeRuleActions(actions); } -void GuhCore::guhTimeout() +void GuhCore::onTimeChanged(const QTime ¤tTime) { - m_deviceManager->timeTick(); - - // TODO: evaluate special times - - // Minute based time -> only evaluate time based rules if the minute changed - if (m_currentDateTime.time().minute() != QDateTime::currentDateTime().time().minute()) { - qCDebug(dcApplication) << "Guh time changed" << QDateTime::currentDateTime().time().toString("hh:mm:ss"); - m_currentDateTime = QDateTime::currentDateTime(); - - // TODO: evaluate timeDescriptor based rules - - } + qCDebug(dcTimeManager) << currentTime.toString("hh:mm"); +} +void GuhCore::onDateChanged(const QDate ¤tDate) +{ + qCDebug(dcTimeManager) << currentDate.toString("dd.MM.yyyy"); } /*! Return the instance of the log engine */ diff --git a/server/guhcore.h b/server/guhcore.h index 54f03fa3..0e2484e8 100644 --- a/server/guhcore.h +++ b/server/guhcore.h @@ -32,8 +32,9 @@ #include "ruleengine.h" #include "servermanager.h" +#include "time/timemanager.h" + #include -#include class Device; @@ -68,6 +69,7 @@ public: RestServer *restServer() const; DeviceManager *deviceManager() const; RuleEngine *ruleEngine() const; + TimeManager *timeManager() const; signals: void eventTriggered(const Event &event); @@ -94,18 +96,15 @@ private: ServerManager *m_serverManager; DeviceManager *m_deviceManager; RuleEngine *m_ruleEngine; - LogEngine *m_logger; + TimeManager *m_timeManager; QHash m_pendingActions; - QTimer *m_guhTimer; - QDateTime m_currentDateTime; - - private slots: void gotEvent(const Event &event); - void guhTimeout(); + void onTimeChanged(const QTime ¤tTime); + void onDateChanged(const QDate ¤tDate); void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status); }; diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index 691fcbc1..46681c66 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -43,6 +43,7 @@ \value Bool \value Variant \value Color + \value Time \value Object */ @@ -283,7 +284,25 @@ void JsonTypes::init() s_logEntry.insert("o:eventType", loggingEventTypeRef()); s_logEntry.insert("o:errorCode", basicTypeToString(String)); + // TimeDescriptor + s_timeDescriptor.insert("o:calendarItems", QVariantList() << calendarItemRef()); + s_timeDescriptor.insert("o:timeEventItems", QVariantList() << timeEventItemRef()); + // CalendarItem + s_calendarItem.insert("o:datetime", basicTypeToString(QVariant::UInt)); + s_calendarItem.insert("o:startTime", basicTypeToString(QVariant::Time)); + s_calendarItem.insert("duration", basicTypeToString(QVariant::UInt)); + s_calendarItem.insert("o:repeating", repeatingOptionRef()); + + // TimeEventItem + s_timeEventItem.insert("o:datetime", basicTypeToString(QVariant::UInt)); + s_timeEventItem.insert("o:time", basicTypeToString(QVariant::Time)); + s_timeEventItem.insert("o:repeating", repeatingOptionRef()); + + // RepeatingOption + s_repeatingOption.insert("mode", repeatingModeRef()); + s_repeatingOption.insert("o:weekDays", QVariantList() << basicTypeToString(Int)); + s_repeatingOption.insert("o:monthDays", QVariantList() << basicTypeToString(Int)); s_initialized = true; } @@ -792,6 +811,71 @@ QVariantList JsonTypes::packCreateMethods(DeviceClass::CreateMethods createMetho return ret; } +/*! Returns a variant map of the given \a option. */ +QVariantMap JsonTypes::packRepeatingOption(const RepeatingOption &option) +{ + QVariantMap optionVariant; + optionVariant.insert("mode", s_repeatingMode.at(option.mode())); + if (!option.weekDays().isEmpty()) + optionVariant.insert("weekDays", QVariant::fromValue< QList >(option.weekDays())); + + if (!option.monthDays().isEmpty()) + optionVariant.insert("monthDays", QVariant::fromValue< QList >(option.monthDays())); + + return optionVariant; +} + +/*! Returns a variant map of the given \a calendarItem. */ +QVariantMap JsonTypes::packCalendarItem(const CalendarItem &calendarItem) +{ + QVariantMap calendarItemVariant; + calendarItemVariant.insert("duration", calendarItem.duration()); + + if (!calendarItem.startTime().isNull()) + calendarItemVariant.insert("startTime", calendarItem.startTime().toString("hh:mm")); + + if (!calendarItem.dateTime().isNull()) + calendarItemVariant.insert("datetime", calendarItem.dateTime().toTime_t()); + + if (!calendarItem.repeatingOption().isEmtpy()) + calendarItemVariant.insert("repeating", packRepeatingOption(calendarItem.repeatingOption())); + + return calendarItemVariant; +} + +QVariantMap JsonTypes::packTimeEventItem(const TimeEventItem &timeEventItem) +{ + QVariantMap timeEventItemVariant; + + if (!timeEventItem.dateTime().isNull()) + timeEventItemVariant.insert("datetime", timeEventItem.dateTime().toTime_t()); + + if (!timeEventItem.time().isNull()) + timeEventItemVariant.insert("time", timeEventItem.time().toString("hh:mm")); + + if (!timeEventItem.repatingOption().isEmtpy()) + timeEventItemVariant.insert("repeating", packRepeatingOption(timeEventItem.repatingOption())); + + return timeEventItemVariant; +} + +/*! Returns a variant map of the given \a timeDescriptor. */ +QVariantMap JsonTypes::packTimeDescriptor(const TimeDescriptor &timeDescriptor) +{ + QVariantMap timeDescriptorVariant; + + if (!timeDescriptor.calendarItems().isEmpty()) { + QVariantList calendarItems; + foreach (const CalendarItem &calendarItem, timeDescriptor.calendarItems()) { + calendarItems.append(packCalendarItem(calendarItem)); + } + } + + // TODO: TimeEventItems + + return timeDescriptorVariant; +} + /*! Returns a variant list of the supported vendors. */ QVariantList JsonTypes::packSupportedVendors() { @@ -1103,6 +1187,76 @@ LogFilter JsonTypes::unpackLogFilter(const QVariantMap &logFilterMap) return filter; } +/*! Returns a \l{RepeatingOption} created from the given \a repeatingOptionMap. */ +RepeatingOption JsonTypes::unpackRepeatingOption(const QVariantMap &repeatingOptionMap) +{ + RepeatingOption::RepeatingMode mode = (RepeatingOption::RepeatingMode)s_repeatingMode.indexOf(repeatingOptionMap.value("mode").toString()); + + QList weekDays; + if (repeatingOptionMap.contains("weekDays")) { + foreach (const QVariant weekDayVariant, repeatingOptionMap.value("weekDays").toList()) { + weekDays.append(weekDayVariant.toInt()); + } + } + + QList monthDays; + if (repeatingOptionMap.contains("monthDays")) { + foreach (const QVariant monthDayVariant, repeatingOptionMap.value("monthDays").toList()) { + monthDays.append(monthDayVariant.toInt()); + } + } + + return RepeatingOption(mode, weekDays, monthDays); +} + +/*! Returns a \l{CalendarItem} created from the given \a calendarItemMap. */ +CalendarItem JsonTypes::unpackCalendarItem(const QVariantMap &calendarItemMap) +{ + CalendarItem calendarItem; + + if (calendarItemMap.contains("datetime")) + calendarItem.setDateTime(QDateTime::fromTime_t(calendarItemMap.value("datetime").toUInt())); + + if (calendarItemMap.contains("startTime")) + calendarItem.setStartTime(calendarItemMap.value("startTime").toTime()); + + if (calendarItemMap.contains("repeating")) + calendarItem.setRepeatingOption(unpackRepeatingOption(calendarItemMap.value("repeating").toMap())); + + return calendarItem; +} + +TimeEventItem JsonTypes::unpackTimeEventItem(const QVariantMap &timeEventItemMap) +{ + TimeEventItem timeEventItem; + + if (timeEventItemMap.contains("datetime")) + timeEventItem.setDateTime(timeEventItemMap.value("datetime").toUInt()); + + if (timeEventItemMap.contains("time")) + timeEventItem.setTime(timeEventItemMap.value("time").toTime()); + + if (timeEventItemMap.contains("repeating")) + timeEventItem.setRepeatingOption(unpackRepeatingOption(timeEventItemMap.value("repeating").toMap())); + + return timeEventItem; +} + +TimeDescriptor JsonTypes::unpackTimeDescriptor(const QVariantMap &timeDescriptorMap) +{ + TimeDescriptor timeDescriptor; + + if (timeDescriptorMap.contains("calendarItems")) { + QList calendarItems; + foreach (const QVariant &calendarItemValiant, timeDescriptorMap.value("calendarItems").toList()) { + calendarItems.append(unpackCalendarItem(calendarItemValiant.toMap())); + } + timeDescriptor.setCalendarItems(calendarItems); + } + + return timeDescriptor; +} + /*! Compairs the given \a map with the given \a templateMap. Returns the error string and false if the params are not valid. */ QPair JsonTypes::validateMap(const QVariantMap &templateMap, const QVariantMap &map) diff --git a/server/jsonrpc/jsontypes.h b/server/jsonrpc/jsontypes.h index 4ae351d1..45d82724 100644 --- a/server/jsonrpc/jsontypes.h +++ b/server/jsonrpc/jsontypes.h @@ -171,12 +171,17 @@ public: static QVariantMap packDevice(Device *device); static QVariantMap packDeviceDescriptor(const DeviceDescriptor &descriptor); static QVariantMap packRule(const Rule &rule); - static QVariantList packRules(const QList rules); static QVariantMap packRuleDescription(const Rule &rule); static QVariantMap packLogEntry(const LogEntry &logEntry); - static QVariantList packCreateMethods(DeviceClass::CreateMethods createMethods); + static QVariantMap packRepeatingOption(const RepeatingOption &option); + static QVariantMap packCalendarItem(const CalendarItem &calendarItem); + static QVariantMap packTimeEventItem(const TimeEventItem &timeEventItem); + static QVariantMap packTimeDescriptor(const TimeDescriptor &timeDescriptor); + // pack resources + static QVariantList packRules(const QList rules); + static QVariantList packCreateMethods(DeviceClass::CreateMethods createMethods); static QVariantList packSupportedVendors(); static QVariantList packSupportedDevices(const VendorId &vendorId); static QVariantList packConfiguredDevices(); @@ -204,6 +209,10 @@ public: static StateEvaluator unpackStateEvaluator(const QVariantMap &stateEvaluatorMap); static StateDescriptor unpackStateDescriptor(const QVariantMap &stateDescriptorMap); static LogFilter unpackLogFilter(const QVariantMap &logFilterMap); + static RepeatingOption unpackRepeatingOption(const QVariantMap &repeatingOptionMap); + static CalendarItem unpackCalendarItem(const QVariantMap &calendarItemMap); + static TimeEventItem unpackTimeEventItem(const QVariantMap &timeEventItemMap); + static TimeDescriptor unpackTimeDescriptor(const QVariantMap &timeDescriptorMap); // validate static QPair validateMap(const QVariantMap &templateMap, const QVariantMap &map); diff --git a/server/main.cpp b/server/main.cpp index 2e8514c7..81378159 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) s_loggingFilters.insert("JsonRpc", true); s_loggingFilters.insert("Rest", true); s_loggingFilters.insert("OAuth2", false); - s_loggingFilters.insert("Coap", false); + s_loggingFilters.insert("TimeManager", false); QHash loggingFiltersPlugins; foreach (const QJsonObject &pluginMetadata, DeviceManager::pluginsMetadata()) { diff --git a/server/server.pri b/server/server.pri index 9e9230c1..adbdad5e 100644 --- a/server/server.pri +++ b/server/server.pri @@ -1,46 +1,13 @@ - +# check websocket support contains(DEFINES, WEBSOCKET){ QT += websockets - SOURCES += $$top_srcdir/server/websocketserver.cpp HEADERS += $$top_srcdir/server/websocketserver.h + SOURCES += $$top_srcdir/server/websocketserver.cpp } -RESOURCES += $$top_srcdir/icons.qrc -SOURCES += $$top_srcdir/server/guhcore.cpp \ - $$top_srcdir/server/tcpserver.cpp \ - $$top_srcdir/server/ruleengine.cpp \ - $$top_srcdir/server/rule.cpp \ - $$top_srcdir/server/jsonrpc/jsonrpcserver.cpp \ - $$top_srcdir/server/jsonrpc/jsonhandler.cpp \ - $$top_srcdir/server/jsonrpc/devicehandler.cpp \ - $$top_srcdir/server/jsonrpc/jsontypes.cpp \ - $$top_srcdir/server/jsonrpc/ruleshandler.cpp \ - $$top_srcdir/server/jsonrpc/actionhandler.cpp \ - $$top_srcdir/server/jsonrpc/eventhandler.cpp \ - $$top_srcdir/server/jsonrpc/statehandler.cpp \ - $$top_srcdir/server/jsonrpc/logginghandler.cpp \ - $$top_srcdir/server/stateevaluator.cpp \ - $$top_srcdir/server/logging/logengine.cpp \ - $$top_srcdir/server/logging/logfilter.cpp \ - $$top_srcdir/server/logging/logentry.cpp \ - $$top_srcdir/server/webserver.cpp \ - $$top_srcdir/server/transportinterface.cpp \ - $$top_srcdir/server/servermanager.cpp \ - $$top_srcdir/server/httprequest.cpp \ - $$top_srcdir/server/httpreply.cpp \ - $$top_srcdir/server/rest/restserver.cpp \ - $$top_srcdir/server/rest/restresource.cpp \ - $$top_srcdir/server/rest/devicesresource.cpp \ - $$top_srcdir/server/rest/deviceclassesresource.cpp \ - $$top_srcdir/server/rest/vendorsresource.cpp \ - $$top_srcdir/server/rest/logsresource.cpp \ - $$top_srcdir/server/rest/pluginsresource.cpp \ - $$top_srcdir/server/rest/rulesresource.cpp \ - $$top_srcdir/server/time/timedescriptor.cpp \ - $$top_srcdir/server/time/calendaritem.cpp \ - $$top_srcdir/server/time/repeatingoption.cpp \ - $$top_srcdir/server/time/timeeventitem.cpp \ +# icons for the webserver +RESOURCES += $$top_srcdir/icons.qrc HEADERS += $$top_srcdir/server/guhcore.h \ @@ -78,5 +45,41 @@ HEADERS += $$top_srcdir/server/guhcore.h \ $$top_srcdir/server/time/calendaritem.h \ $$top_srcdir/server/time/repeatingoption.h \ $$top_srcdir/server/time/timeeventitem.h \ + $$top_srcdir/server/time/timemanager.h +SOURCES += $$top_srcdir/server/guhcore.cpp \ + $$top_srcdir/server/tcpserver.cpp \ + $$top_srcdir/server/ruleengine.cpp \ + $$top_srcdir/server/rule.cpp \ + $$top_srcdir/server/jsonrpc/jsonrpcserver.cpp \ + $$top_srcdir/server/jsonrpc/jsonhandler.cpp \ + $$top_srcdir/server/jsonrpc/devicehandler.cpp \ + $$top_srcdir/server/jsonrpc/jsontypes.cpp \ + $$top_srcdir/server/jsonrpc/ruleshandler.cpp \ + $$top_srcdir/server/jsonrpc/actionhandler.cpp \ + $$top_srcdir/server/jsonrpc/eventhandler.cpp \ + $$top_srcdir/server/jsonrpc/statehandler.cpp \ + $$top_srcdir/server/jsonrpc/logginghandler.cpp \ + $$top_srcdir/server/stateevaluator.cpp \ + $$top_srcdir/server/logging/logengine.cpp \ + $$top_srcdir/server/logging/logfilter.cpp \ + $$top_srcdir/server/logging/logentry.cpp \ + $$top_srcdir/server/webserver.cpp \ + $$top_srcdir/server/transportinterface.cpp \ + $$top_srcdir/server/servermanager.cpp \ + $$top_srcdir/server/httprequest.cpp \ + $$top_srcdir/server/httpreply.cpp \ + $$top_srcdir/server/rest/restserver.cpp \ + $$top_srcdir/server/rest/restresource.cpp \ + $$top_srcdir/server/rest/devicesresource.cpp \ + $$top_srcdir/server/rest/deviceclassesresource.cpp \ + $$top_srcdir/server/rest/vendorsresource.cpp \ + $$top_srcdir/server/rest/logsresource.cpp \ + $$top_srcdir/server/rest/pluginsresource.cpp \ + $$top_srcdir/server/rest/rulesresource.cpp \ + $$top_srcdir/server/time/timedescriptor.cpp \ + $$top_srcdir/server/time/calendaritem.cpp \ + $$top_srcdir/server/time/repeatingoption.cpp \ + $$top_srcdir/server/time/timeeventitem.cpp \ + $$top_srcdir/server/time/timemanager.cpp diff --git a/server/time/calendaritem.cpp b/server/time/calendaritem.cpp index a19a3b72..b5315074 100644 --- a/server/time/calendaritem.cpp +++ b/server/time/calendaritem.cpp @@ -32,54 +32,183 @@ namespace guhserver { - -/*! Construct a invalid \l{CalendarItem}.*/ -CalendarItem::CalendarItem() +/*! Construct a invalid \l{CalendarItem}. */ +CalendarItem::CalendarItem(): + m_duration(0) { } -/*! Construct a \l{CalendarItem} with the given \a startTime, \a duration and \a repeatingOption.*/ -CalendarItem::CalendarItem(const QTime &startTime, const QTime &duration, const RepeatingOption &repeatingOption) : - m_startTime(startTime), - m_duration(duration), - m_repeatingOption(repeatingOption) +/*! Returns the date time of this \l{CalendarItem}. */ +QDateTime CalendarItem::dateTime() const { - + return m_dateTime; } -/*! Returns the start time of this \l{CalendarItem}.*/ +/*! Sets the \a dateTime of this \l{CalendarItem}. */ +void CalendarItem::setDateTime(const QDateTime &dateTime) +{ + m_dateTime = dateTime; +} + +/*! Returns the start time of this \l{CalendarItem}. */ QTime CalendarItem::startTime() const { return m_startTime; } -/*! Returns the duratiorn of this \l{CalendarItem}.*/ -QTime CalendarItem::duration() const +/*! Sets the \a startTime of this \l{CalendarItem}. */ +void CalendarItem::setStartTime(const QTime &startTime) +{ + m_startTime = startTime; +} + +/*! Returns the duratiorn of this \l{CalendarItem}. */ +uint CalendarItem::duration() const { return m_duration; } -/*! Returns the \l{RepeatingOption} of this \l{CalendarItem}.*/ +/*! Sets the \a duration of this \l{CalendarItem}. */ +void CalendarItem::setDuration(const uint &duration) +{ + m_duration = duration; +} + +/*! Returns the \l{RepeatingOption} of this \l{CalendarItem}. */ RepeatingOption CalendarItem::repeatingOption() const { return m_repeatingOption; } -/*! Returns true if this \l{CalendarItem} is valid. A \l{CalendarItem} is valid if the start time and the duration are set.*/ -bool CalendarItem::isValid() const +/*! Sets the \a repeatingOption of this \l{CalendarItem}. */ +void CalendarItem::setRepeatingOption(const RepeatingOption &repeatingOption) { - return !m_startTime.isNull() && !m_duration.isNull(); + m_repeatingOption = repeatingOption; } -/*! Returns true, if the given \a dateTime matches this \l{CalendarItem}.*/ +/*! Returns true if this \l{CalendarItem} is valid. A \l{CalendarItem} is invalid + if start time and datetime are set or if the duration is 0. +*/ +bool CalendarItem::isValid() const +{ + return (!m_startTime.isNull() != !m_dateTime.isNull()) && m_duration > 0; +} + +/*! Returns true, if the given \a dateTime matches this \l{CalendarItem}. */ bool CalendarItem::evaluate(const QDateTime &dateTime) const { - Q_UNUSED(dateTime) + if (!isValid()) + return false; - // TODO: evaluate the calendar item, return true if the current time matches the calendar item, otherwise false + if (!repeatingOption().isValid()) + return false; + + switch (repeatingOption().mode()) { + case RepeatingOption::RepeatingModeNone: + // If there is no repeating option, we assume it is meant daily. + return evaluateDaily(dateTime); + case RepeatingOption::RepeatingModeHourly: + return evaluateHourly(dateTime); + case RepeatingOption::RepeatingModeDaily: + return evaluateDaily(dateTime); + case RepeatingOption::RepeatingModeWeekly: + return evaluateWeekly(dateTime); + case RepeatingOption::RepeatingModeMonthly: + return evaluateMonthly(dateTime); + default: + return false; + } + +} + +bool CalendarItem::evaluateHourly(const QDateTime &dateTime) const +{ + // check if + if (startTime().isNull()) { + + } + QDateTime startDateTime = QDateTime(dateTime.date(), QTime(dateTime.time().hour(), startTime().minute())); + QDateTime endDateTime = startDateTime.addSecs(duration() * 60); + + bool timeValid = dateTime >= startDateTime && dateTime < endDateTime; + bool weekdayValid = repeatingOption().evaluateWeekDay(dateTime); + bool monthdayValid = repeatingOption().evaluateMonthDay(dateTime); + + return timeValid && weekdayValid && monthdayValid; +} + +bool CalendarItem::evaluateDaily(const QDateTime &dateTime) const +{ + // If the duration is longer than a day, this calendar item is always true + // 1 day has 1440 minutes + if (duration() >= 1440) + return true; + + QDateTime startDateTime = QDateTime(dateTime.date(), startTime()); + QDateTime endDateTime = startDateTime.addSecs(duration() * 60); + + bool timeValid = false; + + if (startDateTime.date() == endDateTime.date()) { + timeValid = dateTime >= startDateTime && dateTime < endDateTime; + } else { + // If the time duration changes the date, + // check only if the time is smaler than the overlapping time + timeValid = dateTime.time() < endDateTime.time(); + } + + return timeValid; +} + +bool CalendarItem::evaluateWeekly(const QDateTime &dateTime) const +{ + // If the duration is longer than a week, this calendar item is always true + // 1 week has 10080 minutes + if (duration() >= 10080) + return true; + + // get the first day of this week with the correct start time + QDateTime weekStartDateTime = dateTime.addDays(-dateTime.date().dayOfWeek()); + weekStartDateTime.setTime(m_startTime); + + // Check each week day in the list + foreach (const int &weekDay, repeatingOption().weekDays()) { + QDateTime startDateTime = weekStartDateTime.addDays(weekDay -1); + QDateTime endDateTime = startDateTime.addSecs(duration() * 60); + + bool overlapping = false; + + // Check if this calendar item overlaps a week + if (startDateTime.date().weekNumber() != endDateTime.date().weekNumber()) + overlapping = true; + + if (overlapping) { + // Jump one week into the past + QDateTime startPreviouseDateTime = startDateTime.addDays(-7); + QDateTime endPreviouseDateTime = startPreviouseDateTime.addSecs(duration() * 60); + + if (dateTime >= startPreviouseDateTime && dateTime < endPreviouseDateTime) + // return true if the current time is between start + // and end of this calendar item from the previouse week + return true; + + } else if (dateTime >= startDateTime && dateTime < endDateTime) { + // return true if the current time is between start + // and end of this calendar item + return true; + } + } + + return false; +} + +bool CalendarItem::evaluateMonthly(const QDateTime &dateTime) const +{ + Q_UNUSED(dateTime) return false; } } + diff --git a/server/time/calendaritem.h b/server/time/calendaritem.h index aaed216a..925281b9 100644 --- a/server/time/calendaritem.h +++ b/server/time/calendaritem.h @@ -31,22 +31,34 @@ class CalendarItem { public: CalendarItem(); - CalendarItem(const QTime &startTime, const QTime &duration, const RepeatingOption &repeatingOption); + + QDateTime dateTime() const; + void setDateTime(const QDateTime &dateTime); QTime startTime() const; - QTime duration() const; + void setStartTime(const QTime &startTime); + + uint duration() const; + void setDuration(const uint &duration); RepeatingOption repeatingOption() const; + void setRepeatingOption(const RepeatingOption &repeatingOption); bool isValid() const; - bool evaluate(const QDateTime &dateTime) const; private: + QDateTime m_dateTime; QTime m_startTime; - QTime m_duration; + QTime m_endTime; + uint m_duration; RepeatingOption m_repeatingOption; + + bool evaluateHourly(const QDateTime &dateTime) const; + bool evaluateDaily(const QDateTime &dateTime) const; + bool evaluateWeekly(const QDateTime &dateTime) const; + bool evaluateMonthly(const QDateTime &dateTime) const; }; } diff --git a/server/time/repeatingoption.cpp b/server/time/repeatingoption.cpp index 45517d0c..f4e6e526 100644 --- a/server/time/repeatingoption.cpp +++ b/server/time/repeatingoption.cpp @@ -20,25 +20,76 @@ /*! \class guhserver::RepeatingOption - \brief Describes a clendar item for a time based \l{guhserver::Rule}{Rule}. + \brief Describes the repeating option of a time item. \ingroup rules \inmodule core + The list of \l{weekDays()} can contain following values: + \table + \header + \li Weekday + \li int + \row + \li Monday + \li 1 + \row + \li Tuesday + \li 2 + \row + \li Wednesday + \li 3 + \row + \li Thursday + \li 4 + \row + \li Friday + \li 5 + \row + \li Saturday + \li 6 + \row + \li Sunday + \li 7 + \endtable + + \sa Rule, TimeDescriptor */ +/*! \enum guhserver::RepeatingOption::RepeatingMode + + This enum type specifies the mode of a \l{RepeatingOption}. + + \value RepeatingModeNone + There is no special repeating mode. The \l{RuleEngine} will assume a daily repeating. + \value RepeatingModeHourly + The time item should be repeated hourly. + \value RepeatingModeDaily + The time item should be repeated daily. + \value RepeatingModeWeekly + The time item should be repeated weekly. A week starts at Monday. This mode needs a list of \l{weekDays()}. + The \l{monthDays()} list will be ignored. + \value RepeatingModeMonthly + The time item should be repeated monthly. This mode needs a list of \l{monthDays()}. + The \l{monthDays()} list will be ignored. + +*/ #include "repeatingoption.h" +#include + namespace guhserver { +/*! Constructs an empty \l{RepeatingOption}. */ RepeatingOption::RepeatingOption() : m_mode(RepeatingModeNone) { } +/*! Constructs a \l{RepeatingOption} with the given \a mode, \a weekDays list and \a monthDays list. */ RepeatingOption::RepeatingOption(const RepeatingMode &mode, const QList &weekDays, const QList &monthDays) : m_mode(mode), m_weekDays(weekDays), @@ -47,24 +98,83 @@ RepeatingOption::RepeatingOption(const RepeatingMode &mode, const QList &we } +/*! Returns the mode of this \l{RepeatingOption}. */ RepeatingOption::RepeatingMode RepeatingOption::mode() const { return m_mode; } +/*! Returns the list of week days on which this \l{RepeatingOption} should be valid. */ QList RepeatingOption::weekDays() const { return m_weekDays; } +/*! Returns the list of month days on which this \l{RepeatingOption} should be valid. */ QList RepeatingOption::monthDays() const { return m_monthDays; } +/*! Clears this RepeatingOption */ +void RepeatingOption::clear() +{ + m_mode = RepeatingModeNone; + m_weekDays.clear(); + m_monthDays.clear(); +} + +/*! Returns true if this \l{RepeatingOption} is empty. */ bool RepeatingOption::isEmtpy() const { return m_mode == RepeatingModeNone && m_weekDays.isEmpty() && m_monthDays.isEmpty(); } +/*! Returns true if this \l{RepeatingOption} is valid. */ +bool RepeatingOption::isValid() const +{ + switch (m_mode) { + case RepeatingModeNone: + return m_weekDays.isEmpty() && m_monthDays.isEmpty(); + case RepeatingModeHourly: + return m_weekDays.isEmpty() && m_monthDays.isEmpty(); + case RepeatingModeDaily: + return m_weekDays.isEmpty() && m_monthDays.isEmpty(); + case RepeatingModeWeekly: + return !m_weekDays.isEmpty() && m_monthDays.isEmpty(); + case RepeatingModeMonthly: + return m_weekDays.isEmpty() && !m_monthDays.isEmpty(); + default: + return false; + } +} + +/*! Returns true if the week day of the given \a dateTime matches this \l{RepeatingOption}. */ +bool RepeatingOption::evaluateWeekDay(const QDateTime &dateTime) const +{ + // If there is no weekday specified it's always true + if (m_weekDays.isEmpty()) + return true; + + // Check if dateTime week day matches one of the specified week days + if (m_weekDays.contains(dateTime.date().dayOfWeek())) + return true; + + return false; +} + +/*! Returns true if the month day of the given \a dateTime matches this \l{RepeatingOption}. */ +bool RepeatingOption::evaluateMonthDay(const QDateTime &dateTime) const +{ + // If there is no month days specified it's always true + if (m_monthDays.isEmpty()) + return true; + + // Check if dateTime month day matches one of the specified month days + if (m_monthDays.contains(dateTime.date().day())) + return true; + + return false; +} + } diff --git a/server/time/repeatingoption.h b/server/time/repeatingoption.h index 573cdd4d..df4a3cc2 100644 --- a/server/time/repeatingoption.h +++ b/server/time/repeatingoption.h @@ -24,6 +24,8 @@ #include #include +class QDateTime; + namespace guhserver { class RepeatingOption @@ -48,7 +50,12 @@ public: QList weekDays() const; QList monthDays() const; + void clear(); bool isEmtpy() const; + bool isValid() const; + + bool evaluateWeekDay(const QDateTime &dateTime) const; + bool evaluateMonthDay(const QDateTime &dateTime) const; private: RepeatingMode m_mode; diff --git a/server/time/timedescriptor.cpp b/server/time/timedescriptor.cpp index 36ca0daa..e9a76c3b 100644 --- a/server/time/timedescriptor.cpp +++ b/server/time/timedescriptor.cpp @@ -70,7 +70,24 @@ void TimeDescriptor::setCalendarItems(const QList &calendarItems) /*! Returns true if either the calendarItems list is not empty or the timeEventItems list.*/ bool TimeDescriptor::isValid() const { - return !m_timeEventItems.isEmpty() || !m_calendarItems.isEmpty(); + return !m_timeEventItems.isEmpty() && !m_calendarItems.isEmpty(); +} + +/*! Returns true if the calendarItems list and the timeEventItems list is empty.*/ +bool TimeDescriptor::isEmpty() const +{ + return m_calendarItems.isEmpty() && m_timeEventItems.isEmpty(); +} + +/*! Returns true if this \l{TimeDescriptor} is valid for the given \a dateTime. A \l{TimeDescriptor} is + valid if the \l{TimeEventItem}{TimeEventItems} or \l{CalendarItem}{CalendarItems} match + the given \a dateTime. +*/ +bool TimeDescriptor::evaluate(const QDateTime &dateTime) const +{ + Q_UNUSED(dateTime) + + return false; } } diff --git a/server/time/timedescriptor.h b/server/time/timedescriptor.h index b2cfd142..5e703995 100644 --- a/server/time/timedescriptor.h +++ b/server/time/timedescriptor.h @@ -38,10 +38,14 @@ public: void setCalendarItems(const QList &calendarItems); bool isValid() const; + bool isEmpty() const; + + bool evaluate(const QDateTime &dateTime) const; private: QList m_timeEventItems; QList m_calendarItems; + }; } diff --git a/server/time/timeeventitem.cpp b/server/time/timeeventitem.cpp index 8883adb1..a9d55963 100644 --- a/server/time/timeeventitem.cpp +++ b/server/time/timeeventitem.cpp @@ -32,7 +32,7 @@ QDateTime TimeEventItem::dateTime() const return m_dateTimer; } -void TimeEventItem::setDateTimer(const int &timeStamp) +void TimeEventItem::setDateTime(const int &timeStamp) { m_dateTimer = QDateTime::fromTime_t(timeStamp); } @@ -42,7 +42,7 @@ QTime TimeEventItem::time() const return m_time; } -void TimeEventItem::setTimer(const QTime &time) +void TimeEventItem::setTime(const QTime &time) { m_time = time; } @@ -62,4 +62,13 @@ bool TimeEventItem::isValid() const return !m_dateTimer.isNull() || !m_time.isNull(); } +bool TimeEventItem::evaluate(const QDateTime &dateTime) const +{ + Q_UNUSED(dateTime) + + // TODO: evaluate the calendar item, return true if the current time matches the calendar item, otherwise false + + return false; +} + } diff --git a/server/time/timeeventitem.h b/server/time/timeeventitem.h index 74e36b47..ed6a3d88 100644 --- a/server/time/timeeventitem.h +++ b/server/time/timeeventitem.h @@ -33,10 +33,10 @@ public: TimeEventItem(); QDateTime dateTime() const; - void setDateTimer(const int &timeStamp); + void setDateTime(const int &timeStamp); QTime time() const; - void setTimer(const QTime &time); + void setTime(const QTime &time); RepeatingOption repatingOption() const; void setRepeatingOption(const RepeatingOption &repeatingOption); @@ -45,6 +45,8 @@ public: bool isValid() const; + bool evaluate(const QDateTime &dateTime) const; + private: QDateTime m_dateTimer; QTime m_time; diff --git a/server/time/timemanager.cpp b/server/time/timemanager.cpp new file mode 100644 index 00000000..f043c5ad --- /dev/null +++ b/server/time/timemanager.cpp @@ -0,0 +1,97 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stuerz * + * * + * This file is part of guh. * + * * + * Guh is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * Guh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "timemanager.h" +#include "loggingcategories.h" + +namespace guhserver { + +TimeManager::TimeManager(const QByteArray &timeZone, QObject *parent) : + QObject(parent) +{ + m_dateTime = QDateTime::currentDateTimeUtc(); + m_dateTime.setTimeSpec(Qt::UTC); + qCDebug(dcTimeManager()) << "UTC" << m_dateTime.toString("dd.MM.yyyy hh:mm:ss"); + + setTimeZone(timeZone); + qCDebug(dcTimeManager) << m_dateTime.toTimeZone(m_timeZone).toString("dd.MM.yyyy hh:mm:ss"); + + m_guhTimer = new QTimer(this); + m_guhTimer->setInterval(1000); + m_guhTimer->setSingleShot(false); + + connect(m_guhTimer, &QTimer::timeout, this, &TimeManager::guhTimeout); + + m_guhTimer->start(); +} + +QByteArray TimeManager::timeZone() const +{ + return m_timeZone.id(); +} + +void TimeManager::setTimeZone(const QByteArray &timeZone) +{ + if (!QTimeZone(timeZone).isValid()) { + qCWarning(dcTimeManager()) << "Invalid time zone" << timeZone; + qCWarning(dcTimeManager()) << "Using default system timezone" << QTimeZone::systemTimeZoneId(); + m_timeZone = QTimeZone(QTimeZone::systemTimeZoneId()); + } else { + qCDebug(dcTimeManager()) << "Set time zone" << timeZone; + m_timeZone = QTimeZone(timeZone); + } +} + +QDateTime TimeManager::currentDateTime() const +{ + return QDateTime::currentDateTimeUtc().toTimeZone(m_timeZone); +} + +QTime TimeManager::currentTime() const +{ + return QDateTime::currentDateTimeUtc().toTimeZone(m_timeZone).time(); +} + +QDate TimeManager::currentDate() const +{ + return QDateTime::currentDateTimeUtc().toTimeZone(m_timeZone).date(); +} + +void TimeManager::guhTimeout() +{ + // tick for deviceManager + emit tick(); + + QDateTime currentDateTime = QDateTime::currentDateTimeUtc(); + //qCDebug(dcTimeManager) << "Time changed" << currentDateTime.toTimeZone(m_timeZone).time().toString("hh:mm:ss"); + + // Minute based guh time + if (m_dateTime.time().minute() != currentDateTime.toTimeZone(m_timeZone).time().minute()) { + m_dateTime = currentDateTime; + emit timeChanged(m_dateTime.toTimeZone(m_timeZone).time()); + } + + // check if day changed + if (m_dateTime.date() != currentDateTime.toTimeZone(m_timeZone).date()) { + emit dateChanged(m_dateTime.toTimeZone(m_timeZone).date()); + } +} + +} diff --git a/server/time/timemanager.h b/server/time/timemanager.h new file mode 100644 index 00000000..26322857 --- /dev/null +++ b/server/time/timemanager.h @@ -0,0 +1,62 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2016 Simon Stuerz * + * * + * This file is part of guh. * + * * + * Guh is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * Guh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef TIMEMANAGER_H +#define TIMEMANAGER_H + +#include +#include +#include +#include + +namespace guhserver { + +class TimeManager : public QObject +{ + Q_OBJECT +public: + explicit TimeManager(const QByteArray &timeZone, QObject *parent = 0); + + QByteArray timeZone() const; + void setTimeZone(const QByteArray &timeZone = QTimeZone::systemTimeZoneId()); + + QDateTime currentDateTime() const; + QTime currentTime() const; + QDate currentDate() const; + +private: + QTimeZone m_timeZone; + QDateTime m_dateTime; + QTimer *m_guhTimer; + +signals: + void tick(); + void dateChanged(const QDate ¤tDate); + void timeChanged(const QTime ¤tTime); + + +private slots: + void guhTimeout(); + +}; + +} + +#endif // TIMEMANAGER_H