Skip to content

Python API

Generated from the docstrings of the ciplink package. The other languages are described in Other languages.

Drivers

ciplink.LogixDriver

Bases: CIPDriver

An Ethernet/IP Client driver for reading and writing tags in ControlLogix and CompactLogix PLCs.

The options and behaviour are those of the reference ciplink driver (see its documentation): init_tags, init_program_tags, tag_namespace_filter, lazy_tags, tag_cache_file, socket_timeout and connection_size. To share the tag definitions between connections to the same controller, open the second one with init_tags=False and set plc2._tags = plc1.tags.

tags (all the tag definitions uploaded from the controller) and data_types (all the data type definitions; the entry of an AOI type has an 'aoi' entry once probed: {'whole_read': bool, 'readable': [...], 'refused': [...]}) are read-only attributes.

tags_json property

Read-only property to access all the tag definitions uploaded from the controller. Filters out any non-JSON serializable objects. A new copy on each call; within it each data type is copied once and shared by the tags and members of that type, as in :attr:tags.

info property

Property containing a dict of all the information collected about the connected PLC: vendor, product_type, product_code, revision, serial, product_name, status, keyswitch, name and, after a tag list upload, programs, tasks and modules.

revision_major property

Returns the major revision for the PLC or 0 if not available

name property

Returns:

Type Description
Optional[str]

name of PLC program

get_plc_name()

Requests the name of the program running in the PLC.

Returns:

Type Description
str

the controller program name

get_plc_info()

Reads basic information from the controller, returns it and stores it in the info property.

get_plc_time(fmt='%A, %B %d, %Y %I:%M:%S%p', tz=None)

Gets the current time of the PLC system clock. The value attribute will be a dict containing the time in 3 different forms, datetime is a Python datetime.datetime object, microseconds is the integer value epoch time, and string is the datetime formatted using strftime and the fmt parameter. The time is in the client PC's local timezone unless tz is given (pass datetime.timezone.utc for UTC); microseconds is always UTC epoch time and string uses the same timezone as datetime.

Parameters:

Name Type Description Default
fmt str

format string for converting the time to a string

'%A, %B %d, %Y %I:%M:%S%p'
tz timezone

timezone for datetime/string, the client PC's local timezone if omitted or None

None

Returns:

Type Description
Tag

a Tag object with the current time

set_plc_time(microseconds=None)

Set the time of the PLC system clock.

Parameters:

Name Type Description Default
microseconds Optional[int]

None to use client PC clock, else timestamp in microseconds to set the PLC clock to. Timestamp is uS since epoch 1970-1-1, 00:00 UTC

None

Returns:

Type Description
Tag

Tag with status of request

get_tag_list(program=None, cache=True, tag_namespace_filter='')

Reads the tag list from the controller and the definition for each tag.

Parameters:

Name Type Description Default
program str

scope to retrieve tag list, None for controller-only tags, '*' for all tags, else name of program

None
cache bool

store the retrieved list in the :attr:.tags property

True
tag_namespace_filter str

prefix match on the tag name (without the Program:X. part)

''

Returns:

Type Description
List[dict]

a list containing dicts for each tag definition collected

check_tag_table()

Loads the tag definitions again if the controller's tag table changed since they were loaded, e.g. an online edit added, renamed or deleted a tag. :meth:.read and :meth:.write check this on their own.

Returns:

Type Description
bool

True if the tag definitions were loaded again

get_tag_info(tag_name)

Returns the tag information for a tag collected during the tag list upload. Can be a base tag or an attribute.

With lazy_tags enabled, an uncached definition is resolved from the controller.

Parameters:

Name Type Description Default
tag_name str

name of tag to get info for

required

Returns:

Type Description
Optional[dict]

a dict of the tag's definition

read(*tags)

Read the value of tag(s). Automatically will split tags into multiple requests by tracking the request and response size. Will use the multi-service request to group many tags into a single packet and also will automatically use fragmented read requests if the response size will not fit in a single packet. Supports arrays (specify element count in using curly braces (array{10}). Also supports full structure reading (when possible), return value will be a dict of {attribute name: value}.

Parameters:

Name Type Description Default
tags str

one or many tags to read

()

Returns:

Type Description
ReadWriteReturnType

a single or list of Tag objects

write(*tags_values)

Write to tag(s). Supports arrays (specify element count in using curly braces (array{10}) and full structure writing (a sequence of values or a dict of {attribute: value} matching the structure of the destination tag).

Parameters:

Name Type Description Default
tags_values Union[str, TagValueType, Tuple[str, TagValueType]]

a tag name and value as two arguments, write('tag', value), or one or more (tag, value) tuples, write(('tag1', 1), ('tag2', 2)). To write a list of pairs, unpack it: write(*pairs).

()

Returns:

Type Description
ReadWriteReturnType

a single or list of Tag objects.

trend(*tags, period_ms=10, capacity=4096, duration=None)

Starts a controller-side trend: the controller samples tags every period_ms into its trend buffer and the trend uploads them on a connection of its own, see :mod:ciplink.trend.

Parameters:

Name Type Description Default
tags str

atomic tags, members or array elements of up to 32 bits, or bits (Tag.3)

()
period_ms float

sample period, 1 ms or more (whole microseconds)

10
capacity int

samples kept until :meth:Trend.read, the oldest are dropped when full

4096
duration Optional[float]

stop after this many seconds, None to run until closed

None

Returns:

Type Description
Trend

the running :class:Trend, close it (or use it as a context manager) to stop it

authenticate_metadata(credentials=None)

Authenticate the current CIP connection for experimental metadata reads.

Uses the bundled or caller-supplied credentials. Authentication applies to this connection only; call again after a reconnect. No controller values or persistent permissions are changed.

get_tag_description(tag_name, language=127, encoding='utf-8')

Read a tag, member, or array element's Description.

get_tag_descriptions(*tag_names, language=127, encoding='utf-8')

Read the Descriptions of tags, members, or array elements, a list of Tags in the order of tag_names, same results as get_tag_description of each but many tags share each multi-service packet.

ciplink.SLCDriver

Bases: CIPDriver

An Ethernet/IP Client driver for reading and writing of data files in SLC or MicroLogix PLCs.

read(*addresses)

Reads data file addresses. To read multiple words add the word count to the address using curly braces, e.g. N120:10{10}.

Does not track request/response size like the CLXDriver.

Parameters:

Name Type Description Default
addresses str

one or many data file addresses to read

()

Returns:

Type Description
ReadWriteReturnType

a single or list of Tag objects

write(*address_values)

Write values to data file addresses. To write to multiple words in a file use curly braces in the address to indicate the number of words, then set the value to a list of values to write e.g. ('N120:10{10}', [1, 2, ...]).

Does not track request/response size like the CLXDriver.

Parameters:

Name Type Description Default
address_values Tuple[str, TagValueType]

one or many 2-element tuples of (address, value)

()

Returns:

Type Description
ReadWriteReturnType

a single or list of Tag objects

get_processor_type()

the processor's catalog number, e.g. '1747-L552', None if the processor reported an error

get_datalog_queue(num_data_logs, queue_num)

reads num_data_logs entries of data log queue queue_num, then one more to clear the queue

get_file_directory()

the data files of the processor, {'N7': {'elements': 100, 'length': 200}, ...}

ciplink.CIPDriver

A base CIP driver for the SLCDriver and LogixDriver classes. Implements common CIP services like (un)registering sessions, forward open/close, generic messaging, etc.

connected property

Read-Only Property to check whether or not a connection is open.

Returns:

Type Description
bool

True if a connection is open, False otherwise

connection_size property writable

Maximum connected message size; configure before a Forward Open.

socket_timeout property writable

Socket open connection timeout, in seconds

list_identity(path) classmethod

Uses the ListIdentity service to identify the target

Returns:

Type Description
Optional[Dict[str, Any]]

device identity if reply contains valid response else {}

discover(broadcast_address='255.255.255.255') classmethod

Discovers available devices on the current network(s). Returns a list of the discovered devices Identity Object (as dict).

get_module_info(slot)

Get the Identity object for a given slot in the rack of the current connection

get_identity(route='')

Get the Identity object of the device at route.

Routes are relative to the device at the driver's IP address and ignore its connection path: "" is that device, "bp/3" (or "1/3") the module in slot 3 of its backplane, "bp/2/enet/192.168.1.10" a device behind the module in slot 2.

get_app(route='')

What the ciplink device at route runs (its ciplink object), None for any other device: name, version, detail, summary (the line a device browser shows, e.g. "Station · station 2 · owned by 10.0.0.5, RPI 20 ms"), ciplink_version, assemblies and connections. :meth:browse_port reads it for every device it finds at an IP address (node["app"]).

get_ports(route='')

Get the ports of the device at route (its Port Object, class 0xF4), like the networks RSWho shows under a device. Each port is a dict: number (the port of a route through it), type, type_name (e.g. "Backplane", "EtherNet/IP"), name, address (this device's own slot, node or IP address on it, or None), ip and netmask (of an EtherNet/IP port, else None) and nodes (the addresses :meth:browse_port probes, as strings: the slots or nodes, or on an EtherNet/IP port every host of its subnet, at most the /24 around its address; None if not known). Empty if the device has no Port Object.

browse_port(route, port, nodes=None, workers=16)

Probe every slot, node or IP address on a port of the device at route for its identity, like expanding a backplane or network in RSWho.

An empty slot or missing device costs a reply from the bridge, which may wait for its Unconnected Send timeout (about 4 s), so workers connections of their own to the device probe at once.

Parameters:

Name Type Description Default
port

a port dict from :meth:get_ports, or a port number

required
nodes Optional[Sequence]

the addresses to probe, by default the port's nodes

None

Returns:

Type Description
List[Dict[str, Any]]

a dict per address, in their order: route (to browse it further), address, identity (as :meth:get_identity, None if nothing answered), error and app (as :meth:get_app, only for a ciplink device at an IP address, else None)

iter_port(route, port, nodes=None, workers=16)

:meth:browse_port, yielding each address as it answers (in any order). Closing the iterator early stops the scan.

open()

Creates a new Ethernet/IP socket connection to target device and registers a CIP session.

Returns:

Type Description

True if successful, False otherwise

close()

Closes the current connection and un-registers the session.

generic_message(service, class_code, instance, attribute=b'', request_data=b'', data_type=None, name='generic', connected=True, unconnected_send=False, route_path=True, **kwargs)

Perform a generic CIP message. Similar to how MSG instructions work in Logix.

Parameters:

Name Type Description Default
service Union[int, bytes]

service code for the request (single byte)

required
class_code Union[int, bytes]

request object class ID

required
instance Union[int, bytes]

ID for an instance of the class If set with 0, request class attributes.

required
attribute Union[int, bytes]

(optional) attribute ID for the service/class/instance

b''
request_data Any

(optional) any additional data required for the request.

b''
data_type Optional[Union[Type[DataType], DataType]]

DataType used to decode the reply data, None for raw bytes. Only the bytes the type needs are decoded, so use an array (INT[n], INT[None]) or a Struct for replies with several values (e.g. an assembly); leave None for services that return no data (Set_Attribute_*)

None
name str

return Tag.tag value, arbitrary but can be used for tracking returned Tags

'generic'
connected bool

True if service required a CIP connection (forward open), False to use UCMM

True
unconnected_send bool

(Unconnected only) wrap the service in an UnconnectedSend

False
route_path Union[bool, Sequence[CIPSegment], bytes, str]

(UnconnectedSend only) route for the UnconnectedSend wrapper: True = current connection path, False/None = none, or a path string, list of segments or encoded bytes. Ignored unless unconnected_send=True.

True

Returns:

Type Description
Tag

a Tag whose value is the (decoded) reply data, b'' when the reply carries none

send(request)

Sends a request packet built by the caller through the core and returns its response packet. Connected packets (SendUnitData) go over the current CIP connection with the driver's next sequence count (the count the packet took is replaced), unconnected ones (SendRRData) over the session.

Results

ciplink.Tag

Bases: NamedTuple

error_kind property

the :class:ErrorKind of error, None if there is no error

ciplink.ErrorKind

Bases: str, Enum

what kind of failure a :attr:Tag.error is, see :attr:Tag.error_kind

ciplink.TagError

Bases: str

A :attr:Tag.error message that also carries its kind (:class:ErrorKind), and the CIP general status and first extended status word ext of a controller error (else None)

ciplink.Trend

A running controller-side trend. Use it as a context manager, or call :meth:close: closing stops the trend, deletes it in the controller and closes its connection. The controller also deletes the trend of a connection that drops, so a trend never outlives its process.

start_time_us property

controller time the trend started, microseconds since 1970 UTC

running property

True until the trend is closed, its duration is over, or it failed

received_samples property

samples uploaded so far, including dropped ones

dropped_samples property

samples dropped because capacity samples were waiting to be read

error property

why the trend failed, None if it has not

read()

Takes the samples uploaded since the last read, oldest first. Once the trend failed and every sample was taken, raises its error.

close()

Stops the trend and waits for its cleanup; raises the trend's error if it failed.

ciplink.TrendSample

Bases: NamedTuple

timestamp property

the controller time of the sample as a UTC datetime

ciplink.TrendPen

Bases: NamedTuple

Adapter

ciplink.adapter.Adapter

A running adapter, until :meth:stop (or the end of a with block).

inputs, outputs and configs map assembly instance -> size in bytes. identity sets the Identity object: vendor_id (0 is a placeholder; a device shipped with an EDS needs its vendor's own ID), device_type, product_code, revision (major, minor), serial and product_name. bind picks the network card (and the one multicast leaves from); port and io_port are 0 for a free port in tests. RPIs are in seconds. The other keywords are those of the Rust AdapterConfig.

address property

(host, port) explicit messages and ListIdentity are served on

io_address property

(host, port) class 1 I/O is received on

set_input(instance, data)

Sets an input assembly's data, sent in the next T->O packets; shorter data leaves the rest zero.

input(instance)

An input assembly's current data

output(instance)

(data, run) of an output assembly: its latest data and the controller's run bit (False before the first packet and after the owner closed or timed out)

config(instance)

A configuration assembly's data, as the last Forward Open with data sent it

next_event(timeout=None)

The next event, waiting up to timeout seconds (None: until one comes or the adapter stops); None if none came. An event is a dict with event:

  • opened, closed, timed_out, with connection (a dict as :meth:connections returns)
  • output with instance: an output assembly's data or run bit changed; sent once until :meth:output reads it
  • config with instance and data: a Forward Open carried configuration data

connections()

The open class 1 connections, each a dict: kind (exclusive_owner, input_only or listen_only), originator (the controller's IP), o_t_rpi and t_o_rpi (seconds), input and input_size, output (None unless an owner) and output_size, multicast, o_t_id and t_o_id.

set_detail(detail)

Sets the application's status text that device browsers show, e.g. "station 2"

stats()

I/O timing counters: produced packets, late ones (1 ms or more) and max_late (seconds)

stop()

Stops the adapter: closes its sockets and joins its threads. Controllers then time out.

Tag server

ciplink.server.TagServer

A tag server running in this process, on threads of its own, until :meth:stop (or the end of a with block). host and port override the config file's; port 0 picks a free port.

address property

(host, port) the server listens on

stop()

Stops the server: pressed buttons are released, the PLC connections closed, the audit log flushed.

Metadata credentials

ciplink.LogixMetadataCredentials

Caller-provided credential and matching PKCS #1 or PKCS #8 DER key.

Keep unwrapped keys out of logs. The credential alone cannot complete the handshake. The optional sealed bundle uses reversible obfuscation. RSA blinding is used because Python integer operations are not constant-time.

bundled() classmethod

Load the generated Python credential bundle without external files.

answer_challenge(data)

Return the 20-byte proof for a successful service 0x4B reply payload.

validate_completion(data)

Require the metadata claim in the controller's granted-claims reply.

Exceptions

PycommError

Bases: Exception

Base exception for all exceptions raised by ciplink

CommError

Bases: PycommError

For exceptions raised during connection related issues

DataError

Bases: PycommError

For exceptions raised during binary encoding/decoding of data

BufferEmptyError

Bases: DataError

Raised when trying to decode an empty buffer

ResponseError

Bases: PycommError

For exceptions raised during handling for responses to requests

RequestError

Bases: PycommError

For exceptions raised due to issues building requests or processing of user supplied data

Data types

The CIP types used to decode generic message replies (data_type=) and to encode request data.

DataType

Base class to represent a CIP data type. Instances of a type are only used when defining the members of a structure.

Each type class provides encode / decode class methods. If overriding them, they must catch any unhandled exception and raise a DataError from it. For decode, BufferEmptyError should be reraised immediately without modification. The buffer empty error is needed for decoding arrays of unknown length. Typically for custom types, overriding the private _encode/_decode methods are sufficient. The private methods do not need to do any exception handling if using the base public methods. For _decode use the private _stream_read method instead of stream.read, so that BufferEmptyError exceptions are raised appropriately.

encode(value) classmethod

Serializes a Python object value to bytes.

.. note:: Any subclass overriding this method must catch any exception and re-raise a DataError

decode(buffer) classmethod

Deserializes a Python object from the buffer of bytes

.. note:: Any subclass overriding this method must catch any exception and re-raise as a DataError. Except BufferEmptyErrors they must be re-raised as such, array decoding relies on this.

ElementaryDataType

Bases: DataType

Type that represents a single primitive value in CIP.

BOOL

Bases: ElementaryDataType

A boolean value, decodes 0x00 and False and True otherwise. True encoded as 0xFF and False as 0x00

SINT

Bases: ElementaryDataType

Signed 8-bit integer

INT

Bases: ElementaryDataType

Signed 16-bit integer

DINT

Bases: ElementaryDataType

Signed 32-bit integer

LINT

Bases: ElementaryDataType

Signed 64-bit integer

USINT

Bases: ElementaryDataType

Unsigned 8-bit integer

UINT

Bases: ElementaryDataType

Unsigned 16-bit integer

UDINT

Bases: ElementaryDataType

Unsigned 32-bit integer

ULINT

Bases: ElementaryDataType

Unsigned 64-bit integer

REAL

Bases: ElementaryDataType

32-bit floating point

LREAL

Bases: ElementaryDataType

64-bit floating point

STIME

Bases: DINT

Synchronous time information

DATE

Bases: UINT

Date information

TIME_OF_DAY

Bases: UDINT

Time of day

DATE_AND_TIME

Bases: ElementaryDataType

Date and time of day

DT

Bases: ULINT

Date and time in microseconds

LDT

Bases: ULINT

Long date and time in nanoseconds

TIME32

Bases: DINT

Duration of time in microseconds

StringDataType

Bases: ElementaryDataType

Base class for any string type

LOGIX_STRING

Bases: StringDataType

Character string, 1-byte per character, 4-byte length

STRING

Bases: StringDataType

Character string, 1-byte per character, 2-byte length

BytesDataType

Bases: ElementaryDataType

Base type for placeholder bytes.

BitArrayType

Bases: ElementaryDataType

Array of bits (Python bools) for host_type integer value

BYTE

Bases: BitArrayType

bit string - 8-bits

WORD

Bases: BitArrayType

bit string - 16-bits

DWORD

Bases: BitArrayType

bit string - 32-bits

LWORD

Bases: BitArrayType

bit string - 64-bits

STRING2

Bases: StringDataType

character string, 2-bytes per character

FTIME

Bases: DINT

duration - high resolution

LTIME

Bases: LINT

duration - long

ITIME

Bases: INT

duration - short

STRINGN

Bases: StringDataType

character string, n-bytes per character

SHORT_STRING

Bases: StringDataType

character string, 1-byte per character, 1-byte length

TIME

Bases: DINT

duration - milliseconds

EPATH

Bases: ElementaryDataType

CIP path segments

ENGUNIT

Bases: WORD

engineering units

STRINGI

Bases: StringDataType

international character string

encode(*strings) classmethod

Encodes strings to bytes

DerivedDataType

Bases: DataType

Base type for types composed of :class:ElementaryDataType

ArrayType

Bases: DerivedDataType

Base type for an array

StructType

Bases: DerivedDataType

Base type for a structure

CIPSegment

Bases: DataType

Base type for a CIP path segment

+----+----+----+---+---+---+---+---+ | Segment Type | Segment Format | +====+====+====+===+===+===+===+===+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+---+---+---+---+---+

encode(segment, padded=False) classmethod

Encodes an instance of a CIPSegment to bytes

decode(buffer) classmethod

.. attention:: Not Implemented

PortSegment

Bases: CIPSegment

Port segment of a CIP path.

+----+----+----+--------------------+----+----+----+----+ | Segment Type | Extended Link Addr | Port Identifier | +====+====+====+====================+====+====+====+====+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+--------------------+----+----+----+----+

LogicalSegment

Bases: CIPSegment

Logical segment of a CIP path

+----+----+----+----+----+----+-------+--------+ | Segment Type | Logical Type | Logical Format | +====+====+====+====+====+====+=======+========+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+-------+--------+

DataSegment

Bases: CIPSegment

+----+----+----+---+---+---+---+---+ | Segment Type | Segment Sub-Type | +====+====+====+===+===+===+===+===+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+---+---+---+---+---+

DataTypes

Bases: EnumMap

Lookup table/map of elementary data types. Reverse lookup is by CIP code for data type.

n_bytes(count, name='')

Create an instance of a byte string of count length. Setting count to -1 will consume the entire remaining buffer.

Array(length_, element_type_)

Creates a new array type from element_type_ of length_.

length_ can be 3 possible types: - int - fixed length of the array - DataType - length read from beginning of buffer as type - None - unbound array, consumes entire buffer on decode

Struct(*members_)

Creates a new structure type comprised of members_. Members can be instances of a DataType with a name. The decoded value of a struct will a dictionary of {member.name: value}, members without names will be excluded from the return value. To encode a struct, the value should be a dict of {member.name: value} or a sequence of just values (nesting as needed). Avoid multiple no-name members if planning on encoding the struct using a dict.

FixedSizeString(size_, len_type_=UDINT, max_len_=None)

Creates a custom string tag type