Bagys and Map Bagys
ROSH provides libraries for two types of YAML-based message storage: bagys and map bagys. Regular bagys, just like ROS bags, store a sequence of messages. Map bagys are like Python dictionaries: they store a mapping between a key and a message.
Bagys are like ROS bags, except they are based on a human-readable YAML format. They also have some other key differences:
- The message type is not stored in the bagy
- (Corollary) They can only store a single message type
- The do not record message receipt time
rostopic echo /my_topic > file.bagy
Similarly, the bagy format is is identical to the input of rostopic pub and rosservice call. That means that it is possible to echo a single bagy message into either of these tools. In the future, this will be improved upon.
Writing to a bagy
bagys have an API similar to files, i.e. open().
Using with statement
m1 = msg.std_msgs.String('foo') m2 = msg.std_msgs.String('bar') with Bagy('foo.bagy', 'w') as b: b.write(m1) b.write(m2)
m1 = msg.std_msgs.String('foo') m2 = msg.std_msgs.String('bar') b = Bagy('foo.bagy', 'w') b.write(m1) b.write(m2) b.close()
Recording a bagy
If you don't want to write messages individually to a bagy, you can setup a bagy to record a single topic. You can call stop() and start() to pause/resume the recording.
WARNING: bagy recording within ROSH is not high performance.
b = Bagy('record.bagy', 'w') b.record(topics.rosout_agg) b.stop() b.start() b.close()
Reading from a bagy
There are a variety of ways of reading from a bagy, depending on your needs.
Read all messages into a list
with Bagy('foo.bagy', 'r', msg.std_msgs.String) as b: msgs = b.read()
Iterate over messages
b = Bagy('foo.bagy', 'r', msg.std_msgs.String) for m in b: print m b.close()
Read messages one by one
b = Bagy('foo.bagy', 'r', msg.std_msgs.String) m = b.next() b.close()
It is possible to record a bagy with one message type and read it back as another provided that the type you are reading into has at least the fields present in the original type, and the fields are of compatible types. For example, geometry_msgs/Vector3 vs. geometry_msgs/Point have identical specifications.
There are several potential use cases for this:
- Service requests/responses that have identical fields
- Actions that have identical fields
You can also manually create "sparse" bagys, where you only store only a subset of the message's fields. When you load from the bagy, the rest of the fields will get default value assignments.
Recipe: Bagy-based Service
This creates the get_outlets service. In this particular case, our bagy contains a list of poses that we want to return.
with Bagy(findros('foo', 'outlet_poses.yaml'), srv.pr2_plugs_msgs.GetOutletsResponse) as b: resp = b.read() # assume only one message in file Service('get_outlets', srv.pr2_plugs_msgs.GetOutlets, lambda x: resp) serve_forever()
Map bagys are bagys that are indexed by keys. Whereas normal bagys are useful for storing a sequence of messages, map bagys are useful for storing a dictionary of messages.
Creating a map bagy
Map bagys have a file-like API, though there are various ways you can choose to write data to them.
with MapBagy('foo1.bagy', 'w') as b: b['key1'] = m1 b['key2'] = m2 b['key3'] = m3
with MapBagy('foo2.bagy', 'w') as b: b.write('key1', m1) b.write('key2', m2) b.write('key3', m3)
with MapBagy('foo.bagy', 'w') as b: b.write(key1=m1, key2=m2, key3=m3)
Reading from a key bagy
Read entire bagy into dictionary
with MapBagy(b, 'r', msg.std_msgs.String) as b: d = b.read() print d['key1']
Iterator style (default iterator iterates over keys and msgs)
with MapBagy('foo1.bagy', 'r', msg.std_msgs.String) as b: for k,m in b: print k, m
with MapBagy('foo.bagy', 'r', msg.std_msgs.String) as b: m = b['key1']
Recipe: Map-Bagy-based Service
This creates a fictional get_outlet service. In this particular case, our bagy contains a list of poses that we want to return, indexed by name. Our fictional query has a single name field that specifies which outlet we want.
with MapBagy(findros('foo', 'outlet_db.yaml'), srv.pr2_plugs_msgs.GetOutletResponse) as b: responses = b.read() Service('get_outlets', srv.pr2_plugs_msgs.GetOutlet, lambda x: responses[x.name]) serve_forever()