defmodule XMAVLink.SerialConnection do @moduledoc """ XMAVLink.Router delegate for Serial connections """ @smallest_mavlink_message 8 require Logger alias XMAVLink.Frame alias Circuits.UART import XMAVLink.Frame, only: [binary_to_frame_and_tail: 1, validate_and_unpack: 2] defstruct port: nil, baud: nil, uart: nil, buffer: <<>> @type t :: %XMAVLink.SerialConnection{ port: binary, baud: non_neg_integer, uart: pid, buffer: binary } def handle_info( {:circuits_uart, port, raw}, receiving_connection = %XMAVLink.SerialConnection{buffer: buffer}, dialect ) do case binary_to_frame_and_tail(buffer <> raw) do :not_a_frame -> # Noise or malformed frame if byte_size(buffer) + byte_size(raw) > 0 do :ok = Logger.debug("SerialConnection.handle_info: Not a frame: #{inspect(buffer <> raw)}") end {:error, :not_a_frame, port, struct(receiving_connection, buffer: <<>>)} {nil, rest} -> {:error, :incomplete_frame, port, struct(receiving_connection, buffer: rest)} {received_frame, rest} -> # Rest could include a complete message, return later to try emptying the buffer if byte_size(rest) >= @smallest_mavlink_message, do: send(self(), {:circuits_uart, port, <<>>}) case validate_and_unpack(received_frame, dialect) do {:ok, valid_frame} -> {:ok, port, struct(receiving_connection, buffer: rest), valid_frame} :unknown_message -> # We re-broadcast valid frames with unknown messages :ok = Logger.debug("rebroadcasting unknown message with id #{received_frame.message_id}}") {:ok, port, struct(receiving_connection, buffer: rest), struct(received_frame, target: :broadcast)} reason -> :ok = Logger.debug( "SerialConnection.handle_info: frame received failed: #{Atom.to_string(reason)}" ) {:error, reason, port, struct(receiving_connection, buffer: rest)} end end end def connect(["serial", port, baud, uart], controlling_process) do if Map.has_key?(UART.enumerate(), port) do case UART.open(uart, port, speed: baud, active: true) do :ok -> :ok = Logger.info("Opened serial port #{port} at #{baud} baud") send( controlling_process, { :add_connection, port, struct( XMAVLink.SerialConnection, port: port, baud: baud, uart: uart ) } ) UART.controlling_process(uart, controlling_process) {:error, _} -> :ok = Logger.warning("Could not open serial port #{port}. Retrying in 1 second") :timer.sleep(1000) connect(["serial", port, baud, uart], controlling_process) end else :ok = Logger.warning("Serial port #{port} not attached. Retrying in 1 second") :timer.sleep(1000) connect(["serial", port, baud, uart], controlling_process) end end def forward( %XMAVLink.SerialConnection{uart: uart}, %Frame{version: 1, mavlink_1_raw: packet} ) do UART.write(uart, packet) end def forward( %XMAVLink.SerialConnection{uart: uart}, %Frame{version: 2, mavlink_2_raw: packet} ) do UART.write(uart, packet) end end