Files
localiserd/lib/localiser/rssi_buffer.ex
T
2026-04-16 15:46:00 +02:00

73 lines
2.0 KiB
Elixir

defmodule Localiser.RSSI.Buffer do
use GenServer
alias Localiser.Localisation
alias Localiser.Localisation.Tag.Filter, as: TagFilter
alias Localiser.Localisation.Sensor.Server, as: SensorServer
@flush_interval_ms 500
# reading :: %{sensor_id: String.t(), tag_id: String.t(), rssi: integer()}
def push(reading) do
GenServer.cast(__MODULE__, {:push, reading})
end
def start_link(_args) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
@impl true
def init(state) do
schedule_flush()
{:ok, state}
end
@impl true
def handle_cast({:push, %{tag_id: tag_id} = reading}, state) do
{:noreply, Map.update(state, tag_id, [reading], &[reading | &1])}
end
@impl true
def handle_info(:flush, state) do
flush_batches(state)
schedule_flush()
{:noreply, %{}}
end
defp flush_batches(batches) do
Enum.each(batches, fn {tag_id, readings} ->
case Registry.lookup(Localiser.Registry, {:filter, tag_id}) do
[{_pid, _}] ->
measurements = Enum.flat_map(readings, &resolve_measurement/1)
if measurements != [], do: TagFilter.ingest(tag_id, measurements)
[] ->
:ok
end
end)
end
# Resolves a raw RSSI reading to a measurement with sensor location and distance.
# If the sensor is in calibration mode, feeds the reading to Sensor.Server instead
# and returns [] so the sample is excluded from Tag.Filter measurements.
# If the sensor server isn't running, returns [].
defp resolve_measurement(%{sensor_id: sensor_id, rssi: rssi}) do
case Registry.lookup(Localiser.Registry, {:sensor, sensor_id}) do
[{_pid, _}] ->
if SensorServer.calibrating?(sensor_id) do
SensorServer.calibration_reading(sensor_id, rssi)
[]
else
[SensorServer.measure(sensor_id, rssi)]
end
[] ->
[]
end
end
defp schedule_flush do
Process.send_after(self(), :flush, @flush_interval_ms)
end
end