Add network node evaluation and readd node if nwk address changed

This commit is contained in:
Simon Stürz 2020-11-30 17:44:12 +01:00
parent 9e3bbc55ca
commit 4e8254fcb9
14 changed files with 160 additions and 54 deletions

Binary file not shown.

View File

@ -671,8 +671,15 @@ void ZigbeeNetworkDeconz::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress
} }
if (hasNode(ieeeAddress)) { if (hasNode(ieeeAddress)) {
qCWarning(dcZigbeeNetwork()) << "Already known device announced. FIXME: Ignoring announcement" << ieeeAddress.toString(); ZigbeeNode *node = getZigbeeNode(ieeeAddress);
return; if (shortAddress == node->shortAddress()) {
qCDebug(dcZigbeeNetwork()) << "Already known device announced and is reachable again" << node;
setNodeReachable(node, true);
return;
} else {
qCDebug(dcZigbeeNetwork()) << "Already known device announced with different network address. Removing node and reinitialize...";
removeNode(node);
}
} }
ZigbeeNode *node = createNode(shortAddress, ieeeAddress, macCapabilities, this); ZigbeeNode *node = createNode(shortAddress, ieeeAddress, macCapabilities, this);

View File

@ -88,33 +88,13 @@ ZigbeeNetworkReply *ZigbeeNetworkNxp::sendRequest(const ZigbeeNetworkRequest &re
// Finish the reply right the way if the network is offline // Finish the reply right the way if the network is offline
if (!m_controller->available() || state() == ZigbeeNetwork::StateOffline) { if (!m_controller->available() || state() == ZigbeeNetwork::StateOffline) {
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorNetworkOffline); finishReplyInternally(reply, ZigbeeNetworkReply::ErrorNetworkOffline);
return reply; return reply;
} }
ZigbeeInterfaceNxpReply *interfaceReply = m_controller->requestSendRequest(request); // Enqueu reply and send next one if we have enouth capacity
connect(interfaceReply, &ZigbeeInterfaceNxpReply::finished, reply, [this, reply, interfaceReply](){ m_replyQueue.enqueue(reply);
if (interfaceReply->status() != Nxp::StatusSuccess) { sendNextReply();
qCWarning(dcZigbeeController()) << "Could send request to controller. SQN:" << interfaceReply->sequenceNumber() << interfaceReply->status();
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorInterfaceError);
return;
}
// Note: this is a special case for nxp coordinator requests, they don't send a confirm because the request will not be sent trough the network
if (reply->request().destinationShortAddress() == 0x0000 && reply->request().profileId() == Zigbee::ZigbeeProfileDevice) {
qCDebug(dcZigbeeNetwork()) << "Finish reply since there will be no CONFIRM for local node requests.";
finishNetworkReply(reply);
return;
}
quint8 networkRequestId = interfaceReply->responseData().at(0);
//qCDebug(dcZigbeeNetwork()) << "Request has network SQN" << networkRequestId;
reply->request().setRequestId(networkRequestId);
//qCWarning(dcZigbeeNetwork()) << "#### Insert network reply" << reply << "ID:" << networkRequestId << "Current reply count" << m_pendingReplies.count();
m_pendingReplies.insert(networkRequestId, reply);
// The request has been sent successfully to the device, start the timeout timer now
startWaitingReply(reply);
});
return reply; return reply;
} }
@ -191,6 +171,52 @@ void ZigbeeNetworkNxp::setPermitJoining(quint8 duration, quint16 address)
}); });
} }
void ZigbeeNetworkNxp::sendNextReply()
{
if (m_replyQueue.isEmpty())
return;
if (m_currentReply)
return;
ZigbeeNetworkReply *reply = m_replyQueue.dequeue();
ZigbeeInterfaceNxpReply *interfaceReply = m_controller->requestSendRequest(reply->request());
connect(interfaceReply, &ZigbeeInterfaceNxpReply::finished, reply, [this, reply, interfaceReply](){
if (interfaceReply->status() != Nxp::StatusSuccess) {
qCWarning(dcZigbeeController()) << "Could send request to controller. SQN:" << interfaceReply->sequenceNumber() << interfaceReply->status();
finishReplyInternally(reply, ZigbeeNetworkReply::ErrorInterfaceError);
return;
}
// Note: this is a special case for nxp coordinator requests, they don't send a confirm because the request will not be sent trough the network
if (reply->request().destinationShortAddress() == 0x0000 && reply->request().profileId() == Zigbee::ZigbeeProfileDevice) {
qCDebug(dcZigbeeNetwork()) << "Finish reply since there will be no CONFIRM for local node requests.";
finishReplyInternally(reply);
return;
}
quint8 networkRequestId = interfaceReply->responseData().at(0);
//qCDebug(dcZigbeeNetwork()) << "Request has network SQN" << networkRequestId;
reply->request().setRequestId(networkRequestId);
//qCWarning(dcZigbeeNetwork()) << "#### Insert network reply" << reply << "ID:" << networkRequestId << "Current reply count" << m_pendingReplies.count();
m_pendingReplies.insert(networkRequestId, reply);
// The request has been sent successfully to the device, start the timeout timer now
startWaitingReply(reply);
});
}
void ZigbeeNetworkNxp::finishReplyInternally(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error)
{
finishNetworkReply(reply, error);
if (m_currentReply == reply) {
m_currentReply = nullptr;
}
sendNextReply();
}
ZigbeeNetworkReply *ZigbeeNetworkNxp::requestSetPermitJoin(quint16 shortAddress, quint8 duration) ZigbeeNetworkReply *ZigbeeNetworkNxp::requestSetPermitJoin(quint16 shortAddress, quint8 duration)
{ {
// Get the power descriptor // Get the power descriptor
@ -410,22 +436,22 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr
connect(coordinatorNode, &ZigbeeNode::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){ connect(coordinatorNode, &ZigbeeNode::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){
if (state == ZigbeeNode::StateInitialized) { if (state == ZigbeeNode::StateInitialized) {
qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode; qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode;
// ZigbeeClusterGroups *groupsCluster = coordinatorNode->getEndpoint(0x01)->inputCluster<ZigbeeClusterGroups>(ZigbeeClusterLibrary::ClusterIdGroups); // ZigbeeClusterGroups *groupsCluster = coordinatorNode->getEndpoint(0x01)->inputCluster<ZigbeeClusterGroups>(ZigbeeClusterLibrary::ClusterIdGroups);
// if (!groupsCluster) { // if (!groupsCluster) {
// qCWarning(dcZigbeeNetwork()) << "Failed to get groups cluster from coordinator. The coordinator will not be in default group 0x0000"; // qCWarning(dcZigbeeNetwork()) << "Failed to get groups cluster from coordinator. The coordinator will not be in default group 0x0000";
// setState(StateRunning); // setState(StateRunning);
// setPermitJoining(0); // setPermitJoining(0);
// return; // return;
// } // }
// ZigbeeClusterReply *reply = groupsCluster->addGroup(0x0000, "Default"); // ZigbeeClusterReply *reply = groupsCluster->addGroup(0x0000, "Default");
// connect(reply, &ZigbeeClusterReply::finished, this, [=](){ // connect(reply, &ZigbeeClusterReply::finished, this, [=](){
// if (reply->error() != ZigbeeClusterReply::ErrorNoError) { // if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
// qCWarning(dcZigbeeNetwork()) << "Failed to add coordinator to default group 0x0000. The coordinator will not be in default group 0x0000"; // qCWarning(dcZigbeeNetwork()) << "Failed to add coordinator to default group 0x0000. The coordinator will not be in default group 0x0000";
// } // }
// setState(StateRunning); // setState(StateRunning);
// setPermitJoining(0); // setPermitJoining(0);
// }); // });
setState(StateRunning); setState(StateRunning);
setPermitJoining(0); setPermitJoining(0);
@ -581,8 +607,15 @@ void ZigbeeNetworkNxp::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress iee
} }
if (hasNode(ieeeAddress)) { if (hasNode(ieeeAddress)) {
qCWarning(dcZigbeeNetwork()) << "Already known device announced. FIXME: Ignoring announcement" << ieeeAddress.toString(); ZigbeeNode *node = getZigbeeNode(ieeeAddress);
return; if (shortAddress == node->shortAddress()) {
qCDebug(dcZigbeeNetwork()) << "Already known device announced and is reachable again" << node;
setNodeReachable(node, true);
return;
} else {
qCDebug(dcZigbeeNetwork()) << "Already known device announced with different network address. Removing node and reinitialize...";
removeNode(node);
}
} }
ZigbeeNode *node = createNode(shortAddress, ieeeAddress, macCapabilities, this); ZigbeeNode *node = createNode(shortAddress, ieeeAddress, macCapabilities, this);

View File

@ -51,9 +51,15 @@ public:
private: private:
ZigbeeBridgeControllerNxp *m_controller = nullptr; ZigbeeBridgeControllerNxp *m_controller = nullptr;
bool m_networkRunning = false; bool m_networkRunning = false;
QHash<quint8, ZigbeeNetworkReply *> m_pendingReplies;
int m_reconnectCounter = 0;
QHash<quint8, ZigbeeNetworkReply *> m_pendingReplies;
QQueue<ZigbeeNetworkReply *> m_replyQueue;
ZigbeeNetworkReply *m_currentReply = nullptr;
void sendNextReply();
void finishReplyInternally(ZigbeeNetworkReply *reply, ZigbeeNetworkReply::Error error = ZigbeeNetworkReply::ErrorNoError);
int m_reconnectCounter = 0;
bool processVersionReply(ZigbeeInterfaceNxpReply *reply); bool processVersionReply(ZigbeeInterfaceNxpReply *reply);
// ZDO // ZDO

View File

@ -323,7 +323,8 @@ public:
ZigbeeNwkLayerStatusRouteDiscoveryFailed = 0xd0, ZigbeeNwkLayerStatusRouteDiscoveryFailed = 0xd0,
ZigbeeNwkLayerStatusRouteError = 0xd1, ZigbeeNwkLayerStatusRouteError = 0xd1,
ZigbeeNwkLayerStatusBtTableFull = 0xd2, ZigbeeNwkLayerStatusBtTableFull = 0xd2,
ZigbeeNwkLayerStatusFrameNotBuffered = 0xd3 ZigbeeNwkLayerStatusFrameNotBuffered = 0xd3,
ZigbeeNwkLayerStatusFrameBuffered = 0xd4
}; };
Q_ENUM(ZigbeeNwkLayerStatus) Q_ENUM(ZigbeeNwkLayerStatus)

View File

@ -53,13 +53,21 @@ ZigbeeNetwork::ZigbeeNetwork(const QUuid &networkUuid, QObject *parent) :
}); });
m_reachableRefreshTimer = new QTimer(this);
m_reachableRefreshTimer->setInterval(300000);
m_reachableRefreshTimer->setSingleShot(false);
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){
if (state != ZigbeeNetwork::StateRunning) { if (state == ZigbeeNetwork::StateRunning) {
evaluateNodeReachableStates();
m_reachableRefreshTimer->start();
} else {
foreach (ZigbeeNode *node, m_nodes) { foreach (ZigbeeNode *node, m_nodes) {
node->setReachable(false); node->setReachable(false);
} }
m_reachableRefreshTimer->stop();
} }
}); });
} }
@ -549,6 +557,11 @@ void ZigbeeNetwork::removeUninitializedNode(ZigbeeNode *node)
node->deleteLater(); node->deleteLater();
} }
void ZigbeeNetwork::setNodeReachable(ZigbeeNode *node, bool reachable)
{
node->setReachable(reachable);
}
void ZigbeeNetwork::setState(ZigbeeNetwork::State state) void ZigbeeNetwork::setState(ZigbeeNetwork::State state)
{ {
if (m_state == state) if (m_state == state)
@ -599,7 +612,7 @@ void ZigbeeNetwork::setReplyResponseError(ZigbeeNetworkReply *reply, Zigbee::Zig
} else { } else {
// There has been an error while transporting the request to the device // There has been an error while transporting the request to the device
// Note: if the APS status is >= 0xc1, it has to interpreted as NWK layer error // Note: if the APS status is >= 0xc1, it has to interpreted as NWK layer error
if (zigbeeApsStatus >= 0xc1 && zigbeeApsStatus <= 0xd3) { if (zigbeeApsStatus >= 0xc1 && zigbeeApsStatus <= 0xd4) {
reply->m_zigbeeNwkStatus = static_cast<Zigbee::ZigbeeNwkLayerStatus>(static_cast<quint8>(zigbeeApsStatus)); reply->m_zigbeeNwkStatus = static_cast<Zigbee::ZigbeeNwkLayerStatus>(static_cast<quint8>(zigbeeApsStatus));
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorZigbeeNwkStatusError); finishNetworkReply(reply, ZigbeeNetworkReply::ErrorZigbeeNwkStatusError);
} else if (zigbeeApsStatus >= 0xE0 && zigbeeApsStatus <= 0xF4) { } else if (zigbeeApsStatus >= 0xE0 && zigbeeApsStatus <= 0xF4) {
@ -662,6 +675,32 @@ void ZigbeeNetwork::onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const
m_database->saveAttribute(cluster, attribute); m_database->saveAttribute(cluster, attribute);
} }
void ZigbeeNetwork::evaluateNodeReachableStates()
{
qCDebug(dcZigbeeNetwork()) << "Evaluate reachable state of nodes";
foreach (ZigbeeNode *node, m_nodes) {
if (node->macCapabilities().receiverOnWhenIdle && node->shortAddress() != 0x0000) {
ZigbeeDeviceObjectReply *zdoReply = node->deviceObject()->requestMgmtLqi();
connect(zdoReply, &ZigbeeDeviceObjectReply::finished, this, [=](){
if (zdoReply->error()) {
qCWarning(dcZigbeeNetwork()) << node << "seems not to be reachable" << zdoReply->error();
setNodeReachable(node, false);
}
});
} else {
// Note: sleeping devices should send some message within 6 hours,
// otherwise the device might not be reachable any more
int msSinceLastSeen = node->lastSeen().msecsTo(QDateTime::currentDateTimeUtc());
qCDebug(dcZigbeeNetwork()) << node << "last seen" << QTime::fromMSecsSinceStartOfDay(msSinceLastSeen).toString();
if (msSinceLastSeen < 1000*60*60*6) {
setNodeReachable(node, true);
} else {
setNodeReachable(node, false);
}
}
}
}
QDebug operator<<(QDebug debug, ZigbeeNetwork *network) QDebug operator<<(QDebug debug, ZigbeeNetwork *network)
{ {
debug.nospace().noquote() << "ZigbeeNetwork(" << network->macAddress().toString() << ", " debug.nospace().noquote() << "ZigbeeNetwork(" << network->macAddress().toString() << ", "

View File

@ -175,6 +175,8 @@ protected:
quint8 m_permitJoiningDuration = 120; quint8 m_permitJoiningDuration = 120;
quint8 m_permitJoiningRemaining = 0; quint8 m_permitJoiningRemaining = 0;
QTimer *m_reachableRefreshTimer = nullptr;
void setPermitJoiningEnabled(bool permitJoiningEnabled); void setPermitJoiningEnabled(bool permitJoiningEnabled);
void setPermitJoiningDuration(quint8 duration); void setPermitJoiningDuration(quint8 duration);
void setPermitJoiningRemaining(quint8 remaining); void setPermitJoiningRemaining(quint8 remaining);
@ -188,6 +190,8 @@ protected:
void removeNode(ZigbeeNode *node); void removeNode(ZigbeeNode *node);
void removeUninitializedNode(ZigbeeNode *node); void removeUninitializedNode(ZigbeeNode *node);
void setNodeReachable(ZigbeeNode *node, bool reachable);
void setState(State state); void setState(State state);
void setError(Error error); void setError(Error error);
@ -227,6 +231,7 @@ signals:
private slots: private slots:
void onNodeStateChanged(ZigbeeNode::State state); void onNodeStateChanged(ZigbeeNode::State state);
void onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute); void onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
void evaluateNodeReachableStates();
public slots: public slots:
virtual void startNetwork() = 0; virtual void startNetwork() = 0;

View File

@ -244,7 +244,7 @@ void ZigbeeNode::initNodeDescriptor()
m_requestRetry++; m_requestRetry++;
if (m_requestRetry < 3) { if (m_requestRetry < 3) {
qCDebug(dcZigbeeNode()) << "Retry to request node descriptor" << m_requestRetry << "/" << "3"; qCDebug(dcZigbeeNode()) << "Retry to request node descriptor" << m_requestRetry << "/" << "3";
initNodeDescriptor(); QTimer::singleShot(1000, this, [=](){ initNodeDescriptor(); });
} else { } else {
qCWarning(dcZigbeeNode()) << "Failed to read node descriptor from" << this << "after 3 attempts. Giving up."; qCWarning(dcZigbeeNode()) << "Failed to read node descriptor from" << this << "after 3 attempts. Giving up.";
m_requestRetry = 0; m_requestRetry = 0;
@ -273,7 +273,7 @@ void ZigbeeNode::initPowerDescriptor()
if (m_requestRetry < 3) { if (m_requestRetry < 3) {
m_requestRetry++; m_requestRetry++;
qCDebug(dcZigbeeNode()) << "Retry to request power descriptor from" << this << m_requestRetry << "/" << "3 attempts."; qCDebug(dcZigbeeNode()) << "Retry to request power descriptor from" << this << m_requestRetry << "/" << "3 attempts.";
initPowerDescriptor(); QTimer::singleShot(1000, this, [=](){ initPowerDescriptor(); });
} else { } else {
qCWarning(dcZigbeeNode()) << "Failed to read power descriptor from" << this << "after 3 attempts. Giving up."; qCWarning(dcZigbeeNode()) << "Failed to read power descriptor from" << this << "after 3 attempts. Giving up.";
m_requestRetry = 0; m_requestRetry = 0;
@ -305,7 +305,7 @@ void ZigbeeNode::initEndpoints()
if (m_requestRetry < 3) { if (m_requestRetry < 3) {
m_requestRetry++; m_requestRetry++;
qCDebug(dcZigbeeNode()) << "Retry to request active endpoints from" << this << m_requestRetry << "/" << "3 attempts."; qCDebug(dcZigbeeNode()) << "Retry to request active endpoints from" << this << m_requestRetry << "/" << "3 attempts.";
initEndpoints(); QTimer::singleShot(1000, this, [=](){ initEndpoints(); });
} else { } else {
qCWarning(dcZigbeeNode()) << "Failed to read active endpoints from" << this << "after 3 attempts. Giving up."; qCWarning(dcZigbeeNode()) << "Failed to read active endpoints from" << this << "after 3 attempts. Giving up.";
m_requestRetry = 0; m_requestRetry = 0;
@ -355,7 +355,7 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
if (m_requestRetry < 3) { if (m_requestRetry < 3) {
m_requestRetry++; m_requestRetry++;
qCDebug(dcZigbeeNode()) << "Retry to request simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << m_requestRetry << "/" << "3 attempts."; qCDebug(dcZigbeeNode()) << "Retry to request simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << m_requestRetry << "/" << "3 attempts.";
initEndpoint(endpointId); QTimer::singleShot(1000, this, [=](){ initEndpoint(endpointId); });
} else { } else {
qCWarning(dcZigbeeNode()) << "Failed to read simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << "after 3 attempts. Giving up."; qCWarning(dcZigbeeNode()) << "Failed to read simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << "after 3 attempts. Giving up.";
m_requestRetry = 0; m_requestRetry = 0;
@ -721,7 +721,19 @@ QDebug operator<<(QDebug debug, ZigbeeNode *node)
{ {
debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress()); debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress());
debug.nospace().noquote() << ", " << node->extendedAddress().toString(); debug.nospace().noquote() << ", " << node->extendedAddress().toString();
debug.nospace().noquote() << ", RX on:" << node->macCapabilities().receiverOnWhenIdle; switch (node->nodeDescriptor().nodeType) {
case ZigbeeDeviceProfile::NodeTypeCoordinator:
debug.nospace().noquote() << ", Coordinator";
break;
case ZigbeeDeviceProfile::NodeTypeRouter:
debug.nospace().noquote() << ", Router";
break;
case ZigbeeDeviceProfile::NodeTypeEndDevice:
debug.nospace().noquote() << ", End device";
break;
}
debug.nospace().noquote() << ", RxOn:" << node->macCapabilities().receiverOnWhenIdle;
debug.nospace().noquote() << ")"; debug.nospace().noquote() << ")";
return debug.space().quote(); return debug.space().quote();
} }

View File

@ -56,6 +56,9 @@ public:
Q_ENUM(State) Q_ENUM(State)
State state() const; State state() const;
// Note: For sleepy devices this indicates best effort.
// If a device does not send any data within 6h, it will be assumed to no reachable
bool reachable() const; bool reachable() const;
QUuid networkUuid() const; QUuid networkUuid() const;