Current section
Files
Jump to
Current section
Files
lib/mavlink/serial_connection.ex
defmodule XMAVLink.SerialConnection do
@moduledoc """
XMAVLink.Router delegate for Serial connections
"""
@smallest_mavlink_message 8
require Logger
alias XMAVLink.ConnectionWorker
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: <<>>,
worker: nil
@type t :: %XMAVLink.SerialConnection{
port: binary,
baud: non_neg_integer,
uart: pid,
buffer: binary,
worker: pid | nil
}
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 open(["serial", port, baud], controlling_process) do
if Map.has_key?(UART.enumerate(), port) do
uart = :poolboy.checkout(XMAVLink.UARTPool)
case UART.open(uart, port, speed: baud, active: true) do
:ok ->
:ok = Logger.info("Opened serial port #{port} at #{baud} baud")
:ok = UART.controlling_process(uart, controlling_process)
{:ok, port,
struct(
XMAVLink.SerialConnection,
port: port,
baud: baud,
uart: uart,
worker: controlling_process
)}
{:error, reason} ->
:poolboy.checkin(XMAVLink.UARTPool, uart)
{:error, {:open_failed, reason}}
end
else
{:error, :not_attached}
end
end
def close(%XMAVLink.SerialConnection{uart: uart}) do
_ = UART.close(uart)
:poolboy.checkin(XMAVLink.UARTPool, uart)
end
def forward(
connection = %XMAVLink.SerialConnection{worker: worker},
frame
)
when is_pid(worker) do
ConnectionWorker.forward(worker, connection, frame)
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