def __populate_schema_cache(self, schema_sig, field_name, type, count): """Cache schema on the assumption that the set of schemas is much smaller than the number of times they are used.""" schema = data.Schema( field_name, collections.OrderedDict([(field_name, [type, count, '%d'])])) self.__schema_cache[schema_sig] = schema return schema
class RecordHeader(data.Data): """Object that represents a FIT file record header.""" rh_schema = data.Schema( 'rh', collections.OrderedDict([('record_header', ['UINT8', 1, '%x'])])) message_type_string = ['data', 'definition'] def __init__(self, file): """Return a RecordHeader instance created by reading the record geader data from a FIT file.""" super(RecordHeader, self).__init__(file, self.rh_schema) self.message_class = MessageClass(self.message_type()) def __compressed_timestamp(self): return (self.record_header & 0x80) == 0x80 def message_type(self): return (self.record_header & 0x40) == 0x40 def developer_data(self): return (self.record_header & 0x60) == 0x60 def local_message(self): return (self.record_header & 0x0f) def __str__(self): """Return a string representation of a RecordHeader instance.""" return ("RecordHeader: Local %s message %d (Compressed %d)" % (self.message_class.name, self.local_message(), self.__compressed_timestamp())) def __repr__(self): """Return a string representation of a RecordHeader instance.""" return self.__str__()
class FileHeader(data.Data): """Class that represents a FIT file header.""" fh_primary_schema = data.Schema( 'fh_primary', collections.OrderedDict( [ ('header_size', ['UINT8', 1, '%d']), ('protocol_version', ['UINT8', 1, '%x']), ('profile_version', ['UINT16', 1, '%d']), ('data_size', ['UINT32', 1, '%d']), ('data_type', ['CHAR', 4, '%c']) ] ) ) fh_optional_schema = data.Schema( 'fh_optional', collections.OrderedDict([('crc', ['UINT16', 1, '%x'])]) ) profile_version_str = {100 : 'activity', 1602 : 'device'} min_file_header_size = 12 opt_file_header_size = 14 min_protocol_version = 0x10 file_data_type = [46, 70, 73, 84] # file_data_type = ['.', 'F', 'I', 'T'] def __init__(self, file): """Return a FileHeader instance created by reading data from a Fit file.""" super(FileHeader, self).__init__(file, FileHeader.fh_primary_schema, [(FileHeader.fh_optional_schema, self.__decode_secondary)]) self.__check() def __decode_secondary(self): return (self.header_size >= FileHeader.opt_file_header_size) def __check(self): if self.header_size < FileHeader.min_file_header_size: raise exceptions.FitFileBadHeaderSize("%d < %d" % (self.header_size, FileHeader.min_file_header_size)) if self.protocol_version < FileHeader.min_protocol_version: raise exceptions.FitFileBadProtocolVersion("%d < %d" % (self.protocol_version, FileHeader.min_protocol_version)) if self.data_type != FileHeader.file_data_type: raise exceptions.FitFileDataType("%r < %r" % (self.data_type, FileHeader.file_data_type)) def __str__(self): """Return a string representation of a FileHeader instance.""" return ("%s(header size %d prot ver %x prof ver %d)" % (self.__class__.__name__, self.header_size, self.protocol_version, self.profile_version))
def __init__(self, file, definition_message, dev_field_definition, measurement_system): self.dev_field_definition = dev_field_definition self.measurement_system = measurement_system self.field = dev_field_definition.field() type = dev_field_definition.type_string() count = dev_field_definition.type_count() schema = data.Schema( self.field.name, collections.OrderedDict([(self.field.name, [type, count, '%d'])])) super(DevDataField, self).__init__(file, schema, None, definition_message.endian)
class FieldDefinition(data.Data, base_type.BaseType): """Object that defines the structure of a FIT file message field.""" fd_schema = data.Schema( 'fd', collections.OrderedDict([('field_definition_number', ['UINT8', 1, '%x']), ('size', ['UINT8', 1, '%x']), ('base_type', ['UINT8', 1, '%x'])])) def __init__(self, file): """ Return a FieldDefinition instance created by reading data from a FIT file. Paramters: file (File): a FIT File instance. """ super(FieldDefinition, self).__init__(file, FieldDefinition.fd_schema) def base_type(self): """Return the base type for the field.""" return self._base_type(self.base_type) def type_endian(self): """Return the endian value for the field.""" return self._type_endian(self.base_type) def type_name(self): """Return the type name for the field.""" return self._type_name(self.base_type) def invalid(self): """Return the invalid value for the field.""" return self._invalid(self.base_type) def type_string(self): """Return the type string for the field.""" return self._type_string(self.base_type) def type_count(self): """Return the number of values for the field.""" type_size = data.Schema.type_to_size(self.type_string()) return (self.size / type_size) def __str__(self): """Return a string representation for the FieldDefinition instance.""" return ("FieldDefinition: type %d: %d of %s" % (self.field_definition_number, self.type_count(), self.type_string()))
class DeveloperFieldDefinition(data.Data, base_type.BaseType): """Developer filed definitions decoded from a FIT file.""" dfd_schema = data.Schema( 'dfd_schema', collections.OrderedDict( [ ('field_number', ['UINT8', 1, '%x']), ('size', ['UINT8', 1, '%x']), ('developer_data_index', ['UINT8', 1, '%x']) ] ) ) def __init__(self, dev_field_dict, file): """ Return a DeveloperFieldDefinition instance created by reading data from a FIT file. Paramters: dev_field_dict (dict): a dictionary of developer defined fields. file (File): a FIT File instance. """ super(DeveloperFieldDefinition, self).__init__(file, DeveloperFieldDefinition.dfd_schema) self.dev_field = dev_field_dict.get(self.field_number) if self.dev_field is None: raise exceptions.FitUndefDevMessageType('Dev field %d undefined in %r' % (self.field_number, dev_field_dict)) self.field_name = self.dev_field['field_name'].value self.native_message_num = self.dev_field['native_message_num'].value self.native_field_num = self.dev_field['native_field_num'].value self.units = self.dev_field['units'].value self.offset = self.dev_field['offset'].value self.scale = self.dev_field['scale'].value def __base_type_value(self): return self.dev_field['fit_base_type_id'].orig def field(self): """Return a DevField instance representing the field for this DeveloperFieldDefinition instance.""" # if self.native_message_num is not None and self.native_field_num is not None: # message_data = DefinitionMessageData.get_message(self.native_message_num) # field_dict = message_data[1] # return field_dict[self.native_field_num] return fields.DevField('dev_' + self.field_name, self.units, self.scale, self.offset) def base_type(self): """Return the base type for the field.""" return self._base_type(self.__base_type_value()) def type_endian(self): """Return the endian value for the field.""" return self._type_endian(self.__base_type_value()) def type_name(self): """Return the type name for the field.""" return self._type_name(self.__base_type_value()) def invalid(self): """Return the invalid value for the field.""" return self._invalid(self.__base_type_value()) def type_string(self): """Return the type string for the field.""" return self._type_string(self.__base_type_value()) def type_count(self): """Return the number of values for the field.""" type_size = data.Schema.type_to_size(self.type_string()) return (self.size / type_size) def __str__(self): """Return a string representation for the DeveloperFieldDefinition instance.""" return ("%s: type %d: %d %d of %s" % (self.__class__.__name__, self.field_number, self.developer_data_index, self.type_count(), self.type_string()))
class DefinitionMessage(data.Data): """FIT file definition message.""" dm_primary_schema = data.Schema( 'dm_primary', collections.OrderedDict([('reserved', ['UINT8', 1, '%x']), ('architecture', ['UINT8', 1, '%x'])])) dm_secondary_schema = data.Schema( 'dm_secondary', collections.OrderedDict([('global_message_number', ['UINT16', 1, '%x']), ('fields', ['UINT8', 1, '%x'])])) dm_dev_schema = data.Schema( 'dm_dev', collections.OrderedDict([('dev_fields', ['UINT8', 1, '%x'])])) def __init__(self, record_header, dev_field_dict, file): """ Return a DefinitionMessage instance created by reading data from a FIT file. Paramters: record_header (RecordHeader): the record header associated with this definition message. dev_field_dict (dict): a dictionary of developer defoined fields in the FIT file. file (File): the FIT file instance to read the definition message data from. """ super(DefinitionMessage, self).__init__( file, DefinitionMessage.dm_primary_schema, [(DefinitionMessage.dm_secondary_schema, self.__decode_secondary)]) self.message_type = MessageType.get_type(self.global_message_number) self.message_data = DefinitionMessageData.get_message_definition( self.message_type) self.field_definitions = [] for index in xrange(self.fields): field_definition = FieldDefinition(file) self.file_size += field_definition.file_size self.field_definitions.append(field_definition) self.has_dev_fields = record_header.developer_data() self.dev_field_definitions = [] if self.has_dev_fields: self._decode(DefinitionMessage.dm_dev_schema) for index in xrange(self.dev_fields): dev_field_definition = DeveloperFieldDefinition( dev_field_dict, file) self.file_size += dev_field_definition.file_size self.dev_field_definitions.append(dev_field_definition) def __decode_secondary(self): self.endian = data.Architecture(self.architecture) return True def field(self, field_number): """Return an instance of the proper Field subclass for the given field definition.""" return DefinitionMessageData.reserved_field_indexes.get( field_number, self.message_data.get(field_number, fields.UnknownField(field_number))) def __str__(self): """Return a string representation of a DefinitionMessage instance.""" return ("DefinitionMessage: %r %d %s fields" % (self.message_type, self.fields, self.endian.name))