Add support for state based rule action params

pull/148/head
Michael Zanetti 2019-03-26 12:21:29 +01:00
parent 87595faf6d
commit 774452ff7c
10 changed files with 467 additions and 168 deletions

View File

@ -188,6 +188,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:deviceId", basicTypeToString(Uuid));
s_ruleActionParam.insert("o:stateTypeId", basicTypeToString(Uuid));
// ParamDescriptor
s_paramDescriptor.insert("o:paramTypeId", basicTypeToString(Uuid));
@ -612,10 +614,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("deviceId", ruleActionParam.deviceId().toString());
variantMap.insert("stateTypeId", ruleActionParam.stateTypeId().toString());
} else {
variantMap.insert("value", ruleActionParam.value());
}
@ -1429,13 +1434,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.setDeviceId(DeviceId(ruleActionParamMap.value("deviceId").toString()));
param.setStateTypeId(StateTypeId(ruleActionParamMap.value("stateTypeId").toString()));
return param;
}
/*! Returns a \l{RuleActionParamList} created from the given \a ruleActionParamList. */

View File

@ -438,31 +438,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.deviceId());
if (!stateDevice) {
qCWarning(dcRuleEngine()) << "Cannot find device" << ruleActionParam.deviceId() << "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.deviceId());
if (!stateDevice) {
qCWarning(dcRuleEngine()) << "Cannot find device" << ruleActionParam.deviceId() << "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);
@ -652,9 +706,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

@ -401,55 +401,6 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
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()) {
@ -471,77 +422,145 @@ RuleEngine::RuleError RuleEngine::addRule(const Rule &rule, bool fromEdit)
return RuleError::RuleErrorInvalidParameter;
}
}
// TODO: Check params
}
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (ruleActionParam.isEventBased()) {
// We have an eventTypeId, see if the rule actually has such a event
if (rule.eventDescriptors().isEmpty() || !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(static_cast<int>(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 if (ruleActionParam.isStateBased()) {
Device *d = NymeaCore::instance()->deviceManager()->findConfiguredDevice(ruleActionParam.deviceId());
if (!d) {
qCWarning(dcRuleEngine()) << "Cannot create Rule. DeviceId from RuleAction" << action.actionTypeId() << "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(action.actionTypeId(), 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 {
if (ruleActionParam.value().isNull()) {
qCDebug(dcRuleEngine()) << "Cannot create rule. No param value given for action:" << ruleActionParam.paramTypeId().toString();
return RuleErrorInvalidRuleActionParameter;
}
QVariant::Type actionParamType = getActionParamType(action.actionTypeId(), ruleActionParam.paramTypeId());
if (ruleActionParam.value().type() != actionParamType && !ruleActionParam.value().canConvert(static_cast<int>(actionParamType))) {
qCDebug(dcRuleEngine()) << "Cannot create rule. Given param value for action" << ruleActionParam.paramTypeId().toString() << "does not match type";
return RuleErrorInvalidRuleActionParameter;
}
}
}
foreach (const RuleActionParam &ruleActionParam, action.ruleActionParams()) {
if (!ruleActionParam.isValid()) {
qCWarning(dcRuleEngine) << "Cannot create rule. There must be only one out of \"value\", \"eventTypeId/eventParamTypeID\" or \"deviceId/stateTypeId\".";
return RuleEngine::RuleErrorInvalidRuleActionParameter;
}
}
}
// Check exit actions
foreach (const RuleAction &ruleAction, rule.exitActions()) {
if (!ruleAction.isValid()) {
foreach (const RuleAction &ruleExitAction, rule.exitActions()) {
if (!ruleExitAction.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 (ruleExitAction.type() == RuleAction::TypeDevice) {
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(ruleExitAction.deviceId());
if (!device) {
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for exit action with actionTypeId" << ruleAction.actionTypeId();
qCWarning(dcRuleEngine) << "Cannot create rule. No configured device for exit action with actionTypeId" << ruleExitAction.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();
if (!deviceClass.hasActionType(ruleExitAction.actionTypeId())) {
qCWarning(dcRuleEngine) << "Cannot create rule. Device " + device->name() + " has no action type:" << ruleExitAction.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());
Interface iface = NymeaCore::instance()->deviceManager()->supportedInterfaces().findByName(ruleExitAction.interface());
if (!iface.isValid()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << ruleAction.interface();
qCWarning(dcRuleEngine()) << "Cannot create rule. No such interface:" << ruleExitAction.interface();
return RuleError::RuleErrorInterfaceNotFound;
}
ActionType ifaceActionType = iface.actionTypes().findByName(ruleAction.interfaceAction());
ActionType ifaceActionType = iface.actionTypes().findByName(ruleExitAction.interfaceAction());
if (ifaceActionType.name().isEmpty()) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface" << iface.name() << "does not implement action" << ruleAction.interfaceAction();
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface" << iface.name() << "does not implement action" << ruleExitAction.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());
if (!ruleExitAction.ruleActionParams().hasParam(ifaceActionParamType.name())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action" << iface.name() << ":" << ruleExitAction.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());
if (!ruleExitAction.ruleActionParam(ifaceActionParamType.name()).value().canConvert(ifaceActionParamType.type())) {
qCWarning(dcRuleEngine()) << "Cannot create rule. Interface action parameter" << iface.name() << ":" << ruleExitAction.interfaceAction() << ":" << ifaceActionParamType.name() << "has wrong type. Expected" << QVariant::typeToName(ifaceActionParamType.type());
return RuleError::RuleErrorInvalidParameter;
}
}
// TODO: Check params
}
foreach (const RuleActionParam &ruleActionParam, ruleExitAction.ruleActionParams()) {
if (ruleActionParam.isEventBased()) {
// We have an eventTypeId, see if the rule actually has such a event
qCWarning(dcRuleEngine) << "Cannot create rule. Exit actions cannot be event based.";
return RuleErrorInvalidRuleActionParameter;
} else if (ruleActionParam.isStateBased()) {
Device *d = NymeaCore::instance()->deviceManager()->findConfiguredDevice(ruleActionParam.deviceId());
if (!d) {
qCWarning(dcRuleEngine()) << "Cannot create Rule. DeviceId from RuleAction" << ruleExitAction.actionTypeId() << "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(ruleExitAction.actionTypeId(), 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 {
if (ruleActionParam.value().isNull()) {
qCDebug(dcRuleEngine()) << "Cannot create rule. No param value given for action:" << ruleActionParam.paramTypeId().toString();
return RuleErrorInvalidRuleActionParameter;
}
QVariant::Type actionParamType = getActionParamType(ruleExitAction.actionTypeId(), ruleActionParam.paramTypeId());
if (ruleActionParam.value().type() != actionParamType && !ruleActionParam.value().canConvert(static_cast<int>(actionParamType))) {
qCDebug(dcRuleEngine()) << "Cannot create rule. Given param value for action" << ruleActionParam.paramTypeId().toString() << "does not match type";
return RuleErrorInvalidRuleActionParameter;
}
}
}
foreach (const RuleActionParam &ruleActionParam, ruleExitAction.ruleActionParams()) {
if (!ruleActionParam.isValid()) {
qCWarning(dcRuleEngine) << "Cannot create rule. There must be only one out of \"value\", \"eventTypeId/eventParamTypeID\" or \"deviceId/stateTypeId\".";
return RuleEngine::RuleErrorInvalidRuleActionParameter;
}
}
}
@ -698,9 +717,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;
}
@ -1203,11 +1222,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("deviceId", param.deviceId().toString());
settings.setValue("stateTypeId", param.stateTypeId());
}
settings.endGroup();
}
@ -1276,7 +1298,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");
@ -1313,7 +1335,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");
@ -1414,6 +1436,8 @@ 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 deviceId = DeviceId(settings.value("deviceId", 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();
@ -1427,19 +1451,20 @@ void RuleEngine::init()
}
}
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.setDeviceId(deviceId);
param.setStateTypeId(stateTypeId);
params.append(param);
params.append(param);
settings.endGroup();
}
}

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

@ -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 deviceId 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 &deviceId, const StateTypeId &stateTypeId):
m_paramTypeId(paramTypeId),
m_deviceId(deviceId),
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 deviceId and \a stateTypeId.
* \sa Param, Event, */
RuleActionParam::RuleActionParam(const QString &paramName, const DeviceId &deviceId, const StateTypeId &stateTypeId):
m_paramName(paramName),
m_deviceId(deviceId),
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::deviceId() const
{
return m_deviceId;
}
/*! Sets the deviceId identifying the device to use a state value from. */
void RuleActionParam::setDeviceId(const DeviceId &deviceId)
{
m_deviceId = deviceId;
}
/*! 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_deviceId.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 &deviceId, 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 &deviceId, 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 deviceId() const;
void setDeviceId(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_deviceId;
StateTypeId m_stateTypeId;
};
Q_DECLARE_METATYPE(RuleActionParam)

View File

@ -93,9 +93,10 @@ private slots:
void enableDisableRule();
void testEventBasedAction();
void testEventBasedRuleWithExitAction();
void testStateBasedAction();
void removePolicyUpdate();
void removePolicyCascade();
void removePolicyUpdateRendersUselessRule();
@ -2214,6 +2215,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("deviceId", m_mockDeviceId);
param1.insert("stateTypeId", mockIntStateId);
QVariantMap param2;
param2.insert("paramTypeId", mockActionParam2ParamTypeId);
param2.insert("deviceId", 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);
qDebug() << addRuleParams;
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