Skip to content

Commit

Permalink
Handle setting user codes correctly.
Browse files Browse the repository at this point in the history
Fixes SRHUB-139
  • Loading branch information
jfcloutier authored and mattludwigs committed Oct 25, 2019
1 parent bcd7b46 commit 6e822b5
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 29 deletions.
10 changes: 10 additions & 0 deletions lib/grizzly/command/encoding.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ defmodule Grizzly.Command.Encoding do
| [specs]
| {:encode_with, atom}
| {:encode_with, atom, atom}
| {:encode_in_context_with, atom, atom}
| {:range, integer, integer}
| %{required(atom) => specs}
@type spec ::
:byte
| :byte
| :integer
| :positive_integer
| :binary
| :bit
| {sizable, size}
Expand Down Expand Up @@ -78,6 +80,10 @@ defmodule Grizzly.Command.Encoding do
{:ok, value}
end

defp validate_arg(:positive_integer, value, _command) when is_integer(value) and value >= 0 do
{:ok, value}
end

defp validate_arg(:binary, value, _command) when is_binary(value) do
{:ok, value}
end
Expand Down Expand Up @@ -190,6 +196,10 @@ defmodule Grizzly.Command.Encoding do
apply(module, encode_method, [value])
end

defp validate_arg({:encode_in_context_with, module, encode_method}, value, command) do
apply(module, encode_method, [value, Map.from_struct(command)])
end

defp validate_arg(_spec, value, _command) do
{:error, :invalid_arg, value}
end
Expand Down
29 changes: 15 additions & 14 deletions lib/grizzly/command_class/user_code.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Grizzly.CommandClass.UserCode do
@type slot_id :: pos_integer
@type slot_status :: :occupied | :available
@type user_code :: [0..9]
@type user_code :: String.t()

@doc """
The default empty code to be sent.
Expand All @@ -14,28 +14,29 @@ defmodule Grizzly.CommandClass.UserCode do
[0, 0, 0, 0, 0, 0, 0, 0]
end

@spec encode_status(slot_status) :: {:ok, 0x01 | 0x00} | {:error, :invalid_arg, any()}
def encode_status(:occupied), do: {:ok, 0x01}
def encode_status(:available), do: {:ok, 0x00}
@spec encode_status(slot_status, map) :: {:ok, 0x01 | 0x00} | {:error, :invalid_arg, any()}
def encode_status(:occupied, %{slot_id: 0}), do: {:error, :invalid_arg, :occupied}
def encode_status(:occupied, _), do: {:ok, 0x01}
def encode_status(:available, _), do: {:ok, 0x00}
def encode_status(other), do: {:error, :invalid_arg, other}

@spec decode_slot_status(0x01 | 0x00) :: slot_status
def decode_slot_status(0x01), do: :occupied
def decode_slot_status(0x00), do: :available

@spec encode_user_code(user_code) :: {:ok, [0x30..0x39]} | {:error, :invalid_arg, any()}
def encode_user_code(user_code) do
if Enum.all?(user_code, &(&1 in 0..9)) do
encoded =
user_code
|> Enum.map(&digit_to_acsii/1)
@spec encode_user_code(user_code, map) :: {:ok, [0x30..0x39]} | {:error, :invalid_arg, any()}
def encode_user_code(_user_code, %{slot_id: 0}), do: {:ok, :erlang.list_to_binary(empty_code())}

{:ok, encoded}
def encode_user_code(_user_code, %{slot_status: :available}),
do: {:ok, :erlang.list_to_binary(empty_code())}

def encode_user_code(user_code, _) do
ascii_digits = :erlang.binary_to_list(user_code)

if Enum.all?(ascii_digits, &(&1 in 0x30..0x39)) do
{:ok, :erlang.list_to_binary(ascii_digits)}
else
{:error, :invalid_arg, user_code}
end
end

defp digit_to_acsii(0), do: 0x30
defp digit_to_acsii(n) when n in 1..9, do: 0x30 + n
end
2 changes: 1 addition & 1 deletion lib/grizzly/command_class/user_code/get.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defmodule Grizzly.CommandClass.UserCode.Get do
def encode(%__MODULE__{seq_number: seq_number, slot_id: slot_id} = command) do
with {:ok, _encoded} <-
Encoding.encode_and_validate_args(command, %{
slot_id: :byte
slot_id: {:range, 1, 255}
}) do
binary = Packet.header(seq_number) <> <<0x63, 0x02, slot_id>>

Expand Down
14 changes: 8 additions & 6 deletions lib/grizzly/command_class/user_code/set.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,26 @@ defmodule Grizzly.CommandClass.UserCode.Set do
| {:seq_number, Grizzly.seq_number()}
| {:retries, non_neg_integer()}

defstruct slot_id: nil, slot_status: nil, user_code: [], seq_number: nil, retries: 2
defstruct slot_id: nil, slot_status: nil, user_code: nil, seq_number: nil, retries: 2

@spec init([opt]) :: {:ok, t}
def init(opts) do
{:ok, struct(__MODULE__, opts)}
end

@spec encode(t) :: {:ok, binary} | {:error, EncodeError.t()}
def encode(%__MODULE__{user_code: _user_code, slot_status: _slot_status} = command) do
def encode(
%__MODULE__{slot_id: _slot_id, user_code: _user_code, slot_status: _slot_status} = command
) do
with {:ok, encoded} <-
Encoding.encode_and_validate_args(command, %{
user_code: {:encode_with, UserCode, :encode_user_code},
slot_status: {:encode_with, UserCode, :encode_status}
slot_id: :positive_integer,
user_code: {:encode_in_context_with, UserCode, :encode_user_code},
slot_status: {:encode_in_context_with, UserCode, :encode_status}
}) do
binary =
Packet.header(command.seq_number) <>
<<0x63, 0x01, command.slot_id, encoded.slot_status>> <>
:erlang.list_to_binary(encoded.user_code)
<<0x63, 0x01, command.slot_id, encoded.slot_status>> <> encoded.user_code

{:ok, binary}
end
Expand Down
4 changes: 2 additions & 2 deletions test/grizzly/command_class/user_code/get_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ defmodule Grizzly.CommandClass.UserCode.Get.Test do
end

test "encodes incorrectly" do
{:ok, command} = Get.init(seq_number: 10, slot_id: :blue)
{:ok, command} = Get.init(seq_number: 10, slot_id: 0)

error = EncodeError.new({:invalid_argument_value, :slot_id, :blue, Get})
error = EncodeError.new({:invalid_argument_value, :slot_id, 0, Get})

assert {:error, error} == Get.encode(command)
end
Expand Down
6 changes: 3 additions & 3 deletions test/grizzly/command_class/user_code/set_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule Grizzly.CommandClass.UserCode.Set.Test do
Set.init(
slot_id: 1,
slot_status: :occupied,
user_code: [1, 2, 3, 4],
user_code: "1234",
seq_number: 10
)

Expand All @@ -30,11 +30,11 @@ defmodule Grizzly.CommandClass.UserCode.Set.Test do
Set.init(
slot_id: 1,
slot_status: :occupied,
user_code: [1, 2, 3, 4, :blue],
user_code: "1234Z",
seq_number: 10
)

error = EncodeError.new({:invalid_argument_value, :user_code, [1, 2, 3, 4, :blue], Set})
error = EncodeError.new({:invalid_argument_value, :user_code, "1234Z", Set})

assert {:error, error} == Set.encode(command)
end
Expand Down
8 changes: 5 additions & 3 deletions test/grizzly/command_class/user_codes_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ defmodule Grizzly.CommandClass.UserCode.Test do
end

test "occupied slot status to hex" do
assert {:ok, 0x01} == UserCode.encode_status(:occupied)
assert {:ok, 0x01} == UserCode.encode_status(:occupied, %{slot_id: 1})
assert {:error, :invalid_arg, :occupied} == UserCode.encode_status(:occupied, %{slot_id: 0})
end

test "available slot status to hex" do
assert {:ok, 0x00} == UserCode.encode_status(:available)
assert {:ok, 0x00} == UserCode.encode_status(:available, %{slot_id: 1})
end

test "hex to occupied slot status" do
Expand All @@ -24,6 +25,7 @@ defmodule Grizzly.CommandClass.UserCode.Test do
end

test "encodes a user code" do
assert {:ok, [0x31, 0x34, 0x36, 0x39]} == UserCode.encode_user_code([1, 4, 6, 9])
assert {:ok, "1469"} == UserCode.encode_user_code("1469", %{slot_id: 1})
assert {:ok, <<0, 0, 0, 0, 0, 0, 0, 0>>} == UserCode.encode_user_code("1469", %{slot_id: 0})
end
end

0 comments on commit 6e822b5

Please sign in to comment.