194 lines
6.2 KiB
Elixir
194 lines
6.2 KiB
Elixir
defmodule Localiser.Web.Controllers.SensorController do
|
|
use Phoenix.Controller, formats: [:json]
|
|
use OpenApiSpex.ControllerSpecs
|
|
|
|
alias Localiser.Domain.Sensors
|
|
alias Localiser.Localisation.Sensor.Server, as: SensorServer
|
|
alias Localiser.Web.Schemas
|
|
|
|
tags ["Sensors"]
|
|
security [%{"bearerAuth" => []}]
|
|
|
|
operation :index,
|
|
summary: "List all sensors",
|
|
responses: [
|
|
ok: {"Sensor list", "application/json", %OpenApiSpex.Schema{type: :array, items: Schemas.Sensor}},
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error}
|
|
]
|
|
|
|
operation :unplaced,
|
|
summary: "List sensors not yet assigned to a room",
|
|
responses: [
|
|
ok: {"Sensor list", "application/json", %OpenApiSpex.Schema{type: :array, items: Schemas.Sensor}},
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error}
|
|
]
|
|
|
|
operation :show,
|
|
summary: "Get a sensor",
|
|
parameters: [id: [in: :path, type: :integer, required: true]],
|
|
responses: [
|
|
ok: {"Sensor", "application/json", Schemas.Sensor},
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error}
|
|
]
|
|
|
|
operation :update,
|
|
summary: "Update sensor metadata",
|
|
parameters: [id: [in: :path, type: :integer, required: true]],
|
|
request_body: {"Sensor params", "application/json", Schemas.SensorUpdateParams},
|
|
responses: [
|
|
ok: {"Updated sensor", "application/json", Schemas.Sensor},
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error},
|
|
unprocessable_entity: {"Validation errors", "application/json", Schemas.ValidationErrors}
|
|
]
|
|
|
|
operation :delete,
|
|
summary: "Delete / unenrol a sensor",
|
|
parameters: [id: [in: :path, type: :integer, required: true]],
|
|
responses: [
|
|
no_content: "Deleted",
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error}
|
|
]
|
|
|
|
operation :place,
|
|
summary: "Place sensor at a position in a room",
|
|
parameters: [id: [in: :path, type: :integer, required: true]],
|
|
request_body: {"Place params", "application/json", Schemas.SensorPlaceParams, required: true},
|
|
responses: [
|
|
ok: {"Updated sensor", "application/json", Schemas.Sensor},
|
|
bad_request: {"Missing params", "application/json", Schemas.Error},
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error},
|
|
unprocessable_entity: {"Validation errors", "application/json", Schemas.ValidationErrors}
|
|
]
|
|
|
|
operation :unplace,
|
|
summary: "Remove sensor from floor layout",
|
|
parameters: [id: [in: :path, type: :integer, required: true]],
|
|
responses: [
|
|
ok: {"Updated sensor", "application/json", Schemas.Sensor},
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error}
|
|
]
|
|
|
|
operation :calibration_start,
|
|
summary: "Begin RSSI calibration",
|
|
parameters: [id: [in: :path, type: :integer, required: true]],
|
|
request_body: {"Calibration params", "application/json", Schemas.CalibrationStartParams, required: true},
|
|
responses: [
|
|
ok: {"Calibration started", "application/json", Schemas.CalibrationStatus},
|
|
bad_request: {"Missing reference_distance", "application/json", Schemas.Error},
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error}
|
|
]
|
|
|
|
operation :calibration_stop,
|
|
summary: "Abort active calibration",
|
|
parameters: [id: [in: :path, type: :integer, required: true]],
|
|
responses: [
|
|
ok: {"Calibration aborted", "application/json", Schemas.CalibrationStatus},
|
|
unauthorized: {"Unauthorized", "application/json", Schemas.Error}
|
|
]
|
|
|
|
def index(conn, _params) do
|
|
json(conn, Enum.map(Sensors.list_sensors(), &render_sensor/1))
|
|
end
|
|
|
|
def unplaced(conn, _params) do
|
|
json(conn, Enum.map(Sensors.list_unplaced(), &render_sensor/1))
|
|
end
|
|
|
|
def show(conn, %{"id" => id}) do
|
|
sensor = Sensors.get_sensor!(id)
|
|
json(conn, render_sensor(sensor))
|
|
end
|
|
|
|
def update(conn, %{"id" => id} = params) do
|
|
sensor = Sensors.get_sensor!(id)
|
|
attrs = Map.drop(params, ["id"])
|
|
|
|
case Sensors.update_sensor(sensor, attrs) do
|
|
{:ok, updated} ->
|
|
json(conn, render_sensor(updated))
|
|
|
|
{:error, changeset} ->
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{errors: format_errors(changeset)})
|
|
end
|
|
end
|
|
|
|
def delete(conn, %{"id" => id}) do
|
|
sensor = Sensors.get_sensor!(id)
|
|
{:ok, _} = Sensors.delete_sensor(sensor)
|
|
send_resp(conn, :no_content, "")
|
|
end
|
|
|
|
def place(conn, %{"id" => id, "room_id" => room_id, "x" => x, "y" => y}) do
|
|
sensor = Sensors.get_sensor!(id)
|
|
|
|
case Sensors.place_sensor(sensor, room_id, {x, y}) do
|
|
{:ok, updated} ->
|
|
json(conn, render_sensor(updated))
|
|
|
|
{:error, changeset} ->
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{errors: format_errors(changeset)})
|
|
end
|
|
end
|
|
|
|
def place(conn, _params) do
|
|
conn
|
|
|> put_status(:bad_request)
|
|
|> json(%{error: "room_id, x, and y are required"})
|
|
end
|
|
|
|
def unplace(conn, %{"id" => id}) do
|
|
sensor = Sensors.get_sensor!(id)
|
|
|
|
case Sensors.remove_from_layout(sensor) do
|
|
{:ok, updated} ->
|
|
json(conn, render_sensor(updated))
|
|
|
|
{:error, changeset} ->
|
|
conn
|
|
|> put_status(:unprocessable_entity)
|
|
|> json(%{errors: format_errors(changeset)})
|
|
end
|
|
end
|
|
|
|
def calibration_start(conn, %{"id" => id, "reference_distance" => ref_dist}) do
|
|
sensor = Sensors.get_sensor!(id)
|
|
:ok = SensorServer.begin_calibration(sensor.sensor_id, ref_dist)
|
|
json(conn, %{status: "calibrating"})
|
|
end
|
|
|
|
def calibration_start(conn, _params) do
|
|
conn
|
|
|> put_status(:bad_request)
|
|
|> json(%{error: "reference_distance is required"})
|
|
end
|
|
|
|
def calibration_stop(conn, %{"id" => id}) do
|
|
sensor = Sensors.get_sensor!(id)
|
|
:ok = SensorServer.abort_calibration(sensor.sensor_id)
|
|
json(conn, %{status: "idle"})
|
|
end
|
|
|
|
defp render_sensor(sensor) do
|
|
%{
|
|
id: sensor.id,
|
|
sensor_id: sensor.sensor_id,
|
|
room_id: sensor.room_id,
|
|
floor_x: sensor.floor_x,
|
|
floor_y: sensor.floor_y,
|
|
rssi_ref: sensor.rssi_ref
|
|
}
|
|
end
|
|
|
|
defp format_errors(changeset) do
|
|
Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
|
|
Enum.reduce(opts, msg, fn {key, val}, acc ->
|
|
String.replace(acc, "%{#{key}}", to_string(val))
|
|
end)
|
|
end)
|
|
end
|
|
end
|