libplctag / libplctag.net Goto Github PK
View Code? Open in Web Editor NEWA .NET wrapper for libplctag.
Home Page: https://libplctag.github.io/
License: Mozilla Public License 2.0
A .NET wrapper for libplctag.
Home Page: https://libplctag.github.io/
License: Mozilla Public License 2.0
All the tests need to check for is that a DllNotFoundException isn't thrown, it doesn't need to communicate with a PLC.
Could supply a mock dll that implements the plctag interface but outputs to console or something.
When a developer needs to make use of an update to the libplctag.NativeImport package that includes updated native runtimes, they will need to manually delete the existing plctag.dll/libplctag.so/libplctag.dylib.
New applications do not suffer from this issue because they will always extract the correct runtime.
NativeImport only replaces the runtime if it does not already exist on disk.
Furthermore, Visual Studio Build->Clean does not delete these files, it only deletes files it has created, so the developer must manually delete these files.
A separate, but related, issue is when application developers change their application's target platform. The target platform needs to match the native runtime architecture.
Some options
I wanted to break these out from #66 as that is getting a bit long!
Do you need to have constructors for tags within the TagGroup class? This seems like it may unnecessarily couple Tags and TagGroups. Perhaps I am missing something subtle here. At least in my mental model of this, Tags would be created independently and then grouped after they were created. My mental model also allows a Tag instance to be in more than one group at a time. It seems like that is possible here too.
On the read/write methods, does the break out of two kinds of async help? Perhaps I am just not seeing common use cases here. At least in my own code (the plural of anecdote is not data!), I either use blocking with a timeout or I do all the timeout management myself. The difference seems to be in the existence of the timeout parameter. What happens if you provide a timeout parameter? Does it automatically throw an exception on timeout? On the blocking method, what happens on timeout?
I am not used to C# async code, so if this model is a common idiom, then let me know and I'll shut up and go away :-)
First of all, thanks for the great work on libplctag and libplctag.NET.
#72 updated InitializeAsync
, ReadAsync
and WriteAsync
but it did not update the 2 occurrences of token
in the inner using
:
using (token.Register(() => Abort()))
{
while (GetStatus() == Status.Pending)
{
await Task.Delay(ASYNC_STATUS_POLL_INTERVAL, token);
}
}
token
should be replaced with cts.Token
:
using (cts.Token.Register(() => Abort()))
{
while (GetStatus() == Status.Pending)
{
await Task.Delay(ASYNC_STATUS_POLL_INTERVAL, cts.Token);
}
}
to have the task canceled after the configured time out.
We know that Task.Delay()
throws an TaskCanceledException
when the time-out expires. Wouldn't it be better to catch the TaskCanceledException and throw a LibPlcTagException(Status.ErrorTimeout)
instead?
using (cts.Token.Register(() => Abort()))
{
while (GetStatus() == Status.Pending)
{
try
{
await Task.Delay(ASYNC_STATUS_POLL_INTERVAL, cts.Token);
}
catch (TaskCanceledException)
{
throw new LibPlcTagException(Status.ErrorTimeout);
}
}
}
PS: #96 might also be related to this issue, I experienced similar behavior while I was testing.
There is support for a read cache to speed up performance. Expose this in the Tag constructor and as a property getter/setter
https://github.com/libplctag/libplctag/wiki/Tag-String-Attributes
I am working on a project to get the list of tag available inside a AB PLC controller. The tag available is shown in the diagram below.
In this project, I am using ethernet connection to connect PLC with my laptop. The IP was 192.168.236.20. Below is the code that I deployed to get list of tags.
`var tags = new Tag<TagInfoPlcMapper, TagInfo[]>()
{
Gateway = "192.168.236.20",
Path = "1,0",
PlcType = PlcType.ControlLogix,
Protocol = Protocol.ab_eip,
Name = "TestDINTArray[0]"
};
tags.Read();
ConsoleTable
.From(tags.Value.Select(t => new
{
t.Id,
Type = $"0x{t.Type:X}",
t.Name,
t.Length,
Dimensions = string.Join(",", t.Dimensions)
}))
.Configure(o => o.NumberAlignment = Alignment.Right)
.Write(Format.Default);`
However, when I run the program, I got nothing in result. What is the possible root cause of this?
When the native libplctag recieves a timeout value of 0, it interprets that as the user to use non-blocking mode.
The API that this wrapper exposes to its consumes indicates that when using Read/Write/Initialize they are using a synchronous (blocking) mode. There are dedicated methods suffixed with Async for the asynchronous mode.
Arguably, a timeout of 0 is incompatible with blocking methods because they will always take a non-zero amount of time.
Is this acceptable or should it throw a relevant exception?
I'm working with Kyle (libplctag author) to try to create a github organization for libplctag and the associated wrappers for different languages. I'm primarily a C# programmer and I was planning on starting from the Corsinvest wrapper to build up something a little more featureful that also included the newer features Kyle had added. However, I quickly realized the Corsinvest code is GPL3 and not particularly useful for me in a professional sense.
After some discussion, Kyle is planning on re-licensing libplctag under the MPL. This is a nice balance between some copyleft but still allowing for people to compile the code directly into other binaries.
Would you consider re-licensing your code as MPL and transferring it to the libplctag organization? I suppose it could also be kept as MIT if you feel strongly about it, but I'd prefer the MPL if I'm going to fork it. Also, would you be interested in helping maintain the C# wrapper within the organization going forward?
I found an issue with the original libplctag code where it would not allow you to read from tag-based alarm tags. I notified the original developers/supporters of libplctag library and they have fixed the issue. I reached out to the c# wrapper library developer on github and they are no longer supporting their code and pointed me to here.
Can you include the new changes to the libplctag to this wrapper so that people can read tag-based alarms?
Thanks,
Here is the original thread from the original libplctag git-hub.
We have the base types working in #50, now we need to implement the Arrays of base types. I've started work in branch generic-typed-arrays
.
When a PLC is disconnected, the Initialize method seems to take about 15 seconds to timeout. It seemed to me in the documentation of the code that it would use whatever value is set to the Timeout property. That Timeout property seems to work as expected for read/write timeouts if I unplug the PLC while my program is running, but it doesn't seem to for the first Initialize timeout if I start my program with the PLC disconnected.
It is possible to retrieve the version number of the library by using the get_attribute function.
Expose this as a static function on the Tag object.
https://github.com/libplctag/libplctag/wiki/API#tag-internal-attribute-functions
Also expose plc_tag_check_lib_version
This may be useful to libraries looking to build on top of this one.
Hi,
When I run .NET core project, the code is running fine. However, when I try to run on .NET framework project, it gave an error on this. I am running at "Any CPU" with an option "Prefer 32-bit" at ".NET Framework 4.7.2".
System.BadImageFormatException: 'An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)'
I'm having this error log when trying to get the plc tag on Raspbian 10.
The same app compiled for windows does not have this problem.
2020-11-26 14:43:56.994 -03:00 [ERR] System.TypeLoadException: This platform is not supported, could not load appropriate unmanaged library
at libplctag.NativeImport.LibraryExtractor.GetResourceName()
at libplctag.NativeImport.LibraryExtractor.ExtractAppropriateLibrary(String folder)
at libplctag.NativeImport.LibraryExtractor.Init(Boolean forceExtract)
at libplctag.NativeImport.plctag.ExtractLibraryIfRequired()
at libplctag.Tag.Initialize()
at libplctag.Tag`2.Read()
On the original libplctag documentation says that RaspberryPi is supported. Do it mean that libplctag.NET supports it too?
would plctag.ForceExtractLibrary = false;
solve this issue?
The Os details are the following
The NuGet package is
The current class constructor provided by Tag
is restrictive, and has the following issues:
@tags
does not take an element size or count, but the class forces the developer to use count of 1 if they don't supply another number - some string types might not work with a defined element size.The primary drive behind having constructor as it currently stands is to force the application developer to use the library properly, but a lot of validation is being done by the core library (and is now being flagged by copious status checking).
A proposal could be:
This comes up every couple of weeks either in email directly to me or on the forum. I think it would be really great if someone could make an example in C# or VB that listed tags. I know that several users have used one of the C# wrappers and done this by looking at what the C code does.
It feels like the Tag object should use a generic so that I can just new Tag<INT>
and not have to know which special method to call when I do a read/return against that tag. We can keep the current Tag object as a base type and implement this on top.
Thoughts?
Is reading strings from a MicroLogix (SLC plc type) supported?
I'm reading from and writing to other data types fine, but when reading a string, e.g. ST102:0, I get the error PLCTAG_ERR_TOO_SMALL. Element size is 88, so seems right, confused why the error.
I haven't fully isolated this one, but I built a ASP.NET Core Windows Service and found out the hard way that it won't extract the plctag.dll
during service startup.
I'm assuming this might be do to the permissions the service is running as. For now I just have to include plctag.dll
in my build as a file.
During some testing I realized that if I attempt InitializeAsync or ReadAsync with no network connection to the PLC, it hangs indefinitely (no timeout).
Sorry guys, dumb question: I am really close to figuring out how to dispose of C-style string data allocated in the native DLL for Java and I have been poking around a bit but my Google fu is weak today; can .Net do this too?
The reason I am asking is that I think I can make a C string API that is much, much easier to use than what I came up with before. But it requires that I return a newly allocated C string. Here is what the new string API would look like:
char *plc_tag_get_string(int32_t tag_id, int string_start_offset);
int plc_tag_set_string(int32_t tag_id, int string_start_offset, const char *string_val);
int plc_tag_get_string_capacity(int32_t tag_id, int string_start_offset);
int plc_tag_get_string_total_length(int32_t tag_id, int string_start_offset);
It is that first function that is the problem child. I have to allocate memory in order for this to really be easy to use. But then the caller needs to free it. I am about 99% certain that I have figured out how to do this in Java, I want to make sure that it can be done in .Net.
Can it be done without both of you tearing your hair out?
C# APIs generally throw errors rather than returning status codes. This ticket requests that all operations are checked for errors and then throw the appropriate Exception.
We've had a couple requests for source code for a particular release. I think Nuget will always be the primary way the package is consumed, but it's probably worth adding releases/tags to our build script.
Currently does not support .NET Framework.
Workaround is to manually copy plctag.dll into same directory as libplctag.dll
Only windows is supported
Currently a fake file
https://github.com/timyhac/libplctag.NET/blob/master/runtimes/linux-x86/native/plctag.so
Are we able to pull out all the tag info from UDT?
If you try to instantiate a tag with the proper Name but the wrong Datatype (ex. "SomeInt" as a DINT) the native.GetInt32() returns the minimum value to signify an error and liblctag.NET throws an exception with no message.
It'd be nice to improve on this behavior to help debug.
Good: "The tag definition is invalid"
Better: "Cannot call GetInt32 on an Int16 tag"
We should determine what the next steps are for the c# wrapper now that it has been incorporated into the libplctag organization.
I feel a good place to start with is agreeing on what the goals should be. Maybe these could be similar goals to wrappers for other languages, maybe not. I have some goals written on the README for this project but definitely open to changing these goals.
One issue with the current goals is that we can simplify how this works because we no longer have to support the ability for end-users to supply their own plctag dll with statically linked projects.
When protocol is left null (the default value, due to it being nullable) the attribute string is calculated as "protocol=", when in fact it should not appear at all.
While testing the examples I ran one that was trying to hit a tag at a 192 IP address. Instead of any sort of error message it simply sat in the StatusPending loop. I tried again and waited quite a bit to see if it would timeout, but no luck.
The examples run with a keyword default
value for timeout on the tag constructors. I'm guessing this default value is infinity. However, there wasn't any device at the IP we were attempting to connect to so I would expect this to fail immediately and not wait on a timeout.
Since I just managed to put a broken version of 2.1.7 out on master, I will take this a kick in the pants to actually redo my branch use that I've been thinking about for a while.
Starting soon (probably this weekend), I will create some new branches in the core library repo:
release - the real main branch. Nothing goes here except tested code that has "soaked" for a while as a pre-release unless it is an urgent bug fix or such a narrow change that the risk is small. I will make this the default branch so that when people check out the code without specifying a branch, this is what they get.
prerelease - once I am done with my development and tests, I will merge to prerelease. All prerelease drops will come from this branch. When these are validated with more testing and a lack of user bugs, I will merge this over to release.
I am not sure what to do with the master branch. I will try to keep it up to date with release for a while, but generally I would like to drop it.
How much heartburn will this give you guys?
The primary goal of this project is to provide a package that is easy to consume for .NET projects. This means that publishing to nuget is high on the ToDo list.
Latest two builds (that used github actions) don't include the native runtimes (and therefore don't work).
Hi.
I started to uses your wrapper. In that time libplctag.0.0.27-alpha13 were latest release so is still in use on my side
Today I have some doubts if understand your examples, or there is some bug.
On simple example bellow I see that tag after .Dispose() has still status .isInitialized = true.
Is there a way how to Dispose, Release Tag ? Am i doing this in correct way ?
Tag sStringOut_LEN = new Tag
{
Gateway = "192.168.7.100",
Path = "1,0",
PlcType = PlcType.ControlLogix,
Protocol = Protocol.ab_eip,
Name = $"PC_DATA.String_In.LEN",
ElementSize = 4,
ElementCount = 1
};
// Tag.isInitialized = false
sStringOut_LEN.Initialize(5000);
// Tag.isInitialized = true
sStringOut_LEN.Read(5000);
var someData = sStringOut_LEN.GetInt32(offset: 0);
// Tag.isInitialized = true
sStringOut_LEN.Dispose();
// Tag.isInitialized = true . Why ??
This is an extension of the latest merge #73
We need to support 2D and 3D arrays.
I'm getting an exception when I set a value in the PLC to the lower bound of the data type. For example, setting TestSINT.7 to 1 with all other bits set to 0 will result in this exception. I have had this happen with SINTs and DINTs. I haven't investigated if other data types have the same error. All other reads and writes are working fine, it's only when I have only the last bit set on a PLC tag.
Exception of type 'libplctag.LibPlcTagException' was thrown.
at libplctag.Tag.GetInt8(Int32 offset)
at libplctag.DataTypes.SintPlcMapper.Decode(Tag tag, Int32 offset)
at libplctag.DataTypes.PlcMapperBase`1.DecodeArray(Tag tag)
at libplctag.DataTypes.PlcMapperBase`1.libplctag.DataTypes.IPlcMapper<T[]>.Decode(Tag tag)
at libplctag.Tag`2.DecodeAll()
at libplctag.Tag`2.Initialize()
at [MyMethod].ReadPLCData(LptLink link) in [MyMethod].cs:line 293
My ReadPLCData method just initializes the tag with a tagname passed in, reads it, and returns the data converted to an array of strings.
{libplctag.Tag<libplctag.DataTypes.SintPlcMapper, sbyte[]>}
Please let me know if you need any more information on how to recreate.
Many users will be using VB.Net in their projects. Provide a VB.Net example.
The tag attribute string supports connected messaging. Expose this in the Tag constructor.
https://github.com/libplctag/libplctag/wiki/Tag-String-Attributes
The design of the DataType class is unusual. Its main purpose currently is to provide element size of various Tag types.
Another time where Tag type is important is during Tag listing, where the underlying integer values are different as they refer to CIP types vs element size. (See this comment)
Is it possible to reduce the DataType to a basic enum (as opposed to a class of static values), and have some mapping elsewhere that provides the appropriate element size?
Proposal would be something like this:
public enum DataType
{
SINT,
INT,
DINT,
REAL,
....
}
The Tag constructor could take either a DataType enum or a data size (separate constructors).
Other concerns:
New APIs are available in the core dll. Expose these functions to end users.
try red
"var tag4 = new Tag("192.168.0.100", CpuType.SLC, "O0:0.0", DataType.Int16, 5);"
return error code =29
Currently the consumer of this project must supply their own plctag.dll.
Ideally all they have to do is install this package from nuget and the dll copying is taken care of.
If timeouts are provided on Write() and Read() in libplctag, the function returns immediately and is queued for later. Expose this to the wrapper in an idiomatic way.
Using plc_tag_set_int_attribute it is possible to set debugLevel after instantiation. Expose this via a property setter on the DebugLevel property
https://github.com/libplctag/libplctag/wiki/API#tag-internal-attribute-functions
One of my co-workers discovered this and it's a bit annoying.
The way we implemented the StringPlcMapper
if you write a string like "12345" it will write a value of 5 to the string.len
tag and then it will write 5 bytes into the actual byte array for the string.
The issue comes about if you then write a shorter string, "abc". The string.len
will get updated to 3 and the first three bytes will be overwritten. BUT the value for the string array in the PLC will now be [a,b,c,4,5,\0,\0...] and this confuses some PLC functions and external programs.
I fixed this locally by creating a new, local mapper in my project that always writes the full max string length - padding it out with zeros. But it raises some questions:
@kyle-github and @timyhac - What do you guys think?
I have a list of tag variable in an UDT as shown below. For such case, if I would like to create my own PlcMapper, is the code below correct? As the example used only involved in DINT, not SINT, therefore I would like to confirm if my written code is correct for this situation or not.
var DINT0 = tag.GetInt32(0)
var DINT1 = tag.GetInt8(4)
var DINT2 = tag.GetInt8(5)
MoveIndex = DINT0;
BitArray bitArray1 = new BitArray(new int[] { DINT1 });
MoveAbsPB = bitArray1[31];
OS_MoveAbs = bitArray1[30];
...
BitArray bitArray2 = new BitArray(new int[] { DINT2 });
OS_Jog = bitArray2[31];
SV_OnPB = bitArray2[30];
...
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.