Skip to content

Commit

Permalink
Added Nuget package README files
Browse files Browse the repository at this point in the history
  • Loading branch information
timyhac committed Jul 11, 2024
1 parent 307d061 commit be1c9e3
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 62 deletions.
106 changes: 44 additions & 62 deletions docs/libplctag.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
# libplctag.NET

[libplctag](https://github.com/libplctag/libplctag) is a library provides a portable and simple API for accessing Allen-Bradley and Modbus PLC data over Ethernet.
[libplctag](https://github.com/libplctag/libplctag) is a C library for Linux, Android, Windows and macOS that uses EtherNet/IP or Modbus TCP to read and write tags in PLCs.

[libplctag.NET](https://www.nuget.org/packages/libplctag/) provides wrapper packages for libplctag, with an API naturalised to .NET by supporting the following features:
[libplctag.NET](https://www.nuget.org/packages/libplctag/) provides wrapper packages for libplctag, with an API naturalised to .NET by adding the following features:

* Values are strongly-typed (both Atomic types and User-Defined Types).
* Errors are thrown as Exceptions
* Async/Await
* Native resource cleanup

Except for the above, all of the libplctag functionality is provided by the core libplctag library.


## Getting Started

Add the libplctag package in the Visual Studio Nuget Package Manager UI or via the commandline:

`> dotnet add package libplctag`

Then add the following in a C# application:
## How to use

```csharp
// Example tag configuration for a global DINT tag in an Allen-Bradley CompactLogix/ControlLogix PLC
Expand All @@ -41,67 +32,53 @@ output = myTag.Read();
Console.WriteLine($"Updated value: {output}");
```


## Introduction

Libplctag does a great job at abstracting many of the details of Ethernet/IP and Modbus, there are still some aspects of these network protocols that library users must understand.

The manuals provided by your device manufacturer are the best source of information on these details.

### Gateway and Path
Gateway refers to the IP Address or hostname
Usually this is the IP Address of the PLC CPU or network module.
Ethernet/IP communication is routed through a series of devices, so the `path` configures that routing.

### Name
libplctag converts the `name` string into a format appropriate for the network protocol.

Some examples where
* Program tags
* Array elements
* Unions
* Struct members
Before describing the concrete parts of the API a brief explanation of the model is needed.
The library models each tag, or field within a tag, as an individual handle that your program can use.
There is no exposed concept of a PLC.
Just tags.

### Memory layout
For
A tag is a local reference to a region of PLC memory.
Depending on the PLC type and protocol the region may be named.
For some protocols, the region is simply a type and register number (e.g. Modbus).
For other protocols, it is a name, possible array element, field names etc. (e.g. a CIP-based PLC).

The library provides functions to retrieve and set the values in the PLC's memory as well as many functions to extract and set specific data elements within a tag.
Note that a tag may not correspond directly to a named area in a PLC.
For instance, MyNiftyArray[14] with a length of 2 elements will get elements MyNiftyTag[14] and MyNiftyTag[15] from the tag MyNiftyTag.

Tags are designed to be very long lived.
They can live weeks or months.
In order to use the library to its full potential, it is important to note this.
Setting up and tearing down connections to a PLC are the heaviest operations possible.
They take significant time and multiple packets to handshake the connection set up.
The library automatically closes PLC connections after periods of no use (typically five seconds) and automatically reopens them when you try to do something with a tag.

Performance Tip: Avoid creating and destroying tags often.

Your program directly controls the lifetime of a handle.
It opens a handle to a PLC tag through the `Initialize()` function and frees the resources used with the `Dispose()` function.
All other resources are handled internally within the library.
You will likely run into system constraints such as available memory, network bandwidth and CPU long before exhausting the internal limits of the library.

### Network Protocol (Ethernet/IP Explicit Messaging and Modbus)
The lowest level of access to a tag is via the `Read()` and `Write()` operations.
In most cases you must explicitly call these functions to write to the PLC or read from the PLC.
There are also attributes that can be passed when creating a tag handle to make it either automatically write to the PLC when the local copy of the tag is updated or read from the PLC periodically, or both.
See the wiki page on auto sync tag string attributes for more information.

Ethernet/IP explicit messaging is the protocol used by libplctag to communicate
It is important to note that using libplctag does not preclude you from understanding the Ethernet/IP API offered by your PLC system.
## Mappers and `Tag<M,T>`

The most obvious example of this is how to connect to the target device; i.e. `Gateway` and `Path`.
In your .NET application, you will usually need to convert the raw bytes into a .NET type.
It is possible to use `GetInt32()` and `SetInt32()` (and [others](https://github.com/libplctag/libplctag/wiki/API#tag-data-accessors)) provided by the `Tag` class to perform this conversion.
Most of the time however, there will only be one sensible way to interpret the bytes for a given tag.

For example, a `DINT` tag defined in a PLC is a 32bit signed integer, whose natural counterpart in C# would be an `int` type.
This data is exposed as a 4 byte array that represents a little-endian encoded signed integer.
It would be rare to want to interpret this data instead as a `float`, or a 4-byte ASCII string.

One common example of this is accessing String tags.
String tags are a special case of a STRUCT that vary significantly across devices and so it is necessary to know how the String is encoded in memory.

In general, when working with any UDTs you will need to reverse-engineer the structure.
An example can be found [here](src/Examples/CSharp%20DotNetCore/SequencePlcMapper.cs).

Another example of this is how the Tag name influences the memory layout.
For example, a BOOL Array Tag on Omron PLCs will present different memory layout when the name is `MyBoolArray` versus `MyBoolArray[0]`.

## Concepts

### Tag

A tag is the primary concept in libplctag.
It provides a way to read and write to tags in a PLC.

The Tag value is exposed as a byte array, which can be
* Read from the PLC to the client with `Read()`.
* Manipulated on the client through the getter/setter functions such as `GetInt32(int offset)` / `SetInt32(int offset, int value)`.
* Written back to the PLC with `Write()`.

### Mapper
A mapper handles the mapping between a .NET type (e.g. `int`, `float`) and the PLC type (e.g. `DINT`, `REAL`) by calling the appropriate functions on a Tag.
For example, in a PLC a tag could be defined of type `DINT` - a 32bit signed integer, and the natural counterpart in C# would be an `int` type.
The Mapper would encapsulate the calls to `GetInt32(int offset)` and `SetInt32(int offset, int value)` as well as providing other information that libplctag needs for this mapping.
To this end, libplctag.NET offers a typed tag class `Tag<M,T>` that exposes the tag value as a C# type instead of the complete set of Getter/Setter functions.
It pairs with a "Mapper", which encapsulates the mapping between a .NET type (e.g. `int`, `float`) and the PLC type (e.g. `DINT`, `REAL`) by calling the appropriate functions on a Tag (as well as providing other information that libplctag needs for this mapping).

Mappers are implemented as an [`IPlcMapper`](src/libplctag/DataTypes/IPlcMapper.cs) and are used in a `Tag<M,T>` to provide an interface that is convenient for application developers to to work with.

Expand All @@ -122,6 +99,12 @@ myTag.Value = 1234;
myTag.Write();
```

In general, you will need prior knowedge of the structure of the tag data, and you may need to reverse-engineer it.
An example for reverse engineering a UDT can be found [here](src/Examples/CSharp%20DotNetCore/SequencePlcMapper.cs).

Because the structure of the data depends on many factors (PLC Make/model, Protocol, even Tag Name), libplctag.NET does not provide built-in Mappers for all types.
The manuals provided by your device manufacturer are the best source of information on these details.

## `libplctag` namepsace
* `Tag` - A wrapper around the core libplctag library tag by providing an interface naturalised to .NET.
* `Tag<M,T>` - A tag type that exposes a .NET type (generic parameter `T`) by delegating data access logic to an `IPlcMapper` (generic parameter `M`).
Expand All @@ -141,7 +124,6 @@ Of note are [TagInfoPlcMapper](src/libplctag/DataTypes/TagInfoPlcMapper.cs) and
* [`TagLreal2D`](src/libplctag/DataTypes/Simple/Definitions.cs#L41)
* ... and so on


## Example

For more detail and further usage, see the examples in the example projects:
Expand Down
2 changes: 2 additions & 0 deletions src/libplctag.NativeImport/libplctag.NativeImport.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageProjectUrl>https://github.com/libplctag/libplctag.NET</PackageProjectUrl>
<PackageReadme>README.md</PackageReadme>
</PropertyGroup>

<ItemGroup>
Expand All @@ -28,6 +29,7 @@
<EmbeddedResource Include="runtime\win_ARM64\plctag.dll" />
<EmbeddedResource Include="runtime\win_ARM\plctag.dll" />
<None Include="$(PackageIconFullPath)" Pack="true" PackagePath="\" />
<None Include="..\..\docs\libplctag.NativeImport.md" Pack="true" PackagePath="\README.md"/>
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/libplctag/libplctag.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageProjectUrl>https://github.com/libplctag/libplctag.NET</PackageProjectUrl>
<PackageReadme>README.md</PackageReadme>
<PackageTags></PackageTags>
</PropertyGroup>

Expand All @@ -23,6 +24,7 @@

<ItemGroup>
<None Include="$(PackageIconFullPath)" Pack="true" PackagePath="\" />
<None Include="..\..\docs\libplctag.md" Pack="true" PackagePath="\README.md"/>
</ItemGroup>

</Project>

0 comments on commit be1c9e3

Please sign in to comment.