libplctag / libplctag.NET

A .NET wrapper for libplctag.
https://libplctag.github.io/
Mozilla Public License 2.0
194 stars 50 forks source link

ErrorNoData exceptions thrown by LibPlcTag are not able to be handled by Try/Catch. #348

Closed ryevdokimov closed 7 months ago

ryevdokimov commented 11 months ago

Hi All,

I am trying to intentionally create a list of tags that do not exist in the PLC but I am having trouble in doing so when using the ReadAsync method. Please let me know if I'm doing something wrong or misunderstanding something.

Error Details:

libplctag.LibPlcTagException HResult=0x80131500 Message=ErrorNoData Source=libplctag StackTrace: at libplctag.NativeTagWrapper.ThrowIfStatusNotOk(Nullable1 status) at libplctag.NativeTagWrapper.GetInt8(Int32 offset) at libplctag.Tag2.<.ctor>b__2_1(Object s, TagEventArgs e)

Code:

await Task.WhenAll(tags.Select(async tag =>
{
    try
    {
        await tag.ReadAsync();
    }
    catch (LibPlcTagException e)
    {
        if (e.Message == "ErrorNotFound")
        {
            failedTags.Add(tag.Name);
        }
        else
        {
            Console.WriteLine(e.Message);
        }
    }
}));
kyle-github commented 11 months ago

I am trying to understand your use case here. Do you have a list of tags you want to find on the PLC but some may not exist? If this is a Rockwell PLC you can list out the tags directly and cross check the list in your own code. That would probably generate a lot less network traffic. PLCs are very shy and delicate little pets, on the network :-) They do not have much capacity and do not handle heavy network load well compared to even a cheap PC.

ryevdokimov commented 11 months ago

My intention was to catch missing tags (if there are any) out of a couples thousand tags that I intend on constantly polling and display them to the user, so I'll be reading all the tags anyways. I haven't investigated much on listing out tags, would this still be efficient if I intend on polling about 2000 bools, out of a program that has hundreds of thousands of tags? I suppose if it is, I could just cross-check as you said before I even begin the reads.

kyle-github commented 11 months ago

I have not personally checked, but it is a rough observation that PLCs do not handle errors efficiently. It may cause performance problems on the PLC if you keep trying to read tags that do not exist. Again, I do not have proof of this, but it seems like I have seen higher CPU loads and slower scan times on PLCs where there is a lot of erroneous network traffic.

I would just list out the tags every hour or two (if the programs change a lot, less often if they are very static) and cross check manually. Your overall performance will increase as you won't be wasting precious network packet space with requests for tags that do not exist.

ryevdokimov commented 11 months ago

I don't intend on continuing to read these tags, in fact the plan is abort the tag reading process after displaying the issue to the user. The section of code in the initial post was simply to initialize the tags, get a first value, and to collect any issues with reading the tags. So, I'm trying to kill several birds with one stone. The problem is I can't seem to handle the error, so the whole program crashes meaning I couldn't even continue reading the tags even if I wanted to. If I change ReadAsync to something like await Task.Run(void () => tag.Read()); it works ok, but it's marginally slower for the WhenAll to complete due to not being entirely asynchronous.

ryevdokimov commented 11 months ago

I think my initial explanation may have been confusing. The program I am writing is not for the sole purpose to catch missing tags, this is just one of small functions to help the user understand issues during the initialization phase. The problem is I can't seem to get past this phase when using ReadAsync due to being unable to handle an exception thrown by this library when a tag is missing.

timyhac commented 8 months ago

Sidestepping the rationale behind the approach, I don't understand this: It sounds like an exception is thrown but a try-catch isn't catching it.

How do you know that the exception is thrown in the first place?

ryevdokimov commented 8 months ago

It is thrown as an unhanded exception by the Visual Studio debugger. The exact same exception can be caught with tag.Read(), but not with tag.ReadAsync(), so I assume that it is not being propagated up to where the try/catch is.

timyhac commented 7 months ago

I'm not sure how to reproduce this - I've developed some unit tests that look for this scenario and the behaviour seems right to me.

Perhaps the mock isn't faithfully reproducing the core library behaviour - are you able to supply debug logs?

CONTRIBUTIONS

ryevdokimov commented 7 months ago

I retested with my original program, but with the latest version of Visual Studio, and was unable to replicate. It must have been something to do with the exception settings in the IDE.