add support for interface based StateDescriptors
This commit is contained in:
parent
5f226df930
commit
e21c2f16a5
@ -206,8 +206,10 @@ void JsonTypes::init()
|
||||
s_state.insert("value", basicTypeToString(Variant));
|
||||
|
||||
// StateDescriptor
|
||||
s_stateDescriptor.insert("stateTypeId", basicTypeToString(Uuid));
|
||||
s_stateDescriptor.insert("deviceId", basicTypeToString(Uuid));
|
||||
s_stateDescriptor.insert("o:stateTypeId", basicTypeToString(Uuid));
|
||||
s_stateDescriptor.insert("o:deviceId", basicTypeToString(Uuid));
|
||||
s_stateDescriptor.insert("o:interface", basicTypeToString(String));
|
||||
s_stateDescriptor.insert("o:interfaceState", basicTypeToString(String));
|
||||
s_stateDescriptor.insert("value", basicTypeToString(Variant));
|
||||
s_stateDescriptor.insert("operator", valueOperatorRef());
|
||||
|
||||
@ -576,7 +578,11 @@ QVariantMap JsonTypes::packRuleAction(const RuleAction &ruleAction)
|
||||
QVariantMap JsonTypes::packRuleActionParam(const RuleActionParam &ruleActionParam)
|
||||
{
|
||||
QVariantMap variantMap;
|
||||
variantMap.insert("paramTypeId", ruleActionParam.paramTypeId().toString());
|
||||
if (!ruleActionParam.paramTypeId().isNull()) {
|
||||
variantMap.insert("paramTypeId", ruleActionParam.paramTypeId().toString());
|
||||
} else {
|
||||
variantMap.insert("paramName", ruleActionParam.paramName());
|
||||
}
|
||||
// if this ruleaction param has a valid EventTypeId, there is no value
|
||||
if (ruleActionParam.eventTypeId() != EventTypeId()) {
|
||||
variantMap.insert("eventTypeId", ruleActionParam.eventTypeId());
|
||||
@ -632,8 +638,13 @@ QVariantMap JsonTypes::packStateType(const StateType &stateType)
|
||||
QVariantMap JsonTypes::packStateDescriptor(const StateDescriptor &stateDescriptor)
|
||||
{
|
||||
QVariantMap variantMap;
|
||||
variantMap.insert("stateTypeId", stateDescriptor.stateTypeId().toString());
|
||||
variantMap.insert("deviceId", stateDescriptor.deviceId().toString());
|
||||
if (stateDescriptor.type() == StateDescriptor::TypeDevice) {
|
||||
variantMap.insert("stateTypeId", stateDescriptor.stateTypeId().toString());
|
||||
variantMap.insert("deviceId", stateDescriptor.deviceId().toString());
|
||||
} else {
|
||||
variantMap.insert("interface", stateDescriptor.interface());
|
||||
variantMap.insert("interfaceState", stateDescriptor.interfaceState());
|
||||
}
|
||||
variantMap.insert("value", stateDescriptor.stateValue());
|
||||
variantMap.insert("operator", s_valueOperator.at(stateDescriptor.operatorType()));
|
||||
return variantMap;
|
||||
@ -1435,9 +1446,15 @@ StateDescriptor JsonTypes::unpackStateDescriptor(const QVariantMap &stateDescrip
|
||||
{
|
||||
StateTypeId stateTypeId(stateDescriptorMap.value("stateTypeId").toString());
|
||||
DeviceId deviceId(stateDescriptorMap.value("deviceId").toString());
|
||||
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());
|
||||
StateDescriptor stateDescriptor(stateTypeId, deviceId, value, operatorType);
|
||||
if (!deviceId.isNull() && !stateTypeId.isNull()) {
|
||||
StateDescriptor stateDescriptor(stateTypeId, deviceId, value, operatorType);
|
||||
return stateDescriptor;
|
||||
}
|
||||
StateDescriptor stateDescriptor(interface, interfaceState, value, operatorType);
|
||||
return stateDescriptor;
|
||||
}
|
||||
|
||||
|
||||
@ -1155,8 +1155,18 @@ bool RuleEngine::containsEvent(const Rule &rule, const Event &event, const Devic
|
||||
|
||||
bool RuleEngine::containsState(const StateEvaluator &stateEvaluator, const Event &stateChangeEvent)
|
||||
{
|
||||
if (stateEvaluator.stateDescriptor().isValid() && stateEvaluator.stateDescriptor().stateTypeId().toString() == stateChangeEvent.eventTypeId().toString()) {
|
||||
return true;
|
||||
if (stateEvaluator.stateDescriptor().isValid()) {
|
||||
if (stateEvaluator.stateDescriptor().type() == StateDescriptor::TypeDevice) {
|
||||
if (stateEvaluator.stateDescriptor().stateTypeId().toString() == stateChangeEvent.eventTypeId().toString()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(stateChangeEvent.deviceId());
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
|
||||
if (deviceClass.interfaces().contains(stateEvaluator.stateDescriptor().interface())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const StateEvaluator &childEvaluator, stateEvaluator.childEvaluators()) {
|
||||
|
||||
@ -102,21 +102,40 @@ bool StateEvaluator::evaluate() const
|
||||
qCDebug(dcRuleEngineDebug()) << "StateEvaluator:" << this << "Evaluating: Operator type" << m_operatorType << "Valid descriptor:" << m_stateDescriptor.isValid() << "Childs:" << m_childEvaluators.count();
|
||||
bool descriptorMatching = true;
|
||||
if (m_stateDescriptor.isValid()) {
|
||||
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(m_stateDescriptor.deviceId());
|
||||
if (!device) {
|
||||
qCWarning(dcRuleEngine) << "StateEvaluator:" << this << "Device not existing!";
|
||||
descriptorMatching = false;
|
||||
} else {
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
|
||||
if (!device->hasState(m_stateDescriptor.stateTypeId())) {
|
||||
qCWarning(dcRuleEngine) << "StateEvaluator:" << this << "Device found, but it does not appear to have such a state!";
|
||||
descriptorMatching = false;
|
||||
}
|
||||
if (m_stateDescriptor != device->state(m_stateDescriptor.stateTypeId())) {
|
||||
// state not matching
|
||||
descriptorMatching = false;
|
||||
}
|
||||
qCDebug(dcRuleEngineDebug()) << "StateEvaluator:" << this << "State" << device->name() << deviceClass.stateTypes().findById(m_stateDescriptor.stateTypeId()).name() << (descriptorMatching ? "is" : "not") << "matching:" << m_stateDescriptor.stateValue() << m_stateDescriptor.operatorType() << device->stateValue(m_stateDescriptor.stateTypeId());
|
||||
descriptorMatching = false;
|
||||
if (m_stateDescriptor.type() == StateDescriptor::TypeDevice) {
|
||||
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(m_stateDescriptor.deviceId());
|
||||
if (device) {
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
|
||||
if (device->hasState(m_stateDescriptor.stateTypeId())) {
|
||||
if (m_stateDescriptor == device->state(m_stateDescriptor.stateTypeId())) {
|
||||
qCDebug(dcRuleEngineDebug()) << "StateEvaluator:" << this << "State" << device->name() << deviceClass.stateTypes().findById(m_stateDescriptor.stateTypeId()).name() << (descriptorMatching ? "is" : "not") << "matching:" << m_stateDescriptor.stateValue() << m_stateDescriptor.operatorType() << device->stateValue(m_stateDescriptor.stateTypeId());
|
||||
descriptorMatching = true;
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcRuleEngine) << "StateEvaluator:" << this << "Device found, but it does not appear to have such a state!";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcRuleEngine) << "StateEvaluator:" << this << "Device not existing!";
|
||||
}
|
||||
} else { // interface
|
||||
foreach (Device* device, NymeaCore::instance()->deviceManager()->configuredDevices()) {
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
|
||||
if (!deviceClass.isValid()) {
|
||||
qCWarning(dcRuleEngine()) << "Could not find DeviceClass for Device" << device->name() << device->id();
|
||||
continue;
|
||||
}
|
||||
if (deviceClass.interfaces().contains(m_stateDescriptor.interface())) {
|
||||
StateType stateType = deviceClass.stateTypes().findByName(m_stateDescriptor.interfaceState());
|
||||
State state = device->state(stateType.id());
|
||||
// As the StateDescriptor can't compare on it's own against interfaces, generate custom one, matching the device
|
||||
StateDescriptor temporaryDescriptor(stateType.id(), device->id(), m_stateDescriptor.stateValue(), m_stateDescriptor.operatorType());
|
||||
if (temporaryDescriptor == state) {
|
||||
descriptorMatching = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,6 +213,8 @@ void StateEvaluator::dumpToSettings(NymeaSettings &settings, const QString &grou
|
||||
settings.beginGroup("stateDescriptor");
|
||||
settings.setValue("stateTypeId", m_stateDescriptor.stateTypeId().toString());
|
||||
settings.setValue("deviceId", m_stateDescriptor.deviceId().toString());
|
||||
settings.setValue("interface", m_stateDescriptor.interface());
|
||||
settings.setValue("interfaceState", m_stateDescriptor.interfaceState());
|
||||
settings.setValue("value", m_stateDescriptor.stateValue());
|
||||
settings.setValue("operator", m_stateDescriptor.operatorType());
|
||||
settings.endGroup();
|
||||
@ -218,8 +239,16 @@ StateEvaluator StateEvaluator::loadFromSettings(NymeaSettings &settings, const Q
|
||||
StateTypeId stateTypeId(settings.value("stateTypeId").toString());
|
||||
DeviceId deviceId(settings.value("deviceId").toString());
|
||||
QVariant stateValue = settings.value("value");
|
||||
QString interface = settings.value("interface").toString();
|
||||
QString interfaceState = settings.value("interfaceState").toString();
|
||||
Types::ValueOperator valueOperator = (Types::ValueOperator)settings.value("operator").toInt();
|
||||
StateDescriptor stateDescriptor(stateTypeId, deviceId, stateValue, valueOperator);
|
||||
StateDescriptor stateDescriptor;
|
||||
if (!deviceId.isNull() && !stateTypeId.isNull()) {
|
||||
stateDescriptor = StateDescriptor(stateTypeId, deviceId, stateValue, valueOperator);
|
||||
} else {
|
||||
stateDescriptor = StateDescriptor(interface, interfaceState, stateValue, valueOperator);
|
||||
}
|
||||
|
||||
settings.endGroup();
|
||||
|
||||
StateEvaluator ret(stateDescriptor);
|
||||
@ -238,51 +267,63 @@ StateEvaluator StateEvaluator::loadFromSettings(NymeaSettings &settings, const Q
|
||||
bool StateEvaluator::isValid() const
|
||||
{
|
||||
if (m_stateDescriptor.isValid()) {
|
||||
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(m_stateDescriptor.deviceId());
|
||||
if (!device) {
|
||||
qCWarning(dcRuleEngine) << "State evaluator device does not exist!";
|
||||
return false;
|
||||
}
|
||||
if (m_stateDescriptor.type() == StateDescriptor::TypeDevice) {
|
||||
Device *device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(m_stateDescriptor.deviceId());
|
||||
if (!device) {
|
||||
qCWarning(dcRuleEngine) << "State evaluator device does not exist!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!device->hasState(m_stateDescriptor.stateTypeId())) {
|
||||
qCWarning(dcRuleEngine) << "State evaluator device found, but it does not appear to have such a state!";
|
||||
return false;
|
||||
}
|
||||
if (!device->hasState(m_stateDescriptor.stateTypeId())) {
|
||||
qCWarning(dcRuleEngine) << "State evaluator device found, but it does not appear to have such a state!";
|
||||
return false;
|
||||
}
|
||||
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
|
||||
foreach (const StateType &stateType, deviceClass.stateTypes()) {
|
||||
if (stateType.id() == m_stateDescriptor.stateTypeId()) {
|
||||
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(device->deviceClassId());
|
||||
foreach (const StateType &stateType, deviceClass.stateTypes()) {
|
||||
if (stateType.id() == m_stateDescriptor.stateTypeId()) {
|
||||
|
||||
if (!m_stateDescriptor.stateValue().canConvert(stateType.type())) {
|
||||
qCWarning(dcRuleEngine) << "Wrong state value for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Expected:" << QVariant::typeToName(stateType.type());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_stateDescriptor.stateValue().convert(stateType.type())) {
|
||||
qCWarning(dcRuleEngine) << "Could not convert value of state descriptor" << m_stateDescriptor.stateTypeId() << " to:" << QVariant::typeToName(stateType.type()) << " Got:" << m_stateDescriptor.stateValue();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stateType.maxValue().isValid() && m_stateDescriptor.stateValue() > stateType.maxValue()) {
|
||||
qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Max:" << stateType.maxValue();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stateType.minValue().isValid() && m_stateDescriptor.stateValue() < stateType.minValue()) {
|
||||
qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Min:" << stateType.minValue();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stateType.possibleValues().isEmpty() && !stateType.possibleValues().contains(m_stateDescriptor.stateValue())) {
|
||||
QStringList possibleValues;
|
||||
foreach (const QVariant &value, stateType.possibleValues()) {
|
||||
possibleValues.append(value.toString());
|
||||
if (!m_stateDescriptor.stateValue().canConvert(stateType.type())) {
|
||||
qCWarning(dcRuleEngine) << "Wrong state value for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Expected:" << QVariant::typeToName(stateType.type());
|
||||
return false;
|
||||
}
|
||||
|
||||
qCWarning(dcRuleEngine) << "Value not in possible values for state type" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Possible values:" << possibleValues.join(", ");
|
||||
return false;
|
||||
if (!m_stateDescriptor.stateValue().convert(stateType.type())) {
|
||||
qCWarning(dcRuleEngine) << "Could not convert value of state descriptor" << m_stateDescriptor.stateTypeId() << " to:" << QVariant::typeToName(stateType.type()) << " Got:" << m_stateDescriptor.stateValue();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stateType.maxValue().isValid() && m_stateDescriptor.stateValue() > stateType.maxValue()) {
|
||||
qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Max:" << stateType.maxValue();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stateType.minValue().isValid() && m_stateDescriptor.stateValue() < stateType.minValue()) {
|
||||
qCWarning(dcRuleEngine) << "Value out of range for state descriptor" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Min:" << stateType.minValue();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stateType.possibleValues().isEmpty() && !stateType.possibleValues().contains(m_stateDescriptor.stateValue())) {
|
||||
QStringList possibleValues;
|
||||
foreach (const QVariant &value, stateType.possibleValues()) {
|
||||
possibleValues.append(value.toString());
|
||||
}
|
||||
|
||||
qCWarning(dcRuleEngine) << "Value not in possible values for state type" << m_stateDescriptor.stateTypeId() << " Got:" << m_stateDescriptor.stateValue() << " Possible values:" << possibleValues.join(", ");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // TypeInterface
|
||||
Interface iface = NymeaCore::instance()->deviceManager()->supportedInterfaces().findByName(m_stateDescriptor.interface());
|
||||
if (!iface.isValid()) {
|
||||
qWarning(dcRuleEngine()) << "No such interface:" << m_stateDescriptor.interface();
|
||||
return false;
|
||||
}
|
||||
if (iface.stateTypes().findByName(m_stateDescriptor.interfaceState()).name().isEmpty()) {
|
||||
qWarning(dcRuleEngine()) << "Interface" << iface.name() << "has no such state:" << m_stateDescriptor.interfaceState();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,9 @@
|
||||
\ingroup rules
|
||||
\inmodule libnymea
|
||||
|
||||
An StateDescriptor describes a \l{State} in order to match it with a \l{nymeaserver::Rule}.
|
||||
A StateDescriptor describes a \l{State} in order to match it with a \l{nymeaserver::Rule}.
|
||||
A StateDescriptor uses either a \l{DeviceId}/\l{StateTypeId} pair to describe a \l{State} or
|
||||
a pair of strings describing the interface and interface action for a \l{State}.
|
||||
|
||||
\sa State, nymeaserver::Rule
|
||||
*/
|
||||
@ -54,6 +56,22 @@ StateDescriptor::StateDescriptor(const StateTypeId &stateTypeId, const DeviceId
|
||||
|
||||
}
|
||||
|
||||
/*! Constructs an StateDescriptor describing an \l{State} with the given \a interface, \a interfaceState, \a stateValue and \a operatorType.*/
|
||||
StateDescriptor::StateDescriptor(const QString &interface, const QString &interfaceState, const QVariant &stateValue, Types::ValueOperator operatorType):
|
||||
m_interface(interface),
|
||||
m_interfaceState(interfaceState),
|
||||
m_stateValue(stateValue),
|
||||
m_operatorType(operatorType)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*! Returns true \l{StateDescriptor::Type}{Type} of this descriptor. */
|
||||
StateDescriptor::Type StateDescriptor::type() const
|
||||
{
|
||||
return (!m_deviceId.isNull() && !m_stateTypeId.isNull()) ? TypeDevice : TypeInterface;
|
||||
}
|
||||
|
||||
/*! Returns the StateTypeId of this \l{State}.*/
|
||||
StateTypeId StateDescriptor::stateTypeId() const
|
||||
{
|
||||
@ -66,6 +84,18 @@ DeviceId StateDescriptor::deviceId() const
|
||||
return m_deviceId;
|
||||
}
|
||||
|
||||
/*! Returns the interface for this \{StateDescriptor}.*/
|
||||
QString StateDescriptor::interface() const
|
||||
{
|
||||
return m_interface;
|
||||
}
|
||||
|
||||
/*! Returns the interface state's name for this \{StateDescriptor}.*/
|
||||
QString StateDescriptor::interfaceState() const
|
||||
{
|
||||
return m_interfaceState;
|
||||
}
|
||||
|
||||
/*! Returns the Value of this \l{State}.*/
|
||||
QVariant StateDescriptor::stateValue() const
|
||||
{
|
||||
@ -84,6 +114,8 @@ bool StateDescriptor::operator ==(const StateDescriptor &other) const
|
||||
{
|
||||
return m_stateTypeId == other.stateTypeId() &&
|
||||
m_deviceId == other.deviceId() &&
|
||||
m_interface == other.interface() &&
|
||||
m_interfaceState == other.interfaceState() &&
|
||||
m_stateValue == other.stateValue() &&
|
||||
m_operatorType == other.operatorType();
|
||||
}
|
||||
@ -121,11 +153,11 @@ bool StateDescriptor::operator !=(const State &state) const
|
||||
return !(operator==(state));
|
||||
}
|
||||
|
||||
/*! Returns the true if this \l{StateDescriptor} is valid. A \l{StateDescriptor} is valid
|
||||
* if the DeviceId and the StateTypeId are set and the state value of this \l{StateDescriptor} is valid.
|
||||
/*! Returns the true if this \l{StateDescriptor} is valid. A valid \l{StateDescriptor} must
|
||||
* have a valid stateValue along with either a DeviceId/StateTypeId pair or an interface/interfaceState pair.
|
||||
* \sa StateDescriptor(), deviceId(), stateValue()
|
||||
*/
|
||||
bool StateDescriptor::isValid() const
|
||||
{
|
||||
return !m_deviceId.isNull() && !m_stateTypeId.isNull() && m_stateValue.isValid();
|
||||
return ((!m_deviceId.isNull() && !m_stateTypeId.isNull()) || (!m_interface.isNull() && !m_interfaceState.isNull())) && m_stateValue.isValid();
|
||||
}
|
||||
|
||||
@ -37,11 +37,23 @@
|
||||
class LIBNYMEA_EXPORT StateDescriptor
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
TypeDevice,
|
||||
TypeInterface
|
||||
};
|
||||
|
||||
StateDescriptor();
|
||||
StateDescriptor(const StateTypeId &stateTypeId, const DeviceId &deviceId, const QVariant &stateValue, Types::ValueOperator operatorType = Types::ValueOperatorEquals);
|
||||
StateDescriptor(const QString &interface, const QString &interfaceState, const QVariant &stateValue, Types::ValueOperator operatorType = Types::ValueOperatorEquals);
|
||||
|
||||
Type type() const;
|
||||
|
||||
StateTypeId stateTypeId() const;
|
||||
DeviceId deviceId() const;
|
||||
|
||||
QString interface() const;
|
||||
QString interfaceState() const;
|
||||
|
||||
QVariant stateValue() const;
|
||||
Types::ValueOperator operatorType() const;
|
||||
|
||||
@ -55,6 +67,8 @@ public:
|
||||
private:
|
||||
StateTypeId m_stateTypeId;
|
||||
DeviceId m_deviceId;
|
||||
QString m_interface;
|
||||
QString m_interfaceState;
|
||||
QVariant m_stateValue;
|
||||
Types::ValueOperator m_operatorType;
|
||||
};
|
||||
|
||||
@ -117,8 +117,8 @@ NymeaTestBase::NymeaTestBase(QObject *parent) :
|
||||
{
|
||||
qRegisterMetaType<QNetworkReply*>();
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch());
|
||||
m_mockDevice1Port = 1337 + (qrand() % 1000);
|
||||
m_mockDevice2Port = 7331 + (qrand() % 1000);
|
||||
m_mockDevice1Port = 1337 + (qrand() % 10000);
|
||||
m_mockDevice2Port = 7331 + (qrand() % 10000);
|
||||
|
||||
// Important for settings
|
||||
QCoreApplication::instance()->setOrganizationName("nymea-test");
|
||||
|
||||
@ -107,7 +107,9 @@ private slots:
|
||||
void testRuleActionPAramsFromEventParameter_data();
|
||||
void testRuleActionPAramsFromEventParameter();
|
||||
|
||||
void testInterfaceBasedRule();
|
||||
void testInterfaceBasedEventRule();
|
||||
|
||||
void testInterfaceBasedStateRule();
|
||||
|
||||
void testHousekeeping_data();
|
||||
void testHousekeeping();
|
||||
@ -499,7 +501,6 @@ void TestRules::addRemoveRules_data()
|
||||
QTest::newRow("valid rule. 2 EventDescriptors, 1 Action, name") << true << validActionNoParams << QVariantMap() << QVariantMap() << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorNoError << true << "TestRule";
|
||||
QTest::newRow("invalid action") << true << invalidAction << QVariantMap() << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorActionTypeNotFound << false << "TestRule";
|
||||
QTest::newRow("invalid event descriptor") << true << validActionNoParams << QVariantMap() << invalidEventDescriptor << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorEventTypeNotFound << false << "TestRule";
|
||||
QTest::newRow("invalid StateDescriptor") << true << validActionNoParams << QVariantMap() << validEventDescriptor1 << QVariantList() << invalidStateEvaluator << RuleEngine::RuleErrorInvalidParameter << true << "TestRule";
|
||||
}
|
||||
|
||||
void TestRules::addRemoveRules()
|
||||
@ -881,7 +882,7 @@ void TestRules::editRules()
|
||||
response = injectAndWait("Rules.EditRule", params);
|
||||
verifyRuleError(response, error);
|
||||
if (error == RuleEngine::RuleErrorNoError){
|
||||
clientSpy.wait(500);
|
||||
clientSpy.wait(1);
|
||||
// We need to get exactly 2 replies. The actual reply and the Changed notification
|
||||
// Make sure there are no other notifications (e.g. RuleAdded or similar)
|
||||
QCOMPARE(clientSpy.count(), 2);
|
||||
@ -1090,8 +1091,19 @@ void TestRules::loadStoreConfig()
|
||||
stateEvaluator3.insert("stateDescriptor", stateDescriptor3);
|
||||
stateEvaluator3.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd));
|
||||
|
||||
QVariantMap stateDescriptor4;
|
||||
stateDescriptor4.insert("interface", "battery");
|
||||
stateDescriptor4.insert("interfaceState", "batteryCritical");
|
||||
stateDescriptor4.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals));
|
||||
stateDescriptor4.insert("value", true);
|
||||
|
||||
QVariantMap stateEvaluator4;
|
||||
stateEvaluator4.insert("stateDescriptor", stateDescriptor4);
|
||||
stateEvaluator4.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd));
|
||||
|
||||
childEvaluators.append(stateEvaluator2);
|
||||
childEvaluators.append(stateEvaluator3);
|
||||
childEvaluators.append(stateEvaluator4);
|
||||
stateEvaluator1.insert("childEvaluators", childEvaluators);
|
||||
stateEvaluator1.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd));
|
||||
|
||||
@ -1178,7 +1190,7 @@ void TestRules::loadStoreConfig()
|
||||
|
||||
response = injectAndWait("Rules.GetRules");
|
||||
QVariantList rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList();
|
||||
qDebug() << response;
|
||||
qDebug() << "GetRules before server shutdown:" << response;
|
||||
|
||||
restartServer();
|
||||
|
||||
@ -1219,19 +1231,26 @@ void TestRules::loadStoreConfig()
|
||||
QVERIFY2(found, "missing eventdescriptor");
|
||||
}
|
||||
|
||||
qDebug() << endl << rule1;
|
||||
qDebug() << "Rule after loading from config:" << rule1;
|
||||
|
||||
QVERIFY2(rule1.value("name").toString() == "TestRule", "Loaded wrong name for rule");
|
||||
QVariantMap replyStateEvaluator= rule1.value("stateEvaluator").toMap();
|
||||
QVariantList replyChildEvaluators = replyStateEvaluator.value("childEvaluators").toList();
|
||||
QVERIFY2(replyChildEvaluators.count() == 2, "There should be exactly 2 childEvaluators");
|
||||
QCOMPARE(replyChildEvaluators.count(), 3);
|
||||
QVERIFY2(replyStateEvaluator.value("operator") == "StateOperatorAnd", "There should be the AND operator.");
|
||||
|
||||
foreach (const QVariant &childEvaluator, replyChildEvaluators) {
|
||||
QVERIFY2(childEvaluator.toMap().contains("stateDescriptor"), "StateDescriptor missing in StateEvaluator");
|
||||
QVariantMap stateDescriptor = childEvaluator.toMap().value("stateDescriptor").toMap();
|
||||
QVERIFY2(stateDescriptor.value("deviceId") == m_mockDeviceId, "DeviceId of stateDescriptor does not match");
|
||||
QVERIFY2(stateDescriptor.value("stateTypeId") == mockIntStateId || stateDescriptor.value("stateTypeId") == mockBoolStateId, "StateTypeId of stateDescriptor doesn't match");
|
||||
if (stateDescriptor.contains("deviceId") && stateDescriptor.contains("stateTypeId")) {
|
||||
QVERIFY2(stateDescriptor.value("deviceId") == m_mockDeviceId, "DeviceId of stateDescriptor does not match");
|
||||
QVERIFY2(stateDescriptor.value("stateTypeId") == mockIntStateId || stateDescriptor.value("stateTypeId") == mockBoolStateId, "StateTypeId of stateDescriptor doesn't match");
|
||||
} else if (stateDescriptor.contains("interface") && stateDescriptor.contains("interfaceState")) {
|
||||
QVERIFY2(stateDescriptor.value("interface") == "battery", "Interface of stateDescriptor does not match");
|
||||
QVERIFY2(stateDescriptor.value("interfaceState") == "batteryCritical", "InterfaceState of stateDescriptor doesn't match");
|
||||
} else {
|
||||
QVERIFY2(false, "StateDescriptor must have either deviceId/stateTypeId or interface/interfaceState.");
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList replyActions = rule1.value("actions").toList();
|
||||
@ -1265,13 +1284,20 @@ void TestRules::loadStoreConfig()
|
||||
QVariantMap replyStateEvaluator2= rule2.value("stateEvaluator").toMap();
|
||||
QVariantList replyChildEvaluators2 = replyStateEvaluator.value("childEvaluators").toList();
|
||||
QVERIFY2(replyStateEvaluator2.value("operator") == "StateOperatorAnd", "There should be the AND operator.");
|
||||
QVERIFY2(replyChildEvaluators2.count() == 2, "There should be exactly 2 childEvaluators");
|
||||
QCOMPARE(replyChildEvaluators2.count(), 3);
|
||||
|
||||
foreach (const QVariant &childEvaluator, replyChildEvaluators2) {
|
||||
QVERIFY2(childEvaluator.toMap().contains("stateDescriptor"), "StateDescriptor missing in StateEvaluator");
|
||||
QVariantMap stateDescriptor = childEvaluator.toMap().value("stateDescriptor").toMap();
|
||||
QVERIFY2(stateDescriptor.value("deviceId") == m_mockDeviceId, "DeviceId of stateDescriptor does not match");
|
||||
QVERIFY2(stateDescriptor.value("stateTypeId") == mockIntStateId || stateDescriptor.value("stateTypeId") == mockBoolStateId, "StateTypeId of stateDescriptor doesn't match");
|
||||
if (stateDescriptor.contains("deviceId") && stateDescriptor.contains("stateTypeId")) {
|
||||
QVERIFY2(stateDescriptor.value("deviceId") == m_mockDeviceId, "DeviceId of stateDescriptor does not match");
|
||||
QVERIFY2(stateDescriptor.value("stateTypeId") == mockIntStateId || stateDescriptor.value("stateTypeId") == mockBoolStateId, "StateTypeId of stateDescriptor doesn't match");
|
||||
} else if (stateDescriptor.contains("interface") && stateDescriptor.contains("interfaceState")) {
|
||||
QVERIFY2(stateDescriptor.value("interface") == "battery", "Interface of stateDescriptor does not match");
|
||||
QVERIFY2(stateDescriptor.value("interfaceState") == "batteryCritical", "InterfaceState of stateDescriptor doesn't match");
|
||||
} else {
|
||||
QVERIFY2(false, "StateDescriptor must have either deviceId/stateTypeId or interface/interfaceState.");
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList replyActions2 = rule2.value("actions").toList();
|
||||
@ -1684,11 +1710,12 @@ void TestRules::testStateEvaluator3()
|
||||
void TestRules::testChildEvaluator_data()
|
||||
{
|
||||
cleanup();
|
||||
enableNotifications();
|
||||
|
||||
DeviceId testDeviceId = addDisplayPinDevice();
|
||||
QVERIFY2(!testDeviceId.isNull(), "Could not add push button device for child evaluators");
|
||||
|
||||
enableNotifications();
|
||||
|
||||
// Create child evaluators
|
||||
// Action
|
||||
QVariantMap action;
|
||||
@ -2238,8 +2265,18 @@ void TestRules::testRuleActionPAramsFromEventParameter()
|
||||
verifyRuleError(response, error);
|
||||
}
|
||||
|
||||
void TestRules::testInterfaceBasedRule()
|
||||
void TestRules::testInterfaceBasedEventRule()
|
||||
{
|
||||
QNetworkAccessManager nam;
|
||||
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
||||
|
||||
// state battery critical state to false initially
|
||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateId.toString()).arg(false)));
|
||||
QNetworkReply *reply = nam.get(request);
|
||||
spy.wait();
|
||||
QCOMPARE(spy.count(), 1);
|
||||
reply->deleteLater();
|
||||
|
||||
QVariantMap powerAction;
|
||||
powerAction.insert("interface", "light");
|
||||
powerAction.insert("interfaceAction", "power");
|
||||
@ -2273,20 +2310,87 @@ void TestRules::testInterfaceBasedRule()
|
||||
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("interface").toString(), QString("light"));
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("interfaceAction").toString(), QString("power"));
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("paramName").toString(), QString("power"));
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("value").toString(), QString("true"));
|
||||
|
||||
|
||||
// Change the state
|
||||
spy.clear();
|
||||
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateId.toString()).arg(true)));
|
||||
reply = nam.get(request);
|
||||
spy.wait();
|
||||
QCOMPARE(spy.count(), 1);
|
||||
reply->deleteLater();
|
||||
|
||||
verifyRuleExecuted(mockActionIdPower);
|
||||
}
|
||||
|
||||
void TestRules::testInterfaceBasedStateRule()
|
||||
{
|
||||
QNetworkAccessManager nam;
|
||||
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
|
||||
|
||||
// state battery critical state to true
|
||||
qDebug() << "setting battery critical state to true";
|
||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateId.toString()).arg(true)));
|
||||
// state battery critical state to false initially
|
||||
QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateId.toString()).arg(false)));
|
||||
QNetworkReply *reply = nam.get(request);
|
||||
spy.wait();
|
||||
QCOMPARE(spy.count(), 1);
|
||||
reply->deleteLater();
|
||||
|
||||
QVariantMap powerAction;
|
||||
powerAction.insert("interface", "light");
|
||||
powerAction.insert("interfaceAction", "power");
|
||||
QVariantMap powerActionParam;
|
||||
powerActionParam.insert("paramName", "power");
|
||||
powerActionParam.insert("value", true);
|
||||
powerAction.insert("ruleActionParams", QVariantList() << powerActionParam);
|
||||
|
||||
QVariantMap lowBatteryState;
|
||||
lowBatteryState.insert("interface", "battery");
|
||||
lowBatteryState.insert("interfaceState", "batteryCritical");
|
||||
|
||||
QVariantMap stateDescriptor;
|
||||
stateDescriptor.insert("interface", "battery");
|
||||
stateDescriptor.insert("interfaceState", "batteryCritical");
|
||||
stateDescriptor.insert("value", true);
|
||||
stateDescriptor.insert("operator", "ValueOperatorEquals");
|
||||
|
||||
QVariantMap stateEvaluator;
|
||||
stateEvaluator.insert("stateDescriptor", stateDescriptor);
|
||||
|
||||
QVariantMap addRuleParams;
|
||||
addRuleParams.insert("name", "TestInterfaceBasedStateRule");
|
||||
addRuleParams.insert("enabled", true);
|
||||
addRuleParams.insert("stateEvaluator", stateEvaluator);
|
||||
addRuleParams.insert("actions", QVariantList() << powerAction);
|
||||
|
||||
QVariant response = injectAndWait("Rules.AddRule", addRuleParams);
|
||||
QCOMPARE(response.toMap().value("status").toString(), QString("success"));
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("ruleError").toString(), QString("RuleErrorNoError"));
|
||||
|
||||
QVariantMap getRuleParams;
|
||||
getRuleParams.insert("ruleId", response.toMap().value("params").toMap().value("ruleId"));
|
||||
response = injectAndWait("Rules.GetRuleDetails", getRuleParams);
|
||||
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("ruleError").toString(), QString("RuleErrorNoError"));
|
||||
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("stateEvaluator").toMap().value("stateDescriptor").toMap().value("interface").toString(), QString("battery"));
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("stateEvaluator").toMap().value("stateDescriptor").toMap().value("interfaceState").toString(), QString("batteryCritical"));
|
||||
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("interface").toString(), QString("light"));
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("interfaceAction").toString(), QString("power"));
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("paramName").toString(), QString("power"));
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("value").toString(), QString("true"));
|
||||
|
||||
|
||||
// Change the state
|
||||
spy.clear();
|
||||
request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateId.toString()).arg(true)));
|
||||
reply = nam.get(request);
|
||||
spy.wait();
|
||||
QCOMPARE(spy.count(), 1);
|
||||
reply->deleteLater();
|
||||
|
||||
verifyRuleExecuted(mockActionIdPower);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user