0
# PyLogix
1
2
A Python communication driver that enables reading and writing data values from tags in Rockwell Automation ControlLogix, CompactLogix, and Micro8xx PLCs over Ethernet I/P. PyLogix provides a simple, Pythonic API for PLC communication without external dependencies, making it ideal for industrial automation applications, data acquisition systems, and monitoring tools.
3
4
## Package Information
5
6
- **Package Name**: pylogix
7
- **Language**: Python
8
- **Installation**: `pip install pylogix`
9
- **Compatibility**: Python 2.7, Python 3.x, MicroPython 1.20.0+
10
- **Dependencies**: None (standard library only)
11
12
## Core Imports
13
14
```python
15
from pylogix import PLC
16
```
17
18
Alternative imports for specific components:
19
20
```python
21
from pylogix.eip import PLC
22
from pylogix.lgx_response import Response
23
from pylogix.lgx_tag import Tag, UDT
24
from pylogix.lgx_device import Device
25
```
26
27
## Basic Usage
28
29
```python
30
from pylogix import PLC
31
32
# Basic read/write operations
33
with PLC() as comm:
34
comm.IPAddress = '192.168.1.100'
35
36
# Read a single tag
37
response = comm.Read('MyTagName')
38
print(f"Tag: {response.TagName}, Value: {response.Value}, Status: {response.Status}")
39
40
# Write a value to a tag
41
response = comm.Write('OutputTag', 42)
42
if response.Status == 'Success':
43
print("Write successful")
44
45
# Read multiple tags
46
tags = ['Tag1', 'Tag2', 'Tag3']
47
responses = comm.Read(tags)
48
for resp in responses:
49
print(f"{resp.TagName}: {resp.Value}")
50
51
# Manual connection management
52
comm = PLC()
53
comm.IPAddress = '192.168.1.100'
54
comm.ProcessorSlot = 0 # Default slot
55
response = comm.Read('MyTag')
56
comm.Close() # Always close when done
57
```
58
59
## Architecture
60
61
PyLogix follows a layered architecture designed for reliability and ease of use:
62
63
- **PLC Class**: High-level interface providing read/write operations and device management
64
- **Connection Layer**: Handles Ethernet I/P communication, connection pooling, and error recovery
65
- **CIP Protocol Layer**: Implements Common Industrial Protocol (CIP) for Rockwell devices
66
- **Response System**: Structured response objects with status codes and error handling
67
- **Tag Management**: Automatic data type discovery, UDT handling, and tag metadata caching
68
69
The library supports both connected and unconnected messaging, automatic data type detection, and handles complex scenarios like bit manipulation, array operations, and User Defined Types (UDTs).
70
71
## Capabilities
72
73
### Core Tag Operations
74
75
Essential read and write operations for PLC tags, supporting individual tags, arrays, and batch operations. Includes automatic data type detection and conversion.
76
77
```python { .api }
78
def Read(tag, count=1, datatype=None):
79
"""
80
Read tag values from the PLC.
81
82
Args:
83
tag (str or list): Tag name(s) to read
84
count (int): Number of elements for array reads
85
datatype (int, optional): Force specific data type
86
87
Returns:
88
Response or list[Response]: Read results
89
"""
90
91
def Write(tag, value=None, datatype=None):
92
"""
93
Write values to PLC tags.
94
95
Args:
96
tag (str or list): Tag name(s) to write
97
value: Value(s) to write
98
datatype (int, optional): Force specific data type
99
100
Returns:
101
Response or list[Response]: Write results
102
"""
103
```
104
105
[Core Operations](./core-operations.md)
106
107
### PLC Time Management
108
109
Functions for getting and setting the PLC's internal clock, useful for time synchronization and timestamp operations.
110
111
```python { .api }
112
def GetPLCTime(raw=False):
113
"""
114
Get the PLC's current time.
115
116
Args:
117
raw (bool): Return raw microseconds if True, datetime if False
118
119
Returns:
120
Response: PLC time information
121
"""
122
123
def SetPLCTime(dst=None):
124
"""
125
Set the PLC's clock to current system time.
126
127
Args:
128
dst (bool, optional): Daylight saving time flag
129
130
Returns:
131
Response: Set time operation result
132
"""
133
```
134
135
[Time Operations](./time-operations.md)
136
137
### Tag Discovery and Introspection
138
139
Capabilities for discovering available tags, programs, and retrieving tag metadata including data types and structure information.
140
141
```python { .api }
142
def GetTagList(allTags=True):
143
"""
144
Retrieve the complete tag list from the PLC.
145
146
Args:
147
allTags (bool): Include program tags if True, controller only if False
148
149
Returns:
150
Response: List of Tag objects with metadata
151
"""
152
153
def GetProgramTagList(programName):
154
"""
155
Get tags for a specific program.
156
157
Args:
158
programName (str): Program name (e.g., "Program:MainProgram")
159
160
Returns:
161
Response: List of program-specific Tag objects
162
"""
163
164
def GetProgramsList():
165
"""
166
Get list of available programs in the PLC.
167
168
Returns:
169
Response: List of program names
170
"""
171
```
172
173
[Tag Discovery](./tag-discovery.md)
174
175
### Device Discovery and Information
176
177
Network device discovery and device property retrieval for system diagnostics and configuration.
178
179
```python { .api }
180
def Discover():
181
"""
182
Discover Ethernet I/P devices on the network.
183
184
Returns:
185
Response: List of Device objects with device information
186
"""
187
188
def GetDeviceProperties():
189
"""
190
Get properties of the connected device.
191
192
Returns:
193
Response: Device object with detailed properties
194
"""
195
196
def GetModuleProperties(slot):
197
"""
198
Get properties of a module in a specific slot.
199
200
Args:
201
slot (int): Module slot number
202
203
Returns:
204
Response: Device object for the module
205
"""
206
```
207
208
[Device Discovery](./device-discovery.md)
209
210
### Advanced Messaging
211
212
Low-level CIP messaging capabilities for custom communication and advanced PLC interactions.
213
214
```python { .api }
215
def Message(cip_service, cip_class, cip_instance, cip_attribute=None, data=b''):
216
"""
217
Send custom CIP message to the PLC.
218
219
Args:
220
cip_service (int): CIP service code
221
cip_class (int): CIP class code
222
cip_instance (int): CIP instance number
223
cip_attribute (int, optional): CIP attribute number
224
data (bytes): Message data payload
225
226
Returns:
227
Response: Raw response from PLC
228
"""
229
230
def ReceiveMessage(ip_address, callback):
231
"""
232
Listen for incoming CIP messages.
233
234
Args:
235
ip_address (str): IP address to listen on
236
callback (function): Callback function for received messages
237
238
Returns:
239
Response: Listener status
240
"""
241
```
242
243
[Advanced Messaging](./advanced-messaging.md)
244
245
## Connection Configuration
246
247
```python { .api }
248
class PLC:
249
def __init__(ip_address="", slot=0, timeout=5.0, Micro800=False, port=44818):
250
"""
251
Initialize PLC connection.
252
253
Args:
254
ip_address (str): PLC IP address
255
slot (int): Processor slot number (default 0)
256
timeout (float): Socket timeout in seconds
257
Micro800 (bool): True for Micro800 series PLCs
258
port (int): Communication port (default 44818)
259
"""
260
261
# Properties
262
IPAddress: str # PLC IP address
263
Port: int # Communication port
264
ProcessorSlot: int # Processor slot
265
SocketTimeout: float # Socket timeout
266
Micro800: bool # Micro800 flag
267
Route: object # Routing configuration
268
269
@property
270
def ConnectionSize(self) -> int:
271
"""
272
Connection packet size for Forward Open requests.
273
274
Default behavior attempts Large Forward Open (508 bytes) followed by
275
Small Forward Open if the first fails. For Explicit (Unconnected)
276
sessions, uses a sensible default size.
277
278
Returns:
279
int: Connection size in bytes (default: 508)
280
"""
281
282
@ConnectionSize.setter
283
def ConnectionSize(self, connection_size: int):
284
"""
285
Set the connection packet size.
286
287
Args:
288
connection_size (int): Desired packet size in bytes
289
"""
290
```
291
292
## Response Objects
293
294
All PLC operations return Response objects containing the results and status information.
295
296
```python { .api }
297
class Response:
298
def __init__(self, tag_name, value, status):
299
"""
300
Response object for PLC operations.
301
302
Args:
303
tag_name (str): Tag name associated with the operation
304
value: Returned value or data
305
status (str or int): Operation status (string or CIP error code)
306
"""
307
308
TagName: str # Tag name associated with the operation
309
Value: any # Operation result value (tag data, lists, objects, etc.)
310
Status: str # Operation status ("Success" or descriptive error message)
311
312
@staticmethod
313
def get_error_code(status) -> str:
314
"""
315
Convert CIP error code to descriptive error message.
316
317
Args:
318
status: CIP error code (int) or error message (str)
319
320
Returns:
321
str: Descriptive error message
322
"""
323
```
324
325
## Data Types and Structures
326
327
```python { .api }
328
class Tag:
329
"""Tag metadata object representing a PLC tag with all its properties."""
330
TagName: str # Tag name
331
InstanceID: int # Instance identifier
332
SymbolType: int # Symbol type code
333
DataTypeValue: int # Data type value
334
DataType: str # Human-readable data type
335
Array: int # Array flag (0=not array, >0=array)
336
Struct: int # Structure flag (0=not struct, 1=struct)
337
Size: int # Tag size for arrays
338
AccessRight: int # Access rights
339
Internal: bool # Internal flag
340
Meta: object # Metadata information
341
Scope0: object # Scope level 0
342
Scope1: object # Scope level 1
343
Bytes: bytes # Raw byte data
344
345
@staticmethod
346
def in_filter(tag: str) -> bool:
347
"""
348
Check if the provided tag is in the filter list.
349
350
Args:
351
tag (str): Tag name to check
352
353
Returns:
354
bool: True if tag should be filtered out
355
"""
356
357
@staticmethod
358
def parse(packet: bytes, program_name: str) -> 'Tag':
359
"""
360
Parse a tag from raw packet data.
361
362
Args:
363
packet (bytes): Raw packet data from PLC
364
program_name (str): Program name for scoping
365
366
Returns:
367
Tag: Parsed tag object
368
"""
369
370
class UDT:
371
"""User Defined Type structure."""
372
Type: int # UDT type identifier
373
Name: str # UDT name
374
Fields: list[Tag] # List of field Tag objects
375
FieldsByName: dict[str, Tag] # Dictionary mapping field names to Tags
376
377
class Device:
378
"""Network device information."""
379
Length: int # Packet length
380
EncapsulationVersion: int # Encapsulation version
381
IPAddress: str # Device IP address
382
VendorID: int # Vendor identifier
383
Vendor: str # Vendor name
384
DeviceID: int # Device identifier
385
DeviceType: str # Device type description
386
ProductCode: int # Product code
387
ProductName: str # Product name
388
Revision: str # Device revision
389
Status: int # Device status
390
SerialNumber: str # Serial number (hex format)
391
ProductNameLength: int # Length of product name
392
State: int # Device state
393
394
@staticmethod
395
def get_device(device_id: int) -> str:
396
"""
397
Get device type description from device ID.
398
399
Args:
400
device_id (int): Device ID code
401
402
Returns:
403
str: Device type description or "Unknown"
404
"""
405
406
@staticmethod
407
def get_vendor(vendor_id: int) -> str:
408
"""
409
Get vendor name from vendor ID.
410
411
Args:
412
vendor_id (int): Vendor ID code
413
414
Returns:
415
str: Vendor name or "Unknown"
416
"""
417
418
@staticmethod
419
def parse(data: bytes, ip_address: str = None) -> 'Device':
420
"""
421
Parse a device from raw packet data.
422
423
Args:
424
data (bytes): Raw packet data
425
ip_address (str, optional): Override IP address
426
427
Returns:
428
Device: Parsed device object
429
"""
430
```
431
432
## Error Handling
433
434
PyLogix uses structured error handling through Response objects. The Status field contains either "Success" or a descriptive error message. Error codes are automatically converted from CIP (Common Industrial Protocol) status codes to human-readable messages.
435
436
### Complete CIP Error Code Mapping
437
438
```python
439
# CIP Error Codes (hex: description)
440
0x00: 'Success'
441
0x01: 'Connection failure'
442
0x02: 'Resource unavailable'
443
0x03: 'Invalid parameter value'
444
0x04: 'Path segment error'
445
0x05: 'Path destination unknown'
446
0x06: 'Partial transfer'
447
0x07: 'Connection lost'
448
0x08: 'Service not supported'
449
0x09: 'Invalid Attribute'
450
0x0A: 'Attribute list error'
451
0x0B: 'Already in requested mode/state'
452
0x0C: 'Object state conflict'
453
0x0D: 'Object already exists'
454
0x0E: 'Attribute not settable'
455
0x0F: 'Privilege violation'
456
0x10: 'Device state conflict'
457
0x11: 'Reply data too large'
458
0x12: 'Fragmentation of a primitive value'
459
0x13: 'Not enough data'
460
0x14: 'Attribute not supported'
461
0x15: 'Too much data'
462
0x16: 'Object does not exist'
463
0x17: 'Service fragmentation sequence not in progress'
464
0x18: 'No stored attribute data'
465
0x19: 'Store operation failure'
466
0x1A: 'Routing failure, request packet too large'
467
0x1B: 'Routing failure, response packet too large'
468
0x1C: 'Missing attribute list entry data'
469
0x1D: 'Invalid attribute value list'
470
0x1E: 'Embedded service error'
471
0x1F: 'Vendor specific'
472
0x20: 'Invalid Parameter'
473
0x21: 'Write once value or medium already written'
474
0x22: 'Invalid reply received'
475
0x23: 'Buffer overflow'
476
0x24: 'Invalid message format'
477
0x25: 'Key failure in path'
478
0x26: 'Path size invalid'
479
0x27: 'Unexpected attribute in list'
480
0x28: 'Invalid member ID'
481
0x29: 'Member not settable'
482
0x2A: 'Group 2 only server general failure'
483
0x2B: 'Unknown Modbus error'
484
0x2C: 'Attribute not gettable'
485
```
486
487
### Common Error Scenarios
488
489
```python
490
from pylogix import PLC
491
492
with PLC() as comm:
493
comm.IPAddress = '192.168.1.100'
494
495
# Handle connection errors
496
response = comm.Read('MyTag')
497
if response.Status == 'Success':
498
print(f"Value: {response.Value}")
499
elif response.Status == 'Connection failure':
500
print("Could not connect to PLC - check IP address and network")
501
elif response.Status == 'Path destination unknown':
502
print("Invalid tag name or PLC path")
503
elif response.Status == 'Object does not exist':
504
print("Tag not found in PLC")
505
else:
506
print(f"Unexpected error: {response.Status}")
507
```