-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c4217e1
commit 74c46d8
Showing
6 changed files
with
368 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#ifndef CONNECTION_H | ||
#define CONNECTION_H | ||
#include "scopy-iioutil_export.h" | ||
#include "commandqueue.h" | ||
#include <iio.h> | ||
#include <QObject> | ||
|
||
namespace scopy { | ||
class SCOPY_IIOUTIL_EXPORT Connection : public QObject | ||
{ | ||
Q_OBJECT | ||
public: | ||
Connection(QString uri); | ||
~Connection(); | ||
|
||
const QString &uri() const; | ||
CommandQueue *commandQueue() const; | ||
struct iio_context *context() const; | ||
|
||
Q_SIGNALS: | ||
void destroyed(); | ||
|
||
private: | ||
QString m_uri; | ||
CommandQueue *m_commandQueue; | ||
struct iio_context* m_context; | ||
}; | ||
} // namespace scopy | ||
|
||
#endif // CONNECTION_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#ifndef CONNECTIONPROVIDER_H | ||
#define CONNECTIONPROVIDER_H | ||
|
||
#include "scopy-iioutil_export.h" | ||
#include "connection.h" | ||
|
||
#include <QObject> | ||
#include <QMap> | ||
#include <mutex> | ||
|
||
namespace scopy { | ||
class SCOPY_IIOUTIL_EXPORT ConnectionRefCounter | ||
{ | ||
public: | ||
ConnectionRefCounter(QString uri); | ||
~ConnectionRefCounter(); | ||
QString uri; | ||
Connection *connection = nullptr; | ||
int refcnt = 0; | ||
}; | ||
|
||
class SCOPY_IIOUTIL_EXPORT ConnectionProvider : public QObject | ||
{ | ||
Q_OBJECT | ||
protected: | ||
ConnectionProvider(QObject *parent = nullptr); | ||
~ConnectionProvider(); | ||
public: | ||
ConnectionProvider(ConnectionProvider &other) = delete; | ||
void operator=(const ConnectionProvider &) = delete; | ||
|
||
static ConnectionProvider *GetInstance(); | ||
struct Connection *open(QString uri); | ||
void close(QString uri, bool force = false); | ||
|
||
private: | ||
static ConnectionProvider *pinstance_; | ||
static std::mutex mutex_; | ||
QMap<QString, ConnectionRefCounter *> map; | ||
|
||
}; | ||
} // namespace scopy | ||
|
||
#endif // CONNECTIONPROVIDER_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#include "connection.h" | ||
|
||
using namespace scopy; | ||
|
||
Connection::Connection(QString uri) | ||
{ | ||
this->m_uri = uri; | ||
this->m_context = iio_create_context_from_uri(uri.toStdString().c_str()); | ||
// check if context is valid | ||
this->m_commandQueue = new CommandQueue(); | ||
} | ||
|
||
Connection::~Connection() | ||
{ | ||
if(this->m_commandQueue) { | ||
delete this->m_commandQueue; | ||
this->m_commandQueue = nullptr; | ||
} | ||
if(this->m_context) { | ||
iio_context_destroy(this->m_context); | ||
} | ||
} | ||
|
||
const QString &Connection::uri() const | ||
{ | ||
return m_uri; | ||
} | ||
|
||
CommandQueue *Connection::commandQueue() const | ||
{ | ||
return m_commandQueue; | ||
} | ||
|
||
iio_context *Connection::context() const | ||
{ | ||
return m_context; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#include "connectionprovider.h" | ||
#include <QLoggingCategory> | ||
#include <QApplication> | ||
|
||
Q_LOGGING_CATEGORY(CAT_CONNECTIONMGR, "ConnectionProvider") | ||
|
||
using namespace scopy; | ||
|
||
ConnectionRefCounter::ConnectionRefCounter(QString uri) | ||
{ | ||
this->uri = uri; | ||
this->refcnt++; | ||
QElapsedTimer t; | ||
t.start(); | ||
this->connection = new Connection(uri); | ||
qInfo(CAT_CONNECTIONMGR) << "Acquiring connection object " << uri << "took " << t.elapsed(); | ||
} | ||
|
||
ConnectionRefCounter::~ConnectionRefCounter() | ||
{ | ||
if(this->connection) { | ||
delete this->connection; | ||
} | ||
} | ||
|
||
ConnectionProvider *ConnectionProvider::pinstance_{nullptr}; | ||
std::mutex ConnectionProvider::mutex_; | ||
|
||
scopy::ConnectionProvider::ConnectionProvider(QObject *parent) | ||
: QObject(parent) | ||
{ | ||
qDebug(CAT_CONNECTIONMGR) << "connection object ctor"; | ||
} | ||
|
||
scopy::ConnectionProvider::~ConnectionProvider() | ||
{ | ||
qDebug(CAT_CONNECTIONMGR) << "connection object dtor"; | ||
} | ||
|
||
ConnectionProvider *ConnectionProvider::GetInstance() | ||
{ | ||
std::lock_guard<std::mutex> lock(mutex_); | ||
if(pinstance_ == nullptr) { | ||
pinstance_ = new ConnectionProvider(QApplication::instance()); // singleton has the app as parent | ||
} else { | ||
qDebug(CAT_CONNECTIONMGR) << "got instance from singleton"; | ||
} | ||
return pinstance_; | ||
} | ||
|
||
Connection *ConnectionProvider::open(QString uri) | ||
{ | ||
std::lock_guard<std::mutex> lock(mutex_); | ||
Connection *connectionObject = nullptr; | ||
if(map.contains(uri)) { | ||
map.value(uri)->refcnt++; | ||
qDebug(CAT_CONNECTIONMGR) << uri << "opening - found - refcnt++ = " << map.value(uri)->refcnt; | ||
} else { | ||
ConnectionRefCounter *ctxref = new ConnectionRefCounter(uri); | ||
if(ctxref->connection == nullptr) { | ||
qWarning(CAT_CONNECTIONMGR) << uri << "not a valid context"; | ||
delete ctxref; | ||
return nullptr; | ||
} | ||
map.insert(uri, ctxref); | ||
qDebug(CAT_CONNECTIONMGR) << uri << "opening - created in map - refcnt = " << map.value(uri)->refcnt; | ||
} | ||
|
||
connectionObject = map.value(uri)->connection; | ||
return connectionObject; | ||
} | ||
|
||
void ConnectionProvider::close(QString uri, bool force) | ||
{ | ||
std::lock_guard<std::mutex> lock(mutex_); | ||
if(map.contains(uri)) { | ||
map.value(uri)->refcnt--; | ||
qDebug(CAT_CONNECTIONMGR) << uri << "closing - found in map - refcnt-- = " << map.value(uri)->refcnt; | ||
if((map[uri]->refcnt == 0) || force ) { | ||
Q_EMIT map[uri]->connection->destroyed(); | ||
delete map[uri]; | ||
map.remove(uri); | ||
qDebug(CAT_CONNECTIONMGR) << uri << "removed from map"; | ||
} | ||
} else { | ||
qInfo(CAT_CONNECTIONMGR) << uri << "not found in map. nop"; | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
#include <iioutil/connectionprovider.h> | ||
#include <iioutil/commandqueue.h> | ||
|
||
#include <QTest> | ||
|
||
using namespace scopy; | ||
|
||
class TestCommandAdd : public Command | ||
{ | ||
Q_OBJECT | ||
public: | ||
explicit TestCommandAdd(int a, int b, QObject *parent) | ||
: m_a(a) | ||
, m_b(b) | ||
{ | ||
this->setParent(parent); | ||
m_cmdResult = new CommandResult(); | ||
} | ||
|
||
virtual ~TestCommandAdd() { qDebug() << "TestCommand deleted"; } | ||
|
||
virtual void execute() override | ||
{ | ||
Q_EMIT started(this); | ||
m_cmdResult->errorCode = m_a + m_b; | ||
Q_EMIT finished(this); | ||
} | ||
|
||
private: | ||
int m_a, m_b; | ||
}; | ||
|
||
|
||
class Plugin1 : public QObject | ||
{ | ||
Q_OBJECT | ||
public: | ||
explicit Plugin1(QString boarduri) | ||
{ | ||
uri = boarduri; | ||
conn = ConnectionProvider::GetInstance()->open(uri); | ||
connect(conn, &Connection::destroyed, this, &Plugin1::handleClose); | ||
commandQueue = conn->commandQueue(); | ||
ctx = conn->context(); | ||
} | ||
|
||
virtual ~Plugin1() | ||
{ | ||
ConnectionProvider::GetInstance()->close(uri); | ||
} | ||
|
||
void close(bool force) | ||
{ | ||
ConnectionProvider::GetInstance()->close(uri, force); | ||
} | ||
|
||
const QString& getUri() const | ||
{ | ||
return uri; | ||
} | ||
|
||
Connection* getConn() const | ||
{ | ||
return conn; | ||
} | ||
|
||
CommandQueue* getCommandQueue() const | ||
{ | ||
return commandQueue; | ||
} | ||
|
||
iio_context* getCtx() const | ||
{ | ||
return ctx; | ||
} | ||
|
||
public Q_SLOTS: | ||
void handleClose() | ||
{ | ||
// notify any thread that might be using command queue or ctx | ||
conn = nullptr; | ||
commandQueue = nullptr; | ||
uri = nullptr; | ||
} | ||
private: | ||
QString uri; | ||
Connection *conn; | ||
CommandQueue *commandQueue; | ||
struct iio_context *ctx; | ||
}; | ||
|
||
class TST_ConnectionProvider : public QObject | ||
{ | ||
Q_OBJECT | ||
private Q_SLOTS: | ||
void testConnection(); | ||
void testForceClose(); | ||
void testConnectionCmdQ(); | ||
private: | ||
int TEST_A = 100; | ||
int TEST_B = 20; | ||
}; | ||
|
||
void TST_ConnectionProvider::testConnection() | ||
{ | ||
QString uri = "ip:192.168.2.1"; | ||
Plugin1 *p1 = new Plugin1(uri); | ||
Plugin1 *p2 = new Plugin1(uri); | ||
Plugin1 *p3 = new Plugin1(uri); | ||
|
||
p1->close(false); | ||
p2->close(false); | ||
p3->close(false); | ||
QVERIFY2(p1->getConn() == nullptr, "P1 object connection not closed"); | ||
QVERIFY2(p2->getConn() == nullptr, "P2 object connection not closed"); | ||
QVERIFY2(p3->getConn() == nullptr, "P3 object connection not closed"); | ||
} | ||
|
||
void TST_ConnectionProvider::testForceClose() | ||
{ | ||
QString uri = "ip:192.168.2.1"; | ||
Plugin1 *p1 = new Plugin1(uri); | ||
Plugin1 *p2 = new Plugin1(uri); | ||
Plugin1 *p3 = new Plugin1(uri); | ||
|
||
p3->close(true); | ||
QVERIFY2(p1->getConn() == nullptr, "P1 object connection not closed"); | ||
QVERIFY2(p2->getConn() == nullptr, "P2 object connection not closed"); | ||
QVERIFY2(p3->getConn() == nullptr, "P3 object connection not closed"); | ||
} | ||
|
||
void TST_ConnectionProvider::testConnectionCmdQ() | ||
{ | ||
Command *cmd1 = new TestCommandAdd(TEST_A, TEST_B, nullptr); | ||
QString uri = "ip:192.168.2.1"; | ||
Plugin1 *p1 = new Plugin1(uri); | ||
Plugin1 *p2 = new Plugin1(uri); | ||
Plugin1 *p3 = new Plugin1(uri); | ||
|
||
connect( | ||
cmd1, &scopy::Command::finished, this, | ||
[=](scopy::Command *cmd) { | ||
if(!cmd) { | ||
cmd = dynamic_cast<Command *>(QObject::sender()); | ||
} | ||
TestCommandAdd *tcmd = dynamic_cast<TestCommandAdd *>(cmd); | ||
QVERIFY2(tcmd != nullptr, "Capture command is null"); | ||
QVERIFY2(tcmd == cmd1, "Captured command not the expected one"); | ||
QVERIFY2(tcmd->getReturnCode() == (TEST_B + TEST_A), "TestCommandAdd did not execute"); | ||
}, | ||
Qt::QueuedConnection); | ||
|
||
p1->getCommandQueue()->enqueue(cmd1); | ||
|
||
QTest::qWait(100); | ||
p3->close(false); | ||
p2->close(false); | ||
p1->close(false); | ||
QVERIFY2(p1->getConn() == nullptr, "P1 object connection not closed"); | ||
QVERIFY2(p2->getConn() == nullptr, "P2 object connection not closed"); | ||
QVERIFY2(p3->getConn() == nullptr, "P3 object connection not closed"); | ||
} | ||
|
||
QTEST_MAIN(TST_ConnectionProvider) | ||
#include "tst_connectionprovider.moc" |