Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot Declare Array[0..15] Bool #437

Closed
zN3utr4l opened this issue Jan 27, 2025 · 8 comments
Closed

Cannot Declare Array[0..15] Bool #437

zN3utr4l opened this issue Jan 27, 2025 · 8 comments

Comments

@zN3utr4l
Copy link

tag = new Tag<M, T>
{
    Name = itemModel.ItemID,

    Gateway = configuration.ServerIp,

    Path = configuration.ServerPath,

    PlcType = configuration.PlcType == Enums.EEIPPlcType.CompactLogix ? PlcType.ControlLogix : (PlcType)configuration.PlcType,

    Protocol = configuration.Protocol,

    Timeout = TimeSpan.FromMilliseconds(configuration.ItemTimeOut),
};

if (itemModel.IsArray)
{
    tag.ArrayDimensions = [(int)itemModel.Dimension];
}

tag.Initialize();

<VariableArray itemId="Allarms" dataType="BOOL" dimension="16" />

I have this tag, a bool array of 16 positions, which when I try to monitor gives me the error: OutOfBound, but if I declare the array of size 32 at plc level and go to monitor the 16, as before, it works.

@kyle-github
Copy link
Member

BOOL arrays are very confusing in the PLC itself. Rockwell creates them internally as a set of 32-bit integers. Each bit is one BOOL. Unfortunately, Rockwell did not translate that outside the PLC so the PLC treats a BOOL array as an array of 32-bit integers with a length of the number of BOOL elements divided by 32 and a size rounded up.

That means that if you create a BOOL array of 10 elements, you will actually get a BOOL array of 32 elements but the PLC and the CIP network protocol will show this as an array of one element of 32-bits. A BOOL array of 33 elements will show up as a 32-bit integer array of 2 elements.

So take the number of BOOL elements, divide by 32, and round up.

BOOL[10] -> UDINT[1]
BOOL[32] -> UDINT[1]
BOOL[36] -> UDINT[2]
BOOL[64] -> UDINT[2]
BOOL[90] -> UDINT[3]
BOOL[99] -> UDINT[4]
...

Even more unfortunately, Omron does not do the same thing. Omron uses 16-bit integers as the base element, but internally does NOT pad unused bytes within that 16-bit integer. We have seen garbage bits in the unused space.

@kyle-github
Copy link
Member

I forgot to mention that you need to set the number of elements in the tag (in the .NET code creating the tag) to the number of UDINT elements, NOT the number of BOOL elements.

@timyhac
Copy link
Collaborator

timyhac commented Jan 27, 2025

We attempted to encode this logic into a BoolMapper, but have found it only works in some very specific scenarios. Bool arrays were one of the reasons I decided to deprecate (and eventually remove) the Mapper system entirely.

In terms of debugging this following Kyle's advice you could create a Tag as follows:

Tag tag = new()
{
    Name = "Allarms",
    Gateway = ...,
    Path = ...,
    PlcType = PlcType.ControlLogix,
    Protocol = Protocol.ab_eip,
    ElementSize = 4,
    ElementCount = 1,
};

tag.Initialize();
tag.SetBit(3, true);       // Sets "Allarms[3]" to true
tag.Write();

If you want to continue to use the Mapper system, you will need to code test this for all scenarios you expect to support - I see that you're allowing for multiple Protocols and PlcTypes in your code, so you'll need to determine how each combination encodes BOOL arrays and code for that.

@zN3utr4l
Copy link
Author

Exactly, I have some generic functions that initialize the Tag:

protected internal IItem? GetArrayItem(EEIPItemModel itemModel)
{
    return itemModel.DataType switch
    {
        ItemDataType.SINT => InitializeTag<SintPlcMapper, sbyte[]>(itemModel),

        ItemDataType.USINT => InitializeTag<BytePlcMapper, byte[]>(itemModel),

        ItemDataType.INT16 => InitializeTag<IntPlcMapper, short[]>(itemModel),

        ItemDataType.DINT => InitializeTag<DintPlcMapper, int[]>(itemModel),

        ItemDataType.LINT => InitializeTag<LintPlcMapper, long[]>(itemModel),

        ItemDataType.WORD => InitializeTag<UShortPlcMapper, ushort[]>(itemModel),

        ItemDataType.DWORD => InitializeTag<UIntPlcMapper, uint[]>(itemModel),

        ItemDataType.ULINT => InitializeTag<ULongPlcMapper, ulong[]>(itemModel),

        ItemDataType.REAL => InitializeTag<RealPlcMapper, float[]>(itemModel),

        ItemDataType.LREAL => InitializeTag<LrealPlcMapper, double[]>(itemModel),

        ItemDataType.BOOL => configuration.PlcType == EEIPPlcType.CompactLogix
                                                            ? InitializeTag<ExtesionClass.BoolPlcMapper, bool[]>(itemModel)
                                                            : InitializeTag<libplctag.DataTypes.BoolPlcMapper, bool[]>(itemModel),

        ItemDataType.STRING => InitializeTag<ExtesionClass.StringPlcMapper, string[]>(itemModel),

        _ => null,
    };
}

protected internal IItem? GetItem(EEIPItemModel itemModel)
{
    return itemModel.DataType switch
    {
        ItemDataType.SINT => InitializeTag<SintPlcMapper, sbyte>(itemModel),

        ItemDataType.USINT => InitializeTag<BytePlcMapper, byte>(itemModel),

        ItemDataType.INT16 => InitializeTag<IntPlcMapper, short>(itemModel),

        ItemDataType.DINT => InitializeTag<DintPlcMapper, int>(itemModel),

        ItemDataType.LINT => InitializeTag<LintPlcMapper, long>(itemModel),

        ItemDataType.WORD => InitializeTag<UShortPlcMapper, ushort>(itemModel),

        ItemDataType.DWORD => InitializeTag<UIntPlcMapper, uint>(itemModel),

        ItemDataType.ULINT => InitializeTag<ULongPlcMapper, ulong>(itemModel),

        ItemDataType.REAL => InitializeTag<RealPlcMapper, float>(itemModel),

        ItemDataType.LREAL => InitializeTag<LrealPlcMapper, double>(itemModel),

        ItemDataType.BOOL => configuration.PlcType == EEIPPlcType.CompactLogix
                                                            ? InitializeTag<ExtesionClass.BoolPlcMapper, bool>(itemModel)
                                                            : InitializeTag<libplctag.DataTypes.BoolPlcMapper, bool>(itemModel),

        ItemDataType.STRING => InitializeTag<ExtesionClass.StringPlcMapper, string>(itemModel),

        _ => null,
    };
}

protected internal IItem? InitializeTag<M, T>(EEIPItemModel itemModel) where M : IPlcMapper<T>, new()
{
    try
    {
        return new EEIPItem<M, T>(itemModel, configuration);
    }
    catch (LibPlcTagException ex)
    {
        throw new ItemException(MessagesError.EEIPItemCreateException, ex);
    }
}

new EEIPItem() performs the tag initialization action I mentioned in the first comment.
So in practice this generic logic is not good ? because ElementSize of each mapper should change for each plc?

Some of the mappers I had to create myself (Bool, Byte, String, UInt, ULong, UShort)

@kyle-github
Copy link
Member

Be careful. A single BOOL is returned as a byte value (IIRC). But a BOOL array is returned as some number of UDINT values (actually data type 0xD3). If the BOOL is in a UDT, then it is different again. I should really write up a wiki page just for BOOL.

@timyhac
Copy link
Collaborator

timyhac commented Jan 29, 2025

In terms of my stance towards Tag<M,T>, there is a fairly lengthy discussion on that issue. Looking at your listing above, you might find the code example on this comment relevant to you.

Were you able to use the advice above to work with the BOOL[0..15] tag?

@zN3utr4l
Copy link
Author

No, for now I'm on another project, as I said above I bypassed the problem by declaring the array with 32 positions, but monitoring only 16 of them, as soon as I have some time I think I'll stop using mappers and I'll try to find one generic solution to directly instantiate Tag().

Thanks for sharing the link to the discussion on Tag<M, T> I missed it.
Like the commenter on that thread, I also thought that Tag<M, T> was the best way to use the library, I tried to create a generic library for all PLCs by wrapping libplctag.NET.

@timyhac
Copy link
Collaborator

timyhac commented Jan 29, 2025

Ok great - thanks!

@timyhac timyhac closed this as completed Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants