aprs.me/lib/aprsme/packet_producer.ex

56 lines
1.7 KiB
Elixir

defmodule Aprsme.PacketProducer do
@moduledoc """
GenStage producer that handles incoming APRS packets and sends them to consumers
for efficient batch processing.
"""
use GenStage
require Logger
def start_link(opts \\ []) do
GenStage.start_link(__MODULE__, opts, name: __MODULE__)
end
def submit_packet(packet_data) do
GenStage.cast(__MODULE__, {:packet, packet_data})
end
@impl true
def init(opts) do
{:producer, %{demand: 0, buffer: [], max_buffer_size: opts[:max_buffer_size] || 1000}}
end
@impl true
def handle_demand(incoming_demand, %{demand: demand, buffer: buffer} = state) do
{events, remaining_buffer, remaining_demand} = dispatch_events(buffer, demand + incoming_demand)
{:noreply, events, %{state | demand: remaining_demand, buffer: remaining_buffer}}
end
@impl true
def handle_cast({:packet, packet_data}, %{demand: demand, buffer: buffer, max_buffer_size: max_size} = state) do
if demand > 0 do
# We have demand, send immediately
{:noreply, [packet_data], %{state | demand: demand - 1}}
else
# No demand, buffer the packet
new_buffer = [packet_data | buffer]
if length(new_buffer) > max_size do
# Buffer is full, drop oldest packet
Logger.warning("Packet buffer full, dropping oldest packet")
{:noreply, [], %{state | buffer: Enum.take(new_buffer, max_size)}}
else
{:noreply, [], %{state | buffer: new_buffer}}
end
end
end
defp dispatch_events(buffer, demand) when demand > 0 and length(buffer) > 0 do
{events, remaining} = Enum.split(buffer, demand)
{events, remaining, demand - length(events)}
end
defp dispatch_events(buffer, demand) do
{[], buffer, demand}
end
end