Editing Message Passing

Jump to: navigation, search

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision Your text
Line 1: Line 1:
[[Category:Usage Manual]]
 
 
== Introduction ==
 
== Introduction ==
  
Line 28: Line 27:
 
(PMTs) in GNU Radio. For further information about these data
 
(PMTs) in GNU Radio. For further information about these data
 
structures, see the page [[Polymorphic Types (PMTs)]].
 
structures, see the page [[Polymorphic Types (PMTs)]].
 
[[File:Gnuradio_pdu_illustration.png|550px|Gnuradio_pdu_illustration.png]]
 
  
 
== Message Passing API ==
 
== Message Passing API ==
Line 79: Line 76:
 
the messages that are posted to it. After using the
 
the messages that are posted to it. After using the
 
gr::basic_block::message_port_register_in to declare a subscriber port, we
 
gr::basic_block::message_port_register_in to declare a subscriber port, we
must then bind this port to the message handler.  
+
must then bind this port to the message handler. For this, we use
 +
Boost's 'bind' function:
  
Starting in GNU Radio 3.8¹ using C++11 we do that using a lambda function:
+
   set_msg_handler(pmt::pmt_t port_id,
 
+
     boost::bind(&block_class::message_handler_function, this, _1));
   set_msg_handler(pmt::pmt_t port_id,  
 
     [this](const pmt::pmt_t& msg) { message_handler_function(msg); });
 
  
 
In Python:
 
In Python:
Line 94: Line 90:
 
The 'port_id' is the same PMT as used when registering the input
 
The 'port_id' is the same PMT as used when registering the input
 
port. The 'block_class::message_handler_function' is the member
 
port. The 'block_class::message_handler_function' is the member
function of the class designated to handle messages to this port.  
+
function of the class designated to handle messages to this port. The
 
+
'this' and '_1' are standard ways of using the Boost bind function to
The prototype for all message handling functions
+
pass the 'this' pointer as the first argument to the class (standard
 +
OOP practice) and the _1 is an indicator that the function expects 1
 +
additional argument. The prototype for all message handling functions
 
is:
 
is:
  
   void block_class::message_handler_function(const pmt::pmt_t& msg);
+
   void block_class::message_handler_function(pmt::pmt_t msg);
  
 
In Python the equivalent function would be:
 
In Python the equivalent function would be:
Line 111: Line 109:
 
From the flowgraph level, we have instrumented a gr::hier_block2::msg_connect
 
From the flowgraph level, we have instrumented a gr::hier_block2::msg_connect
 
method to make it easy to subscribe blocks to other blocks'
 
method to make it easy to subscribe blocks to other blocks'
messages. Assume that the block '''src''' has an output message port named
+
messages. Assume that the block \b src has an output message port named
''pdus'' and the block '''dbg''' has an input port named ''print''. The message  
+
\a pdus and the block \b dbg has an input port named \a print. The message  
 
connection in the flowgraph (in Python) looks like the following:
 
connection in the flowgraph (in Python) looks like the following:
  
Line 206: Line 204:
  
 
== Code Examples ==
 
== Code Examples ==
 
=== C++ ===
 
  
 
The following is snippets of code from blocks currently in GNU Radio
 
The following is snippets of code from blocks currently in GNU Radio
Line 215: Line 211:
  
 
The gr::blocks::message_debug block is used for debugging the message
 
The gr::blocks::message_debug block is used for debugging the message
passing system. It describes two input message ports: ''print'' and
+
passing system. It describes three input message ports: ''print'',
''store''. The ''print'' port simply prints out all
+
''store'', and ''pdu_print''. The ''print'' port simply prints out all
 
messages to standard out while the ''store'' port keeps a list of all
 
messages to standard out while the ''store'' port keeps a list of all
messages posted to it. The ''store'' port works in
+
messages posted to it. The ''pdu_print'' port specially formats PDU
conjunction with a gr::blocks::message_debug::get_message(size_t i) call
+
messages for printing to standard out. The ''store'' port works in
 +
conjunction with a gr::blocks::message_debug::get_message(int i) call
 
that allows us to retrieve message i afterward.
 
that allows us to retrieve message i afterward.
  
 
The constructor of this block looks like this:
 
The constructor of this block looks like this:
  
<syntaxhighlight lang="cpp">
 
 
  {
 
  {
 
   message_port_register_in(pmt::mp("print"));
 
   message_port_register_in(pmt::mp("print"));
 
   set_msg_handler(pmt::mp("print"),
 
   set_msg_handler(pmt::mp("print"),
     [this](const pmt::pmt_t& msg) { print(msg); });
+
     boost::bind(&message_debug_impl::print, this, _1));
 
   
 
   
 
   message_port_register_in(pmt::mp("store"));
 
   message_port_register_in(pmt::mp("store"));
 
   set_msg_handler(pmt::mp("store"),
 
   set_msg_handler(pmt::mp("store"),
     [this](const pmt::pmt_t& msg) { store(msg); });
+
     boost::bind(&message_debug_impl::store, this, _1));
 +
 +
  message_port_register_in(pmt::mp("print_pdu"));
 +
  set_msg_handler(pmt::mp("print_pdu"),
 +
    boost::bind(&message_debug_impl::print_pdu, this, _1));
 
  }
 
  }
</syntaxhighlight>
 
  
 
The three message input ports are registered by their respective
 
The three message input ports are registered by their respective
 
names. We then use the gr::basic_block::set_msg_handler function to  
 
names. We then use the gr::basic_block::set_msg_handler function to  
identify this particular port name with a callback function.
+
identify this particular port name with a callback function. The
 
+
Boost ''bind'' function ([http://www.boost.org/doc/libs/1_52_0/libs/bind/bind.html Boost::bind])
So now
+
here binds the callback to a function of this block's class. So now
 
the functions in the block's private implementation class,
 
the functions in the block's private implementation class,
gr::blocks::message_debug_impl::print and
+
gr::blocks::message_debug_impl::print,
gr::blocks::message_debug_impl::store are assigned to handle
+
gr::blocks::message_debug_impl::store, and
 +
gr::blocks::message_debug_impl::print_pdu, are assigned to handle
 
messages passed to them. Below is the ''print'' function for reference.
 
messages passed to them. Below is the ''print'' function for reference.
  
<syntaxhighlight lang="cpp">
 
 
  void
 
  void
  message_debug_impl::print(const pmt::pmt_t& msg)
+
  message_debug_impl::print(pmt::pmt_t msg)
 
  {
 
  {
 
   std::cout << "***** MESSAGE DEBUG PRINT ********\n";
 
   std::cout << "***** MESSAGE DEBUG PRINT ********\n";
Line 254: Line 253:
 
   std::cout << "**********************************\n";
 
   std::cout << "**********************************\n";
 
  }
 
  }
</syntaxhighlight>
 
  
 
The function simply takes in the PMT message and prints it. The method
 
The function simply takes in the PMT message and prints it. The method
Line 263: Line 261:
 
output message port. In this case, its constructor contains the line:
 
output message port. In this case, its constructor contains the line:
  
<syntaxhighlight lang="cpp">
+
{
{
 
 
   message_port_register_out(pdu_port_id);
 
   message_port_register_out(pdu_port_id);
}
+
}
</syntaxhighlight>
 
  
 
So we are only creating a single output port where ''pdu_port_id''
 
So we are only creating a single output port where ''pdu_port_id''
Line 287: Line 283:
 
shown below.
 
shown below.
  
<syntaxhighlight lang="cpp">
 
 
  void
 
  void
 
  tagged_stream_to_pdu_impl::send_message()
 
  tagged_stream_to_pdu_impl::send_message()
Line 305: Line 300:
 
   d_inpdu = false;
 
   d_inpdu = false;
 
  }
 
  }
</syntaxhighlight>
 
  
 
This function does a bit of checking to make sure the PDU is OK as
 
This function does a bit of checking to make sure the PDU is OK as
Line 322: Line 316:
 
flowgraph using the gr::blocks::pdu_to_tagged_stream block.
 
flowgraph using the gr::blocks::pdu_to_tagged_stream block.
  
=== Python ===
+
For a Python block example, see [[Python Blocks#Using Message Passing]].
 
 
A Python Block example:
 
 
 
<syntaxhighlight lang="python">
 
class msg_block(gr.basic_block):
 
    def __init__(self):
 
        gr.basic_block.__init__(
 
            self,
 
            name="msg_block",
 
            in_sig=None,
 
            out_sig=None)
 
 
        self.message_port_register_out(pmt.intern('msg_out'))
 
        self.message_port_register_in(pmt.intern('msg_in'))
 
        self.set_msg_handler(pmt.intern('msg_in'), self.handle_msg)
 
 
    def handle_msg(self, msg):
 
        self.message_port_pub(pmt.intern('msg_out'), pmt.intern('message received!'))
 
</syntaxhighlight>
 
 
 
== Flowgraph Example ==
 
 
 
Here's a simple example of a flow graph using both streaming and messages:
 
 
 
[[File:strobe.grc.png|550px|strobe.grc.png]]
 
 
 
There are several interesting things to point out. First, there are two source blocks, which both output items at regular intervals, one every 1000 and one every 750 milliseconds. Dotted lines denote connected message ports, as opposed to solid lines, which denote connected streaming ports. In the top half of the flow graph, we can see that it is, in fact, possible to switch between message passing and streaming ports, but only if the type of the PMTs matches the type of the streaming ports (in this example, the pink color of the streaming ports denotes bytes, which means the PMT should be a u8vector if we want to stream the same data we sent as PMT).
 
 
 
Another interesting fact is that we can connect more than one message output port to a single message input port, which is not possible with streaming ports. This is due to the '''asynchronous''' nature of messages: The receiving block will process all messages whenever it has a chance to do so, and not necessarily in any specific order. Receiving messages from multiple blocks simply means that there might be more messages to process.
 
 
 
What happens to a message once it was posted to a block? This depends on the actual block implementation, but there are two possibilities:
 
 
 
1) A message handler is called, which processes the message immediately.<br />
 
2) The message is written to a FIFO buffer, and the block can make use of it whenever it likes, usually in the work function.
 
 
 
For a block that has both message ports and streaming ports, any of these two options is OK, depending on the application. However, we strongly discourage the processing of messages inside of a work function and instead recommend the use of message handlers. Using messages in the work function encourages us to block in work waiting for a message to arrive. This is bad behavior for a work function, which should never block. If a block depends upon a message to operate, use the message handler concept to receive the message, which may then be used to inform the block's actions when the work function is called. Only on specially, well-identified occasions should we use method 2 above in a block.
 
 
 
With a message passing interface, we can write blocks that don't have streaming ports, and then the work function becomes useless, since it's a function that is designed to work on streaming items. In fact, blocks that don't have streaming ports usually don't even have a work function.
 
 
 
=== PDUs ===
 
 
 
In the previous flow graph, we have a block called ''PDU to Tagged Stream''. A PDU (protocol data unit) in GNU Radio has a special PMT type, it is a pair of a dictionary (on CAR) and a uniform vector type. So, this would yield a valid PDU, with no metadata and 10 zeros as stream data:
 
 
 
<pre>pdu = pmt.cons(pmt.make_dict(), pmt.make_u8vector(10, 0))</pre>
 
The key/value pairs in the dictionary are then interpreted as key/value pairs of stream tags.
 
 
 
== Flowgraph Example: Chat Application ==
 
 
 
Let's build an application that uses message passing. A chat program is an ideal use case, since it waits for the user to type a message, and then sends it. Because of that, no [[Throttle]] block is needed.
 
 
 
Create the following flowgraph and save it as 'chat_app2.grc':
 
 
 
[[File:Chat_app2_fg.png]]
 
 
 
The ZMQ Message blocks have an Address of 'tcp://127.0.0.1:50261'. Typing in the [[QT GUI Message Edit Box]] will send the text once the Enter key is pressed. Output is on the terminal screen where gnuradio-companion was started.
 
 
 
If you want to talk to another user (instead of just yourself), you can create an additional flowgraph with a different name such as 'chat_app3.grc'. Then change the ZMQ port numbers as follows:
 
* chat_app2
 
** ZMQ PUSH Sink: tcp://127.0.0.1:50261
 
** ZMQ PULL Source: tcp://127.0.0.1:50262
 
 
 
* chat_app3
 
** ZMQ PUSH Sink: tcp://127.0.0.1:50262
 
** ZMQ PULL Source: tcp://127.0.0.1:50261
 
 
 
When using GRC, doing a Generate and/or Run creates a Python file with the same name as the .grc file. You can execute the Python file without running GRC again.
 
 
 
For testing this system we will use two processes, so we will need two terminal windows.
 
 
 
Terminal 1:
 
* since you just finished building the chat_app3 flowgraph, you can just do a Run.
 
 
 
Terminal 2:
 
Open another terminal window.
 
* change to whatever directory you used to generate the flowgraph for chat_app2.
 
* execute the following command:
 
    python3 -u chat_app2.py
 
 
 
Typing in the Message Edit Box for chat_app2 should be displayed on the Terminal 1 screen (chat_app3) and vice versa.
 
 
 
To terminate each of the processes cleanly, click on the 'X' in the upper corner of the GUI rather than using Control-C.
 
 
 
 
 
----
 
 
 
¹  In old GNU Radio 3.7, we used Boost's 'bind' function:
 
 
 
  set_msg_handler(pmt::pmt_t port_id,
 
  boost::bind(&block_class::message_handler_function, this, _1));}}
 
 
 
The 'this' and '_1' are standard ways of using the Boost bind function to
 
pass the 'this' pointer as the first argument to the class (standard
 
OOP practice) and the _1 is an indicator that the function expects 1
 
additional argument.
 
 
 
[[Category:Guided Tutorials]]
 

Please note that all contributions to GNU Radio are considered to be released under the Creative Commons Attribution-ShareAlike (see GNU Radio:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To edit this page, please answer the question that appears below (more info):

Cancel | Editing help (opens in new window)