Coder Social home page Coder Social logo

l5x's Introduction

RSLogix .L5X Interface

This package aims to implement an interface for manipulating content of RSLogix .L5X export files using a native Pythonic approach as opposed to dealing with raw XML.

Getting Started

All access to .L5X data is through a top-level Project object, instantiated by passing a filename to the constructor. If the project is to be modified the write method writes the updated data back to a file for importing into RSLogix. Typical execution flow is as follows:

import l5x
prj = l5x.Project('project.L5X')

# Read or modify data as needed.

prj.write('modified.L5X')

Controller

The controller attribute of a project has the following attributes:

tags:
A tag scope containing controller tags; see Tags.
comm_path:
Permits reading and modifying the controller's communication path. Setting to None will delete the communication path.
>>> prj.controller.tags['tag_name'].description = 'A controller tag'
>>> prj.controller.comm_path
'AB_ETHIP-1\\192.168.1.10\\Backplane\\0'
snn:
Safety network number; see Modules for details.

Programs

A project's programs attribute contains a names attribute that evaluates to an iterable of program names, members of which can be used as indices to access program-scoped tags.

>>> prj.programs.names
['MainProgram', 'AnotherProgram']
>>> prj.programs['MainProgram'].tags['a_program_tag'].value = 50

Tags

The top-level project contains tag scope objects, such as controller or programs, which provide access to their respective tags. Indexing a scope object with a tag's name will return a tag object providing access to the various properties of the tag. An iterable of tag names can also be acquired from a scope's names attribute.

ctl_tags = prj.controller.tags
tag_names = ctl_tags.names
some_tag = ctl_tags[tag_names[0]]

All tag objects have at least the following attributes:

data_type
A string describing the tag's data type, such as DINT or TIMER.
value
The tag's complete value, the type of which varies based on the tag's type. For base data types this will be a single value, such as an integer, however, container objects are utilized for compound data types such as arrays and UDTs. See documentation below for details. This attribute can be read to acquire the current value or written to set a new value.
description

The tag's top-level comment. See data type specific documentation for data types which support commenting subelements such as individual array members or integer bits. In addition to normal read/write activities, setting this attribute to None will delete any existing comment.

Recent versions of RSLogix have implemented maintaining comments and descriptions in multiple languages. Adding, removing, or modifying comments with the L5X package will only affect comments in the project's current language. Changing the current language, or manipulating comments in other languages is currently not implemented.

Consumed tags include these additional read/write attributes:

producer
Name of the producing controller.
remote_tag
Remote tag name.

Integers

DINT, INT, and SINT data types accept integer values.

prj.controller.tags['dint_tag'].value = 42

Accessing individual bits is available via index notation with a zero-based integer index:

prj.controller.tags['dint_tag'][3].value = 1
prj.controller.tags['dint_tag'][2].description = 'this is bit 2'

Booleans

Like integers, BOOL data types accept integer values, albeit only 0 or 1.

Floats

REAL data types use floating point values. If an integer value is desired, it must first be converted to a float before assignment or a TypeError will be raised. Infinite and not-a-number values may not be used.

Structures

Structured data types include UDTs and built-ins such as TIMER. Individual members are accessed using the member's name as an index as follows:

prj.controller.tags['timer']['PRE'].value = 100
prj.controller.tags['timer']['DN'].description = 'done bit'

An iterable set of member identifiers is available with the names attribute:

>>> prj.controller.tags['timer'].names
['PRE', 'ACC', 'TT', 'EN', 'DN']

Accessing the value of the structure as a whole is also possible using dictionaries keyed by member name.

d = {'PRE':0, 'ACC':0, 'EN':0, 'TT':0, 'DN':0}
prj.controller.tags['timer'].value = d

Arrays

Array elements are accessed with standard index notation using integer indices. Multidimensional arrays use a series of indices, each within their own bracket as opposed to the comma-separated style of RSLogix.

>>> prj.controller.tags['single_dim_array'][3].value = 16
>>> prj.controller.tags['multi_dim_array'][2][5].description
'This is multi_dim_array[2,5]'

The value of entire array is available through the value attribute using lists. Multidimensional arrays use lists of lists and arrays of complex data types are supported, for example an array of UDTs is a list of dicts.

>>> l = [0, 1, 2, 3, 4]
>>> prj.controller.tags['single_dim_array'].value = l
>>> prj.controller.tags['multi_dim_array'].value
[[0, 1], [2, 3], [4, 5]]

An array's dimensions may be read with the shape attribute, which returns a tuple containing the size of each dimension. The following example shows output for a tag of type DINT[4,3,2]. Note the tuple's reversed display order as the number of elements in DimX is placed in shape[X].

>>> prj.controller.tags['array'].shape
(2, 3, 4)

Arrays may also be resized by assigning the shape attribute to a new set of dimensions. Keep in mind the reversed appearance of dimensions described above. Specifying a shape tuple of (x, y, z) will yield an array sized as if Dim0=x, Dim1=y, and Dim2=z were used in the Logix tag dialog. Also the array's element values and descriptions are undefined following a resize operation, even if the new shape is a subset of the original. If original content needs to be retained across a resize, it should be copied to separate variables before assigning a new shape.

>>> prj.controller.tags['DINT_array'].value
[0, 1, 2, 3]
>>> prj.controller.tags['DINT_array'].shape = (2, 2)
>>> prj.controller.tags['DINT_array'].value
[[0, 0], [0, 0]]

Alias Tags

Alias tags have two available attributes:

description
Same as the description attribute of a regular tag.
alias_for
A string containing the name of the tag the alias points to. The L5X module does not ensure the target tag exists if the alias_for attribute is altered. Changing the alias_for attribute removes any operand comments the original alias contained. For example, if the alias points to a timer and the alias contained a comment for the PRE member, changing the alias to point to a new tag will remove that comment even if the new tag is also a timer. This does not apply to any comments in the target tags; only the alias comments are affected.
>> prj.controller.tags['alias'].description
'Tag description'
>> prj.controller.tags['alias'].alias_for = 'target_tag'

No other attributes, such as value, are implemented for alias tags, nor can they be indexed to access members of the target data type.

Modules

The project's modules attribute provides access to modules defined in the I/O Configuration tree. A list of modules can be obtained with the names attribute.

>> prj.modules.names
['Controller', 'DOUT1', 'ENBT']

Each module is comprised of a set of communication ports identified by a unique integer. Ports feature a read-only type attribute to query the interface type and a read-write address attribute to get or set the type-specific address. A typical example for manipulating the IP address of an Ethernet port, which is usually port 2:

>> prj.modules['ENBT'].ports[2].type
'Ethernet'
>> prj.modules['ENBT'].ports[2].address = '192.168.0.1'

Ports configured for network address translation(NAT) can access the NAT address through the nat_address attribute. NAT addresses can only be read or altered by the L5X module, not enabled or disabled. In other words, the port must first be configured for NAT by RSLogix before the NAT address can be accessed, and the L5X module can not be used to disable NAT.

>> prj.modules['no_nat'].ports[2].nat_address # NAT not configured.
None
>> prj.modules['ENBT'].ports[2].nat_address
'10.0.0.1'
>> prj.modules['ENBT'].ports[2].nat_address = '192.168.0.1'

A module's connection inhibit selection can be read or altered with the inhibit attribute:

>> prj.modules['ENBT'].inhibited
False
>> prj.modules['ENBT'].inhibited = True

Safety Network Numbers

Safety network numbers for safety modules, including the controller, can be accessed via the snn attribute of either the module or its ports. For modules with a single safety network number, such as safety I/O modules, the snn is an attribute of the module itself. Safety modules with multiple communication ports, such as controllers with integrated Ethernet ports, have multiple safety network numbers, which are attributes of its ports.

Module and port safety network numbers use the same format: a twelve character string representing the hexadecimal safety network number; intervening underscores as seen with RSLogix are stripped away. Acceptable values to set a new number need not be zero padded and may contain intervening underscores, however, it must be a string yielding a hexadecimal number not exceeding 48 bits.

>>> prj.controller.snn # Controller with a single SNN.
'000011112222'
>>> prj.controller.ports[0].snn # Controller with multiple, port-specific SNNs.
'0123456789AB'
>>> prj.modules['safe_in'].snn
'AAAABBBBCCCC'
>>> prj.controller.snn = '42'
>>> prj.modules['safe_out'].snn = '0001_0002_0003'

l5x's People

Contributors

jvalenzuela avatar

Stargazers

 avatar Lamsamr avatar Réverti Ivanov avatar x07yansu avatar  avatar  avatar  avatar Matt Revelle avatar Trevor Flynn avatar Ben Hutcheson avatar CJ avatar  avatar Josh Coles avatar Krystian Tolloczko avatar Jim Betsinger avatar  avatar Addison Williams avatar  avatar Justin Lopez avatar Joe Tressler avatar George Fearnall avatar  avatar  avatar Ran Wang avatar  avatar  avatar  avatar Brad Taylor avatar Martin Laprise avatar Kyle McNicoll avatar Jakob Gabriel avatar  avatar schermobianco avatar Aldon Smith avatar  avatar Damir Khakimov avatar  avatar Steak Guo 牛排 avatar  avatar Sylvain Laurent avatar Jiri Kastner avatar Luca Barba avatar DGG avatar Jo avatar Andrew avatar

Watchers

Jiri Kastner avatar  avatar Damir Khakimov avatar  avatar  avatar Justin Lopez avatar

l5x's Issues

Port NAT address & SNN

Request to add access to communication port NAT address and SNN:

class Port(ElementAccess):
    """Accessor object for a module's port."""
    address = AttributeDescriptor('Address')
    type = AttributeDescriptor('Type', True)
    snn = SafetyNetworkNumber()
    nat = AttributeDescriptor('NATActualAddress')

project export even with no changes is causing an issue

running the following code and then opening the l5x in designer is giving the errors below; i'll update with what I do to fix, but probably wont get into your code to figure out why this is happening

code:
prj = project.Project(fn) # where fn is some l5x file
def saveFile(saveIdx):
prj.write(controller + str(saveIdx) + 'AutoGen.L5X')
saveFile('NoEdits')

import errors:
Error: Line 60783: Required CDATA for element 'Line' was missing.
RSLogix5000Content/Controller/AddOnInstructionDefinitions/AddOnInstructionDefinition[@name="AOI_DaysSince"]/Routines/Routine[@name="Logic"]/STContent/Line[@Number="5"]
Error: Line 61646: Error creating 'LocalTag[@name="AOI_DaySince"]' (Data type does not exist.).
RSLogix5000Content/Controller/AddOnInstructionDefinitions/AddOnInstructionDefinition[@name="AOI_ISO_Weeknumber"]/LocalTags/LocalTag[@name="AOI_DaySince"]
Error: Line 72136: Required CDATA for element 'Line' was missing.
RSLogix5000Content/Controller/AddOnInstructionDefinitions/AddOnInstructionDefinition[@name="AOI_TIME_ADD"]/Routines/Routine[@name="Logic"]/STContent/Line[@Number="23"]
Error: Line 73468: Required CDATA for element 'Line' was missing.
RSLogix5000Content/Controller/AddOnInstructionDefinitions/AddOnInstructionDefinition[@name="AOI_TIME_DIFFERENCE"]/Routines/Routine[@name="Logic"]/STContent/Line[@Number="25"]
Error: Line 74626: Error creating 'LocalTag[@name="AOI_SNTP_Time_To_Current_Date"]' (Data type does not exist.).
RSLogix5000Content/Controller/AddOnInstructionDefinitions/AddOnInstructionDefinition[@name="AOI_SNTP_QUERY"]/LocalTags/LocalTag[@name="AOI_SNTP_Time_To_Current_Date"]
Error: Line 158502: Error creating 'Tag[@name="DSTAOIWeekNumMar"]' (Data type does not exist.).
RSLogix5000Content/Controller/Tags/Tag[@name="DSTAOIWeekNumMar"]
Error: Line 158564: Error creating 'Tag[@name="DSTAOIWeekNumNov"]' (Data type does not exist.).
RSLogix5000Content/Controller/Tags/Tag[@name="DSTAOIWeekNumNov"]
Error: Line 580186: Required CDATA for element 'Line' was missing.
RSLogix5000Content/Controller/Programs/Program[@name="FS_PCI"]/Routines/Routine[@name="R001_Init"]/STContent/Line[@Number="3"]
Error: Line 609113: Error creating 'Tag[@name="SNTP_AOI_TAG"]' (Data type does not exist.).
RSLogix5000Content/Controller/Programs/Program[@name="MainProgram"]/Tags/Tag[@name="SNTP_AOI_TAG"]

UDT long dimension

Hello,

Seems doesn't work in case of UDT with big Dim 1 dimension.

Example
with MyTag with data type MyUDT[100] it works well :

prj.controller.tags['MyTag'][int(0)]['Member1'].value
returns
"17.5"

but if MyUDT[10000]
prj.controller.tags['MyTag'][int(0)]['Member1'].value
returns
"RuntimeError: Decoded data content not found for MyTag tag. Ensure Encode Source Protected Content option is disabled when saving L5X"

I'm sure that I have saving L5x without source protection..

Adding Tags

Are you able to add tags to the code by using this lib?

Alias support

I have a need to change alias tags. Can something like ".alias" be added to the tag element? I'd like the ability to reference global scope and program scope. something like:
prj.controller.tags['global tag'].alias = prj.controller.tags['a different global tag'].name
OR
prj.programs['MainProgram'].tags['a_program_tag'].alias = prj.controller.tags['global tag'].name
prj.programs['MainProgram'].tags['a_program_tag'].alias = prj.programs['MainProgram'].tags['a_different_program_tag'].name

This would reference the "AliasFor" attribute. I would hope it checks the tag has the attribute TagType="Alias".

tag name change

Hello! Is there any way to update the tag name? Thank you in advance!

Module names

Some modules have no name attribute, such as peripheral cards in a VFD. This causes a KeyErrror exception when accessing the modules name attribute.

BOOL values

Add support/documentation/tests to permit BOOL tags to be assigned True/False values in addition to 0 or 1.

Not able to access data stored in REAL type tag array.

Hi all,

Looking for help to resolve the following issue that I have encountered. In Studio 5000 I am able to see that "Data_1" tag array exists and contains the value 69 at the index "1". The recommendation to "Ensure Encode Source Protected Content is disabled when saving L5X" I have been unable to follow because that option is check and grayed out in every menu I have found it. This could be an issue of me not understanding where to look to be able to disable this option, but have been unsuccessful in finding a solution so far.

Other research/feedback I have gotten is that if the content isn't protected, it shouldn't matter. I am not aware of how to determine if the content is protected or not as the guides I have found have been for much older RSLogix versions of the software. I am currently running v28.03.00 of Studio 5000.

To my best knowledge I have tried everything recommended on the README. Any support would be greatly appreciated!

image

Traceback (most recent call last): File "C:\Git\AP4-PowerNet-Test-Bench-Script\main.py", line 121, in <module> print(prj.controller.tags['Data_1'][1].value) ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^ File "C:\Users\jaewing\AppData\Local\anaconda3\envs\AP4_Test_Bench\Lib\site-packages\l5x\dom.py", line 294, in __getitem__ return self.create_value_object(element) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\jaewing\AppData\Local\anaconda3\envs\AP4_Test_Bench\Lib\site-packages\l5x\dom.py", line 304, in create_value_object return self.value_type(*args) ^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\jaewing\AppData\Local\anaconda3\envs\AP4_Test_Bench\Lib\site-packages\l5x\tag.py", line 100, in __init__ self.data = data_class(self.get_data_element(), self) ^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\jaewing\AppData\Local\anaconda3\envs\AP4_Test_Bench\Lib\site-packages\l5x\tag.py", line 112, in get_data_element raise RuntimeError("Decoded data content not found for {0} tag. " RuntimeError: Decoded data content not found for Data_1 tag. Ensure Encode Source Protected Content option is disabled when saving L5X.

DINT in binary "style"

We use UDTs that have DINTs formatted in binary style. It's stupid, and I'll be changing the UDTs going forward because it's also causing issues for my other forms of automation as well.

However, the library expects a dint to be a base 10 number (which makes sense) but that's not what I get.

My code:

import l5x

prj = l5x.Project('FILENAME.L5X')

# for tag in prj.controller.tags:
#     print(tag)
# print(prj.controller.tags.names)
names = prj.controller.tags.names

for name in names:
    print(name)
    print(prj.controller.tags[name].data_type)
    print(prj.controller.tags[name].description)
    print(prj.controller.tags[name].value)

with output:

Traceback (most recent call last):
  File "c:\VMs\SharedFilesBetweenVMs\l5x testing\test.py", line 15, in <module>
    print(prj.controller.tags[name].value)
  File "C:\Users\Wes\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\l5x\tag.py", line 31, in __get__
    return getattr(tag.data, self.attr)
  File "C:\Users\Wes\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\l5x\tag.py", line 519, in __get__
    return dict(zip(member_names, [struct[m].value for m in member_names]))
  File "C:\Users\Wes\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\l5x\tag.py", line 519, in <listcomp>
    return dict(zip(member_names, [struct[m].value for m in member_names]))
  File "C:\Users\Wes\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\l5x\tag.py", line 372, in __get__
    return int(instance.element.attrib['Value'])
ValueError: invalid literal for int() with base 10: '2#0000_0001_1110_0000_0000_1010_0000_0000'

version attribute

I would like to be able to request the version programmatically. Ideally, using the __version__ attribute.

See pep 0396

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.