Add network node evaluation and readd node if nwk address changed
This commit is contained in:
parent
9e3bbc55ca
commit
4e8254fcb9
Binary file not shown.
Binary file not shown.
BIN
docs/nxp/ZigBee 3.0 Device User Guide - JN-UG-3114.pdf
Normal file
BIN
docs/nxp/ZigBee 3.0 Device User Guide - JN-UG-3114.pdf
Normal file
Binary file not shown.
BIN
docs/nxp/ZigBee Cluster Library User Guide - JN-UG-3115.pdf
Normal file
BIN
docs/nxp/ZigBee Cluster Library User Guide - JN-UG-3115.pdf
Normal file
Binary file not shown.
BIN
docs/nxp/ZigBee Green Power User Guide - JN-UG-3119.pdf
Normal file
BIN
docs/nxp/ZigBee Green Power User Guide - JN-UG-3119.pdf
Normal file
Binary file not shown.
@ -671,8 +671,15 @@ void ZigbeeNetworkDeconz::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress
|
||||
}
|
||||
|
||||
if (hasNode(ieeeAddress)) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Already known device announced. FIXME: Ignoring announcement" << ieeeAddress.toString();
|
||||
return;
|
||||
ZigbeeNode *node = getZigbeeNode(ieeeAddress);
|
||||
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);
|
||||
|
||||
@ -88,33 +88,13 @@ ZigbeeNetworkReply *ZigbeeNetworkNxp::sendRequest(const ZigbeeNetworkRequest &re
|
||||
|
||||
// Finish the reply right the way if the network is offline
|
||||
if (!m_controller->available() || state() == ZigbeeNetwork::StateOffline) {
|
||||
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorNetworkOffline);
|
||||
finishReplyInternally(reply, ZigbeeNetworkReply::ErrorNetworkOffline);
|
||||
return reply;
|
||||
}
|
||||
|
||||
ZigbeeInterfaceNxpReply *interfaceReply = m_controller->requestSendRequest(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();
|
||||
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);
|
||||
});
|
||||
// Enqueu reply and send next one if we have enouth capacity
|
||||
m_replyQueue.enqueue(reply);
|
||||
sendNextReply();
|
||||
|
||||
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)
|
||||
{
|
||||
// Get the power descriptor
|
||||
@ -410,22 +436,22 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr
|
||||
connect(coordinatorNode, &ZigbeeNode::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){
|
||||
if (state == ZigbeeNode::StateInitialized) {
|
||||
qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode;
|
||||
// ZigbeeClusterGroups *groupsCluster = coordinatorNode->getEndpoint(0x01)->inputCluster<ZigbeeClusterGroups>(ZigbeeClusterLibrary::ClusterIdGroups);
|
||||
// if (!groupsCluster) {
|
||||
// qCWarning(dcZigbeeNetwork()) << "Failed to get groups cluster from coordinator. The coordinator will not be in default group 0x0000";
|
||||
// setState(StateRunning);
|
||||
// setPermitJoining(0);
|
||||
// return;
|
||||
// }
|
||||
// ZigbeeClusterGroups *groupsCluster = coordinatorNode->getEndpoint(0x01)->inputCluster<ZigbeeClusterGroups>(ZigbeeClusterLibrary::ClusterIdGroups);
|
||||
// if (!groupsCluster) {
|
||||
// qCWarning(dcZigbeeNetwork()) << "Failed to get groups cluster from coordinator. The coordinator will not be in default group 0x0000";
|
||||
// setState(StateRunning);
|
||||
// setPermitJoining(0);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// ZigbeeClusterReply *reply = groupsCluster->addGroup(0x0000, "Default");
|
||||
// connect(reply, &ZigbeeClusterReply::finished, this, [=](){
|
||||
// if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||
// qCWarning(dcZigbeeNetwork()) << "Failed to add coordinator to default group 0x0000. The coordinator will not be in default group 0x0000";
|
||||
// }
|
||||
// setState(StateRunning);
|
||||
// setPermitJoining(0);
|
||||
// });
|
||||
// ZigbeeClusterReply *reply = groupsCluster->addGroup(0x0000, "Default");
|
||||
// connect(reply, &ZigbeeClusterReply::finished, this, [=](){
|
||||
// if (reply->error() != ZigbeeClusterReply::ErrorNoError) {
|
||||
// qCWarning(dcZigbeeNetwork()) << "Failed to add coordinator to default group 0x0000. The coordinator will not be in default group 0x0000";
|
||||
// }
|
||||
// setState(StateRunning);
|
||||
// setPermitJoining(0);
|
||||
// });
|
||||
|
||||
setState(StateRunning);
|
||||
setPermitJoining(0);
|
||||
@ -581,8 +607,15 @@ void ZigbeeNetworkNxp::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress iee
|
||||
}
|
||||
|
||||
if (hasNode(ieeeAddress)) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Already known device announced. FIXME: Ignoring announcement" << ieeeAddress.toString();
|
||||
return;
|
||||
ZigbeeNode *node = getZigbeeNode(ieeeAddress);
|
||||
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);
|
||||
|
||||
@ -51,9 +51,15 @@ public:
|
||||
private:
|
||||
ZigbeeBridgeControllerNxp *m_controller = nullptr;
|
||||
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);
|
||||
|
||||
// ZDO
|
||||
|
||||
@ -323,7 +323,8 @@ public:
|
||||
ZigbeeNwkLayerStatusRouteDiscoveryFailed = 0xd0,
|
||||
ZigbeeNwkLayerStatusRouteError = 0xd1,
|
||||
ZigbeeNwkLayerStatusBtTableFull = 0xd2,
|
||||
ZigbeeNwkLayerStatusFrameNotBuffered = 0xd3
|
||||
ZigbeeNwkLayerStatusFrameNotBuffered = 0xd3,
|
||||
ZigbeeNwkLayerStatusFrameBuffered = 0xd4
|
||||
};
|
||||
Q_ENUM(ZigbeeNwkLayerStatus)
|
||||
|
||||
|
||||
@ -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){
|
||||
if (state != ZigbeeNetwork::StateRunning) {
|
||||
if (state == ZigbeeNetwork::StateRunning) {
|
||||
evaluateNodeReachableStates();
|
||||
m_reachableRefreshTimer->start();
|
||||
} else {
|
||||
foreach (ZigbeeNode *node, m_nodes) {
|
||||
node->setReachable(false);
|
||||
}
|
||||
m_reachableRefreshTimer->stop();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -549,6 +557,11 @@ void ZigbeeNetwork::removeUninitializedNode(ZigbeeNode *node)
|
||||
node->deleteLater();
|
||||
}
|
||||
|
||||
void ZigbeeNetwork::setNodeReachable(ZigbeeNode *node, bool reachable)
|
||||
{
|
||||
node->setReachable(reachable);
|
||||
}
|
||||
|
||||
void ZigbeeNetwork::setState(ZigbeeNetwork::State state)
|
||||
{
|
||||
if (m_state == state)
|
||||
@ -599,7 +612,7 @@ void ZigbeeNetwork::setReplyResponseError(ZigbeeNetworkReply *reply, Zigbee::Zig
|
||||
} else {
|
||||
// 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
|
||||
if (zigbeeApsStatus >= 0xc1 && zigbeeApsStatus <= 0xd3) {
|
||||
if (zigbeeApsStatus >= 0xc1 && zigbeeApsStatus <= 0xd4) {
|
||||
reply->m_zigbeeNwkStatus = static_cast<Zigbee::ZigbeeNwkLayerStatus>(static_cast<quint8>(zigbeeApsStatus));
|
||||
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorZigbeeNwkStatusError);
|
||||
} else if (zigbeeApsStatus >= 0xE0 && zigbeeApsStatus <= 0xF4) {
|
||||
@ -662,6 +675,32 @@ void ZigbeeNetwork::onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const
|
||||
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)
|
||||
{
|
||||
debug.nospace().noquote() << "ZigbeeNetwork(" << network->macAddress().toString() << ", "
|
||||
|
||||
@ -175,6 +175,8 @@ protected:
|
||||
quint8 m_permitJoiningDuration = 120;
|
||||
quint8 m_permitJoiningRemaining = 0;
|
||||
|
||||
QTimer *m_reachableRefreshTimer = nullptr;
|
||||
|
||||
void setPermitJoiningEnabled(bool permitJoiningEnabled);
|
||||
void setPermitJoiningDuration(quint8 duration);
|
||||
void setPermitJoiningRemaining(quint8 remaining);
|
||||
@ -188,6 +190,8 @@ protected:
|
||||
void removeNode(ZigbeeNode *node);
|
||||
void removeUninitializedNode(ZigbeeNode *node);
|
||||
|
||||
void setNodeReachable(ZigbeeNode *node, bool reachable);
|
||||
|
||||
void setState(State state);
|
||||
void setError(Error error);
|
||||
|
||||
@ -227,6 +231,7 @@ signals:
|
||||
private slots:
|
||||
void onNodeStateChanged(ZigbeeNode::State state);
|
||||
void onNodeClusterAttributeChanged(ZigbeeCluster *cluster, const ZigbeeClusterAttribute &attribute);
|
||||
void evaluateNodeReachableStates();
|
||||
|
||||
public slots:
|
||||
virtual void startNetwork() = 0;
|
||||
|
||||
@ -244,7 +244,7 @@ void ZigbeeNode::initNodeDescriptor()
|
||||
m_requestRetry++;
|
||||
if (m_requestRetry < 3) {
|
||||
qCDebug(dcZigbeeNode()) << "Retry to request node descriptor" << m_requestRetry << "/" << "3";
|
||||
initNodeDescriptor();
|
||||
QTimer::singleShot(1000, this, [=](){ initNodeDescriptor(); });
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read node descriptor from" << this << "after 3 attempts. Giving up.";
|
||||
m_requestRetry = 0;
|
||||
@ -273,7 +273,7 @@ void ZigbeeNode::initPowerDescriptor()
|
||||
if (m_requestRetry < 3) {
|
||||
m_requestRetry++;
|
||||
qCDebug(dcZigbeeNode()) << "Retry to request power descriptor from" << this << m_requestRetry << "/" << "3 attempts.";
|
||||
initPowerDescriptor();
|
||||
QTimer::singleShot(1000, this, [=](){ initPowerDescriptor(); });
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read power descriptor from" << this << "after 3 attempts. Giving up.";
|
||||
m_requestRetry = 0;
|
||||
@ -305,7 +305,7 @@ void ZigbeeNode::initEndpoints()
|
||||
if (m_requestRetry < 3) {
|
||||
m_requestRetry++;
|
||||
qCDebug(dcZigbeeNode()) << "Retry to request active endpoints from" << this << m_requestRetry << "/" << "3 attempts.";
|
||||
initEndpoints();
|
||||
QTimer::singleShot(1000, this, [=](){ initEndpoints(); });
|
||||
} else {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read active endpoints from" << this << "after 3 attempts. Giving up.";
|
||||
m_requestRetry = 0;
|
||||
@ -355,7 +355,7 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
|
||||
if (m_requestRetry < 3) {
|
||||
m_requestRetry++;
|
||||
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 {
|
||||
qCWarning(dcZigbeeNode()) << "Failed to read simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << "after 3 attempts. Giving up.";
|
||||
m_requestRetry = 0;
|
||||
@ -721,7 +721,19 @@ QDebug operator<<(QDebug debug, ZigbeeNode *node)
|
||||
{
|
||||
debug.nospace().noquote() << "ZigbeeNode(" << ZigbeeUtils::convertUint16ToHexString(node->shortAddress());
|
||||
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() << ")";
|
||||
return debug.space().quote();
|
||||
}
|
||||
|
||||
@ -56,6 +56,9 @@ public:
|
||||
Q_ENUM(State)
|
||||
|
||||
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;
|
||||
|
||||
QUuid networkUuid() const;
|
||||
|
||||
Reference in New Issue
Block a user