L5Sharp Save

A library for intuitively interacting with Rockwell's L5X import/export files.

Project README

L5Sharp

A library for intuitively interacting with Rockwell's L5X import/export files.

Purpose

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.

Goals

The following are some high level goals this project aimed to accomplish.

  1. Provide a simple and intuitive API, making the learning curve as low as possible.
  2. Ensure performance as much as possible without sacrificing simplicity.
  3. Make it easy and seamless to extend the API to support custom queries or functions.
  4. Support strongly typed mutable tag data, so we can reference complex structures statically at compile time.

Packages

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.

Quick Start

  1. Install package from Nuget.

    Install-Package L5Sharp
    
  2. Load an L5X file using the L5X class static factory methods.

    var content = L5X.Load("C:\PathToMyFile\FileName.L5X");
    
  3. 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 an IEnumerable<T>, allowing for complex queries using LINQ and the strongly typed objects in the library. Since Query<T>() queries the entire L5X for the specified type, the above query will return all Tag components found, including controller and program tags.

Releases Notes

Usage

The following is some basic examples of how you can use this library to query and modify the L5X content.

Querying

Using Container Collections

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:

  • Controller
  • DataType
  • AddOnInstruction
  • Module
  • Tag
  • Program
  • Routine
  • Task
  • Trend
  • WatchList

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");
Using Query Methods

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");
Using Index Methods

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

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");

Release Notes

Version 2.0

This release follows from version 0.19.X. The reason for the version change is to represent this as a mostly complete and no longer a work in progress library. The reason for v2 and not v1 is because I accidentally released v1 a long time ago before backing into v0.1.

This update mostly focused on adding .NET 2.0 standard support and merging how LogixTypes were deserialized with how all other LogixElements were deserialized, which makes the entire project more uniform and gives one point for deserialization of any element/type. Most of the API surface is the same with a couple minor tweaks. You should not notice too may changes.

Highlights
  1. Adds support for .NET Standard 2.0 to make library compatible with .NET Framework applications.
  2. LogixType now implements LogixElement, making them wrap an underlying element where possible, and allowing a single base class for all types.
  3. Combined the old LogixData with the LogixSerializer to have a single point of deserialization for all elements.
  4. Created new LogixObject which implements LogixElement and moved the common L5X, Scope, Container, and methods for adding, replacing, and removing the object from the L5X.
  5. Removed AtomicType bit members to avoid exceedingly large number of member tags it would generate when attemping to all tags in a file.
  6. Removed Class and Family properties from LogixType since they are not really useful.
  7. Renamed LogixType to LogixData, as well as all derivatives to better match the L5X naming convention.

Documentation

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.

Feedback

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 is always appreciated. If you have ideas or suggestions for features or things that would make this library more useful, I welcome your idea! Just send me a message.

Open Source Agenda is not affiliated with "L5Sharp" Project. README Source: tnunnink/L5Sharp
Stars
38
Open Issues
0
Last Commit
4 weeks ago
Repository
License
MIT

Open Source Agenda Badge

Open Source Agenda Rating