Skip to content

Commit

Permalink
Support NoOperation command class
Browse files Browse the repository at this point in the history
Fixes SRHUB-122
  • Loading branch information
jfcloutier authored and mattludwigs committed Sep 3, 2019
1 parent ac56267 commit b118a6e
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 0 deletions.
76 changes: 76 additions & 0 deletions lib/grizzly/command_class/no_operation/no_operation.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
defmodule Grizzly.CommandClass.NoOperation do
@moduledoc """
Command module to work with the NO_OPERATION command class
Command Options:
* `:seq_number` - The sequence number of the Z/IP Packet
* `:retries` - The number of times to try to send the command (default 2)
"""
@behaviour Grizzly.Command

alias Grizzly.Packet

@type t :: %__MODULE__{
seq_number: Grizzly.seq_number(),
retries: non_neg_integer()
}

@type opt ::
{:seq_number, Grizzly.seq_number()}
| {:retries, non_neg_integer()}

defstruct seq_number: nil,
retries: 2

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

@spec encode(t) :: {:ok, binary}
def encode(%__MODULE__{seq_number: seq_number}) do
binary = Packet.header(seq_number) <> <<0x00>>

{:ok, binary}
end

@spec handle_response(t, Packet.t()) ::
{:continue, t} | {:done, {:error, :nack_response}} | {:done, :ok} | {:retry, t}
def handle_response(%__MODULE__{seq_number: seq_number}, %Packet{
seq_number: seq_number,
types: [:ack_response]
}) do
{:done, :ok}
end

def handle_response(%__MODULE__{seq_number: seq_number, retries: 0}, %Packet{
seq_number: seq_number,
types: [:nack_response]
}) do
{:done, {:error, :nack_response}}
end

def handle_response(%__MODULE__{seq_number: seq_number, retries: n} = command, %Packet{
seq_number: seq_number,
types: [:nack_response]
}) do
{:retry, %{command | retries: n - 1}}
end

def handle_response(
%__MODULE__{seq_number: seq_number} = command,
%Packet{
seq_number: seq_number,
types: [:nack_response, :nack_waiting]
} = packet
) do
if Packet.sleeping_delay?(packet) do
{:queued, command}
else
{:continue, command}
end
end

def handle_response(command, _), do: {:continue, command}
end
71 changes: 71 additions & 0 deletions test/grizzly/command_class/no_operation/no_operation_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
defmodule Grizzly.CommandClass.NetworkManagementInstallationMaintenance.NoOperationTest do
use ExUnit.Case, async: true

alias Grizzly.Packet
alias Grizzly.CommandClass.NoOperation

describe "implements Grizzly.Command behaviour" do
test "initializes to the correct command state" do
assert {:ok, %NoOperation{}} = NoOperation.init([])
end

test "encodes correctly" do
{:ok, command} = NoOperation.init(seq_number: 0x08)
binary = <<35, 2, 128, 208, 8, 0, 0, 3, 2, 0, 0x00>>

assert {:ok, binary} == NoOperation.encode(command)
end

test "handles ack response" do
{:ok, command} = NoOperation.init(seq_number: 0x10)
packet = Packet.new(seq_number: 0x10, types: [:ack_response])

assert {:done, :ok} = NoOperation.handle_response(command, packet)
end

test "handles nack response" do
{:ok, command} = NoOperation.init(seq_number: 0x10, retries: 0)
packet = Packet.new(seq_number: 0x10, types: [:nack_response])

assert {:done, {:error, :nack_response}} ==
NoOperation.handle_response(command, packet)
end

test "handles retries" do
{:ok, command} = NoOperation.init(seq_number: 0x10)
packet = Packet.new(seq_number: 0x10, types: [:nack_response])

assert {:retry, _command} = NoOperation.handle_response(command, packet)
end

test "handles queued for wake up nodes" do
{:ok, command} = NoOperation.init(seq_number: 0x01, command_class: :switch_binary)

packet =
Packet.new(seq_number: 0x01, types: [:nack_response, :nack_waiting])
|> Packet.put_expected_delay(5000)

assert {:queued, ^command} = NoOperation.handle_response(command, packet)
end

test "handles nack waiting when delay is 1 or less" do
{:ok, command} = NoOperation.init(seq_number: 0x01)

packet =
Packet.new(seq_number: 0x01, types: [:nack_response, :nack_waiting])
|> Packet.put_expected_delay(1)

assert {:continue, ^command} = NoOperation.handle_response(command, packet)
end

test "handles response" do
{:ok, command} = NoOperation.init([])

assert {:continue, %NoOperation{}} ==
NoOperation.handle_response(
command,
%{command_class: :door_lock, value: :foo, command: :report}
)
end
end
end

0 comments on commit b118a6e

Please sign in to comment.