add qtcoap to the lib

add osdomotics plugin
This commit is contained in:
Simon Stürz 2015-11-12 15:21:54 +01:00 committed by Michael Zanetti
parent 94202684d8
commit b00bba1c2b
23 changed files with 2729 additions and 0 deletions

473
libguh/coap/coap.cpp Normal file
View File

@ -0,0 +1,473 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "coap.h"
#include "coappdu.h"
#include "coapoption.h"
Coap::Coap(QObject *parent, const quint16 &port) :
QObject(parent),
m_reply(0)
{
m_socket = new QUdpSocket(this);
if (!m_socket->bind(QHostAddress::Any, port))
qWarning() << "Could not bind to port" << port << m_socket->errorString();
connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
}
CoapReply *Coap::ping(const CoapRequest &request)
{
CoapReply *reply = new CoapReply(request, this);
reply->setRequestMethod(CoapPdu::Empty);
connect(reply, &CoapReply::timeout, this, &Coap::onReplyTimeout);
connect(reply, &CoapReply::finished, this, &Coap::onReplyFinished);
if (request.url().scheme() != "coap") {
reply->setError(CoapReply::InvalidUrlSchemeError);
reply->m_isFinished = true;
return reply;
}
// check if there is a request running
if (m_reply == 0) {
m_reply = reply;
lookupHost();
} else {
m_replyQueue.enqueue(reply);
}
return reply;
}
CoapReply *Coap::get(const CoapRequest &request)
{
CoapReply *reply = new CoapReply(request, this);
reply->setRequestMethod(CoapPdu::Get);
connect(reply, &CoapReply::timeout, this, &Coap::onReplyTimeout);
connect(reply, &CoapReply::finished, this, &Coap::onReplyFinished);
if (request.url().scheme() != "coap") {
reply->setError(CoapReply::InvalidUrlSchemeError);
reply->m_isFinished = true;
return reply;
}
// check if there is a request running
if (m_reply == 0) {
m_reply = reply;
lookupHost();
} else {
m_replyQueue.enqueue(reply);
}
return reply;
}
CoapReply *Coap::put(const CoapRequest &request, const QByteArray &data)
{
CoapReply *reply = new CoapReply(request, this);
reply->setRequestMethod(CoapPdu::Put);
reply->setRequestPayload(data);
connect(reply, &CoapReply::timeout, this, &Coap::onReplyTimeout);
connect(reply, &CoapReply::finished, this, &Coap::onReplyFinished);
if (request.url().scheme() != "coap") {
reply->setError(CoapReply::InvalidUrlSchemeError);
reply->m_isFinished = true;
return reply;
}
// check if there is a request running
if (m_reply == 0) {
m_reply = reply;
lookupHost();
} else {
m_replyQueue.enqueue(reply);
}
return reply;
}
CoapReply *Coap::post(const CoapRequest &request, const QByteArray &data)
{
CoapReply *reply = new CoapReply(request, this);
reply->setRequestMethod(CoapPdu::Post);
reply->setRequestPayload(data);
connect(reply, &CoapReply::timeout, this, &Coap::onReplyTimeout);
connect(reply, &CoapReply::finished, this, &Coap::onReplyFinished);
if (request.url().scheme() != "coap") {
reply->setError(CoapReply::InvalidUrlSchemeError);
reply->m_isFinished = true;
return reply;
}
// check if there is a request running
if (m_reply == 0) {
m_reply = reply;
lookupHost();
} else {
m_replyQueue.enqueue(reply);
}
return reply;
}
CoapReply *Coap::deleteResource(const CoapRequest &request)
{
CoapReply *reply = new CoapReply(request, this);
reply->setRequestMethod(CoapPdu::Delete);
connect(reply, &CoapReply::timeout, this, &Coap::onReplyTimeout);
connect(reply, &CoapReply::finished, this, &Coap::onReplyFinished);
if (request.url().scheme() != "coap") {
reply->setError(CoapReply::InvalidUrlSchemeError);
reply->m_isFinished = true;
return reply;
}
// check if there is a request running
if (m_reply == 0) {
m_reply = reply;
lookupHost();
} else {
m_replyQueue.enqueue(reply);
}
return reply;
}
void Coap::lookupHost()
{
int lookupId = QHostInfo::lookupHost(m_reply->request().url().host(), this, SLOT(hostLookupFinished(QHostInfo)));
m_runningHostLookups.insert(lookupId, m_reply);
}
void Coap::sendRequest(CoapReply *reply, const bool &lookedUp)
{
CoapPdu pdu;
pdu.setMessageType(reply->request().messageType());
pdu.setStatusCode(reply->requestMethod());
pdu.createMessageId();
pdu.createToken();
if (lookedUp)
pdu.addOption(CoapOption::UriHost, reply->request().url().host().toUtf8());
QStringList urlTokens = reply->request().url().path().split("/");
urlTokens.removeAll(QString());
foreach (const QString &token, urlTokens)
pdu.addOption(CoapOption::UriPath, token.toUtf8());
if (reply->request().url().hasQuery())
pdu.addOption(CoapOption::UriQuery, reply->request().url().query().toUtf8());
if (reply->requestMethod() == CoapPdu::Get)
pdu.addOption(CoapOption::Block2, CoapPduBlock::createBlock(0));
if (reply->requestMethod() == CoapPdu::Post || reply->requestMethod() == CoapPdu::Put) {
pdu.addOption(CoapOption::ContentFormat, QByteArray(1, ((quint8)reply->request().contentType())));
// check if we have to block the payload
if (reply->requestPayload().size() > 64) {
pdu.addOption(CoapOption::Block1, CoapPduBlock::createBlock(0, 2, true));
pdu.setPayload(reply->requestPayload().mid(0, 64));
} else {
pdu.setPayload(reply->requestPayload());
}
}
QByteArray pduData = pdu.pack();
reply->setRequestData(pduData);
reply->setMessageId(pdu.messageId());
reply->setMessageToken(pdu.token());
reply->m_lockedUp = lookedUp;
reply->m_timer->start();
qDebug() << "--->" << pdu;
// send the data
if (reply->request().messageType() == CoapPdu::NonConfirmable) {
sendData(reply->hostAddress(), reply->port(), pduData);
reply->setFinished();
} else {
sendData(reply->hostAddress(), reply->port(), pduData);
}
}
void Coap::sendData(const QHostAddress &hostAddress, const quint16 &port, const QByteArray &data)
{
m_socket->writeDatagram(data, hostAddress, port);
}
void Coap::sendCoapPdu(const QHostAddress &hostAddress, const quint16 &port, const CoapPdu &pdu)
{
qDebug() << "--->" << pdu;
m_socket->writeDatagram(pdu.pack(), hostAddress, port);
}
void Coap::processResponse(const CoapPdu &pdu)
{
qDebug() << "<---" << pdu;
if (!pdu.isValid()) {
qWarning() << "Got invalid PDU";
m_reply->setError(CoapReply::InvalidPduError);
m_reply->setFinished();
return;
}
// check if the message is a response to a reply (message id based check)
if (m_reply->messageId() == pdu.messageId()) {
processIdBasedResponse(m_reply, pdu);
return;
}
// check if we know the message by token (message token based check)
if (m_reply->messageToken() == pdu.token()) {
processTokenBasedResponse(m_reply, pdu);
return;
}
qDebug() << "Got message without request";
}
void Coap::processIdBasedResponse(CoapReply *reply, const CoapPdu &pdu)
{
// check if this is an empty ACK response (which indicates a separated response)
if (pdu.statusCode() == CoapPdu::Empty && pdu.messageType() == CoapPdu::Acknowledgement) {
reply->m_timer->stop();
qDebug() << "Got empty ACK. Data will be sent separated.";
return;
}
// check if this is a Block1 pdu
if (pdu.messageType() == CoapPdu::Acknowledgement && pdu.hasOption(CoapOption::Block1)) {
processBlock1Response(reply, pdu);
return;
}
// check if this is a Block2 pdu
if (pdu.messageType() == CoapPdu::Acknowledgement && pdu.hasOption(CoapOption::Block2)) {
processBlock2Response(reply, pdu);
return;
}
// Piggybacked response
reply->setStatusCode(pdu.statusCode());
reply->setContentType(pdu.contentType());
reply->appendPayloadData(pdu.payload());
reply->setFinished();
}
void Coap::processTokenBasedResponse(CoapReply *reply, const CoapPdu &pdu)
{
// Separate Response
CoapPdu responsePdu;
responsePdu.setMessageType(CoapPdu::Acknowledgement);
responsePdu.setStatusCode(CoapPdu::Empty);
responsePdu.setMessageId(pdu.messageId());
sendCoapPdu(reply->hostAddress(), reply->port(), responsePdu);
reply->setStatusCode(pdu.statusCode());
reply->setContentType(pdu.contentType());
reply->appendPayloadData(pdu.payload());
reply->setFinished();
}
void Coap::processBlock1Response(CoapReply *reply, const CoapPdu &pdu)
{
qDebug() << "sent successfully block #" << pdu.block().blockNumber();
// create next block
int index = (pdu.block().blockNumber() * 64) + 64;
QByteArray newBlockData = reply->requestPayload().mid(index, 64);
bool moreFlag = true;
// check if this was the last block
if (newBlockData.isEmpty()) {
reply->setStatusCode(pdu.statusCode());
reply->setContentType(pdu.contentType());
reply->setFinished();
return;
}
// check if this is the last block or there will be no next block
if (newBlockData.size() < 64 || (index + 64) == reply->requestPayload().size())
moreFlag = false;
CoapPdu nextBlockRequest;
nextBlockRequest.setContentType(reply->request().contentType());
nextBlockRequest.setMessageType(reply->request().messageType());
nextBlockRequest.setStatusCode(reply->requestMethod());
nextBlockRequest.setMessageId(pdu.messageId() + 1);
nextBlockRequest.setToken(pdu.token());
if (reply->m_lockedUp)
nextBlockRequest.addOption(CoapOption::UriHost, reply->request().url().host().toUtf8());
if (reply->port() != 5683)
nextBlockRequest.addOption(CoapOption::UriPort, QByteArray::number(reply->request().url().port()));
QStringList urlTokens = reply->request().url().path().split("/");
urlTokens.removeAll(QString());
foreach (const QString &token, urlTokens)
nextBlockRequest.addOption(CoapOption::UriPath, token.toUtf8());
if (reply->request().url().hasQuery())
nextBlockRequest.addOption(CoapOption::UriQuery, reply->request().url().query().toUtf8());
nextBlockRequest.addOption(CoapOption::Block1, CoapPduBlock::createBlock(pdu.block().blockNumber() + 1, 2, moreFlag));
nextBlockRequest.setPayload(newBlockData);
QByteArray pduData = nextBlockRequest.pack();
reply->setRequestData(pduData);
reply->m_timer->start();
reply->m_retransmissions = 1;
reply->setMessageId(nextBlockRequest.messageId());
qDebug() << "--->" << nextBlockRequest;
sendData(reply->hostAddress(), reply->port(), pduData);
}
void Coap::processBlock2Response(CoapReply *reply, const CoapPdu &pdu)
{
reply->appendPayloadData(pdu.payload());
// check if this was the last block
if (!pdu.block().moreFlag()) {
reply->setStatusCode(pdu.statusCode());
reply->setContentType(pdu.contentType());
reply->setFinished();
return;
}
CoapPdu nextBlockRequest;
nextBlockRequest.setContentType(reply->request().contentType());
nextBlockRequest.setMessageType(reply->request().messageType());
nextBlockRequest.setStatusCode(reply->requestMethod());
nextBlockRequest.setMessageId(pdu.messageId() + 1);
nextBlockRequest.setToken(pdu.token());
if (reply->m_lockedUp)
nextBlockRequest.addOption(CoapOption::UriHost, reply->request().url().host().toUtf8());
if (reply->port() != 5683)
nextBlockRequest.addOption(CoapOption::UriPort, QByteArray::number(reply->request().url().port()));
QStringList urlTokens = reply->request().url().path().split("/");
urlTokens.removeAll(QString());
foreach (const QString &token, urlTokens)
nextBlockRequest.addOption(CoapOption::UriPath, token.toUtf8());
if (reply->request().url().hasQuery())
nextBlockRequest.addOption(CoapOption::UriQuery, reply->request().url().query().toUtf8());
nextBlockRequest.addOption(CoapOption::Block2, CoapPduBlock::createBlock(pdu.block().blockNumber() + 1, 2, false));
QByteArray pduData = nextBlockRequest.pack();
reply->setRequestData(pduData);
reply->m_timer->start();
reply->setMessageId(nextBlockRequest.messageId());
qDebug() << "--->" << nextBlockRequest;
sendData(reply->hostAddress(), reply->port(), pduData);
}
void Coap::hostLookupFinished(const QHostInfo &hostInfo)
{
CoapReply *reply = m_runningHostLookups.take(hostInfo.lookupId());;
reply->setPort(reply->request().url().port(5683));
if (hostInfo.error() != QHostInfo::NoError) {
qDebug() << "Host lookup for" << reply->request().url().host() << "failed:" << hostInfo.errorString();
reply->setError(CoapReply::HostNotFoundError);
reply->setFinished();
return;
}
QHostAddress hostAddress = hostInfo.addresses().first();
reply->setHostAddress(hostAddress);
// check if the url had to be looked up
if (reply->request().url().host() != hostAddress.toString()) {
qDebug() << reply->request().url().host() << " -> " << hostAddress.toString();
sendRequest(reply, true);
} else {
sendRequest(reply);
}
}
void Coap::onReadyRead()
{
QHostAddress hostAddress;
QByteArray data;
quint16 port;
while (m_socket->hasPendingDatagrams()) {
data.resize(m_socket->pendingDatagramSize());
m_socket->readDatagram(data.data(), data.size(), &hostAddress, &port);
}
CoapPdu pdu(data);
processResponse(pdu);
}
void Coap::onReplyTimeout()
{
CoapReply *reply = qobject_cast<CoapReply *>(sender());
if (reply->m_retransmissions < 5) {
qDebug() << QString("Reply timeout: resending message %1/4").arg(reply->m_retransmissions);
}
reply->resend();
m_socket->writeDatagram(reply->requestData(), reply->hostAddress(), reply->port());
}
void Coap::onReplyFinished()
{
CoapReply *reply = qobject_cast<CoapReply *>(sender());
if (reply != m_reply)
qWarning() << "This should never happen!! Please report a bug if you get this message!";
emit replyFinished(reply);
m_reply = 0;
// check if there is a request in the queue
if (!m_replyQueue.isEmpty()) {
m_reply = m_replyQueue.dequeue();
if (m_reply)
lookupHost();
}
}

84
libguh/coap/coap.h Normal file
View File

@ -0,0 +1,84 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef COAP_H
#define COAP_H
#include <QObject>
#include <QHostInfo>
#include <QUdpSocket>
#include <QHostAddress>
#include <QQueue>
#include "coaprequest.h"
#include "coapreply.h"
/* Information about CoAP
*
* The Constrained Application Protocol (CoAP) : https://tools.ietf.org/html/rfc7252
* Blockwise transfers in CoAP : https://tools.ietf.org/html/draft-ietf-core-block-18
* Constrained RESTful Environments (CoRE) Link Format : http://tools.ietf.org/html/rfc6690
* Observing Resources in CoAP : https://tools.ietf.org/html/rfc7641
*/
class Coap : public QObject
{
Q_OBJECT
public:
explicit Coap(QObject *parent = 0, const quint16 &port = 5683);
CoapReply *ping(const CoapRequest &request);
CoapReply *get(const CoapRequest &request);
CoapReply *put(const CoapRequest &request, const QByteArray &data = QByteArray());
CoapReply *post(const CoapRequest &request, const QByteArray &data = QByteArray());
CoapReply *deleteResource(const CoapRequest &request);
private:
QUdpSocket *m_socket;
CoapReply *m_reply;
QHash<int, CoapReply *> m_runningHostLookups;
QQueue<CoapReply *> m_replyQueue;
void lookupHost();
void sendRequest(CoapReply *reply, const bool &lookedUp = false);
void sendData(const QHostAddress &hostAddress, const quint16 &port, const QByteArray &data);
void sendCoapPdu(const QHostAddress &hostAddress, const quint16 &port, const CoapPdu &pdu);
void processResponse(const CoapPdu &pdu);
void processIdBasedResponse(CoapReply *reply, const CoapPdu &pdu);
void processTokenBasedResponse(CoapReply *reply, const CoapPdu &pdu);
void processBlock1Response(CoapReply *reply, const CoapPdu &pdu);
void processBlock2Response(CoapReply *reply, const CoapPdu &pdu);
signals:
void replyFinished(CoapReply *reply);
private slots:
void hostLookupFinished(const QHostInfo &hostInfo);
void onReadyRead();
void onReplyTimeout();
void onReplyFinished();
};
#endif // COAP_H

24
libguh/coap/coap.pri Normal file
View File

@ -0,0 +1,24 @@
QT += network
QMAKE_CXXFLAGS += -Werror
CONFIG += c++11
SOURCES += \
$$PWD/coap.cpp \
$$PWD/coappdu.cpp \
$$PWD/coapoption.cpp \
$$PWD/coaprequest.cpp \
$$PWD/coapreply.cpp \
$$PWD/coappdublock.cpp \
$$PWD/corelinkparser.cpp \
$$PWD/corelink.cpp
HEADERS += \
$$PWD/coap.h \
$$PWD/coappdu.h \
$$PWD/coapoption.h \
$$PWD/coaprequest.h \
$$PWD/coapreply.h \
$$PWD/coappdublock.h \
$$PWD/corelinkparser.h \
$$PWD/corelink.h

105
libguh/coap/coapoption.cpp Normal file
View File

@ -0,0 +1,105 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "coapoption.h"
#include <QMetaEnum>
CoapOption::CoapOption()
{
}
CoapOption::CoapOption(const CoapOption::Option &option, const QByteArray &data) :
m_option(option),
m_data(data)
{
}
void CoapOption::setOption(const CoapOption::Option &option)
{
m_option = option;
}
CoapOption::Option CoapOption::option() const
{
return m_option;
}
void CoapOption::setData(const QByteArray &data)
{
m_data = data;
}
QByteArray CoapOption::data() const
{
return m_data;
}
#include "coappdu.h"
QDebug operator<<(QDebug debug, const CoapOption &coapOption)
{
const QMetaObject &metaObject = CoapOption::staticMetaObject;
QMetaEnum optionEnum = metaObject.enumerator(metaObject.indexOfEnumerator("Option"));
switch (coapOption.option()) {
case CoapOption::ETag:
debug.nospace() << "CoapOption(" << optionEnum.valueToKey(coapOption.option()) << "): " << "0x" + coapOption.data().toHex() << endl;
break;
case CoapOption::UriHost:
debug.nospace() << "CoapOption(" << optionEnum.valueToKey(coapOption.option()) << "): " << coapOption.data() << endl;
break;
case CoapOption::UriPath:
debug.nospace() << "CoapOption(" << optionEnum.valueToKey(coapOption.option()) << "): " << coapOption.data() << endl;
break;
case CoapOption::UriQuery:
debug.nospace() << "CoapOption(" << optionEnum.valueToKey(coapOption.option()) << "): " << coapOption.data() << endl;
break;
case CoapOption::ContentFormat: {
const QMetaObject &pduMetaObject = CoapPdu::staticMetaObject;
QMetaEnum contentEnum = pduMetaObject.enumerator(pduMetaObject.indexOfEnumerator("ContentType"));
debug.nospace() << "CoapOption(" << optionEnum.valueToKey(coapOption.option()) << "): " << contentEnum.valueToKey(static_cast<CoapPdu::ContentType>(coapOption.data().toHex().toInt(0, 16))) << endl;
break;
}
case CoapOption::Block1: {
// SZX = size exponent
CoapPduBlock block(coapOption.data());
debug.nospace() << "CoapOption(" << optionEnum.valueToKey(coapOption.option()) << "): " << coapOption.data().toHex() << " Block #" << block.blockNumber() << ", More flag = " << block.moreFlag() << ", SZX:" << block.blockSize() << endl;
break;
}
case CoapOption::Block2: {
// SZX = size exponent
CoapPduBlock block(coapOption.data());
debug.nospace() << "CoapOption(" << optionEnum.valueToKey(coapOption.option()) << "): " << coapOption.data().toHex() << " Block #" << block.blockNumber() << ", More flag = " << block.moreFlag() << ", SZX:" << block.blockSize() << endl;
break;
}
default:
QString optionName = optionEnum.valueToKey(coapOption.option());
if (optionName.isNull()) {
debug.nospace() << "CoapOption(" << "Unknown" << "): " << "value = " << coapOption.option() << " -> " << coapOption.data() << " = " << "0x" + coapOption.data().toHex() << endl;
} else {
debug.nospace() << "CoapOption(" << optionName << "): " << coapOption.data() << " = " << "0x" + coapOption.data().toHex() << endl;
}
break;
}
return debug.space();
}

73
libguh/coap/coapoption.h Normal file
View File

@ -0,0 +1,73 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef COAPOPTION_H
#define COAPOPTION_H
#include <QDebug>
#include <QObject>
#include <QByteArray>
class CoapOption
{
Q_GADGET
Q_ENUMS(Option)
public:
// Options format: https://tools.ietf.org/html/rfc7252#section-3.1
enum Option {
IfMatch = 1,
UriHost = 3,
ETag = 4,
IfNoneMatch = 5,
UriPort = 7,
LocationPath = 8,
UriPath = 11,
ContentFormat = 12,
MaxAge = 14,
UriQuery = 15,
Accept = 17,
LocationQuery = 20,
Block2 = 23, // (Block)
Block1 = 27, // (Block)
ProxyUri = 35,
ProxyScheme = 39,
Size1 = 60
};
CoapOption();
CoapOption(const Option &option, const QByteArray &data);
void setOption(const Option &option);
Option option() const;
void setData(const QByteArray &data);
QByteArray data() const;
private:
Option m_option;
QByteArray m_data;
};
Q_DECLARE_METATYPE(CoapOption)
QDebug operator<<(QDebug debug, const CoapOption &coapOption);
#endif // COAPOPTION_H

412
libguh/coap/coappdu.cpp Normal file
View File

@ -0,0 +1,412 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "coappdu.h"
#include "coapoption.h"
#include <QMetaEnum>
#include <QTime>
CoapPdu::CoapPdu(QObject *parent) :
QObject(parent),
m_version(1),
m_messageType(Confirmable),
m_statusCode(Empty),
m_messageId(0),
m_contentType(TextPlain),
m_payload(QByteArray()),
m_error(NoError)
{
qsrand(QDateTime::currentMSecsSinceEpoch());
}
CoapPdu::CoapPdu(const QByteArray &data, QObject *parent) :
QObject(parent),
m_version(1),
m_messageType(Confirmable),
m_statusCode(Empty),
m_messageId(0),
m_contentType(TextPlain),
m_payload(QByteArray()),
m_error(NoError)
{
qsrand(QDateTime::currentMSecsSinceEpoch());
unpack(data);
}
QString CoapPdu::getStatusCodeString(const CoapPdu::StatusCode &statusCode)
{
QString statusCodeString;
const QMetaObject &metaObject = CoapPdu::staticMetaObject;
QMetaEnum statusCodeEnum = metaObject.enumerator(metaObject.indexOfEnumerator("StatusCode"));
int classNumber = (statusCode & 0xE0) >> 5;
int detailNumber = statusCode & 0x1F;
statusCodeString.append(QString::number(classNumber) + ".");
if (detailNumber < 10)
statusCodeString.append("0");
statusCodeString.append(QString::number(detailNumber) + " ");
statusCodeString.append(statusCodeEnum.valueToKey(statusCode));
return statusCodeString;
}
quint8 CoapPdu::version() const
{
return m_version;
}
void CoapPdu::setVersion(const quint8 &version)
{
m_version = version;
}
CoapPdu::MessageType CoapPdu::messageType() const
{
return m_messageType;
}
void CoapPdu::setMessageType(const CoapPdu::MessageType &messageType)
{
m_messageType = messageType;
}
CoapPdu::StatusCode CoapPdu::statusCode() const
{
return m_statusCode;
}
void CoapPdu::setStatusCode(const CoapPdu::StatusCode &statusCode)
{
m_statusCode = statusCode;
}
quint16 CoapPdu::messageId() const
{
return m_messageId;
}
void CoapPdu::createMessageId()
{
setMessageId((quint16)qrand() % 65536);
}
void CoapPdu::setMessageId(const quint16 &messageId)
{
m_messageId = messageId;
}
CoapPdu::ContentType CoapPdu::contentType() const
{
return m_contentType;
}
void CoapPdu::setContentType(const CoapPdu::ContentType &contentType)
{
// TODO: add the contentFormat option
m_contentType = contentType;
}
QByteArray CoapPdu::token() const
{
return m_token;
}
void CoapPdu::createToken()
{
m_token.clear();
// make sure that the toke has a minimum size of 1
quint8 length = (quint8)(qrand() % 7) + 1;
for (int i = 0; i < length; i++) {
m_token.append((char)qrand() % 256);
}
}
void CoapPdu::setToken(const QByteArray &token)
{
m_token = token;
}
QByteArray CoapPdu::payload() const
{
return m_payload;
}
void CoapPdu::setPayload(const QByteArray &payload)
{
m_payload = payload;
}
QList<CoapOption> CoapPdu::options() const
{
return m_options;
}
void CoapPdu::addOption(const CoapOption::Option &option, const QByteArray &data)
{
// set pdu data from the option
switch (option) {
case CoapOption::ContentFormat: {
if (data.isEmpty()) {
setContentType(TextPlain);
} else {
setContentType(static_cast<ContentType>(data.toHex().toInt(0, 16)));
}
break;
}
case CoapOption::Block1: {
m_block = CoapPduBlock(data);
break;
}
case CoapOption::Block2: {
m_block = CoapPduBlock(data);
break;
}
default:
break;
}
// insert option (keep the list sorted to ensure a positiv option delta)
int index = 0;
for (int i = 0; i < m_options.length(); i ++) {
index = i;
if (m_options.at(i).option() <= option) {
continue;
} else {
break;
}
}
m_options.insert(index + 1, CoapOption(option, data));
}
CoapPduBlock CoapPdu::block() const
{
return m_block;
}
bool CoapPdu::hasOption(const CoapOption::Option &option) const
{
foreach (const CoapOption &o, m_options) {
if (o.option() == option)
return true;
}
return false;
}
void CoapPdu::clear()
{
m_version = 1;
m_messageType = Confirmable;
m_statusCode = Empty;
m_messageId = 0;
m_contentType = TextPlain;
m_token.clear();
m_payload.clear();
m_options.clear();
m_error = NoError;
}
bool CoapPdu::isValid() const
{
return (m_error == NoError);
}
QByteArray CoapPdu::pack() const
{
QByteArray pduData;
// header
QByteArray header;
header.resize(4);
header.fill('0');
quint8 *rawHeader = (quint8 *)header.data();
rawHeader[0] = m_version << 6;
rawHeader[0] |= (quint8)m_messageType << 4;
rawHeader[0] |= (quint8)m_token.size();
rawHeader[1] = (quint8)m_statusCode;
rawHeader[2] = (quint8)(m_messageId >> 8);
rawHeader[3] = (quint8)(m_messageId & 0xff);
pduData = QByteArray::fromRawData((char *)rawHeader, 4);
// token
pduData.append(m_token);
// options
QByteArray optionsData;
quint8 prevOption = 0;
foreach (const CoapOption &option, m_options) {
quint8 optionByte = 0;
// encode option delta
quint16 optionDelta = (quint8)option.option() - prevOption;
prevOption = (quint8)option.option();
quint8 extendedOptionDeltaByte = 0;
quint16 bigExtendedOptionDeltaByte = 0;
if (optionDelta < 13) {
optionByte = optionDelta << 4;
} else if (optionDelta < 270) {
// extended 8 bit option delta
optionByte = 13 << 4;
extendedOptionDeltaByte = optionDelta - 13;
} else {
// extended 16 bit option delta
optionByte = 14 << 4;
bigExtendedOptionDeltaByte = ((optionDelta - 269) >> 8) & 0xff;
bigExtendedOptionDeltaByte = (optionDelta - 269) & 0xff;
}
// encode option length
int optionLength = option.data().length();
quint8 extendedOptionLengthByte = 0;
quint16 bigExtendedOptionLengthByte = 0;
if (optionLength < 13) {
optionByte |= optionLength;
} else if (optionLength < 270) {
// extended 8 bit option length
optionByte |= 13;
extendedOptionLengthByte = optionLength - 13;
} else {
// extended 16 bit option length
optionByte |= 14;
bigExtendedOptionLengthByte = ((optionLength - 269) >> 8) & 0xff;
bigExtendedOptionLengthByte = (optionLength - 269) & 0xff;
}
// add obligatory option byte
pduData.append((char)optionByte);
// check extended option delta bytes
if (extendedOptionDeltaByte != 0)
pduData.append((char)extendedOptionDeltaByte);
if (bigExtendedOptionDeltaByte != 0)
pduData.append((char)bigExtendedOptionDeltaByte);
// check extended option length bytes
if (extendedOptionLengthByte != 0)
pduData.append((char)extendedOptionLengthByte);
if (bigExtendedOptionLengthByte != 0)
pduData.append((char)extendedOptionLengthByte);
// add the option data
pduData.append(option.data());
}
pduData.append(optionsData);
if (!m_payload.isEmpty()) {
pduData.append((char)255);
pduData.append(m_payload.data());
}
return pduData;
}
void CoapPdu::unpack(const QByteArray &data)
{
// create a CoapPDU
if (data.length() < 4) {
m_error = InvalidPduSizeError;
qWarning() << "pdu to small" << data.length();
}
quint8 *rawData = (quint8 *)data.data();
setVersion((rawData[0] & 0xc0) >> 6);
setMessageType(static_cast<MessageType>((rawData[0] & 0x30) >> 4));
quint8 tokenLength = (rawData[0] & 0xf);
if (tokenLength > 8) {
m_error = InvalidTokenError;
qWarning() << "PDU token to long";
}
setToken(QByteArray((const char *)rawData + 4, tokenLength));
setStatusCode(static_cast<StatusCode>(rawData[1]));
setMessageId((qint16)data.mid(2,2).toHex().toUInt(0,16));
// parse options
int index = 4 + tokenLength;
quint8 optionByte = rawData[index];
quint16 delta = 0;
while (QByteArray::number(optionByte, 16) != "ff" && optionByte != 0) {
quint16 optionNumber = ((optionByte & 0xf0) >> 4);
// check option delta
if (optionNumber < 13) {
delta += optionNumber;
} else if (optionNumber == 13) {
// extended 8 bit option delta
delta += (quint8)(rawData[index + 1] + 13);
index += 1;
} else if (optionNumber == 14) {
// extended 16 bit option delta
delta += ((rawData[index + 1] << 8) | rawData[index + 2]) + 269;
index += 2;
} else if (optionNumber == 15) {
m_error = InvalidOptionDeltaError;
}
// check option length
quint16 optionLength = (optionByte & 0xf);
if (optionLength == 13) {
// extended 8 bit option length
optionLength = (quint8)(rawData[index + 1] - 13);
index += 1;
} else if (optionLength == 14) {
// extended 16 bit option delta
optionLength = ((rawData[index + 1] << 8) | rawData[index + 2]) - 269;
index += 2;
} else if (optionLength == 15) {
m_error = InvalidOptionLengthError;
}
QByteArray optionData = QByteArray((const char *)rawData + index + 1, optionLength);
addOption(static_cast<CoapOption::Option>(delta), optionData);
index += optionLength + 1;
optionByte = rawData[index];
if (QByteArray::number(optionByte, 16) == "ff") {
setPayload(data.right(data.length() - index - 1));
break;
}
}
}
QDebug operator<<(QDebug debug, const CoapPdu &coapPdu)
{
const QMetaObject &metaObject = CoapPdu::staticMetaObject;
QMetaEnum messageTypeEnum = metaObject.enumerator(metaObject.indexOfEnumerator("MessageType"));
debug.nospace() << "CoapPdu(" << messageTypeEnum.valueToKey(coapPdu.messageType()) << ")" << endl;
debug.nospace() << " Code: " << CoapPdu::getStatusCodeString(coapPdu.statusCode()) << endl;
debug.nospace() << " Ver: " << coapPdu.version() << endl;
debug.nospace() << " Token: " << coapPdu.token().length() << " " << "0x"+ coapPdu.token().toHex() << endl;
debug.nospace() << " Message ID: " << coapPdu.messageId() << endl;
debug.nospace() << " Payload size: " << coapPdu.payload().size() << endl;
foreach (const CoapOption &option, coapPdu.options()) {
debug.nospace() << " " << option;
}
if (!coapPdu.payload().isEmpty())
debug.nospace() << endl << coapPdu.payload() << endl;
return debug.space();
}

175
libguh/coap/coappdu.h Normal file
View File

@ -0,0 +1,175 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef COAPPDU_H
#define COAPPDU_H
#include <QDebug>
#include <QObject>
#include "coapoption.h"
#include "coappdublock.h"
// PDU = Protocol Data Unit
/* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |Ver| T | TKL | Code | Message ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Token (if any, TKL bytes) ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Options (if any) ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |1 1 1 1 1 1 1 1| Payload (if any) ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
class CoapPdu : public QObject
{
Q_OBJECT
Q_ENUMS(MessageType)
Q_ENUMS(StatusCode)
Q_ENUMS(ContentType)
public:
enum MessageType {
Confirmable = 0x00,
NonConfirmable = 0x01,
Acknowledgement = 0x02,
Reset = 0x03
};
// Methods: https://tools.ietf.org/html/rfc7252#section-5.8
// Respond codes: https://tools.ietf.org/html/rfc7252#section-12.1.2
enum StatusCode {
Empty = 0x00, // Empty mesage (ping)
Get = 0x01, // Method GET
Post = 0x02, // Method POST
Put = 0x03, // Method PUT
Delete = 0x04, // Method DELETE
Created = 0x41, // 2.01
Deleted = 0x42, // 2.02
Valid = 0x43, // 2.03
Changed = 0x44, // 2.04
Content = 0x45, // 2.05
Continue = 0x5f, // 2.31 (Block)
BadRequest = 0x80, // 4.00
Unauthorized = 0x81, // 4.01
BadOption = 0x82, // 4.02
Forbidden = 0x83, // 4.03
NotFound = 0x84, // 4.04
MethodNotAllowed = 0x85, // 4.05
NotAcceptable = 0x86, // 4.06
RequestEntityIncomplete = 0x88, // 4.08 (Block)
PreconditionFailed = 0x8c, // 4.12
RequestEntityTooLarge = 0x8d, // 4.13 (Block)
UnsupportedContentFormat = 0x8f, // 4.15
InternalServerError = 0xa0, // 5.00
NotImplemented = 0xa1, // 5.01
BadGateway = 0xa2, // 5.02
ServiceUnavailabl = 0xa3, // 5.03
GatewayTimeout = 0xa4, // 5.04
ProxyingNotSupported = 0xa5 // 5.05
};
// https://tools.ietf.org/html/rfc7252#section-12.3
enum ContentType {
TextPlain = 0,
ApplicationLink = 40,
ApplicationXml = 41,
ApplicationOctet = 42,
ApplicationExi = 47,
ApplicationJson = 50
};
enum Error {
NoError,
InvalidTokenError,
InvalidPduSizeError,
InvalidOptionDeltaError,
InvalidOptionLengthError,
UnknownOptionError
};
CoapPdu(QObject *parent = 0);
CoapPdu(const QByteArray &data, QObject *parent = 0);
static QString getStatusCodeString(const StatusCode &statusCode);
// header fields
quint8 version() const;
void setVersion(const quint8 &version);
MessageType messageType() const;
void setMessageType(const MessageType &messageType);
StatusCode statusCode() const;
void setStatusCode(const StatusCode &statusCode);
quint16 messageId() const;
void createMessageId();
void setMessageId(const quint16 &messageId);
ContentType contentType() const;
void setContentType(const ContentType &contentType);
QByteArray token() const;
void createToken();
void setToken(const QByteArray &token);
// payload
QByteArray payload() const;
void setPayload(const QByteArray &payload);
QList<CoapOption> options() const;
void addOption(const CoapOption::Option &option, const QByteArray &data);
CoapPduBlock block() const;
bool hasOption(const CoapOption::Option &option) const;
void clear();
bool isValid() const;
bool isNull() const;
QByteArray pack() const;
private:
quint8 m_version;
MessageType m_messageType;
StatusCode m_statusCode;
quint16 m_messageId;
ContentType m_contentType;
QByteArray m_token;
QByteArray m_payload;
QList<CoapOption> m_options;
CoapPduBlock m_block;
Error m_error;
void unpack(const QByteArray &data);
};
QDebug operator<<(QDebug debug, const CoapPdu &coapPdu);
#endif // COAPPDU_H

View File

@ -0,0 +1,76 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "coappdublock.h"
CoapPduBlock::CoapPduBlock()
{
}
CoapPduBlock::CoapPduBlock(const QByteArray &blockData)
{
if (blockData.size() == 1) {
quint8 block = (quint8)blockData.at(0);
m_blockNumber = (block & 0xf0) >> 4;
m_blockSize = (quint8) pow(2, (block & 0x07) + 4);
m_moreFlag = (bool)((block & 0x8) >> 3);
} else if (blockData.size() == 2) {
quint16 block = (quint16)blockData.toHex().toUInt(0, 16);
m_blockNumber = (int)(block & 0xff0) >> 4;
m_blockSize = (int) pow(2, (block & 0x07) + 4);
m_moreFlag = (bool)((block & 0x08) >> 3);
}
}
QByteArray CoapPduBlock::createBlock(const int &blockNumber, const int &blockSize, const bool &moreFlag)
{
QByteArray blockData;
if (blockNumber < 16) {
quint8 block = (quint8)blockSize;
block |= (quint8)moreFlag << 3;
block |= (quint8)blockNumber << 4;
blockData = QByteArray(1, (char)block);
} else {
quint16 block = (quint16)blockSize;
block |= (quint16)moreFlag << 3;
block |= (quint8)blockNumber << 4;
blockData.resize(2);
blockData.fill(0);
blockData[0] = (quint8)(block >> 8);
blockData[1] = (quint8)(block & 0xff);
}
return blockData;
}
int CoapPduBlock::blockNumber() const
{
return m_blockNumber;
}
int CoapPduBlock::blockSize() const
{
return m_blockSize;
}
bool CoapPduBlock::moreFlag() const
{
return m_moreFlag;
}

View File

@ -0,0 +1,45 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef COAPPDUBLOCK_H
#define COAPPDUBLOCK_H
#include <QByteArray>
class CoapPduBlock
{
public:
CoapPduBlock();
CoapPduBlock(const QByteArray &blockData);
static QByteArray createBlock(const int &blockNumber, const int &blockSize = 2, const bool &moreFlag = false);
int blockNumber() const;
int blockSize() const;
bool moreFlag() const;
private:
int m_blockNumber;
int m_blockSize;
bool m_moreFlag;
};
#endif // COAPPDUBLOCK_H

236
libguh/coap/coapreply.cpp Normal file
View File

@ -0,0 +1,236 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "coapreply.h"
#include "coappdu.h"
#include <QMetaEnum>
CoapRequest CoapReply::request() const
{
return m_request;
}
QByteArray CoapReply::payload() const
{
return m_payload;
}
bool CoapReply::isFinished() const
{
return m_isFinished;
}
bool CoapReply::isRunning() const
{
return m_timer->isActive();
}
CoapReply::Error CoapReply::error() const
{
return m_error;
}
QString CoapReply::errorString() const
{
QString errorString;
switch (m_error) {
case NoError:
break;
case HostNotFoundError:
errorString = "The remote host name was not found (invalid hostname).";
break;
case TimeoutError:
errorString = "The server did not respond after 4 retransmissions.";
break;
case InvalidUrlSchemeError:
errorString = "The given URL does not have a valid scheme.";
break;
case InvalidPduError:
errorString = "The package data unit (PDU) could not be parsed successfully.";
break;
default:
break;
}
return errorString;
}
CoapPdu::ContentType CoapReply::contentType() const
{
return m_contentType;
}
CoapPdu::MessageType CoapReply::messageType() const
{
return m_messageType;
}
CoapPdu::StatusCode CoapReply::statusCode() const
{
return m_statusCode;
}
CoapReply::CoapReply(const CoapRequest &request, QObject *parent) :
QObject(parent),
m_request(request),
m_error(NoError),
m_isFinished(false),
m_retransmissions(1),
m_contentType(CoapPdu::TextPlain),
m_messageType(CoapPdu::Acknowledgement),
m_statusCode(CoapPdu::Empty),
m_lockedUp(false)
{
m_timer = new QTimer(this);
m_timer->setSingleShot(false);
m_timer->setInterval(2000);
connect(m_timer, &QTimer::timeout, this, &CoapReply::timeout);
}
QByteArray CoapReply::requestData() const
{
return m_requestData;
}
int CoapReply::messageId() const
{
return m_messageId;
}
void CoapReply::setMessageId(const int &messageId)
{
m_messageId = messageId;
}
QByteArray CoapReply::messageToken() const
{
return m_messageToken;
}
void CoapReply::setMessageToken(const QByteArray &messageToken)
{
m_messageToken = messageToken;
}
void CoapReply::setFinished()
{
m_isFinished = true;
m_timer->stop();
emit finished();
}
void CoapReply::setError(const CoapReply::Error &code)
{
m_error = code;
emit error(m_error);
}
void CoapReply::resend()
{
m_retransmissions++;
if (m_retransmissions > 5) {
setError(CoapReply::TimeoutError);
setFinished();
}
}
void CoapReply::setContentType(const CoapPdu::ContentType contentType)
{
m_contentType = contentType;
}
void CoapReply::setMessageType(const CoapPdu::MessageType &messageType)
{
m_messageType = messageType;
}
void CoapReply::setStatusCode(const CoapPdu::StatusCode &statusCode)
{
m_statusCode = statusCode;
}
void CoapReply::setHostAddress(const QHostAddress &address)
{
m_hostAddress = address;
}
QHostAddress CoapReply::hostAddress() const
{
return m_hostAddress;
}
void CoapReply::setPort(const int &port)
{
m_port = port;
}
int CoapReply::port() const
{
return m_port;
}
void CoapReply::setRequestPayload(const QByteArray &requestPayload)
{
m_requestPayload = requestPayload;
}
QByteArray CoapReply::requestPayload() const
{
return m_requestPayload;
}
void CoapReply::setRequestMethod(const CoapPdu::StatusCode &method)
{
m_requestMethod = method;
}
CoapPdu::StatusCode CoapReply::requestMethod() const
{
return m_requestMethod;
}
void CoapReply::appendPayloadData(const QByteArray &data)
{
m_payload.append(data);
m_timer->start();
m_retransmissions = 1;
}
void CoapReply::setRequestData(const QByteArray &requestData)
{
m_requestData = requestData;
}
QDebug operator<<(QDebug debug, CoapReply *reply)
{
const QMetaObject &metaObject = CoapPdu::staticMetaObject;
QMetaEnum messageTypeEnum = metaObject.enumerator(metaObject.indexOfEnumerator("MessageType"));
QMetaEnum contentTypeEnum = metaObject.enumerator(metaObject.indexOfEnumerator("ContentType"));
debug.nospace() << "CoapReply(" << messageTypeEnum.valueToKey(reply->messageType()) << ")" << endl;
debug.nospace() << " Status code: " << CoapPdu::getStatusCodeString(reply->statusCode()) << endl;
debug.nospace() << " Content type: " << contentTypeEnum.valueToKey(reply->contentType()) << endl;
debug.nospace() << " Payload size: " << reply->payload().size() << endl;
if (!reply->payload().isEmpty())
debug.nospace() << endl << reply->payload() << endl;
return debug.space();
}

126
libguh/coap/coapreply.h Normal file
View File

@ -0,0 +1,126 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef COAPREPLY_H
#define COAPREPLY_H
#include <QObject>
#include <QTimer>
#include "coappdu.h"
#include "coapoption.h"
#include "coaprequest.h"
class CoapReply : public QObject
{
friend class Coap;
Q_OBJECT
Q_ENUMS(Error)
public:
enum Error {
NoError,
HostNotFoundError,
TimeoutError,
InvalidUrlSchemeError,
InvalidPduError
};
CoapRequest request() const;
QByteArray payload() const;
bool isFinished() const;
bool isRunning() const;
Error error() const;
QString errorString() const;
CoapPdu::ContentType contentType() const;
CoapPdu::MessageType messageType() const;
CoapPdu::StatusCode statusCode() const;
private:
CoapReply(const CoapRequest &request, QObject *parent = 0);
void appendPayloadData(const QByteArray &data);
void setFinished();
void setError(const Error &error);
void resend();
void setContentType(const CoapPdu::ContentType contentType = CoapPdu::TextPlain);
void setMessageType(const CoapPdu::MessageType &messageType);
void setStatusCode(const CoapPdu::StatusCode &statusCode);
QTimer *m_timer;
CoapRequest m_request;
QByteArray m_payload;
Error m_error;
bool m_isFinished;
int m_retransmissions;
CoapPdu::ContentType m_contentType;
CoapPdu::MessageType m_messageType;
CoapPdu::StatusCode m_statusCode;
// data for the request
void setHostAddress(const QHostAddress &address);
QHostAddress hostAddress() const;
void setPort(const int &port);
int port() const;
void setRequestPayload(const QByteArray &requestPayload);
QByteArray requestPayload() const;
void setRequestMethod(const CoapPdu::StatusCode &method);
CoapPdu::StatusCode requestMethod() const;
void setRequestData(const QByteArray &requestData);
QByteArray requestData() const;
int messageId() const;
void setMessageId(const int &messageId);
QByteArray messageToken() const;
void setMessageToken(const QByteArray &messageToken);
QHostAddress m_hostAddress;
int m_port;
CoapPdu::StatusCode m_requestMethod;
QByteArray m_requestPayload;
QByteArray m_requestData;
bool m_lockedUp;
int m_messageId;
QByteArray m_messageToken;
signals:
void timeout();
void finished();
void error(const Error &code);
};
QDebug operator<<(QDebug debug, CoapReply *reply);
#endif // COAPREPLY_H

View File

@ -0,0 +1,72 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "coaprequest.h"
CoapRequest::CoapRequest(const QUrl &url) :
m_url(url),
m_contentType(CoapPdu::TextPlain),
m_messageType(CoapPdu::Confirmable),
m_statusCode(CoapPdu::Empty)
{
}
void CoapRequest::setUrl(const QUrl &url)
{
m_url = url;
}
QUrl CoapRequest::url() const
{
return m_url;
}
void CoapRequest::setContentType(const CoapPdu::ContentType contentType)
{
m_contentType = contentType;
}
CoapPdu::ContentType CoapRequest::contentType() const
{
return m_contentType;
}
void CoapRequest::setMessageType(const CoapPdu::MessageType &messageType)
{
m_messageType = messageType;
}
CoapPdu::MessageType CoapRequest::messageType() const
{
return m_messageType;
}
void CoapRequest::setStatusCode(const CoapPdu::StatusCode &statusCode)
{
m_statusCode = statusCode;
}
CoapPdu::StatusCode CoapRequest::statusCode()
{
return m_statusCode;
}

58
libguh/coap/coaprequest.h Normal file
View File

@ -0,0 +1,58 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef COAPREQUEST_H
#define COAPREQUEST_H
#include <QUrl>
#include <QHostAddress>
#include "coappdu.h"
#include "coapoption.h"
//class Coap;
class CoapRequest
{
// friend class Coap;
public:
CoapRequest(const QUrl &url = QUrl());
void setUrl(const QUrl &url);
QUrl url() const;
void setContentType(const CoapPdu::ContentType contentType = CoapPdu::TextPlain);
CoapPdu::ContentType contentType() const;
void setMessageType(const CoapPdu::MessageType &messageType);
CoapPdu::MessageType messageType() const;
private:
QUrl m_url;
CoapPdu::ContentType m_contentType;
CoapPdu::MessageType m_messageType;
CoapPdu::StatusCode m_statusCode;
void setStatusCode(const CoapPdu::StatusCode &statusCode);
CoapPdu::StatusCode statusCode();
};
#endif // COAPREQUEST_H

124
libguh/coap/corelink.cpp Normal file
View File

@ -0,0 +1,124 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "corelink.h"
#include <QMetaEnum>
CoreLink::CoreLink() :
m_contentType(CoapPdu::TextPlain),
m_maximumSize(-1),
m_observable(false)
{
}
QString CoreLink::path() const
{
return m_path;
}
void CoreLink::setPath(const QString &path)
{
m_path = path;
}
QString CoreLink::title() const
{
return m_title;
}
void CoreLink::setTitle(const QString &title)
{
m_title = title;
}
QString CoreLink::resourceType() const
{
return m_resourceType;
}
void CoreLink::setResourceType(const QString &resourceType)
{
m_resourceType = resourceType;
}
QString CoreLink::interfaceDescription() const
{
return m_interfaceDescription;
}
void CoreLink::setInterfaceDescription(const QString &interfaceDescription)
{
m_interfaceDescription = interfaceDescription;
}
CoapPdu::ContentType CoreLink::contentType() const
{
return m_contentType;
}
void CoreLink::setContentType(const CoapPdu::ContentType &contentType)
{
m_contentType = contentType;
}
int CoreLink::maximumSize() const
{
return m_maximumSize;
}
void CoreLink::setMaximumSize(const int &maximumSize)
{
m_maximumSize = maximumSize;
}
bool CoreLink::observable() const
{
return m_observable;
}
void CoreLink::setObservable(const bool &observable)
{
m_observable = observable;
}
QDebug operator<<(QDebug debug, const CoreLink &link)
{
const QMetaObject &metaObject = CoapPdu::staticMetaObject;
QMetaEnum contentTypeEnum = metaObject.enumerator(metaObject.indexOfEnumerator("ContentType"));
debug.nospace() << "CoapLink(" << link.path() << ")" << endl;
if (!link.title().isEmpty())
debug.nospace() << " Title: " << link.title() << endl;
debug.nospace() << " Resource type: " << link.resourceType() << endl;
debug.nospace() << " Content type: " << contentTypeEnum.valueToKey(link.contentType()) << endl;
if (link.observable())
debug.nospace() << " Observable: " << link.observable() << endl;
if (!link.interfaceDescription().isEmpty())
debug.nospace() << " Interface description: " << link.interfaceDescription() << endl;
if (link.maximumSize() >= 0)
debug.nospace() << " Maximum size: " << link.maximumSize() << endl;
return debug.space();
}

70
libguh/coap/corelink.h Normal file
View File

@ -0,0 +1,70 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef CORELINK_H
#define CORELINK_H
#include <QObject>
#include <QDebug>
#include "coappdu.h"
class CoreLink
{
public:
CoreLink();
QString path() const;
void setPath(const QString &path);
// link params
QString title() const;
void setTitle(const QString &title);
QString resourceType() const;
void setResourceType(const QString &resourceType);
QString interfaceDescription() const;
void setInterfaceDescription(const QString &interfaceDescription);
CoapPdu::ContentType contentType() const;
void setContentType(const CoapPdu::ContentType &contentType);
int maximumSize() const;
void setMaximumSize(const int &maximumSize);
bool observable() const;
void setObservable(const bool &observable);
private:
QString m_path;
QString m_title;
QString m_resourceType;
QString m_interfaceDescription;
CoapPdu::ContentType m_contentType;
int m_maximumSize;
bool m_observable;
};
QDebug operator<<(QDebug debug, const CoreLink &link);
#endif // CORELINK_H

View File

@ -0,0 +1,63 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "corelinkparser.h"
#include <QDebug>
CoreLinkParser::CoreLinkParser(const QByteArray &data, QObject *parent) :
QObject(parent),
m_data(data)
{
QList<QByteArray> linkList = data.split(',');
foreach (const QByteArray &linkLine, linkList) {
qDebug() << "-------------------------------------";
qDebug() << linkLine;
QList<QByteArray> valueList = linkLine.split(';');
CoreLink link;
foreach (const QByteArray &value, valueList) {
qDebug() << value;
if (value.startsWith("<")) {
link.setPath(QString(value.mid(1, value.length() - 2)));
} else if (value.startsWith("rt=")) {
link.setResourceType(QString(value.right(value.length() - 3)).remove('"'));
} else if (value.startsWith("if=")) {
link.setInterfaceDescription(QString(value.right(value.length() - 3)).remove('"'));
} else if (value.startsWith("sz=")) {
link.setMaximumSize(value.right(value.length() - 3).toInt());
} else if (value.startsWith("ct=")) {
link.setContentType(static_cast<CoapPdu::ContentType>(value.right(value.length() - 3).toUInt()));
} else if (value.startsWith("title=")) {
link.setTitle(QString(value.right(value.length() - 6)).remove('"'));
} else if (value == "obs") {
link.setObservable(true);
}
}
qDebug() << endl << link;
m_links.append(link);
}
}
QList<CoreLink> CoreLinkParser::links() const
{
return m_links;
}

View File

@ -0,0 +1,43 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of QtCoap. *
* *
* QtCoap is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 3 of the License. *
* *
* QtCoap is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with QtCoap. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef COREPARSER_H
#define COREPARSER_H
#include <QObject>
#include "corelink.h"
// Constrained RESTful Environments (CoRE) Link Format : http://tools.ietf.org/html/rfc6690
class CoreLinkParser : public QObject
{
Q_OBJECT
public:
explicit CoreLinkParser(const QByteArray &data, QObject *parent = 0);
QList<CoreLink> links() const;
private:
QByteArray m_data;
QList<CoreLink> m_links;
};
#endif // COREPARSER_H

View File

@ -16,6 +16,8 @@ contains(DEFINES, BLUETOOTH_LE) {
bluetooth/bluetoothlowenergydevice.h \
}
include(coap/coap.pri)
SOURCES += devicemanager.cpp \
loggingcategories.cpp \
guhsettings.cpp \

View File

@ -21,6 +21,7 @@ SUBDIRS += elro \
elgato \
awattar \
netatmo \
osdomotics \

View File

@ -0,0 +1,312 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\page osdomotics.html
\title OSDomotics
\ingroup plugins
\ingroup network
This plugin allows you to connect guh to a 6LoWPAN network by adding a Mercury Board from OSDomotics
as a RPL router to your devices \l{OSDomotics Tutorial}{http://osdwiki.open-entry.com/doku.php/de:tutorials:contiki:merkur_board_rpl_usb_router}.
All nodes in the 6LoWPAN network of the added RPL router will appear automatically in the system.
\note Currently the plugin recognizes only one node. That node has to be flashed like the Node in this \l{OSDomotics tutorial}{http://osdwiki.open-entry.com/doku.php/de:tutorials:contiki:use_example_firmware}.
\chapter Plugin properties
Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses}
and \l{Vendor}{Vendors} of this \l{DevicePlugin}.
Each \l{DeviceClass} has a list of \l{ParamType}{paramTypes}, \l{ActionType}{actionTypes}, \l{StateType}{stateTypes}
and \l{EventType}{eventTypes}. The \l{DeviceClass::CreateMethod}{createMethods} parameter describes how the \l{Device}
will be created in the system. A device can have more than one \l{DeviceClass::CreateMethod}{CreateMethod}.
The \l{DeviceClass::SetupMethod}{setupMethod} describes the setup method of the \l{Device}.
The detailed implementation of each \l{DeviceClass} can be found in the source code.
\note If a \l{StateType} has the parameter \tt{"writable": {...}}, an \l{ActionType} with the same uuid and \l{ParamType}{ParamTypes}
will be created automatically.
\quotefile plugins/deviceplugins/osdomotics/devicepluginosdomotics.json
*/
#include "devicepluginosdomotics.h"
#include "plugin/device.h"
#include "plugininfo.h"
DevicePluginOsdomotics::DevicePluginOsdomotics()
{
m_coap = new Coap(this);
connect(m_coap, SIGNAL(replyFinished(CoapReply*)), this, SLOT(coapReplyFinished(CoapReply*)));
}
DeviceManager::HardwareResources DevicePluginOsdomotics::requiredHardware() const
{
return DeviceManager::HardwareResourceNetworkManager | DeviceManager::HardwareResourceTimer;
}
DeviceManager::DeviceSetupStatus DevicePluginOsdomotics::setupDevice(Device *device)
{
if (device->deviceClassId() == rplRouterDeviceClassId) {
qCDebug(dcOsdomotics) << "setup RPL router" << device->paramValue("host").toString();
QHostAddress address(device->paramValue("host").toString());
if (address.isNull()) {
qCWarning(dcOsdomotics) << "Got invalid address" << device->paramValue("host").toString();
return DeviceManager::DeviceSetupStatusFailure;
}
QUrl url;
url.setScheme("http");
url.setHost(address.toString());
QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
m_asyncSetup.insert(reply, device);
return DeviceManager::DeviceSetupStatusAsync;
} else if (device->deviceClassId() == merkurNodeDeviceClassId) {
qCDebug(dcOsdomotics) << "setup Merkur node" << device->paramValue("host").toString();
device->setParentId(DeviceId(device->paramValue("router id").toString()));
return DeviceManager::DeviceSetupStatusSuccess;
}
return DeviceManager::DeviceSetupStatusFailure;
}
void DevicePluginOsdomotics::deviceRemoved(Device *device)
{
Q_UNUSED(device)
}
void DevicePluginOsdomotics::networkManagerReplyReady(QNetworkReply *reply)
{
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// create user finished
if (m_asyncSetup.contains(reply)) {
Device *device = m_asyncSetup.take(reply);
// check HTTP status code
if (status != 200) {
qCWarning(dcOsdomotics) << "Setup reply HTTP error:" << reply->errorString();
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
parseNodes(device, data);
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
} else if (m_asyncNodeRescans.contains(reply)) {
Device *device = m_asyncSetup.take(reply);
// check HTTP status code
if (status != 200) {
qCWarning(dcOsdomotics) << "Setup reply HTTP error:" << reply->errorString();
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure);
reply->deleteLater();
return;
}
QByteArray data = reply->readAll();
parseNodes(device, data);
}
reply->deleteLater();
}
void DevicePluginOsdomotics::postSetupDevice(Device *device)
{
updateNode(device);
}
void DevicePluginOsdomotics::guhTimer()
{
foreach (Device *device, myDevices()) {
if (device->deviceClassId() == merkurNodeDeviceClassId) {
updateNode(device);
} else if(device->deviceClassId() == rplRouterDeviceClassId) {
scanNodes(device);
}
}
}
DeviceManager::DeviceError DevicePluginOsdomotics::executeAction(Device *device, const Action &action)
{
if (device->deviceClassId() == merkurNodeDeviceClassId) {
if (action.actionTypeId() == toggleLedActionTypeId) {
QUrl url;
url.setScheme("coap");
url.setHost(device->paramValue("host").toString());
url.setPath("/actuators/toggle");
qCDebug(dcOsdomotics) << "Toggle light";
CoapReply *reply = m_coap->post(CoapRequest(url));
if (reply->isFinished()) {
if (reply->error() != CoapReply::NoError) {
qCWarning(dcOsdomotics) << "CoAP reply finished with error" << reply->errorString();
reply->deleteLater();
return DeviceManager::DeviceErrorHardwareNotAvailable;
}
}
m_toggleLightRequests.insert(reply, action);
return DeviceManager::DeviceErrorAsync;
}
return DeviceManager::DeviceErrorActionTypeNotFound;
}
return DeviceManager::DeviceErrorDeviceClassNotFound;
}
void DevicePluginOsdomotics::scanNodes(Device *device)
{
QHostAddress address(device->paramValue("host").toString());
qCDebug(dcOsdomotics) << "Scan for new nodes" << address.toString();
QUrl url;
url.setScheme("http");
url.setHost(address.toString());
QNetworkReply *reply = networkManagerGet(QNetworkRequest(url));
m_asyncNodeRescans.insert(reply, device);
}
void DevicePluginOsdomotics::parseNodes(Device *device, const QByteArray &data)
{
//qCDebug(dcOsdomotics) << data;
// TODO: get all nodes
// find better method to get nodes
int index = data.indexOf("Routes<pre>") + 11;
int delta = data.indexOf("/128",index);
QHostAddress nodeAddress(QString(data.mid(index, delta - index)));
// check if we allready have found this node
foreach (Device *device, myDevices()) {
if (device->paramValue("host").toString() == nodeAddress.toString()) {
return;
}
}
QUrl url;
url.setScheme("coap");
url.setHost(nodeAddress.toString());
url.setPath("/.well-known/core");
qCDebug(dcOsdomotics) << "discover node on" << url.toString();
CoapReply *reply = m_coap->get(CoapRequest(url));
if (reply->isFinished()) {
if (reply->error() != CoapReply::NoError) {
qCWarning(dcOsdomotics) << "Reply finished with error" << reply->errorString();
} else {
qCDebug(dcOsdomotics) << "Reply finished" << reply;
}
// Note: please don't forget to delete the reply
reply->deleteLater();
return;
}
m_discoveryRequests.insert(reply, device);
}
void DevicePluginOsdomotics::updateNode(Device *device)
{
qCDebug(dcOsdomotics) << "Update node" << device->paramValue("host").toString() << "battery value";
QUrl url;
url.setScheme("coap");
url.setHost(device->paramValue("host").toString());
url.setPath("/sensors/battery");
CoapReply *reply = m_coap->get(CoapRequest(url));
if (reply->isFinished()) {
if (reply->error() != CoapReply::NoError) {
qCWarning(dcOsdomotics) << "CoAP reply finished with error" << reply->errorString();
reply->deleteLater();
}
}
m_updateRequests.insert(reply, device);
}
Device *DevicePluginOsdomotics::findDevice(const QHostAddress &address)
{
foreach (Device *device, myDevices()) {
if (device->paramValue("host").toString() == address.toString()) {
return device;
}
}
return 0;
}
void DevicePluginOsdomotics::coapReplyFinished(CoapReply *reply)
{
qCDebug(dcOsdomotics) << "coap reply finished" << reply;
if (m_discoveryRequests.contains(reply)) {
Device *device = m_discoveryRequests.take(reply);
if (reply->error() != CoapReply::NoError) {
qCWarning(dcOsdomotics) << "CoAP discover reply finished with error" << reply->errorString();
reply->deleteLater();
return;
}
// TODO: parse CoRE links and get the type of the node
DeviceDescriptor descriptor(merkurNodeDeviceClassId, "Merkur Node", reply->request().url().host());
ParamList params;
params.append(Param("name", "Merkur Node"));
params.append(Param("host", reply->request().url().host()));
params.append(Param("router id", device->id()));
descriptor.setParams(params);
emit autoDevicesAppeared(merkurNodeDeviceClassId, QList<DeviceDescriptor>() << descriptor);
} else if (m_toggleLightRequests.contains(reply)) {
Action action = m_toggleLightRequests.take(reply);
if (reply->error() != CoapReply::NoError) {
qCWarning(dcOsdomotics) << "CoAP toggle reply finished with error" << reply->errorString();
reply->deleteLater();
emit actionExecutionFinished(action.id(), DeviceManager::DeviceErrorHardwareFailure);
return;
}
emit actionExecutionFinished(action.id(), DeviceManager::DeviceErrorNoError);
} else if (m_updateRequests.contains(reply)) {
Device *device = m_updateRequests.take(reply);
if (reply->error() != CoapReply::NoError) {
qCWarning(dcOsdomotics) << "CoAP update reply finished with error" << reply->errorString();
reply->deleteLater();
return;
}
int batteryValue = reply->payload().toInt();
qCDebug(dcOsdomotics) << "Node updated" << batteryValue;
device->setStateValue(batteryStateTypeId, batteryValue);
}
reply->deleteLater();
}

View File

@ -0,0 +1,70 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICEPLUGINOSDOMOTICS_H
#define DEVICEPLUGINOSDOMOTICS_H
#include "plugin/deviceplugin.h"
#include <QHash>
#include <QDebug>
#include "coap/coap.h"
class DevicePluginOsdomotics : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "guru.guh.DevicePlugin" FILE "devicepluginosdomotics.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginOsdomotics();
DeviceManager::HardwareResources requiredHardware() const override;
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
void deviceRemoved(Device *device) override;
void networkManagerReplyReady(QNetworkReply *reply) override;
void postSetupDevice(Device *device) override;
void guhTimer() override;
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
private:
Coap *m_coap;
QHash<QNetworkReply *, Device *> m_asyncSetup;
QHash<QNetworkReply *, Device *> m_asyncNodeRescans;
QHash<CoapReply *, Device *> m_discoveryRequests;
QHash<CoapReply *, Device *> m_updateRequests;
QHash<CoapReply *, Action> m_toggleLightRequests;
void scanNodes(Device *device);
void parseNodes(Device *device, const QByteArray &data);
void updateNode(Device *device);
Device *findDevice(const QHostAddress &address);
private slots:
void coapReplyFinished(CoapReply *reply);
};
#endif // DEVICEPLUGINOSDOMOTICS_H

View File

@ -0,0 +1,74 @@
{
"name": "OSDomotics",
"idName": "Osdomotics",
"id": "78927596-e266-4a23-8634-7311b2c5fe32",
"vendors": [
{
"name": "OSDomotics",
"idName": "osdomotics",
"id": "e2912117-3b2f-4888-950e-6d259b699102",
"deviceClasses": [
{
"deviceClassId": "57d1b080-36a0-46af-a676-78c6b78d08ae",
"idName": "rplRouter",
"name": "RPL Router",
"createMethods": ["user"],
"paramTypes": [
{
"name": "name",
"type": "QString",
"inputType": "TextLine",
"defaultValue": "Merkur RPL Router"
},
{
"name": "host",
"type": "QString",
"inputType": "IPv6Address"
}
]
},
{
"deviceClassId": "4454e05e-ac0a-4b10-b9dd-56a1475895d7",
"idName": "merkurNode",
"name": "Merkur Node",
"createMethods": ["auto"],
"paramTypes": [
{
"name": "name",
"type": "QString",
"inputType": "TextLine",
"defaultValue": "Merkur Node"
},
{
"name": "host",
"type": "QString",
"inputType": "IPv6Address",
"readOnly": true
},
{
"name": "router id",
"type": "QString",
"readOnly": true
}
],
"stateTypes": [
{
"id": "06cf4e66-f102-4a8e-ae76-fac250a07753",
"idName": "battery",
"name": "battery voltage",
"type": "double",
"defaultValue": 0
}
],
"actionTypes": [
{
"id": "a91db0e7-9d0f-4071-9a8f-2bda45ed4c9d",
"idName": "toggleLed",
"name": "toggle led"
}
]
}
]
}
]
}

View File

@ -0,0 +1,11 @@
include(../../plugins.pri)
TARGET = $$qtLibraryTarget(guh_devicepluginosdomotics)
SOURCES += \
devicepluginosdomotics.cpp
HEADERS += \
devicepluginosdomotics.h