defmodule Localiser.Localisation.Room.Server do use GenServer @pubsub Localiser.PubSub defstruct [:id, :name, :x, :y, :width, :height, occupants: MapSet.new()] def start_link(room) do GenServer.start_link(__MODULE__, room, name: via(room.id)) end def via(room_id) do {:via, Registry, {Localiser.Registry, {:room, room_id}}} end def tag_entered(room_id, tag_id) do GenServer.cast(via(room_id), {:tag_entered, tag_id}) end def tag_left(room_id, tag_id) do GenServer.cast(via(room_id), {:tag_left, tag_id}) end def get_occupants(room_id) do GenServer.call(via(room_id), :get_occupants) end @impl true def init(room) do Phoenix.PubSub.subscribe(@pubsub, "rooms") state = %__MODULE__{ id: room.id, name: room.name, x: room.x || 0.0, y: room.y || 0.0, width: room.width || 0.0, height: room.height || 0.0 } {:ok, state} end @impl true def handle_cast({:tag_entered, tag_id}, state) do new_occupants = MapSet.put(state.occupants, tag_id) if new_occupants != state.occupants do broadcast(state.id, new_occupants) end {:noreply, %{state | occupants: new_occupants}} end def handle_cast({:tag_left, tag_id}, state) do new_occupants = MapSet.delete(state.occupants, tag_id) if new_occupants != state.occupants do broadcast(state.id, new_occupants) end {:noreply, %{state | occupants: new_occupants}} end @impl true def handle_call(:get_occupants, _from, state) do {:reply, state.occupants, state} end @impl true def handle_info({:room_updated, %{id: id} = room}, %{id: id} = state) do {:noreply, %{state | name: room.name, x: room.x || 0.0, y: room.y || 0.0, width: room.width || 0.0, height: room.height || 0.0 }} end def handle_info(_msg, state), do: {:noreply, state} defp broadcast(room_id, occupants) do msg = {:room_occupancy_changed, room_id, occupants} Phoenix.PubSub.broadcast(@pubsub, "room:#{room_id}", msg) Phoenix.PubSub.broadcast(@pubsub, "rooms:occupancy", msg) end end