Skip to content

Commit

Permalink
connection object rework
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandraTrifan committed Oct 17, 2023
1 parent c4217e1 commit 74c46d8
Show file tree
Hide file tree
Showing 6 changed files with 368 additions and 0 deletions.
30 changes: 30 additions & 0 deletions iioutil/include/iioutil/connection.h
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
44 changes: 44 additions & 0 deletions iioutil/include/iioutil/connectionprovider.h
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
38 changes: 38 additions & 0 deletions iioutil/src/connection.cpp
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;
}

90 changes: 90 additions & 0 deletions iioutil/src/connectionprovider.cpp
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";
}
}


1 change: 1 addition & 0 deletions iioutil/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ cmake_minimum_required(VERSION 3.5)
include(ScopyTest)

setup_scopy_tests(iiocommandqueue)
setup_scopy_tests(connectionprovider)
165 changes: 165 additions & 0 deletions iioutil/test/tst_connectionprovider.cpp
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"

0 comments on commit 74c46d8

Please sign in to comment.