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, |
None
|
cache
|
bool
|
store the retrieved list in the :attr: |
True
|
tag_namespace_filter
|
str
|
prefix match on the tag name (without the |
''
|
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 |
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, |
()
|
Returns:
| Type | Description |
|---|---|
ReadWriteReturnType
|
a single or list of |
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 ( |
()
|
period_ms
|
float
|
sample period, 1 ms or more (whole microseconds) |
10
|
capacity
|
int
|
samples kept until :meth: |
4096
|
duration
|
Optional[float]
|
stop after this many seconds, None to run until closed |
None
|
Returns:
| Type | Description |
|---|---|
Trend
|
the running :class: |
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 |
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 |
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: |
required | |
nodes
|
Optional[Sequence]
|
the addresses to probe, by default the port's |
None
|
Returns:
| Type | Description |
|---|---|
List[Dict[str, Any]]
|
a dict per address, in their order: |
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]]
|
|
None
|
name
|
str
|
return |
'generic'
|
connected
|
bool
|
|
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
|
Returns:
| Type | Description |
|---|---|
Tag
|
a Tag whose value is the (decoded) reply data, |
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)
Trends¶
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, withconnection(a dict as :meth:connectionsreturns)outputwithinstance: an output assembly's data or run bit changed; sent once until :meth:outputreads itconfigwithinstanceanddata: 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.
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.
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
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.
BOOL
¶
Bases: ElementaryDataType
A boolean value, decodes 0x00 and False and True otherwise.
True encoded as 0xFF and False as 0x00
SINT
¶
INT
¶
DINT
¶
LINT
¶
USINT
¶
UINT
¶
UDINT
¶
ULINT
¶
REAL
¶
LREAL
¶
DATE_AND_TIME
¶
StringDataType
¶
LOGIX_STRING
¶
STRING
¶
BytesDataType
¶
BitArrayType
¶
BYTE
¶
WORD
¶
DWORD
¶
LWORD
¶
STRING2
¶
STRINGN
¶
SHORT_STRING
¶
EPATH
¶
STRINGI
¶
Bases: StringDataType
international character string
encode(*strings)
classmethod
¶
Encodes strings to bytes
ArrayType
¶
StructType
¶
CIPSegment
¶
Bases: DataType
Base type for a CIP path segment
+----+----+----+---+---+---+---+---+ | Segment Type | Segment Format | +====+====+====+===+===+===+===+===+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+---+---+---+---+---+
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