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)
|
||||
{
|
||||
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
|
||||
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtLqiRequest);
|
||||
@ -597,7 +597,7 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtLqi(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
|
||||
ZigbeeNetworkRequest request = buildZdoRequest(ZigbeeDeviceProfile::MgmtBindRequest);
|
||||
@ -637,6 +637,49 @@ ZigbeeDeviceObjectReply *ZigbeeDeviceObject::requestMgmtBind(quint8 startIndex)
|
||||
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 request;
|
||||
|
||||
@ -61,6 +61,7 @@ public:
|
||||
ZigbeeDeviceObjectReply *requestMgmtLeaveNetwork(bool rejoin = false, bool removeChildren = false);
|
||||
ZigbeeDeviceObjectReply *requestMgmtLqi(quint8 startIndex = 0x00);
|
||||
ZigbeeDeviceObjectReply *requestMgmtBind(quint8 startIndex = 0x00);
|
||||
ZigbeeDeviceObjectReply *requestMgmtRtg(quint8 startIndex = 0x00);
|
||||
|
||||
// TODO: write all requests
|
||||
|
||||
|
||||
@ -174,6 +174,67 @@ ZigbeeDeviceProfile::PowerDescriptor ZigbeeDeviceProfile::parsePowerDescriptor(q
|
||||
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)
|
||||
{
|
||||
QDataStream stream(adpu);
|
||||
@ -187,12 +248,12 @@ ZigbeeDeviceProfile::Adpu ZigbeeDeviceProfile::parseAdpu(const QByteArray &adpu)
|
||||
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() << deviceAdpu.status << ", ";
|
||||
debug.nospace() << ZigbeeUtils::convertUint16ToHexString(deviceAdpu.addressOfInterest) << ", ";
|
||||
debug.nospace() << "Payload: " << ZigbeeUtils::convertByteArrayToHexString(deviceAdpu.payload) << ")";
|
||||
debug.nospace() << "DeviceProfileAdpu(SQN: " << adpu.transactionSequenceNumber << ", ";
|
||||
debug.nospace() << adpu.status << ", ";
|
||||
debug.nospace() << ZigbeeUtils::convertUint16ToHexString(adpu.addressOfInterest) << ", ";
|
||||
debug.nospace() << "Payload: " << ZigbeeUtils::convertByteArrayToHexString(adpu.payload) << ")";
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
@ -273,3 +334,29 @@ QDebug operator<<(QDebug debug, const ZigbeeDeviceProfile::BindingTableListRecor
|
||||
}
|
||||
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)
|
||||
|
||||
enum Relationship {
|
||||
Parent,
|
||||
Child,
|
||||
Sibling
|
||||
RelationshipParent,
|
||||
RelationshipChild,
|
||||
RelationshipSibling,
|
||||
RelationshipNone,
|
||||
RelationshipPreviousChild
|
||||
};
|
||||
Q_ENUM(Relationship)
|
||||
|
||||
enum RouteStatus {
|
||||
RouteStatusActive,
|
||||
RouteStatusDiscoveryUnderway,
|
||||
RouteStatusDiscoveryFailed,
|
||||
RouteStatusInactive,
|
||||
RouteStatusValidationUnderway
|
||||
};
|
||||
Q_ENUM(RouteStatus)
|
||||
|
||||
enum PowerMode {
|
||||
PowerModeAlwaysOn,
|
||||
PowerModeOnPeriodically,
|
||||
@ -287,12 +298,48 @@ public:
|
||||
quint8 destinationEndpoint; // Only for destination address 0x03
|
||||
} 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 MacCapabilities parseMacCapabilities(quint8 macCapabilitiesFlag);
|
||||
static ServerMask parseServerMask(quint16 serverMaskFlag);
|
||||
static DescriptorCapabilities parseDescriptorCapabilities(quint8 descriptorCapabilitiesFlag);
|
||||
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);
|
||||
@ -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::PowerDescriptor &powerDescriptor);
|
||||
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
|
||||
|
||||
@ -53,7 +53,6 @@ ZigbeeNetwork::ZigbeeNetwork(const QUuid &networkUuid, QObject *parent) :
|
||||
|
||||
m_reachableRefreshTimer = new QTimer(this);
|
||||
m_reachableRefreshTimer->setInterval(120000);
|
||||
m_reachableRefreshTimer->setSingleShot(false);
|
||||
connect(m_reachableRefreshTimer, &QTimer::timeout, this, &ZigbeeNetwork::evaluateNodeReachableStates);
|
||||
|
||||
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()
|
||||
{
|
||||
qCDebug(dcZigbeeNetwork()) << this;
|
||||
@ -454,30 +463,43 @@ ZigbeeNode *ZigbeeNetwork::createNode(quint16 shortAddress, const ZigbeeAddress
|
||||
return node;
|
||||
}
|
||||
|
||||
void ZigbeeNetwork::evaluateNextNodeReachableState()
|
||||
void ZigbeeNetwork::fetchNextNodeLqiTable()
|
||||
{
|
||||
if (m_reachableRefreshAddresses.isEmpty())
|
||||
return;
|
||||
|
||||
ZigbeeNode *node = getZigbeeNode(m_reachableRefreshAddresses.takeFirst());
|
||||
ZigbeeNode *node = nullptr;
|
||||
while (!node && !m_refreshNeighborTableAddresses.isEmpty()) {
|
||||
node = getZigbeeNode(m_refreshNeighborTableAddresses.takeFirst());
|
||||
}
|
||||
while (!node && !m_reachableRefreshAddresses.isEmpty()) {
|
||||
node = getZigbeeNode(m_reachableRefreshAddresses.takeFirst());
|
||||
}
|
||||
if (!node) {
|
||||
// Not does not exit any more...continue
|
||||
evaluateNextNodeReachableState();
|
||||
// Nothing to do...
|
||||
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
|
||||
ZigbeeDeviceObjectReply *zdoReply = node->deviceObject()->requestNetworkAddress();
|
||||
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, this, [=](){
|
||||
if (zdoReply->error()) {
|
||||
qCWarning(dcZigbeeNetwork()) << node << "seems not to be reachable" << zdoReply->error();
|
||||
ZigbeeReply *reply = node->readLqiTableEntries();
|
||||
connect(reply, &ZigbeeReply::finished, this, [=](){
|
||||
if (reply->error()) {
|
||||
qCWarning(dcZigbeeNetwork()) << node << "seems not to be reachable" << reply->error();
|
||||
setNodeReachable(node, false);
|
||||
} else {
|
||||
setNodeReachable(node, true);
|
||||
}
|
||||
|
||||
// Give some time for other requests to be processed
|
||||
QTimer::singleShot(5000, this, &ZigbeeNetwork::evaluateNextNodeReachableState);
|
||||
ZigbeeReply *reply = node->readRoutingTableEntries();
|
||||
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) {
|
||||
printNetwork();
|
||||
|
||||
}
|
||||
emit stateChanged(m_state);
|
||||
}
|
||||
@ -927,29 +950,35 @@ void ZigbeeNetwork::onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const
|
||||
|
||||
void ZigbeeNetwork::evaluateNodeReachableStates()
|
||||
{
|
||||
qCDebug(dcZigbeeNetwork()) << "Evaluate reachable state of nodes";
|
||||
m_reachableRefreshAddresses.clear();
|
||||
qCDebug(dcZigbeeNetwork()) << "Evaluating reachable state of 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;
|
||||
}
|
||||
|
||||
if (node->macCapabilities().receiverOnWhenIdle && node->shortAddress() != 0x0000) {
|
||||
if (node->macCapabilities().receiverOnWhenIdle) {
|
||||
|
||||
// Lets send a request to all things which are not reachable
|
||||
if (!node->reachable()) {
|
||||
qCDebug(dcZigbeeNetwork()) << node << "enqueue evaluating reachable state";
|
||||
m_reachableRefreshAddresses.append(node->extendedAddress());
|
||||
if (!m_reachableRefreshAddresses.contains(node->extendedAddress())) {
|
||||
qCDebug(dcZigbeeNetwork()) << node << "is not reachable. Scheduling LQI request.";
|
||||
m_reachableRefreshAddresses.append(node->extendedAddress());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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.";
|
||||
// 10 min = 10 * 60 * 1000 = 600000 ms
|
||||
if (msSinceLastSeen > 600000) {
|
||||
qCDebug(dcZigbeeNetwork()) << node << "enqueue evaluating reachable state";
|
||||
if (msSinceLastSeen > 600000 && !m_reachableRefreshAddresses.contains(node->extendedAddress())) {
|
||||
qCDebug(dcZigbeeNetwork()) << node << "has not been seen in" << (msSinceLastSeen / 1000 / 60) << "minutes. Scheduling LQI request.";
|
||||
m_reachableRefreshAddresses.append(node->extendedAddress());
|
||||
}
|
||||
} else {
|
||||
@ -966,7 +995,7 @@ void ZigbeeNetwork::evaluateNodeReachableStates()
|
||||
}
|
||||
}
|
||||
|
||||
evaluateNextNodeReachableState();
|
||||
fetchNextNodeLqiTable();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, ZigbeeNetwork *network)
|
||||
|
||||
@ -132,6 +132,8 @@ public:
|
||||
|
||||
void removeZigbeeNode(const ZigbeeAddress &address);
|
||||
|
||||
void refreshNeighborTable();
|
||||
|
||||
private:
|
||||
QUuid m_networkUuid;
|
||||
State m_state = StateUninitialized;
|
||||
@ -185,7 +187,8 @@ protected:
|
||||
|
||||
QTimer *m_reachableRefreshTimer = nullptr;
|
||||
QList<ZigbeeAddress> m_reachableRefreshAddresses;
|
||||
void evaluateNextNodeReachableState();
|
||||
QList<ZigbeeAddress> m_refreshNeighborTableAddresses;
|
||||
void fetchNextNodeLqiTable();
|
||||
|
||||
void setPermitJoiningState(bool permitJoiningEnabled, quint8 duration = 0);
|
||||
|
||||
|
||||
@ -147,6 +147,16 @@ QList<ZigbeeDeviceProfile::BindingTableListRecord> ZigbeeNode::bindingTableRecor
|
||||
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)
|
||||
{
|
||||
if (m_state == state)
|
||||
@ -264,6 +274,20 @@ ZigbeeReply *ZigbeeNode::readBindingTableEntries()
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
// Connect after initialization for out of spec nodes
|
||||
|
||||
@ -92,12 +92,16 @@ public:
|
||||
|
||||
// Only available if fetched
|
||||
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)
|
||||
void startInitialization();
|
||||
|
||||
ZigbeeReply *removeAllBindings();
|
||||
ZigbeeReply *readBindingTableEntries();
|
||||
ZigbeeReply *readLqiTableEntries();
|
||||
ZigbeeReply *readRoutingTableEntries();
|
||||
|
||||
private:
|
||||
ZigbeeNode(ZigbeeNetwork *network, quint16 shortAddress, const ZigbeeAddress &extendedAddress, QObject *parent = nullptr);
|
||||
@ -126,6 +130,10 @@ private:
|
||||
bool m_powerDescriptorAvailable = false;
|
||||
|
||||
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 setReachable(bool reachable);
|
||||
@ -140,6 +148,8 @@ private:
|
||||
void initEndpoint(quint8 endpointId);
|
||||
|
||||
void removeNextBinding(ZigbeeReply *reply);
|
||||
void readNeighborTableChunk(ZigbeeReply *reply, quint8 startIndex);
|
||||
void readRoutingTableChunk(ZigbeeReply *reply, quint8 startIndex);
|
||||
|
||||
void setupEndpointInternal(ZigbeeNodeEndpoint *endpoint);
|
||||
|
||||
@ -151,6 +161,7 @@ private:
|
||||
|
||||
void handleDataIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
|
||||
signals:
|
||||
void nodeInitializationFailed();
|
||||
void stateChanged(State state);
|
||||
@ -162,6 +173,8 @@ signals:
|
||||
void versionChanged(const QString &version);
|
||||
void reachableChanged(bool reachable);
|
||||
void bindingTableRecordsChanged();
|
||||
void neighborTableRecordsChanged();
|
||||
void routingTableRecordsChanged();
|
||||
void clusterAdded(ZigbeeCluster *cluster);
|
||||
void endpointClusterAttributeChanged(ZigbeeNodeEndpoint *endpoint, ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user