- Add hardware/config fields migration to beacon_monitors table - Update BeaconMonitor schema with provision/config changesets - Add context functions: create_hardware, update_config, list_all_monitors - Remove user-facing monitor creation (browser POST + API POST) - Update settings page: show assigned monitors table with hardware info - Update profile page: show assigned monitors, remove register links - Fix all tests to match new API
111 lines
3.5 KiB
Elixir
111 lines
3.5 KiB
Elixir
defmodule Microwaveprop.BeaconMonitors.BeaconMonitor do
|
|
@moduledoc """
|
|
A beacon monitor is a physical SDR-based hardware unit assigned to a
|
|
user. The unit runs the `propmonitor` client which reports beacon
|
|
reception measurements back to Microwaveprop.
|
|
|
|
Each monitor has a unique random token the client uses to authenticate
|
|
its measurement uploads.
|
|
"""
|
|
|
|
use Ecto.Schema
|
|
|
|
import Ecto.Changeset
|
|
|
|
alias Microwaveprop.Accounts.User
|
|
alias Microwaveprop.Beacons.Beacon
|
|
|
|
@primary_key {:id, :binary_id, autogenerate: true}
|
|
@foreign_key_type :binary_id
|
|
schema "beacon_monitors" do
|
|
# Identity / label
|
|
field :name, :string
|
|
field :token, :string
|
|
field :last_seen_at, :utc_datetime
|
|
|
|
# Hardware identity
|
|
field :hardware_type, :string
|
|
field :hardware_id, :string
|
|
field :firmware_version, :string
|
|
|
|
# Antenna / installation
|
|
field :antenna_type, :string
|
|
field :antenna_gain_dbi, :float
|
|
field :lat, :float
|
|
field :lon, :float
|
|
|
|
# Active configuration (admin + assigned user can change)
|
|
field :config_frequency_hz, :integer
|
|
field :config_integration_s, :integer
|
|
field :config_mode, :string
|
|
|
|
# Relationships
|
|
belongs_to :user, User
|
|
belongs_to :beacon, Beacon
|
|
belongs_to :assigned_by, User
|
|
|
|
timestamps(type: :utc_datetime)
|
|
end
|
|
|
|
@type t :: %__MODULE__{}
|
|
|
|
@doc """
|
|
General changeset — allows basic fields. Context-level functions
|
|
enforce which caller-role can set which fields.
|
|
"""
|
|
@spec changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
|
|
def changeset(monitor, attrs) do
|
|
monitor
|
|
|> cast(attrs, [:name])
|
|
|> validate_required([:name])
|
|
|> validate_length(:name, min: 1, max: 100)
|
|
end
|
|
|
|
@doc """
|
|
Provisioning changeset — used by admins to register new hardware.
|
|
Casts all hardware-identity, antenna, and assignment fields.
|
|
"""
|
|
@spec provision_changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
|
|
def provision_changeset(monitor, attrs) do
|
|
monitor
|
|
|> cast(attrs, [
|
|
:name,
|
|
:hardware_type,
|
|
:hardware_id,
|
|
:firmware_version,
|
|
:antenna_type,
|
|
:antenna_gain_dbi,
|
|
:lat,
|
|
:lon,
|
|
:user_id,
|
|
:assigned_by_id
|
|
])
|
|
|> validate_required([:name])
|
|
|> validate_length(:name, min: 1, max: 100)
|
|
|> validate_length(:hardware_type, max: 100)
|
|
|> validate_length(:hardware_id, max: 255)
|
|
|> validate_length(:firmware_version, max: 50)
|
|
|> validate_length(:antenna_type, max: 100)
|
|
|> validate_number(:antenna_gain_dbi, greater_than_or_equal_to: -20, less_than_or_equal_to: 60)
|
|
|> validate_number(:lat, greater_than_or_equal_to: -90, less_than_or_equal_to: 90)
|
|
|> validate_number(:lon, greater_than_or_equal_to: -180, less_than_or_equal_to: 180)
|
|
|> foreign_key_constraint(:user_id)
|
|
|> foreign_key_constraint(:assigned_by_id)
|
|
end
|
|
|
|
@doc """
|
|
Configuration changeset — used by admins and assigned users to update
|
|
what the monitor is listening for.
|
|
"""
|
|
@spec config_changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
|
|
def config_changeset(monitor, attrs) do
|
|
monitor
|
|
|> cast(attrs, [:name, :beacon_id, :config_frequency_hz, :config_integration_s, :config_mode])
|
|
|> validate_required([:name])
|
|
|> validate_length(:name, min: 1, max: 100)
|
|
|> validate_length(:config_mode, max: 50)
|
|
|> validate_number(:config_frequency_hz, greater_than: 0)
|
|
|> validate_number(:config_integration_s, greater_than_or_equal_to: 5, less_than_or_equal_to: 3600)
|
|
|> foreign_key_constraint(:beacon_id)
|
|
end
|
|
end
|