PDA

View Full Version : Writing custom QIODevice with 2 channels



notfound404
15th August 2017, 17:15
I am trying to implement custom QIODevice.
I have detector which will be using 2 tcp sockets, one for sending commands and receiving responses, the other one just for reading data.
How should I do it? I tried to create my class, derive it from QIODevice and implement pure virtual methods, but I faced some dificulties.

I have the following piece of code:


class Detector : public QIODevice {
Q_OBJECT
Q_DISABLE_COPY(MasterDevice)

public:
enum CHANNEL_TYPE {
DataChannel,
ControlChannel
};

private:
QTcpSocket *controlDevice;
QTcpSocket *dataDevice;

QHostAddress hostAddress;

quint16 dataPort;
quint16 controlPort;

public:
explicit Detector(QObject *parent, QHostAddress hostAddress, quint16 dataPort, quint16 controlPort)
: QIODevice(parent)
, hostAddress(hostAddress)
, dataPort(dataPort)
, controlPort(controlPort)
{
controlDevice = new QTcpSocket(this);
connect(controlDevice, SIGNAL(readyRead()),this, SLOT(controlChannelReadReady()));

dataDevice = new QTcpSocket(this);
connect(dataDevice, SIGNAL(readyRead()),this, SLOT(dataChannelReadReady()));
}

virtual ~Detector() {}

bool open(OpenMode mode) override {
QIODevice::open(mode);

controlDevice->connectToHost(hostAddress, controlPort, QTcpSocket::ReadWrite);
dataDevice->connectToHost(hostAddress, dataPort, QTcpSocket::ReadOnly);
}

qint64 readData(char *data, qint64 maxSize) override {
QTcpSocket *socket;

switch ( currentReadChannel() ) {
case DataChannel:
socket = dataDevice;
break;
case ControlChannel:
socket = controlDevice;
break;
default:
return -1;
break;
}

return socket->read(data, maxSize);
}

qint64 writeData(const char * data, qint64 maxSize) override {
QTcpSocket *socket;

switch ( currentWriteChannel() ) {
case DataChannel:
socket = dataDevice;
break;
case ControlChannel:
socket = controlDevice;
break;
default:
return -1;
break;
}

return socket->write(data, maxSize);
}

private slots:
void controlChannelReadReady() {
emit channelReadyRead(ControlChannel);
}

void dataChannelReadReady() {
emit channelReadyRead(DataChannel);
}
};

So basically my question is, how do I handle those channels, because for example from qt documentation I have this




bool QIODevice::open(OpenMode mode)

Opens the device and sets its OpenMode to mode. Returns true if successful; otherwise returns false. This function should be called from any reimplementations of open() or other functions that open the device.

So I have to invoke it from my overrided Open() method, but I looked at the implementation of this method and I saw that it is setting the number of channels to 1. How can I change this number?
What for are those buffers that I am inheriting from QIODevice, how should I use them?
Am I missing the point of creating custom QIODevice? If so, please explain it to me.

high_flyer
15th August 2017, 21:51
Umm...
Based on your code, there is no need to sublcass QIODevice, since you are doing nothing that the current QTcpSockets are not doing (QTcpSocket is a QIODevice).
You detector class can be a simple QObject, which basically managers the the reading and writing to your two sockets (which is basically what your code appears to be trying to do).
Line 37 in your code simply is not needed and is doing nothing of value.
This way you don't have to implement the virtuals of QIOdevice and you can mold your API as you need it.

notfound404
16th August 2017, 09:15
First of all thank you for your reply. Much appreciated.



Based on your code, there is no need to subcass QIODevice, since you are doing nothing that the current QTcpSockets are not doing (QTcpSocket is a QIODevice). You detector class can be a simple QObject...

I agree that looking at my code there is no need for that, but I do want to extend my custom object, so that I will be able to take advantage of many other funcionalities of QIODevice, like for example being able to use QDataStream, which in constructor takes QIODevice. Moreover I would like to make my application very easy to reuse, by just replacing my custom QIODevice object to any other QIODevice object when it would be necessary.


Line 37 in your code simply is not needed and is doing nothing of value.

Yes, for now I am not using it, however it is setting some variables (Readonly / Writeonly) which I can check later on, in for example read / write methods to made them work corectly.

And one more thing, why QT made such a beautiful class as QIODevice with support for multichannel communication when as far as I know no other QT object that is inheriting from it is using those channels?

high_flyer
16th August 2017, 12:10
I agree that looking at my code there is no need for that, but I do want to extend my custom object, so that I will be able to take advantage of many other funcionalities of QIODevice, like for example being able to use QDataStream, which in constructor takes QIODevice. Moreover I would like to make my application very easy to reuse, by just replacing my custom QIODevice object to any other QIODevice object when it would be necessary.
Again, you CAN take advantage of these features by calling them on your QTcpSocket.


Yes, for now I am not using it, however it is setting some variables (Readonly / Writeonly) which I can check later on, in for example read / write methods to made them work corectly.

And one more thing, why QT made such a beautiful class as QIODevice with support for multichannel communication when as far as I know no other QT object that is inheriting from it is using those channels?
Its sound to me you do not fully understand what subclassing is used for.
Qt offers QIODevice for people to be able to write new QIODevices and to have an "interface" class to all QIODevices that are already implemented by Qt, this is what OOP is all about.
That means if you have a new type of hardware that needs different implementation of writing, reading etc, then you can implement it in a way that will conform to QIODevice and allow you to use it with other Qt QIODevice consumer classes.
This is however NOT what you are doing.
You are fully content with using the functionality offered by QTcpSocket. (Based on the posted code).
You don't need a new low level implementation of the socket, you simply have a case where you need to coordinate between two sockets - that is NOT a new device, that is simply a manager class.