PDA

View Full Version : how to find all signals connected to specific slot



davidovv
14th April 2012, 23:55
Hi
Here is the problem. i have a class for a device, with a signal "send(QByteArray)" and slot "receive(QByteArray)". There are a lot of other devices that could connect their send signal with another receive slot. I am trying to "monitor" communication for any chosen device. my idea was to intercept its send and receive functions - connect my monitor's sent slot to device's send signal, and to connect all signals connected to device's slot receive to monitor's slot received. What seams to be hard is to find all signals connected to specific slot. I don't need to know about senders, i just want to intercept data and attach my monitor to these 2 functions. is there a way to connect to all signals connected to specific slot.
thanks, (for reading if you have no answer :)

wysota
15th April 2012, 00:26
Signals and slots are meant to be used when you don't care who registers for the signals or who sends the signal. If you require a tight coupling such as this, it might be better to implement a classic observer pattern. An alternative would be to use QSignalMapper to enumerate all signalling objects but then you'd lose your QByteArray argument. However you could implement something that works in a similar fashion. On the other hand you'd be practically implementing the observer pattern so it's probably easier to do it the right way in the beginning.

davidovv
15th April 2012, 21:31
wysota, thanks for answering
I hava a solution that can do the job: in the first line inside receive(QByteArray) slot i could emit another signal received(QByteArray), and to monitor (observe) communication i could connect to "send" and "received" signals, which (correct me if i am wrong) is the classic observer pattern. Somehow it seamed "dirty" to insert another signal when there are allready signals that do emit the same information, and i just need to find them and connect with them. I just wanted to monitor communication without modifying existing classes.
Even if i have to do some for loops to find all signals connected to a slot, as oposed to connecting to a single "received" signal, i still think that the second way is "dirty" because i need to modify exixting class, which is complete, because of my new classes. And Signal/Slot idea is about not carring who is on the other side.
So, I have a solution but i am searching for "correct" one.

wysota
15th April 2012, 21:42
I meant something like having a "register" method that feeds instances of a known interface to the class being observed that then calls methods from that interface when something interesting happens.


class MonitorIface {
public:
virtual void onReceived(const QByteArray &ba) = 0;
};

void XX::registerListener(MontorIface *iface) {
m_listeners << iface;
}

void XX::received() {
QByteArray ba = ...;
foreach(MonitorIface *iface, m_listeners) iface->onReceived(ba);
}

Of course my example works "the other way round" but the principle is the same.

davidovv
16th April 2012, 00:03
wysota, thanks again, but
my question is not just related to this example and it "evolves" to: is it possible to connect slot to slot, or in other words one slot to all signals of another slot?

I know that i can count the number of receivers of a signal, but can i also connect to signals knowing only the slot?
Imagine this "funny" scenario: you are at the restaurant, eating, i come after some time, and say to waiter give me the same thing he is eating, and i point to you. Is it possible to do that without looking into menu?
Your suggestion in this scenario sounds like: i have to ask you to notify me when you order something so that i can order it too.
I know you have a good taste in food but i dont want to disturb you, there is waiter that is paid for the job, and knows what you are eating :)

wysota
16th April 2012, 09:04
There is no such thing as "signals of a slot", I have no idea what you mean. If you tell me what final result (in terms of functionality and not technical means to obtain it), maybe I'll be able to suggest a good solution.

davidovv
16th April 2012, 12:44
when i say "signals of a slot" i mean all signals connected to that slot.
I thought that the restaurant example would help.
Here is another pseudo code example


class Person{
signals:
void speak(QString);
public slots:
void hear(QString)
}

class Spy{
public:
void spyOnPerson(Person*)
public slots:
void personHeard(QString)
void personSaid(QString)
}


//somewhere in application
Person user1, user2, user3
...
connect(&user1, speak(QString), &user2 hear(QString);
connect(&user2, speak(QString), &user1 hear(QString);
connect(&user3, speak(QString), &user1 hear(QString);

//now the spy comes to the scene
Spy spy;
...
spy.spyOnPerson(&user1);
//user1 is some kind of criminal, and i wont to spy on him
//i want spy to hear what user1 hears and what user1 says
//in this exaple spy hears what user1 says, and what user2 and user3 say to user1
//the problem is in assigning person to spy
//i can say
void Spy::spyOnPerson(Person *p){
connect(p, speak(QString), this, personSaid(QString);
//but i also need to hear what are the others speaking to user1
connect(p, hear(QString), this personHeard(QString);
//since hear is a slot i cant connect it to anything
//but how can i search for other signals (user2 speak and user3 speak)
//if i just know that hear is a slot and there are signals that call that slot
//of course i can reemit another signal inside hear slot, and i did that
//but it seamed like workaround to insert new signal that actualy executes when a signal (from another user) emits.
//and signals are connectable to signals, and why should i create new signal, just search for the original signal and connect to it...
}


Thinking more of this i start to understand that this is impossible (for now), I can't hear what i want without modifying Persons, i need to put a bug inside persons ears, and to connect to that... I just wanted to acomplish spying without modifying Person class.

wysota
16th April 2012, 13:34
I don't want examples. I want you to tell me what you need the solution for.

davidovv
16th April 2012, 17:36
Ok, I have a lot of different devices (with different protocols) connected to a single serial port. Thread class that works with serial port emits signals when data is received, and has a slot for sending data. All devices have signals for sending and slots for receiving data. Device paired with port "exchanges" send and receive signals and slots. Because of the number of devices there is allways some activity on port, i want to monitor comunication for specific device, that is idle most of the time. I have a simple widget, created on demand, with log window that shows communication lines like
timestamp <- "data recevied"
timestamp -> "data sent"
when i create this monitoring widget i connect it with serial port and show all communication
too much communication makes it hard to catch the lines of this, almost idle, device
i wanted to connect this widget with that specific device so that i can view only what is this device sending and receiving
and i wanted my widget to be universal for monitoring any kind of communication
As i said, i have a working solution, but i dont like that i have to edit classes for all devices, and to add a new "dataReceived" signal
If it would be possible to "duplicate" connections of one specific receive slot, to another receive slot (of widget), that would solve my problem easy.
Now i have to edit class files for all devices to implement this new behaviour, to reemit signal when data is received, for monitoring widget (if there is one attached).
I just had a feeling that i could do this without editing the device class, and with having only the pointer of the device before showing the widget.
I hope that this explanation didn't isnt unclear as previous ones.

summary: i need to filter comunication from specific serial port, and tho show data for and from a specific device connected to it.

wysota
16th April 2012, 17:52
If you ask me then what you need is a multiplexer/demultiplexer. When data is received on the port, something has to determine which device it is related to and needs to pass the data to the handler(s) for that device only. If you register your log widget as a handler for a particular device, it will also receive the data. You can implement it with signals or without them (e.g. by using QMetaObject::invokeMethod() or by ivoking a known interface as in my earlier example). I admit I don't see where the "who is connected to slot X" issue fits here :)

davidovv
16th April 2012, 18:22
Multiplexing/demultiplexing idea is on my mind for some time, but too many devices, too many protocols. Anyway, every devices receive data for all devices, because they are on same serial port, and all devices have to be "resistant" on other devices data.
Too many devices, too many protocols, and devices allready know how to handle "redudant" data, so demultiplexing is kind of redudant itself

If you register your log widget as a handler for a particular device, it will also receive the data
This sentence looks promising, can you point me to some examples, i have never done something simmilar.
My widget should be created on user demand, and after the widget is closed, the device should continue to work like before widget.

wysota
16th April 2012, 18:50
Multiplexing/demultiplexing idea is on my mind for some time, but too many devices, too many protocols. Anyway, every devices receive data for all devices, because they are on same serial port, and all devices have to be "resistant" on other devices data.
So how do you expect to show a log for only a single device?


Too many devices, too many protocols, and devices allready know how to handle "redudant" data, so demultiplexing is kind of redudant itself
Not really because if you have N devices, each message is processed N times instead of just one time.


This sentence looks promising, can you point me to some examples, i have never done something simmilar.
I'm talking about the observer pattern mentioned earlier.

I think I don't really see what you're having problems with.

davidovv
16th April 2012, 19:24
So how do you expect to show a log for only a single device?
(i knew that you will come with this hard question :)) I want to see what a single device sends, and what that same single device receives. If it receives extra junk data, what can i do... i have to show that too, but sent data has to be only from that device.


Not really because if you have N devices, each message is processed N times instead of just one time.
I can live with that, because that is the real situation on serial port, every message is processed on every device. Actualy protocols have start sequence, end sequence, addresses checksums, so devices just ignore junk data that is not recognized as messages. If a real device can handle that, my class has to also, and processing it in every instance is not a problem, it is a "idiot proof" test of my protocol implementation also. I am still thinking of muliplexer/demultiplexer but i have no idea how to implement this new "protocol" layer.


I'm talking about the observer pattern mentioned earlier.
Observer pattern from wiki
7592
if you look at notifyObserver as signal
then it means that i have to emit a signal when i receive data, (i allready emit a signal when i send data)
so the solution where i have to emit dataReceived signal in slot where i receive data kind of complies with observer pattern, and thank you for pointing me to that.
I can emit this dataReceived signal only if the message is recognized, so monitoring can show only recognized messages


I think I don't really see what you're having problems with.
I have a solution for my problem, but i just wasn't sure that it is the "correct" solution.
I thought that "correct" solution was to "hook" my slot on another slot if it is possible, but as i understand it isn't, SLOT DOESN'T EXIST :)
Thanks for helping

wysota
16th April 2012, 19:49
if you look at notifyObserver as signal
Forget signals. Signals are good instead of an observer pattern, not as part of it.

I think you are greatly complicating the problem where it doesn't need to be complicated.

As for me you can implement your situation like this:


struct CommunicationListener {
virtual void onReceiveData(const QByteArray &ba) = 0;
virtual void onSendData(Device *dev, const QByteArray &ba) = 0;
};

void CommunicationController::registerListener(Communic ationListener *h) {
m_listeners << h;
}

/*!
slot for accepting data from devices
*/
void CommunicationController::sendData(const QByteArray &ba) {
Device *dev = qobject_cast<Device*>(sender());
foreach(CommunicationListener *l, m_listeners) l->onSendData(dev, ba);
sendData_real(ba);
}

/*!
method for receiving data from the port
*/
void CommunicationController::receiveData(const QByteArray &ba) {
foreach(CommunicationListener *l, m_listeners) l->onReceiveData(ba);
receiveData_real(ba);
};

class LogWidget : public QListWidget, public CommunicationListener {
public:
// ...
void onSendData(Device *dev, const QByteArray &ba) {
if(dev != theOneIWant) return;
QListWidgetItem *item = new QListWidgetItem(this);
item->setText(ba);
}
void onReceiveData(const QByteArray &ba) {
QListWidgetItem *item = new QListWidgetItem(this);
item->setText(ba);
}
};

Of course if you are using threads, remember to make sure each method is called from the proper thread context.

davidovv
16th April 2012, 20:51
I was thinking "the other way round"
First to create abstract class observableDevice,
all devices should inherit from this one,
the logwidget can take observableDevice as argument in constructor,
and in constructor i can connect apropriate signals and slots

This example is looks interesting.
If i understand well, you suggest that i insert new layer (CommunicationControler) between devices and ports,
devices now comunicate only with controllers, controllers are observable,
log widget observes controller not device as i thought (with extra conditions if needed),
and, what is important, no source file for device is changed, just their connections moved to controllers.

I will definitly look more into this example, thanks

wysota
16th April 2012, 20:57
This example is looks interesting.
If i understand well, you suggest that i insert new layer (CommunicationControler) between devices and ports,
devices now comunicate only with controllers, controllers are observable,
log widget observes controller not device as i thought (with extra conditions if needed),
and, what is important, no source file for device is changed, just their connections moved to controllers.

It's hard to call it a layer, it's not above or between anything. It's more of a side block.

davidovv
29th April 2012, 01:39
Forget signals. Signals are good instead of an observer pattern, not as part of it.

Hi (wysota) :)
After some time of "struggling" with new code i reached a dead end. I ended up with multiple virtual inheritance of QObject, and that is just not supported. So i made few steps back and now i am on the way to "forget signals" and to implement observer pattern (like you suggested the first time).
I am using threads, and before i get a wrong idea how things work (again), i better ask how to:


make sure each method is called from the proper thread context.
a small example woul be appreciated

wysota
10th May 2012, 00:21
The simplest thing you can do is to do the callback via a custom event (posted to the object using QCoreApplication::postEvent()). Then Qt makes sure the method is called in the proper context.