ADS, Beckhoff's Automation Device Specification, in pure Elixir, as a client: reading, writing and watching the variables of a TwinCAT PLC by name, over TCP or over Secure ADS (TLS).

  • ADS.Client talks to a PLC, or any other ADS device, over one connection, with many requests on the way at once, and reconnects by itself.
  • ADS.Discovery finds PLCs on the network, and adds and deletes routes on them, over UDP; ADS.Client.routes/2 lists them, and mix ads.route does all three from the command line; mix ads.info shows a system's state, host name and network adapters.
  • ADS.Codepage is the code page of the PLC's strings, Windows-1252 unless told otherwise.
  • ADS.Type turns PLC values into Elixir terms and back, from the types the PLC describes or ones given by hand.
  • ADS.Symbol and ADS.DataType are the PLC's own descriptions of its variables and types.
  • ADS.AMS, ADS.Command and ADS.Sum encode and decode, for those who bring their own transport; ADS.Secure and ADS.Certificate are for Secure ADS.

Addresses

An ADS device is reached by the AMS NetId of the system it runs on, six numbers such as "5.36.69.176.1.1", and its AMS port on that system:

PortDevice
851, 852, ...the PLC runtimes of TwinCAT 3
801, 811, ...the PLC runtimes of TwinCAT 2
10000the system service
300I/O
500NC

A NetId has nothing to do with an IP address, though it's often made from one by adding .1.1. Inside a device, data is addressed by an index group and an index offset, both 32 bits, or by the name of a variable, such as "MAIN.nCounter".

States

A device is in one of the ADS states, an atom here: :invalid, :idle, :reset, :init, :start, :run, :stop, :save_config, :load_config, :power_failure, :power_good, :error, :shutdown, :suspend, :resume, :config and :reconfig; or the number of a state that has no name.

Errors

  • {:error, name}: the device or a router answered with an ADS return code, such as :device_symbol_not_found (see ADS.ReturnCode), or its number if it has no name
  • {:error, :timeout}: no answer in time
  • {:error, :closed}: there's no connection, or it was lost before the answer came
  • {:error, {:invalid_response, data}}: the answer doesn't fit the request

A request that doesn't fit the protocol, or a value that doesn't fit its type, raises ArgumentError in the caller and is never sent.

Summary

Types

An AMS port, such as 851 for the first PLC runtime of TwinCAT 3.

An AMS NetId, such as "5.36.69.176.1.1".

Functions

The number of an ADS state, given by name or number.

The name of an ADS state, or its number if it has none.

Types

ams_port()

@type ams_port() :: 0..65535

An AMS port, such as 851 for the first PLC runtime of TwinCAT 3.

error()

@type error() ::
  ADS.ReturnCode.name()
  | non_neg_integer()
  | :timeout
  | :closed
  | {:invalid_response, binary()}

index_group()

@type index_group() :: 0..4_294_967_295

index_offset()

@type index_offset() :: 0..4_294_967_295

net_id()

@type net_id() :: String.t()

An AMS NetId, such as "5.36.69.176.1.1".

state()

@type state() ::
  :invalid
  | :idle
  | :reset
  | :init
  | :start
  | :run
  | :stop
  | :save_config
  | :load_config
  | :power_failure
  | :power_good
  | :error
  | :shutdown
  | :suspend
  | :resume
  | :config
  | :reconfig
  | 0..65535

Functions

state_code(code)

@spec state_code(state()) :: 0..65535

The number of an ADS state, given by name or number.

iex> ADS.state_code(:stop)
6

state_name(code)

@spec state_name(0..65535) :: state()

The name of an ADS state, or its number if it has none.

iex> ADS.state_name(5)
:run
iex> ADS.state_name(99)
99