Obtain LQI neighbor and routing tables from the network
This commit is contained in:
parent
a70afb965d
commit
acfd73c271
@ -555,7 +555,7 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLeaveNetwork(bool rejoin
|
|||||||
|
|
||||||
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLqi(quint8 startIndex)
|
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLqi(quint8 startIndex)
|
||||||
{
|
{
|
||||||
qCDebug(dcZigbeeDeviceObject()) << "Request lqi table from" << m_node << "start index" << startIndex;
|
qCDebug(dcZigbeeDeviceObject()) << "Requesting lqi table from" << m_node << "start index" << startIndex;
|
||||||
|
|
||||||
// Build APS request
|
// Build APS request
|
||||||
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtLqiRequest);
|
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtLqiRequest);
|
||||||
@ -597,7 +597,7 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLqi(quint8 startIndex)
|
|||||||
|
|
||||||
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtBind(quint8 startIndex)
|
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtBind(quint8 startIndex)
|
||||||
{
|
{
|
||||||
qCDebug(dcZigbeeDeviceObject()) << "Request management bind table from" << m_node << "start index" << startIndex;
|
qCDebug(dcZigbeeDeviceObject()) << "Requesting management bind table from" << m_node << "start index" << startIndex;
|
||||||
|
|
||||||
// Build APS request
|
// Build APS request
|
||||||
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtBindRequest);
|
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtBindRequest);
|
||||||
@ -637,6 +637,49 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtBind(quint8 startIndex)
|
|||||||
return zdoReply;
|
return zdoReply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtRtg(quint8 startIndex)
|
||||||
|
{
|
||||||
|
qCDebug(dcZigbeeDeviceObject()) << "Requesting routing table from" << m_node << "start index" << startIndex;
|
||||||
|
|
||||||
|
// Build APS request
|
||||||
|
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtRoutingTableRequest);
|
||||||
|
|
||||||
|
// Generate a new transaction sequence number for this device object
|
||||||
|
quint8 transactionSequenceNumber = m_transactionSequenceNumber++;
|
||||||
|
|
||||||
|
QByteArray asdu;
|
||||||
|
QDataStream stream(&asdu, QIODevice::WriteOnly);
|
||||||
|
stream.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
stream << transactionSequenceNumber << startIndex;
|
||||||
|
|
||||||
|
// Set the ZDO frame as APS request payload
|
||||||
|
request.setAsdu(asdu);
|
||||||
|
|
||||||
|
// Create the device object reply and wait for the response indication
|
||||||
|
ZigbeeDeviceObjectReply *zdoReply = createZigbeeDeviceObjectReply(request, transactionSequenceNumber);
|
||||||
|
|
||||||
|
// Send the request, on finished read the confirm information
|
||||||
|
ZigbeeNetworkReply *networkReply = m_network->sendRequest(request);
|
||||||
|
connect(networkReply, &ZigbeeNetworkReply::finished, zdoReply, [this, networkReply, zdoReply](){
|
||||||
|
if (!verifyNetworkError(zdoReply, networkReply)) {
|
||||||
|
finishZdoReply(zdoReply);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The request was successfully sent to the device
|
||||||
|
// Now check if the expected indication response received already
|
||||||
|
if (zdoReply->isComplete()) {
|
||||||
|
qCDebug(dcZigbeeDeviceObject()) << "Successfully received response for" << static_cast<ZigbeeDeviceProfile::ZdoCommand>(networkReply->request().clusterId());
|
||||||
|
finishZdoReply(zdoReply);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We received the confirmation but not yet the indication
|
||||||
|
});
|
||||||
|
|
||||||
|
return zdoReply;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ZigbeeNetworkRequest ZigbeeDeviceObject::buildZdoRequest(quint16 zdoRequest)
|
ZigbeeNetworkRequest ZigbeeDeviceObject::buildZdoRequest(quint16 zdoRequest)
|
||||||
{
|
{
|
||||||
ZigbeeNetworkRequest request;
|
ZigbeeNetworkRequest request;
|
||||||
|
|||||||
@ -61,6 +61,7 @@ public:
|
|||||||
ZigbeeDeviceObjectReply *requestMgmtLeaveNetwork(bool rejoin = false, bool removeChildren = false);
|
ZigbeeDeviceObjectReply *requestMgmtLeaveNetwork(bool rejoin = false, bool removeChildren = false);
|
||||||
ZigbeeDeviceObjectReply *requestMgmtLqi(quint8 startIndex = 0x00);
|
ZigbeeDeviceObjectReply *requestMgmtLqi(quint8 startIndex = 0x00);
|
||||||
ZigbeeDeviceObjectReply *requestMgmtBind(quint8 startIndex = 0x00);
|
ZigbeeDeviceObjectReply *requestMgmtBind(quint8 startIndex = 0x00);
|
||||||
|
ZigbeeDeviceObjectReply *requestMgmtRtg(quint8 startIndex = 0x00);
|
||||||
|
|
||||||
// TODO: write all requests
|
// TODO: write all requests
|
||||||
|
|
||||||
|
|||||||
@ -174,6 +174,67 @@ ZigbeeDeviceProfile::PowerDescriptor ZigbeeDeviceProfile::parsePowerDescriptor(q
|
|||||||
return powerDescriptor;
|
return powerDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZigbeeDeviceProfile::NeighborTable ZigbeeDeviceProfile::parseNeighborTable(const QByteArray &payload)
|
||||||
|
{
|
||||||
|
ZigbeeDeviceProfile::NeighborTable table;
|
||||||
|
QDataStream stream(payload);
|
||||||
|
stream.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
|
||||||
|
quint8 messageId; quint8 listRecordCount;
|
||||||
|
stream >> messageId >> table.status >> table.tableSize >> table.startIndex >> listRecordCount;
|
||||||
|
for (int i = 0; i < listRecordCount; i++) {
|
||||||
|
ZigbeeDeviceProfile::NeighborTableListRecord record;
|
||||||
|
stream >> record.extendedPanId;
|
||||||
|
quint64 ieeeAddress;
|
||||||
|
stream >> ieeeAddress;
|
||||||
|
record.ieeeAddress = ZigbeeAddress(ieeeAddress);
|
||||||
|
stream >> record.shortAddress;
|
||||||
|
|
||||||
|
quint8 nodeFlags;
|
||||||
|
stream >> nodeFlags;
|
||||||
|
record.nodeType = static_cast<NodeType>(nodeFlags & 0x03);
|
||||||
|
record.receiverOnWhenIdle = static_cast<bool>((nodeFlags & 0x0c) >> 2);
|
||||||
|
record.relationship = static_cast<Relationship>((nodeFlags & 0x70) >> 4);
|
||||||
|
|
||||||
|
stream >> nodeFlags;
|
||||||
|
record.permitJoining = (nodeFlags & 0x03) == 1;
|
||||||
|
stream >> record.depth;
|
||||||
|
stream >> record.lqi;
|
||||||
|
|
||||||
|
table.records.append(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZigbeeDeviceProfile::RoutingTable ZigbeeDeviceProfile::parseRoutingTable(const QByteArray &payload)
|
||||||
|
{
|
||||||
|
ZigbeeDeviceProfile::RoutingTable table;
|
||||||
|
QDataStream stream(payload);
|
||||||
|
stream.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
|
||||||
|
quint8 messageId; quint8 listRecordCount;
|
||||||
|
stream >> messageId >> table.status >> table.tableSize >> table.startIndex >> listRecordCount;
|
||||||
|
for (int i = 0; i < listRecordCount; i++) {
|
||||||
|
ZigbeeDeviceProfile::RoutingTableListRecord record;
|
||||||
|
stream >> record.destinationAddress;
|
||||||
|
|
||||||
|
qint8 flags;
|
||||||
|
stream >> flags;
|
||||||
|
record.status = static_cast<RouteStatus>(flags & 0x07);
|
||||||
|
record.memoryConstrained = (flags & 0x08) > 0;
|
||||||
|
record.manyToOne = (flags & 0x10) > 0;
|
||||||
|
record.routeRecordRequired = (flags & 0x20) > 0;
|
||||||
|
|
||||||
|
stream >> record.nextHopAddress;
|
||||||
|
|
||||||
|
table.records.append(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
ZigbeeDeviceProfile::Adpu ZigbeeDeviceProfile::parseAdpu(const QByteArray &adpu)
|
ZigbeeDeviceProfile::Adpu ZigbeeDeviceProfile::parseAdpu(const QByteArray &adpu)
|
||||||
{
|
{
|
||||||
QDataStream stream(adpu);
|
QDataStream stream(adpu);
|
||||||
@ -187,12 +248,12 @@ ZigbeeDeviceProfile::Adpu ZigbeeDeviceProfile::parseAdpu(const QByteArray &adpu)
|
|||||||
return deviceAdpu;
|
return deviceAdpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::Adpu &deviceAdpu)
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::Adpu &adpu)
|
||||||
{
|
{
|
||||||
debug.nospace() << "DeviceProfileAdpu(SQN: " << deviceAdpu.transactionSequenceNumber << ", ";
|
debug.nospace() << "DeviceProfileAdpu(SQN: " << adpu.transactionSequenceNumber << ", ";
|
||||||
debug.nospace() << deviceAdpu.status << ", ";
|
debug.nospace() << adpu.status << ", ";
|
||||||
debug.nospace() << ZigbeeUtils::convertUint16ToHexString(deviceAdpu.addressOfInterest) << ", ";
|
debug.nospace() << ZigbeeUtils::convertUint16ToHexString(adpu.addressOfInterest) << ", ";
|
||||||
debug.nospace() << "Payload: " << ZigbeeUtils::convertByteArrayToHexString(deviceAdpu.payload) << ")";
|
debug.nospace() << "Payload: " << ZigbeeUtils::convertByteArrayToHexString(adpu.payload) << ")";
|
||||||
return debug.space();
|
return debug.space();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,3 +334,29 @@ QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::BindingTableListRecor
|
|||||||
}
|
}
|
||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::NeighborTableListRecord &neighborTableListRecord)
|
||||||
|
{
|
||||||
|
debug.nospace() << "NeighborTableListRecord(" << neighborTableListRecord.ieeeAddress.toString() << ", ";
|
||||||
|
debug.nospace() << "NWK address: " << ZigbeeUtils::convertUint16ToHexString(neighborTableListRecord.shortAddress) << ", ";
|
||||||
|
debug.nospace() << "Extended PAN ID: " << neighborTableListRecord.extendedPanId << ", ";
|
||||||
|
debug.nospace() << "Node type: " << neighborTableListRecord.nodeType << ", ";
|
||||||
|
debug.nospace() << "RxOn: " << neighborTableListRecord.receiverOnWhenIdle << ", ";
|
||||||
|
debug.nospace() << "Relationship: " << neighborTableListRecord.relationship << ", ";
|
||||||
|
debug.nospace() << "Permit join: " << neighborTableListRecord.permitJoining << ", ";
|
||||||
|
debug.nospace() << "Depth: " << neighborTableListRecord.depth << ", ";
|
||||||
|
debug.nospace() << "LQI: " << neighborTableListRecord.lqi << ") ";
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::RoutingTableListRecord &routingTableListRecord)
|
||||||
|
{
|
||||||
|
debug.nospace() << "RoutingTableListRecord(";
|
||||||
|
debug.nospace() << "Destination address: " << ZigbeeUtils::convertUint16ToHexString(routingTableListRecord.destinationAddress) << ", ";
|
||||||
|
debug.nospace() << "Next hop: " << routingTableListRecord.nextHopAddress << ", ";
|
||||||
|
debug.nospace() << "Status: " << routingTableListRecord.status << ", ";
|
||||||
|
debug.nospace() << "Memory constrained: " << routingTableListRecord.memoryConstrained << ", ";
|
||||||
|
debug.nospace() << "Many-to-one: " << routingTableListRecord.manyToOne << ", ";
|
||||||
|
debug.nospace() << "RRR: " << routingTableListRecord.routeRecordRequired << ")";
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|||||||
@ -198,12 +198,23 @@ public:
|
|||||||
Q_ENUM(DeviceType)
|
Q_ENUM(DeviceType)
|
||||||
|
|
||||||
enum Relationship {
|
enum Relationship {
|
||||||
Parent,
|
RelationshipParent,
|
||||||
Child,
|
RelationshipChild,
|
||||||
Sibling
|
RelationshipSibling,
|
||||||
|
RelationshipNone,
|
||||||
|
RelationshipPreviousChild
|
||||||
};
|
};
|
||||||
Q_ENUM(Relationship)
|
Q_ENUM(Relationship)
|
||||||
|
|
||||||
|
enum RouteStatus {
|
||||||
|
RouteStatusActive,
|
||||||
|
RouteStatusDiscoveryUnderway,
|
||||||
|
RouteStatusDiscoveryFailed,
|
||||||
|
RouteStatusInactive,
|
||||||
|
RouteStatusValidationUnderway
|
||||||
|
};
|
||||||
|
Q_ENUM(RouteStatus)
|
||||||
|
|
||||||
enum PowerMode {
|
enum PowerMode {
|
||||||
PowerModeAlwaysOn,
|
PowerModeAlwaysOn,
|
||||||
PowerModeOnPeriodically,
|
PowerModeOnPeriodically,
|
||||||
@ -287,12 +298,48 @@ public:
|
|||||||
quint8 destinationEndpoint; // Only for destination address 0x03
|
quint8 destinationEndpoint; // Only for destination address 0x03
|
||||||
} BindingTableListRecord;
|
} BindingTableListRecord;
|
||||||
|
|
||||||
|
typedef struct NeighborTableListRecord {
|
||||||
|
quint64 extendedPanId;
|
||||||
|
ZigbeeAddress ieeeAddress;
|
||||||
|
quint16 shortAddress;
|
||||||
|
NodeType nodeType;
|
||||||
|
bool receiverOnWhenIdle;
|
||||||
|
Relationship relationship;
|
||||||
|
bool permitJoining;
|
||||||
|
quint8 depth;
|
||||||
|
quint8 lqi;
|
||||||
|
} NeighborTableListRecord;
|
||||||
|
|
||||||
|
typedef struct NeighborTable{
|
||||||
|
quint8 status;
|
||||||
|
quint8 tableSize;
|
||||||
|
quint8 startIndex;
|
||||||
|
QList<NeighborTableListRecord> records;
|
||||||
|
} NeighborTable;
|
||||||
|
|
||||||
|
typedef struct RoutingTableListRecord {
|
||||||
|
quint16 destinationAddress;
|
||||||
|
RouteStatus status;
|
||||||
|
bool memoryConstrained;
|
||||||
|
bool manyToOne;
|
||||||
|
bool routeRecordRequired;
|
||||||
|
quint16 nextHopAddress;
|
||||||
|
} RoutingTableListRecord;
|
||||||
|
|
||||||
|
typedef struct RoutingTable{
|
||||||
|
quint8 status;
|
||||||
|
quint8 tableSize;
|
||||||
|
quint8 startIndex;
|
||||||
|
QList<RoutingTableListRecord> records;
|
||||||
|
} RoutingTable;
|
||||||
|
|
||||||
static NodeDescriptor parseNodeDescriptor(const QByteArray &payload);
|
static NodeDescriptor parseNodeDescriptor(const QByteArray &payload);
|
||||||
static MacCapabilities parseMacCapabilities(quint8 macCapabilitiesFlag);
|
static MacCapabilities parseMacCapabilities(quint8 macCapabilitiesFlag);
|
||||||
static ServerMask parseServerMask(quint16 serverMaskFlag);
|
static ServerMask parseServerMask(quint16 serverMaskFlag);
|
||||||
static DescriptorCapabilities parseDescriptorCapabilities(quint8 descriptorCapabilitiesFlag);
|
static DescriptorCapabilities parseDescriptorCapabilities(quint8 descriptorCapabilitiesFlag);
|
||||||
static PowerDescriptor parsePowerDescriptor(quint16 powerDescriptorFlag);
|
static PowerDescriptor parsePowerDescriptor(quint16 powerDescriptorFlag);
|
||||||
|
static NeighborTable parseNeighborTable(const QByteArray &payload);
|
||||||
|
static RoutingTable parseRoutingTable(const QByteArray &payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::Adpu &deviceAdpu);
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::Adpu &deviceAdpu);
|
||||||
@ -302,5 +349,7 @@ QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::ServerMask &serverMas
|
|||||||
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::DescriptorCapabilities &descriptorCapabilities);
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::DescriptorCapabilities &descriptorCapabilities);
|
||||||
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::PowerDescriptor &powerDescriptor);
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::PowerDescriptor &powerDescriptor);
|
||||||
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::BindingTableListRecord &bindingTableListRecord);
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::BindingTableListRecord &bindingTableListRecord);
|
||||||
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::NeighborTableListRecord &neighborTableListRecord);
|
||||||
|
QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::RoutingTableListRecord &routingTableListRecord);
|
||||||
|
|
||||||
#endif // ZIGBEEDEVICEPROFILE_H
|
#endif // ZIGBEEDEVICEPROFILE_H
|
||||||
|
|||||||
@ -53,7 +53,6 @@ ZigbeeNetwork::ZigbeeNetwork(const QUuid &networkUuid, QObject *parent) :
|
|||||||
|
|
||||||
m_reachableRefreshTimer = new QTimer(this);
|
m_reachableRefreshTimer = new QTimer(this);
|
||||||
m_reachableRefreshTimer->setInterval(120000);
|
m_reachableRefreshTimer->setInterval(120000);
|
||||||
m_reachableRefreshTimer->setSingleShot(false);
|
|
||||||
connect(m_reachableRefreshTimer, &QTimer::timeout, this, &ZigbeeNetwork::evaluateNodeReachableStates);
|
connect(m_reachableRefreshTimer, &QTimer::timeout, this, &ZigbeeNetwork::evaluateNodeReachableStates);
|
||||||
|
|
||||||
connect(this, &ZigbeeNetwork::stateChanged, this, [this](ZigbeeNetwork::State state){
|
connect(this, &ZigbeeNetwork::stateChanged, this, [this](ZigbeeNetwork::State state){
|
||||||
@ -331,6 +330,16 @@ void ZigbeeNetwork::removeZigbeeNode(const ZigbeeAddress &address)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ZigbeeNetwork::refreshNeighborTable()
|
||||||
|
{
|
||||||
|
foreach (ZigbeeNode *node, m_nodes) {
|
||||||
|
if (node->macCapabilities().receiverOnWhenIdle) {
|
||||||
|
m_refreshNeighborTableAddresses.append(node->extendedAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchNextNodeLqiTable();
|
||||||
|
}
|
||||||
|
|
||||||
void ZigbeeNetwork::printNetwork()
|
void ZigbeeNetwork::printNetwork()
|
||||||
{
|
{
|
||||||
qCDebug(dcZigbeeNetwork()) << this;
|
qCDebug(dcZigbeeNetwork()) << this;
|
||||||
@ -454,30 +463,43 @@ ZigbeeNode *ZigbeeNetwork::createNode(quint16 shortAddress, const ZigbeeAddress
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZigbeeNetwork::evaluateNextNodeReachableState()
|
void ZigbeeNetwork::fetchNextNodeLqiTable()
|
||||||
{
|
{
|
||||||
if (m_reachableRefreshAddresses.isEmpty())
|
ZigbeeNode *node = nullptr;
|
||||||
return;
|
while (!node && !m_refreshNeighborTableAddresses.isEmpty()) {
|
||||||
|
node = getZigbeeNode(m_refreshNeighborTableAddresses.takeFirst());
|
||||||
ZigbeeNode *node = getZigbeeNode(m_reachableRefreshAddresses.takeFirst());
|
}
|
||||||
|
while (!node && !m_reachableRefreshAddresses.isEmpty()) {
|
||||||
|
node = getZigbeeNode(m_reachableRefreshAddresses.takeFirst());
|
||||||
|
}
|
||||||
if (!node) {
|
if (!node) {
|
||||||
// Not does not exit any more...continue
|
// Nothing to do...
|
||||||
evaluateNextNodeReachableState();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qCDebug(dcZigbeeNetwork()) << "Refreshing LQI neighbor table for node" << node->shortAddress() << node->modelName();
|
||||||
|
|
||||||
// Make a lqi request in order to check if the node is reachable
|
// Make a lqi request in order to check if the node is reachable
|
||||||
ZigbeeDeviceObjectReply *zdoReply = node->deviceObject()->requestNetworkAddress();
|
ZigbeeReply *reply = node->readLqiTableEntries();
|
||||||
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, this, [=](){
|
connect(reply, &ZigbeeReply::finished, this, [=](){
|
||||||
if (zdoReply->error()) {
|
if (reply->error()) {
|
||||||
qCWarning(dcZigbeeNetwork()) << node << "seems not to be reachable" << zdoReply->error();
|
qCWarning(dcZigbeeNetwork()) << node << "seems not to be reachable" << reply->error();
|
||||||
setNodeReachable(node, false);
|
setNodeReachable(node, false);
|
||||||
} else {
|
} else {
|
||||||
setNodeReachable(node, true);
|
setNodeReachable(node, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give some time for other requests to be processed
|
ZigbeeReply *reply = node->readRoutingTableEntries();
|
||||||
QTimer::singleShot(5000, this, &ZigbeeNetwork::evaluateNextNodeReachableState);
|
connect(reply, &ZigbeeReply::finished, this, [=]() {
|
||||||
|
|
||||||
|
// While we still need to refresh neighbor tables, send the next request right away...
|
||||||
|
if (!m_refreshNeighborTableAddresses.isEmpty()) {
|
||||||
|
fetchNextNodeLqiTable();
|
||||||
|
} else {
|
||||||
|
// ... else be easier on the resources for the cyclic refresh
|
||||||
|
QTimer::singleShot(5000, this, &ZigbeeNetwork::fetchNextNodeLqiTable);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,6 +674,7 @@ void ZigbeeNetwork::setState(ZigbeeNetwork::State state)
|
|||||||
|
|
||||||
if (state == StateRunning) {
|
if (state == StateRunning) {
|
||||||
printNetwork();
|
printNetwork();
|
||||||
|
|
||||||
}
|
}
|
||||||
emit stateChanged(m_state);
|
emit stateChanged(m_state);
|
||||||
}
|
}
|
||||||
@ -927,29 +950,35 @@ void ZigbeeNetwork::onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const
|
|||||||
|
|
||||||
void ZigbeeNetwork::evaluateNodeReachableStates()
|
void ZigbeeNetwork::evaluateNodeReachableStates()
|
||||||
{
|
{
|
||||||
qCDebug(dcZigbeeNetwork()) << "Evaluate reachable state of nodes";
|
qCDebug(dcZigbeeNetwork()) << "Evaluating reachable state of nodes...";
|
||||||
m_reachableRefreshAddresses.clear();
|
|
||||||
|
|
||||||
foreach (ZigbeeNode *node, m_nodes) {
|
foreach (ZigbeeNode *node, m_nodes) {
|
||||||
// Skip the coordinator
|
if (node->shortAddress() == 0x0000) {
|
||||||
if (node->shortAddress() == 0x0000)
|
// While we wouldn't need to check ourselves for being reachable, do it nevertheless so we keep the
|
||||||
|
// neighbor table in sync which is useful in logs
|
||||||
|
if (!m_reachableRefreshAddresses.contains(node->extendedAddress())) {
|
||||||
|
m_reachableRefreshAddresses.append(node->extendedAddress());
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (node->macCapabilities().receiverOnWhenIdle && node->shortAddress() != 0x0000) {
|
if (node->macCapabilities().receiverOnWhenIdle) {
|
||||||
|
|
||||||
// Lets send a request to all things which are not reachable
|
// Lets send a request to all things which are not reachable
|
||||||
if (!node->reachable()) {
|
if (!node->reachable()) {
|
||||||
qCDebug(dcZigbeeNetwork()) << node << "enqueue evaluating reachable state";
|
if (!m_reachableRefreshAddresses.contains(node->extendedAddress())) {
|
||||||
m_reachableRefreshAddresses.append(node->extendedAddress());
|
qCDebug(dcZigbeeNetwork()) << node << "is not reachable. Scheduling LQI request.";
|
||||||
|
m_reachableRefreshAddresses.append(node->extendedAddress());
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lets send a request to nodes which have not been seen more than 10 min
|
// Lets send a request to nodes which have not been seen more than 10 min
|
||||||
int msSinceLastSeen = node->lastSeen().msecsTo(QDateTime::currentDateTimeUtc());
|
qulonglong msSinceLastSeen = node->lastSeen().msecsTo(QDateTime::currentDateTimeUtc());
|
||||||
qCDebug(dcZigbeeNetwork()) << node << "has been seen the last time" << QTime::fromMSecsSinceStartOfDay(msSinceLastSeen).toString() << "ago.";
|
qCDebug(dcZigbeeNetwork()) << node << "has been seen the last time" << QTime::fromMSecsSinceStartOfDay(msSinceLastSeen).toString() << "ago.";
|
||||||
// 10 min = 10 * 60 * 1000 = 600000 ms
|
// 10 min = 10 * 60 * 1000 = 600000 ms
|
||||||
if (msSinceLastSeen > 600000) {
|
if (msSinceLastSeen > 600000 && !m_reachableRefreshAddresses.contains(node->extendedAddress())) {
|
||||||
qCDebug(dcZigbeeNetwork()) << node << "enqueue evaluating reachable state";
|
qCDebug(dcZigbeeNetwork()) << node << "has not been seen in" << (msSinceLastSeen / 1000 / 60) << "minutes. Scheduling LQI request.";
|
||||||
m_reachableRefreshAddresses.append(node->extendedAddress());
|
m_reachableRefreshAddresses.append(node->extendedAddress());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -966,7 +995,7 @@ void ZigbeeNetwork::evaluateNodeReachableStates()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateNextNodeReachableState();
|
fetchNextNodeLqiTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, ZigbeeNetwork *network)
|
QDebug operator<<(QDebug debug, ZigbeeNetwork *network)
|
||||||
|
|||||||
@ -132,6 +132,8 @@ public:
|
|||||||
|
|
||||||
void removeZigbeeNode(const ZigbeeAddress &address);
|
void removeZigbeeNode(const ZigbeeAddress &address);
|
||||||
|
|
||||||
|
void refreshNeighborTable();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUuid m_networkUuid;
|
QUuid m_networkUuid;
|
||||||
State m_state = StateUninitialized;
|
State m_state = StateUninitialized;
|
||||||
@ -185,7 +187,8 @@ protected:
|
|||||||
|
|
||||||
QTimer *m_reachableRefreshTimer = nullptr;
|
QTimer *m_reachableRefreshTimer = nullptr;
|
||||||
QList<ZigbeeAddress> m_reachableRefreshAddresses;
|
QList<ZigbeeAddress> m_reachableRefreshAddresses;
|
||||||
void evaluateNextNodeReachableState();
|
QList<ZigbeeAddress> m_refreshNeighborTableAddresses;
|
||||||
|
void fetchNextNodeLqiTable();
|
||||||
|
|
||||||
void setPermitJoiningState(bool permitJoiningEnabled, quint8 duration = 0);
|
void setPermitJoiningState(bool permitJoiningEnabled, quint8 duration = 0);
|
||||||
|
|
||||||
|
|||||||
@ -147,6 +147,16 @@ QList<ZigbeeDeviceProfile::BindingTableListRecord> ZigbeeNode::bindingTableRecor
|
|||||||
return m_bindingTableRecords;
|
return m_bindingTableRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<ZigbeeDeviceProfile::NeighborTableListRecord> ZigbeeNode::neighborTableRecords() const
|
||||||
|
{
|
||||||
|
return m_neighborTableRecords.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<ZigbeeDeviceProfile::RoutingTableListRecord> ZigbeeNode::routingTableRecords() const
|
||||||
|
{
|
||||||
|
return m_routingTableRecords.values();
|
||||||
|
}
|
||||||
|
|
||||||
void ZigbeeNode::setState(ZigbeeNode::State state)
|
void ZigbeeNode::setState(ZigbeeNode::State state)
|
||||||
{
|
{
|
||||||
if (m_state == state)
|
if (m_state == state)
|
||||||
@ -264,6 +274,20 @@ ZigbeeReply *ZigbeeNode::readBindingTableEntries()
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZigbeeReply *ZigbeeNode::readLqiTableEntries()
|
||||||
|
{
|
||||||
|
ZigbeeReply *reply = new ZigbeeReply(this);
|
||||||
|
readNeighborTableChunk(reply, 0);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZigbeeReply *ZigbeeNode::readRoutingTableEntries()
|
||||||
|
{
|
||||||
|
ZigbeeReply *reply = new ZigbeeReply(this);
|
||||||
|
readRoutingTableChunk(reply, 0);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
void ZigbeeNode::initNodeDescriptor()
|
void ZigbeeNode::initNodeDescriptor()
|
||||||
{
|
{
|
||||||
qCDebug(dcZigbeeNode()) << "Requesting node descriptor from" << this;
|
qCDebug(dcZigbeeNode()) << "Requesting node descriptor from" << this;
|
||||||
@ -510,6 +534,126 @@ void ZigbeeNode::removeNextBinding(ZigbeeReply *reply)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ZigbeeNode::readNeighborTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||||
|
{
|
||||||
|
ZigbeeDeviceObjectReply *zdoReply = deviceObject()->requestMgmtLqi(startIndex);
|
||||||
|
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, reply, [=](){
|
||||||
|
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||||
|
qCWarning(dcZigbeeNode()) << "Failed to read neighbor table" << zdoReply->error();
|
||||||
|
if (zdoReply->zigbeeDeviceObjectStatus() == ZigbeeDeviceProfile::StatusNotSupported) {
|
||||||
|
// Not an error, merely a valid response code that this optional request is not supported.
|
||||||
|
reply->finishReply(ZigbeeReply::ErrorNoError);
|
||||||
|
} else {
|
||||||
|
reply->finishReply(ZigbeeReply::ErrorZigbeeError);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// qCDebug(dcZigbeeNode()) << "LQI response:" << zdoReply->responseData().toHex();
|
||||||
|
ZigbeeDeviceProfile::NeighborTable neighborTable = ZigbeeDeviceProfile::parseNeighborTable(zdoReply->responseData());
|
||||||
|
if (neighborTable.startIndex == 0) {
|
||||||
|
m_neighborTable = neighborTable;
|
||||||
|
} else if (m_neighborTable.records.count() == neighborTable.startIndex) {
|
||||||
|
m_neighborTable.records.append(neighborTable.records);
|
||||||
|
} else {
|
||||||
|
qCWarning(dcZigbeeNode()) << "Error processing neighbor table. Indices not matching. Starting from scratch.";
|
||||||
|
readNeighborTableChunk(reply, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_neighborTable.records.count() < m_neighborTable.tableSize) {
|
||||||
|
qCDebug(dcZigbeeNode) << "Neighbor table not complete yet. Fetching next chunk";
|
||||||
|
readNeighborTableChunk(reply, m_neighborTable.records.count());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<quint16> removedRecords = m_neighborTableRecords.keys();
|
||||||
|
foreach(const ZigbeeDeviceProfile::NeighborTableListRecord &record, m_neighborTable.records) {
|
||||||
|
qCDebug(dcZigbeeNode()).nospace() << "LQI neighbor for node: " << ZigbeeUtils::convertUint16ToHexString(m_shortAddress) << ": " << record;
|
||||||
|
removedRecords.removeAll(record.shortAddress);
|
||||||
|
if (m_neighborTableRecords.contains(record.shortAddress)) {
|
||||||
|
ZigbeeDeviceProfile::NeighborTableListRecord existingRecord = m_neighborTableRecords.value(record.shortAddress);
|
||||||
|
if (existingRecord.relationship != record.relationship
|
||||||
|
|| existingRecord.permitJoining != record.permitJoining
|
||||||
|
|| existingRecord.depth != record.depth
|
||||||
|
|| existingRecord.lqi != record.lqi) {
|
||||||
|
m_neighborTableRecords[record.shortAddress] = record;
|
||||||
|
emit neighborTableRecordsChanged();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_neighborTableRecords.insert(record.shortAddress, record);
|
||||||
|
emit neighborTableRecordsChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (quint16 removedRecord, removedRecords) {
|
||||||
|
m_neighborTableRecords.remove(removedRecord);
|
||||||
|
emit neighborTableRecordsChanged();
|
||||||
|
}
|
||||||
|
reply->finishReply(ZigbeeReply::ErrorNoError);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeNode::readRoutingTableChunk(ZigbeeReply *reply, quint8 startIndex)
|
||||||
|
{
|
||||||
|
ZigbeeDeviceObjectReply *zdoReply = deviceObject()->requestMgmtRtg(startIndex);
|
||||||
|
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, reply, [=](){
|
||||||
|
if (zdoReply->error() != ZigbeeDeviceObjectReply::ErrorNoError) {
|
||||||
|
qCWarning(dcZigbeeNode()) << "Failed to read routing table" << zdoReply->error();
|
||||||
|
if (zdoReply->zigbeeDeviceObjectStatus() == ZigbeeDeviceProfile::StatusNotSupported) {
|
||||||
|
// Not an error, merely a valid response code that this optional request is not supported.
|
||||||
|
reply->finishReply(ZigbeeReply::ErrorNoError);
|
||||||
|
} else {
|
||||||
|
reply->finishReply(ZigbeeReply::ErrorZigbeeError);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// qCDebug(dcZigbeeNetwork()) << "Routing table reply from" << ZigbeeUtils::convertUint16ToHexString(shortAddress()) << zdoReply->responseData().toHex();
|
||||||
|
|
||||||
|
ZigbeeDeviceProfile::RoutingTable routingTable = ZigbeeDeviceProfile::parseRoutingTable(zdoReply->responseData());
|
||||||
|
if (routingTable.startIndex == 0) {
|
||||||
|
m_routingTable = routingTable;
|
||||||
|
} else if (m_routingTable.records.count() == routingTable.startIndex) {
|
||||||
|
m_routingTable.records.append(routingTable.records);
|
||||||
|
} else {
|
||||||
|
qCWarning(dcZigbeeNode()) << "Error processing routing table. Indices not matching. Starting from scratch.";
|
||||||
|
readRoutingTableChunk(reply, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_routingTable.records.count() < m_routingTable.tableSize) {
|
||||||
|
qCDebug(dcZigbeeNode) << "Routing table not complete yet. Fetching next chunk";
|
||||||
|
readRoutingTableChunk(reply, m_routingTable.records.count());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<quint16> removedRecords = m_routingTableRecords.keys();
|
||||||
|
foreach(const ZigbeeDeviceProfile::RoutingTableListRecord &record, m_routingTable.records) {
|
||||||
|
qCDebug(dcZigbeeNode()).nospace() << "Route entry for node: " << ZigbeeUtils::convertUint16ToHexString(m_shortAddress) << ": " << record;
|
||||||
|
removedRecords.removeAll(record.destinationAddress);
|
||||||
|
if (m_routingTableRecords.contains(record.destinationAddress)) {
|
||||||
|
ZigbeeDeviceProfile::RoutingTableListRecord existingRecord = m_routingTableRecords.value(record.destinationAddress);
|
||||||
|
if (existingRecord.status != record.status
|
||||||
|
|| existingRecord.memoryConstrained != record.memoryConstrained
|
||||||
|
|| existingRecord.manyToOne != record.manyToOne
|
||||||
|
|| existingRecord.routeRecordRequired != record.routeRecordRequired
|
||||||
|
|| existingRecord.nextHopAddress != record.nextHopAddress) {
|
||||||
|
m_routingTableRecords[record.destinationAddress] = record;
|
||||||
|
emit routingTableRecordsChanged();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_routingTableRecords.insert(record.destinationAddress, record);
|
||||||
|
emit routingTableRecordsChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (quint16 removedRecord, removedRecords) {
|
||||||
|
m_routingTableRecords.remove(removedRecord);
|
||||||
|
emit routingTableRecordsChanged();
|
||||||
|
}
|
||||||
|
reply->finishReply(ZigbeeReply::ErrorNoError);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void ZigbeeNode::setupEndpointInternal(ZigbeeNodeEndpoint *endpoint)
|
void ZigbeeNode::setupEndpointInternal(ZigbeeNodeEndpoint *endpoint)
|
||||||
{
|
{
|
||||||
// Connect after initialization for out of spec nodes
|
// Connect after initialization for out of spec nodes
|
||||||
|
|||||||
@ -92,12 +92,16 @@ public:
|
|||||||
|
|
||||||
// Only available if fetched
|
// Only available if fetched
|
||||||
QList<ZigbeeDeviceProfile::BindingTableListRecord> bindingTableRecords() const;
|
QList<ZigbeeDeviceProfile::BindingTableListRecord> bindingTableRecords() const;
|
||||||
|
QList<ZigbeeDeviceProfile::NeighborTableListRecord> neighborTableRecords() const;
|
||||||
|
QList<ZigbeeDeviceProfile::RoutingTableListRecord> routingTableRecords() const;
|
||||||
|
|
||||||
// This method starts the node initialization phase (read descriptors and endpoints)
|
// This method starts the node initialization phase (read descriptors and endpoints)
|
||||||
void startInitialization();
|
void startInitialization();
|
||||||
|
|
||||||
ZigbeeReply *removeAllBindings();
|
ZigbeeReply *removeAllBindings();
|
||||||
ZigbeeReply *readBindingTableEntries();
|
ZigbeeReply *readBindingTableEntries();
|
||||||
|
ZigbeeReply *readLqiTableEntries();
|
||||||
|
ZigbeeReply *readRoutingTableEntries();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ZigbeeNode(ZigbeeNetwork *network, quint16 shortAddress, const ZigbeeAddress &extendedAddress, QObject *parent = nullptr);
|
ZigbeeNode(ZigbeeNetwork *network, quint16 shortAddress, const ZigbeeAddress &extendedAddress, QObject *parent = nullptr);
|
||||||
@ -126,6 +130,10 @@ private:
|
|||||||
bool m_powerDescriptorAvailable = false;
|
bool m_powerDescriptorAvailable = false;
|
||||||
|
|
||||||
QList<ZigbeeDeviceProfile::BindingTableListRecord> m_bindingTableRecords;
|
QList<ZigbeeDeviceProfile::BindingTableListRecord> m_bindingTableRecords;
|
||||||
|
QHash<quint16, ZigbeeDeviceProfile::NeighborTableListRecord> m_neighborTableRecords;
|
||||||
|
QHash<quint16, ZigbeeDeviceProfile::RoutingTableListRecord> m_routingTableRecords;
|
||||||
|
ZigbeeDeviceProfile::NeighborTable m_neighborTable; // Used internally to sync the table from the device in chunks
|
||||||
|
ZigbeeDeviceProfile::RoutingTable m_routingTable; // Used internally to sync the table from the device in chunks
|
||||||
|
|
||||||
void setState(State state);
|
void setState(State state);
|
||||||
void setReachable(bool reachable);
|
void setReachable(bool reachable);
|
||||||
@ -140,6 +148,8 @@ private:
|
|||||||
void initEndpoint(quint8 endpointId);
|
void initEndpoint(quint8 endpointId);
|
||||||
|
|
||||||
void removeNextBinding(ZigbeeReply *reply);
|
void removeNextBinding(ZigbeeReply *reply);
|
||||||
|
void readNeighborTableChunk(ZigbeeReply *reply, quint8 startIndex);
|
||||||
|
void readRoutingTableChunk(ZigbeeReply *reply, quint8 startIndex);
|
||||||
|
|
||||||
void setupEndpointInternal(ZigbeeNodeEndpoint *endpoint);
|
void setupEndpointInternal(ZigbeeNodeEndpoint *endpoint);
|
||||||
|
|
||||||
@ -151,6 +161,7 @@ private:
|
|||||||
|
|
||||||
void handleDataIndication(const Zigbee::ApsdeDataIndication &indication);
|
void handleDataIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||||
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nodeInitializationFailed();
|
void nodeInitializationFailed();
|
||||||
void stateChanged(State state);
|
void stateChanged(State state);
|
||||||
@ -162,6 +173,8 @@ signals:
|
|||||||
void versionChanged(const QString &version);
|
void versionChanged(const QString &version);
|
||||||
void reachableChanged(bool reachable);
|
void reachableChanged(bool reachable);
|
||||||
void bindingTableRecordsChanged();
|
void bindingTableRecordsChanged();
|
||||||
|
void neighborTableRecordsChanged();
|
||||||
|
void routingTableRecordsChanged();
|
||||||
void clusterAdded(ZigbeeCluster *cluster);
|
void clusterAdded(ZigbeeCluster *cluster);
|
||||||
void endpointClusterAttributeChanged(ZigbeeNodeEndpoint *endpoint, ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
|
void endpointClusterAttributeChanged(ZigbeeNodeEndpoint *endpoint, ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user