8.2. Hello World with protobuf (Python)
In the last section you learned how to send strings to an eCAL Topic. Using strings is great for simple data that has a textual representation. Quite often however your data will be more complex, so you need some kind of protocol that defines how your data is structured.
Our recommended way is to use Google protobuf to do that, because:
It solves the problem of how to serialize and de-serialize data for you
You get downward compatibility out of the box (if you follow the guidelines)
It is maintained by Google and the API is stable
The eCAL Monitor can display a nice reflection view of the data
Important
It is important to remember, that all your applications must agree on the data format.
As protobuf messages are defined in .proto
files, all of your applications should share the same files.
8.2.1. Protobuf in Python
Let’s start with creating the protobuf file. As the sender and receiver need the same .proto files, we place them in a separate directory that will be accessible both for the sender and receiver script.
So, somewhere on your hard drive, create a directory proto_messages
with a file hello_world.proto
and paste the following content into that file.
|fa-folder-open|
└─ |fa-folder-open| proto_messages
└─ |fa-file-alt| hello_world.proto
1syntax = "proto3";
2
3package proto_messages;
4
5message HelloWorld
6{
7 string name = 1;
8 uint32 id = 2;
9 string msg = 3;
10}
Note
What is happening here?
Line 3 assigns a package name. In Python, the package name is ignored, as python organizes its modules according to their location in the file system.
Line 5-10 Creates a message “HelloWorld”, that holds the fields “name”, “id” and “msg”.
Tip
Specify a package name, even though python doesn’t use it.
Keep the package name in sync to the directory structure.
This makes it easier for you to use the same proto files for C++ and python. It also makes it easier to create nested messages (i.e. Protobuf messages that contain other Protobuf messages by importing them based on their package name and message name)
Now you have the description of your message structure.
However, to make it available in python, we need to translate it to a python file.
This is done by the Protobuf Compiler (protoc
).
|fa-windows| On Windows this tool is shipped with eCAL, so if you have eCAL installed, you already got it. If you don’t, please install eCAL.
For running your python script, you don’t need to have eCAL installed, even though it may be beneficial e.g. to use eCAL Mon for introspection of your topics.
|fa-ubuntu| On Ubuntu you can easily obtain it standalone from the apt repository:
sudo apt-get install protobuf-compiler
Now convert the .proto file to a .py file on command line. The command is the same for both Windows and Linux.
cd proto_messages
protoc --python_out=. hello_world.proto
If you check the content of the proto_messages
directory, you will now find a file hello_world_pb2.py
.
This is the file we are going to use in python in the next step.
8.2.2. Protobuf Sender
Now let’s start implementing the actual python code, that sends some protobuf-serialized data to an eCAL topic.
For that, create a file protobuf_snd.py
next to your proto_messages
directory and paste the following content:
|fa-folder-open|
├─ |fa-folder-open| proto_messages
│ └─ |fa-file-alt| hello_world.proto
└─ |fa-python| protobuf_snd.py
1import sys
2import time
3
4import ecal.core.core as ecal_core
5from ecal.core.publisher import ProtoPublisher
6
7# Import the "hello_world_pb2.py" file that we have just generated from the
8# proto_messages directory
9import proto_messages.hello_world_pb2 as hello_world_pb2
10
11if __name__ == "__main__":
12 # initialize eCAL API. The name of our Process will be
13 # "Python Protobuf Publisher"
14 ecal_core.initialize(sys.argv, "Python Protobuf Publisher")
15
16 # Create a Protobuf Publisher that publishes on the topic
17 # "hello_world_python_protobuf_topic". We also pass it the datatype we are
18 # going to send later. By doing this we enable the eCAL Monitor to dynamically
19 # show the content of the messages.
20 pub = ProtoPublisher("hello_world_python_protobuf_topic"
21 , hello_world_pb2.HelloWorld)
22
23 # Create a counter and some messages, so we see something changing in the
24 # message, later
25 counter = 0
26 dummy_messages = ["The story so far:"
27 , "In the beginning the Universe was created."
28 , "This has made a lot of people very angry"
29 , "and been widely regarded as a bad move."]
30
31 # Infinite loop (using ecal_core.ok() will enable us to gracefully shutdown
32 # the process from another application)
33 while ecal_core.ok():
34 # Create a message and fill it with some data
35 protobuf_message = hello_world_pb2.HelloWorld()
36 protobuf_message.name = "Douglas Adams"
37 protobuf_message.id = counter
38 protobuf_message.msg = dummy_messages[counter % 4]
39
40 print("Sending message {}".format(counter))
41
42 # actually send the message to the topic this publisher was created for
43 pub.send(protobuf_message)
44
45 # Sleep 1s
46 time.sleep(1)
47
48 counter = counter + 1
49
50 # finalize eCAL API
51 ecal_core.finalize()
Note
What is happening here?
Line 5 imports the Protobuf Publisher from eCAL
Line 9 imports the hello_world_pb2.py
that we just have created.
Line 14 initializes eCAL. The name of the node is “Python Protobuf Publisher”.
By also providing sys.argv
you get the ability to pass command line arguments to eCAL, e.g. for loading an ecal.yaml
configuration file from a non-standard path.
In practice this is not often used and you could also pass []
as arguments.
Line 20 creates an eCAL Publisher for the topic “hello_world_python_protobuf_topic”. The second parameter is the type from our converted “hello_world.proto” message description. By providing the type, eCAL knows how to dynamically deserialize the message.
If you would omit passing the type here, you would still be able to receive the messages, but eCAL Mon would only display a boring hexadecimal representation of the content.
Line 35-38 instantiate the protobuf message and fill it with content.
Line 43 sends the entire protobuf message out to all subscribers on the topic.
When you execute the python script, you can now already see the data in the eCAL Monitor!
8.2.3. Protobuf Receiver
We know that the sender already works. So, let’s implement a python script that can receive the serialized protobuf data, deserialize it and display the content of the message!
Create a file protobuf_rec.py
next to the sender and paste the following content:
|fa-folder-open|
├─ |fa-folder-open| proto_messages
│ └─ |fa-file-alt| hello_world.proto
├─ |fa-python| protobuf_snd.py
└─ |fa-python| protobuf_rec.py
1import sys
2import time
3
4import ecal.core.core as ecal_core
5from ecal.core.subscriber import ProtoSubscriber
6
7# Import the "hello_world_pb2.py" file that we have just generated from the
8# proto_messages directory
9import proto_messages.hello_world_pb2 as hello_world_pb2
10
11# Callback for receiving messages
12def callback(topic_name, hello_world_proto_msg, time):
13 print("Message {} from {}: {}".format(hello_world_proto_msg.id
14 , hello_world_proto_msg.name
15 , hello_world_proto_msg.msg))
16
17if __name__ == "__main__":
18 # initialize eCAL API. The name of our Process will be
19 # "Python Protobuf Subscriber"
20 ecal_core.initialize(sys.argv, "Python Protobuf Subscriber")
21
22 # Create a Protobuf Publisher that publishes on the topic
23 # "hello_world_python_protobuf_topic". The second parameter tells eCAL which
24 # datatype we are expecting to receive on that topic.
25 sub = ProtoSubscriber("hello_world_python_protobuf_topic"
26 , hello_world_pb2.HelloWorld)
27
28 # Set the Callback
29 sub.set_callback(callback)
30
31 # Just don't exit
32 while ecal_core.ok():
33 time.sleep(0.5)
34
35 # finalize eCAL API
36 ecal_core.finalize()
Note
What is happening here?
Line 5 imports the Protobuf Subscriber from eCAL
Line 9 imports the hello_world_pb2.py
that we have created in the first step.
Line 12 - 15 will be our callback method that is called every time a new message is received on our topic. We will set it as message callback for the eCAL subscriber in line 29.
Line 25 creates an eCAL Publisher for the topic “hello_world_python_protobuf_topic”. The second parameter is the type from our converted “hello_world.proto” message description. Providing the correct datatype here is crucial for eCAL to automatically de-serialize the data and to provide it to the callback as the correct type.
Line 29 sets our callback as callback method for the eCAL Subscriber.
Now execute both python scripts to see if they work!
Congratulations, you have completed the Getting Started chapter! Now go ahead and use eCAL in your real-world scenario.
If you experience issues, you can create a GitHub issue, to get help.
8.2.4. Files
|fa-folder-open| ├─ |fa-folder-open| proto_messages │ └─ |fa-file-alt|hello_world.proto
├─ |fa-python|protobuf_snd.py
└─ |fa-python|protobuf_rec.py