A library for intuitively interacting with Rockwell's L5X import/export files.
The purpose of this library is to offer a reusable, strongly typed, intuitive API over Rockwell's L5X schema, allowing developers to quickly modify or generate L5X content. In doing so, this library can aid in creation of tools or scripts that automate the PLC development, maintenance, or testing processes for automation engineers.
The following are some high level goals this project aimed to accomplish.
You can consume the library via NuGet.
Install-Package L5Sharp
Previously I had two separate libraries but have since consolidated to a single package to avoid confusion and since I think most people would want all functionality anyway.
L5Sharp.Extensions
is no longer maintained.
Install-Package L5Sharp
L5X
class static factory methods.
var content = L5X.Load("C:\PathToMyFile\FileName.L5X");
Get started by querying elements across the L5X using the Query()
methods and LINQ extensions.
The following query gets all tags and their nested tag members of type TIMER and returns the TagName,
Description, and Preset value in a flat list ordered by TagName.
var results = content.Query<Tag>()
.SelectMany(t => t.Members())
.Where(t => t.DataType == "TIMER")
.Select(t => new {t.TagName, t.Description, Preset = t["PRE"].Value})
.OrderBy(v => v.TagName)
.ToList();
Query<T>()
returns anIEnumerable<T>
, allowing for complex queries using LINQ and the strongly typed objects in the library. SinceQuery<T>()
queries the entire L5X for the specified type, the above query will return all Tag components found, including controller and program tags.
The following is some basic examples of how you can use this library to query and modify the L5X content.
Once the L5X is created, you can use the component properties to access the LogixContainer
for the specified type.
This gets you simple collection APIs that allow you to query for component objects.
This library supports all primary components including the following:
For example, the following shows some simple querying via the Tags
component container.
//Get all controller tags.
var tags = content.Tags.ToList();
//Get a tag by name.
var tag = content.Tags.Find("MyTag");
//Use LINQ to query further.
var results = content.Tags.Where(t => t.DataType == "TIMER");
The previous code will only return controller scoped tags since the Tags
container on the
root L5X represents controller scoped tag elements. To get program tags we would
have to access the Tags
container on a specific program.
//Get the first program in the L5X and find a tag called 'MyProgramTag'
var programTags = content.Programs.First().Tags.Find("MyProgramTag");
This is a little cumbersome. It would be nice to have a single way to query for all tags
across the file and filter those results as well. And there is using the Query
methods. These generic methods
will return all instances of the type found in the file and allow you to further filter them using LINQ.
//Gets all tags (controller, program, module/IO)
var allTags = content.Query<Tag>().ToList();
var programTags = allTags.Where(t => t.Scope == Scope.Program);
var ioTags = allTags.Where(t => t.Name.Contains(':'));
var readWriteTags = allTags.Where(t => t.ExternalAccess == ExternalAccess.ReadWrite);
var timerTags = allTags.Where(t => t.DataType == "TIMER");
Some L5X projects can contain a lot of Tag
components especially when you consider all the nested
tag members as an individual tag that can be referenced in the project.
If you have to perform continuous lookup of tag objects for some operation or task, that could be costly
or time intensive. We need a fast way to retrieve components, and this library offers that as well.
The following methods use an internal index or set of dictionaries to quickly find a component with by name and type.
//Find a data type component by name
var tag = content.Find<DataType>("MyUserDefined");
//Get a data type component by name
//(the different here is Get throws an exception if not found)
var tag = content.Get<DataType>("MyUserDefined");
//Get a nested tag member
//(we don't need to specify <Tag> since it is implied with the Tagname overload)
var tag = content.Find("MyTag.Member[1].SubMember.1");
//Gets all tags with name
//(this could be controller and/or program tags)
var tags = content.All("ScopedTag").ToList();
The above calls all operate in constant time since the components are indexed using dictionaries. The L5X is
only indexed when the first call to an index method such as Find
, Get
or All
is made. The index is cached
so everytime after we don't need to recreate it. It will also be maintained internally as components are
added or removed.
Modifying components is simple as well. The same component collection interface offers methods for adding, removing, and updating components.
//Add a new component.
var tag = new Tag { Name = "MyTag", Value = 100 };
content.Tags.Add(tag);
//Remove an existing component.
var result = content.Tags.Remove("MyTag");
//Repalce an existing component.
var result = content.Tags.Find("MyTag").Replace(tag);
//Configure individual properties.
var tag = content.Tags.Get("MyTag");
tag.Value = 100;
tag.Description = "This is an update";
tag.ExternalAccess = ExternalAccess.ReadOnly;
tag.Constant = true;
//Apply update funtion to all components that satisfy a condition.
content.Tags.Update(t => t.DataType == "TIMER", t => t.Description = "Updated TIMER description");
//Save changes when done.
content.Save("C:\PathToMyOutputFile\FileName.L5X");
I started working on a GitHub Page here for more in depth articles and API documentation, but it's a work in progress so bear with me. In fact it is currently out of date, so reference this readme for the most up to date information. If you think something is unclear or can be improved upon, feel free to let me know in the issues tab of the project repository.
If you like or use this project, leave a star! If you have questions or issues, please post in the issues tab of the project repository. Any feedback or contributions are welcome!