Add ZigBee routing information to topology map
parent
a12ea9775c
commit
4a06e05115
|
|
@ -424,4 +424,18 @@ void ZigbeeManager::updateNodeProperties(ZigbeeNode *node, const QVariantMap &no
|
|||
neighbors.append(networkAddress);
|
||||
}
|
||||
node->commitNeighbors(neighbors);
|
||||
|
||||
QList<quint16> routes;
|
||||
foreach (const QVariant &route, nodeMap.value("routingTableRecords").toList()) {
|
||||
QVariantMap routeMap = route.toMap();
|
||||
quint16 destinationAddress = routeMap.value("destinationAddress").toUInt();
|
||||
quint16 nextHopAddress = routeMap.value("nextHopAddress").toUInt();
|
||||
QMetaEnum routeStatusEnum = QMetaEnum::fromType<ZigbeeNode::ZigbeeNodeRouteStatus>();
|
||||
ZigbeeNode::ZigbeeNodeRouteStatus routeStatus = static_cast<ZigbeeNode::ZigbeeNodeRouteStatus>(routeStatusEnum.keyToValue(routeMap.value("status").toByteArray().data()));
|
||||
bool memoryConstrained = routeMap.value("memoryConstrained").toBool();
|
||||
bool manyToOne = routeMap.value("manyToOne").toBool();
|
||||
node->addOrUpdateRoute(destinationAddress, nextHopAddress, routeStatus, memoryConstrained, manyToOne);
|
||||
routes.append(destinationAddress);
|
||||
}
|
||||
node->commitRoutes(routes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,12 +210,17 @@ void ZigbeeNode::addOrUpdateNeighbor(quint16 networkAddress, ZigbeeNodeRelations
|
|||
neighbor->setPermitJoining(permitJoining);
|
||||
m_neighborsDirty = true;
|
||||
}
|
||||
if (neighbor->depth() != depth) {
|
||||
neighbor->setDepth(depth);
|
||||
m_neighborsDirty = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
ZigbeeNodeNeighbor *neighbor = new ZigbeeNodeNeighbor(networkAddress, this);
|
||||
neighbor->setRelationship(relationship);
|
||||
neighbor->setLqi(lqi);
|
||||
neighbor->setPermitJoining(permitJoining);
|
||||
neighbor->setDepth(depth);
|
||||
m_neighbors.append(neighbor);
|
||||
m_neighborsDirty = true;
|
||||
|
|
@ -239,6 +244,61 @@ void ZigbeeNode::commitNeighbors(QList<quint16> toBeKept)
|
|||
}
|
||||
}
|
||||
|
||||
QList<ZigbeeNodeRoute *> ZigbeeNode::routes() const
|
||||
{
|
||||
return m_routes;
|
||||
}
|
||||
|
||||
void ZigbeeNode::addOrUpdateRoute(quint16 destinationAddress, quint16 nextHopAddress, ZigbeeNodeRouteStatus status, bool memoryConstrained, bool manyToOne)
|
||||
{
|
||||
foreach (ZigbeeNodeRoute *route, m_routes) {
|
||||
if (route->destinationAddress() == destinationAddress) {
|
||||
if (route->nextHopAddress() != nextHopAddress) {
|
||||
route->setNextHopAddress(nextHopAddress);
|
||||
m_routesDirty = true;
|
||||
}
|
||||
if (route->status() != status) {
|
||||
route->setStatus(status);
|
||||
m_routesDirty = true;
|
||||
}
|
||||
if (route->memoryConstrained() != memoryConstrained) {
|
||||
route->setMemoryConstrained(memoryConstrained);
|
||||
m_routesDirty = true;
|
||||
}
|
||||
if (route->manyToOne() != manyToOne) {
|
||||
route->setManyToOne(manyToOne);
|
||||
m_routesDirty = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
ZigbeeNodeRoute *route = new ZigbeeNodeRoute(destinationAddress, this);
|
||||
route->setNextHopAddress(nextHopAddress);
|
||||
route->setStatus(status);
|
||||
route->setMemoryConstrained(memoryConstrained);
|
||||
route->setManyToOne(manyToOne);
|
||||
m_routes.append(route);
|
||||
m_routesDirty = true;
|
||||
}
|
||||
|
||||
void ZigbeeNode::commitRoutes(QList<quint16> toBeKept)
|
||||
{
|
||||
QMutableListIterator<ZigbeeNodeRoute*> iter(m_routes);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
ZigbeeNodeRoute *route = iter.next();
|
||||
if (!toBeKept.contains(route->destinationAddress())) {
|
||||
iter.remove();
|
||||
route->deleteLater();
|
||||
m_routesDirty = true;
|
||||
}
|
||||
}
|
||||
if (m_routesDirty) {
|
||||
emit routesChanged();
|
||||
m_routesDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
ZigbeeNode::ZigbeeNodeState ZigbeeNode::stringToNodeState(const QString &nodeState)
|
||||
{
|
||||
if (nodeState == "ZigbeeNodeStateUninitialized") {
|
||||
|
|
@ -326,3 +386,67 @@ void ZigbeeNodeNeighbor::setPermitJoining(bool permitJoining)
|
|||
emit permitJoiningChanged();
|
||||
}
|
||||
}
|
||||
|
||||
ZigbeeNodeRoute::ZigbeeNodeRoute(quint16 destinationAddress, QObject *parent):
|
||||
QObject(parent),
|
||||
m_destinationAddress(destinationAddress)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
quint16 ZigbeeNodeRoute::destinationAddress() const
|
||||
{
|
||||
return m_destinationAddress;
|
||||
}
|
||||
|
||||
quint16 ZigbeeNodeRoute::nextHopAddress() const
|
||||
{
|
||||
return m_nextHopAddress;
|
||||
}
|
||||
|
||||
void ZigbeeNodeRoute::setNextHopAddress(quint16 nextHopAddress)
|
||||
{
|
||||
if (m_nextHopAddress != nextHopAddress) {
|
||||
m_nextHopAddress = nextHopAddress;
|
||||
emit nextHopAddressChanged();
|
||||
}
|
||||
}
|
||||
|
||||
ZigbeeNode::ZigbeeNodeRouteStatus ZigbeeNodeRoute::status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void ZigbeeNodeRoute::setStatus(ZigbeeNode::ZigbeeNodeRouteStatus status)
|
||||
{
|
||||
if (m_status != status) {
|
||||
m_status = status;
|
||||
emit statusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool ZigbeeNodeRoute::memoryConstrained() const
|
||||
{
|
||||
return m_memoryConstrained;
|
||||
}
|
||||
|
||||
void ZigbeeNodeRoute::setMemoryConstrained(bool memoryConstrained)
|
||||
{
|
||||
if (m_memoryConstrained != memoryConstrained) {
|
||||
m_memoryConstrained = memoryConstrained;
|
||||
emit memoryConstrainedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool ZigbeeNodeRoute::manyToOne() const
|
||||
{
|
||||
return m_manyToOne;
|
||||
}
|
||||
|
||||
void ZigbeeNodeRoute::setManyToOne(bool manyToOne)
|
||||
{
|
||||
if (m_manyToOne != manyToOne) {
|
||||
m_manyToOne = manyToOne;
|
||||
emit manyToOneChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include <QVariantMap>
|
||||
|
||||
class ZigbeeNodeNeighbor;
|
||||
class ZigbeeNodeRoute;
|
||||
|
||||
class ZigbeeNode : public QObject
|
||||
{
|
||||
|
|
@ -54,6 +55,7 @@ class ZigbeeNode : public QObject
|
|||
Q_PROPERTY(uint lqi READ lqi WRITE setLqi NOTIFY lqiChanged)
|
||||
Q_PROPERTY(QDateTime lastSeen READ lastSeen WRITE setLastSeen NOTIFY lastSeenChanged)
|
||||
Q_PROPERTY(QList<ZigbeeNodeNeighbor*> neighbors READ neighbors NOTIFY neighborsChanged)
|
||||
Q_PROPERTY(QList<ZigbeeNodeRoute*> routes READ routes NOTIFY routesChanged)
|
||||
|
||||
public:
|
||||
enum ZigbeeNodeType {
|
||||
|
|
@ -80,6 +82,15 @@ public:
|
|||
};
|
||||
Q_ENUM(ZigbeeNodeRelationship)
|
||||
|
||||
enum ZigbeeNodeRouteStatus {
|
||||
ZigbeeNodeRouteStatusActive,
|
||||
ZigbeeNodeRouteStatusDiscoveryUnderway,
|
||||
ZigbeeNodeRouteStatusDiscoveryFailed,
|
||||
ZigbeeNodeRouteStatusInactive,
|
||||
ZigbeeNodeRouteStatusValidationUnderway
|
||||
};
|
||||
Q_ENUM(ZigbeeNodeRouteStatus)
|
||||
|
||||
explicit ZigbeeNode(const QUuid &networkUuid, const QString &ieeeAddress, QObject *parent = nullptr);
|
||||
|
||||
QUuid networkUuid() const;
|
||||
|
|
@ -119,6 +130,10 @@ public:
|
|||
void addOrUpdateNeighbor(quint16 networkAddress, ZigbeeNodeRelationship relationship, quint8 lqi, quint8 depth, bool permitJoining);
|
||||
void commitNeighbors(QList<quint16> toBeKept);
|
||||
|
||||
QList<ZigbeeNodeRoute*> routes() const;
|
||||
void addOrUpdateRoute(quint16 destinationAddress, quint16 nextHopAddress, ZigbeeNodeRouteStatus status, bool memoryConstrained, bool manyToOne);
|
||||
void commitRoutes(QList<quint16> toBeKept);
|
||||
|
||||
static ZigbeeNodeState stringToNodeState(const QString &nodeState);
|
||||
static ZigbeeNodeType stringToNodeType(const QString &nodeType);
|
||||
|
||||
|
|
@ -134,6 +149,7 @@ signals:
|
|||
void lqiChanged(uint lqi);
|
||||
void lastSeenChanged(const QDateTime &lastSeen);
|
||||
void neighborsChanged();
|
||||
void routesChanged();
|
||||
|
||||
private:
|
||||
QUuid m_networkUuid;
|
||||
|
|
@ -150,6 +166,8 @@ private:
|
|||
QDateTime m_lastSeen;
|
||||
QList<ZigbeeNodeNeighbor*> m_neighbors;
|
||||
bool m_neighborsDirty = false;
|
||||
QList<ZigbeeNodeRoute*> m_routes;
|
||||
bool m_routesDirty = false;
|
||||
};
|
||||
|
||||
class ZigbeeNodeNeighbor: public QObject
|
||||
|
|
@ -192,4 +210,44 @@ private:
|
|||
bool m_permitJoining = false;
|
||||
};
|
||||
|
||||
class ZigbeeNodeRoute: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint16 destinationAddress READ destinationAddress CONSTANT)
|
||||
Q_PROPERTY(quint16 nextHopAddress READ nextHopAddress NOTIFY nextHopAddressChanged)
|
||||
Q_PROPERTY(ZigbeeNode::ZigbeeNodeRouteStatus status READ status NOTIFY statusChanged)
|
||||
Q_PROPERTY(bool memoryConstrained READ memoryConstrained NOTIFY memoryConstrainedChanged)
|
||||
Q_PROPERTY(bool manyToOne READ manyToOne NOTIFY manyToOneChanged)
|
||||
|
||||
public:
|
||||
ZigbeeNodeRoute(quint16 destinationAddress, QObject *parent);
|
||||
|
||||
quint16 destinationAddress() const;
|
||||
|
||||
quint16 nextHopAddress() const;
|
||||
void setNextHopAddress(quint16 nextHopAddress);
|
||||
|
||||
ZigbeeNode::ZigbeeNodeRouteStatus status() const;
|
||||
void setStatus(ZigbeeNode::ZigbeeNodeRouteStatus status);
|
||||
|
||||
bool memoryConstrained() const;
|
||||
void setMemoryConstrained(bool memoryConstrained);
|
||||
|
||||
bool manyToOne() const;
|
||||
void setManyToOne(bool manyToOne);
|
||||
|
||||
signals:
|
||||
void nextHopAddressChanged();
|
||||
void statusChanged();
|
||||
void memoryConstrainedChanged();
|
||||
void manyToOneChanged();
|
||||
|
||||
private:
|
||||
quint16 m_destinationAddress;
|
||||
quint16 m_nextHopAddress;
|
||||
ZigbeeNode::ZigbeeNodeRouteStatus m_status = ZigbeeNode::ZigbeeNodeRouteStatusInactive;
|
||||
bool m_memoryConstrained = false;
|
||||
bool m_manyToOne = false;
|
||||
};
|
||||
|
||||
#endif // ZIGBEENODE_H
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Page {
|
|||
property ZigbeeManager zigbeeManager: null
|
||||
property ZigbeeNetwork network: null
|
||||
|
||||
readonly property int nodeDistance: 150
|
||||
readonly property int nodeDistance: Style.iconSize * 2
|
||||
readonly property int nodeSize: Style.iconSize + Style.margins
|
||||
readonly property double scale: 1
|
||||
|
||||
|
|
@ -26,7 +26,12 @@ Page {
|
|||
|
||||
reload();
|
||||
for (var i = 0; i < network.nodes.count; i++) {
|
||||
network.nodes.get(i).neighborsChanged.connect(function() {root.reload()})
|
||||
network.nodes.get(i).neighborsChanged.connect(root.reload)
|
||||
}
|
||||
}
|
||||
Component.onDestruction: {
|
||||
for (var i = 0; i < network.nodes.count; i++) {
|
||||
network.nodes.get(i).neighborsChanged.disconnect(root.reload)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,12 +39,12 @@ Page {
|
|||
target: root.network.nodes
|
||||
onNodeAdded: {
|
||||
root.reload()
|
||||
node.neighborsChanged.connect(function() {root.reload()});
|
||||
node.neighborsChanged.connect(root.reload);
|
||||
}
|
||||
}
|
||||
|
||||
function generateNodeList() {
|
||||
d.nodeItems = []
|
||||
var nodeItems = []
|
||||
var coordinator = {}
|
||||
var routers = []
|
||||
var endDevices = []
|
||||
|
|
@ -60,19 +65,20 @@ Page {
|
|||
|
||||
var startAngle = -90
|
||||
|
||||
var x = root.nodeDistance * Math.cos(startAngle * Math.PI / 180)
|
||||
var y = root.nodeDistance * Math.sin(startAngle * Math.PI / 180)
|
||||
d.nodeItems.push(createNodeItem(coordinator, x, y, startAngle))
|
||||
var routersCircumference = Math.max(5, routers.length) * (root.nodeSize + root.nodeDistance) * root.scale
|
||||
var distanceFromCenter = routersCircumference / 2 / Math.PI
|
||||
|
||||
routers.unshift(coordinator)
|
||||
|
||||
var handledEndDevices = []
|
||||
|
||||
var angle = 360 / (routers.length + 1);
|
||||
var angle = 360 / routers.length;
|
||||
for (var i = 0; i < routers.length; i++) {
|
||||
var router = routers[i]
|
||||
var nodeAngle = startAngle + angle * (i + 1);
|
||||
var x = root.nodeDistance * Math.cos(nodeAngle * Math.PI / 180)
|
||||
var y = root.nodeDistance * Math.sin(nodeAngle * Math.PI / 180)
|
||||
d.nodeItems.push(createNodeItem(routers[i], x, y, nodeAngle));
|
||||
var nodeAngle = startAngle + angle * i;
|
||||
var x = distanceFromCenter * Math.cos(nodeAngle * Math.PI / 180)
|
||||
var y = distanceFromCenter * Math.sin(nodeAngle * Math.PI / 180)
|
||||
nodeItems.push(createNodeItem(routers[i], x, y, nodeAngle));
|
||||
|
||||
|
||||
var neighborCounter = 0;
|
||||
|
|
@ -88,13 +94,12 @@ Page {
|
|||
}
|
||||
handledEndDevices.push(neighborNode.networkAddress)
|
||||
|
||||
|
||||
var neighborAngle = nodeAngle + neighborCounter * 8
|
||||
var neighborDistance = root.nodeDistance * 1.5 * root.scale + neighborCounter * root.nodeSize * 0.75 * root.scale
|
||||
var neighborDistance = (distanceFromCenter + root.nodeDistance + root.nodeSize) * root.scale + neighborCounter * root.nodeDistance * .5 * root.scale
|
||||
|
||||
x = neighborDistance * Math.cos(neighborAngle * Math.PI / 180)
|
||||
y = neighborDistance * Math.sin(neighborAngle * Math.PI / 180)
|
||||
d.nodeItems.push(createNodeItem(neighborNode, x, y, angle))
|
||||
nodeItems.push(createNodeItem(neighborNode, x, y, angle))
|
||||
|
||||
neighborCounter++
|
||||
}
|
||||
|
|
@ -120,10 +125,10 @@ Page {
|
|||
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))
|
||||
var y = Style.margins + cellHeight * row + root.nodeSize - canvas.height / 2
|
||||
nodeItems.push(createNodeItem(node, x, y, 0))
|
||||
}
|
||||
d.nodeItems = nodeItems
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -173,16 +178,13 @@ Page {
|
|||
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
|
||||
print("repainting", flickable.contentX, flickable.contentY)
|
||||
if (flickable.contentX == 0 && flickable.contentY == 0) {
|
||||
flickable.contentX = (flickable.contentWidth - flickable.width) / 2
|
||||
flickable.contentY = (flickable.contentHeight - flickable.height) / 2
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
|
|
@ -190,6 +192,17 @@ Page {
|
|||
property var nodeItems: []
|
||||
|
||||
property int selectedNodeAddress: -1
|
||||
readonly property var selectedNodeItem: {
|
||||
print("selected", selectedNodeAddress)
|
||||
for (var i = 0; i < nodeItems.length; i++) {
|
||||
print("checking", nodeItems[i].node.networkAddress)
|
||||
if (nodeItems[i].node.networkAddress === selectedNodeAddress) {
|
||||
return nodeItems[i]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
readonly property ZigbeeNode selectedNode: selectedNodeAddress >= 0 ? network.nodes.getNodeByNetworkAddress(selectedNodeAddress) : null
|
||||
|
||||
property int minX: 0
|
||||
|
|
@ -249,11 +262,69 @@ Page {
|
|||
for (var i = 0; i < d.nodeItems.length; i++) {
|
||||
paintEdges(ctx, d.nodeItems[i], true)
|
||||
}
|
||||
if (d.selectedNodeItem) {
|
||||
paintRoute(ctx, d.selectedNodeItem)
|
||||
}
|
||||
for (var i = 0; i < d.nodeItems.length; i++) {
|
||||
paintNode(ctx, d.nodeItems[i])
|
||||
}
|
||||
}
|
||||
|
||||
function paintRoute(ctx, nodeItem) {
|
||||
var node = nodeItem.node
|
||||
var nextHop = -1
|
||||
if (node.type === ZigbeeNode.ZigbeeNodeTypeRouter) {
|
||||
for (var i = 0; i < node.routes.length; i++) {
|
||||
if (node.routes[i].destinationAddress === 0) {
|
||||
nextHop = node.routes[i].nextHopAddress
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (node.type === ZigbeeNode.ZigbeeNodeTypeEndDevice) {
|
||||
for (var i = 0; i < network.nodes.count; i++) {
|
||||
for (var j = 0; j < network.nodes.get(i).neighbors.length; j++) {
|
||||
if (network.nodes.get(i).neighbors[j].networkAddress === node.networkAddress) {
|
||||
nextHop = network.nodes.get(i).networkAddress
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("next hop", nextHop)
|
||||
if (nextHop == -1) {
|
||||
return;
|
||||
}
|
||||
var toNodeItem = null
|
||||
for (var i = 0; i < d.nodeItems.length; i++) {
|
||||
if (d.nodeItems[i].node.networkAddress == nextHop) {
|
||||
toNodeItem = d.nodeItems[i]
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!toNodeItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.save()
|
||||
|
||||
ctx.lineWidth = 2
|
||||
ctx.setLineDash([4, 4])
|
||||
ctx.strokeStyle = Style.blue
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(scale * nodeItem.x, scale * nodeItem.y)
|
||||
ctx.lineTo(scale * toNodeItem.x, scale * toNodeItem.y)
|
||||
|
||||
ctx.stroke();
|
||||
ctx.closePath()
|
||||
ctx.setLineDash([1,0])
|
||||
ctx.restore();
|
||||
|
||||
paintRoute(ctx, toNodeItem)
|
||||
|
||||
}
|
||||
|
||||
function paintEdges(ctx, nodeItem, selected) {
|
||||
for (var i = 0; i < nodeItem.node.neighbors.length; i++) {
|
||||
var neighbor = nodeItem.node.neighbors[i]
|
||||
|
|
@ -346,7 +417,11 @@ Page {
|
|||
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.selectedNodeAddress = nodeItem.node.networkAddress;
|
||||
print("sleecting", nodeItem.node.networkAddress)
|
||||
print("selecting", nodeItem.node.networkAddress)
|
||||
for (var j = 0; j < nodeItem.node.routes.length; j++) {
|
||||
var route = nodeItem.node.routes[j]
|
||||
print("route:", route.destinationAddress, "via", route.nextHopAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -418,7 +493,7 @@ Page {
|
|||
|
||||
contentItem: ListView {
|
||||
id: tableListView
|
||||
spacing: app.margins
|
||||
// spacing: app.margins
|
||||
implicitHeight: Math.min(root.height / 4, count * Style.smallIconSize)
|
||||
clip: true
|
||||
model: d.selectedNode ? d.selectedNode.neighbors.length : 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue