Merge PR #148: Add support for state based rule action parameters

This commit is contained in:
Jenkins 2019-04-12 13:42:30 +02:00
commit d510aa13bb
15 changed files with 574 additions and 267 deletions

View File

@ -184,6 +184,8 @@ void JsonTypes::init()
s_ruleActionParam.insert("o:value", basicTypeRef());
s_ruleActionParam.insert("o:eventTypeId", basicTypeToString(Uuid));
s_ruleActionParam.insert("o:eventParamTypeId", basicTypeToString(Uuid));
s_ruleActionParam.insert("o:stateDeviceId", basicTypeToString(Uuid));
s_ruleActionParam.insert("o:stateTypeId", basicTypeToString(Uuid));
// ParamDescriptor
s_paramDescriptor.insert("o:paramTypeId", basicTypeToString(Uuid));
@ -592,10 +594,13 @@ QVariantMap JsonTypes::packRuleActionParam(const RuleActionParam &ruleActionPara
} else {
variantMap.insert("paramName", ruleActionParam.paramName());
}
// if this ruleaction param has a valid EventTypeId, there is no value
if (ruleActionParam.eventTypeId() != EventTypeId()) {
if (ruleActionParam.isEventBased()) {
variantMap.insert("eventTypeId", ruleActionParam.eventTypeId().toString());
variantMap.insert("eventParamTypeId", ruleActionParam.eventParamTypeId().toString());
} else if (ruleActionParam.isStateBased()) {
variantMap.insert("stateDeviceId", ruleActionParam.stateDeviceId().toString());
variantMap.insert("stateTypeId", ruleActionParam.stateTypeId().toString());
} else {
variantMap.insert("value", ruleActionParam.value());
}
@ -1388,13 +1393,19 @@ RuleActionParam JsonTypes::unpackRuleActionParam(const QVariantMap &ruleActionPa
ParamTypeId paramTypeId = ParamTypeId(ruleActionParamMap.value("paramTypeId").toString());
QString paramName = ruleActionParamMap.value("paramName").toString();
QVariant value = ruleActionParamMap.value("value");
EventTypeId eventTypeId = EventTypeId(ruleActionParamMap.value("eventTypeId").toString());
ParamTypeId eventParamTypeId = ParamTypeId(ruleActionParamMap.value("eventParamTypeId").toString());
RuleActionParam param;
if (paramTypeId.isNull()) {
return RuleActionParam(paramName, value, eventTypeId, eventParamTypeId);
param = RuleActionParam(paramName);
} else {
param = RuleActionParam(paramTypeId);
}
return RuleActionParam(paramTypeId, value, eventTypeId, eventParamTypeId);
param.setValue(ruleActionParamMap.value("value"));
param.setEventTypeId(EventTypeId(ruleActionParamMap.value("eventTypeId").toString()));
param.setEventParamTypeId(ParamTypeId(ruleActionParamMap.value("eventParamTypeId").toString()));
param.setStateDeviceId(DeviceId(ruleActionParamMap.value("stateDeviceId").toString()));
param.setStateTypeId(StateTypeId(ruleActionParamMap.value("stateTypeId").toString()));
return param;
}
/*! Returns a \l{RuleActionParamList} created from the given \a ruleActionParamList. */
@ -1414,7 +1425,7 @@ ParamDescriptor JsonTypes::unpackParamDescriptor(const QVariantMap &paramMap)
QMetaObject metaObject = Types::staticMetaObject;
int enumIndex = metaObject.indexOfEnumerator("ValueOperator");
QMetaEnum metaEnum = metaObject.enumerator(enumIndex);
Types::ValueOperator valueOperator = (Types::ValueOperator)metaEnum.keyToValue(operatorString.toLatin1().data());
Types::ValueOperator valueOperator = static_cast<Types::ValueOperator>(metaEnum.keyToValue(operatorString.toLatin1().data()));
if (paramMap.contains("paramTypeId")) {
ParamDescriptor param = ParamDescriptor(ParamTypeId(paramMap.value("paramTypeId").toString()), paramMap.value("value"));
@ -1455,7 +1466,7 @@ StateEvaluator JsonTypes::unpackStateEvaluator(const QVariantMap &stateEvaluator
{
StateEvaluator ret(unpackStateDescriptor(stateEvaluatorMap.value("stateDescriptor").toMap()));
if (stateEvaluatorMap.contains("operator")) {
ret.setOperatorType((Types::StateOperator)s_stateOperator.indexOf(stateEvaluatorMap.value("operator").toString()));
ret.setOperatorType(static_cast<Types::StateOperator>(s_stateOperator.indexOf(stateEvaluatorMap.value("operator").toString())));
} else {
ret.setOperatorType(Types::StateOperatorAnd);
}
@ -1476,7 +1487,7 @@ StateDescriptor JsonTypes::unpackStateDescriptor(const QVariantMap &stateDescrip
QString interface(stateDescriptorMap.value("interface").toString());
QString interfaceState(stateDescriptorMap.value("interfaceState").toString());
QVariant value = stateDescriptorMap.value("value");
Types::ValueOperator operatorType = (Types::ValueOperator)s_valueOperator.indexOf(stateDescriptorMap.value("operator").toString());
Types::ValueOperator operatorType = static_cast<Types::ValueOperator>(s_valueOperator.indexOf(stateDescriptorMap.value("operator").toString()));
if (!deviceId.isNull() && !stateTypeId.isNull()) {
StateDescriptor stateDescriptor(stateTypeId, deviceId, value, operatorType);
return stateDescriptor;
@ -1495,10 +1506,10 @@ LogFilter JsonTypes::unpackLogFilter(const QVariantMap &logFilterMap)
QVariantMap timeFilterMap = timeFilter.toMap();
QDateTime startDate; QDateTime endDate;
if (timeFilterMap.contains("startDate"))
startDate = QDateTime::fromTime_t(timeFilterMap.value("startDate").toInt());
startDate = QDateTime::fromTime_t(timeFilterMap.value("startDate").toUInt());
if (timeFilterMap.contains("endDate"))
endDate = QDateTime::fromTime_t(timeFilterMap.value("endDate").toInt());
endDate = QDateTime::fromTime_t(timeFilterMap.value("endDate").toUInt());
filter.addTimeFilter(startDate, endDate);
}
@ -1507,19 +1518,19 @@ LogFilter JsonTypes::unpackLogFilter(const QVariantMap &logFilterMap)
if (logFilterMap.contains("loggingSources")) {
QVariantList loggingSources = logFilterMap.value("loggingSources").toList();
foreach (const QVariant &source, loggingSources) {
filter.addLoggingSource((Logging::LoggingSource)s_loggingSource.indexOf(source.toString()));
filter.addLoggingSource(static_cast<Logging::LoggingSource>(s_loggingSource.indexOf(source.toString())));
}
}
if (logFilterMap.contains("loggingLevels")) {
QVariantList loggingLevels = logFilterMap.value("loggingLevels").toList();
foreach (const QVariant &level, loggingLevels) {
filter.addLoggingLevel((Logging::LoggingLevel)s_loggingLevel.indexOf(level.toString()));
filter.addLoggingLevel(static_cast<Logging::LoggingLevel>(s_loggingLevel.indexOf(level.toString())));
}
}
if (logFilterMap.contains("eventTypes")) {
QVariantList eventTypes = logFilterMap.value("eventTypes").toList();
foreach (const QVariant &eventType, eventTypes) {
filter.addLoggingEventType((Logging::LoggingEventType)s_loggingEventType.indexOf(eventType.toString()));
filter.addLoggingEventType(static_cast<Logging::LoggingEventType>(s_loggingEventType.indexOf(eventType.toString())));
}
}
if (logFilterMap.contains("typeIds")) {
@ -1553,7 +1564,7 @@ LogFilter JsonTypes::unpackLogFilter(const QVariantMap &logFilterMap)
/*! 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());
RepeatingOption::RepeatingMode mode = static_cast<RepeatingOption::RepeatingMode>(s_repeatingMode.indexOf(repeatingOptionMap.value("mode").toString()));
QList<int> weekDays;
if (repeatingOptionMap.contains("weekDays")) {

View File

@ -440,31 +440,85 @@ void NymeaCore::executeRuleActions(const QList<RuleAction> ruleActions)
QList<Action> actions;
foreach (const RuleAction &ruleAction, ruleActions) {
if (ruleAction.type() == RuleAction::TypeDevice) {
actions.append(ruleAction.toAction());
Device *device = m_deviceManager->findConfiguredDevice(ruleAction.deviceId());
ActionTypeId actionTypeId = ruleAction.actionTypeId();
ParamList params;
bool ok = true;
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
if (ruleActionParam.isValueBased()) {
params.append(Param(ruleActionParam.paramTypeId(), ruleActionParam.value()));
} else if (ruleActionParam.isStateBased()) {
Device *stateDevice = m_deviceManager->findConfiguredDevice(ruleActionParam.stateDeviceId());
if (!stateDevice) {
qCWarning(dcRuleEngine()) << "Cannot find device" << ruleActionParam.stateDeviceId() << "required by rule action" << ruleAction.id();
ok = false;
break;
}
DeviceClass stateDeviceClass = m_deviceManager->findDeviceClass(stateDevice->deviceClassId());
if (!stateDeviceClass.hasStateType(ruleActionParam.stateTypeId())) {
qCWarning(dcRuleEngine()) << "Device" << device->name() << device->id() << "does not have a state type" << ruleActionParam.stateTypeId();
ok = false;
break;
}
params.append(Param(ruleActionParam.paramTypeId(), stateDevice->stateValue(ruleActionParam.stateTypeId())));
}
}
if (!ok) {
qCWarning(dcRuleEngine()) << "Not executing rule action" << ruleAction.id();
continue;
}
Action action(actionTypeId, device->id());
action.setParams(params);
actions.append(action);
} else {
QList<Device*> devices = m_deviceManager->findConfiguredDevices(ruleAction.interface());
foreach (Device* device, devices) {
DeviceClass dc = m_deviceManager->findDeviceClass(device->deviceClassId());
ActionType at = dc.actionTypes().findByName(ruleAction.interfaceAction());
if (at.id().isNull()) {
DeviceClass deviceClass = m_deviceManager->findDeviceClass(device->deviceClassId());
ActionType actionType = deviceClass.actionTypes().findByName(ruleAction.interfaceAction());
if (actionType.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. The given DeviceClass does not implement action:" << ruleAction.interfaceAction();
continue;
}
Action action = Action(at.id(), device->id());
ParamList params;
foreach (const RuleActionParam &rap, ruleAction.ruleActionParams()) {
ParamType pt = at.paramTypes().findByName(rap.paramName());
if (pt.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. Failed to match interface param type to DeviceClass paramtype.";
bool ok = true;
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
ParamType paramType = actionType.paramTypes().findByName(ruleActionParam.paramName());
if (paramType.id().isNull()) {
qCWarning(dcRuleEngine()) << "Error creating Action. The given ActionType does not have a parameter:" << ruleActionParam.paramName();
ok = false;
continue;
}
params.append(Param(pt.id(), rap.value()));
if (ruleActionParam.isValueBased()) {
params.append(Param(paramType.id(), ruleActionParam.value()));
} else if (ruleActionParam.isStateBased()) {
Device *stateDevice = m_deviceManager->findConfiguredDevice(ruleActionParam.stateDeviceId());
if (!stateDevice) {
qCWarning(dcRuleEngine()) << "Cannot find device" << ruleActionParam.stateDeviceId() << "required by rule action" << ruleAction.id();
ok = false;
break;
}
DeviceClass stateDeviceClass = m_deviceManager->findDeviceClass(stateDevice->deviceClassId());
if (!stateDeviceClass.hasStateType(ruleActionParam.stateTypeId())) {
qCWarning(dcRuleEngine()) << "Device" << device->name() << device->id() << "does not have a state type" << ruleActionParam.stateTypeId();
ok = false;
break;
}
params.append(Param(paramType.id(), stateDevice->stateValue(ruleActionParam.stateTypeId())));
}
}
if (!ok) {
qCWarning(dcRuleEngine()) << "Not executing rule action" << ruleAction.id();
continue;
}
Action action = Action(actionType.id(), device->id());
action.setParams(params);
actions.append(action);
}
}
}
foreach (const Action &action, actions) {
qCDebug(dcRuleEngine) << "Executing action" << action.actionTypeId() << action.params();
DeviceManager::DeviceError status = executeAction(action);
@ -654,9 +708,7 @@ void NymeaCore::gotEvent(const Event &event)
foreach (RuleActionParam ruleActionParam, ruleAction.ruleActionParams()) {
// if this event param should be taken over in this action
if (event.eventTypeId() == ruleActionParam.eventTypeId()) {
QVariant eventValue = event.params().first().value();
// TODO: get param names...when an event has more than one parameter
QVariant eventValue = event.params().paramValue(ruleActionParam.eventParamTypeId());
// TODO: limits / scale calculation -> actionValue = eventValue * x
// something like a EventParamDescriptor

View File

@ -383,165 +383,18 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
// Check actions
foreach (const RuleAction &action, rule.actions()) {
if (!action.isValid()) {
qWarning(dcRuleEngine()) << "Action is incomplete. It must have either actionTypeId and deviceId, or interface and interfaceAction";
return RuleErrorActionTypeNotFound;
}
if (action.type() == RuleAction::TypeDevice) {
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(action.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for action with actionTypeId" << action.actionTypeId();
return RuleErrorDeviceNotFound;
}
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
if (!deviceClass.hasActionType(action.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << action.actionTypeId();
return RuleErrorActionTypeNotFound;
}
// check possible eventTypeIds in params
if (action.isEventBased()) {
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (ruleActionParam.eventTypeId() != EventTypeId()) {
// We have an eventTypeId
if (rule.eventDescriptors().isEmpty()) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleAction" << action.actionTypeId() << "contains an eventTypeId, but there are no eventDescriptors.";
return RuleErrorInvalidRuleActionParameter;
}
// now check if this eventType is in the eventDescriptorList of this rule
if (!checkEventDescriptors(rule.eventDescriptors(), ruleActionParam.eventTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. EventTypeId from RuleAction" << action.actionTypeId() << "not in eventDescriptors.";
return RuleErrorInvalidRuleActionParameter;
}
// check if the param type of the event and the action match
QVariant::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamTypeId());
QVariant v(eventParamType);
QVariant::Type actionParamType = getActionParamType(action.actionTypeId(), ruleActionParam.paramTypeId());
if (eventParamType != actionParamType && !v.canConvert(actionParamType)) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given event param " << ruleActionParam.eventParamTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << actionParamType;
qCWarning(dcRuleEngine) << " -> eventParamType:" << eventParamType;
return RuleErrorTypesNotMatching;
}
}
}
} else {
// verify action params
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
if (actionType.id() == action.actionTypeId()) {
ParamList finalParams = action.toAction().params();
DeviceManager::DeviceError paramCheck = NymeaCore::instance()->deviceManager()->verifyParams(actionType.paramTypes(), finalParams);
if (paramCheck != DeviceManager::DeviceErrorNoError) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an invalid actionParam.";
return RuleErrorInvalidRuleActionParameter;
}
}
}
}
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (!ruleActionParam.isValid()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an actionParam with \"value\" AND \"eventTypeId\".";
return RuleEngine::RuleErrorInvalidRuleActionParameter;
}
}
} else { // Is TypeInterface
Interface iface = NymeaCore::instance()->deviceManager()->supportedInterfaces().findByName(action.interface());
if (!iface.isValid()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << action.interface();
return RuleError::RuleErrorInterfaceNotFound;
}
ActionType ifaceActionType = iface.actionTypes().findByName(action.interfaceAction());
if (ifaceActionType.name().isEmpty()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface" << iface.name() << "does not implement action" << action.interfaceAction();
return RuleError::RuleErrorActionTypeNotFound;
}
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
if (!action.ruleActionParams().hasParam(ifaceActionParamType.name())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action" << iface.name() << ":" << action.interfaceAction() << "requires a" << ifaceActionParamType.name() << "param of type" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorMissingParameter;
}
if (!action.ruleActionParam(ifaceActionParamType.name()).value().canConvert(ifaceActionParamType.type())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action parameter" << iface.name() << ":" << action.interfaceAction() << ":" << ifaceActionParamType.name() << "has wrong type. Expected" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorInvalidParameter;
}
}
// TODO: Check params
foreach (const RuleAction &ruleAction, rule.actions()) {
RuleError ruleActionError = checkRuleAction(ruleAction, rule);
if (ruleActionError != RuleErrorNoError) {
return ruleActionError;
}
}
// Check exit actions
foreach (const RuleAction &ruleAction, rule.exitActions()) {
if (!ruleAction.isValid()) {
qWarning(dcRuleEngine()) << "Exit Action is incomplete. It must have either actionTypeId and deviceId, or interface and interfaceAction";
return RuleErrorActionTypeNotFound;
}
if (ruleAction.type() == RuleAction::TypeDevice) {
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(ruleAction.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for exit action with actionTypeId" << ruleAction.actionTypeId();
return RuleErrorDeviceNotFound;
}
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
if (!deviceClass.hasActionType(ruleAction.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << ruleAction.actionTypeId();
return RuleErrorActionTypeNotFound;
}
// verify action params
foreach (const ActionType &actionType, deviceClass.actionTypes()) {
if (actionType.id() == ruleAction.actionTypeId()) {
ParamList finalParams = ruleAction.toAction().params();
DeviceManager::DeviceError paramCheck = NymeaCore::instance()->deviceManager()->verifyParams(actionType.paramTypes(), finalParams);
if (paramCheck != DeviceManager::DeviceErrorNoError) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an invalid exit actionParam.";
return RuleErrorInvalidRuleActionParameter;
}
}
}
// Exit action can never be event based.
if (ruleAction.isEventBased()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got exitAction with an actionParam containing an eventTypeId. ";
return RuleErrorInvalidRuleActionParameter;
}
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
if (!ruleActionParam.isValid()) {
qCWarning(dcRuleEngine) << "Cannot create rule. Got an actionParam with \"value\" AND \"eventTypeId\".";
return RuleEngine::RuleErrorInvalidRuleActionParameter;
}
}
} else { // Is TypeInterface
Interface iface = NymeaCore::instance()->deviceManager()->supportedInterfaces().findByName(ruleAction.interface());
if (!iface.isValid()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << ruleAction.interface();
return RuleError::RuleErrorInterfaceNotFound;
}
ActionType ifaceActionType = iface.actionTypes().findByName(ruleAction.interfaceAction());
if (ifaceActionType.name().isEmpty()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface" << iface.name() << "does not implement action" << ruleAction.interfaceAction();
return RuleError::RuleErrorActionTypeNotFound;
}
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
if (!ruleAction.ruleActionParams().hasParam(ifaceActionParamType.name())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action" << iface.name() << ":" << ruleAction.interfaceAction() << "requires a" << ifaceActionParamType.name() << "param of type" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorMissingParameter;
}
if (!ruleAction.ruleActionParam(ifaceActionParamType.name()).value().canConvert(ifaceActionParamType.type())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action parameter" << iface.name() << ":" << ruleAction.interfaceAction() << ":" << ifaceActionParamType.name() << "has wrong type. Expected" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorInvalidParameter;
}
}
// TODO: Check params
foreach (const RuleAction &ruleExitAction, rule.exitActions()) {
RuleError ruleActionError = checkRuleAction(ruleExitAction, rule);
if (ruleActionError != RuleErrorNoError) {
return ruleActionError;
}
}
@ -698,9 +551,9 @@ RuleEngine::RuleError RuleEngine::disableRule(const RuleId &ruleId)
*/
RuleEngine::RuleError RuleEngine::executeActions(const RuleId &ruleId)
{
// check if rule exits
// check if rule exists
if (!m_rules.contains(ruleId)) {
qCWarning(dcRuleEngine) << "Not executing rule actions: rule not found.";
qCWarning(dcRuleEngine) << "Not executing rule actions: Rule not found.";
return RuleErrorRuleNotFound;
}
@ -790,6 +643,15 @@ QList<RuleId> RuleEngine::findRules(const DeviceId &deviceId) const
offending = true;
break;
}
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (ruleActionParam.stateDeviceId() == deviceId) {
offending = true;
break;
}
}
if (offending) {
break;
}
}
}
@ -799,6 +661,15 @@ QList<RuleId> RuleEngine::findRules(const DeviceId &deviceId) const
offending = true;
break;
}
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (ruleActionParam.stateDeviceId() == deviceId) {
offending = true;
break;
}
}
if (offending) {
break;
}
}
}
@ -867,6 +738,13 @@ void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId
for (int i = 0; i < actions.count(); i++) {
if (actions.at(i).deviceId() == deviceId) {
removeIndexes.append(i);
continue;
}
foreach (const RuleActionParam &param, actions.at(i).ruleActionParams()) {
if (param.stateDeviceId() == deviceId) {
removeIndexes.append(i);
break;
}
}
}
while (removeIndexes.count() > 0) {
@ -878,6 +756,13 @@ void RuleEngine::removeDeviceFromRule(const RuleId &id, const DeviceId &deviceId
for (int i = 0; i < exitActions.count(); i++) {
if (exitActions.at(i).deviceId() == deviceId) {
removeIndexes.append(i);
continue;
}
foreach (const RuleActionParam &param, exitActions.at(i).ruleActionParams()) {
if (param.stateDeviceId() == deviceId) {
removeIndexes.append(i);
break;
}
}
}
while (removeIndexes.count() > 0) {
@ -1024,15 +909,133 @@ bool RuleEngine::containsState(const StateEvaluator &stateEvaluator, const Event
return false;
}
bool RuleEngine::checkEventDescriptors(const QList<EventDescriptor> eventDescriptors, const EventTypeId &eventTypeId)
RuleEngine::RuleError RuleEngine::checkRuleAction(const RuleAction &ruleAction, const Rule &rule)
{
foreach (const EventDescriptor eventDescriptor, eventDescriptors) {
if (eventDescriptor.eventTypeId() == eventTypeId) {
return true;
if (!ruleAction.isValid()) {
qWarning(dcRuleEngine()) << "Action is incomplete. It must have either actionTypeId and deviceId, or interface and interfaceAction";
return RuleErrorActionTypeNotFound;
}
ActionType actionType;
if (ruleAction.type() == RuleAction::TypeDevice) {
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(ruleAction.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for action with actionTypeId" << ruleAction.actionTypeId();
return RuleErrorDeviceNotFound;
}
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
if (!deviceClass.hasActionType(ruleAction.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << ruleAction.actionTypeId();
return RuleErrorActionTypeNotFound;
}
actionType = deviceClass.actionTypes().findById(ruleAction.actionTypeId());
} else if (ruleAction.type() == RuleAction::TypeInterface) {
Interface iface = NymeaCore::instance()->deviceManager()->supportedInterfaces().findByName(ruleAction.interface());
if (!iface.isValid()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << ruleAction.interface();
return RuleError::RuleErrorInterfaceNotFound;
}
actionType = iface.actionTypes().findByName(ruleAction.interfaceAction());
if (actionType.name().isEmpty()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface" << iface.name() << "does not implement action" << ruleAction.interfaceAction();
return RuleError::RuleErrorActionTypeNotFound;
}
} else {
return RuleErrorActionTypeNotFound;
}
// Verify given params
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
RuleError ruleActionParamError = checkRuleActionParam(ruleActionParam, actionType, rule);
if (ruleActionParamError != RuleErrorNoError) {
return ruleActionParamError;
}
}
return false;
// Verify all required params are given
foreach (const ParamType &paramType, actionType.paramTypes()) {
bool found = false;
foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) {
if (ruleActionParam.paramTypeId() == paramType.id()
|| ruleActionParam.paramName() == paramType.name()) {
found = true;
break;
}
}
if (!found) {
return RuleErrorMissingParameter;
}
}
return RuleErrorNoError;
}
RuleEngine::RuleError RuleEngine::checkRuleActionParam(const RuleActionParam &ruleActionParam, const ActionType &actionType, const Rule &rule)
{
// Check param identifier (either paramTypeId or paramName)
ParamType paramType;
if (!ruleActionParam.paramTypeId().isNull()) {
paramType = actionType.paramTypes().findById(ruleActionParam.paramTypeId());
} else if (!ruleActionParam.paramName().isEmpty()) {
paramType = actionType.paramTypes().findByName(ruleActionParam.paramName());
} else {
return RuleErrorInvalidRuleActionParameter;
}
if (ruleActionParam.isEventBased()) {
// We have an eventTypeId, see if the rule actually has such a event
bool found = false;
foreach (const EventDescriptor &ed, rule.eventDescriptors()) {
if (ed.eventTypeId() == ruleActionParam.eventTypeId()) {
found = true;
}
}
if (!found) {
qCWarning(dcRuleEngine) << "Cannot create rule. EventTypeId" << ruleActionParam.eventTypeId() << "not found in rule's eventDescriptors.";
return RuleErrorInvalidRuleActionParameter;
}
// check if the param type of the event and the action match
QVariant::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamTypeId());
QVariant v(eventParamType);
if (eventParamType != paramType.type() && !v.canConvert(static_cast<int>(paramType.type()))) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given event param " << ruleActionParam.eventParamTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << paramType.type();
qCWarning(dcRuleEngine) << " -> eventParamType:" << eventParamType;
return RuleErrorTypesNotMatching;
}
} else if (ruleActionParam.isStateBased()) {
Device *d = NymeaCore::instance()->deviceManager()->findConfiguredDevice(ruleActionParam.stateDeviceId());
if (!d) {
qCWarning(dcRuleEngine()) << "Cannot create Rule. DeviceId from RuleActionParam" << ruleActionParam.paramTypeId() << "not found in system.";
return RuleErrorDeviceNotFound;
}
DeviceClass stateDeviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(d->deviceClassId());
StateType stateType = stateDeviceClass.stateTypes().findById(ruleActionParam.stateTypeId());
QVariant::Type actionParamType = getActionParamType(actionType.id(), ruleActionParam.paramTypeId());
QVariant v(stateType.type());
if (actionParamType != stateType.type() && !v.canConvert(static_cast<int>(actionParamType))) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given state based param " << ruleActionParam.stateTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << actionParamType;
qCWarning(dcRuleEngine) << " -> stateType:" << stateType.type();
return RuleErrorTypesNotMatching;
}
} else { // Is value based
if (ruleActionParam.value().isNull()) {
qCDebug(dcRuleEngine()) << "Cannot create rule. No param value given for action:" << ruleActionParam.paramTypeId().toString();
return RuleErrorInvalidRuleActionParameter;
}
if (paramType.type() != ruleActionParam.value().type() && !ruleActionParam.value().canConvert(static_cast<int>(paramType.type()))) {
qCWarning(dcRuleEngine) << "Cannot create rule. RuleActionParam" << ruleActionParam.paramTypeId().toString() << " and given state based param " << ruleActionParam.stateTypeId().toString() << "have not the same type:";
qCWarning(dcRuleEngine) << " -> actionParamType:" << paramType.type();
qCWarning(dcRuleEngine) << " -> stateType:" << ruleActionParam.value().type();
return RuleErrorTypesNotMatching;
}
}
return RuleErrorNoError;
}
QVariant::Type RuleEngine::getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId &paramTypeId)
@ -1174,7 +1177,7 @@ void RuleEngine::saveRule(const Rule &rule)
} else {
settings.beginGroup("ParamDescriptor-" + paramDescriptor.paramName());
}
settings.setValue("valueType", (int)paramDescriptor.value().type());
settings.setValue("valueType", static_cast<int>(paramDescriptor.value().type()));
settings.setValue("value", paramDescriptor.value());
settings.setValue("operator", paramDescriptor.operatorType());
settings.endGroup();
@ -1204,11 +1207,14 @@ void RuleEngine::saveRule(const Rule &rule)
} else {
settings.beginGroup("RuleActionParam-" + param.paramName());
}
settings.setValue("valueType", (int)param.value().type());
settings.setValue("valueType", static_cast<int>(param.value().type()));
settings.setValue("value", param.value());
if (param.eventTypeId() != EventTypeId()) {
if (param.isEventBased()) {
settings.setValue("eventTypeId", param.eventTypeId().toString());
settings.setValue("eventParamTypeId", param.eventParamTypeId());
} else if (param.isStateBased()) {
settings.setValue("stateDeviceId", param.stateDeviceId().toString());
settings.setValue("stateTypeId", param.stateTypeId());
}
settings.endGroup();
}
@ -1235,7 +1241,7 @@ void RuleEngine::saveRule(const Rule &rule)
} else {
settings.beginGroup("RuleActionParam-" + param.paramName());
}
settings.setValue("valueType", (int)param.value().type());
settings.setValue("valueType", static_cast<int>(param.value().type()));
settings.setValue("value", param.value());
settings.endGroup();
}
@ -1277,7 +1283,7 @@ void RuleEngine::init()
QList<int> weekDays;
QList<int> monthDays;
RepeatingOption::RepeatingMode mode = (RepeatingOption::RepeatingMode)settings.value("mode", 0).toInt();
RepeatingOption::RepeatingMode mode = static_cast<RepeatingOption::RepeatingMode>(settings.value("mode", 0).toInt());
// Load weekDays
int weekDaysCount = settings.beginReadArray("weekDays");
@ -1314,7 +1320,7 @@ void RuleEngine::init()
QList<int> weekDays;
QList<int> monthDays;
RepeatingOption::RepeatingMode mode = (RepeatingOption::RepeatingMode)settings.value("mode", 0).toInt();
RepeatingOption::RepeatingMode mode = static_cast<RepeatingOption::RepeatingMode>(settings.value("mode", 0).toInt());
// Load weekDays
int weekDaysCount = settings.beginReadArray("weekDays");
@ -1362,24 +1368,24 @@ void RuleEngine::init()
QVariant value = settings.value("value");
if (settings.contains("valueType")) {
QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt();
QVariant::Type valueType = static_cast<QVariant::Type>(settings.value("valueType").toInt());
// Note: only warn, and continue with the QVariant guessed type
if (valueType == QVariant::Invalid) {
qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the param descriptor" << strippedGroupName << ". The value type will be guessed by QVariant.";
} else if (!value.canConvert(valueType)) {
} else if (!value.canConvert(static_cast<int>(valueType))) {
qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the param descriptor value" << value << "to the stored type" << valueType;
} else {
value.convert(valueType);
value.convert(static_cast<int>(valueType));
}
}
if (!ParamTypeId(strippedGroupName).isNull()) {
ParamDescriptor paramDescriptor(ParamTypeId(strippedGroupName), value);
paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt());
paramDescriptor.setOperatorType(static_cast<Types::ValueOperator>(settings.value("operator").toInt()));
params.append(paramDescriptor);
} else {
ParamDescriptor paramDescriptor(strippedGroupName, value);
paramDescriptor.setOperatorType((Types::ValueOperator)settings.value("operator").toInt());
paramDescriptor.setOperatorType(static_cast<Types::ValueOperator>(settings.value("operator").toInt()));
params.append(paramDescriptor);
}
settings.endGroup();
@ -1415,32 +1421,34 @@ void RuleEngine::init()
QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-"));
EventTypeId eventTypeId = EventTypeId(settings.value("eventTypeId", EventTypeId()).toString());
ParamTypeId eventParamTypeId = ParamTypeId(settings.value("eventParamTypeId", ParamTypeId()).toString());
DeviceId stateDeviceId = DeviceId(settings.value("stateDeviceId", DeviceId()).toString());
StateTypeId stateTypeId = StateTypeId(settings.value("stateTypeId", StateTypeId()).toString());
QVariant value = settings.value("value");
if (settings.contains("valueType")) {
QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt();
QVariant::Type valueType = static_cast<QVariant::Type>(settings.value("valueType").toInt());
// Note: only warn, and continue with the QVariant guessed type
if (valueType == QVariant::Invalid) {
qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant.";
} else if (!value.canConvert(valueType)) {
} else if (!value.canConvert(static_cast<int>(valueType))) {
qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the rule action param value" << value << "to the stored type" << valueType;
} else {
value.convert(valueType);
value.convert(static_cast<int>(valueType));
}
}
RuleActionParam param;
if (!ParamTypeId(strippedParamTypeIdString).isNull()) {
RuleActionParam param(ParamTypeId(strippedParamTypeIdString),
value,
eventTypeId,
eventParamTypeId);
params.append(param);
// By ParamTypeId
param = RuleActionParam(ParamTypeId(strippedParamTypeIdString), value);
} else {
RuleActionParam param(strippedParamTypeIdString,
value,
eventTypeId,
eventParamTypeId);
params.append(param);
// By param name
param = RuleActionParam(strippedParamTypeIdString, value);
}
param.setEventTypeId(eventTypeId);
param.setEventParamTypeId(eventParamTypeId);
param.setStateDeviceId(stateDeviceId);
param.setStateTypeId(stateTypeId);
params.append(param);
settings.endGroup();
}
}
@ -1472,14 +1480,14 @@ void RuleEngine::init()
QString strippedParamTypeIdString = paramTypeIdString.remove(QRegExp("^RuleActionParam-"));
QVariant value = settings.value("value");
if (settings.contains("valueType")) {
QVariant::Type valueType = (QVariant::Type)settings.value("valueType").toInt();
QVariant::Type valueType = static_cast<QVariant::Type>(settings.value("valueType").toInt());
// Note: only warn, and continue with the QVariant guessed type
if (valueType == QVariant::Invalid) {
qCWarning(dcRuleEngine()) << name << idString << "Could not load the value type of the rule action param " << strippedParamTypeIdString << ". The value type will be guessed by QVariant.";
} else if (!value.canConvert(valueType)) {
} else if (!value.canConvert(static_cast<int>(valueType))) {
qCWarning(dcRuleEngine()) << "Error loading rule" << name << idString << ". Could not convert the rule action param value" << value << "to the stored type" << valueType;
} else {
value.convert(valueType);
value.convert(static_cast<int>(valueType));
}
}

View File

@ -104,7 +104,9 @@ private:
bool containsEvent(const Rule &rule, const Event &event, const DeviceClassId &deviceClassId);
bool containsState(const StateEvaluator &stateEvaluator, const Event &stateChangeEvent);
bool checkEventDescriptors(const QList<EventDescriptor> eventDescriptors, const EventTypeId &eventTypeId);
RuleError checkRuleAction(const RuleAction &ruleAction, const Rule &rule);
RuleError checkRuleActionParam(const RuleActionParam &ruleActionParam, const ActionType &actionType, const Rule &rule);
QVariant::Type getActionParamType(const ActionTypeId &actionTypeId, const ParamTypeId &paramTypeId);
QVariant::Type getEventParamType(const EventTypeId &eventTypeId, const ParamTypeId &paramTypeId);

View File

@ -48,7 +48,7 @@ QDateTime TimeEventItem::dateTime() const
}
/*! Sets the dateTime of this \l{TimeEventItem} to the given \a timeStamp. */
void TimeEventItem::setDateTime(const int &timeStamp)
void TimeEventItem::setDateTime(const uint &timeStamp)
{
m_dateTime = QDateTime::fromTime_t(timeStamp);
}

View File

@ -33,7 +33,7 @@ public:
TimeEventItem();
QDateTime dateTime() const;
void setDateTime(const int &timeStamp);
void setDateTime(const uint &timeStamp);
QTime time() const;
void setTime(const QTime &time);

View File

@ -33,7 +33,7 @@
class LIBNYMEA_EXPORT ActionType
{
public:
ActionType(const ActionTypeId &id);
ActionType(const ActionTypeId &id = ActionTypeId());
ActionTypeId id() const;

View File

@ -49,6 +49,11 @@
#include "eventdescriptor.h"
EventDescriptor::EventDescriptor()
{
}
/*! Constructs an EventDescriptor describing an \l{Event} with the given \a eventTypeId, \a deviceId and the given \a paramDescriptors. */
EventDescriptor::EventDescriptor(const EventTypeId &eventTypeId, const DeviceId &deviceId, const QList<ParamDescriptor> &paramDescriptors):
m_eventTypeId(eventTypeId),

View File

@ -41,6 +41,7 @@ public:
TypeInterface
};
EventDescriptor();
EventDescriptor(const EventTypeId &eventTypeId, const DeviceId &deviceId, const QList<ParamDescriptor> &paramDescriptors = QList<ParamDescriptor>());
EventDescriptor(const QString &interface, const QString &interfaceEvent, const QList<ParamDescriptor> &paramDescriptors = QList<ParamDescriptor>());

View File

@ -99,7 +99,17 @@ RuleAction::Type RuleAction::type() const
bool RuleAction::isEventBased() const
{
foreach (const RuleActionParam &param, m_ruleActionParams) {
if (param.eventTypeId() != EventTypeId()) {
if (param.isEventBased()) {
return true;
}
}
return false;
}
bool RuleAction::isStateBased() const
{
foreach (const RuleActionParam &param, m_ruleActionParams) {
if (param.isStateBased()) {
return true;
}
}
@ -167,7 +177,7 @@ RuleActionParam RuleAction::ruleActionParam(const ParamTypeId &ruleActionParamTy
return ruleActionParam;
}
}
return RuleActionParam(QString());
return RuleActionParam();
}
/*! Returns the \l{RuleActionParam} of this RuleAction with the given \a ruleActionParamName.
@ -180,7 +190,7 @@ RuleActionParam RuleAction::ruleActionParam(const QString &ruleActionParamName)
return ruleActionParam;
}
}
return RuleActionParam(QString());
return RuleActionParam();
}
/*! Copy the data to a \l{RuleAction} from an \a other rule action. */

View File

@ -45,6 +45,7 @@ public:
Type type() const;
bool isEventBased() const;
bool isStateBased() const;
Action toAction() const;

View File

@ -32,6 +32,21 @@
A RuleActionParam allows rules to take over an \l{Event} parameter into a rule
\l{RuleAction}.
RuleActionParams are identified by either paramTypeId or paramName (for interface based actions).
The parameter value can either be a static \l{value}, a pair of \l{EventTypeId} and \l{ParamTypeId} or a pair of
\l{DeviceId} and \l{StateTypeId}.
When composing the actual Param for the executeAction() call the value is generated as follows:
- Static value params are filled with \l{RuleActionParam::paramTypeId()} and the \l{RuleActionParam::value()}
- Event based actions are filled with \l{RuleActionParam::paramTypeId()} and the param value of the event that triggered this rule, identified by \l{RuleActionParam::eventTypeId()} and \l{RuleActionParam::eventParamTypeId()}
- State based actions are filled with \l{RuleActionParam::paramTypeId()} and the current value of the state identified by \l{RuleActionParam::deviceId()} and \l{RuleActionParam::stateTypeId()}
If the param types are not matching, nymea will do a best effort to cast the values. E.g. a RuleActionParam for
a param of type "string" and a state of type "int" would cast the integer to a string which would always work.
However, the other way round, having a parameter requiring an "int" value, and reading the value from a state of type
"string" might work, if the string does only hold numbers but would fail.
\sa nymeaserver::Rule, RuleAction,
*/
@ -41,33 +56,68 @@
* \sa Param, */
RuleActionParam::RuleActionParam(const Param &param) :
m_paramTypeId(param.paramTypeId()),
m_value(param.value()),
m_eventTypeId(EventTypeId()),
m_eventParamTypeId(ParamTypeId())
m_value(param.value())
{
}
/*! Constructs a \l{RuleActionParam} with the given \a paramTypeId, \a value, \a eventTypeId and \a eventParamTypeId.
/*! Constructs a \l{RuleActionParam} with the given \a paramTypeId and \a value.
* \sa Param, Event, */
RuleActionParam::RuleActionParam(const ParamTypeId &paramTypeId, const QVariant &value, const EventTypeId &eventTypeId, const ParamTypeId &eventParamTypeId) :
RuleActionParam::RuleActionParam(const ParamTypeId &paramTypeId, const QVariant &value):
m_paramTypeId(paramTypeId),
m_value(value)
{
}
/*! Constructs a \l{RuleActionParam} with the given \a paramTypeId, \a eventTypeId and \a eventParamTypeId.
* \sa Param, Event, */
RuleActionParam::RuleActionParam(const ParamTypeId &paramTypeId, const EventTypeId &eventTypeId, const ParamTypeId &eventParamTypeId):
m_paramTypeId(paramTypeId),
m_value(value),
m_eventTypeId(eventTypeId),
m_eventParamTypeId(eventParamTypeId)
{
}
/*! Constructs a \l{RuleActionParam} with the given \a paramName, \a value, \a eventTypeId and \a eventParamTypeId.
/*! Constructs a \l{RuleActionParam} with the given \a paramTypeId, \a stateDeviceId and \a stateTypeId.
* \sa Param, Event, */
RuleActionParam::RuleActionParam(const QString &paramName, const QVariant &value, const EventTypeId &eventTypeId, const ParamTypeId &eventParamTypeId):
RuleActionParam::RuleActionParam(const ParamTypeId &paramTypeId, const DeviceId &stateDeviceId, const StateTypeId &stateTypeId):
m_paramTypeId(paramTypeId),
m_stateDeviceId(stateDeviceId),
m_stateTypeId(stateTypeId)
{
}
/*! Constructs a \l{RuleActionParam} with the given \a paramName and \a value.
* \sa Param, Event, */
RuleActionParam::RuleActionParam(const QString &paramName, const QVariant &value):
m_paramName(paramName),
m_value(value)
{
}
/*! Constructs a \l{RuleActionParam} with the given \a paramName, \a eventTypeId and \a eventParamTypeId.
* \sa Param, Event, */
RuleActionParam::RuleActionParam(const QString &paramName, const EventTypeId &eventTypeId, const ParamTypeId &eventParamTypeId):
m_paramName(paramName),
m_value(value),
m_eventTypeId(eventTypeId),
m_eventParamTypeId(eventParamTypeId)
{
}
/*! Constructs a \l{RuleActionParam} with the given \a paramName, \a stateDeviceId and \a stateTypeId.
* \sa Param, Event, */
RuleActionParam::RuleActionParam(const QString &paramName, const DeviceId &stateDeviceId, const StateTypeId &stateTypeId):
m_paramName(paramName),
m_stateDeviceId(stateDeviceId),
m_stateTypeId(stateTypeId)
{
}
/*! Returns the \l ParamTypeId of this \l RuleActionParam. */
ParamTypeId RuleActionParam::paramTypeId() const
{
@ -80,18 +130,6 @@ QString RuleActionParam::paramName() const
return m_paramName;
}
/*! Returns the eventParamTypeId of this RuleActionParam. */
ParamTypeId RuleActionParam::eventParamTypeId() const
{
return m_eventParamTypeId;
}
/*! Sets the \a eventParamTypeId of this RuleActionParam. */
void RuleActionParam::setEventParamTypeId(const ParamTypeId &eventParamTypeId)
{
m_eventParamTypeId = eventParamTypeId;
}
/*! Returns the value of this RuleActionParam. */
QVariant RuleActionParam::value() const
{
@ -104,14 +142,6 @@ void RuleActionParam::setValue(const QVariant &value)
m_value = value;
}
/*! Returns true if the \tt{(paramTypeId AND value) XOR (paramTypeId AND eventTypeId AND eventParamName)} of this RuleActionParam are set.*/
bool RuleActionParam::isValid() const
{
bool validValue = (!m_paramTypeId.isNull() && m_value.isValid() && m_eventTypeId.isNull() && m_eventParamTypeId.isNull());
bool validEvent = (!m_paramTypeId.isNull() && !m_value.isValid() && !m_eventTypeId.isNull() && !m_eventParamTypeId.isNull());
return validValue ^ validEvent;
}
/*! Return the EventTypeId of the \l{Event} with the \l{Param} which will be taken over in the \l{RuleAction}. */
EventTypeId RuleActionParam::eventTypeId() const
{
@ -124,6 +154,66 @@ void RuleActionParam::setEventTypeId(const EventTypeId &eventTypeId)
m_eventTypeId = eventTypeId;
}
/*! Returns the eventParamTypeId of this RuleActionParam. */
ParamTypeId RuleActionParam::eventParamTypeId() const
{
return m_eventParamTypeId;
}
/*! Sets the \a eventParamTypeId of this RuleActionParam. */
void RuleActionParam::setEventParamTypeId(const ParamTypeId &eventParamTypeId)
{
m_eventParamTypeId = eventParamTypeId;
}
/*! Returns the deviceId identifying the device to use a state value from. */
DeviceId RuleActionParam::stateDeviceId() const
{
return m_stateDeviceId;
}
/*! Sets the deviceId identifying the device to use a state value from. */
void RuleActionParam::setStateDeviceId(const DeviceId &stateDeviceId)
{
m_stateDeviceId = stateDeviceId;
}
/*! Returns the stateTypeId identifying the state to use the value. */
StateTypeId RuleActionParam::stateTypeId() const
{
return m_stateTypeId;
}
/*! Sets the stateTypeId identifying the state to use the value from. */
void RuleActionParam::setStateTypeId(const StateTypeId &stateTypeId)
{
m_stateTypeId = stateTypeId;
}
/*! Returns true if the \tt{(paramTypeId AND value) XOR (paramTypeId AND eventTypeId AND eventParamName)} of this RuleActionParam are set.*/
bool RuleActionParam::isValid() const
{
if (m_paramTypeId.isNull() && m_paramName.isNull()) {
return false;
}
return isValueBased() ^ isEventBased() ^ isStateBased();
}
bool RuleActionParam::isValueBased() const
{
return !m_value.isNull();
}
bool RuleActionParam::isEventBased() const
{
return !m_eventTypeId.isNull() && !m_eventParamTypeId.isNull();
}
bool RuleActionParam::isStateBased() const
{
return !m_stateDeviceId.isNull() && !m_stateTypeId.isNull();
}
/*! Writes the paramTypeId, value, eventId and eventParamTypeId of the given \a ruleActionParam to \a dbg. */
QDebug operator<<(QDebug dbg, const RuleActionParam &ruleActionParam)
{

View File

@ -36,29 +36,46 @@ class LIBNYMEA_EXPORT RuleActionParam
{
public:
RuleActionParam(const Param &param = Param());
RuleActionParam(const ParamTypeId &paramTypeId, const QVariant &value = QVariant(), const EventTypeId &eventTypeId = EventTypeId(), const ParamTypeId &eventParamTypeId = ParamTypeId());
RuleActionParam(const QString &paramName, const QVariant &value = QVariant(), const EventTypeId &eventTypeId = EventTypeId(), const ParamTypeId &eventParamTypeId = ParamTypeId());
RuleActionParam(const ParamTypeId &paramTypeId, const QVariant &value = QVariant());
RuleActionParam(const ParamTypeId &paramTypeId, const EventTypeId &eventTypeId, const ParamTypeId &eventParamTypeId);
RuleActionParam(const ParamTypeId &paramTypeId, const DeviceId &stateDeviceId, const StateTypeId &stateTypeId);
RuleActionParam(const QString &paramName, const QVariant &value = QVariant());
RuleActionParam(const QString &paramName, const EventTypeId &eventTypeId, const ParamTypeId &eventParamTypeId);
RuleActionParam(const QString &paramName, const DeviceId &stateDeviceId, const StateTypeId &stateTypeId);
ParamTypeId paramTypeId() const;
QString paramName() const;
ParamTypeId eventParamTypeId() const;
void setEventParamTypeId(const ParamTypeId &eventParamTypeId);
bool isValid() const;
bool isValueBased() const;
bool isEventBased() const;
bool isStateBased() const;
QVariant value() const;
void setValue(const QVariant &value);
bool isValid() const;
EventTypeId eventTypeId() const;
void setEventTypeId(const EventTypeId &eventTypeId);
ParamTypeId eventParamTypeId() const;
void setEventParamTypeId(const ParamTypeId &eventParamTypeId);
DeviceId stateDeviceId() const;
void setStateDeviceId(const DeviceId &deviceId);
StateTypeId stateTypeId() const;
void setStateTypeId(const StateTypeId &stateTypeId);
private:
ParamTypeId m_paramTypeId;
QString m_paramName;
QVariant m_value;
EventTypeId m_eventTypeId;
ParamTypeId m_eventParamTypeId;
DeviceId m_stateDeviceId;
StateTypeId m_stateTypeId;
};
Q_DECLARE_METATYPE(RuleActionParam)

View File

@ -1486,6 +1486,8 @@
"o:eventTypeId": "Uuid",
"o:paramName": "String",
"o:paramTypeId": "Uuid",
"o:stateDeviceId": "Uuid",
"o:stateTypeId": "Uuid",
"o:value": "$ref:BasicType"
},
"RuleDescription": {

View File

@ -93,9 +93,10 @@ private slots:
void enableDisableRule();
void testEventBasedAction();
void testEventBasedRuleWithExitAction();
void testStateBasedAction();
void removePolicyUpdate();
void removePolicyCascade();
void removePolicyUpdateRendersUselessRule();
@ -118,7 +119,6 @@ private slots:
void testHousekeeping_data();
void testHousekeeping();
};
void TestRules::cleanupMockHistory() {
@ -1149,7 +1149,6 @@ void TestRules::loadStoreConfig()
QVariantMap action2;
action2.insert("actionTypeId", mockActionIdWithParams);
qDebug() << "got action id" << mockActionIdWithParams;
action2.insert("deviceId", m_mockDeviceId);
QVariantList action2Params;
QVariantMap action2Param1;
@ -1221,6 +1220,7 @@ void TestRules::loadStoreConfig()
actionsInterfaces.append(actionInterfaces);
// rule 1
qCDebug(dcTests()) << "Adding rule 1";
QVariantMap params;
QVariantList actions;
actions.append(action1);
@ -1235,6 +1235,7 @@ void TestRules::loadStoreConfig()
verifyRuleError(response);
// rule 2
qCDebug(dcTests()) << "Adding rule 2";
QVariantMap params2;
QVariantList actions2;
actions2.append(action1);
@ -1250,6 +1251,7 @@ void TestRules::loadStoreConfig()
verifyRuleError(response2);
// rule 3
qCDebug(dcTests()) << "Adding rule 3";
QVariantMap params3;
QVariantList actions3;
actions3.append(validActionEventBased);
@ -1262,6 +1264,7 @@ void TestRules::loadStoreConfig()
verifyRuleError(response3);
// rule 4, interface based
qCDebug(dcTests()) << "Adding rule 4";
QVariantMap params4;
params4.insert("name", "TestRule4 - Interface based");
params4.insert("eventDescriptors", eventDescriptorsInterfaces);
@ -1272,10 +1275,12 @@ void TestRules::loadStoreConfig()
RuleId newRuleId4 = RuleId(response4.toMap().value("params").toMap().value("ruleId").toString());
verifyRuleError(response4);
qCDebug(dcTests()) << "Getting rules";
response = injectAndWait("Rules.GetRules");
QVariantList rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList();
qDebug() << "GetRules before server shutdown:" << response;
qCDebug(dcTests()) << "Restarting server";
restartServer();
response = injectAndWait("Rules.GetRules");
@ -2214,6 +2219,98 @@ void TestRules::testEventBasedRuleWithExitAction()
}
void TestRules::testStateBasedAction()
{
QNetworkAccessManager nam;
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
// Init bool state to true
spy.clear();
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateId.toString()).arg(true)));
QNetworkReply *reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
// Init int state to 11
spy.clear();
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateId.toString()).arg(11)));
reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
// Add a rule
QVariantMap addRuleParams;
QVariantMap eventDescriptor;
eventDescriptor.insert("eventTypeId", mockEvent1Id);
eventDescriptor.insert("deviceId", m_mockDeviceId);
addRuleParams.insert("eventDescriptors", QVariantList() << eventDescriptor);
addRuleParams.insert("name", "TestRule");
addRuleParams.insert("enabled", true);
QVariantList actions;
QVariantMap action;
QVariantList ruleActionParams;
QVariantMap param1;
param1.insert("paramTypeId", mockActionParam1ParamTypeId);
param1.insert("stateDeviceId", m_mockDeviceId);
param1.insert("stateTypeId", mockIntStateId);
QVariantMap param2;
param2.insert("paramTypeId", mockActionParam2ParamTypeId);
param2.insert("stateDeviceId", m_mockDeviceId);
param2.insert("stateTypeId", mockBoolStateId);
ruleActionParams.append(param1);
ruleActionParams.append(param2);
actions.clear();
action.insert("deviceId", m_mockDeviceId);
action.insert("actionTypeId", mockActionIdWithParams);
action.insert("ruleActionParams", ruleActionParams);
actions.append(action);
addRuleParams.insert("actions", actions);
qCDebug(dcTests) << "Adding rule";
QVariant response = injectAndWait("Rules.AddRule", addRuleParams);
verifyRuleError(response);
// trigger event
spy.clear();
request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1Id.toString())));
reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
LogFilter filter;
filter.addDeviceId(m_mockDeviceId);
filter.addTypeId(mockActionIdWithParams);
QList<LogEntry> entries = NymeaCore::instance()->logEngine()->logEntries(filter);
qCDebug(dcTests()) << "Log entries:" << entries;
// set bool state to false
spy.clear();
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateId.toString()).arg(false)));
reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
// trigger event
spy.clear();
request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1Id.toString())));
reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
entries = NymeaCore::instance()->logEngine()->logEntries(filter);
qCDebug(dcTests()) << "Log entries:" << entries;
}
void TestRules::removePolicyUpdate()
{
// ADD parent device
@ -2377,6 +2474,7 @@ void TestRules::removePolicyUpdateRendersUselessRule()
params.insert("deviceClassId", mockParentDeviceClassId);
params.insert("name", "Parent device");
qCDebug(dcTests()) << "Adding device";
QVariant response = injectAndWait("Devices.AddConfiguredDevice", params);
verifyDeviceError(response);
@ -2384,6 +2482,7 @@ void TestRules::removePolicyUpdateRendersUselessRule()
QVERIFY(!parentDeviceId.isNull());
// find child device
qCDebug(dcTests()) << "Gettin devices";
response = injectAndWait("Devices.GetConfiguredDevices");
QVariantList devices = response.toMap().value("params").toMap().value("devices").toList();
@ -2414,26 +2513,34 @@ void TestRules::removePolicyUpdateRendersUselessRule()
QVariantMap action;
action.insert("deviceId", childDeviceId);
action.insert("actionTypeId", mockParentChildActionId);
QVariantMap ruleActionParam;
ruleActionParam.insert("paramTypeId", mockParentChildActionId);
ruleActionParam.insert("value", true);
action.insert("ruleActionParams", QVariantList() << ruleActionParam);
params.insert("actions", QVariantList() << action);
qCDebug(dcTests()) << "Adding Rule";
response = injectAndWait("Rules.AddRule", params);
verifyRuleError(response);
RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString());
QVERIFY2(!ruleId.isNull(), "Could not get ruleId");
// Try to remove child device
qCDebug(dcTests()) << "Removing device (expecing failure - device is child)";
params.clear(); response.clear();
params.insert("deviceId", childDeviceId);
response = injectAndWait("Devices.RemoveConfiguredDevice", params);
verifyDeviceError(response, DeviceManager::DeviceErrorDeviceIsChild);
// Try to remove child device
qCDebug(dcTests()) << "Removing device (expeciting failure - device in use)";
params.clear(); response.clear();
params.insert("deviceId", parentDeviceId);
response = injectAndWait("Devices.RemoveConfiguredDevice", params);
verifyDeviceError(response, DeviceManager::DeviceErrorDeviceInRule);
// Remove policy
qCDebug(dcTests()) << "Removing device with update policy";
params.clear(); response.clear();
params.insert("deviceId", parentDeviceId);
params.insert("removePolicy", "RemovePolicyUpdate");
@ -2441,6 +2548,7 @@ void TestRules::removePolicyUpdateRendersUselessRule()
verifyDeviceError(response);
// get updated rule. It should've been deleted given it ended up with no actions
qCDebug(dcTests()) << "Getting details";
params.clear();
params.insert("ruleId", ruleId);
response = injectAndWait("Rules.GetRuleDetails", params);
@ -2480,10 +2588,10 @@ void TestRules::testRuleActionParams_data()
QTest::newRow("valid action params") << action << QVariantMap() << RuleEngine::RuleErrorNoError;
QTest::newRow("valid action and exit action params") << action << action << RuleEngine::RuleErrorNoError;
QTest::newRow("invalid action params1") << invalidAction1 << QVariantMap() << RuleEngine::RuleErrorInvalidRuleActionParameter;
QTest::newRow("invalid action params2") << invalidAction2 << QVariantMap() << RuleEngine::RuleErrorInvalidRuleActionParameter;
QTest::newRow("valid action and invalid exit action params1") << action << invalidAction1 << RuleEngine::RuleErrorInvalidRuleActionParameter;
QTest::newRow("valid action and invalid exit action params2") << action << invalidAction2 << RuleEngine::RuleErrorInvalidRuleActionParameter;
QTest::newRow("invalid action params1") << invalidAction1 << QVariantMap() << RuleEngine::RuleErrorMissingParameter;
QTest::newRow("invalid action params2") << invalidAction2 << QVariantMap() << RuleEngine::RuleErrorMissingParameter;
QTest::newRow("valid action and invalid exit action params1") << action << invalidAction1 << RuleEngine::RuleErrorMissingParameter;
QTest::newRow("valid action and invalid exit action params2") << action << invalidAction2 << RuleEngine::RuleErrorMissingParameter;
}
void TestRules::testRuleActionParams()