Implement first attempt for network database loading and saving
parent
80d4b1416e
commit
5085487bda
|
|
@ -354,11 +354,7 @@ void ZigbeeNetworkDeconz::handleZigbeeClusterLibraryIndication(const Zigbee::Aps
|
||||||
if (!node) {
|
if (!node) {
|
||||||
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication;
|
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication;
|
||||||
// FIXME: maybe create and init the node, since it is in the network, but not recognized
|
// FIXME: maybe create and init the node, since it is in the network, but not recognized
|
||||||
|
// FIXME: maybe remove this node since we might have removed it but it did not respond, or we not explicitly allowed it to join.
|
||||||
// Remove this node since we might have removed it but it did not respond, or we not explicitly allowed it to join.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,16 @@ ZigbeeCluster::ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNod
|
||||||
//qCDebug(dcZigbeeCluster()) << "Create cluster" << ZigbeeUtils::convertUint16ToHexString(clusterId) << direction;
|
//qCDebug(dcZigbeeCluster()) << "Create cluster" << ZigbeeUtils::convertUint16ToHexString(clusterId) << direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZigbeeNode *ZigbeeCluster::node() const
|
||||||
|
{
|
||||||
|
return m_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZigbeeNodeEndpoint *ZigbeeCluster::endpoint() const
|
||||||
|
{
|
||||||
|
return m_endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
ZigbeeCluster::Direction ZigbeeCluster::direction() const
|
ZigbeeCluster::Direction ZigbeeCluster::direction() const
|
||||||
{
|
{
|
||||||
return m_direction;
|
return m_direction;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ class ZigbeeCluster : public QObject
|
||||||
|
|
||||||
friend class ZigbeeNode;
|
friend class ZigbeeNode;
|
||||||
friend class ZigbeeNetwork;
|
friend class ZigbeeNetwork;
|
||||||
|
friend class ZigbeeNetworkDatabase;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Direction {
|
enum Direction {
|
||||||
|
|
@ -124,6 +125,9 @@ public:
|
||||||
|
|
||||||
explicit ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Zigbee::ClusterId clusterId, Direction direction, QObject *parent = nullptr);
|
explicit ZigbeeCluster(ZigbeeNetwork *network, ZigbeeNode *node, ZigbeeNodeEndpoint *endpoint, Zigbee::ClusterId clusterId, Direction direction, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
ZigbeeNode *node() const;
|
||||||
|
ZigbeeNodeEndpoint *endpoint() const;
|
||||||
|
|
||||||
Direction direction() const;
|
Direction direction() const;
|
||||||
|
|
||||||
Zigbee::ClusterId clusterId() const;
|
Zigbee::ClusterId clusterId() const;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@
|
||||||
#include "loggingcategory.h"
|
#include "loggingcategory.h"
|
||||||
#include "zdo/zigbeedeviceprofile.h"
|
#include "zdo/zigbeedeviceprofile.h"
|
||||||
|
|
||||||
#include <QSqlQuery>
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
ZigbeeNetwork::ZigbeeNetwork(QObject *parent) :
|
ZigbeeNetwork::ZigbeeNetwork(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
|
|
@ -231,7 +232,6 @@ bool ZigbeeNetwork::hasNode(const ZigbeeAddress &address) const
|
||||||
|
|
||||||
void ZigbeeNetwork::removeZigbeeNode(const ZigbeeAddress &address)
|
void ZigbeeNetwork::removeZigbeeNode(const ZigbeeAddress &address)
|
||||||
{
|
{
|
||||||
|
|
||||||
ZigbeeNode *node = getZigbeeNode(address);
|
ZigbeeNode *node = getZigbeeNode(address);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
qCWarning(dcZigbeeNetwork()) << "Failed remove zigbee node since there is no node with" << address;
|
qCWarning(dcZigbeeNetwork()) << "Failed remove zigbee node since there is no node with" << address;
|
||||||
|
|
@ -313,6 +313,12 @@ void ZigbeeNetwork::saveNetwork()
|
||||||
void ZigbeeNetwork::loadNetwork()
|
void ZigbeeNetwork::loadNetwork()
|
||||||
{
|
{
|
||||||
qCDebug(dcZigbeeNetwork()) << "Load current network configuration from" << m_settingsFileName;
|
qCDebug(dcZigbeeNetwork()) << "Load current network configuration from" << m_settingsFileName;
|
||||||
|
|
||||||
|
if (!m_database) {
|
||||||
|
QDir storagePath = QFileInfo(m_settingsFileName).absoluteDir();
|
||||||
|
m_database = new ZigbeeNetworkDatabase(this, storagePath.absolutePath() + QDir::separator() + "zigbee-network.db", this);
|
||||||
|
}
|
||||||
|
|
||||||
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
|
QSettings settings(m_settingsFileName, QSettings::IniFormat, this);
|
||||||
|
|
||||||
settings.beginGroup("Network");
|
settings.beginGroup("Network");
|
||||||
|
|
@ -381,6 +387,35 @@ void ZigbeeNetwork::loadNetwork()
|
||||||
addNodeInternally(node);
|
addNodeInternally(node);
|
||||||
}
|
}
|
||||||
settings.endGroup(); // Nodes
|
settings.endGroup(); // Nodes
|
||||||
|
|
||||||
|
qCWarning(dcZigbeeNetwork()) << this;
|
||||||
|
|
||||||
|
// FIXME: for testing
|
||||||
|
foreach(ZigbeeNode *node, m_nodes) {
|
||||||
|
m_database->saveNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now load and print the nodes again for checking
|
||||||
|
QList<ZigbeeNode *> dbNodes = m_database->loadNodes();
|
||||||
|
|
||||||
|
foreach (ZigbeeNode *node, dbNodes) {
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " ---> " << node << endl;
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " " << node->nodeDescriptor();
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " " << node->powerDescriptor();
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " Endpoints: " << node->endpoints().count() << endl;
|
||||||
|
foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) {
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " - " << endpoint << endl;
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " Input clusters:" << endl;
|
||||||
|
foreach (ZigbeeCluster *cluster, endpoint->inputClusters()) {
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " - " << cluster << endl;
|
||||||
|
}
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " Output clusters:" << endl;
|
||||||
|
foreach (ZigbeeCluster *cluster, endpoint->outputClusters()) {
|
||||||
|
qCWarning(dcZigbeeNetwork()) << " - " << cluster << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZigbeeNetwork::clearSettings()
|
void ZigbeeNetwork::clearSettings()
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,9 @@
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
|
|
||||||
#include "zigbeenode.h"
|
#include "zigbeenode.h"
|
||||||
#include "zigbeenodeendpoint.h"
|
|
||||||
#include "zigbeechannelmask.h"
|
#include "zigbeechannelmask.h"
|
||||||
|
#include "zigbeenodeendpoint.h"
|
||||||
|
#include "zigbeenetworkdatabase.h"
|
||||||
#include "zigbeebridgecontroller.h"
|
#include "zigbeebridgecontroller.h"
|
||||||
#include "zigbeesecurityconfiguration.h"
|
#include "zigbeesecurityconfiguration.h"
|
||||||
|
|
||||||
|
|
@ -132,6 +133,7 @@ private:
|
||||||
ZigbeeDeviceProfile::NodeType m_nodeType = ZigbeeDeviceProfile::NodeTypeCoordinator;
|
ZigbeeDeviceProfile::NodeType m_nodeType = ZigbeeDeviceProfile::NodeTypeCoordinator;
|
||||||
|
|
||||||
// Network storage
|
// Network storage
|
||||||
|
ZigbeeNetworkDatabase *m_database = nullptr;
|
||||||
QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf";
|
QString m_settingsFileName = "/etc/nymea/nymea-zigbee.conf";
|
||||||
QList<ZigbeeNode *> m_nodes;
|
QList<ZigbeeNode *> m_nodes;
|
||||||
QList<ZigbeeNode *> m_uninitializedNodes;
|
QList<ZigbeeNode *> m_uninitializedNodes;
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,16 @@
|
||||||
|
|
||||||
#include "zigbeenetworkdatabase.h"
|
#include "zigbeenetworkdatabase.h"
|
||||||
#include "loggingcategory.h"
|
#include "loggingcategory.h"
|
||||||
|
#include "zigbeenetwork.h"
|
||||||
|
#include "zigbeeutils.h"
|
||||||
|
#include "zigbeenode.h"
|
||||||
|
|
||||||
ZigbeeNetworkDatabase::ZigbeeNetworkDatabase(const QString &databaseName, QObject *parent) : QObject(parent)
|
#include <QSqlError>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
|
||||||
|
ZigbeeNetworkDatabase::ZigbeeNetworkDatabase(ZigbeeNetwork *network, const QString &databaseName, QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_network(network)
|
||||||
{
|
{
|
||||||
m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), "zigbee");
|
m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), "zigbee");
|
||||||
m_db.setDatabaseName(databaseName);
|
m_db.setDatabaseName(databaseName);
|
||||||
|
|
@ -36,7 +44,295 @@ ZigbeeNetworkDatabase::ZigbeeNetworkDatabase(const QString &databaseName, QObjec
|
||||||
|
|
||||||
if (!m_db.isValid()) {
|
if (!m_db.isValid()) {
|
||||||
qCWarning(dcZigbeeNetworkDatabase()) << "The zigbee network database is not valid" << m_db.databaseName();
|
qCWarning(dcZigbeeNetworkDatabase()) << "The zigbee network database is not valid" << m_db.databaseName();
|
||||||
// FIXME: create a new one
|
// FIXME: rotate database
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initDatabase()) {
|
||||||
|
qCWarning(dcZigbeeNetworkDatabase()) << "Failed to initialize the database" << m_db.databaseName();
|
||||||
|
// FIXME: rotate database
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<ZigbeeNode *> ZigbeeNetworkDatabase::loadNodes()
|
||||||
|
{
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Loading nodes from database" << m_db.databaseName();
|
||||||
|
QList<ZigbeeNode *> nodes;
|
||||||
|
QString query("SELECT * FROM nodes;");
|
||||||
|
QSqlQuery nodesQuery = m_db.exec(query);
|
||||||
|
while (nodesQuery.next()) {
|
||||||
|
quint64 ieeeAddress = nodesQuery.value("ieeeAddress").toULongLong();
|
||||||
|
quint16 shortAddress = nodesQuery.value("shortAddress").toUInt();
|
||||||
|
QByteArray nodeDescriptor = QByteArray::fromBase64(nodesQuery.value("nodeDescriptor").toByteArray());
|
||||||
|
quint16 powerDescriptor = nodesQuery.value("powerDescriptor").toUInt();
|
||||||
|
|
||||||
|
// Build the node object
|
||||||
|
ZigbeeNode *node = new ZigbeeNode(m_network, shortAddress, ZigbeeAddress(ieeeAddress), m_network);
|
||||||
|
node->m_nodeDescriptor = ZigbeeDeviceProfile::parseNodeDescriptor(nodeDescriptor);
|
||||||
|
node->m_powerDescriptor = ZigbeeDeviceProfile::parsePowerDescriptor(powerDescriptor);
|
||||||
|
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Loaded node" << node;
|
||||||
|
|
||||||
|
// Now load all endpoints for this node
|
||||||
|
query = QString("SELECT * FROM endpoints WHERE ieeeAddress = \"%1\";").arg(ieeeAddress);
|
||||||
|
QSqlQuery endpointsQuery = m_db.exec(query);
|
||||||
|
while (endpointsQuery.next()) {
|
||||||
|
quint8 endpointId = endpointsQuery.value("endpointId").toUInt();
|
||||||
|
ZigbeeNodeEndpoint *endpoint = new ZigbeeNodeEndpoint(m_network, node, endpointId, node);
|
||||||
|
endpoint->setProfile(static_cast<Zigbee::ZigbeeProfile>(endpointsQuery.value("profileId").toUInt()));
|
||||||
|
endpoint->setDeviceId(static_cast<Zigbee::ZigbeeProfile>(endpointsQuery.value("deviceId").toUInt()));
|
||||||
|
endpoint->setDeviceVersion(static_cast<Zigbee::ZigbeeProfile>(endpointsQuery.value("deviceVersion").toUInt()));
|
||||||
|
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Loaded endpoint" << endpoint;
|
||||||
|
|
||||||
|
// Load input clusters for this endpoint
|
||||||
|
query = QString("SELECT * FROM serverClusters WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\";").arg(ieeeAddress).arg(endpointId);
|
||||||
|
QSqlQuery inputClustersQuery = m_db.exec(query);
|
||||||
|
while (inputClustersQuery.next()) {
|
||||||
|
Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(inputClustersQuery.value("clusterId").toUInt());
|
||||||
|
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Server);
|
||||||
|
endpoint->addInputCluster(cluster);
|
||||||
|
|
||||||
|
// Load cluster attributes for the server cluster
|
||||||
|
query = QString("SELECT * FROM attributes WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\" AND clusterId = \"%3\";")
|
||||||
|
.arg(ieeeAddress)
|
||||||
|
.arg(endpointId)
|
||||||
|
.arg(cluster->clusterId());
|
||||||
|
QSqlQuery attributesQuery = m_db.exec(query);
|
||||||
|
while (attributesQuery.next()) {
|
||||||
|
quint16 attributeId = attributesQuery.value("attributeId").toUInt();
|
||||||
|
Zigbee::DataType type = static_cast<Zigbee::DataType>(attributesQuery.value("dataType").toUInt());
|
||||||
|
QByteArray data = attributesQuery.value("data").toByteArray();
|
||||||
|
cluster->setAttribute(ZigbeeClusterAttribute(attributeId, ZigbeeDataType(type, data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load output clusters for this endpoint
|
||||||
|
query = QString("SELECT * FROM clientClusters WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\";").arg(ieeeAddress).arg(endpointId);
|
||||||
|
QSqlQuery outputClustersQuery = m_db.exec(query);
|
||||||
|
while (outputClustersQuery.next()) {
|
||||||
|
Zigbee::ClusterId clusterId = static_cast<Zigbee::ClusterId>(outputClustersQuery.value("clusterId").toUInt());
|
||||||
|
ZigbeeCluster *cluster = endpoint->createCluster(clusterId, ZigbeeCluster::Client);
|
||||||
|
endpoint->addOutputCluster(cluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
node->m_endpoints.append(endpoint);
|
||||||
|
}
|
||||||
|
nodes.append(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZigbeeNetworkDatabase::initDatabase()
|
||||||
|
{
|
||||||
|
// Reopen the db to make sure it can be written
|
||||||
|
m_db.close();
|
||||||
|
if (!m_db.open()) {
|
||||||
|
qCWarning(dcZigbeeNetworkDatabase()) << "Could not open zigbee database" << m_db.databaseName() << "Initialization failed.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable foreigen keys
|
||||||
|
m_db.exec("PRAGMA foreign_keys = ON;");
|
||||||
|
|
||||||
|
// Create nodes table
|
||||||
|
createTable("nodes", "(ieeeAddress INTEGER PRIMARY KEY, " // uint64
|
||||||
|
"shortAddress INTEGER NOT NULL, " // uint16
|
||||||
|
"nodeDescriptor BLOB NOT NULL, " // bytes as received from the node
|
||||||
|
"powerDescriptor INTEGER NOT NULL)"); // uint16
|
||||||
|
createIndices("ieeeAddressIndex", "nodes", "ieeeAddress");
|
||||||
|
|
||||||
|
|
||||||
|
// Create endpoints table
|
||||||
|
createTable("endpoints", "(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
|
||||||
|
"ieeeAddress INTEGER NOT NULL, " // uint64
|
||||||
|
"endpointId INTEGER NOT NULL, " // uint8
|
||||||
|
"profileId INTEGER NOT NULL, " // uint16
|
||||||
|
"deviceId INTEGER NOT NULL, " // uint16
|
||||||
|
"deviceVersion INTEGER, " // uint8
|
||||||
|
"CONSTRAINT fk_ieeeAddress FOREIGN KEY(ieeeAddress) REFERENCES nodes(ieeeAddress) ON DELETE CASCADE)");
|
||||||
|
createIndices("endpointIndex", "endpoints", "ieeeAddress, endpointId");
|
||||||
|
|
||||||
|
|
||||||
|
// Create server cluster table
|
||||||
|
createTable("serverClusters", "(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
|
||||||
|
"ieeeAddress INTEGER NOT NULL, " // uint64
|
||||||
|
"endpointId INTEGER NOT NULL, " // uint8
|
||||||
|
"clusterId INTEGER NOT NULL, " // uint16
|
||||||
|
"CONSTRAINT fk_endpoint FOREIGN KEY(ieeeAddress, endpointId) REFERENCES endpoints(ieeeAddress, endpointId) ON DELETE CASCADE)");
|
||||||
|
createIndices("serverClusterIndex", "serverClusters", "ieeeAddress, endpointId, clusterId");
|
||||||
|
|
||||||
|
|
||||||
|
// Create client cluster table
|
||||||
|
createTable("clientClusters", "(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
|
||||||
|
"ieeeAddress INTEGER NOT NULL, " // uint64
|
||||||
|
"endpointId INTEGER NOT NULL, " // uint8
|
||||||
|
"clusterId INTEGER NOT NULL, " // uint16
|
||||||
|
"CONSTRAINT fk_endpoint FOREIGN KEY(ieeeAddress, endpointId) REFERENCES endpoints(ieeeAddress, endpointId) ON DELETE CASCADE)");
|
||||||
|
createIndices("clientClusterIndex", "clientClusters", "ieeeAddress, endpointId, clusterId");
|
||||||
|
|
||||||
|
// Create cluster attributes table
|
||||||
|
createTable("attributes", "(id INTEGER PRIMARY KEY AUTOINCREMENT, " // for db relation
|
||||||
|
"ieeeAddress INTEGER NOT NULL, " // uint64
|
||||||
|
"endpointId INTEGER NOT NULL, " // uint8
|
||||||
|
"clusterId INTEGER NOT NULL, " // uint16
|
||||||
|
"attributeId INTEGER NOT NULL, " // uint16
|
||||||
|
"dataType INTEGER NOT NULL, " // uint8
|
||||||
|
"data BLOB NOT NULL, " // raw data from attribute
|
||||||
|
"CONSTRAINT fk_cluster FOREIGN KEY(ieeeAddress, endpointId, clusterId) REFERENCES serverClusters(ieeeAddress, endpointId, clusterId) ON DELETE CASCADE)");
|
||||||
|
createIndices("attributesIndex", "attributes", "ieeeAddress, endpointId, clusterId, attributeId");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeNetworkDatabase::createTable(const QString &tableName, const QString &schema)
|
||||||
|
{
|
||||||
|
m_db.exec(QString("CREATE TABLE IF NOT EXISTS %1 %2;").arg(tableName).arg(schema));
|
||||||
|
m_db.exec(QString("PRAGMA schema_version = %1;").arg(DB_VERSION));
|
||||||
|
m_db.exec(QString("PRAGMA user_version = %1;").arg(DB_VERSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeNetworkDatabase::createIndices(const QString &indexName, const QString &tableName, const QString &columns)
|
||||||
|
{
|
||||||
|
m_db.exec(QString("CREATE UNIQUE INDEX IF NOT EXISTS %1 ON %2(%3);").arg(indexName).arg(tableName).arg(columns));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZigbeeNetworkDatabase::saveNodeEndpoint(ZigbeeNodeEndpoint *endpoint)
|
||||||
|
{
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Store" << endpoint;
|
||||||
|
QString queryString = QString("INSERT OR REPLACE INTO endpoints (ieeeAddress, endpointId, profileId, deviceId, deviceVersion) "
|
||||||
|
"VALUES (\"%1\", \"%2\", \"%3\", \"%4\", \"%5\");")
|
||||||
|
.arg(endpoint->node()->extendedAddress().toUInt64())
|
||||||
|
.arg(endpoint->endpointId())
|
||||||
|
.arg(static_cast<quint16>(endpoint->profile()))
|
||||||
|
.arg(static_cast<quint16>(endpoint->deviceId()))
|
||||||
|
.arg(static_cast<quint8>(endpoint->deviceVersion()));
|
||||||
|
|
||||||
|
m_db.exec(queryString);
|
||||||
|
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save input/output clusters
|
||||||
|
foreach(ZigbeeCluster *cluster, endpoint->inputClusters()) {
|
||||||
|
if (!saveInputCluster(cluster)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(const ZigbeeClusterAttribute &attribute, cluster->attributes()) {
|
||||||
|
if (!saveAttribute(cluster, attribute)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(ZigbeeCluster *cluster, endpoint->outputClusters()) {
|
||||||
|
if (!saveOutputCluster(cluster)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZigbeeNetworkDatabase::saveInputCluster(ZigbeeCluster *cluster)
|
||||||
|
{
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Store" << cluster;
|
||||||
|
QString queryString = QString("INSERT OR REPLACE INTO serverClusters (ieeeAddress, endpointId, clusterId) "
|
||||||
|
"VALUES (\"%1\", \"%2\", \"%3\");")
|
||||||
|
.arg(cluster->node()->extendedAddress().toUInt64())
|
||||||
|
.arg(cluster->endpoint()->endpointId())
|
||||||
|
.arg(static_cast<quint16>(cluster->clusterId()));
|
||||||
|
|
||||||
|
m_db.exec(queryString);
|
||||||
|
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save input cluster into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZigbeeNetworkDatabase::saveOutputCluster(ZigbeeCluster *cluster)
|
||||||
|
{
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Store" << cluster;
|
||||||
|
QString queryString = QString("INSERT OR REPLACE INTO clientClusters (ieeeAddress, endpointId, clusterId) "
|
||||||
|
"VALUES (\"%1\", \"%2\", \"%3\");")
|
||||||
|
.arg(cluster->node()->extendedAddress().toUInt64())
|
||||||
|
.arg(cluster->endpoint()->endpointId())
|
||||||
|
.arg(static_cast<quint16>(cluster->clusterId()));
|
||||||
|
|
||||||
|
m_db.exec(queryString);
|
||||||
|
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save output cluster into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZigbeeNetworkDatabase::saveAttribute(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute)
|
||||||
|
{
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Store" << cluster << attribute;
|
||||||
|
QString queryString = QString("INSERT OR REPLACE INTO attributes (ieeeAddress, endpointId, clusterId, attributeId, dataType, data) "
|
||||||
|
"VALUES (\"%1\", \"%2\", \"%3\", \"%4\", \"%5\", \"%6\");")
|
||||||
|
.arg(cluster->node()->extendedAddress().toUInt64())
|
||||||
|
.arg(cluster->endpoint()->endpointId())
|
||||||
|
.arg(static_cast<quint16>(cluster->clusterId()))
|
||||||
|
.arg(static_cast<quint16>(attribute.id()))
|
||||||
|
.arg(static_cast<quint8>(attribute.dataType().dataType()))
|
||||||
|
.arg((attribute.dataType().data().toBase64().data()));
|
||||||
|
|
||||||
|
m_db.exec(queryString);
|
||||||
|
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save cluster cluster attribute into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZigbeeNetworkDatabase::saveNode(ZigbeeNode *node)
|
||||||
|
{
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Store" << node;
|
||||||
|
QString queryString = QString("INSERT OR REPLACE INTO nodes (ieeeAddress, shortAddress, nodeDescriptor, powerDescriptor) "
|
||||||
|
"VALUES (\"%1\", \"%2\", \"%3\", \"%4\");")
|
||||||
|
.arg(node->extendedAddress().toUInt64())
|
||||||
|
.arg(node->shortAddress())
|
||||||
|
.arg(node->nodeDescriptor().descriptorRawData.toBase64().data()) // Note: convert to base64 for saving zeros as string
|
||||||
|
.arg(node->powerDescriptor().powerDescriptoFlag);
|
||||||
|
|
||||||
|
m_db.exec(queryString);
|
||||||
|
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save endpoints
|
||||||
|
foreach (ZigbeeNodeEndpoint *endpoint, node->endpoints()) {
|
||||||
|
if (!saveNodeEndpoint(endpoint)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZigbeeNetworkDatabase::removeNode(ZigbeeNode *node)
|
||||||
|
{
|
||||||
|
qCDebug(dcZigbeeNetworkDatabase()) << "Remove" << node;
|
||||||
|
// Note: cascade delete will clean up all other tables
|
||||||
|
QString queryString = QString("DELETE FROM nodes WHERE ieeeAddress = %1;").arg(node->extendedAddress().toUInt64());
|
||||||
|
m_db.exec(queryString);
|
||||||
|
if (m_db.lastError().type() != QSqlError::NoError) {
|
||||||
|
qCWarning(dcZigbeeNetworkDatabase()) << "Could not remove node from database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,18 +31,38 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
|
|
||||||
|
#define DB_VERSION 1
|
||||||
|
|
||||||
|
class ZigbeeNode;
|
||||||
|
class ZigbeeCluster;
|
||||||
|
class ZigbeeNetwork;
|
||||||
|
class ZigbeeNodeEndpoint;
|
||||||
|
class ZigbeeClusterAttribute;
|
||||||
|
|
||||||
class ZigbeeNetworkDatabase : public QObject
|
class ZigbeeNetworkDatabase : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ZigbeeNetworkDatabase(const QString &databaseName, QObject *parent = nullptr);
|
explicit ZigbeeNetworkDatabase(ZigbeeNetwork *network, const QString &databaseName, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QList<ZigbeeNode *> loadNodes();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ZigbeeNetwork *m_network = nullptr;
|
||||||
QSqlDatabase m_db;
|
QSqlDatabase m_db;
|
||||||
|
|
||||||
|
bool initDatabase();
|
||||||
|
void createTable(const QString &tableName, const QString &schema);
|
||||||
|
void createIndices(const QString &indexName, const QString &tableName, const QString &columns);
|
||||||
|
|
||||||
signals:
|
bool saveNodeEndpoint(ZigbeeNodeEndpoint *endpoint);
|
||||||
|
bool saveInputCluster(ZigbeeCluster *cluster);
|
||||||
|
bool saveOutputCluster(ZigbeeCluster *cluster);
|
||||||
|
bool saveAttribute(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool saveNode(ZigbeeNode *node);
|
||||||
|
bool removeNode(ZigbeeNode *node);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ class ZigbeeNode : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
friend class ZigbeeNetwork;
|
friend class ZigbeeNetwork;
|
||||||
|
friend class ZigbeeNetworkDatabase;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum State {
|
enum State {
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ class ZigbeeNodeEndpoint : public QObject
|
||||||
|
|
||||||
friend class ZigbeeNode;
|
friend class ZigbeeNode;
|
||||||
friend class ZigbeeNetwork;
|
friend class ZigbeeNetwork;
|
||||||
|
friend class ZigbeeNetworkDatabase;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
quint8 endpointId() const;
|
quint8 endpointId() const;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue