Rework entire uart communication flow and prepare level cluster command execution signals

pull/7/head
Simon Stürz 2020-06-06 14:09:11 +02:00
parent 3cfcf3d6cc
commit 0992028a8a
12 changed files with 242 additions and 180 deletions

View File

@ -227,7 +227,7 @@ void ZigbeeInterfaceDeconz::sendPackage(const QByteArray &package)
qCWarning(dcZigbeeInterface()) << "Could not stream byte" << ZigbeeUtils::convertByteArrayToHexString(data);
}
//m_serialPort->flush();
m_serialPort->flush();
}
bool ZigbeeInterfaceDeconz::enable(const QString &serialPort, qint32 baudrate)

View File

@ -32,6 +32,11 @@ ZigbeeNetworkRequest ZigbeeInterfaceDeconzReply::networkRequest() const
return m_networkRequest;
}
QString ZigbeeInterfaceDeconzReply::requestName()
{
return m_requestName;
}
Deconz::Command ZigbeeInterfaceDeconzReply::command() const
{
return m_command;
@ -42,6 +47,11 @@ quint8 ZigbeeInterfaceDeconzReply::sequenceNumber() const
return m_sequenceNumber;
}
QByteArray ZigbeeInterfaceDeconzReply::requestData() const
{
return m_requestData;
}
QByteArray ZigbeeInterfaceDeconzReply::responseData() const
{
return m_responseData;
@ -64,24 +74,37 @@ bool ZigbeeInterfaceDeconzReply::aborted() const
void ZigbeeInterfaceDeconzReply::abort()
{
m_timer->stop();
m_aborted = true;
emit finished();
}
ZigbeeInterfaceDeconzReply::ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent) :
ZigbeeInterfaceDeconzReply::ZigbeeInterfaceDeconzReply(Deconz::Command command, QObject *parent) :
QObject(parent),
m_timer(new QTimer(this)),
m_command(command),
m_sequenceNumber(sequenceNumber)
m_command(command)
{
m_timer->setInterval(5000);
m_timer->setInterval(3000);
m_timer->setSingleShot(true);
connect(m_timer, &QTimer::timeout, this, &ZigbeeInterfaceDeconzReply::onTimeout);
}
void ZigbeeInterfaceDeconzReply::setSequenceNumber(quint8 sequenceNumber)
{
m_sequenceNumber = sequenceNumber;
// Put the sequence number into the request data payload, it's always the second byte
m_requestData[1] = m_sequenceNumber;
}
void ZigbeeInterfaceDeconzReply::onTimeout()
{
m_timeout = true;
emit timeout();
emit finished();
}
QDebug operator<<(QDebug debug, ZigbeeInterfaceDeconzReply *reply)
{
debug.nospace() << "InterfaceReply(" << reply->requestName() << ", " << reply->sequenceNumber() << ")";
return debug.space();
}

View File

@ -43,8 +43,10 @@ class ZigbeeInterfaceDeconzReply : public QObject
public:
// Request content
ZigbeeNetworkRequest networkRequest() const;
QString requestName();
Deconz::Command command() const;
quint8 sequenceNumber() const;
QByteArray requestData() const;
QByteArray responseData() const;
// Response content
@ -55,15 +57,19 @@ public:
void abort();
private:
explicit ZigbeeInterfaceDeconzReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent = nullptr);
explicit ZigbeeInterfaceDeconzReply(Deconz::Command command, QObject *parent = nullptr);
ZigbeeNetworkRequest m_networkRequest;
QTimer *m_timer = nullptr;
bool m_timeout = false;
bool m_aborted = false;
void setSequenceNumber(quint8 sequenceNumber);
// Request content
QString m_requestName;
Deconz::Command m_command;
quint8 m_sequenceNumber = 0;
QByteArray m_requestData;
// Response content
Deconz::StatusCode m_statusCode = Deconz::StatusCodeError;
@ -78,4 +84,6 @@ signals:
};
QDebug operator<<(QDebug debug, ZigbeeInterfaceDeconzReply *reply);
#endif // ZIGBEEINTERFACEDECONZREPLY_H

View File

@ -69,81 +69,57 @@ Deconz::NetworkState ZigbeeBridgeControllerDeconz::networkState() const
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestVersion()
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request version. SQN:" << sequenceNumber;
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandVersion);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(5); // Frame length
ZigbeeInterfaceDeconzReply *reply = new ZigbeeInterfaceDeconzReply(Deconz::CommandVersion, sequenceNumber, this);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, reply, &ZigbeeInterfaceDeconzReply::deleteLater, Qt::QueuedConnection);
m_pendingReplies.insert(sequenceNumber, reply);
m_interface->sendPackage(message);
return reply;
return createReply(Deconz::CommandVersion, "Request controller version", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestDeviceState()
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request device state. SQN:" << sequenceNumber;
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandDeviceState);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(8); // Frame length
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint8>(0); // Reserverd
ZigbeeInterfaceDeconzReply *reply = new ZigbeeInterfaceDeconzReply(Deconz::CommandDeviceState, sequenceNumber, this);
connect(reply, &ZigbeeInterfaceDeconzReply::finished, reply, &ZigbeeInterfaceDeconzReply::deleteLater, Qt::QueuedConnection);
m_pendingReplies.insert(sequenceNumber, reply);
m_interface->sendPackage(message);
return createReply(Deconz::CommandDeviceState, sequenceNumber, this);
return createReply(Deconz::CommandDeviceState, "Request controller device state", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestReadParameter(Deconz::Parameter parameter)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request read parameter. SQN:" << sequenceNumber << parameter;
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandReadParameter);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(8); // Frame length 7 + 1 payload
stream << static_cast<quint16>(1); // Payload length
stream << static_cast<quint8>(parameter);
m_interface->sendPackage(message);
return createReply(Deconz::CommandReadParameter, sequenceNumber, this);
return createReply(Deconz::CommandReadParameter, "Request controller read parameter", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestWriteParameter(Deconz::Parameter parameter, const QByteArray &data)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request write parameter. SQN:" << sequenceNumber << parameter << ZigbeeUtils::convertByteArrayToHexString(data);
quint16 payloadLength = static_cast<quint16>(1 + data.length());
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandWriteParameter);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length 7 + payload length
stream << static_cast<quint16>(payloadLength); // 1 parameter + parameter data length
@ -152,35 +128,25 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestWriteParameter(
stream << static_cast<quint8>(data.at(i));
}
m_interface->sendPackage(message);
return createReply(Deconz::CommandWriteParameter, sequenceNumber, this);
return createReply(Deconz::CommandWriteParameter, "Request controller write parameter", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestChangeNetworkState(Deconz::NetworkState networkState)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request change network state. SQN:" << sequenceNumber << networkState;
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandChangeNetworkState);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(6); // Frame length
stream << static_cast<quint8>(networkState);
m_interface->sendPackage(message);
return createReply(Deconz::CommandChangeNetworkState, sequenceNumber, this);
return createReply(Deconz::CommandChangeNetworkState, "Request controller write parameter", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestReadReceivedDataIndication(Deconz::SourceAddressMode sourceAddressMode)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request read received data indication. SQN:" << sequenceNumber << ZigbeeUtils::convertByteToHexString(sourceAddressMode);
quint16 payloadLength = 0;
if (sourceAddressMode != Deconz::SourceAddressModeNone) {
payloadLength = 1;
@ -190,45 +156,32 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestReadReceivedDat
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandApsDataIndication);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length + payload length
stream << static_cast<quint16>(payloadLength); // payload length
if (payloadLength > 0)
stream << static_cast<quint8>(sourceAddressMode);
m_interface->sendPackage(message);
return createReply(Deconz::CommandApsDataIndication, sequenceNumber, this);
return createReply(Deconz::CommandApsDataIndication, "Request read received data indication", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestQuerySendDataConfirm()
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request query send data confirm. SQN:" << sequenceNumber;
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandApsDataConfirm);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7); // Frame length
stream << static_cast<quint16>(0); // Payload length
m_interface->sendPackage(message);
return createReply(Deconz::CommandApsDataConfirm, sequenceNumber, this);
return createReply(Deconz::CommandApsDataConfirm, "Request query send data confirm", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestSendRequest(const ZigbeeNetworkRequest &request)
{
// Send the request only if there are free slots on the device, otherwise enque request
// if (m_apsFreeSlotsAvailable) {
// }
qCDebug(dcZigbeeAps()) << "APSDE-DATA.request" << request;
ZigbeeInterfaceDeconzReply *interfaceReply = nullptr;
switch (request.destinationAddressMode()) {
case Zigbee::DestinationAddressModeGroup:
@ -251,44 +204,73 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestSendRequest(con
return interfaceReply;
}
quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber()
void ZigbeeBridgeControllerDeconz::sendNextRequest()
{
m_sequenceNumber += 3;
return m_sequenceNumber;
// Check if there is a reply request to send
if (m_replyQueue.isEmpty())
return;
// Check if there is currently a running reply
if (m_currentReply)
return;
// // If the controler request queue is full, wait until it's free again
// if (!m_apsFreeSlotsAvailable)
// return;
// Get the next reply, set the sequence number, send the request data over the interface and start waiting
m_currentReply = m_replyQueue.dequeue();
m_currentReply->setSequenceNumber(generateSequenceNumber());
qCDebug(dcZigbeeController()) << "Send request" << m_currentReply;
m_interface->sendPackage(m_currentReply->requestData());
m_currentReply->m_timer->start();
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::createReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent)
quint8 ZigbeeBridgeControllerDeconz::generateSequenceNumber()
{
return m_sequenceNumber++;
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::createReply(Deconz::Command command, const QString &requestName, const QByteArray &requestData, QObject *parent)
{
// Create the reply
ZigbeeInterfaceDeconzReply *reply = new ZigbeeInterfaceDeconzReply(command, sequenceNumber, parent);
ZigbeeInterfaceDeconzReply *reply = new ZigbeeInterfaceDeconzReply(command, parent);
reply->m_requestName = requestName;
reply->m_requestData = requestData;
// Make sure we clean up on timeout
connect(reply, &ZigbeeInterfaceDeconzReply::timeout, this, [this, reply](){
qCWarning(dcZigbeeController()) << "Reply timeout" << reply->command() << "SQN:" << reply->sequenceNumber();
if (m_pendingReplies.contains(reply->sequenceNumber())) {
m_pendingReplies.remove(reply->sequenceNumber());
// Note: will be deleted with the finished signal
qCWarning(dcZigbeeController()) << "Reply timeout" << reply;
if (m_currentReply == reply) {
m_currentReply = nullptr;
QMetaObject::invokeMethod(this, "sendNextRequest", Qt::QueuedConnection);
}
});
// Auto delete the object on finished
connect(reply, &ZigbeeInterfaceDeconzReply::finished, reply, &ZigbeeInterfaceDeconzReply::deleteLater, Qt::QueuedConnection);
// Add it to the pending list
m_pendingReplies.insert(sequenceNumber, reply);
// Fixme: start the timer once actually sent to the interface
reply->m_timer->start();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, reply, [this, reply](){
reply->deleteLater();
if (m_currentReply == reply) {
m_currentReply = nullptr;
QMetaObject::invokeMethod(this, "sendNextRequest", Qt::QueuedConnection);
}
});
// Enqueu this reply and send it once the current reply slot is free
m_replyQueue.enqueue(reply);
qCDebug(dcZigbeeController()) << "Enqueue request:" << reply->requestName();
QMetaObject::invokeMethod(this, "sendNextRequest", Qt::QueuedConnection);
return reply;
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataGroup(quint8 requestId, quint16 groupAddress, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request enqueue send data to group" << ZigbeeUtils::convertUint16ToHexString(groupAddress)
<< "SQN:" << sequenceNumber
<< static_cast<Zigbee::ZigbeeProfile>(profileId)
<< ZigbeeUtils::convertUint16ToHexString(clusterId)
<< ZigbeeUtils::convertByteToHexString(sourceEndpoint);
// quint8 sequenceNumber = generateSequenceNumber();
// qCDebug(dcZigbeeController()) << "Request enqueue send data to group" << ZigbeeUtils::convertUint16ToHexString(groupAddress)
// << "SQN:" << sequenceNumber
// << static_cast<Zigbee::ZigbeeProfile>(profileId)
// << ZigbeeUtils::convertUint16ToHexString(clusterId)
// << ZigbeeUtils::convertByteToHexString(sourceEndpoint);
Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes");
@ -299,7 +281,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandApsDataRequest);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length
stream << payloadLength;
@ -317,20 +299,18 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
stream << static_cast<quint8>(txOptions);
stream << radius;
m_interface->sendPackage(message);
return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this);
return createReply(Deconz::CommandApsDataRequest, "Request enqueue send data to group", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request enqueue send data to short address" << ZigbeeUtils::convertUint16ToHexString(shortAddress)
<< "SQN:" << sequenceNumber
<< ZigbeeUtils::convertByteToHexString(destinationEndpoint)
<< static_cast<Zigbee::ZigbeeProfile>(profileId)
<< ZigbeeUtils::convertUint16ToHexString(clusterId)
<< ZigbeeUtils::convertByteToHexString(sourceEndpoint);
// quint8 sequenceNumber = generateSequenceNumber();
// qCDebug(dcZigbeeController()) << "Request enqueue send data to short address" << ZigbeeUtils::convertUint16ToHexString(shortAddress)
// << "SQN:" << sequenceNumber
// << ZigbeeUtils::convertByteToHexString(destinationEndpoint)
// << static_cast<Zigbee::ZigbeeProfile>(profileId)
// << ZigbeeUtils::convertUint16ToHexString(clusterId)
// << ZigbeeUtils::convertByteToHexString(sourceEndpoint);
Q_ASSERT_X(asdu.length() <= 127, "ASDU", "ASDU package length has to <= 127 bytes");
@ -341,7 +321,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandApsDataRequest);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length
stream << payloadLength;
@ -359,19 +339,17 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
stream << static_cast<quint8>(txOptions); // TX Options: Use APS ACKs
stream << radius;
m_interface->sendPackage(message);
return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this);
return createReply(Deconz::CommandApsDataRequest, "Request enqueue send data to short address", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius)
{
quint8 sequenceNumber = generateSequenceNumber();
qCDebug(dcZigbeeController()) << "Request enqueue send data to IEEE address" << ieeeAddress.toString()
<< "SQN:" << sequenceNumber
<< ZigbeeUtils::convertByteToHexString(destinationEndpoint)
<< profileId << clusterId
<< ZigbeeUtils::convertByteToHexString(sourceEndpoint);
// quint8 sequenceNumber = generateSequenceNumber();
// qCDebug(dcZigbeeController()) << "Request enqueue send data to IEEE address" << ieeeAddress.toString()
// << "SQN:" << sequenceNumber
// << ZigbeeUtils::convertByteToHexString(destinationEndpoint)
// << profileId << clusterId
// << ZigbeeUtils::convertByteToHexString(sourceEndpoint);
Q_ASSERT_X(asdu.length() <= 127, "ZigbeeController", "ASDU package length has to be <= 127 bytes");
@ -382,7 +360,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
QDataStream stream(&message, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << static_cast<quint8>(Deconz::CommandApsDataRequest);
stream << static_cast<quint8>(sequenceNumber);
stream << static_cast<quint8>(0); // SQN will be generated right before sending
stream << static_cast<quint8>(0); // Reserverd
stream << static_cast<quint16>(7 + payloadLength); // Frame length
stream << payloadLength;
@ -400,9 +378,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::requestEnqueueSendData
stream << static_cast<quint8>(txOptions); // TX Options: Use APS ACKs
stream << radius;
m_interface->sendPackage(message);
return createReply(Deconz::CommandApsDataRequest, sequenceNumber, this);
return createReply(Deconz::CommandApsDataRequest, "Request enqueue send data to IEEE address", message, this);
}
ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters()
@ -414,7 +390,7 @@ ZigbeeInterfaceDeconzReply *ZigbeeBridgeControllerDeconz::readNetworkParameters(
// If read request failes, this mehtod returns the status code of the failed request.
// Create an independent reply for finishing the entire read sequence
ZigbeeInterfaceDeconzReply *readNetworkParametersReply = new ZigbeeInterfaceDeconzReply(Deconz::CommandReadParameter, generateSequenceNumber(), this);
ZigbeeInterfaceDeconzReply *readNetworkParametersReply = new ZigbeeInterfaceDeconzReply(Deconz::CommandReadParameter, this);
connect(readNetworkParametersReply, &ZigbeeInterfaceDeconzReply::finished, readNetworkParametersReply, &ZigbeeInterfaceDeconzReply::deleteLater, Qt::QueuedConnection);
// Read MAC address of the bridge
@ -756,14 +732,25 @@ DeconzDeviceState ZigbeeBridgeControllerDeconz::parseDeviceStateFlag(quint8 devi
void ZigbeeBridgeControllerDeconz::readDataIndication()
{
ZigbeeInterfaceDeconzReply *reply = requestReadReceivedDataIndication();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (m_readIndicationReply) {
// There is alreay a read indication reply in the queue, let finish that first before creating a new one
return;
}
m_readIndicationReply = requestReadReceivedDataIndication();
connect(m_readIndicationReply, &ZigbeeInterfaceDeconzReply::finished, this, [this](){
ZigbeeInterfaceDeconzReply *reply = m_readIndicationReply;
// Allow to send the next read indication reply if required
m_readIndicationReply = nullptr;
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read data indication." << "SQN:" << reply->sequenceNumber() << reply->statusCode();
qCWarning(dcZigbeeController()) << "Could not read data indication." << "SQN:" << m_readIndicationReply->sequenceNumber() << m_readIndicationReply->statusCode();
// FIXME: set an appropriate error
return;
}
// APS data indication received, process the content
qCDebug(dcZigbeeController()) << "Reading data indication finished successfully" << "SQN:" << reply->sequenceNumber();
processDataIndication(reply->responseData());
@ -772,8 +759,18 @@ void ZigbeeBridgeControllerDeconz::readDataIndication()
void ZigbeeBridgeControllerDeconz::readDataConfirm()
{
ZigbeeInterfaceDeconzReply *reply = requestQuerySendDataConfirm();
connect(reply, &ZigbeeInterfaceDeconzReply::finished, this, [this, reply](){
if (m_readConfirmReply) {
// There is alreay a read confirm reply in the queue, let finish that first before creating a new one
return;
}
m_readConfirmReply = requestQuerySendDataConfirm();
connect(m_readConfirmReply, &ZigbeeInterfaceDeconzReply::finished, this, [this](){
ZigbeeInterfaceDeconzReply *reply = m_readConfirmReply;
// Allow to send the next read confirm reply if required
m_readConfirmReply = nullptr;
if (reply->statusCode() != Deconz::StatusCodeSuccess) {
qCWarning(dcZigbeeController()) << "Could not read data confirm." << "SQN:" << reply->sequenceNumber() << reply->statusCode();
// FIXME: set an appropriate error
@ -801,9 +798,10 @@ void ZigbeeBridgeControllerDeconz::processDeviceState(DeconzDeviceState deviceSt
if (!m_apsFreeSlotsAvailable) {
qCWarning(dcZigbeeController()) << "The APS request table is full on the device. Cannot send requests until the queue gets processed on the controller.";
return;
} else {
qCDebug(dcZigbeeController()) << "The APS request table is free again. Sending the next request";
sendNextRequest();
}
// FIXME: if changed to true, send next aps data request
}
if (m_networkState != Deconz::NetworkStateConnected)
@ -909,15 +907,18 @@ void ZigbeeBridgeControllerDeconz::onInterfaceAvailableChanged(bool available)
qCDebug(dcZigbeeController()) << "Interface available changed" << available;
if (!available) {
// Clean up any pending replies
foreach (quint8 id, m_pendingReplies.keys()) {
ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(id);
while (!m_replyQueue.isEmpty()) {
ZigbeeInterfaceDeconzReply *reply = m_replyQueue.dequeue();
reply->abort();
}
m_sequenceNumber = 0;
m_apsFreeSlotsAvailable = true;
m_watchdogTimer->stop();
}
setAvailable(available);
sendNextRequest();
}
void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray &package)
@ -933,29 +934,18 @@ void ZigbeeBridgeControllerDeconz::onInterfacePackageReceived(const QByteArray &
qCDebug(dcZigbeeController()) << "Interface message received" << command << "SQN:" << sequenceNumber
<< status << "Frame length:" << frameLength << ZigbeeUtils::convertByteArrayToHexString(data);
// Check if this is an interface response for a pending reply
if (m_pendingReplies.contains(sequenceNumber)) {
if (m_pendingReplies.value(sequenceNumber)->command() == command) {
// SQN and command maches
ZigbeeInterfaceDeconzReply *reply = m_pendingReplies.take(sequenceNumber);
if (!reply) {
qCWarning(dcZigbeeController()) << "Received message but the corresponding reply does not exist any more.";
return;
}
reply->m_responseData = data;
reply->m_statusCode = status;
reply->finished();
return;
} else {
qCWarning(dcZigbeeController()) << "Received message with a pending request SQN but the command does not match. SQN mismatch.";
qCWarning(dcZigbeeController()) << "The SQN matches" << m_pendingReplies.value(sequenceNumber)->command() << "but command" << command << "received for SQN" << sequenceNumber;
}
// Check if this is the response to the current active reply
if (m_currentReply && m_currentReply->sequenceNumber() == sequenceNumber && m_currentReply->command() == command) {
m_currentReply->m_responseData = data;
m_currentReply->m_statusCode = status;
m_currentReply->finished();
// Note: the current reply will be cleand up in the finished slot
return;
}
// Note: we got a notification, lets set the current sequence number to the notification id,
// so the next request will be a continuouse increase
//m_sequenceNumber = sequenceNumber + 10;
// We got a notification, lets set the current sequence number to the notification id,
// so the next request will be a continuouse increase
m_sequenceNumber = sequenceNumber;
// No request for this data, lets check which notification and process the data
switch (command) {

View File

@ -93,11 +93,7 @@ public:
ZigbeeInterfaceDeconzReply *requestWriteParameter(Deconz::Parameter parameter, const QByteArray &data);
ZigbeeInterfaceDeconzReply *requestChangeNetworkState(Deconz::NetworkState networkState);
// Receive data
ZigbeeInterfaceDeconzReply *requestReadReceivedDataIndication(Deconz::SourceAddressMode sourceAddressMode = Deconz::SourceAddressModeShortSourceAddress);
ZigbeeInterfaceDeconzReply *requestQuerySendDataConfirm();
// Send data
// Send APS request data
ZigbeeInterfaceDeconzReply *requestSendRequest(const ZigbeeNetworkRequest &request);
private:
@ -105,21 +101,22 @@ private:
quint8 m_sequenceNumber = 0;
quint32 m_watchdogTimeout = 300;
int m_watchdogResetTimout = 280;
QHash<quint8, ZigbeeInterfaceDeconzReply *> m_pendingReplies;
DeconzNetworkConfiguration m_networkConfiguration;
Deconz::NetworkState m_networkState = Deconz::NetworkStateOffline;
QTimer *m_watchdogTimer = nullptr;
// Interface queue, send all requests sequentially and always wait for the interface response
// APS request queue
bool m_apsFreeSlotsAvailable = false;
QQueue<ZigbeeNetworkRequest> m_requestQueue;
void sendNextRequest();
bool m_apsFreeSlotsAvailable = true;
ZigbeeInterfaceDeconzReply *m_currentReply = nullptr;
ZigbeeInterfaceDeconzReply *m_readConfirmReply = nullptr;
ZigbeeInterfaceDeconzReply *m_readIndicationReply = nullptr;
QQueue<ZigbeeInterfaceDeconzReply *> m_replyQueue;
quint8 generateSequenceNumber();
ZigbeeInterfaceDeconzReply *createReply(Deconz::Command command, quint8 sequenceNumber, QObject *parent);
ZigbeeInterfaceDeconzReply *createReply(Deconz::Command command, const QString &requestName, const QByteArray &requestData, QObject *parent);
// Send data depending on the request destination address mode
QByteArray buildRequestEnqueueSendDataGroupMessage(quint8 requestId, quint16 groupAddress, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0);
@ -127,6 +124,10 @@ private:
ZigbeeInterfaceDeconzReply *requestEnqueueSendDataShortAddress(quint8 requestId, quint16 shortAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0);
ZigbeeInterfaceDeconzReply *requestEnqueueSendDataIeeeAddress(quint8 requestId, ZigbeeAddress ieeeAddress, quint8 destinationEndpoint, quint16 profileId, quint16 clusterId, quint8 sourceEndpoint, const QByteArray &asdu, Zigbee::ZigbeeTxOptions txOptions, quint8 radius = 0);
// Receive data
ZigbeeInterfaceDeconzReply *requestReadReceivedDataIndication(Deconz::SourceAddressMode sourceAddressMode = Deconz::SourceAddressModeShortSourceAddress);
ZigbeeInterfaceDeconzReply *requestQuerySendDataConfirm();
// Note: this method reads all parameters individual. The returned reply it self will not send or receive any data.
// The data can be fetched from m_networkConfiguration on success.
ZigbeeInterfaceDeconzReply *readNetworkParameters();
@ -151,8 +152,9 @@ signals:
private slots:
void onInterfaceAvailableChanged(bool available);
void onInterfacePackageReceived(const QByteArray &package);
void resetControllerWatchdog();
void sendNextRequest();
public slots:
bool enable(const QString &serialPort, qint32 baudrate);

View File

@ -133,5 +133,24 @@ void ZigbeeClusterLevelControl::processDataIndication(ZigbeeClusterLibrary::Fram
// Increase the tsn for continuouse id increasing on both sides
m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
// FIXME: parse client commands to group
switch (m_direction) {
case Client:
// If the client cluster sends data to a server cluster (independent which), the command was executed on the device like button pressed
if (frame.header.frameControl.direction == ZigbeeClusterLibrary::DirectionClientToServer) {
// Read the payload which is
Command command = static_cast<Command>(frame.header.command);
qCDebug(dcZigbeeCluster()) << "Command sent from" << m_node << m_endpoint << this << command;
switch (command) {
default:
qCWarning(dcZigbeeCluster()) << "Unhandled command sent from" << m_node << m_endpoint << this << command;
break;
}
}
break;
case Server:
qCWarning(dcZigbeeCluster()) << "Unhandled ZCL indication in" << m_node << m_endpoint << this << frame;
break;
}
}

View File

@ -145,7 +145,7 @@ ZigbeeClusterReply *ZigbeeClusterColorControl::commandEnhancedMoveToHue(quint16
QDataStream stream(&payload, QIODevice::WriteOnly);
stream.setByteOrder(QDataStream::LittleEndian);
stream << enhancedHue << static_cast<quint8>(direction) << transitionTime;
return executeClusterCommand(ZigbeeClusterColorControl::CommandEnhancedMoveHue, payload);
return executeClusterCommand(ZigbeeClusterColorControl::CommandEnhancedMoveToHue, payload);
}
ZigbeeClusterReply *ZigbeeClusterColorControl::commandEnhancedMoveHue(ZigbeeClusterColorControl::MoveMode moveMode, quint16 rate)
@ -212,7 +212,7 @@ ZigbeeClusterReply *ZigbeeClusterColorControl::commandStepColorTemperature(Zigbe
void ZigbeeClusterColorControl::setAttribute(const ZigbeeClusterAttribute &attribute)
{
qCDebug(dcZigbeeCluster()) << "Update attribute" << m_node << m_endpoint << this << static_cast<Attribute>(attribute.id()) << attribute.dataType();
qCDebug(dcZigbeeCluster()) << "Attribute changed" << m_node << m_endpoint << this << static_cast<Attribute>(attribute.id()) << attribute.dataType();
if (hasAttribute(attribute.id())) {
m_attributes[attribute.id()] = attribute;
emit attributeChanged(attribute);
@ -220,6 +220,8 @@ void ZigbeeClusterColorControl::setAttribute(const ZigbeeClusterAttribute &attri
m_attributes.insert(attribute.id(), attribute);
emit attributeChanged(attribute);
}
}
void ZigbeeClusterColorControl::processDataIndication(ZigbeeClusterLibrary::Frame frame)

View File

@ -1121,7 +1121,17 @@ QDebug operator<<(QDebug debug, const ZigbeeDataType &dataType)
{
// FIXME: print data depending on the datatype
debug.nospace() << "ZigbeeDataType(" << dataType.name();
debug.nospace() << ", " << ZigbeeUtils::convertByteArrayToHexString(dataType.data());
switch (dataType.dataType()) {
case Zigbee::OctetString:
case Zigbee::LongOctetString:
case Zigbee::LongCharString:
case Zigbee::CharString:
debug.nospace() << ", " << dataType.toString();
break;
default:
debug.nospace() << ", " << ZigbeeUtils::convertByteArrayToHexString(dataType.data());
}
debug.nospace() << ")";
return debug.space();
}

View File

@ -437,6 +437,12 @@ void ZigbeeNetwork::addUnitializedNode(ZigbeeNode *node)
}
connect(node, &ZigbeeNode::stateChanged, this, &ZigbeeNetwork::onNodeStateChanged);
connect(node, &ZigbeeNode::nodeInitializationFailed, this, [this, node](){
qCWarning(dcZigbeeNetwork()) << "The initialization procedure for" << node << "failed. Please retry to add this node by restarting the init procedure.";
m_uninitializedNodes.removeAll(node);
node->deleteLater();
});
m_uninitializedNodes.append(node);
}

View File

@ -62,7 +62,7 @@ QList<ZigbeeNode *> ZigbeeNetworkDatabase::loadNodes()
QString query("SELECT * FROM nodes;");
QSqlQuery nodesQuery = m_db.exec(query);
while (nodesQuery.next()) {
quint64 ieeeAddress = nodesQuery.value("ieeeAddress").toULongLong();
QString ieeeAddress = nodesQuery.value("ieeeAddress").toString();
quint16 shortAddress = nodesQuery.value("shortAddress").toUInt();
QByteArray nodeDescriptor = QByteArray::fromBase64(nodesQuery.value("nodeDescriptor").toByteArray());
quint16 powerDescriptor = nodesQuery.value("powerDescriptor").toUInt();
@ -185,7 +185,7 @@ bool ZigbeeNetworkDatabase::initDatabase()
// Create nodes table
createTable("nodes",
"(ieeeAddress INTEGER PRIMARY KEY, " // uint64
"(ieeeAddress TEXT PRIMARY KEY, " // ieeeAddress to string
"shortAddress INTEGER NOT NULL, " // uint16
"nodeDescriptor BLOB NOT NULL, " // bytes as received from the node
"powerDescriptor INTEGER NOT NULL)"); // uint16
@ -250,7 +250,7 @@ bool ZigbeeNetworkDatabase::saveNodeEndpoint(ZigbeeNodeEndpoint *endpoint)
qCDebug(dcZigbeeNetworkDatabase()) << "Save" << endpoint;
QString queryString = QString("INSERT OR REPLACE INTO endpoints (ieeeAddress, endpointId, profileId, deviceId, deviceVersion) "
"VALUES (\"%1\", \"%2\", \"%3\", \"%4\", \"%5\");")
.arg(endpoint->node()->extendedAddress().toUInt64())
.arg(endpoint->node()->extendedAddress().toString())
.arg(endpoint->endpointId())
.arg(static_cast<quint16>(endpoint->profile()))
.arg(static_cast<quint16>(endpoint->deviceId()))
@ -258,7 +258,7 @@ bool ZigbeeNetworkDatabase::saveNodeEndpoint(ZigbeeNodeEndpoint *endpoint)
m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
return false;
}
@ -288,14 +288,14 @@ bool ZigbeeNetworkDatabase::saveInputCluster(ZigbeeCluster *cluster)
{
qCDebug(dcZigbeeNetworkDatabase()) << "Save" << cluster;
QString endpointIdReferenceQuery = QString("(SELECT id FROM endpoints WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\")")
.arg(cluster->node()->extendedAddress().toUInt64())
.arg(cluster->node()->extendedAddress().toString())
.arg(cluster->endpoint()->endpointId());
QString queryString = QString("INSERT OR REPLACE INTO serverClusters (endpointId, clusterId) VALUES (%1, \"%2\");")
.arg(endpointIdReferenceQuery)
.arg(static_cast<quint16>(cluster->clusterId()));
m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save input cluster into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save input cluster into database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
return false;
}
@ -306,7 +306,7 @@ bool ZigbeeNetworkDatabase::saveOutputCluster(ZigbeeCluster *cluster)
{
qCDebug(dcZigbeeNetworkDatabase()) << "Save" << cluster;
QString endpointIdReferenceQuery = QString("(SELECT id FROM endpoints WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\")")
.arg(cluster->node()->extendedAddress().toUInt64())
.arg(cluster->node()->extendedAddress().toString())
.arg(cluster->endpoint()->endpointId());
QString queryString = QString("INSERT OR REPLACE INTO clientClusters (endpointId, clusterId) VALUES (%1, \"%2\");")
.arg(endpointIdReferenceQuery)
@ -314,7 +314,7 @@ bool ZigbeeNetworkDatabase::saveOutputCluster(ZigbeeCluster *cluster)
m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save output cluster into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save output cluster into database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
return false;
}
@ -327,7 +327,7 @@ bool ZigbeeNetworkDatabase::saveAttribute(ZigbeeCluster *cluster, const ZigbeeCl
QString serverClusterIdReferenceQuery = QString("(SELECT id FROM serverClusters "
"WHERE endpointId = (SELECT id FROM endpoints WHERE ieeeAddress = \"%1\" AND endpointId = \"%2\")"
"AND clusterId = \"%3\")")
.arg(cluster->node()->extendedAddress().toUInt64())
.arg(cluster->node()->extendedAddress().toString())
.arg(cluster->endpoint()->endpointId())
.arg(cluster->clusterId());
@ -340,7 +340,7 @@ bool ZigbeeNetworkDatabase::saveAttribute(ZigbeeCluster *cluster, const ZigbeeCl
m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save cluster cluster attribute into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save cluster cluster attribute into database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
return false;
}
@ -352,14 +352,15 @@ bool ZigbeeNetworkDatabase::saveNode(ZigbeeNode *node)
qCDebug(dcZigbeeNetworkDatabase()) << "Save" << node;
QString queryString = QString("INSERT OR REPLACE INTO nodes (ieeeAddress, shortAddress, nodeDescriptor, powerDescriptor) "
"VALUES (\"%1\", \"%2\", \"%3\", \"%4\");")
.arg(node->extendedAddress().toUInt64())
.arg(node->extendedAddress().toString())
.arg(node->shortAddress())
.arg(node->nodeDescriptor().descriptorRawData.toBase64().data()) // Note: convert to base64 for saving zeros as string
.arg(node->powerDescriptor().powerDescriptoFlag);
qCDebug(dcZigbeeNetworkDatabase()) << queryString;
m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
qCWarning(dcZigbeeNetworkDatabase()) << "Could not save node into database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
return false;
}
@ -377,10 +378,10 @@ bool ZigbeeNetworkDatabase::removeNode(ZigbeeNode *node)
{
qCDebug(dcZigbeeNetworkDatabase()) << "Remove" << node;
// Note: cascade delete will clean up all other tables
QString queryString = QString("DELETE FROM nodes WHERE ieeeAddress = %1;").arg(node->extendedAddress().toUInt64());
QString queryString = QString("DELETE FROM nodes WHERE ieeeAddress = %1;").arg(node->extendedAddress().toString());
m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcZigbeeNetworkDatabase()) << "Could not remove node from database." << m_db.lastError().databaseText() << m_db.lastError().driverText();
qCWarning(dcZigbeeNetworkDatabase()) << "Could not remove node from database." << queryString << m_db.lastError().databaseText() << m_db.lastError().driverText();
return false;
}

View File

@ -153,7 +153,7 @@ void ZigbeeNode::initNodeDescriptor()
} else {
qCWarning(dcZigbeeNode()) << "Failed to read node descriptor from" << this << "after 3 attempts. Giving up.";
m_requestRetry = 0;
// FIXME: decide what to do, remove the node again from network
emit nodeInitializationFailed();
}
return;
}
@ -188,7 +188,7 @@ void ZigbeeNode::initPowerDescriptor()
} else {
qCWarning(dcZigbeeNode()) << "Failed to read power descriptor from" << this << "after 3 attempts. Giving up.";
m_requestRetry = 0;
// FIXME: decide what to do, remove the node again from network or continue with active endpoint request
emit nodeInitializationFailed();
}
return;
}
@ -228,7 +228,7 @@ void ZigbeeNode::initEndpoints()
} else {
qCWarning(dcZigbeeNode()) << "Failed to read active endpoints from" << this << "after 3 attempts. Giving up.";
m_requestRetry = 0;
// FIXME: decide what to do, remove the node again from network
emit nodeInitializationFailed();
}
return;
}
@ -284,7 +284,7 @@ void ZigbeeNode::initEndpoint(quint8 endpointId)
} else {
qCWarning(dcZigbeeNode()) << "Failed to read simple descriptor from" << this << ZigbeeUtils::convertByteToHexString(endpointId) << "after 3 attempts. Giving up.";
m_requestRetry = 0;
// FIXME: decide what to do, remove the node again from network
emit nodeInitializationFailed();
}
return;
}

View File

@ -115,6 +115,7 @@ private:
void readSoftwareBuildId(ZigbeeClusterBasic *basicCluster);
signals:
void nodeInitializationFailed();
void stateChanged(State state);
void lqiChanged(quint8 lqi);
void connectedChanged(bool connected);