def decode_gpb_kv(message, args): """ Decode and print a GPB key-value message """ header = telemetry_kv_pb2.Telemetry() try: header.ParseFromString(message) except Exception as e: print("ERROR decoding header. Not a valid 'Telemetry' message. Full " "message dump below:") print(bytes_to_string(message)) return if args.json_dump: print(json.dumps(proto_to_dict(header))) else: # Print the message header print_gpb_kv_hdr(header) # Loop over the tables within the message, printing either just the first # row or all rows depending on the args specified if args.print_all: for entry in header.fields: print_gpb_kv_field(entry, 2) elif len(header.fields) > 0 and not args.brief: print(" Displaying first entry only") print_gpb_kv_field(header.fields[0], 1)
def print_gpb_compact_msg(field, indent, args): """ Recursively iterate over a compactGPB obejct, displaying all fields at an appropriate indent. Argument: field The object to print. Argument: indent The indent level to start printing at. """ for descriptor in field.DESCRIPTOR.fields: value = getattr(field, descriptor.name) if descriptor.type == descriptor.TYPE_MESSAGE: # # If the value is a sub-message then recursively call this function # to decode it. If the message is repeated then iterate over each # item. # if descriptor.label == descriptor.LABEL_REPEATED: print_at_indent( "{} ({} items) [".format(descriptor.name, len(value)), indent) for i, item in enumerate(value): print_at_indent("{} {} {{".format(descriptor.name, i), indent) print_gpb_compact_msg(item, indent + 1, args) print_at_indent("}", indent) if not args.print_all: # Stop after the first item unless all have been # requested break print_at_indent("]", indent) else: print_at_indent("{} {{".format(descriptor.name), indent) print_gpb_compact_msg(value, indent + 1, args) print_at_indent("}", indent) elif descriptor.type == descriptor.TYPE_ENUM: # # For enum types print the enum name # enum_name = descriptor.enum_type.values[value].name print_at_indent("{}: {}".format(descriptor.name, enum_name), indent) elif descriptor.type == descriptor.TYPE_BYTES: print_at_indent( "{}: {}".format(descriptor.name, bytes_to_string(value)), indent) else: # # For everything else just print the value # print_at_indent("{}: {}".format(descriptor.name, value), indent)
def print_gpb_compact_msg(field, indent, args): """ Recursively iterate over a compactGPB obejct, displaying all fields at an appropriate indent. Argument: field The object to print. Argument: indent The indent level to start printing at. """ for descriptor in field.DESCRIPTOR.fields: value = getattr(field, descriptor.name) if descriptor.type == descriptor.TYPE_MESSAGE: # # If the value is a sub-message then recursively call this function # to decode it. If the message is repeated then iterate over each # item. # if descriptor.label == descriptor.LABEL_REPEATED: print_at_indent("{} ({} items) [".format(descriptor.name, len(value)), indent) for i, item in enumerate(value): print_at_indent("{} {} {{".format(descriptor.name, i), indent) print_gpb_compact_msg(item, indent + 1, args) print_at_indent("}", indent) if not args.print_all: # Stop after the first item unless all have been # requested break print_at_indent("]", indent) else: print_at_indent("{} {{".format(descriptor.name), indent) print_gpb_compact_msg(value, indent + 1, args) print_at_indent("}", indent) elif descriptor.type == descriptor.TYPE_ENUM: # # For enum types print the enum name # enum_name = descriptor.enum_type.values[value].name print_at_indent("{}: {}".format(descriptor.name, enum_name), indent) elif descriptor.type == descriptor.TYPE_BYTES: print_at_indent("{}: {}".format(descriptor.name, bytes_to_string(value)), indent) else: # # For everything else just print the value # print_at_indent("{}: {}".format(descriptor.name, value), indent)
def print_json_data(obj, indent, args): """ Print the body of a JSON message """ if type(obj) == dict: for key in list(obj.keys()): child = obj[key] # If the child object is a list or dictionary then indent using # braces. If the child is a leaf then just print on a single line. if type(child) == dict: print_at_indent("{} {{".format(key), indent) print_json_data(obj[key], indent + 1, args) print_at_indent("}", indent) elif type(child) == list: if not args.print_all: warning = " - displaying first entry only" else: warning = "" print_at_indent( "{} ({} items{}) [".format(key, len(child), warning), indent) print_json_data(obj[key], indent + 1, args) print_at_indent("]", indent) elif key == "CollectionTime": # Pretty-print collection timestamp print_at_indent( "{}: {}".format(key, timestamp_to_string(child)), indent) else: # Try printing values as a string and if that fails print # it as bytes try: print_at_indent("{}: {}".format(key, str(child)), indent) except Exception: print_at_indent( "{}: {}".format(key, bytes_to_string(child)), indent) elif type(obj) == list: for i, item in enumerate(obj): print_at_indent("[{}]".format(i), indent) print_json_data(item, indent + 1, args) if not args.print_all: # Stop after first item break else: print_at_indent("{}".format(str(obj)), indent)
def print_json_data(obj, indent, args): """ Print the body of a JSON message """ if type(obj) == dict: for key in list(obj.keys()): child = obj[key] # If the child object is a list or dictionary then indent using # braces. If the child is a leaf then just print on a single line. if type(child) == dict: print_at_indent("{} {{".format(key), indent) print_json_data(obj[key], indent + 1, args) print_at_indent("}", indent) elif type(child) == list: if not args.print_all: warning = " - displaying first entry only" else: warning = "" print_at_indent("{} ({} items{}) [".format(key, len(child), warning), indent) print_json_data(obj[key], indent + 1, args) print_at_indent("]", indent) elif key == "CollectionTime": # Pretty-print collection timestamp print_at_indent("{}: {}".format(key, timestamp_to_string(child)), indent) else: # Try printing values as a string and if that fails print # it as bytes try: print_at_indent("{}: {}".format(key, str(child)), indent) except Exception: print_at_indent("{}: {}".format(key, bytes_to_string(child)), indent) elif type(obj) == list: for i, item in enumerate(obj): print_at_indent("[{}]".format(i), indent) print_json_data(item, indent + 1, args) if not args.print_all: # Stop after first item break else: print_at_indent("{}".format(str(obj)), indent)
def print_gpb_kv_field(field, indent): """ Pretty-print a TelemtryField message """ # Decode the timestamp if there is one if field.timestamp != 0: time = timestamp_to_string(field.timestamp) else: time = "" # Find the datatype and print it datatypes = [ "bytes_value", "string_value", "bool_value", "uint32_value", "uint64_value", "sint32_value", "sint64_value", "double_value", "float_value", ] for d in datatypes: datatype = d[:-6] if field.HasField(d): if datatype == "bytes": print_gpb_kv_field_data(field.name, bytes_to_string(field.bytes_value), datatype, time, indent) else: print_gpb_kv_field_data(field.name, getattr(field, d), datatype, time, indent) # If 'fields' is used then recursively call this function to decode if len(field.fields) > 0: print_gpb_kv_field_data( field.name, "fields", "items {}".format(len(field.fields)), "{} {{".format(time), indent ) for f in field.fields: print_gpb_kv_field(f, indent + 1) print_at_indent("}", indent)
def print_gpb_kv_field(field, indent): """ Pretty-print a TelemtryField message """ # Decode the timestamp if there is one if field.timestamp != 0: time = timestamp_to_string(field.timestamp) else: time = "" # Find the datatype and print it datatypes = [ "bytes_value", "string_value", "bool_value", "uint32_value", "uint64_value", "sint32_value", "sint64_value", "double_value", "float_value" ] for d in datatypes: datatype = d[:-6] if field.HasField(d): if datatype == "bytes": print_gpb_kv_field_data(field.name, bytes_to_string(field.bytes_value), datatype, time, indent) else: print_gpb_kv_field_data(field.name, getattr(field, d), datatype, time, indent) # If 'fields' is used then recursively call this function to decode if len(field.fields) > 0: print_gpb_kv_field_data(field.name, "fields", "items {}".format(len(field.fields)), "{} {{".format(time), indent) for f in field.fields: print_gpb_kv_field(f, indent + 1) print_at_indent("}", indent)
def decode_gpb_compact(message, args): """ Decode and print a GPB compact message """ header = telemetry_pb2.TelemetryHeader() try: header.ParseFromString(message) except Exception as e: print("ERROR decoding header. Not a valid 'TelemetryHeader' message. " "Full message dump below:") print(bytes_to_string(message)) return # Check the encoding value is correct ENCODING = 0x87654321 if header.encoding != ENCODING: print("Invalid 'encoding' value {:#x} (expected {:#x})".format(header.encoding, ENCODING)) return # Print the message header json_dict = {} if args.json_dump: # Convert the protobuf into a dictionary in preparation for dumping # it as JSON. json_dict = proto_to_dict(header) else: print_gpb_compact_hdr(header) # Loop over the tables within the message, printing either just the first # row or all rows depending on the args specified for t, entry in enumerate(header.tables): schema_path = entry.policy_path if not args.json_dump: print(INDENT + "Schema Path:{}".format(schema_path)) warning = "" if not args.print_all: warning = "(Only first row displayed)" print(INDENT + "# Rows:{} {}".format(len(entry.row), warning)) if not schema_path in decoder_dict.keys(): print(INDENT + "No decoder available") if args.json_dump: json_dict["tables"][t]["row"][0] = "<No decoder available>" else: for i, row in enumerate(entry.row): row_msg = decoder_dict[schema_path]() try: row_msg.ParseFromString(row) if args.json_dump: # Replace the bytes in the 'row' field with a decoded # dict table = json_dict["tables"][t] table["row"][i] = proto_to_dict(row_msg) else: print(INDENT * 2 + "Row {}:".format(i)) print_gpb_compact_msg(row_msg, 2, args) print("") except Exception as e: print("ERROR decoding row. Not a valid GPB message. Full " "message dump below: {}".format(e)) print(bytes_to_string(row)) if not args.print_all and not args.json_dump: break if args.json_dump: print(json.dumps(json_dict))
DECODE_FN_MAP = { FieldDescriptor.TYPE_DOUBLE: float, FieldDescriptor.TYPE_FLOAT: float, FieldDescriptor.TYPE_INT32: int, FieldDescriptor.TYPE_INT64: long, FieldDescriptor.TYPE_UINT32: int, FieldDescriptor.TYPE_UINT64: long, FieldDescriptor.TYPE_SINT32: int, FieldDescriptor.TYPE_SINT64: long, FieldDescriptor.TYPE_FIXED32: int, FieldDescriptor.TYPE_FIXED64: long, FieldDescriptor.TYPE_SFIXED32: int, FieldDescriptor.TYPE_SFIXED64: long, FieldDescriptor.TYPE_BOOL: bool, FieldDescriptor.TYPE_STRING: unicode, FieldDescriptor.TYPE_BYTES: lambda b: bytes_to_string(b), FieldDescriptor.TYPE_ENUM: int, } def field_type_to_fn(msg, field): if field.type == FieldDescriptor.TYPE_MESSAGE: # For embedded messages recursively call this function. If it is # a repeated field return a list result = lambda msg: proto_to_dict(msg) elif field.type in DECODE_FN_MAP: result = DECODE_FN_MAP[field.type] else: raise TypeError("Field %s.%s has unrecognised type id %d" % (msg.__class__.__name__, field.name, field.type)) return result
def decode_gpb_compact(message, args): """ Decode and print a GPB compact message """ header = telemetry_pb2.TelemetryHeader() try: header.ParseFromString(message) except Exception as e: print("ERROR decoding header. Not a valid 'TelemetryHeader' message. " "Full message dump below:") print(bytes_to_string(message)) return # Check the encoding value is correct ENCODING = 0x87654321 if header.encoding != ENCODING: print("Invalid 'encoding' value {:#x} (expected {:#x})".format( header.encoding, ENCODING)) return # Print the message header json_dict = {} if args.json_dump: # Convert the protobuf into a dictionary in preparation for dumping # it as JSON. json_dict = proto_to_dict(header) else: print_gpb_compact_hdr(header) # Loop over the tables within the message, printing either just the first # row or all rows depending on the args specified for t, entry in enumerate(header.tables): schema_path = entry.policy_path if not args.json_dump: print(INDENT + "Schema Path:{}".format(schema_path)) warning = "" if not args.print_all: warning = "(Only first row displayed)" print(INDENT + "# Rows:{} {}".format(len(entry.row), warning)) if not schema_path in decoder_dict.keys(): print(INDENT + "No decoder available") if args.json_dump: json_dict["tables"][t]["row"][0] = "<No decoder available>" else: for i, row in enumerate(entry.row): row_msg = decoder_dict[schema_path]() try: row_msg.ParseFromString(row) if args.json_dump: # Replace the bytes in the 'row' field with a decoded # dict table = json_dict["tables"][t] table["row"][i] = proto_to_dict(row_msg) else: print(INDENT * 2 + "Row {}:".format(i)) print_gpb_compact_msg(row_msg, 2, args) print("") except Exception as e: print("ERROR decoding row. Not a valid GPB message. Full " "message dump below: {}".format(e)) print(bytes_to_string(row)) if not args.print_all and not args.json_dump: break if args.json_dump: print(json.dumps(json_dict))