Skip to content

Commit

Permalink
updated readme
Browse files Browse the repository at this point in the history
  • Loading branch information
tnunnink committed Jul 26, 2023
1 parent f246a7a commit cc8226f
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 161 deletions.
203 changes: 48 additions & 155 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,68 +1,69 @@


# L5Sharp
A C# library for interacting with Rockwell's L5X import/export files.
The goal of this project was to provide a simple and reusable library for
querying and manipulating L5X files to aid in the creation of tools that
automate tasks related RSLogix 5000 PLC development.

## Getting started
This project is still under development and in a preview phase, but you can
start using it by adding the package from Nuget.
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.

## Feedback
If you like or use this project, leave a star. If you have questions or issues,
please post in the [issues](https://github.com/tnunnink/L5Sharp/issues) tab
of the project repository. Any feedback is always appreciated. Enjoy!

## Quick Start
Install package from Nuget.
```powershell
Install-Package L5Sharp
```
If you would like to contribute, have feedback or questions, please reach out!
If you are using or like the progress being made, please leave a star. Thanks!


## Overview
The following is a basic walk through of some of the high level features
ofr the library. I plan to create a more in depth wiki at some point, but for
now please refer to the following information to understand how to use the
library.

### Entry Point
The main entry point to the L5X is the `LogixContent` class.
Use the factory methods `Load` to load a file or `Parse` to parse a string,
exactly how you would with `XDocument`.
Load an L5X file using the primary entry point `LogixContent` class.
```c#
var content = LogixContent.Load("C:\PathToMyFile\FileName.L5X");
```

`LogixContent` has a `L5X` property, which is essentially the root content element
of the document. So if you ever need access the XML directly, use that property.
It also contains the root content attributes, such as target type, target component,
software version, and others.
Get started by querying any type across the L5X using the `Find<T>()` and LINQ extensions.
```csharp
var content = LogixContent.Load(Known.Test);

content.L5X.Should().NotBeNull();
content.L5X.SchemaRevision.Should().Be(new Revision());
content.L5X.SoftwareRevision.Should().Be(new Revision(32, 02));
content.L5X.TargetName.Should().Be("TestController");
content.L5X.TargetType.Should().Be("Controller");
content.L5X.ContainsContext.Should().Be(false);
content.L5X.Owner.Should().Be("tnunnink, EN Engineering");
content.L5X.ExportDate.Should().NotBeNull();
var results = content.Find<Tag>()
.SelectMany(t => t.Members())
.Where(t => t.DataType == "TIMER")
.Select(t => new { t.TagName, t.Comment, t.Value.As<TIMER>().PRE })
.ToList();
```

### Querying Content
The above query gets all tags and their nested tag members of type TIMER and returns the TagName,
Comment, and Preset value in a flat list.
>[!NOTE]
>`Find<T>()` returns an `IEnumerable<T>`, allowing for complex queries
using LINQ and the strongly typed objects in the library.
> Since `Find<T>()` queries the entire L5X for the specified type, the above query
> will return all **Tag** components found, including controller and program tags.
## Usage
The following is some basic examples of how you can use this library
to query and modify the L5X content.

### Querying
Once the LogixContent is created, you can use the container properties
to get access to all of the primary L5X components,
such as `Tag`, `DataType`, `Module`, `Program`, and more.
The following shows some simple querying via the tags component collection interface.
The following shows some simple querying via the `Tags` component container.
```c#
//Get all controller tags.
var controllerTags = content.Tags;
var tags = content.Tags.ToList();

//Get a tag by name.
var myTag = content.Tags.Find("MyTag");
var tag = content.Tags.Find("MyTag");

//Use LINQ to query further.
var timerTypeTags = content.Tags.Where(t => t.DataType == "TIMER");
var results = content.Tags.Where(t => t.DataType == "TIMER");
```
### Modifying Content
### Modifying
Modifying components is simple as well.
The same component collection interface offers methods for adding,
removing, and replacing components.
Expand All @@ -82,115 +83,7 @@ var result = content.Tags.Find("MyTag").Replace(tag);
content.Save("C:\PathToMyOutputFile\FileName.L5X");
```

### More On Querying
Tags are unique components in that they have different scopes.
We have to specify a scope to know where to modify the existing collection.
For example, if you call `Tags().Add(tag)`, where do we add the tag?
This is why you have to specify a scope name for the tags collection when interacting with it.
The same applies to `Routine`.

But what if I just want to query across the entire file, how do I do that?
The answer is use the `Find<T>()` method and specify the type to query as the
generic argument.
```csharp
var allTagsInFile = content.Find<Tag>();
```
NOTE: `Find<T>()` just returns an `IEnumerable<T>`, so it is essentially read only,
but still allows you to form more complex queries using LINQ and the strongly typed
components in the library.


### Tag Data
Tag components also contain simple or complex data structures.
This library includes prebuilt `Atomic` and `Predefined` data structures
that allow in memory creation of tag data.

When creating a Tag component, initialize the `Value` property to get
a new instance of instantiated tag data for the type.

The following creates a tag with the predefined `TIMER` tag structure.
```csharp
var tag = new Tag
{
Name = "TimerTag",
Value = new TIMER()
};
```

You can then get members of the tag using the Member() or Members() methods.
```csharp
//Get the preset member of the timer tag.
var pre = tag.Member("PRE");

//Set the value of the DINT preset member.
pre.Value = 5000;

//Get all tag members.
var members = tag.Members();
```
When serialized back to the L5X, this component will include all the
tag data you have set on the component. Nice!

### Custom Data Types
The goal is to continue adding all predefined data structures, but if
one you require is missing, or if you have a user defined type that
you want to create, you can add them yourself.

Just inherit from `StructureType` and start creating type members like so.
```csharp
/// <summary>
/// A test type used to test nested complex data structure code
/// </summary>
public class MyNestedType : StructureType
{
public MyNestedType() : base(nameof(MyNestedType))
{
}

public override DataTypeClass Class => DataTypeClass.User;

/// <summary>
/// A simple boolean member
/// </summary>
public BOOL Indy { get; set; } = new();

/// <summary>
/// A string member
/// </summary>
public STRING Str { get; set; } = new();

/// <summary>
/// A nested timer member
/// </summary>
public TIMER Tmr { get; set; } = new();

/// <summary>
/// A nested user defined type
/// </summary>
public MySimpleType Simple { get; set; } = new();

/// <summary>
/// A nested array of atomic values.
/// Using this Logix.Array will create array filled with instantiated types.
/// This will aviod issues with the structure type members collection containing
/// null data for you members.
/// </summary>
public BOOL[] Flags { get; set; } = Logix.Array<BOOL>(10).ToArray();

/// <summary>
/// A nested array of structure types.
/// </summary>
public MESSAGE[] Messages { get; set; } = Logix.Array<MESSAGE>(10).ToArray();
}
```
IMPORTANT: When creating custom types, it is important to make them properties,
and initialize them straight away. Internally, StructureType will generate the
Members collection using reflection and adding all ILogixType members. If not initialized,
Members will contain null instances of the data types, which may cause you some
runtime exceptions when trying to access their members.

## Documentation
For further reading on Rockwell's import/export features,
see the following published document.
[Logix 5000 Controllers Import/Export](https://literature.rockwellautomation.com/idc/groups/literature/documents/rm/1756-rm084_-en-p.pdf)
See my project GitHub Page [here](https://tnunnink.github.io/L5Sharp/index.html) for more in depth articles and API documentation.

9 changes: 3 additions & 6 deletions src/.idea/.idea.L5Sharp/.idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit cc8226f

Please sign in to comment.