I am currently implementing a program that realizes TCP communication between PC program and external devices in QT. The problem I have is more general, but I will use this one as an example.
My class hierarchy looks like this:
Program
/ \
Server_A <--> Server_B <--- External system
|
Sockets[]
/ \
Commands Confirmations
/ | \
Interf1 Interf2 Interf3
I can get a command from device (Socket
), my command gets into Confirmation
class, realizes any Interface
job, and returns confirmation back to Socket
, which sends it back to device.
The problem occurs when I want to have a command send from an external system, which I also have to confirm.
Server_B
and pass it to Server_A
with information about: socket
to send command to and command to realize.socket
Socket
sends a command to Commands
, as there is logic for an External System commands.Commands
prepares a message, runs logic, and sends(through socket) message to deviceSocket
waits for responseSocket
gets the response, understands that it was a response to an external system command, and passes it back to Commands
Commands
realizes its logic.Here it would all be fine, but the next step is:
Commands
need to confirm the success(or failure) to external system.So basically, what I have to do is pass a message from Commands
to Server_B
this way:
Commands->Socket->Server_A->Server_B
. For all these classes, I would have to create an unnecessary method just to pass this one information. Is there a way to somehow solve this problem? During my programming, it often occurs that I have to pass something to the higher layer of my class structure, and it looks redundant to realize it through additional methods that only passes information further.
I have provided a sample pseudocode for this problem:
class Program
{
ServerB serverB;
ServerA serverA;
}
class ServerB
{
void send(QString msg);
}
class ServerA
{
QVector<MySocket*> sockets;
}
class MySocket
{
Commands commands;
Confirmations confirmations;
}
class Commands
{
void doLogic();
void sendToExternalSystem(QString message); //How to realize it?
}
My program is much bigger, but I hope it will give you a clue what I am trying to achieve. The simplest solution would be to add a method void sendToExternalSystem(QString message)
into Sockets, Server_A and Server_B, aswell as providing a pointer for each parent during construction (commands will have access to sockets, sockets will have access to server_a, and server_a will have access to server_b)
Finally, I came up with a solution. It was necessary to implement ExternalCommand
class, which instances were created in Server_B
.
In the minimal solution, it has: 1. Field QString Message
, 2. Method QString getMessage()
, 3. Method void finish(QString)
, 4. Signal void sendToExternal(QString)
When I read the message sent from the external system in Server_B
, I create an instance of this class, and connect it to the Server_B
send method. In my code, it looks like that:
ExternalCommand::ExternalCommand(QString message, QObject* parent) : QObject(parent)
{
this->message=message;
}
QString ExternalCommand::getMessage()
{
return this->message;
}
void finish(QString outputMessage)
{
emit sendToExternal(outputMessage);
}
void Server_B::onReadyRead()
{
QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
QString message = socket->readAll();
ExternalCommand* cmd = new ExternalCommand(message);
connect(cmd, &ExternalCommand::sendToExternal, socket,
[socket](QString message) {socket->write(message.toUtf8());});
}
It was also necessary to implement some type of object destruction in ExternalCommand
once the command is sent, but it isn't the point of this question.
So once this is implemented, instead of the message as QString
, the message is passed to the lower levels as ExternalCommand*
, and once an answer is got, it is possible to send it back to the External System, by calling ExternalCommand::finish(QString outputMessage);
. Of course, this is just a minimal solution for this problem.
Thanks to @MatG for pointing me to Promise/Future pattern, which was helpful in finding this solution.