defmodule ExViva.Decoders.Sample do defmacrop v!(key) do quote do Map.fetch!(var!(sample), unquote(key)) end end def simple_decode(sample) do unit = v!("Unit") type = parse_type(v!("Type"), unit) %ExViva.Sample{ heading: v!("Heading"), unit: v!("Unit"), trend: v!("Trend"), water_level_reference: v!("WaterLevelReference"), calm: v!("Calm"), type: type, value: parse_value(v!("Value"), unit, type) } |> with_name(sample) |> with_common(sample) end defp with_station_id(result, %{"StationID" => station_id}) do %{result | station_id: station_id} end defp with_message(result, %{"Msg" => message}) do %{result | message: message} end defp with_quality(result, %{"Quality" => "Ok"}) do %{result | quality: "ok"} end defp with_quality(result, %{"Quality" => quality}) do %{result | quality: quality} end defp with_timestamp(result, %{"Updated" => updated}) do %{result | updated_at: parse_timestamp(updated)} end defp with_name(result, %{"Name" => name}) do %{result | name: name} end defp with_common(result, sample) do result |> with_name(sample) |> with_station_id(sample) |> with_message(sample) |> with_quality(sample) |> with_timestamp(sample) end defp directional_float(value) do case String.split(value, " ", parts: 2) do [_dir, value] -> String.to_float(value) [value] -> single_float(value) end end defp parse_timestamp(timestamp) do String.replace(timestamp, " ", "T") |> NaiveDateTime.from_iso8601!() end defp single_float(value) do try do String.to_float(value) rescue ArgumentError -> String.to_integer(value) * 1.0 end end defp parse_value(value, unit, type) do try do parse_value(value, unit) catch error -> require Logger Logger.error("failed to decode: #{inspect(type)}") raise error end end defp parse_value(value, "m³/s"), do: single_float(value) defp parse_value(value, "mm/h"), do: single_float(value) defp parse_value(value, "#/cm2/h"), do: single_float(value) defp parse_value("-", _unit), do: nil defp parse_value(value, unit) when unit in ["m/s", "knop", "s"], do: directional_float(value) defp parse_value(value, "cm"), do: single_float(value) defp parse_value(value, unit) when unit in ["‰", "%", "kg/m³", "°C", "mbar"], do: single_float(value) defp parse_value(">" <> value, "m") do {:less_than, String.to_integer(value)} end defp parse_value(value, "m"), do: directional_float(value) defp parse_type("wind", _unit), do: :wind defp parse_type("level", _unit), do: :water_level defp parse_type("watertemp", _unit), do: :water_temperature defp parse_type("stream", _unit), do: :stream defp parse_type("water", "‰"), do: :salinity defp parse_type("water", "kg/m³"), do: :water_density defp parse_type("water", "m³/s"), do: :water_flow defp parse_type("pressure", "mbar"), do: :air_pressure defp parse_type("air", "%"), do: :humidity defp parse_type("airtemp", "°C"), do: :temperature defp parse_type("sight", "m"), do: :sight defp parse_type("wave", "m"), do: :wave_height defp parse_type("wave", "s"), do: :wave_period defp parse_type("rain", "mm/h"), do: :rain defp parse_type("rain", "#/cm2/h"), do: :hail_intensity end