Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

FitReader

fit_reader.livemd

FitReader

Mix.install([:fitreader, :req, :kino, :inflex])

Section

{:ok, pid} = Fit.read("/home/kuku/11240774353_ACTIVITY.fit")
Fit.record(pid, 3)
field = 325

Fit.record(pid, field)
|> Enum.take(1)
|> IO.inspect()

Fit.record(pid, field)
fit =
  "/home/kuku/Projects/fit_decoder/priv/fit.json"
  |> File.read!()
  |> Jason.decode!()

Kino.Tree.new(fit)
num_mesg = for {m, n} <- fit["MesgNum"], into: %{}, do: {n, String.to_atom(Inflex.underscore(m))}
defmodule FitDecoder.Header do
  defstruct [:protocol_version, :profile_version, :data_size, :header_crc, :crc]

  def parse(
        <<14, protocol_version::size(8), profile_version::little-size(16),
          data_size::little-size(32), ?., ?F, ?I, ?T, header_crc::little-size(16),
          rest::bytes-size(data_size), crc::size(16)>>
      ) do
    {%__MODULE__{
       protocol_version: protocol_version,
       profile_version: profile_version,
       data_size: data_size,
       header_crc: header_crc,
       crc: crc
     }, rest}
  end

  def parse(
        <<12, protocol_version::size(8), profile_version::little-size(16),
          data_size::little-size(32), ?., ?F, ?I, ?T, rest::bytes-size(data_size), crc::size(16)>>
      ) do
    {%__MODULE__{
       protocol_version: protocol_version,
       profile_version: profile_version,
       data_size: data_size,
       header_crc: nil,
       crc: crc
     }, rest}
  end
end
defmodule FitDecoder.DefinitionMessage do
  def parse(
        <<_reserved::size(8), endianess::size(8), message_number::little-size(16),
          fields::size(8)>> <> rest,
        acc,
        settings
      ) do
    endianess = if endianess == 1, do: :big, else: :little
    acc = %{acc | endianess: endianess}
    parse_fields(rest, fields, acc, settings)
  end

  def parse_fields(rest, 0, acc, _) do
    {acc, rest}
  end

  def parse_fields(
        <> <> rest,
        fields,
        acc,
        settings
      ) do
    <> = rest
    %{acc | fields: [data | acc.fields]}
    {{settings[field_definition], size, base_type_number, data}, acc}
  end
end

defmodule FitDecoder.Message do
  def parse(data, %{type: :definition} = acc, settings) do
    FitDecoder.DefinitionMessage.parse(data, acc, settings)
  end
end

defmodule FitDecoder.RecordHeader do
  @normal_header 0
  @compressed_header 1
  def parse(
        <<@normal_header::size(1), type::size(1), type_specific::size(1), reserved::size(1),
          local_type::size(4)>> <> rest,
        acc
      ) do
    type = if type == 1, do: :definition, else: :data
    {%{acc | type: type, type_specific: type_specific, local_type: local_type}, rest}
  end

  def parse(
        <<@compressed_header::size(1), local_type::size(2), timestamp_offset::size(5)>> <>
          rest,
        acc
      ) do
    {%{acc | type: :timestamp, local_type: local_type, timestamp_offset: timestamp_offset}, rest}
  end
end

defmodule FitDecoder.Record do
  defstruct [:endianess, :type, :type_specific, :local_type, :fields, :timestamp_offset]

  def parse(data, settings) do
    {acc, rest} = FitDecoder.RecordHeader.parse(data, %__MODULE__{})
    FitDecoder.Message.parse(rest, acc, settings)
  end
end

defmodule FitDecoder do
  def decode(file, settings) do
    {header, records} = FitDecoder.Header.parse(file)
    IO.inspect(header)
    FitDecoder.Record.parse(records, settings)
  end
end

File.read!("/home/kuku/11240774353_ACTIVITY.fit")
|> IO.inspect()
|> FitDecoder.decode(num_mesg)
"/home/kuku/profile.js"
|> File.read!()
# |> String.downcase()
|> String.replace("{", "%{")
|> String.replace("/", "#")
|> String.replace(~r"(\s\d+):", " \\1 => ")
|> String.replace(~r"(\s0x[0-9A-F]+):", " \\1 => ")
|> String.replace(":[", ": [")
|> String.replace(" :", ":")
|> String.replace("export default Profile;", "end\nend")
|> String.replace("const Profile =", "defmodule Profile do\n def parse do")
|> Code.string_to_quoted!()
# |> elem(2)
# |> Enum.at(1)
|> Code.compile_quoted()
<> = <<2, 0, 0, 0>>
x