// SPDX-License-Identifier: LGPL-3.0-or-later /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2013 - 2024, nymea GmbH * Copyright (C) 2024 - 2025, chargebyte austria GmbH * * This file is part of libnymea-app. * * libnymea-app is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * libnymea-app 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with libnymea-app. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "rule.h" #include "eventdescriptor.h" #include "eventdescriptors.h" #include "stateevaluator.h" #include "stateevaluators.h" #include "statedescriptor.h" #include "ruleaction.h" #include "ruleactions.h" #include "timedescriptor.h" #include "timeeventitems.h" #include "timeeventitem.h" #include "calendaritems.h" #include "calendaritem.h" #include "ruleactionparams.h" #include "ruleactionparam.h" #include Rule::Rule(const QUuid &id, QObject *parent) : QObject(parent), m_id(id), m_eventDescriptors(new EventDescriptors(this)), // m_stateEvaluator(new StateEvaluator(this)), m_actions(new RuleActions(this)), m_exitActions(new RuleActions(this)), m_timeDescriptor(new TimeDescriptor(this)) { // qDebug() << "### Creating rule" << this; } Rule::~Rule() { } QUuid Rule::id() const { return m_id; } QString Rule::name() const { return m_name; } void Rule::setName(const QString &name) { if (m_name != name) { m_name = name; emit nameChanged(); } } bool Rule::enabled() const { return m_enabled; } void Rule::setEnabled(bool enabled) { if (m_enabled != enabled) { m_enabled = enabled; emit enabledChanged(); } } bool Rule::active() const { return m_active; } void Rule::setActive(bool active) { if (m_active != active) { m_active = active; emit activeChanged(); } } bool Rule::executable() const { return m_executable; } void Rule::setExecutable(bool executable) { if (m_executable != executable) { m_executable = executable; emit executableChanged(); } } EventDescriptors *Rule::eventDescriptors() const { return m_eventDescriptors; } StateEvaluator *Rule::stateEvaluator() const { return m_stateEvaluator; } RuleActions *Rule::actions() const { return m_actions; } RuleActions *Rule::exitActions() const { return m_exitActions; } TimeDescriptor *Rule::timeDescriptor() const { return m_timeDescriptor; } void Rule::setStateEvaluator(StateEvaluator *stateEvaluator) { if (m_stateEvaluator) { m_stateEvaluator->deleteLater(); } m_stateEvaluator = stateEvaluator; if (m_stateEvaluator) { // Might be a nullptr now if cleared m_stateEvaluator->setParent(this); } emit stateEvaluatorChanged(); } StateEvaluator* Rule::createStateEvaluator() const { return new StateEvaluator(); } Rule *Rule::clone() const { Rule *ret = new Rule(this->id()); ret->setName(this->name()); ret->setEnabled(this->enabled()); ret->setExecutable(this->executable()); for (int i = 0; i < this->eventDescriptors()->rowCount(); i++) { ret->eventDescriptors()->addEventDescriptor(this->eventDescriptors()->get(i)->clone()); } for (int i = 0; i < this->timeDescriptor()->timeEventItems()->rowCount(); i++) { ret->timeDescriptor()->timeEventItems()->addTimeEventItem(this->timeDescriptor()->timeEventItems()->get(i)->clone()); } for (int i = 0; i < this->timeDescriptor()->calendarItems()->rowCount(); i++) { ret->timeDescriptor()->calendarItems()->addCalendarItem(this->timeDescriptor()->calendarItems()->get(i)->clone()); } if (this->stateEvaluator()) { ret->setStateEvaluator(this->stateEvaluator()->clone()); } for (int i = 0; i < this->actions()->rowCount(); i++) { ret->actions()->addRuleAction(this->actions()->get(i)->clone()); } for (int i = 0; i < this->exitActions()->rowCount(); i++) { ret->exitActions()->addRuleAction(this->exitActions()->get(i)->clone()); } return ret; } bool Rule::compare(Rule *other) const { qDebug() << "comparing rule" << this << "to" << other; return this->operator==(other); } #define COMPARE(a, b) if (a != b) { qDebug() << a << "!=" << b; return false; } #define COMPARE_PTR(a, b) if (!a && !b) return true; if (!a || !b) return false; if (!a->operator==(b)) { qDebug() << a << "!=" << b; return false; } bool Rule::operator==(Rule *other) const { COMPARE(m_id, other->id()); COMPARE(m_name, other->name()); COMPARE(m_enabled, other->enabled()); COMPARE(m_executable, other->executable()); COMPARE_PTR(m_eventDescriptors, other->eventDescriptors()); COMPARE_PTR(m_stateEvaluator, other->stateEvaluator()); COMPARE_PTR(m_actions, other->actions()); COMPARE_PTR(m_exitActions, other->exitActions()); COMPARE_PTR(m_timeDescriptor, other->timeDescriptor()); return true; } QDebug operator <<(QDebug &dbg, Rule *rule) { dbg << rule->name() << " (Enabled:" << rule->enabled() << "Active:" << rule->active() << ")" << Qt::endl; if (rule->eventDescriptors()->rowCount() > 0) { dbg << "Event descriptors:" << Qt::endl; } for (int i = 0; i < rule->eventDescriptors()->rowCount(); i++) { EventDescriptor *ed = rule->eventDescriptors()->get(i); dbg << " " << i << ":"; if (!ed->thingId().isNull() && !ed->eventTypeId().isNull()) { dbg << "Thing ID:" << ed->thingId() << "Event Type ID:" << ed->eventTypeId() << Qt::endl; } else { dbg << "Interface Name:" << ed->interfaceName() << "Event Name:" << ed->interfaceEvent() << Qt::endl; } for (int j = 0; j < ed->paramDescriptors()->rowCount(); j++) { ParamDescriptor *epd = ed->paramDescriptors()->get(j); QString operatorString; switch (epd->operatorType()) { case ParamDescriptor::ValueOperatorLess: operatorString = "<"; break; case ParamDescriptor::ValueOperatorEquals: operatorString = "="; break; case ParamDescriptor::ValueOperatorGreater: operatorString = ">"; break; case ParamDescriptor::ValueOperatorNotEquals: operatorString = "!="; break; case ParamDescriptor::ValueOperatorLessOrEqual: operatorString = "<="; break; case ParamDescriptor::ValueOperatorGreaterOrEqual: operatorString = ">="; break; } dbg << " Param" << j << ": ID:" << epd->paramTypeId() << operatorString << " Value:" << epd->value() << Qt::endl; } } if (rule->stateEvaluator()) { dbg << "State Evaluator:" << Qt::endl; printStateEvaluator(dbg, rule->stateEvaluator()); } if (rule->actions()->rowCount() > 0) { dbg << "Actions:" << Qt::endl; } for (int i = 0; i < rule->actions()->rowCount(); i++) { RuleAction *ra = rule->actions()->get(i); dbg << " " << i << ":"; if (!ra->thingId().isNull() && !ra->actionTypeId().isNull()) { dbg << "Thing ID:" << ra->thingId() << "Action Type ID:" << ra->actionTypeId() << Qt::endl; } else { dbg << "Interface Name:" << ra->interfaceName() << "Action Name:" << ra->interfaceAction() << Qt::endl; } for (int j = 0; j < ra->ruleActionParams()->rowCount(); j++) { RuleActionParam *rap = ra->ruleActionParams()->get(j); if (rap->eventTypeId().isNull()) { dbg << " Param" << j << ": ID:" << rap->paramTypeId() << " Value:" << rap->value() << Qt::endl; } else { dbg << " Param" << j << ": ID:" << rap->paramTypeId() << " Source Event Type ID:" << rap->eventTypeId() << "Source Event Param ID:" << rap->eventParamTypeId() << Qt::endl; } } } if (rule->exitActions()->rowCount() > 0) { dbg << "Exit Actions:" << Qt::endl; } for (int i = 0; i < rule->exitActions()->rowCount(); i++) { RuleAction *ra = rule->exitActions()->get(i); dbg << " " << i << ":"; if (!ra->thingId().isNull() && !ra->actionTypeId().isNull()) { dbg << "Thing ID:" << ra->thingId() << "Action Type ID:" << ra->actionTypeId() << Qt::endl;; } else { dbg << "Interface Name:" << ra->interfaceName() << "Action Name:" << ra->interfaceAction() << Qt::endl;; } for (int j = 0; j < ra->ruleActionParams()->rowCount(); j++) { RuleActionParam *rap = ra->ruleActionParams()->get(j); if (rap->eventTypeId().isNull()) { dbg << " Param" << j << ": ID:" << rap->paramTypeId() << " Value:" << rap->value() << Qt::endl; } else { dbg << " Param" << j << ": ID:" << rap->paramTypeId() << " Source Event Type ID:" << rap->eventTypeId() << "Source Event Param ID:" << rap->eventParamTypeId() << Qt::endl; } } } return dbg; } QDebug printStateEvaluator(QDebug &dbg, StateEvaluator *stateEvaluator, int indentLevel) { if (stateEvaluator->stateDescriptor()) { for (int i = 0; i < indentLevel; i++) { dbg << " "; } dbg << "State Descriptor:"; if (!stateEvaluator->stateDescriptor()->thingId().isNull() && !stateEvaluator->stateDescriptor()->stateTypeId().isNull()) { dbg << "Thing ID:" << stateEvaluator->stateDescriptor()->thingId().toString() << "State Type ID:" << stateEvaluator->stateDescriptor()->stateTypeId().toString(); } else { dbg << "Interface name:" << stateEvaluator->stateDescriptor()->interfaceName() << "State Name:" << stateEvaluator->stateDescriptor()->interfaceState(); } switch (stateEvaluator->stateDescriptor()->valueOperator()) { case StateDescriptor::ValueOperatorLess: dbg << "<"; break; case StateDescriptor::ValueOperatorEquals: dbg << "="; break; case StateDescriptor::ValueOperatorGreater: dbg << ">"; break; case StateDescriptor::ValueOperatorNotEquals: dbg << "!="; break; case StateDescriptor::ValueOperatorLessOrEqual: dbg << "<="; break; case StateDescriptor::ValueOperatorGreaterOrEqual: dbg << ">="; break; } dbg << stateEvaluator->stateDescriptor()->value() << '/' << stateEvaluator->stateDescriptor()->valueThingId() << stateEvaluator->stateDescriptor()->valueStateTypeId() << Qt::endl; } if (stateEvaluator->childEvaluators()->rowCount() > 0) { for (int i = 0; i < indentLevel; i++) { dbg << " "; } dbg << (stateEvaluator->stateOperator() == StateEvaluator::StateOperatorAnd ? "AND" : "OR") << Qt::endl; } for (int i = 0; i < stateEvaluator->childEvaluators()->rowCount(); i++) { printStateEvaluator(dbg, stateEvaluator->childEvaluators()->get(i), indentLevel+1); } return dbg; }