Was using pturmel's Ethernet/IP V2 module to import L5X files to build tag paths from fake host devices and came across this anomaly (?) - difference in OPC tag paths.
I have an array of BOOL values in the PLC at the address
VM01.GEN_ALARMS | BOOL[32]
Trying to access these tags with paths like so
VM01.GEN_ALARMS[2]
results in a configuration error The node id refers to a node that does not exist in the server address space.
Using the OPC browser it looks like the driver expects a path like so
VM01.GEN_ALARMS[0].2
@pturmel
The path I originally generated was through the OPC browser using your Ethernet/IP module as a host simulator. Not sure if that matters.
This topic recently came up in the office when a colleague mentioned that a bool takes up as much memory as an integer, which in Logix can be used as multiple bools.
In a UDT, BOOLs are assembled from SINTs, and the SINTs are marked as Hidden. Consecutive BOOLs are packed into SINTs. Those underlying SINTs pack in with other data types as normal. (My EtherNet/IP user manual has a detailed description of that.)
In an AOI definition, BOOLs are consolidated into 32-bit DWORD types, with the first 32 BOOLs in the DWORD at the beginning of the data type. The booleans do not have to be consecutive parameters or locals to be packed together.
The EtherNet/IP specification calls out BYTE, WORD, DWORD, and LWORD as "bit string" data types that are distinct from the signed SINT, INT, DINT, and LINT, and unsigned USINT, UINT, UDINT, and ULINT data types of the same sizes. My driver treats them like the unsigned variants.
That's because DINTs have to be 32-bit aligned, and the entire data type has to be a multiple of 32 bits, so DINT2 after BOOL2 causes three SINTs to be wasted on padding. Try DINT, then BOOL, then SINT, then INT. Will fit in 8 bytes.
As I said, described in my user manual.
If you wish to always minimize data type sizes, the simplest strategy is to group all members that have the same natural alignment together, and then order the groups by descending alignment size. LREAL, LINT, ULINT, and LWORD first, then REAL, DINT, UDINT, DWORD, then INT, UINT, and WORD, then SINT, USINT, and BYTE, with all of the BOOLs last. Natural alignment of any primitive type is its bit size.
For nested elements (in modern Logix):
Arrays and UDTs and AOI types containing any 64-bit types are aligned to 64 bits and padded to 64 bits.
Other arrays (of any type) and UDTs and AOI types not containing any 64-bit types are aligned to 32 bits and padded to 32 bits.
That makes sense. I believe the idea was that if you only need small number of bools or one bool, you might as well add whatever datatype fills up the space because it's consumed either way. During the discussion, I argued for keeping it simple, but I also reminisced about my early programming days [on a Commodore 128 and a 486] when such considerations were unavoidable. I had to commend my colleague, who still does retro stuff, for being so considerate.
When the bools reflect something that naturally has a name, like a state in a state machine, I almost always use a named boolean in a UDT. So that later re-arrangement and/or re-ordering of multiple states/conditions doesn't break the actual rung logic.
Me too. If need something like trap logic for diagnostic purposes, that's when I usually find myself throwing an integer into the controller for use as bools. test.0, test.1, ...