add qtcoap to the lib
add osdomotics plugin
This commit is contained in:
parent
94202684d8
commit
b00bba1c2b
473
libguh/coap/coap.cpp
Normal file
473
libguh/coap/coap.cpp
Normal 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
84
libguh/coap/coap.h
Normal 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
24
libguh/coap/coap.pri
Normal 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
105
libguh/coap/coapoption.cpp
Normal 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
73
libguh/coap/coapoption.h
Normal 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
412
libguh/coap/coappdu.cpp
Normal 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
175
libguh/coap/coappdu.h
Normal 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
|
||||
76
libguh/coap/coappdublock.cpp
Normal file
76
libguh/coap/coappdublock.cpp
Normal 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;
|
||||
}
|
||||
|
||||
45
libguh/coap/coappdublock.h
Normal file
45
libguh/coap/coappdublock.h
Normal 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
236
libguh/coap/coapreply.cpp
Normal 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
126
libguh/coap/coapreply.h
Normal 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
|
||||
72
libguh/coap/coaprequest.cpp
Normal file
72
libguh/coap/coaprequest.cpp
Normal 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
58
libguh/coap/coaprequest.h
Normal 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
124
libguh/coap/corelink.cpp
Normal 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
70
libguh/coap/corelink.h
Normal 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
|
||||
63
libguh/coap/corelinkparser.cpp
Normal file
63
libguh/coap/corelinkparser.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
43
libguh/coap/corelinkparser.h
Normal file
43
libguh/coap/corelinkparser.h
Normal 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
|
||||
@ -16,6 +16,8 @@ contains(DEFINES, BLUETOOTH_LE) {
|
||||
bluetooth/bluetoothlowenergydevice.h \
|
||||
}
|
||||
|
||||
include(coap/coap.pri)
|
||||
|
||||
SOURCES += devicemanager.cpp \
|
||||
loggingcategories.cpp \
|
||||
guhsettings.cpp \
|
||||
|
||||
@ -21,6 +21,7 @@ SUBDIRS += elro \
|
||||
elgato \
|
||||
awattar \
|
||||
netatmo \
|
||||
osdomotics \
|
||||
|
||||
|
||||
|
||||
|
||||
312
plugins/deviceplugins/osdomotics/devicepluginosdomotics.cpp
Normal file
312
plugins/deviceplugins/osdomotics/devicepluginosdomotics.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
70
plugins/deviceplugins/osdomotics/devicepluginosdomotics.h
Normal file
70
plugins/deviceplugins/osdomotics/devicepluginosdomotics.h
Normal 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
|
||||
74
plugins/deviceplugins/osdomotics/devicepluginosdomotics.json
Normal file
74
plugins/deviceplugins/osdomotics/devicepluginosdomotics.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
11
plugins/deviceplugins/osdomotics/osdomotics.pro
Normal file
11
plugins/deviceplugins/osdomotics/osdomotics.pro
Normal file
@ -0,0 +1,11 @@
|
||||
include(../../plugins.pri)
|
||||
|
||||
TARGET = $$qtLibraryTarget(guh_devicepluginosdomotics)
|
||||
|
||||
SOURCES += \
|
||||
devicepluginosdomotics.cpp
|
||||
|
||||
HEADERS += \
|
||||
devicepluginosdomotics.h
|
||||
|
||||
|
||||
Reference in New Issue
Block a user