Some improvements in the zigbee topology map

pull/872/head
Michael Zanetti 2022-09-05 17:36:27 +02:00
parent 160cd0c4de
commit 76f52acb33
10 changed files with 139 additions and 57 deletions

View File

@ -151,6 +151,11 @@ int ZigbeeManager::removeNode(const QUuid &networkUuid, const QString &ieeeAddre
return m_engine->jsonRpcClient()->sendCommand("Zigbee.RemoveNode", params, this, "removeNodeResponse");
}
void ZigbeeManager::refreshNeighborTables(const QUuid &networkUuid)
{
m_engine->jsonRpcClient()->sendCommand("Zigbee.RefreshNeighborTables", {{"networkUuid", networkUuid}});
}
void ZigbeeManager::init()
{
m_fetchingData = true;
@ -378,7 +383,8 @@ void ZigbeeManager::fillNetworkData(ZigbeeNetwork *network, const QVariantMap &n
network->setPermitJoiningDuration(networkMap.value("permitJoiningDuration").toUInt());
network->setPermitJoiningRemaining(networkMap.value("permitJoiningRemaining").toUInt());
network->setBackend(networkMap.value("backend").toString());
network->setNetworkState(ZigbeeNetwork::stringToZigbeeNetworkState(networkMap.value("networkState").toString()));
QMetaEnum networkStateEnum = QMetaEnum::fromType<ZigbeeNetwork::ZigbeeNetworkState>();
network->setNetworkState(static_cast<ZigbeeNetwork::ZigbeeNetworkState>(networkStateEnum.keyToValue(networkMap.value("networkState").toByteArray())));
}
void ZigbeeManager::addOrUpdateNode(ZigbeeNetwork *network, const QVariantMap &nodeMap)
@ -413,7 +419,8 @@ void ZigbeeManager::updateNodeProperties(ZigbeeNode *node, const QVariantMap &no
ZigbeeNode::ZigbeeNodeRelationship relationship = static_cast<ZigbeeNode::ZigbeeNodeRelationship>(relationshipEnum.keyToValue(neighborMap.value("relationship").toByteArray().data()));
quint8 lqi = neighborMap.value("lqi").toUInt();
quint8 depth = neighborMap.value("depth").toUInt();
node->addOrUpdateNeighbor(networkAddress, relationship, lqi, depth);
bool permitJoining = neighborMap.value("permitJoining").toBool();
node->addOrUpdateNeighbor(networkAddress, relationship, lqi, depth, permitJoining);
neighbors.append(networkAddress);
}
node->commitNeighbors(neighbors);

View File

@ -95,6 +95,7 @@ public:
Q_INVOKABLE void factoryResetNetwork(const QUuid &networkUuid);
Q_INVOKABLE void getNodes(const QUuid &networkUuid);
Q_INVOKABLE int removeNode(const QUuid &networkUuid, const QString &ieeeAddress);
Q_INVOKABLE void refreshNeighborTables(const QUuid &networkUuid);
signals:
void engineChanged();

View File

@ -253,22 +253,3 @@ void ZigbeeNetwork::setNetworkState(ZigbeeNetwork::ZigbeeNetworkState networkSta
m_networkState = networkState;
emit networkStateChanged();
}
ZigbeeNetwork::ZigbeeNetworkState ZigbeeNetwork::stringToZigbeeNetworkState(const QString &networkStateString)
{
if (networkStateString == "ZigbeeNetworkStateOffline") {
return ZigbeeNetworkStateOffline;
} else if (networkStateString == "ZigbeeNetworkStateStarting") {
return ZigbeeNetworkStateStarting;
} else if (networkStateString == "ZigbeeNetworkStateUpdating") {
return ZigbeeNetworkStateUpdating;
} else if (networkStateString == "ZigbeeNetworkStateOnline") {
return ZigbeeNetworkStateOnline;
} else if (networkStateString == "ZigbeeNetworkStateError") {
return ZigbeeNetworkStateError;
} else {
return ZigbeeNetworkStateError;
}
}

View File

@ -115,8 +115,6 @@ public:
ZigbeeNetworkState networkState() const;
void setNetworkState(ZigbeeNetworkState networkState);
static ZigbeeNetworkState stringToZigbeeNetworkState(const QString &networkStateString);
signals:
void networkUuidChanged();
void enabledChanged();

View File

@ -194,7 +194,7 @@ QList<ZigbeeNodeNeighbor *> ZigbeeNode::neighbors() const
return m_neighbors;
}
void ZigbeeNode::addOrUpdateNeighbor(quint16 networkAddress, ZigbeeNodeRelationship relationship, quint8 lqi, quint8 depth)
void ZigbeeNode::addOrUpdateNeighbor(quint16 networkAddress, ZigbeeNodeRelationship relationship, quint8 lqi, quint8 depth, bool permitJoining)
{
foreach (ZigbeeNodeNeighbor *neighbor, m_neighbors) {
if (neighbor->networkAddress() == networkAddress) {
@ -206,6 +206,10 @@ void ZigbeeNode::addOrUpdateNeighbor(quint16 networkAddress, ZigbeeNodeRelations
neighbor->setLqi(lqi);
m_neighborsDirty = true;
}
if (neighbor->permitJoining() != permitJoining) {
neighbor->setPermitJoining(permitJoining);
m_neighborsDirty = true;
}
return;
}
}
@ -214,6 +218,7 @@ void ZigbeeNode::addOrUpdateNeighbor(quint16 networkAddress, ZigbeeNodeRelations
neighbor->setLqi(lqi);
neighbor->setDepth(depth);
m_neighbors.append(neighbor);
m_neighborsDirty = true;
}
void ZigbeeNode::commitNeighbors(QList<quint16> toBeKept)
@ -308,3 +313,16 @@ void ZigbeeNodeNeighbor::setDepth(quint8 depth)
emit depthChanged();
}
}
bool ZigbeeNodeNeighbor::permitJoining() const
{
return m_permitJoining;
}
void ZigbeeNodeNeighbor::setPermitJoining(bool permitJoining)
{
if (m_permitJoining != permitJoining) {
m_permitJoining = permitJoining;
emit permitJoiningChanged();
}
}

View File

@ -116,7 +116,7 @@ public:
void setLastSeen(const QDateTime &lastSeen);
QList<ZigbeeNodeNeighbor*> neighbors() const;
void addOrUpdateNeighbor(quint16 networkAddress, ZigbeeNodeRelationship relationship, quint8 lqi, quint8 depth);
void addOrUpdateNeighbor(quint16 networkAddress, ZigbeeNodeRelationship relationship, quint8 lqi, quint8 depth, bool permitJoining);
void commitNeighbors(QList<quint16> toBeKept);
static ZigbeeNodeState stringToNodeState(const QString &nodeState);
@ -159,6 +159,7 @@ class ZigbeeNodeNeighbor: public QObject
Q_PROPERTY(ZigbeeNode::ZigbeeNodeRelationship relationship READ relationship NOTIFY relationshipChanged)
Q_PROPERTY(quint8 lqi READ lqi NOTIFY lqiChanged)
Q_PROPERTY(quint8 depth READ depth NOTIFY depthChanged)
Q_PROPERTY(bool permitJoining READ permitJoining NOTIFY permitJoiningChanged)
public:
ZigbeeNodeNeighbor(quint16 networkAddress, QObject *parent);
@ -174,16 +175,21 @@ public:
quint8 depth() const;
void setDepth(quint8 depth);
bool permitJoining() const;
void setPermitJoining(bool permitJoining);
signals:
void relationshipChanged();
void lqiChanged();
void depthChanged();
void permitJoiningChanged();
private:
quint16 m_networkAddress;
ZigbeeNode::ZigbeeNodeRelationship m_relationship;
quint8 m_lqi = 0;
quint8 m_depth = 0;
bool m_permitJoining = false;
};
#endif // ZIGBEENODE_H

View File

@ -149,6 +149,8 @@ void ZigbeeNodes::addNode(ZigbeeNode *node)
endInsertRows();
emit countChanged();
emit nodeAdded(node);
}
void ZigbeeNodes::removeNode(const QString &ieeeAddress)
@ -159,6 +161,7 @@ void ZigbeeNodes::removeNode(const QString &ieeeAddress)
m_nodes.takeAt(i)->deleteLater();
endRemoveRows();
emit countChanged();
emit nodeRemoved(ieeeAddress);
return;
}
}

View File

@ -76,6 +76,8 @@ public:
signals:
void countChanged();
void nodeAdded(ZigbeeNode *node);
void nodeRemoved(const QString &ieeeAddress);
protected:
QList<ZigbeeNode *> m_nodes;

View File

@ -92,7 +92,9 @@ SettingsPageBase {
NymeaItemDelegate {
Layout.fillWidth: true
text: qsTr("Network topology")
onClicked: pageStack.push(Qt.resolvedUrl("ZigbeeTopologyPage.qml"), {network: root.network})
onClicked: {
pageStack.push(Qt.resolvedUrl("ZigbeeTopologyPage.qml"), {zigbeeManager: root.zigbeeManager, network: root.network})
}
}
SettingsPageSectionHeader {

View File

@ -13,6 +13,7 @@ Page {
onBackPressed: pageStack.pop()
}
property ZigbeeManager zigbeeManager: null
property ZigbeeNetwork network: null
readonly property int nodeDistance: 150
@ -21,13 +22,24 @@ Page {
Component.onCompleted: {
generateNodeList()
canvas.requestPaint()
flickable.contentX = (flickable.contentWidth - flickable.width) / 2
flickable.contentY = (flickable.contentHeight - flickable.height) / 2
zigbeeManager.refreshNeighborTables(network.networkUuid)
reload();
for (var i = 0; i < network.nodes.count; i++) {
network.nodes.get(i).neighborsChanged.connect(function() {root.reload()})
}
}
Connections {
target: root.network.nodes
onNodeAdded: {
root.reload()
node.neighborsChanged.connect(function() {root.reload()});
}
}
function generateNodeList() {
d.nodeItems = []
var coordinator = {}
var routers = []
var endDevices = []
@ -88,6 +100,30 @@ Page {
}
}
}
var unconnectedNodes = []
for (var i = 0; i < network.nodes.count; i++) {
var node = network.nodes.get(i)
if (node.type == ZigbeeNode.ZigbeeNodeTypeEndDevice && handledEndDevices.indexOf(node.networkAddress) < 0) {
print("Adding unconnected node:","0x" + node.networkAddress.toString(16))
unconnectedNodes.push(node)
}
}
var cellWidth = root.nodeSize * 2
var cellHeight = root.nodeSize * 2
var maxColumns = (root.width - Style.bigMargins * 2) / cellWidth
var columns = Math.min(unconnectedNodes.length, maxColumns)
var rowWidth = columns * cellWidth
print("columns:", columns, "maxCols", maxColumns)
for (var i = 0; i < unconnectedNodes.length; i++) {
var node = unconnectedNodes[i]
var column = i % columns;
var row = Math.floor(i / columns)
var x = cellWidth * column + cellWidth / 2 - rowWidth / 2
var y = Style.margins + cellHeight * row + root.nodeSize - root.height / 3
var point = root.mapToItem(canvas, x, y)
d.nodeItems.push(createNodeItem(node, point.x, point.y, 0))
}
}
@ -126,19 +162,35 @@ Page {
thing: thing
}
print("creared node", thing ? thing.name : "", " at", x, y)
// print("creared node", thing ? thing.name : "", " at", x, y)
d.adjustSize(x, y)
return nodeItem
}
function reload() {
print("Reloading network map")
while (d.nodeItems.length > 0) {
var nodeItem = d.nodeItems.shift()
nodeItem.image.destroy();
}
// d.selectedNodeItem= null
d.minX = 0
d.minY = 0
d.maxX = 0
d.maxY = 0
d.size = 0
generateNodeList();
canvas.requestPaint()
flickable.contentX = (flickable.contentWidth - flickable.width) / 2
flickable.contentY = (flickable.contentHeight - flickable.height) / 2
}
QtObject {
id: d
property var nodeTree: ({})
property var handledNodes: []
property var nodeItems: []
property var selectedNodeItem: null
property int selectedNodeAddress: -1
readonly property ZigbeeNode selectedNode: selectedNodeAddress >= 0 ? network.nodes.getNodeByNetworkAddress(selectedNodeAddress) : null
property int minX: 0
property int minY: 0
@ -180,7 +232,7 @@ Page {
clip: true
onPaint: {
print("**** height:", canvas.height, "width", canvas.width)
// print("**** height:", canvas.height, "width", canvas.width)
var ctx = getContext("2d");
ctx.reset();
@ -209,7 +261,7 @@ Page {
for (var k = 0; k < d.nodeItems.length; k++) {
if (d.nodeItems[k].node.networkAddress == neighbor.networkAddress) {
var toNodeItem = d.nodeItems[k]
if (nodeItem === d.selectedNodeItem || toNodeItem === d.selectedNodeItem) {
if (nodeItem.node.networkAddress === d.selectedNodeAddress || toNodeItem.node.networkAddress === d.selectedNodeAddress) {
if (selected) {
paintEdge(ctx, nodeItem, d.nodeItems[k], neighbor.lqi, true)
}
@ -227,11 +279,11 @@ Page {
function paintNode(ctx, nodeItem) {
ctx.save()
ctx.beginPath();
ctx.fillStyle = Style.tileBackgroundColor
ctx.strokeStyle = nodeItem === d.selectedNodeItem ? Style.accentColor : Style.foregroundColor
ctx.fillStyle = nodeItem.node.networkAddress === d.selectedNodeAddress ? Style.tileOverlayColor : Style.tileBackgroundColor
ctx.strokeStyle = nodeItem.node.networkAddress === d.selectedNodeAddress ? Style.accentColor : Style.tileBackgroundColor
ctx.arc(root.scale * nodeItem.x, root.scale * nodeItem.y, root.scale * root.nodeSize / 2, 0, 2 * Math.PI);
ctx.fill();
// ctx.stroke();
// ctx.stroke();
ctx.fillStyle = Style.foregroundColor
ctx.font = "" + Style.extraSmallFont.pixelSize + "px Ubuntu";
var text = ""
@ -267,7 +319,7 @@ Page {
ctx.strokeStyle = Qt.rgba(resultRed, resultGreen, resultBlue, 1)
} else {
ctx.lineWidth = 1
var alpha = d.selectedNodeItem ? .2 : 1
var alpha = d.selectedNodeAddress >= 0 ? .2 : 1
ctx.strokeStyle = Qt.rgba(resultRed, resultGreen, resultBlue, alpha)
}
ctx.beginPath();
@ -287,13 +339,13 @@ Page {
print("clicked:", mouseX, mouseY)
var translatedMouseX = mouseX - canvas.width / 2
var translatedMouseY = mouseY - canvas.height / 2
d.selectedNodeItem = null
d.selectedNodeAddress = -1
for (var i = 0; i < d.nodeItems.length; i++) {
var nodeItem = d.nodeItems[i]
// print("nodeItem at:", root.scale * nodeItem.x, root.scale * nodeItem.y)
if (Math.abs(root.scale * nodeItem.x - translatedMouseX) < (root.scale * root.nodeSize / 2)
&& Math.abs(root.scale * nodeItem.y - translatedMouseY) < (root.scale * root.nodeSize / 2)) {
d.selectedNodeItem = nodeItem;
d.selectedNodeAddress = nodeItem.node.networkAddress;
print("sleecting", nodeItem.node.networkAddress)
}
}
@ -302,33 +354,37 @@ Page {
}
}
}
}
BigTile {
visible: d.selectedNodeItem
visible: d.selectedNodeAddress >= 0
anchors {
top: parent.top
right: parent.right
margins: Style.margins
}
width: 200
width: 260
header: RowLayout {
width: parent.width - Style.smallMargins
spacing: Style.smallMargins
Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: !d.selectedNodeItem
ThingsProxy {
id: selectedThingsProxy
engine: _engine
paramsFilter: {"ieeeAddress": d.selectedNode ? d.selectedNode.ieeeAddress : "---"}
}
text: d.selectedNodeAddress < 0
? ""
: d.selectedNodeItem.node.networkAddress === 0
: d.selectedNodeAddress === 0
? Configuration.systemName
: d.selectedNodeItem.thing
? d.selectedNodeItem.thing.name
: d.selectedNodeItem.node.model
: selectedThingsProxy.count > 0
? selectedThingsProxy.get(0).name
: network.nodes.getNodeByNetworkAddress(d.selectedNode).model
}
ColorIcon {
size: Style.smallIconSize
@ -354,18 +410,23 @@ Page {
size: Style.smallIconSize
name: "/ui/images/things.svg"
}
ColorIcon {
size: Style.smallIconSize
name: "/ui/images/add.svg"
}
}
contentItem: ListView {
id: tableListView
spacing: app.margins
implicitHeight: Math.min(root.height / 4, count * Style.smallIconSize)
clip: true
model: d.selectedNodeItem ? d.selectedNodeItem.node.neighbors.length : 0
model: d.selectedNode ? d.selectedNode.neighbors.length : 0
delegate: RowLayout {
id: neighborTableDelegate
width: parent.width
property ZigbeeNodeNeighbor neighbor: d.selectedNodeItem.node.neighbors[index]
width: tableListView.width
property ZigbeeNodeNeighbor neighbor: d.selectedNode.neighbors[index]
property ZigbeeNode neighborNode: root.network.nodes.getNodeByNetworkAddress(neighbor.networkAddress)
property Thing neighborNodeThing: {
for (var i = 0; i < engine.thingManager.things.count; i++) {
@ -401,9 +462,12 @@ Page {
text: neighborTableDelegate.neighbor.depth
horizontalAlignment: Text.AlignRight
}
ColorIcon {
size: Style.smallIconSize
name: "add"
opacity: neighborTableDelegate.neighbor.permitJoining ? 1 : 0
}
}
}
}
}