Allow states, events and actions in interfaces to be optional

pull/333/head
Michael Zanetti 2020-09-06 02:43:28 +02:00
parent 2ca4b2f32f
commit 49bbd64434
11 changed files with 235 additions and 45 deletions

View File

@ -634,29 +634,29 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
ActionTypes actionTypes(thingClass.actionTypes());
EventTypes eventTypes(thingClass.eventTypes());
foreach (const StateType &ifaceStateType, iface.stateTypes()) {
foreach (const InterfaceStateType &ifaceStateType, iface.stateTypes()) {
StateType stateType = stateTypes.findByName(ifaceStateType.name());
if (stateType.id().isNull()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\"");
hasError = true;
continue;
if (!ifaceStateType.optional()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\"");
hasError = true;
} else {
continue;
}
}
if (ifaceStateType.type() != stateType.type()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching type: \"" + QVariant::typeToName(stateType.type()) + "\" != \"" + QVariant::typeToName(ifaceStateType.type()) + "\"");
hasError = true;
continue;
}
if (ifaceStateType.minValue().isValid() && !ifaceStateType.minValue().isNull()) {
if (ifaceStateType.minValue().toString() == "any") {
if (stateType.minValue().isNull()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has no minimum value defined.");
hasError = true;
continue;
}
} else if (ifaceStateType.minValue() != stateType.minValue()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching minimum value: \"" + ifaceStateType.minValue().toString() + "\" != \"" + stateType.minValue().toString() + "\"");
hasError = true;
continue;
}
}
if (ifaceStateType.maxValue().isValid() && !ifaceStateType.maxValue().isNull()) {
@ -664,32 +664,32 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
if (stateType.maxValue().isNull()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has no maximum value defined.");
hasError = true;
continue;
}
} else if (ifaceStateType.maxValue() != stateType.maxValue()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching maximum value: \"" + ifaceStateType.maxValue().toString() + "\" != \"" + stateType.minValue().toString() + "\"");
hasError = true;
continue;
}
}
if (!ifaceStateType.possibleValues().isEmpty() && ifaceStateType.possibleValues() != stateType.possibleValues()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching allowed values.");
hasError = true;
continue;
}
if (ifaceStateType.unit() != Types::UnitNone && ifaceStateType.unit() != stateType.unit()) {
QMetaEnum unitEnum = QMetaEnum::fromType<Types::Unit>();
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching unit: \"" + unitEnum.valueToKey(ifaceStateType.unit()) + "\" != \"" + unitEnum.valueToKey(stateType.unit()));
hasError = true;
continue;
}
}
foreach (const ActionType &ifaceActionType, iface.actionTypes()) {
foreach (const InterfaceActionType &ifaceActionType, iface.actionTypes()) {
ActionType actionType = actionTypes.findByName(ifaceActionType.name());
if (actionType.id().isNull()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\"");
hasError = true;
if (!ifaceActionType.optional()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\"");
hasError = true;
} else {
continue;
}
}
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name());
@ -711,11 +711,15 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
}
}
foreach (const EventType &ifaceEventType, iface.eventTypes()) {
foreach (const InterfaceEventType &ifaceEventType, iface.eventTypes()) {
EventType eventType = eventTypes.findByName(ifaceEventType.name());
if (!eventType.isValid()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\"");
hasError = true;
if (!ifaceEventType.optional()) {
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\"");
hasError = true;
} else {
continue;
}
}
foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) {
ParamType paramType = eventType.paramTypes().findByName(ifaceEventParamType.name());

View File

@ -189,16 +189,17 @@ Interface ThingUtils::loadInterface(const QString &name)
}
}
StateTypes stateTypes;
ActionTypes actionTypes;
EventTypes eventTypes;
InterfaceStateTypes stateTypes;
InterfaceActionTypes actionTypes;
InterfaceEventTypes eventTypes;
foreach (const QVariant &stateVariant, content.value("states").toList()) {
StateType stateType;
InterfaceStateType stateType;
stateType.setName(stateVariant.toMap().value("name").toString());
stateType.setType(QVariant::nameToType(stateVariant.toMap().value("type").toByteArray()));
stateType.setPossibleValues(stateVariant.toMap().value("allowedValues").toList());
stateType.setMinValue(stateVariant.toMap().value("minValue"));
stateType.setMaxValue(stateVariant.toMap().value("maxValue"));
stateType.setOptional(stateVariant.toMap().value("optional", false).toBool());
if (stateVariant.toMap().contains("unit")) {
QMetaEnum unitEnum = QMetaEnum::fromType<Types::Unit>();
int enumValue = unitEnum.keyToValue("Unit" + stateVariant.toMap().value("unit").toByteArray());
@ -210,8 +211,9 @@ Interface ThingUtils::loadInterface(const QString &name)
}
stateTypes.append(stateType);
EventType stateChangeEventType;
InterfaceEventType stateChangeEventType;
stateChangeEventType.setName(stateType.name());
stateChangeEventType.setOptional(stateType.optional());
ParamType stateChangeEventParamType;
stateChangeEventParamType.setName(stateType.name());
stateChangeEventParamType.setType(stateType.type());
@ -222,16 +224,18 @@ Interface ThingUtils::loadInterface(const QString &name)
eventTypes.append(stateChangeEventType);
if (stateVariant.toMap().value("writable", false).toBool()) {
ActionType stateChangeActionType;
InterfaceActionType stateChangeActionType;
stateChangeActionType.setName(stateType.name());
stateChangeActionType.setOptional(stateType.optional());
stateChangeActionType.setParamTypes(ParamTypes() << stateChangeEventParamType);
actionTypes.append(stateChangeActionType);
}
}
foreach (const QVariant &actionVariant, content.value("actions").toList()) {
ActionType actionType;
InterfaceActionType actionType;
actionType.setName(actionVariant.toMap().value("name").toString());
actionType.setOptional(actionVariant.toMap().value("optional").toBool());
ParamTypes paramTypes;
foreach (const QVariant &actionParamVariant, actionVariant.toMap().value("params").toList()) {
ParamType paramType;
@ -246,8 +250,9 @@ Interface ThingUtils::loadInterface(const QString &name)
}
foreach (const QVariant &eventVariant, content.value("events").toList()) {
EventType eventType;
InterfaceEventType eventType;
eventType.setName(eventVariant.toMap().value("name").toString());
eventType.setOptional(eventVariant.toMap().value("optional").toBool());
ParamTypes paramTypes;
foreach (const QVariant &eventParamVariant, eventVariant.toMap().value("params").toList()) {
ParamType paramType;
@ -267,20 +272,20 @@ Interface ThingUtils::loadInterface(const QString &name)
Interface ThingUtils::mergeInterfaces(const Interface &iface1, const Interface &iface2)
{
EventTypes eventTypes = iface1.eventTypes();
foreach (const EventType &et, iface2.eventTypes()) {
InterfaceEventTypes eventTypes = iface1.eventTypes();
foreach (const InterfaceEventType &et, iface2.eventTypes()) {
if (eventTypes.findByName(et.name()).name().isEmpty()) {
eventTypes.append(et);
}
}
StateTypes stateTypes = iface1.stateTypes();
foreach (const StateType &st, iface2.stateTypes()) {
InterfaceStateTypes stateTypes = iface1.stateTypes();
foreach (const InterfaceStateType &st, iface2.stateTypes()) {
if (stateTypes.findByName(st.name()).name().isEmpty()) {
stateTypes.append(st);
}
}
ActionTypes actionTypes = iface1.actionTypes();
foreach (const ActionType &at, iface2.actionTypes()) {
InterfaceActionTypes actionTypes = iface1.actionTypes();
foreach (const InterfaceActionType &at, iface2.actionTypes()) {
if (actionTypes.findByName(at.name()).name().isEmpty()) {
actionTypes.append(at);
}

View File

@ -35,6 +35,9 @@ HEADERS += \
types/browseritem.h \
types/browseritemaction.h \
types/browseraction.h \
types/interfaceactiontype.h \
types/interfaceeventtype.h \
types/interfacestatetype.h \
types/mediabrowseritem.h \
types/thingclass.h \
typeutils.h \
@ -149,6 +152,9 @@ SOURCES += \
types/browseritem.cpp \
types/browseritemaction.cpp \
types/browseraction.cpp \
types/interfaceactiontype.cpp \
types/interfaceeventtype.cpp \
types/interfacestatetype.cpp \
types/mediabrowseritem.cpp \
types/action.cpp \
types/actiontype.cpp \

View File

@ -30,7 +30,7 @@
#include "interface.h"
Interface::Interface(const QString &name, const ActionTypes &actionTypes, const EventTypes &eventTypes, const StateTypes &stateTypes):
Interface::Interface(const QString &name, const InterfaceActionTypes &actionTypes, const InterfaceEventTypes &eventTypes, const InterfaceStateTypes &stateTypes):
m_name(name),
m_actionTypes(actionTypes),
m_eventTypes(eventTypes),
@ -44,17 +44,17 @@ QString Interface::name() const
return m_name;
}
ActionTypes Interface::actionTypes() const
InterfaceActionTypes Interface::actionTypes() const
{
return m_actionTypes;
}
EventTypes Interface::eventTypes() const
InterfaceEventTypes Interface::eventTypes() const
{
return m_eventTypes;
}
StateTypes Interface::stateTypes() const
InterfaceStateTypes Interface::stateTypes() const
{
return m_stateTypes;
}

View File

@ -31,28 +31,29 @@
#ifndef INTERFACE_H
#define INTERFACE_H
#include "eventtype.h"
#include "actiontype.h"
#include "statetype.h"
#include "interfaceeventtype.h"
#include "interfaceactiontype.h"
#include "interfacestatetype.h"
class Interface{
public:
Interface() = default;
Interface(const QString &name, const ActionTypes &actionTypes, const EventTypes &eventTypes, const StateTypes &stateTypes);
Interface(const QString &name, const InterfaceActionTypes &actionTypes, const InterfaceEventTypes &eventTypes, const InterfaceStateTypes &stateTypes);
QString name() const;
ActionTypes actionTypes() const;
EventTypes eventTypes() const;
StateTypes stateTypes() const;
InterfaceActionTypes actionTypes() const;
InterfaceEventTypes eventTypes() const;
InterfaceStateTypes stateTypes() const;
bool isValid() const;
private:
QUuid m_id;
QString m_name;
ActionTypes m_actionTypes;
EventTypes m_eventTypes;
StateTypes m_stateTypes;
InterfaceActionTypes m_actionTypes;
InterfaceEventTypes m_eventTypes;
InterfaceStateTypes m_stateTypes;
bool m_optional = false;
};
class LIBNYMEA_EXPORT Interfaces: public QList<Interface>

View File

@ -0,0 +1,32 @@
#include "interfaceactiontype.h"
InterfaceActionType::InterfaceActionType()
{
}
bool InterfaceActionType::optional() const
{
return m_optional;
}
void InterfaceActionType::setOptional(bool optional)
{
m_optional = optional;
}
InterfaceActionTypes::InterfaceActionTypes(const QList<InterfaceActionType> &other):
QList<InterfaceActionType>(other)
{
}
InterfaceActionType InterfaceActionTypes::findByName(const QString &name)
{
foreach (const InterfaceActionType &iat, *this) {
if (iat.name() == name) {
return iat;
}
}
return InterfaceActionType();
}

View File

@ -0,0 +1,26 @@
#ifndef INTERFACEACTIONTYPE_H
#define INTERFACEACTIONTYPE_H
#include "actiontype.h"
class InterfaceActionType: public ActionType
{
public:
InterfaceActionType();
bool optional() const;
void setOptional(bool optional);
private:
bool m_optional = false;
};
class InterfaceActionTypes: public QList<InterfaceActionType>
{
public:
InterfaceActionTypes() = default;
InterfaceActionTypes(const QList<InterfaceActionType> &other);
InterfaceActionType findByName(const QString &name);
};
#endif // INTERFACEACTIONTYPE_H

View File

@ -0,0 +1,32 @@
#include "interfaceeventtype.h"
InterfaceEventType::InterfaceEventType()
{
}
bool InterfaceEventType::optional() const
{
return m_optional;
}
void InterfaceEventType::setOptional(bool optional)
{
m_optional = optional;
}
InterfaceEventTypes::InterfaceEventTypes(const QList<InterfaceEventType> &other):
QList<InterfaceEventType>(other)
{
}
InterfaceEventType InterfaceEventTypes::findByName(const QString &name)
{
foreach (const InterfaceEventType &iet, *this) {
if (iet.name() == name) {
return iet;
}
}
return InterfaceEventType();
}

View File

@ -0,0 +1,26 @@
#ifndef INTERFACEEVENTTYPE_H
#define INTERFACEEVENTTYPE_H
#include "eventtype.h"
class InterfaceEventType: public EventType
{
public:
InterfaceEventType();
bool optional() const;
void setOptional(bool optional);
private:
bool m_optional = false;
};
class InterfaceEventTypes: public QList<InterfaceEventType>
{
public:
InterfaceEventTypes() = default;
InterfaceEventTypes(const QList<InterfaceEventType> &other);
InterfaceEventType findByName(const QString &name);
};
#endif // INTERFACEEVENTTYPE_H

View File

@ -0,0 +1,32 @@
#include "interfacestatetype.h"
InterfaceStateType::InterfaceStateType()
{
}
bool InterfaceStateType::optional() const
{
return m_optional;
}
void InterfaceStateType::setOptional(bool optional)
{
m_optional = optional;
}
InterfaceStateTypes::InterfaceStateTypes(const QList<InterfaceStateType> &other):
QList<InterfaceStateType>(other)
{
}
InterfaceStateType InterfaceStateTypes::findByName(const QString &name)
{
foreach (const InterfaceStateType &ist, *this) {
if (ist.name() == name) {
return ist;
}
}
return InterfaceStateType();
}

View File

@ -0,0 +1,26 @@
#ifndef INTERFACESTATETYPE_H
#define INTERFACESTATETYPE_H
#include "statetype.h"
class InterfaceStateType: public StateType
{
public:
InterfaceStateType();
bool optional() const;
void setOptional(bool optional);
private:
bool m_optional = false;
};
class InterfaceStateTypes: public QList<InterfaceStateType>
{
public:
InterfaceStateTypes() = default;
InterfaceStateTypes(const QList<InterfaceStateType> &other);
InterfaceStateType findByName(const QString &name);
};
#endif // INTERFACESTATETYPE_H