Skip to content

Commit

Permalink
docs and ArrayType Name update
Browse files Browse the repository at this point in the history
  • Loading branch information
tnunnink committed Jul 26, 2023
1 parent b84c82b commit f246a7a
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 153 deletions.
155 changes: 112 additions & 43 deletions docfx/index.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# L5Sharp
A C# library for interacting with Rockwell's L5X import/export files.
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,
that be used to build PLC projects files. 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.
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.
Expand All @@ -29,10 +28,16 @@ Load an L5X file using the primary entry point `LogixContent` class.
```c#
var content = LogixContent.Load("C:\PathToMyFile\FileName.L5X");
```
Get started by querying any type across the L5X using the `Find<T>()` method.
Get started by querying any type across the L5X using the `Find<T>()` and LINQ extensions.
```csharp
var tags = content.Find<Tag>();
```
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();
```
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.
Expand All @@ -44,92 +49,156 @@ The `LogixContent` class contains `LogixContainer` collections for all L5X compo
such as [Tag](xref:L5Sharp.Components.Tag), [DataType](xref:L5Sharp.Components.DataType),
[Moulde](xref:L5Sharp.Components.Module), and more.
These classes expose methods for querying and modifying the collections and components within the collections.
The following set of examples demonstrate these features using the Tags container.
The following set of examples demonstrate some of these features using the `Tags` container.

#### Querying
Component containers implement `IEnumerable<T>`, and hence support LINQ extensions such as `ToList()`,
`Where()`, and much more.

###### Get All
Gets a list of all tags.
- Get a list of all components in a container like so:
```c#
var tags = content.Tags.ToList();
```
>[!NOTE]
>`Tags` on the root `LogixContent` class is controller tags only.
>`Tags` on the root `LogixContent` class represent controller tags only.
> To get program specific tags, access the `Tags` container of a
> specific `Program` component.
###### Get Single
Get a tag by name.
- Perform complex filtering using LINQ expressions:
```c#
var tag = content.Tags.Get("MyTag");
var tags = content.Tags.Where(t => t.DataType == "TIMER" && t["PRE"].Value >= 5000);
```
Get a tag by index.

Aside from LINQ, the following are some built in ways to get or find components.

- Get a component at a specific index using the indexer property:
```c#
var tag = content.Tags[4];
```
Find a tag by name.

- Get a component by name using `Get()` and specifying the component name:
```c#
var tag = content.Tags.Get("MyTag");
```
>[!CAUTION]
> `Get()` will throw an exception if the component name was not found or more than one component with
> the specified name exists in the container. This is synonymous with `Single()` from LINQ.
- Find a component by name using `Find()` and specifying the component name:
```c#
var tag = content.Tags.Find("SomeTag");
```
>[!NOTE]
> Using `Find` will not throw an exception if the specified component is not found in
> the L5X. Rather, it will simply return `null`.
###### Filter
Perform complex filtering using LINQ.
```c#
var tags = content.Tags.Where(t => t.DataType == "TIMER" && t.Dimensions.IsEmpty && t["PRE"].Value >= 5000);
```
###### Add
Adds a new component to the container.
> the L5X. Rather, it will simply return `null`. `Find()` is synonymous with `FirstOfDefault()` from LINQ.
#### Modifying
`LogixContainer` implement methods for mutating the collections as well. Create new component objects in memory,
configure their properties, and add them to the container.

- Adds a new component to the container.
```c#
var tag = new Tag { Name = "MyTag", Value = 100 };
content.Tags.Add(tag);
```
Add a new list of components to the container.
>[!CAUTION]
> Components are not validated as they are created or added to a L5X container. Therefore, adding
> duplicate component names or components with invalid property values may results in import failures.
- Add a new list of components to the container.
```c#
content.Tags.AddRange(new List<Tag>
{
new() { Name = "tag01", Value = 100 },
new() { Name = "tag02", Value = 200 },
new() { Name = "tag03", Value = 300 }
new() { Name = "tag02", Value = new TIMER { PRE = 2000 } },
new() { Name = "tag03", Value = "This is a string tag value" }
});
```
###### Update
Updating properties of a component will directly update the underlying L5X content.
- Updating properties of a component will directly update the underlying L5X content.
```c#
var tag = content.Tags.Get("MyTag");
tag.Value = 1234;
tag.Description = "This is a tag's description";
```
You may also want to apply and update to all components in a collection.
- You may also want to apply and update to all components in a collection.
```csharp
content.Tags.Update(t => t.Comment = string.Empty);
```
Or better yet, update only components that satisfy a condition.
- Or better yet, update only components that satisfy a condition.
```csharp
content.Tags.Update(t => t.DataType == "MyType", t => t.Comment = "This is an instance ot MyType");
```
###### Insert
Insert a component at a specific position in the container.
```c#
content.Tags.Insert(3, new new Tag { Name = "MyTag", Value = 100 });
```
###### Remove
Remove a component by name.
- Remove a component by name.
```c#
content.Tags.Remove("MyTag");
```
Remove all components that satisfy a condition.
- Remove all components that satisfy a condition.
```csharp
content.Tags.Remove(t => t.DataType == "TypeName");
```
###### Save
Saving will write the current underlying L5X content to the specified file.
- Saving will write the updated L5X content to the specified file.
```c#
content.Save("C:\PathToMyOutputFile\FileName.L5X");
```
```

## Components

The following is a list of some of the _Logix_ components this library supports.
- DataType
- AddOnInstruction
- Module
- Tag
- Program
- Routine
- Task
- Trend
- WatchList
- ParameterConnection

For more information for each
component, you can read the article [Components] or review the [Api] documentation.

## Tag Data
This library supports static access and in memory creation of complex tag data structures.
The following example illustrates how this is done by creating a tag initialized with a `TIMER` structure,
and accessing it's `PRE` member statically.
```csharp
//Create a TIMER tag.
var tag = new Tag { Name = "Test", Value = new TIMER { PRE = 5000 } };

//Get PRE value statically
var pre = tag.Value.As<TIMER>().PRE;

//Assert that the value is correct.
pre.Shoud().Be(5000);
```
This library also comes built in with all atomic logix types (BOOL, DINT, REAL, etc.) and some predefined
logix types (TIMER, COUNTER, ALARM_ANALOG, PID, etc). You may also create your own user defined types to perform
the same operations as shown above.

For more information on tag data and these complex logix type objects, see the article [Tag Data].

## Extensions
Extending this library and it's components or objects is fairly straight forward. We simply make use
of C# extension methods. Since most objects implement `ILogixSerializable`, you will have access to the underlying XML,
This can be used as a means to write custom LINQ to XML queries or functions.

The following is an example of using an extension method and LINQ to XML to add a query that gets all `DataType`
components that are dependent on a specific data type name. In other words, data types that have members of the
specified data type name.

```csharp
public static IEnumerable<DataType> DependentsOf(this LogixContainer<DataType> dataTypes, string name)
{
return dataTypes.Serialize().Descendants(L5XName.DataType)
.Where(e => e.Descendants(L5XName.Member).Any(m => m.Attribute(L5XName.DataType)?.Value == name))
.Select(e => new DataType(e));
}
```
Here you see we first call `Serialize()` to get the attached `XElement` object. We then perform a LINQ to XML query
and finally return a materialized list of `DataType` component objects.

For more information on extending the library, see the article [Extensions].

## References
For more information regarding the structure and content of Rockwell's L5X file,
Expand Down
11 changes: 3 additions & 8 deletions docs/api/L5Sharp.LogixMember.html
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,11 @@ <h3 id="L5Sharp_LogixMember__ctor_System_String_L5Sharp_LogixType_" data-uid="L5
<h4 class="section">Parameters</h4>
<dl class="parameters">
<dt><code>name</code> <a class="xref" href="https://learn.microsoft.com/dotnet/api/system.string">string</a></dt>
<dd><p>The name of the member.</p>
<dd><p>The name of the member. If <code>null</code> will default to an empty string.</p>
</dd>
<dt><code>type</code> <a class="xref" href="L5Sharp.LogixType.html">LogixType</a></dt>
<dd><p>The <a class="xref" href="L5Sharp.LogixType.html">LogixType</a> object representing the member's data type data.</p>
<dd><p>The <a class="xref" href="L5Sharp.LogixType.html">LogixType</a> representing the member's data. If <code>null</code>
will default to <a class="xref" href="L5Sharp.Types.NullType.html">NullType</a>.</p>
</dd>
</dl>

Expand All @@ -208,12 +209,6 @@ <h4 class="section">Parameters</h4>



<h4 class="section">Exceptions</h4>
<dl class="parameters">
<dt><a class="xref" href="https://learn.microsoft.com/dotnet/api/system.argumentnullexception">ArgumentNullException</a></dt>
<dd><p>name or datatype are null.</p>
</dd>
</dl>



Expand Down
14 changes: 10 additions & 4 deletions docs/api/L5Sharp.Types.ArrayType-1.html
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ <h3 id="L5Sharp_Types_ArrayType_1__ctor_System_Array_" data-uid="L5Sharp.Types.A
<a class="header-action link-secondary" title="View source" href="https://github.com/tnunnink/L5Sharp/blob/main/src/Types/ArrayType.cs/#L299"><i class="bi bi-code-slash"></i></a>
</h3>

<div class="markdown level1 summary"><p>Creates a new array type from the provided logix array object.</p>
<div class="markdown level1 summary"><p>Creates a new array type from the provided array object.</p>
</div>
<div class="markdown level1 conceptual"></div>

Expand All @@ -221,7 +221,8 @@ <h3 id="L5Sharp_Types_ArrayType_1__ctor_System_Array_" data-uid="L5Sharp.Types.A
<h4 class="section">Parameters</h4>
<dl class="parameters">
<dt><code>array</code> <a class="xref" href="https://learn.microsoft.com/dotnet/api/system.array">Array</a></dt>
<dd><p>An array of logix types</p>
<dd><p>An array of logix types. Array can not be empty, contain null items,
or objects of different logix type.</p>
</dd>
</dl>

Expand All @@ -236,10 +237,15 @@ <h4 class="section">Parameters</h4>
<h4 class="section">Exceptions</h4>
<dl class="parameters">
<dt><a class="xref" href="https://learn.microsoft.com/dotnet/api/system.argumentnullexception">ArgumentNullException</a></dt>
<dd><p><code>array</code> or any element of <code>array</code> is null.</p>
<dd><p><code>array</code> is null.</p>
</dd>
<dt><a class="xref" href="https://learn.microsoft.com/dotnet/api/system.argumentexception">ArgumentException</a></dt>
<dd></dd>
<dd><p><code>array</code> contains no elements, <code>null</code> or <code>NullType</code> elements,
or objects with different logix type names.</p>
</dd>
<dt><a class="xref" href="https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception">ArgumentOutOfRangeException</a></dt>
<dd><p><code>array</code> rank is greater than 3 dimensions.</p>
</dd>
</dl>


Expand Down
20 changes: 13 additions & 7 deletions docs/api/L5Sharp.Types.ArrayType.html
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,10 @@ <h2 class="section" id="constructors">Constructors

<h3 id="L5Sharp_Types_ArrayType__ctor_System_Array_" data-uid="L5Sharp.Types.ArrayType.#ctor(System.Array)">
ArrayType(Array)
<a class="header-action link-secondary" title="View source" href="https://github.com/tnunnink/L5Sharp/blob/main/src/Types/ArrayType.cs/#L31"><i class="bi bi-code-slash"></i></a>
<a class="header-action link-secondary" title="View source" href="https://github.com/tnunnink/L5Sharp/blob/main/src/Types/ArrayType.cs/#L35"><i class="bi bi-code-slash"></i></a>
</h3>

<div class="markdown level1 summary"><p>Creates a new array type from the provided logix array object.</p>
<div class="markdown level1 summary"><p>Creates a new array type from the provided array object.</p>
</div>
<div class="markdown level1 conceptual"></div>

Expand All @@ -198,7 +198,8 @@ <h3 id="L5Sharp_Types_ArrayType__ctor_System_Array_" data-uid="L5Sharp.Types.Arr
<h4 class="section">Parameters</h4>
<dl class="parameters">
<dt><code>array</code> <a class="xref" href="https://learn.microsoft.com/dotnet/api/system.array">Array</a></dt>
<dd><p>An array of logix types</p>
<dd><p>An array of logix types. Array can not be empty, contain null items,
or objects of different logix type.</p>
</dd>
</dl>

Expand All @@ -213,10 +214,15 @@ <h4 class="section">Parameters</h4>
<h4 class="section">Exceptions</h4>
<dl class="parameters">
<dt><a class="xref" href="https://learn.microsoft.com/dotnet/api/system.argumentnullexception">ArgumentNullException</a></dt>
<dd><p><code>array</code> or any element of <code>array</code> is null.</p>
<dd><p><code>array</code> is null.</p>
</dd>
<dt><a class="xref" href="https://learn.microsoft.com/dotnet/api/system.argumentexception">ArgumentException</a></dt>
<dd></dd>
<dd><p><code>array</code> contains no elements, <code>null</code> or <code>NullType</code> elements,
or objects with different logix type names.</p>
</dd>
<dt><a class="xref" href="https://learn.microsoft.com/dotnet/api/system.argumentoutofrangeexception">ArgumentOutOfRangeException</a></dt>
<dd><p><code>array</code> rank is greater than 3 dimensions.</p>
</dd>
</dl>


Expand All @@ -225,7 +231,7 @@ <h4 class="section">Exceptions</h4>

<h3 id="L5Sharp_Types_ArrayType__ctor_System_Xml_Linq_XElement_" data-uid="L5Sharp.Types.ArrayType.#ctor(System.Xml.Linq.XElement)">
ArrayType(XElement)
<a class="header-action link-secondary" title="View source" href="https://github.com/tnunnink/L5Sharp/blob/main/src/Types/ArrayType.cs/#L60"><i class="bi bi-code-slash"></i></a>
<a class="header-action link-secondary" title="View source" href="https://github.com/tnunnink/L5Sharp/blob/main/src/Types/ArrayType.cs/#L62"><i class="bi bi-code-slash"></i></a>
</h3>

<div class="markdown level1 summary"><p>Creates a new <a class="xref" href="L5Sharp.Types.ArrayType.html">ArrayType</a> initialized from the provided <a class="xref" href="https://learn.microsoft.com/dotnet/api/system.xml.linq.xelement">XElement</a> data.</p>
Expand Down Expand Up @@ -580,7 +586,7 @@ <h3 id="L5Sharp_Types_ArrayType_Radix" data-uid="L5Sharp.Types.ArrayType.Radix">
<a class="header-action link-secondary" title="View source" href="https://github.com/tnunnink/L5Sharp/blob/main/src/Types/ArrayType.cs/#L96"><i class="bi bi-code-slash"></i></a>
</h3>

<div class="markdown level1 summary"><p>Gets the radix format of the the atomic type array.</p>
<div class="markdown level1 summary"><p>Gets the radix format of the the array type elements.</p>
</div>
<div class="markdown level1 conceptual"></div>

Expand Down
Loading

0 comments on commit f246a7a

Please sign in to comment.