diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 0000000..226f02c --- /dev/null +++ b/.qmake.conf @@ -0,0 +1,2 @@ +TOP_SRCDIR=$$PWD +TOP_BUILDDIR=$$shadowed($$PWD) diff --git a/examples/client/client.pro b/examples/client/client.pro index a0db443..710ba1e 100644 --- a/examples/client/client.pro +++ b/examples/client/client.pro @@ -11,10 +11,10 @@ TARGET = client CONFIG += console CONFIG -= app_bundle QMAKE_CXXFLAGS += -std=c++11 -DESTDIR = $$PWD/../../bin +DESTDIR = $$TOP_SRCDIR/bin -LIBS += -L"$$PWD/../../lib" -lqcoap -INCLUDEPATH += $$PWD/../../src/lib +LIBS += -L"$$TOP_SRCDIR/bin" -lqcoap +INCLUDEPATH += $$TOP_SRCDIR/src/lib TEMPLATE = app diff --git a/examples/client/main.cpp b/examples/client/main.cpp index 217a6a6..c3d2737 100644 --- a/examples/client/main.cpp +++ b/examples/client/main.cpp @@ -63,11 +63,12 @@ void add() { CoapExchange *exchange = new CoapExchange; CoapUri uri; - uri.setHost(QHostAddress("134.102.218.16")); + uri.setHost(QHostAddress("127.0.0.1")); uri.setPort(5683); - uri.setPath("test"); + uri.setPath("hello"); exchange->setUri(uri); - exchange->onCompleted([](){qDebug() << "Lambda!";}); + exchange->onCompleted([=](){qDebug() << "Answer:" << exchange->answer();}); + exchange->onError([](){qDebug() << "Timeout";}); exchange->get(); } @@ -75,9 +76,10 @@ int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); -// CoapEndpoint endpoint(CoapEndpoint::ClientServer); - CoapTimerQueue q; -// endpoint.bind(QHostAddress::Any, 5683); + CoapEndpoint endpoint(CoapEndpoint::ClientServer); + endpoint.bind(QHostAddress::Any, 5688); + + add(); // add(); // endpoint.bind(QHostAddress::LocalHost); diff --git a/src/lib/coapendpoint_p.h b/src/lib/coapendpoint_p.h index 569a539..beee244 100644 --- a/src/lib/coapendpoint_p.h +++ b/src/lib/coapendpoint_p.h @@ -5,6 +5,7 @@ #include #include "coapendpoint.h" +#include "coaptimerqueue.h" class QUdpSocket; class CoapExchange; @@ -37,6 +38,7 @@ class CoapEndpointPrivate CoapEndpoint *q_ptr; quint16 currentMessageId; CoapEndpoint::Type type; + CoapTimerQueue timerQueue; QHash token2exchange; // on pdu rx used to find exchange QHash exchange2token; // on pdu tx used to find token diff --git a/src/lib/coapexchange.cpp b/src/lib/coapexchange.cpp index a6387aa..9994fe4 100644 --- a/src/lib/coapexchange.cpp +++ b/src/lib/coapexchange.cpp @@ -6,12 +6,7 @@ #include "coapendpoint_p.h" CoapExchangePrivate::CoapExchangePrivate() - : flags(Flags(0)), timeout(10000) -{ -} - -CoapExchangePrivate::CoapExchangePrivate(const CoapExchangePrivate &other) : - QSharedData(other) + : flags(Flags(0)) { } @@ -43,38 +38,30 @@ void CoapExchangePrivate::incoming_pdu(const CoapPDU &pdu) qDebug() << "Observed:" << pdu.payload(); } else { status = CoapExchange::Completed; + endpoint->d_ptr->timerQueue.removeTimers(q); if (on_completed) on_completed(); + q->deleteLater(); } } -CoapExchange::CoapExchange() : - d(new CoapExchangePrivate) + +CoapExchange::CoapExchange(QObject *parent) : + QObject(parent), d(new CoapExchangePrivate) { d->q = this; d->endpoint = Coap::defaultEndpoint(); d->status = Invalid; } -CoapExchange::CoapExchange(CoapEndpoint *throughEndpoint) : - d(new CoapExchangePrivate) +CoapExchange::CoapExchange(CoapEndpoint *throughEndpoint, QObject *parent) : + QObject(parent), d(new CoapExchangePrivate) { d->q = this; d->endpoint = throughEndpoint; d->status = Invalid; } -CoapExchange::CoapExchange(const CoapExchange &other) : - d(other.d) -{ -} - -CoapExchange &CoapExchange::operator =(const CoapExchange &other) -{ - d = other.d; - return *this; -} - CoapExchange::~CoapExchange() { d->endpoint->d_ptr->remove_exchange(this); @@ -105,6 +92,23 @@ void CoapExchange::get() pdu.setType(Coap::Type::CONFIRMABLE); d->pdus.push_back(pdu); d->endpoint->d_ptr->send_pdu(this, pdu); + d->retransmitCount = 0; + d->endpoint->d_ptr->timerQueue.addTimer(2000, this, "timeout"); +} + +void CoapExchange::timeout() +{ + if (++d->retransmitCount == 4) { + if (d->on_error) + d->on_error(); + deleteLater(); + return; + } + d->endpoint->d_ptr->send_pdu(this, d->pdus[0]); + quint32 nextRetransmission = 1000 * (1 << d->retransmitCount); + nextRetransmission += rand() % 100; + qDebug() << "next retransmission in" << nextRetransmission; + d->endpoint->d_ptr->timerQueue.addTimer(nextRetransmission, this, "timeout"); } void CoapExchange::observe() @@ -157,3 +161,8 @@ void CoapExchange::onCompleted(std::function lambda) { d->on_completed = lambda; } + +void CoapExchange::onError(std::function lambda) +{ + d->on_error = lambda; +} diff --git a/src/lib/coapexchange.h b/src/lib/coapexchange.h index 7a2ea8f..dc25c29 100644 --- a/src/lib/coapexchange.h +++ b/src/lib/coapexchange.h @@ -3,8 +3,6 @@ #include -#include - #include "coaplib_global.h" #include "coapuri.h" @@ -17,11 +15,12 @@ class CoapPDU; /** * @brief The CoapExchange class */ -class CoapExchange +class CoapExchange : public QObject { + Q_OBJECT public: - CoapExchange(); - CoapExchange(CoapEndpoint *throughEndpoint); + CoapExchange(QObject *parent = 0); + CoapExchange(CoapEndpoint *throughEndpoint, QObject *parent = 0); ~CoapExchange(); @@ -78,14 +77,19 @@ class CoapExchange * @param lambda called when status is changed to Completed */ void onCompleted(std::function lambda); + void onError(std::function lambda); + +private slots: + void timeout(); - friend class CoapEndpoint; - friend class CoapEndpointPrivate; - friend class CoapTimerQueue; private: CoapExchange(const CoapExchange &other); CoapExchange &operator =(const CoapExchange &other); - QExplicitlySharedDataPointer d; + CoapExchangePrivate *d; + + friend class CoapEndpoint; + friend class CoapEndpointPrivate; + friend class CoapTimerQueue; }; #endif // COAPEXCHANGE_H diff --git a/src/lib/coapexchange_p.h b/src/lib/coapexchange_p.h index b1e75a6..ec1e46c 100644 --- a/src/lib/coapexchange_p.h +++ b/src/lib/coapexchange_p.h @@ -3,18 +3,15 @@ #include -#include - #include "coapexchange.h" #include "coapuri.h" #include "coappdu.h" class CoapEndpoint; -class CoapExchangePrivate : public QSharedData +class CoapExchangePrivate { public: CoapExchangePrivate(); - CoapExchangePrivate(const CoapExchangePrivate &other); ~CoapExchangePrivate(); void incoming_pdu(const CoapPDU &pdu); @@ -24,6 +21,7 @@ class CoapExchangePrivate : public QSharedData CoapExchange::Status status; CoapUri uri; QVector pdus; + quint8 retransmitCount = 0; enum Flag { Observe = 1, @@ -31,8 +29,8 @@ class CoapExchangePrivate : public QSharedData Q_DECLARE_FLAGS(Flags, Flag) Flags flags; - quint32 timeout; std::function on_completed; + std::function on_error; }; Q_DECLARE_OPERATORS_FOR_FLAGS(CoapExchangePrivate::Flags) diff --git a/src/lib/coaptimerqueue.cpp b/src/lib/coaptimerqueue.cpp index 30b5c5e..9689350 100644 --- a/src/lib/coaptimerqueue.cpp +++ b/src/lib/coaptimerqueue.cpp @@ -6,20 +6,20 @@ typedef struct { QDateTime dt; - quint8 id; + QObject *object; + const char *method; } coap_timer_t; class CoapTimerQueuePrivate { public: QBasicTimer timer; QList queue; - quint32 delta; }; CoapTimerQueue::CoapTimerQueue(QObject *parent) : QObject(parent), d(new CoapTimerQueuePrivate) { - d->delta = 100; + } CoapTimerQueue::~CoapTimerQueue() @@ -30,7 +30,7 @@ CoapTimerQueue::~CoapTimerQueue() } } -void CoapTimerQueue::addTimer(quint32 msec, quint8 id) +void CoapTimerQueue::addTimer(quint32 msec, QObject *receiver, const char *method) { QDateTime dt = QDateTime::currentDateTime().addMSecs(msec); int idx = 0; @@ -40,15 +40,25 @@ void CoapTimerQueue::addTimer(quint32 msec, quint8 id) if (idx == d->queue.size() - 1) idx += 1; // after last one } - coap_timer_t timer{dt, id}; + coap_timer_t timer{dt, receiver, method}; d->queue.insert(idx, timer); if (idx == 0) d->timer.start(msec, this); } +void CoapTimerQueue::removeTimers(QObject *receiver) +{ + for (int i = 0; i < d->queue.size(); ++i) + if (d->queue[i].object == receiver) + d->queue[i].object = 0; +} + void CoapTimerQueue::timerEvent(QTimerEvent *e) { - qDebug() << "timeout" << d->queue.front().id; + Q_UNUSED(e); + coap_timer_t &timer = d->queue.front(); + if (timer.object) + QMetaObject::invokeMethod(timer.object, timer.method); d->timer.stop(); d->queue.pop_front(); if (!d->queue.isEmpty()) { diff --git a/src/lib/coaptimerqueue.h b/src/lib/coaptimerqueue.h index bacd169..1cc3ba5 100644 --- a/src/lib/coaptimerqueue.h +++ b/src/lib/coaptimerqueue.h @@ -1,6 +1,8 @@ #ifndef COAPTIMERQUEUE_H #define COAPTIMERQUEUE_H +#include + #include class CoapTimerQueuePrivate; @@ -16,7 +18,8 @@ class CoapTimerQueue : public QObject * @brief addTimer adds timer with msec timeout after current time * @param msec */ - void addTimer(quint32 msec, quint8 id); + void addTimer(quint32 msec, QObject *receiver, const char *method); + void removeTimers(QObject *receiver); signals: diff --git a/src/lib/lib.pro b/src/lib/lib.pro index 09dbdd4..47f0c2a 100644 --- a/src/lib/lib.pro +++ b/src/lib/lib.pro @@ -4,8 +4,8 @@ QT -= gui TARGET = qcoap TEMPLATE = lib DEFINES += MAKE_COAPLIB -QMAKE_CXXFLAGS += -std=c++11 -DESTDIR = $$PWD/../../lib +CONFIG += c++11 +DESTDIR = $$TOP_SRCDIR/bin SOURCES += \ coap.cpp \ diff --git a/tests/complex/complex.pro b/tests/complex/complex.pro new file mode 100644 index 0000000..de10c13 --- /dev/null +++ b/tests/complex/complex.pro @@ -0,0 +1,21 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2015-09-12T18:25:16 +# +#------------------------------------------------- + +QT += core + +QT -= gui + +TARGET = complex +CONFIG += console +CONFIG -= app_bundle +CONFIG += c++11 + +TEMPLATE = app + +INCLUDEPATH += $$TOP_SRCDIR/src/lib +LIBS += -L"$$TOP_SRCDIR/bin" -lqcoap + +SOURCES += main.cpp diff --git a/tests/complex/main.cpp b/tests/complex/main.cpp new file mode 100644 index 0000000..cfa26b8 --- /dev/null +++ b/tests/complex/main.cpp @@ -0,0 +1,61 @@ +#include + +#include + +#include +#include + +CoapTimerQueue tq; +QTime startTime; +const int eps = 150; + +void check(const QString &timerName, int shouldBe) +{ + int dt = startTime.msecsTo(QTime::currentTime()); + bool ok = qAbs(dt - shouldBe) < eps; + if (ok) + qDebug() << timerName << ": ok"; + else + qDebug() << timerName << ": failed, should be" << shouldBe << "ms from start, real" << dt << "diff" << qAbs(dt - shouldBe) << QTime::currentTime(); +} + +void timer4test(quint8) { + check("4", 2000); + tq.addTimer(2000, 0, [](quint8){ check("5", 4000); }); +} + +void timer1test(quint8) { + check("1", 1000); + + tq.addTimer(1000, 0, timer4test); +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + startTime = QTime::currentTime(); + qDebug() << "Starting test at:" << QTime::currentTime(); + + /** + * 0s 1s 2s 3s 4s 5s 6s 7s + * N .......|.......|.......|.......|.......|.......|.......| + * 1: x------> + * 4: x-------> + * 5: x---------------> + * 2: x----------------------> + * 3: x------------------------------------------------------> + * + */ + tq.addTimer(1000, 0, timer1test); + + tq.addTimer(3000, 0, [=](quint8){ + check("2", 3000); + }); + tq.addTimer(7000, 0, [=](quint8){ + check("3", 7000); + exit(0); + }); + + return a.exec(); +} diff --git a/tests/core/core.pro b/tests/core/core.pro new file mode 100644 index 0000000..c6cf299 --- /dev/null +++ b/tests/core/core.pro @@ -0,0 +1,23 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2015-09-12T17:57:15 +# +#------------------------------------------------- + +QT += testlib + +QT -= gui + +TARGET = tst_coretest +CONFIG += console +CONFIG -= app_bundle +CONFIG += c++11 + +TEMPLATE = app + + +SOURCES += tst_coretest.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +INCLUDEPATH += $$TOP_SRCDIR/src/lib +LIBS += -L"$$TOP_SRCDIR/bin" -lqcoap diff --git a/tests/core/tst_coretest.cpp b/tests/core/tst_coretest.cpp new file mode 100644 index 0000000..e6e7863 --- /dev/null +++ b/tests/core/tst_coretest.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +class CoreTest : public QObject +{ + Q_OBJECT + +public: + CoreTest(); + +private Q_SLOTS: + void testCoapTimerQueue(); + +private: + +}; + +CoreTest::CoreTest() +{ +} + + + +void CoreTest::testCoapTimerQueue() +{ + +} + +QTEST_MAIN(CoreTest) + +#include "tst_coretest.moc" diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..a3fd3e4 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + core \ + complex