From 246cb43244ca7045717de6626a9104806e15ae5a Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 31 Jan 2023 13:08:17 -0600 Subject: [PATCH] initial packets working --- assets/js/app.js | 34 +++++++-------- lib/aprs/accounts/user.ex | 8 ++-- lib/aprs/accounts/user_token.ex | 23 ++++++----- lib/aprs/archiver.ex | 33 +++++++++++++++ lib/aprs/data_extended.ex | 38 +++++++++++++++++ lib/aprs/is/is.ex | 3 +- lib/aprs/packet.ex | 41 +++++++++++++++++++ lib/aprs/schema.ex | 9 ++++ lib/aprs_web/live/packets_live/index.ex | 13 ++++++ .../live/packets_live/index.html.heex | 8 +++- lib/parser.ex | 5 ++- mix.exs | 2 + mix.lock | 2 + ...0230129191140_create_users_auth_tables.exs | 22 +++++----- .../20230131171908_create_packets.exs | 19 +++++++++ 15 files changed, 215 insertions(+), 45 deletions(-) create mode 100644 lib/aprs/archiver.ex create mode 100644 lib/aprs/data_extended.ex create mode 100644 lib/aprs/packet.ex create mode 100644 lib/aprs/schema.ex create mode 100644 priv/repo/migrations/20230131171908_create_packets.exs diff --git a/assets/js/app.js b/assets/js/app.js index bed25d0..2693fd8 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -65,27 +65,27 @@ window.addEventListener("phx:page-loading-stop", (_info) => { // keyboard: true, // }).setView([mapLatitude, mapLongitude], 12); - var map = L.map("map").setView([51.505, -0.09], 13); + // var map = L.map("map").setView([51.505, -0.09], 13); - let resizeMap = () => { - const height = $(window).height(); - const width = $(window).width(); + // let resizeMap = () => { + // const height = $(window).height(); + // const width = $(window).width(); - document.querySelector("#map").height(height).width(width); - map.invalidateSize(); - }; + // document.querySelector("#map").height(height).width(width); + // map.invalidateSize(); + // }; - // $(window) - // .on("resize", () => { - // resizeMap(); - // }) - // .trigger("resize"); + // // $(window) + // // .on("resize", () => { + // // resizeMap(); + // // }) + // // .trigger("resize"); - L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { - maxZoom: 19, - attribution: - '© OpenStreetMap', - }).addTo(map); + // L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { + // maxZoom: 19, + // attribution: + // '© OpenStreetMap', + // }).addTo(map); // let markerGroup = L.markerClusterGroup({ // removeOutsideVisibleBounds: true, diff --git a/lib/aprs/accounts/user.ex b/lib/aprs/accounts/user.ex index d1fbc36..351d62c 100644 --- a/lib/aprs/accounts/user.ex +++ b/lib/aprs/accounts/user.ex @@ -3,10 +3,10 @@ defmodule Aprs.Accounts.User do import Ecto.Changeset schema "users" do - field :email, :string - field :password, :string, virtual: true, redact: true - field :hashed_password, :string, redact: true - field :confirmed_at, :naive_datetime + field(:email, :string) + field(:password, :string, virtual: true, redact: true) + field(:hashed_password, :string, redact: true) + field(:confirmed_at, :naive_datetime) timestamps() end diff --git a/lib/aprs/accounts/user_token.ex b/lib/aprs/accounts/user_token.ex index 124e1a0..12c888d 100644 --- a/lib/aprs/accounts/user_token.ex +++ b/lib/aprs/accounts/user_token.ex @@ -14,10 +14,10 @@ defmodule Aprs.Accounts.UserToken do @session_validity_in_days 60 schema "users_tokens" do - field :token, :binary - field :context, :string - field :sent_to, :string - belongs_to :user, Aprs.Accounts.User + field(:token, :binary) + field(:context, :string) + field(:sent_to, :string) + belongs_to(:user, Aprs.Accounts.User) timestamps(updated_at: false) end @@ -56,10 +56,11 @@ defmodule Aprs.Accounts.UserToken do """ def verify_session_token_query(token) do query = - from token in token_and_context_query(token, "session"), + from(token in token_and_context_query(token, "session"), join: user in assoc(token, :user), where: token.inserted_at > ago(@session_validity_in_days, "day"), select: user + ) {:ok, query} end @@ -114,10 +115,11 @@ defmodule Aprs.Accounts.UserToken do days = days_for_context(context) query = - from token in token_and_context_query(hashed_token, context), + from(token in token_and_context_query(hashed_token, context), join: user in assoc(token, :user), where: token.inserted_at > ago(^days, "day") and token.sent_to == user.email, select: user + ) {:ok, query} @@ -149,8 +151,9 @@ defmodule Aprs.Accounts.UserToken do hashed_token = :crypto.hash(@hash_algorithm, decoded_token) query = - from token in token_and_context_query(hashed_token, context), + from(token in token_and_context_query(hashed_token, context), where: token.inserted_at > ago(@change_email_validity_in_days, "day") + ) {:ok, query} @@ -163,17 +166,17 @@ defmodule Aprs.Accounts.UserToken do Returns the token struct for the given token value and context. """ def token_and_context_query(token, context) do - from UserToken, where: [token: ^token, context: ^context] + from(UserToken, where: [token: ^token, context: ^context]) end @doc """ Gets all tokens for the given user for the given contexts. """ def user_and_contexts_query(user, :all) do - from t in UserToken, where: t.user_id == ^user.id + from(t in UserToken, where: t.user_id == ^user.id) end def user_and_contexts_query(user, [_ | _] = contexts) do - from t in UserToken, where: t.user_id == ^user.id and t.context in ^contexts + from(t in UserToken, where: t.user_id == ^user.id and t.context in ^contexts) end end diff --git a/lib/aprs/archiver.ex b/lib/aprs/archiver.ex new file mode 100644 index 0000000..fc970dd --- /dev/null +++ b/lib/aprs/archiver.ex @@ -0,0 +1,33 @@ +defmodule Aprs.Archiver do + use GenServer + require Jason + require Logger + + # alias Aprs.{Packet, Repo} + alias AprsWeb.Endpoint + + @topic "call" + + # API + @spec start_link(any) :: :ignore | {:error, any} | {:ok, pid} + def start_link(_args \\ []) do + GenServer.start_link(__MODULE__, [], name: :archiver) + end + + # Callbacks + + @spec init(any) :: {:ok, any} + def init(state \\ []) do + Process.send_after(self(), :connect, 5000) + Endpoint.subscribe(@topic) + {:ok, state} + end + + def handle_info(:connect, state) do + {:noreply, state} + end + + def handle_info(_msg, state) do + {:noreply, state} + end +end diff --git a/lib/aprs/data_extended.ex b/lib/aprs/data_extended.ex new file mode 100644 index 0000000..f9a9798 --- /dev/null +++ b/lib/aprs/data_extended.ex @@ -0,0 +1,38 @@ +defmodule Aprs.DataExtended do + use Ecto.Schema + import Ecto.Changeset + alias Aprs.DataExtended + + embedded_schema do + field :aprs_messaging, :boolean, default: false + field :comment, :string + field :data_type, :string + field :latitude, :decimal + field :longitude, :decimal + field :symbol_code, :string + field :symbol_table_id, :string + end + + @doc false + def changeset(%DataExtended{} = data_extended, attrs) do + data_extended + |> cast(attrs, [ + :aprs_messaging, + :comment, + :data_type, + :latitude, + :longitude, + :symbol_code, + :symbol_table_id + ]) + |> validate_required([ + :aprs_messaging, + :comment, + :data_type, + :latitude, + :longitude, + :symbol_code, + :symbol_table_id + ]) + end +end diff --git a/lib/aprs/is/is.ex b/lib/aprs/is/is.ex index fb0839c..f3f7d9c 100644 --- a/lib/aprs/is/is.ex +++ b/lib/aprs/is/is.ex @@ -15,7 +15,7 @@ defmodule Aprs.Is do # Get startup parameters server = Application.get_env(:aprs, :aprs_is_server, 'rotate.aprs2.net') - port = Application.get_env(:aprs, :aprs_is_port, 14580) + port = Application.get_env(:aprs, :aprs_is_port, 14_580) default_filter = Application.get_env(:aprs, :aprs_is_default_filter, "r/33/-96/100") aprs_user_id = Application.get_env(:aprs, :aprs_is_login_id, "w5isp") aprs_passcode = Application.get_env(:aprs, :aprs_is_password, "-1") @@ -199,6 +199,7 @@ defmodule Aprs.Is do # Registry.dispatch(Registry.PubSub, "aprs_messages", fn entries -> # for {pid, _} <- entries, do: send(pid, {:broadcast, parsed_message}) # end) + AprsWeb.Endpoint.broadcast("aprs_messages", "packet", parsed_message) # Phoenix.PubSub.broadcast( diff --git a/lib/aprs/packet.ex b/lib/aprs/packet.ex new file mode 100644 index 0000000..3e1f190 --- /dev/null +++ b/lib/aprs/packet.ex @@ -0,0 +1,41 @@ +defmodule Aprs.Packet do + use Aprs.Schema + import Ecto.Changeset + alias Aprs.DataExtended + + schema "packets" do + field(:base_callsign, :string) + field(:data_type, :string) + field(:destination, :string) + field(:information_field, :string) + field(:path, :string) + field(:sender, :string) + field(:ssid, :string) + embeds_one(:data_extended, DataExtended) + + timestamps() + end + + @doc false + def changeset(packet, attrs) do + packet + |> cast(attrs, [ + :base_callsign, + :data_type, + :destination, + :information_field, + :path, + :sender, + :ssid + ]) + |> validate_required([ + :base_callsign, + :data_type, + :destination, + :information_field, + :path, + :sender, + :ssid + ]) + end +end diff --git a/lib/aprs/schema.ex b/lib/aprs/schema.ex new file mode 100644 index 0000000..31b35b5 --- /dev/null +++ b/lib/aprs/schema.ex @@ -0,0 +1,9 @@ +defmodule Aprs.Schema do + defmacro __using__(_) do + quote do + use Ecto.Schema + @primary_key {:id, :binary_id, autogenerate: true} + @foreign_key_type :binary_id + end + end +end diff --git a/lib/aprs_web/live/packets_live/index.ex b/lib/aprs_web/live/packets_live/index.ex index e4b9f09..c84987b 100644 --- a/lib/aprs_web/live/packets_live/index.ex +++ b/lib/aprs_web/live/packets_live/index.ex @@ -1,11 +1,24 @@ defmodule AprsWeb.PacketsLive.Index do use AprsWeb, :live_view + alias AprsWeb.Endpoint @impl true def mount(_params, _session, socket) do + if connected?(socket) do + Endpoint.subscribe("aprs_messages") + end + {:ok, assign(socket, :packets, [])} end + @impl true + def handle_info(%{event: "packet", payload: payload}, socket) do + socket = assign(socket, :packets, [payload | socket.assigns.packets]) + {:noreply, socket} + end + + # AprsWeb.PacketsLive.Index.handle_info(%Phoenix.Socket.Broadcast{topic: "aprs_messages", event: "packet", payload: %{base_callsign: "AE5PL", data_extended: %{aprs_messaging?: false, comment: "RNG0001 70cm Voice 441.1625MHz", data_type: :position, latitude: 33.26733333333333, longitude: -96.53266666666667, symbol_code: "&", symbol_table_id: "D"}, data_type: :position, destination: "APJI43", information_field: "!3316.04ND09631.96W&RNG0001 70cm Voice 441.1625MHz", path: "TCPIP*,qAC,AE5PL-IG", sender: "AE5PL-B", ssid: "B"}}, #Phoenix.LiveView.Socket, router: AprsWeb.Router, assigns: %{__changed__: %{}, flash: %{}, live_action: :index, packets: []}, transport_pid: #PID<0.776.0>, ...>) + # @impl true # def handle_params(params, _url, socket) do # {:noreply, apply_action(socket, socket.assigns.live_action, params)} diff --git a/lib/aprs_web/live/packets_live/index.html.heex b/lib/aprs_web/live/packets_live/index.html.heex index 3fde9ff..e6cfa9b 100644 --- a/lib/aprs_web/live/packets_live/index.html.heex +++ b/lib/aprs_web/live/packets_live/index.html.heex @@ -3,5 +3,11 @@ <.table id="packets" rows={@packets}> - <:col :let={packet} label="packet"><%= packet %> + <:col :let={packet} label="sender"><%= packet.sender %> + <:col :let={packet} label="ssid"><%= packet.ssid %> + <:col :let={packet} label="base_callsign"><%= packet.base_callsign %> + <:col :let={packet} label="data_type"><%= packet.data_type %> + <:col :let={packet} label="destination"><%= packet.destination %> + <:col :let={packet} label="information_field"><%= packet.information_field %> + <:col :let={packet} label="path"><%= packet.path %> diff --git a/lib/parser.ex b/lib/parser.ex index ce11b89..d075ec0 100644 --- a/lib/parser.ex +++ b/lib/parser.ex @@ -3,6 +3,7 @@ defmodule Parser do Main parsing library """ # import Bitwise + alias Aprs.Packet alias Parser.Types.{MicE, Position} require Logger @@ -16,7 +17,9 @@ defmodule Parser do [destination, path] <- String.split(path, ",", parts: 2), data_extended <- parse_data(data_type, destination, data) do {:ok, - %{ + %Packet{ + # TODO: temporary for liveview + id: Ecto.UUID.generate(), sender: sender, path: path, destination: destination, diff --git a/mix.exs b/mix.exs index 6936f2d..f9cfb20 100644 --- a/mix.exs +++ b/mix.exs @@ -37,7 +37,9 @@ defmodule Aprs.MixProject do {:certifi, "~> 2.9"}, {:ecto_sql, "~> 3.6"}, {:finch, "~> 0.13"}, + {:geo, "~> 3.4"}, {:geocalc, "~> 0.8"}, + {:geo_postgis, "~> 3.4"}, {:heroicons, "~> 0.5"}, {:jason, "~> 1.2"}, {:libcluster, "~> 3.3"}, diff --git a/mix.lock b/mix.lock index 410e14d..55c7d5d 100644 --- a/mix.lock +++ b/mix.lock @@ -25,6 +25,8 @@ "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "finch": {:hex, :finch, "0.14.0", "619bfdee18fc135190bf590356c4bf5d5f71f916adb12aec94caa3fa9267a4bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5459acaf18c4fdb47a8c22fb3baff5d8173106217c8e56c5ba0b93e66501a8dd"}, "floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"}, + "geo": {:hex, :geo, "3.4.3", "0ddf3f681993d32c397e5ef346e7b4b6f36f39ed138502429832fa4000ebb9d5", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "e23f2892e5437ec8b063cee1beccec89c58fd841ae11133304700235feb25552"}, + "geo_postgis": {:hex, :geo_postgis, "3.4.2", "5a3462b2a2271d6949ba355ceed0212dc89ecfd6d0073ff1dd8fd53de78af867", [:mix], [{:geo, "~> 3.4", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "48d8c9f97f03805546db19217c42a57e972a3eb69fabaa3d11740285d25aaad4"}, "geocalc": {:hex, :geocalc, "0.8.5", "b9886679e44c323e5b72dcd90a64f834d775d2600af0b656ea9f07ccdacaa5a6", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "3870c25c78513ec0456b69324c2be1af2202961002e81fb659559e3db162c802"}, "gettext": {:hex, :gettext, "0.22.0", "a25d71ec21b1848957d9207b81fd61cb25161688d282d58bdafef74c2270bdc4", [:mix], [{:expo, "~> 0.3.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "cb0675141576f73720c8e49b4f0fd3f2c69f0cd8c218202724d4aebab8c70ace"}, "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, diff --git a/priv/repo/migrations/20230129191140_create_users_auth_tables.exs b/priv/repo/migrations/20230129191140_create_users_auth_tables.exs index 324cafc..a5648f7 100644 --- a/priv/repo/migrations/20230129191140_create_users_auth_tables.exs +++ b/priv/repo/migrations/20230129191140_create_users_auth_tables.exs @@ -2,26 +2,26 @@ defmodule Aprs.Repo.Migrations.CreateUsersAuthTables do use Ecto.Migration def change do - execute "CREATE EXTENSION IF NOT EXISTS citext", "" + execute("CREATE EXTENSION IF NOT EXISTS citext", "") create table(:users) do - add :email, :citext, null: false - add :hashed_password, :string, null: false - add :confirmed_at, :naive_datetime + add(:email, :citext, null: false) + add(:hashed_password, :string, null: false) + add(:confirmed_at, :naive_datetime) timestamps() end - create unique_index(:users, [:email]) + create(unique_index(:users, [:email])) create table(:users_tokens) do - add :user_id, references(:users, on_delete: :delete_all), null: false - add :token, :binary, null: false - add :context, :string, null: false - add :sent_to, :string + add(:user_id, references(:users, on_delete: :delete_all), null: false) + add(:token, :binary, null: false) + add(:context, :string, null: false) + add(:sent_to, :string) timestamps(updated_at: false) end - create index(:users_tokens, [:user_id]) - create unique_index(:users_tokens, [:context, :token]) + create(index(:users_tokens, [:user_id])) + create(unique_index(:users_tokens, [:context, :token])) end end diff --git a/priv/repo/migrations/20230131171908_create_packets.exs b/priv/repo/migrations/20230131171908_create_packets.exs new file mode 100644 index 0000000..d0309ab --- /dev/null +++ b/priv/repo/migrations/20230131171908_create_packets.exs @@ -0,0 +1,19 @@ +defmodule Aprs.Repo.Migrations.CreatePackets do + use Ecto.Migration + + def change do + create table(:packets, primary_key: false) do + add(:id, :binary_id, primary_key: true) + add(:base_callsign, :string) + add(:data_type, :string) + add(:destination, :string) + add(:information_field, :string) + add(:path, :string) + add(:sender, :string) + add(:ssid, :string) + add(:data_extended, :map) + + timestamps() + end + end +end