Current section
Files
Jump to
Current section
Files
lib/ethercat/domain/layout.ex
defmodule EtherCAT.Domain.Layout.CyclePlan do
@moduledoc false
@type output_patch :: {non_neg_integer(), pos_integer(), {atom(), term()}}
@type input_slice :: {non_neg_integer(), pos_integer(), {atom(), term()}}
@type t :: %__MODULE__{
image_size: non_neg_integer(),
output_patches: [output_patch()],
input_slices: [input_slice()],
expected_wkc: non_neg_integer()
}
@enforce_keys [:image_size, :output_patches, :input_slices, :expected_wkc]
defstruct [:image_size, :output_patches, :input_slices, :expected_wkc]
end
defmodule EtherCAT.Domain.Layout do
@moduledoc false
alias EtherCAT.Domain.Layout.CyclePlan
@type pdo_key :: {atom(), term()}
@type output_patch :: CyclePlan.output_patch()
@type input_slice :: CyclePlan.input_slice()
@type t :: %__MODULE__{
image_size: non_neg_integer(),
output_patches_rev: [output_patch()],
input_slices_rev: [input_slice()],
output_slave_names: MapSet.t(atom()),
input_slave_names: MapSet.t(atom())
}
# One LRW datagram must fit within the 2047-byte EtherCAT payload limit.
# LRW data consumes 10 bytes of datagram header and 2 bytes of WKC.
@max_lrw_image_bytes 2035
defstruct image_size: 0,
output_patches_rev: [],
input_slices_rev: [],
output_slave_names: MapSet.new(),
input_slave_names: MapSet.new()
@spec new() :: t()
def new, do: %__MODULE__{}
@spec register(t(), pdo_key(), pos_integer(), :input | :output) ::
{non_neg_integer(), t()}
def register(%__MODULE__{} = layout, {slave_name, _pdo_name} = key, size, :output) do
offset = layout.image_size
{offset,
%{
layout
| image_size: offset + size,
output_patches_rev: [{offset, size, key} | layout.output_patches_rev],
output_slave_names: MapSet.put(layout.output_slave_names, slave_name)
}}
end
def register(%__MODULE__{} = layout, {slave_name, _pdo_name} = key, size, :input) do
offset = layout.image_size
{offset,
%{
layout
| image_size: offset + size,
input_slices_rev: [{offset, size, key} | layout.input_slices_rev],
input_slave_names: MapSet.put(layout.input_slave_names, slave_name)
}}
end
@spec image_size(t()) :: non_neg_integer()
def image_size(%__MODULE__{image_size: image_size}), do: image_size
@spec expected_wkc(t()) :: non_neg_integer()
def expected_wkc(%__MODULE__{} = layout) do
MapSet.size(layout.output_slave_names) * 2 + MapSet.size(layout.input_slave_names)
end
@spec prepare(t()) ::
{:ok, CyclePlan.t()}
| {:error, :nothing_registered | {:image_too_large, integer(), integer()}}
def prepare(%__MODULE__{image_size: 0}), do: {:error, :nothing_registered}
def prepare(%__MODULE__{image_size: image_size}) when image_size > @max_lrw_image_bytes do
{:error, {:image_too_large, image_size, @max_lrw_image_bytes}}
end
def prepare(%__MODULE__{} = layout) do
{:ok,
%CyclePlan{
image_size: layout.image_size,
output_patches: Enum.reverse(layout.output_patches_rev),
input_slices: Enum.reverse(layout.input_slices_rev),
expected_wkc: expected_wkc(layout)
}}
end
end