From dd3dc2525dd7521c08ae9f238f058ce46382bddc Mon Sep 17 00:00:00 2001 From: jaketothepast Date: Thu, 27 Apr 2023 10:50:02 -0400 Subject: [PATCH] Clean up code, refactoring, begin to read more than 1 row --- src/FIT.jl | 1 + src/file.jl | 96 ++++++++++++++++++++++++++++++++--------------------- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/FIT.jl b/src/FIT.jl index e2b3da6..7fe87e7 100644 --- a/src/FIT.jl +++ b/src/FIT.jl @@ -1,4 +1,5 @@ module FIT + export read_file include("file.jl") diff --git a/src/file.jl b/src/file.jl index 21200a1..7c1b1c8 100644 --- a/src/file.jl +++ b/src/file.jl @@ -19,14 +19,19 @@ struct FITHeader crc::AbstractVector{<:Real} end +@enum RecordType begin + definition = 1 + data = 2 + timestamp = 3 +end + """ RecordHeader Contains the header byte for a record row """ mutable struct RecordHeader - definition::Bool - data::Bool + type::RecordType local_mesg_type::Int64 end @@ -63,12 +68,12 @@ mutable struct FITFileReader header::Union{FITHeader, Nothing} body::Union{Vector{DataRecord}, Nothing} mesg_map::Dict{Real, AbstractString} + next_row_sz::Int64 end -blank_file(fp::AbstractString) = FITFileReader(fp, nothing, 0, nothing, nothing, Dict()) +blank_file(fp::AbstractString) = FITFileReader(fp, nothing, 0, nothing, nothing, Dict(), 0) blank_row() = DataRecord(nothing) -blank_header_byte() = RecordHeader(false, false, 0) """ open_fit_file!(f::FITFileReader) @@ -123,11 +128,16 @@ end Decode the header byte, determine if the record is a definition, data, or timestamp. """ function decode_header(v::Vector{UInt8})::RecordHeader - record = blank_header_byte() - record.definition = v[7] === 0x01 - record.data = !record.definition - record.local_mesg_type = sum_bits(v[1:4]) - record + type = if v[7] == 1 + timestamp + elseif v[6] == 1 + definition + else + data + end + + local_mesg_type = sum_bits(v[1:4]) + RecordHeader(type, local_mesg_type) end """ @@ -144,6 +154,10 @@ function parse_field(mesg_type::AbstractString, bytes::Vector{UInt8})::FieldDefi ) end +function get_row_size(field_definitions::Vector{FieldDefinition})::Int64 + sum([def.sz_bytes for def ∈ field_definitions]) +end + """ read_record_content!(f::FITFileReader, hdr::RecordHeader)::Union{DefinitionBody, DataBody} @@ -152,29 +166,36 @@ Using f, read the appropriate message body given the hdr (which tells us if this function read_record_content!(f::FITFileReader, hdr::RecordHeader)::Union{DefinitionBody, DataBody} # Read the definition record, return the body if hdr.definition - # Read the fixed 5 bytes first. - def_header_bytes = read_bytes!(f, 5) - global_mesg_type = get_mesg_num_string(byte_vec_to_int(def_header_bytes[3:4])) - def_header_fields = read_bytes!(f, def_header_bytes[end] * 3) - - # Parse the field. global_mesg_type needed to decode the field number description - field_definitions = [parse_field(global_mesg_type, def_header_fields[i:i+2]) for i ∈ 1:3:length(def_header_fields)] - - # See if there is developer data - def_header_developer_flag = read_bytes!(f, 1) - if def_header_developer_flag[1] > 0x00 - def_header_developer_fields = read_bytes!(f, def_header_developer_flag * 3) + # Read all bytes first + header_bytes = read_bytes!(f, 5) + header_field_definitions = read_bytes!(f, header_bytes[end] * 3) # sz * field size + header_developer_flag = read_bytes!(f, 1) + if header_developer_flag[1] > 0x00 + header_developer_fields = read_bytes!(f, header_developer_flag[1] * 3) + else + header_developer_fields::Vector{UInt8} = [] end + # Parse the bytes + global_mesg_type = get_mesg_num_string(byte_vec_to_int(header_bytes[3:4])) + field_definitions = [parse_field(global_mesg_type, header_field_definitions[i:i+2]) for i ∈ 1:3:length(header_field_definitions)] + developer_field_definitions = [parse_field(global_mesg_type, header_developer_fields[1:1+2]) for i ∈ 1:3:length(header_developer_fields)] + + # Get the total row size from parsing the fields. + row_sz = sum(get_row_size.([field_definitions, developer_field_definitions])) + # Associate the global message type with this local message type. f.mesg_map[hdr.local_mesg_type] = global_mesg_type - + f.next_row_sz = row_sz + DefinitionBody( - def_header_bytes[2] == 0 ? "littleendian" : "bigendian", + header_bytes[2] == 0 ? "littleendian" : "bigendian", global_mesg_type, - def_header_bytes[end], + header_bytes[end], field_definitions ) + else + row_bytes = read_bytes!(f, f.next_row_sz) end end @@ -186,24 +207,23 @@ Read all the data record messages. function read_records!(f::FITFileReader) if f.bytes_read < 12 || f.bytes_read > 14 throw("invalid header read, not within header size") + elseif isnothing(f.header) + throw("cant read FIT file, no header") end - # Read header byte - decode - header_bits = bit_vector(read_bytes!(f, 1); little_endian=true) - header = decode_header(header_bits) - - # Read the record content + # Loop through bytes_read until done + while f.bytes_read < f.header.size + # Read header byte. + header_bits = bit_vector(read_bytes!(f, 1); little_endian=true) + header = decode_header(header_bits) + record = DataRecord(header, read_record_content!(f, header)) - record = DataRecord(header, read_record_content!(f, header)) - - # Push record to the body. - if isnothing(f.body) - f.body = [record] - else - push!(f.body, record) + if isnothing(f.body) + f.body = [record] + else + push!(f.body, record) + end end - - record end function read_file(filepath::AbstractString)::FITFileReader