Logix Abbreviated Data Types

I've been working on an open source eip comms library unrelated to ignition. As part of that, I've implemented the "abbreviated data type" crc calculation detailed here (https://www.rockwellautomation.com/content/dam/rockwell-automation/sites/downloads/pdf/TypeEncode_CIPRW.pdf) to verify the user's structs match the returned UDT type when de-serializing.

The problem is they don't document how built-in types (such as TIMER or COUNTER) get their CRC. I can hard-code them for those specific types in a reference library, but that doesn't help for UDTs with nested built-in types.

I tried doing multiple UDTs with slight differences and brute-forcing the unknown part of string to find something that works, but the search space is too large. I'm guessing there are all kind of legacy-compatibility rules that mean there are actually multiple encoding paths depending on some unknown conditions anyway.

I know the encoding of the bulitin types is totally different than a normal UDT also - (ex: bools for a timer such as DN, TT, etc... are the high bits instead of the low bits if I remember correctly, but it has been a minute since I worked on that part) I've ended up with specialized deserialization mappings for the common builtins and along with hard-codec CRCs it works OK when you read them independently. Maybe the CRC string for these is related to this different data packing?

Anyone have any insight into what is going on there?

I reverse engineered this a while back. Some points:

  • Built-in PLC datatypes, which have template instance numbers in the range 0x0f00 through 0x0fff, have their CRC replaced by that instance number wherever directly used. The underlying CRC is not exposed anywhere. The instance numbers for specific built-in types are constant across firmware versions.

  • To generate a CRC wherever any of those are nested in something else, you need to know the list of actual member data types, including the hidden members, so you can follow the rules in TypeEncode_CIPRW. (You may have noted that you must compute the CRC across the fully-expanded string of member types and nested types.)

  • In modern Rockwell firmware, the documented procedure to enumerate tags and data types will enumerate all of the members. In some older firmware, hidden members may be excluded from the enumeration, and you have to guess. In most such cases, the missing elements are DWORD members that host boolean flags at specific locations.

  • When experimenting/guessing, you can check combinations with minimal hassle by creating a user data type that nests a single member of a built-in type, and check what the PLC gives you.

You can skip all of this if you simply point my Generic EtherNet/IP Client driver at a given PLC, with the advanced setting for enumerating all data types. Then examine the comments in the JSON udt definitions that driver exposes.

Thanks. That's exactly the info I think I need.