Basic networking working with NXP modem firmware
parent
a3256af29d
commit
86a5579ff8
|
|
@ -146,9 +146,6 @@ signals:
|
|||
void networkStateChanged(Deconz::NetworkState networkState);
|
||||
void networkConfigurationParameterChanged(const DeconzNetworkConfiguration &networkConfiguration);
|
||||
|
||||
void apsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm);
|
||||
void apsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
private slots:
|
||||
void onInterfaceAvailableChanged(bool available);
|
||||
void onInterfacePackageReceived(const QByteArray &package);
|
||||
|
|
|
|||
|
|
@ -411,7 +411,6 @@ void ZigbeeNetworkDeconz::setPermitJoiningInternal(bool permitJoining)
|
|||
|
||||
qCDebug(dcZigbeeNetwork()) << "Set permit join configuration request finished" << reply->statusCode();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,18 @@ public:
|
|||
|
||||
CommandStartNetwork = 0x07,
|
||||
CommandGetNetworkState = 0x08,
|
||||
CommandSetPermitJoinCoordinator = 0x09
|
||||
CommandSetPermitJoinCoordinator = 0x09,
|
||||
|
||||
|
||||
CommandSendApsDataRequest = 0x20
|
||||
};
|
||||
Q_ENUM(Command)
|
||||
|
||||
enum Notification {
|
||||
NotificationDeviceStatusChanged = 0x7D,
|
||||
NotificationNetworkStarted = 0x7E,
|
||||
NotificationApsDataConfirm = 0x80,
|
||||
NotificationApsDataIndication = 0x81,
|
||||
NotificationDebugMessage = 0xFE
|
||||
};
|
||||
Q_ENUM(Notification)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#include "zigbeebridgecontrollernxp.h"
|
||||
#include "zigbeebridgecontrollernxp.h"
|
||||
#include "loggingcategory.h"
|
||||
#include "zigbeeutils.h"
|
||||
|
||||
|
|
@ -22,6 +22,25 @@ ZigbeeBridgeControllerNxp::ControllerState ZigbeeBridgeControllerNxp::controller
|
|||
return m_controllerState;
|
||||
}
|
||||
|
||||
void ZigbeeBridgeControllerNxp::refreshControllerState()
|
||||
{
|
||||
// Get controller state
|
||||
qCDebug(dcZigbeeController()) << "Refresh controller state";
|
||||
ZigbeeInterfaceNxpReply *reply = requestControllerState();
|
||||
connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){
|
||||
qCDebug(dcZigbeeNetwork()) << "Request controller state" << reply->status();
|
||||
|
||||
if (reply->status() != Nxp::StatusSuccess) {
|
||||
qCWarning(dcZigbeeController()) << "Failed to request controller state" << reply->status();
|
||||
return;
|
||||
}
|
||||
|
||||
m_controllerState = static_cast<ControllerState>(reply->responseData().at(0));
|
||||
qCDebug(dcZigbeeController()) << "Controller state changed" << m_controllerState;
|
||||
emit controllerStateChanged(m_controllerState);
|
||||
});
|
||||
}
|
||||
|
||||
ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestVersion()
|
||||
{
|
||||
QByteArray message;
|
||||
|
|
@ -160,6 +179,32 @@ ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestSetPermitJoinCoordina
|
|||
return createReply(Nxp::CommandSetPermitJoinCoordinator, m_sequenceNumber, "Request set permit join in coordinator", message, this);
|
||||
}
|
||||
|
||||
ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestSendRequest(const ZigbeeNetworkRequest &request)
|
||||
{
|
||||
ZigbeeInterfaceNxpReply *interfaceReply = nullptr;
|
||||
qCDebug(dcZigbeeAps()) << "APSDE-DATA.request" << request;
|
||||
|
||||
switch (request.destinationAddressMode()) {
|
||||
case Zigbee::DestinationAddressModeGroup:
|
||||
interfaceReply = requestEnqueueSendDataGroup(request.requestId(), request.destinationShortAddress(),
|
||||
request.profileId(), request.clusterId(),request.sourceEndpoint(),
|
||||
request.asdu(), request.txOptions(), request.radius());
|
||||
break;
|
||||
case Zigbee::DestinationAddressModeShortAddress:
|
||||
interfaceReply = requestEnqueueSendDataShortAddress(request.requestId(), request.destinationShortAddress(),
|
||||
request.destinationEndpoint(), request.profileId(), request.clusterId(),
|
||||
request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius());
|
||||
break;
|
||||
case Zigbee::DestinationAddressModeIeeeAddress:
|
||||
interfaceReply = requestEnqueueSendDataIeeeAddress(request.requestId(), request.destinationIeeeAddress(),
|
||||
request.destinationEndpoint(), request.profileId(), request.clusterId(),
|
||||
request.sourceEndpoint(), request.asdu(), request.txOptions(), request.radius());
|
||||
break;
|
||||
}
|
||||
|
||||
return interfaceReply;
|
||||
}
|
||||
|
||||
ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::createReply(Nxp::Command command, quint8 sequenceNumber, const QString &requestName, const QByteArray &requestData, QObject *parent)
|
||||
{
|
||||
// Create the reply
|
||||
|
|
@ -193,6 +238,114 @@ void ZigbeeBridgeControllerNxp::bumpSequenceNumber()
|
|||
m_sequenceNumber += 1;
|
||||
}
|
||||
|
||||
ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius)
|
||||
{
|
||||
Q_UNUSED(txOptions)
|
||||
Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes");
|
||||
|
||||
QByteArray payload;
|
||||
QDataStream payloadStream(&payload, QIODevice::WriteOnly);
|
||||
payloadStream.setByteOrder(QDataStream::LittleEndian);
|
||||
payloadStream << requestId;
|
||||
payloadStream << static_cast<quint8>(Zigbee::DestinationAddressModeGroup);
|
||||
payloadStream << groupAddress;
|
||||
payloadStream << static_cast<quint8>(0); // Note: group has no destination endpoint
|
||||
payloadStream << profileId;
|
||||
payloadStream << clusterId;
|
||||
payloadStream << sourceEndpoint;
|
||||
payloadStream << static_cast<quint8>(0x00); // Network and application layer security
|
||||
payloadStream << radius;
|
||||
payloadStream << static_cast<quint16>(asdu.size());
|
||||
for (int i = 0; i < asdu.size(); i++) {
|
||||
payloadStream << static_cast<quint8>(asdu.at(i));
|
||||
}
|
||||
|
||||
QByteArray message;
|
||||
bumpSequenceNumber();
|
||||
QDataStream stream(&message, QIODevice::WriteOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
stream << static_cast<quint8>(Nxp::CommandSendApsDataRequest);
|
||||
stream << static_cast<quint8>(m_sequenceNumber);
|
||||
stream << static_cast<quint16>(payload.size());
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
stream << static_cast<quint8>(payload.at(i));
|
||||
}
|
||||
|
||||
return createReply(Nxp::CommandSendApsDataRequest, m_sequenceNumber, "Request send ASP data request to group", message, this);
|
||||
}
|
||||
|
||||
ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius)
|
||||
{
|
||||
Q_UNUSED(txOptions)
|
||||
Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes");
|
||||
|
||||
QByteArray payload;
|
||||
QDataStream payloadStream(&payload, QIODevice::WriteOnly);
|
||||
payloadStream.setByteOrder(QDataStream::LittleEndian);
|
||||
payloadStream << requestId;
|
||||
payloadStream << static_cast<quint8>(Zigbee::DestinationAddressModeShortAddress);
|
||||
payloadStream << shortAddress;
|
||||
payloadStream << destinationEndpoint;
|
||||
payloadStream << profileId;
|
||||
payloadStream << clusterId;
|
||||
payloadStream << sourceEndpoint;
|
||||
payloadStream << static_cast<quint8>(0x00); // Network and application layer security
|
||||
payloadStream << radius;
|
||||
payloadStream << static_cast<quint16>(asdu.size());
|
||||
for (int i = 0; i < asdu.size(); i++) {
|
||||
payloadStream << static_cast<quint8>(asdu.at(i));
|
||||
}
|
||||
|
||||
QByteArray message;
|
||||
bumpSequenceNumber();
|
||||
QDataStream stream(&message, QIODevice::WriteOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
stream << static_cast<quint8>(Nxp::CommandSendApsDataRequest);
|
||||
stream << static_cast<quint8>(m_sequenceNumber);
|
||||
stream << static_cast<quint16>(payload.size());
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
stream << static_cast<quint8>(payload.at(i));
|
||||
}
|
||||
|
||||
return createReply(Nxp::CommandSendApsDataRequest, m_sequenceNumber, "Request send ASP data request to short address", message, this);
|
||||
}
|
||||
|
||||
ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius)
|
||||
{
|
||||
Q_UNUSED(txOptions)
|
||||
Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes");
|
||||
|
||||
QByteArray payload;
|
||||
QDataStream payloadStream(&payload, QIODevice::WriteOnly);
|
||||
payloadStream.setByteOrder(QDataStream::LittleEndian);
|
||||
payloadStream << requestId;
|
||||
payloadStream << static_cast<quint8>(Zigbee::DestinationAddressModeIeeeAddress);
|
||||
payloadStream << ieeeAddress.toUInt64();
|
||||
payloadStream << destinationEndpoint;
|
||||
payloadStream << profileId;
|
||||
payloadStream << clusterId;
|
||||
payloadStream << sourceEndpoint;
|
||||
payloadStream << static_cast<quint8>(0x00); // Network and application layer security
|
||||
payloadStream << radius;
|
||||
payloadStream << static_cast<quint16>(asdu.size());
|
||||
for (int i = 0; i < asdu.size(); i++) {
|
||||
payloadStream << static_cast<quint8>(asdu.at(i));
|
||||
}
|
||||
|
||||
QByteArray message;
|
||||
bumpSequenceNumber();
|
||||
QDataStream stream(&message, QIODevice::WriteOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
stream << static_cast<quint8>(Nxp::CommandSendApsDataRequest);
|
||||
stream << static_cast<quint8>(m_sequenceNumber);
|
||||
stream << static_cast<quint16>(payload.size());
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
stream << static_cast<quint8>(payload.at(i));
|
||||
}
|
||||
|
||||
return createReply(Nxp::CommandSendApsDataRequest, m_sequenceNumber, "Request send ASP data request to IEEE address", message, this);
|
||||
}
|
||||
|
||||
void ZigbeeBridgeControllerNxp::onInterfaceAvailableChanged(bool available)
|
||||
{
|
||||
qCDebug(dcZigbeeController()) << "Interface available changed" << available;
|
||||
|
|
@ -210,29 +363,98 @@ void ZigbeeBridgeControllerNxp::onInterfacePackageReceived(const QByteArray &pac
|
|||
if (commandInt >= 0x7D) {
|
||||
quint16 payloadLength = 0;
|
||||
stream >> payloadLength;
|
||||
QByteArray data = package.mid(4, payloadLength);
|
||||
QByteArray payload = package.mid(4, payloadLength);
|
||||
if (package.length() != payloadLength + 4) {
|
||||
qCWarning(dcZigbeeController()) << "Invalid package length received" << ZigbeeUtils::convertByteArrayToHexString(package) << payloadLength;
|
||||
return;
|
||||
}
|
||||
|
||||
Nxp::Notification notification = static_cast<Nxp::Notification>(commandInt);
|
||||
qCDebug(dcZigbeeController()) << "Interface notification received" << notification << "SQN:" << sequenceNumber << ZigbeeUtils::convertByteArrayToHexString(data);
|
||||
//qCDebug(dcZigbeeController()) << "Interface notification received" << notification << "SQN:" << sequenceNumber << ZigbeeUtils::convertByteArrayToHexString(payload);
|
||||
switch (notification) {
|
||||
case Nxp::NotificationDebugMessage:
|
||||
if (data.isEmpty()) {
|
||||
qCWarning(dcZigbeeController()) << "Received empty debug log notification";
|
||||
return;
|
||||
}
|
||||
qCDebug(dcZigbeeController()) << "*****DEBUG*****" << static_cast<Nxp::LogLevel>(data.at(0)) << "\n" << qUtf8Printable(data.right(data.length() - 1));
|
||||
break;
|
||||
case Nxp::NotificationDeviceStatusChanged:
|
||||
m_controllerState = static_cast<ControllerState>(data.at(0));
|
||||
m_controllerState = static_cast<ControllerState>(payload.at(0));
|
||||
qCDebug(dcZigbeeController()) << "Controller state changed" << m_controllerState;
|
||||
emit controllerStateChanged(m_controllerState);
|
||||
break;
|
||||
case Nxp::NotificationApsDataConfirm: {
|
||||
QDataStream stream(&payload, QIODevice::ReadOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
Zigbee::ApsdeDataConfirm confirm;
|
||||
stream >> confirm.requestId >> confirm.destinationAddressMode;
|
||||
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeGroup || confirm.destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
|
||||
stream >> confirm.destinationShortAddress;
|
||||
|
||||
if (confirm.destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
|
||||
stream >> confirm.destinationIeeeAddress;
|
||||
|
||||
stream >> confirm.sourceEndpoint >> confirm.destinationEndpoint >> confirm.zigbeeStatusCode;
|
||||
|
||||
qCDebug(dcZigbeeController()) << confirm;
|
||||
qCDebug(dcZigbeeAps()) << "APSDE-DATA.confirm" << confirm;
|
||||
|
||||
emit apsDataConfirmReceived(confirm);
|
||||
break;
|
||||
}
|
||||
case Nxp::NotificationApsDataIndication: {
|
||||
QDataStream stream(&payload, QIODevice::ReadOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
Zigbee::ApsdeDataIndication indication;
|
||||
quint8 status;
|
||||
stream >> status;
|
||||
stream >> indication.destinationAddressMode;
|
||||
Zigbee::DestinationAddressMode destinationAddressMode = static_cast<Zigbee::DestinationAddressMode>(indication.destinationAddressMode);
|
||||
if (destinationAddressMode == Zigbee::DestinationAddressModeGroup || destinationAddressMode == Zigbee::DestinationAddressModeShortAddress)
|
||||
stream >> indication.destinationShortAddress;
|
||||
|
||||
if (destinationAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
|
||||
stream >> indication.destinationIeeeAddress;
|
||||
|
||||
stream >> indication.destinationEndpoint;
|
||||
|
||||
stream >> indication.sourceAddressMode;
|
||||
if (indication.sourceAddressMode == Zigbee::DestinationAddressModeGroup || indication.sourceAddressMode == Zigbee::DestinationAddressModeShortAddress)
|
||||
stream >> indication.sourceShortAddress;
|
||||
|
||||
if (indication.sourceAddressMode == Zigbee::DestinationAddressModeIeeeAddress)
|
||||
stream >> indication.sourceShortAddress;
|
||||
|
||||
stream >> indication.sourceEndpoint;
|
||||
stream >> indication.profileId;
|
||||
stream >> indication.clusterId;
|
||||
quint16 asduLength = 0;
|
||||
stream >> asduLength;
|
||||
for (int i = 0; i < asduLength; i++) {
|
||||
quint8 byte = 0;
|
||||
stream >> byte;
|
||||
indication.asdu.append(static_cast<char>(byte));
|
||||
}
|
||||
stream >> indication.lqi;
|
||||
|
||||
// FIXME: security status
|
||||
|
||||
qCDebug(dcZigbeeController()) << indication;
|
||||
qCDebug(dcZigbeeAps()) << "APSDE-DATA.indication" << indication;
|
||||
|
||||
emit apsDataIndicationReceived(indication);
|
||||
break;
|
||||
}
|
||||
case Nxp::NotificationDebugMessage: {
|
||||
if (payload.isEmpty()) {
|
||||
qCWarning(dcZigbeeController()) << "Received empty debug log notification";
|
||||
return;
|
||||
}
|
||||
Nxp::LogLevel logLevel = static_cast<Nxp::LogLevel>(payload.at(0));
|
||||
QString debugMessage = QString::fromLocal8Bit(payload.right(payload.length() - 1));
|
||||
if (static_cast<quint8>(logLevel) <= static_cast<quint8>(Nxp::LogLevelWarning)) {
|
||||
qCWarning(dcZigbeeController()) << "***** Controller DEBUG *****" << logLevel << debugMessage;
|
||||
} else {
|
||||
qCDebug(dcZigbeeController()) << "***** Controller DEBUG *****" << logLevel << debugMessage;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
emit interfaceNotificationReceived(notification, data);
|
||||
emit interfaceNotificationReceived(notification, payload);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public:
|
|||
Q_ENUM(ControllerState)
|
||||
|
||||
ControllerState controllerState() const;
|
||||
void refreshControllerState();
|
||||
|
||||
// Controllere requests
|
||||
ZigbeeInterfaceNxpReply *requestVersion();
|
||||
|
|
@ -52,6 +53,7 @@ public:
|
|||
ZigbeeInterfaceNxpReply *requestSetPermitJoinCoordinator(quint8 duration);
|
||||
|
||||
// APS
|
||||
ZigbeeInterfaceNxpReply *requestSendRequest(const ZigbeeNetworkRequest &request);
|
||||
|
||||
|
||||
signals:
|
||||
|
|
@ -69,6 +71,11 @@ private:
|
|||
|
||||
void bumpSequenceNumber();
|
||||
|
||||
ZigbeeInterfaceNxpReply *requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0);
|
||||
ZigbeeInterfaceNxpReply *requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0);
|
||||
ZigbeeInterfaceNxpReply *requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0);
|
||||
|
||||
|
||||
private slots:
|
||||
void onInterfaceAvailableChanged(bool available);
|
||||
void onInterfacePackageReceived(const QByteArray &package);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,15 @@ ZigbeeNetworkNxp::ZigbeeNetworkNxp(QObject *parent) :
|
|||
connect(m_controller, &ZigbeeBridgeControllerNxp::availableChanged, this, &ZigbeeNetworkNxp::onControllerAvailableChanged);
|
||||
connect(m_controller, &ZigbeeBridgeControllerNxp::interfaceNotificationReceived, this, &ZigbeeNetworkNxp::onInterfaceNotificationReceived);
|
||||
connect(m_controller, &ZigbeeBridgeControllerNxp::controllerStateChanged, this, &ZigbeeNetworkNxp::onControllerStateChanged);
|
||||
//connect(m_controller, &ZigbeeBridgeControllerNxp::apsDataConfirmReceived, this, &ZigbeeNetworkNxp::onApsDataConfirmReceived);
|
||||
//connect(m_controller, &ZigbeeBridgeControllerNxp::apsDataIndicationReceived, this, &ZigbeeNetworkNxp::onApsDataIndicationReceived);
|
||||
connect(m_controller, &ZigbeeBridgeControllerNxp::apsDataConfirmReceived, this, &ZigbeeNetworkNxp::onApsDataConfirmReceived);
|
||||
connect(m_controller, &ZigbeeBridgeControllerNxp::apsDataIndicationReceived, this, &ZigbeeNetworkNxp::onApsDataIndicationReceived);
|
||||
|
||||
m_permitJoinRefreshTimer = new QTimer(this);
|
||||
m_permitJoinRefreshTimer->setInterval(250 * 1000);
|
||||
m_permitJoinRefreshTimer->setSingleShot(false);
|
||||
connect(m_permitJoinRefreshTimer, &QTimer::timeout, this, [this](){
|
||||
setPermitJoiningInternal(true);
|
||||
});
|
||||
}
|
||||
|
||||
ZigbeeBridgeController *ZigbeeNetworkNxp::bridgeController() const
|
||||
|
|
@ -25,27 +32,130 @@ ZigbeeBridgeController *ZigbeeNetworkNxp::bridgeController() const
|
|||
|
||||
ZigbeeNetworkReply *ZigbeeNetworkNxp::sendRequest(const ZigbeeNetworkRequest &request)
|
||||
{
|
||||
Q_UNUSED(request)
|
||||
return nullptr;
|
||||
ZigbeeNetworkReply *reply = createNetworkReply(request);
|
||||
// Send the request, and keep the reply until transposrt, zigbee trasmission and response arrived
|
||||
connect(reply, &ZigbeeNetworkReply::finished, this, [this, request](){
|
||||
m_pendingReplies.remove(request.requestId());
|
||||
});
|
||||
|
||||
// Finish the reply right the way if the network is offline
|
||||
if (!m_controller->available()) {
|
||||
finishNetworkReply(reply, ZigbeeNetworkReply::ErrorNetworkOffline);
|
||||
return reply;
|
||||
}
|
||||
|
||||
ZigbeeInterfaceNxpReply *interfaceReply = m_controller->requestSendRequest(request);
|
||||
connect(interfaceReply, &ZigbeeInterfaceNxpReply::finished, this, [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);
|
||||
m_pendingReplies.insert(networkRequestId, reply);
|
||||
|
||||
// The request has been sent successfully to the device, start the timeout timer now
|
||||
startWaitingReply(reply);
|
||||
});
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
ZigbeeNetworkReply *ZigbeeNetworkNxp::setPermitJoin(quint16 shortAddress, quint8 duration)
|
||||
{
|
||||
Q_UNUSED(shortAddress)
|
||||
Q_UNUSED(duration)
|
||||
return nullptr;
|
||||
// Get the power descriptor
|
||||
ZigbeeNetworkRequest request;
|
||||
request.setRequestId(generateSequenceNumber());
|
||||
request.setDestinationAddressMode(Zigbee::DestinationAddressModeGroup);
|
||||
request.setDestinationShortAddress(static_cast<quint16>(shortAddress));
|
||||
request.setProfileId(Zigbee::ZigbeeProfileDevice); // ZDP
|
||||
request.setClusterId(ZigbeeDeviceProfile::MgmtPermitJoinRequest);
|
||||
request.setSourceEndpoint(0); // ZDO
|
||||
request.setRadius(0);
|
||||
|
||||
// Build ASDU
|
||||
QByteArray asdu;
|
||||
QDataStream stream(&asdu, QIODevice::WriteOnly);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
stream << request.requestId();
|
||||
stream << duration;
|
||||
stream << static_cast<quint8>(0x01); // TrustCenter significance, always force to 1 according to Spec.
|
||||
request.setTxOptions(Zigbee::ZigbeeTxOptions()); // no ACK for broadcasts
|
||||
request.setAsdu(asdu);
|
||||
|
||||
qCDebug(dcZigbeeNetwork()) << "Send permit join request" << ZigbeeUtils::convertUint16ToHexString(request.destinationShortAddress()) << duration << "s";
|
||||
return sendRequest(request);
|
||||
}
|
||||
|
||||
void ZigbeeNetworkNxp::handleZigbeeDeviceProfileIndication(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
// Check if this is a device announcement
|
||||
if (indication.clusterId == ZigbeeDeviceProfile::DeviceAnnounce) {
|
||||
QDataStream stream(indication.asdu);
|
||||
stream.setByteOrder(QDataStream::LittleEndian);
|
||||
quint8 sequenceNumber = 0; quint16 shortAddress = 0; quint64 ieeeAddress = 0; quint8 macFlag = 0;
|
||||
stream >> sequenceNumber >> shortAddress >> ieeeAddress >> macFlag;
|
||||
onDeviceAnnounced(shortAddress, ZigbeeAddress(ieeeAddress), macFlag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (indication.destinationShortAddress == Zigbee::BroadcastAddressAllNodes ||
|
||||
indication.destinationShortAddress == Zigbee::BroadcastAddressAllRouters ||
|
||||
indication.destinationShortAddress == Zigbee::BroadcastAddressAllNonSleepingNodes) {
|
||||
qCDebug(dcZigbeeNetwork()) << "Received unhandled broadcast ZDO indication" << indication;
|
||||
|
||||
// FIXME: check what we can do with such messages like permit join
|
||||
return;
|
||||
}
|
||||
|
||||
ZigbeeNode *node = getZigbeeNode(indication.sourceShortAddress);
|
||||
if (!node) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Received a ZDO indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication;
|
||||
// FIXME: check if we want to create it since the device definitly exists within the network
|
||||
return;
|
||||
}
|
||||
|
||||
// Let the node handle this indication
|
||||
handleNodeIndication(node, indication);
|
||||
}
|
||||
|
||||
void ZigbeeNetworkNxp::handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
ZigbeeClusterLibrary::Frame frame = ZigbeeClusterLibrary::parseFrameData(indication.asdu);
|
||||
//qCDebug(dcZigbeeNetwork()) << "Handle ZCL indication" << indication << frame;
|
||||
|
||||
// Get the node
|
||||
ZigbeeNode *node = getZigbeeNode(indication.sourceShortAddress);
|
||||
if (!node) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Received a ZCL indication for an unrecognized node. There is no such node in the system. Ignoring indication" << indication;
|
||||
// FIXME: maybe create and init the node, since it is in the network, but not recognized
|
||||
// FIXME: maybe remove this node since we might have removed it but it did not respond, or we not explicitly allowed it to join.
|
||||
return;
|
||||
}
|
||||
// Let the node handle this indication
|
||||
handleNodeIndication(node, indication);
|
||||
}
|
||||
|
||||
void ZigbeeNetworkNxp::onControllerAvailableChanged(bool available)
|
||||
{
|
||||
qCDebug(dcZigbeeNetwork()) << "Controller is" << (available ? "now available" : "not available any more");
|
||||
if (available) {
|
||||
// Get controller state
|
||||
|
||||
m_controller->refreshControllerState();
|
||||
// Get network state, depending on the controller state
|
||||
|
||||
//reset();
|
||||
factoryResetNetwork();
|
||||
//factoryResetNetwork();
|
||||
} else {
|
||||
setState(StateOffline);
|
||||
}
|
||||
|
|
@ -71,7 +181,6 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr
|
|||
qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString;
|
||||
m_controller->setFirmwareVersion(versionString);
|
||||
|
||||
|
||||
qCDebug(dcZigbeeNetwork()) << "Get the current network state";
|
||||
ZigbeeInterfaceNxpReply *reply = m_controller->requestNetworkState();
|
||||
connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){
|
||||
|
|
@ -106,27 +215,18 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr
|
|||
ZigbeeNode *coordinatorNode = createNode(networkAddress, ZigbeeAddress(ieeeAddress), this);
|
||||
m_coordinatorNode = coordinatorNode;
|
||||
|
||||
// TODO: initialize
|
||||
|
||||
m_database->saveNode(m_coordinatorNode);
|
||||
saveNetwork();
|
||||
setState(StateRunning);
|
||||
|
||||
// // Network creation done when coordinator node is initialized
|
||||
// connect(coordinatorNode, &ZigbeeNode::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){
|
||||
// if (state == ZigbeeNode::StateInitialized) {
|
||||
// qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode;
|
||||
// m_initializing = false;
|
||||
// setState(StateRunning);
|
||||
// setPermitJoiningInternal(false);
|
||||
// return;
|
||||
// }
|
||||
// });
|
||||
|
||||
// coordinatorNode->startInitialization();
|
||||
// addUnitializedNode(coordinatorNode);
|
||||
|
||||
// Network creation done when coordinator node is initialized
|
||||
connect(coordinatorNode, &ZigbeeNode::stateChanged, this, [this, coordinatorNode](ZigbeeNode::State state){
|
||||
if (state == ZigbeeNode::StateInitialized) {
|
||||
qCDebug(dcZigbeeNetwork()) << "Coordinator initialized successfully." << coordinatorNode;
|
||||
setState(StateRunning);
|
||||
setPermitJoiningInternal(false);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
coordinatorNode->startInitialization();
|
||||
addUnitializedNode(coordinatorNode);
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
|
@ -226,10 +326,52 @@ void ZigbeeNetworkNxp::onInterfaceNotificationReceived(Nxp::Notification notific
|
|||
}
|
||||
default:
|
||||
qCWarning(dcZigbeeNetwork()) << "Unhandeld interface notification received" << notification << ZigbeeUtils::convertByteArrayToHexString(payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeNetworkNxp::onApsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm)
|
||||
{
|
||||
ZigbeeNetworkReply *reply = m_pendingReplies.value(confirm.requestId);
|
||||
if (!reply) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Received confirmation but could not find any reply. Ignoring the confirmation";
|
||||
return;
|
||||
}
|
||||
|
||||
setReplyResponseError(reply, static_cast<Zigbee::ZigbeeApsStatus>(confirm.zigbeeStatusCode));
|
||||
}
|
||||
|
||||
void ZigbeeNetworkNxp::onApsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication)
|
||||
{
|
||||
// Check if this indocation is related to any pending reply
|
||||
if (indication.profileId == Zigbee::ZigbeeProfileDevice) {
|
||||
handleZigbeeDeviceProfileIndication(indication);
|
||||
return;
|
||||
}
|
||||
|
||||
// Let the node handle this indication
|
||||
handleZigbeeClusterLibraryIndication(indication);
|
||||
}
|
||||
|
||||
void ZigbeeNetworkNxp::onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress ieeeAddress, quint8 macCapabilities)
|
||||
{
|
||||
qCDebug(dcZigbeeNetwork()) << "Device announced" << ZigbeeUtils::convertUint16ToHexString(shortAddress) << ieeeAddress.toString() << ZigbeeUtils::convertByteToHexString(macCapabilities);
|
||||
|
||||
// Lets check if this device is in the uninitialized node list, if so, remove it and recreate the device
|
||||
if (hasUninitializedNode(ieeeAddress)) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Device announced but there is already an initialization running for it. Remove the device and restart the initialization.";
|
||||
ZigbeeNode *uninitializedNode = getZigbeeNode(ieeeAddress);
|
||||
removeUninitializedNode(uninitializedNode);
|
||||
}
|
||||
|
||||
if (hasNode(ieeeAddress)) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Already known device announced. FIXME: Ignoring announcement" << ieeeAddress.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
ZigbeeNode *node = createNode(shortAddress, ieeeAddress, macCapabilities, this);
|
||||
addUnitializedNode(node);
|
||||
node->startInitialization();
|
||||
}
|
||||
|
||||
void ZigbeeNetworkNxp::setPermitJoiningInternal(bool permitJoining)
|
||||
|
|
@ -241,14 +383,40 @@ void ZigbeeNetworkNxp::setPermitJoiningInternal(bool permitJoining)
|
|||
duration = 255;
|
||||
}
|
||||
|
||||
// TODO: send broadcast message using ZDO and refrash
|
||||
// Note: since compliance version >= 21 the value 255 is not any more Qt::endless.
|
||||
// we need to refresh the command on timeout
|
||||
|
||||
qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator node to" << duration << "[s]";
|
||||
ZigbeeInterfaceNxpReply *reply = m_controller->requestSetPermitJoinCoordinator(duration);
|
||||
connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply, permitJoining](){
|
||||
qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator finished" << reply->status();
|
||||
m_permitJoining = permitJoining;
|
||||
emit permitJoiningChanged(m_permitJoining);
|
||||
|
||||
ZigbeeNetworkReply *reply = setPermitJoin(Zigbee::BroadcastAddressAllRouters, duration);
|
||||
connect(reply, &ZigbeeNetworkReply::finished, this, [this, reply, permitJoining, duration](){
|
||||
if (reply->zigbeeApsStatus() != Zigbee::ZigbeeApsStatusSuccess) {
|
||||
qCDebug(dcZigbeeNetwork()) << "Could not set permit join to" << duration;
|
||||
m_permitJoining = false;
|
||||
emit permitJoiningChanged(m_permitJoining);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeNetwork()) << "Permit join request finished successfully";
|
||||
if (permitJoining) {
|
||||
m_permitJoinRefreshTimer->start();
|
||||
} else {
|
||||
m_permitJoinRefreshTimer->stop();
|
||||
}
|
||||
|
||||
if (m_permitJoining != permitJoining) {
|
||||
m_permitJoining = permitJoining;
|
||||
emit permitJoiningChanged(m_permitJoining);
|
||||
}
|
||||
|
||||
qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator node to" << duration << "[s]";
|
||||
ZigbeeInterfaceNxpReply *reply = m_controller->requestSetPermitJoinCoordinator(duration);
|
||||
connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){
|
||||
qCDebug(dcZigbeeNetwork()) << "Set permit join in the coordinator finished" << reply->status();
|
||||
if (reply->status() != Nxp::StatusSuccess) {
|
||||
qCWarning(dcZigbeeNetwork()) << "Failed to set permit join status in coordinator";
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -256,6 +424,9 @@ void ZigbeeNetworkNxp::startNetwork()
|
|||
{
|
||||
loadNetwork();
|
||||
|
||||
m_permitJoining = false;
|
||||
emit permitJoiningChanged(m_permitJoining);
|
||||
|
||||
if (!m_controller->enable(serialPortName(), serialBaudrate())) {
|
||||
m_permitJoining = false;
|
||||
emit permitJoiningChanged(m_permitJoining);
|
||||
|
|
@ -264,10 +435,7 @@ void ZigbeeNetworkNxp::startNetwork()
|
|||
return;
|
||||
}
|
||||
|
||||
m_permitJoining = false;
|
||||
emit permitJoiningChanged(m_permitJoining);
|
||||
|
||||
// Get current state and load information
|
||||
// Wait for available signal...
|
||||
}
|
||||
|
||||
void ZigbeeNetworkNxp::stopNetwork()
|
||||
|
|
@ -287,6 +455,8 @@ void ZigbeeNetworkNxp::reset()
|
|||
void ZigbeeNetworkNxp::factoryResetNetwork()
|
||||
{
|
||||
qCDebug(dcZigbeeNetwork()) << "Factory reset network and forget all information. This cannot be undone.";
|
||||
clearSettings();
|
||||
|
||||
ZigbeeInterfaceNxpReply *reply = m_controller->requestFactoryResetController();
|
||||
connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){
|
||||
qCDebug(dcZigbeeNetwork()) << "Factory reset reply finished" << reply->status();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define ZIGBEENETWORKNXP_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include "zigbeenetwork.h"
|
||||
#include "zigbeechannelmask.h"
|
||||
|
|
@ -23,16 +24,29 @@ public:
|
|||
private:
|
||||
ZigbeeBridgeControllerNxp *m_controller = nullptr;
|
||||
bool m_networkRunning = false;
|
||||
QHash<quint8, ZigbeeNetworkReply *> m_pendingReplies;
|
||||
|
||||
QTimer *m_permitJoinRefreshTimer = nullptr;
|
||||
|
||||
// ZDO
|
||||
void handleZigbeeDeviceProfileIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
// ZCL
|
||||
void handleZigbeeClusterLibraryIndication(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
private slots:
|
||||
void onControllerAvailableChanged(bool available);
|
||||
void onControllerStateChanged(ZigbeeBridgeControllerNxp::ControllerState controllerState);
|
||||
void onInterfaceNotificationReceived(Nxp::Notification notification, const QByteArray &payload);
|
||||
|
||||
void onApsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm);
|
||||
void onApsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
void onDeviceAnnounced(quint16 shortAddress, ZigbeeAddress ieeeAddress, quint8 macCapabilities);
|
||||
|
||||
protected:
|
||||
void setPermitJoiningInternal(bool permitJoining) override;
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "loggingcategory.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QPointer>
|
||||
|
||||
ZigbeeDeviceObject::ZigbeeDeviceObject(ZigbeeNetwork *network, ZigbeeNode *node, QObject *parent) :
|
||||
QObject(parent),
|
||||
|
|
@ -407,6 +408,7 @@ void ZigbeeDeviceObject::finishZdoReply(ZigbeeDeviceObjectReply *zdoReply)
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
m_pendingReplies.remove(zdoReply->transactionSequenceNumber());
|
||||
zdoReply->finished();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#include "zigbee.h"
|
||||
|
||||
class ZigbeeBridgeController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
@ -50,6 +52,10 @@ protected:
|
|||
signals:
|
||||
void availableChanged(bool available);
|
||||
void firmwareVersionChanged(const QString &firmwareVersion);
|
||||
|
||||
void apsDataConfirmReceived(const Zigbee::ApsdeDataConfirm &confirm);
|
||||
void apsDataIndicationReceived(const Zigbee::ApsdeDataIndication &indication);
|
||||
|
||||
};
|
||||
|
||||
#endif // ZIGBEEBRIDGECONTROLLER_H
|
||||
|
|
|
|||
Loading…
Reference in New Issue