AMS packets, which carry every ADS command and response: a 32-byte AMS header with the addresses of both ends, the command, flags, the length of the data, an error code and an invoke id, then the data. All numbers are little-endian.
Over TCP, to port 48898, each packet has the 6-byte AMS/TCP header in front: two reserved bytes,
which must be 0, and the length of the packet. Inside Secure ADS's TLS, on port 8016, packets go
without it, as the AMS header holds their length too. encode/2 and decode/3 take :tcp or
:tls for the one or the other.
iex> packet = %ADS.AMS{target: <<5, 36, 69, 176, 1, 1>>, target_port: 851,
...> source: <<10, 0, 0, 7, 1, 1>>, source_port: 32905, command: 4, invoke_id: 1}
iex> frame = ADS.AMS.encode(packet)
iex> byte_size(frame)
38
iex> {:ok, ^packet, ""} = ADS.AMS.decode(frame)A request has state flags 0x0004, an ADS command, and a response 0x0005; the client picks
the invoke id, and the device copies it into its response.
Summary
Types
With the AMS/TCP header in front, as over TCP, or without, as inside Secure ADS.
Functions
The first packet in what has come over a connection so far
A packet as bytes to send.
The state flags of a request, 0x0004.
Whether a packet is a response, by the lowest bit of its state flags.
The state flags of a response, 0x0005.
Types
@type framing() :: :tcp | :tls
With the AMS/TCP header in front, as over TCP, or without, as inside Secure ADS.
@type t() :: %ADS.AMS{ command: 0..65535, data: binary(), error_code: 0..4_294_967_295, invoke_id: 0..4_294_967_295, source: ADS.NetId.t(), source_port: ADS.ams_port(), state_flags: 0..65535, target: ADS.NetId.t(), target_port: ADS.ams_port() }
Functions
@spec decode(binary(), framing(), non_neg_integer()) :: {:ok, t(), binary()} | {:discard, binary()} | :more | {:error, :invalid_length}
The first packet in what has come over a connection so far:
{:ok, packet, rest}for a whole packet:morefor the start of one{:discard, rest}for a frame that isn't an AMS packet, with reserved bytes other than 0 in its AMS/TCP header, or a length that doesn't agree with its AMS header's{:error, :invalid_length}for one longer thanmax_databytes of data allows, or too short for an AMS header, after which there's no telling where the next starts
The start of a packet, and one too short:
iex> ADS.AMS.decode(<<0, 0, 32, 0, 0, 0, 1, 2, 3>>)
:more
iex> ADS.AMS.decode(<<0, 0, 12, 0, 0, 0>> <> <<0::96>>)
{:error, :invalid_length}
A packet as bytes to send.
@spec request_flags() :: 4
The state flags of a request, 0x0004.
Whether a packet is a response, by the lowest bit of its state flags.
@spec response_flags() :: 5
The state flags of a response, 0x0005.