Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create multiple instances of the same plugin in Qt using QPluginLoader?

and first of all sorry for my english

I have developed driver for fiscal printer using Qt. Driver specifics is communication between PC and device via RS232. Driver stored in shared object (Qt plugin) and server performs its loading by QPluginLoader.

So, about my problem... When i use one device and one driver instance all works fine, but when i attach many devices (3 for example), only last loaded device work. I have done many code checks, read many dumps of logs data, there is no error with devices and ports naming, but if i adress first device with command - 2 left devices receive the same command (according to log entries) and only last loaded device performs commands executing.

For example: from my server i send PrintReceipt command to device 1, at log file of device 1 i see entry : PrintReceipt, at log file of device 2 i see entry : PrintReceipt, : PrintReceipt, and at log file of device 3 i see 3 same entries. So, as i see my problem - QPluginLoader creates one instance for driver for first loaded device, then, when i try to load driver to second device - QPluginLoader creates new instance and replace driver for first device by recently created and so on for every device. So, at least, i have only one instance of driver (plugin) for many devices and my app logic crashes.

My question is: How to create multiple instances of the same plugin in Qt using QPluginLoader? My driver interface and loading code listed below.

class IPrinterDriver : public QObject
{
public:

// Printer driver initialization
virtual bool Init(QextSerialPort* port, const QString& deviceID) = 0;

// Driver identify name
virtual QString GetName() = 0;

// Gets current device state
virtual DeviceStates GetState() = 0;

// Gets device info and state test
virtual QString DeviceInfo() = 0;

// Set print mode to specified
virtual bool SetPrintMode(PrintModes mode) = 0;

// Get current print mode
virtual PrintModes PrintMode() const = 0;

// Sets device password from configuration
virtual void SetDevicePasswords(int operatorPassword, int adminPassword) = 0;

// Sets non-fiscal permissoin to device
virtual void SetNonFiscalModePermission(bool allowed) = 0;

// Gets device operator password
virtual int GetOperatorPassword() const = 0;

// Gets device administrator password
virtual int GetAdministratorPassword() const = 0;

// Gets non-fiscal mode permission
virtual bool GetNonFiscalModePermission() const = 0;

// Payment transaction
virtual bool PaymentTransaction(PaymentItem& item) = 0;

// Query device for X-Report
virtual bool GetXReport() = 0;

// Encashment transaction (Z-Report)
virtual bool Encash(bool fromBuffer) = 0;

// Print transaction
virtual bool Print(QString& text) = 0;

// Performs fiscal sale at device and returns receipt data
virtual FiscalReceiptData FPSSale(int requestID, int amount) = 0;

// Gets last fiscal receipt data
virtual FiscalReceiptData GetLastReceiptData() = 0;

// Gets serial port assigned to device
virtual QextSerialPort* GetDevicePort() = 0;

signals:

// Emits when device logging needed
virtual void DeviceLoggingNeeded(LogEntry entry, const QString& deviceID) = 0;

};

Q_DECLARE_INTERFACE(IPrinterDriver, "InfSys.Devices.IPrinterDriver/1.0")

And driver loading method:

// Performs loading specified driver for device at specified port
IPrinterDriver* PrintSystem::LoadDriver(PortConfiguration portConfig, const QString&   driverName, const QString& adminPassword, const QString& operPassword, const QString& deviceID)
{
IPrinterDriver* result = NULL;

// Prepare plugin loader
QDir driversDir(_driversPath);
QStringList filesMask;
filesMask << tr("libdriver.%1.*").arg(driverName);
driversDir.setNameFilters(filesMask);
QStringList driversFiles = driversDir.entryList(QDir::Files);

// Load plugin with specified driver
foreach(QString driverFile, driversFiles)
{
    // Load current driver;
    QString driverFileName = driversDir.absoluteFilePath(driverFile);
    QPluginLoader driversLoader(driverFileName);

    // Try to init driver
    QObject *driverObject = driversLoader.instance();
    if (driverObject)
    {
        result = qobject_cast<IPrinterDriver *>(driverObject);
        if (result && (result->GetName() == driverName))
        {
            QextSerialPort* devicePort = ConfigureSerialPort(portConfig);
            if (devicePort == NULL)
            {
                driversLoader.unload();
                return NULL;
            }

            // Init device
            result->SetDevicePasswords(operPassword.toInt(), adminPassword.toInt());
            result->SetNonFiscalModePermission(false);
            result->SetPrintMode(Fiscal);
            connect(result, SIGNAL(DeviceLoggingNeeded(LogEntry,QString)), App->LoggingModule, SLOT(OnDeviceLoggingNeeded(LogEntry,QString)), Qt::QueuedConnection);
            bool initResult = result->Init(devicePort, deviceID);
            if (!initResult)
            {
                driversLoader.unload();
                return NULL;
            }
        }
        else
            driversLoader.unload();
    }
}

return result;
}
like image 588
Max Davidenko Avatar asked Oct 22 '22 04:10

Max Davidenko


1 Answers

I've been looking at this problem for a while now on my own project. I believe that there isn't any way to do this directly - QPluginLoader is working as designed.

The most straightforward way around the issue that I've come up with so far is to make the primary plugin interface function a factory for the objects you actually want.

In the example below, the thing I actually WANT is multiple IPlaybackDataSource objects. So I create a factory interface that the plugin implements. The plugin then returns as many objects of the type I desire as I want.

IPlaybackDataSource.h:

#include <QSharedPointer>

class IPlaybackDataSource {
public:
    virtual bool open()=0;
};

// This is the interface that the plugin will implement
class IPlaybackDSFactory {
public:
    virtual QSharedPointer<IPlaybackDataSource> newDataSource()=0;
};

Q_DECLARE_INTERFACE(IPlaybackDSFactory,
                     "com.moberg.DeviceSimulators.IPlaybackDSFactory/1.0")

TranFile.h:

#include <QtGui>
#include "TranFile_global.h"
#include "IPlaybackDataSource.h"


class TRANFILESHARED_EXPORT TranFile : public QObject, public IPlaybackDSFactory
{
    Q_OBJECT
    Q_INTERFACES(IPlaybackDSFactory)

public:
    TranFile();
    ~TranFile();

    virtual QSharedPointer<IPlaybackDataSource> newDataSource();
};

TranFile.cpp:

#include "TranFile.h"

Q_EXPORT_PLUGIN2(IPlaybackDSFactory, TranFile );

#include <QtCore>
#include <QDebug>

TranFile::TranFile(): QObject(), IPlaybackDSFactory() {}
TranFile::~TranFile() {}

QSharedPointer<IPlaybackDataSource> TranFile::newDataSource() {
    return QSharedPointer<IPlaybackDataSource>();
}
like image 128
Michael Kohne Avatar answered Oct 27 '22 12:10

Michael Kohne