diff --git a/src/ltcodecs/ros_message_codec.py b/src/ltcodecs/ros_message_codec.py index 6e48f0ca9a6bf78e9401411f2abd0cbee090c2e2..87940c311b6746620b3446cbd76780c27fc8ea41 100644 --- a/src/ltcodecs/ros_message_codec.py +++ b/src/ltcodecs/ros_message_codec.py @@ -14,14 +14,11 @@ class RosMessageCodec(object): self.checksum = checksum if fields_dict: - self.metadata_decoder_fields = {} self.metadata_encoder_fields = {} for field_name, field_params in fields_dict.items(): # We only support a few metadata fields for encoding codec_name = field_params.get('codec', 'auto') - if codec_name in (ltcodecs.metadata_decoders.keys()): - self.metadata_decoder_fields[field_name] = codec_name for field_name, field_params in fields_dict.items(): # We only support a few metadata fields for encoding @@ -29,7 +26,6 @@ class RosMessageCodec(object): if codec_name in (ltcodecs.metadata_encoders.keys()): self.metadata_encoder_fields[field_name] = codec_name else: - self.metadata_decoder_fields = None self.metadata_encoder_fields = None self.root_field_codec = ltcodecs.RosMsgFieldCodec(ros_type=ros_type, fields=fields_dict) @@ -60,7 +56,7 @@ class RosMessageCodec(object): bits_copy.pos = bits_to_decode.pos decoded_dict = self.root_field_codec.decode_as_dict(bits_copy) - ros_msg = self.root_field_codec.decode(bits_to_decode) + ros_msg = self.root_field_codec.decode(bits_to_decode, metadata=received_packet) if self.checksum: msgpack_bytes = msgpack.packb(decoded_dict) @@ -77,22 +73,9 @@ class RosMessageCodec(object): if calculated_crc != received_crc: raise ValueError("Message CRC Mismatch") - # Append metadata - if received_packet: - self._decode_metadata(ros_msg, received_packet) - rospy.loginfo("ROS Message: {}".format(ros_msg)) return ros_msg - def _decode_metadata(self, ros_msg, received_packet): - # This will decode metadata fields into the ros msg. - # it only works on fields of te base ROS message, not any nested ROS messages (for now) - if self.metadata_decoder_fields: - for field_name, codec in self.metadata_decoder_fields.items(): - metadata_attribute = ltcodecs.metadata_decoders[codec] - value = operator.attrgetter(metadata_attribute)(received_packet) - setattr(ros_msg, field_name, value) - def _encode_metadata(self, ros_msg): # Look through the fields dict for one of the magic keywords specified in field_codecs.py if self.metadata_encoder_fields: diff --git a/src/ltcodecs/ros_msg_field_codec.py b/src/ltcodecs/ros_msg_field_codec.py index fd399cf5022b87b622e5c9b200330f777dbe40e5..a4590c6384655e091f88ad3ceb120ad2611e1d0f 100644 --- a/src/ltcodecs/ros_msg_field_codec.py +++ b/src/ltcodecs/ros_msg_field_codec.py @@ -69,13 +69,14 @@ class RosMsgFieldCodec(FieldCodec): if field_params['codec'] not in ltcodecs.metadata_decoders.keys(): self.field_codecs[field_name] = ltcodecs.field_codec_classes[field_params['codec']](**field_params) else: - self.field_codecs[field_name] = None + # The metadata codec just remains a string, which we use for a lookup later. + pass except KeyError as e: raise KeyError("Error parsing codec config for {}. Got params:\n{}\nError: {}".format(field_name, field_params, e)) - def encode(self, message: AnyMsg): + def encode(self, message: AnyMsg, metadata=None): # ROS messages use __slots__, so we can't use __dict__ or vars() to get the attributes as a dict message_dict = {} for field in message.__slots__: @@ -86,16 +87,18 @@ class RosMsgFieldCodec(FieldCodec): for field_name, field_params in self.fields.items(): try: field_codec = self.field_codecs[field_name] - if not field_codec: + # Note that metadata encoding is done at the ros_msg_codec level, not here + if not field_codec or isinstance(field_codec, str): continue field_bits, encoded_dict[field_name] = field_codec.encode(message_dict[field_name]) encoded_bits.append(field_bits) except Exception as e: #print("Codec: {}, max len bits {}".format(field_codec, field_codec.max_length_bits)) - raise Exception('Error encoding field "{}" with codec {} (max len bits {})'.format(field_name, field_codec, field_codec.max_length_bits)).with_traceback(e.__traceback__) + raise Exception('Error encoding field "{}" with codec {} (max len bits {})'.format(field_name, + field_codec, field_codec.max_length_bits)).with_traceback(e.__traceback__) return encoded_bits, encoded_dict - def decode(self, bits_to_decode: ConstBitStream): + def decode(self, bits_to_decode: ConstBitStream, metadata=None): # We go through the bits in sequence until we are decoded. # The ConstBitStream has an internal read pointer. decoded_message = {} @@ -103,8 +106,25 @@ class RosMsgFieldCodec(FieldCodec): field_codec = self.field_codecs[field_name] if not field_codec: continue + if isinstance(field_codec, str): + # try to decode it as a metadata codec + if not metadata: + continue + try: + if field_codec in ltcodecs.metadata_decoders: + metadata_attribute = ltcodecs.metadata_decoders[field_codec] + value = operator.attrgetter(metadata_attribute)(metadata) + setattr(decoded_message, field_name, value) + except KeyError: + # if we don't recognize the metadata codec, just keep going + continue + ## print("Ros decode field {}".format(field_name)) - decoded_message[field_name] = field_codec.decode(bits_to_decode) + # pass metadata only to nested ros msg fields + if isinstance(field_codec, type(self)): + decoded_message[field_name] = field_codec.decode(bits_to_decode, metadata=metadata) + else: + decoded_message[field_name] = field_codec.decode(bits_to_decode) ## print("Ros decode got {}".format(decoded_message)) return self.ros_msg_class(**decoded_message) @@ -129,4 +149,3 @@ class RosMsgFieldCodec(FieldCodec): @property def max_length_bits(self): return sum([c.max_length_bits for c in self.field_codecs.values()]) -