Some improvements in the zigbee topology map
parent
160cd0c4de
commit
76f52acb33
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -115,8 +115,6 @@ public:
|
|||
ZigbeeNetworkState networkState() const;
|
||||
void setNetworkState(ZigbeeNetworkState networkState);
|
||||
|
||||
static ZigbeeNetworkState stringToZigbeeNetworkState(const QString &networkStateString);
|
||||
|
||||
signals:
|
||||
void networkUuidChanged();
|
||||
void enabledChanged();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ public:
|
|||
|
||||
signals:
|
||||
void countChanged();
|
||||
void nodeAdded(ZigbeeNode *node);
|
||||
void nodeRemoved(const QString &ieeeAddress);
|
||||
|
||||
protected:
|
||||
QList<ZigbeeNode *> m_nodes;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue