v2.0.17 released with support for listing tags in a Logix-class PLC

300 views
Skip to first unread message

Kyle

unread,
Jun 2, 2019, 3:21:37 PM6/2/19
to libplctag
I just released version 2.0.17 with support for listing tags in a Logix-class PLC.   This closes some of the longest-standing enhancement requests.

I tested it on Ubuntu 18.04 and Windows 10 (x64).   Seems to work fine.

Please let me know how it goes.

Best,
Kyle

Antonio Gallucci

unread,
Jun 27, 2019, 4:48:34 AM6/27/19
to libplctag
Really great feature. As soon as I have the possibility, I will test it and I will let you know if everything is ok.

Kyle

unread,
Jun 28, 2019, 9:25:59 PM6/28/19
to libplctag
Thanks!

Let me know how it works for you.   It does only work on ControlLogix-class PLCs.   I am not sure it will work on a Micro800 and it will not work on anything older like a PLC/5, SLC 500 or MicroLogix.

Best,
Kyle

Harry

unread,
Jul 18, 2019, 10:31:34 AM7/18/19
to libplctag
Hi Kyle,

Does this functionality exist in the API?  If not, is there a plan to add it so that I can call it from the .dll file?

Thanks,

Harry

Kyle

unread,
Jul 20, 2019, 11:01:09 AM7/20/19
to libplctag
Hi Harry,

The functionality is split.   You use the special tag "@tags" and that will get and return the raw information from the PLC.   Then you have to process that.   The example program list_tags does that.   It is not difficult to do the processing, but it is more difficult than reading an array of DINT.

So the raw library supports getting the tag listing data from the PLC in its native format.  After that, you need to unpack it.

Best,
Kyle

Harry

unread,
Jul 23, 2019, 3:34:34 PM7/23/19
to libplctag
Hi Kyle,

Looking at the list_tags.c program Symbolic Segment Addressing is being used.
Should the tag_type variable be changed dynamically to a unit32_t and be re-read in the event Byte 1= A0 and Byte 2 = 02? (this indicates a structured tag)
Reading the AB 1756-PM020F-EN-P, the size of the Tag Type Service Parameter is 2 bytes for Atomic tags and 4 bytes for PDT's and UDT's

For structured tags, bytes 3 & 4 are the Structure Handle of the Template Object (Template Attribute 1)

Is Symbolic Instance Addressing is required to get the Template Member Information?

Thanks,
Harry

PS - you have done really nice work putting this together :-)

Kyle

unread,
Jul 23, 2019, 8:02:13 PM7/23/19
to libplctag
Thanks for checking on this!

I guess I have not tried it on a system with UDTs.   It looks OK with things like strings and IO.  Aren't those special cases of UDT?

That AB publication is what I got this from, but I will reread it to see where I missed the changing size.   AB really does not want to do this the easy way.  If there is a harder way to do things, they double down on it.

Do you have any PLCs with UDTs that you can try?

As you may guess, this does not support getting the structure/UDT data for structured types.   

There are some really weird things in the data.   You get a 4-byte instance ID back for each entry, but you send the lower 2 bytes.  So I guess something just blows up if you have more than 64k entries?

Best,
Kyle

Kyle

unread,
Jul 23, 2019, 8:51:40 PM7/23/19
to libplctag
Hmm, I just went through the doc again and I do not see the 0xa0 0x02 thing.  The type info appears to always be 16-bit in the commands used to get the tag listing.  That is not done using symbolic addressing.

Now, if you are reading a tag (which does use symbolic addressing), then the code does do some special case handling of that:

        } else if ((*data) == AB_CIP_DATA_ABREV_STRUCT || (*data) == AB_CIP_DATA_ABREV_ARRAY ||
                   (*data) == AB_CIP_DATA_FULL_STRUCT || (*data) == AB_CIP_DATA_FULL_ARRAY) {
            /* this is an aggregate type of some sort, the type info is variable length */
            int type_length =
                *(data + 1) + 2;  /*
                                   * MAGIC
                                   * add 2 to get the total length including
                                   * the type byte and the length byte.
                                   */

 
where the byte values checked include:

#define AB_CIP_DATA_FULL_STRUCT     ((uint8_t)0xA2) /* Data is a struct type descriptor */
 
So, it picks up the leading 0x0A, gets the next byte (which is actually a count of how many additional bytes to get), then gets the remaining ones.  In this case, I just ignore them and save them away for later when doing a write.   But they are saved.  There is no way at the current time to retrieve those bytes, but that could happen at some point.

Best,
Kyle

Harry

unread,
Jul 24, 2019, 7:07:12 AM7/24/19
to libplctag
Hi Kyle,


At the middle of page 15 they indicate that the Tag Type Service Parameter is A16-bit value for atomic tags & Two 16-bit values for structured tags.

On page 38 is where they show that the Tag Type Service Parameter for structures is a 4-byte sequence. The first two bytes are the values A0 and 02, followed by the latter two bytes, which contain a 16-bit calculated field called a Structure Handle.

The PLC we are testing with has UDT's and PDT's. The following is the output from querying the PLC for tags using the example program.
The inconsistencies for tag type is what caused me to check the unpacking of the data returned.
Also not all tags are listed and Index 4's name is incorrectly mapped data.

Using tag string: protocol=ab-eip&gateway=192.168.2.73&path=1,0&cpu=lgx&name=@tags
index 1: Tag name=Map:Local, tag instance ID=1, tag type=1069, element length (in bytes) = 0, array dimensions = (0, 0, 0)
index 2: Tag name=Map:Discrete_IO, tag instance ID=2, tag type=1069, element length (in bytes) = 0, array dimensions = (0, 0, 0)
index 3: Tag name=Local:1:C, tag instance ID=3, tag type=868f, element length (in bytes) = 88, array dimensions = (0, 0, 0)
index 4: Tag name=Cxn:Data:68375461, tag instance ID=4, tag type=107e, element length (in bytes) = 0, array dimensions = (0, 0, 0)
index 5: Tag name=Local:1:I, tag instance ID=5, tag type=8536, element length (in bytes) = 8, array dimensions = (0, 0, 0)
index 6: Tag name=Local:1:O, tag instance ID=6, tag type=8ba1, element length (in bytes) = 4, array dimensions = (0, 0, 0)
index 7: Tag name=Task:MainTask, tag instance ID=7, tag type=1070, element length (in bytes) = 0, array dimensions = (0, 0, 0)
index 8: Tag name=Program:MainProgram, tag instance ID=8, tag type=1068, element length (in bytes) = 0, array dimensions = (0, 0, 0)
index 9: Tag name=Test_Accum, tag instance ID=9, tag type=c4, element length (in bytes) = 4, array dimensions = (0, 0, 0)
index 10: Tag name=Test_Accum_2, tag instance ID=a, tag type=c4, element length (in bytes) = 4, array dimensions = (0, 0, 0)
index 11: Tag name=Test_Alarm, tag instance ID=b, tag type=8ffb, element length (in bytes) = 152, array dimensions = (0, 0, 0)
index 12: Tag name=Test_Counter, tag instance ID=c, tag type=8f82, element length (in bytes) = 12, array dimensions = (0, 0, 0)
index 13: Tag name=Test_String_1, tag instance ID=d, tag type=8fce, element length (in bytes) = 88, array dimensions = (0, 0, 0)
index 14: Tag name=Test_UDT_Tag, tag instance ID=e, tag type=86c0, element length (in bytes) = 116, array dimensions = (0, 0, 0)
index 15: Tag name=Test_UDT_2_Tag, tag instance ID=f, tag type=84a1, element length (in bytes) = 4, array dimensions = (0, 0, 0)
index 16: Tag name=Alarm_Analog, tag instance ID=10, tag type=8ffa, element length (in bytes) = 304, array dimensions = (0, 0, 0)
index 17: Tag name=Alarm_alarm, tag instance ID=11, tag type=8f8b, element length (in bytes) = 96, array dimensions = (0, 0, 0)
index 18: Tag name=CounterArray, tag instance ID=12, tag type=af82, element length (in bytes) = 12, array dimensions = (2, 0, 0)
index 19: Tag name=Test_Input_0, tag instance ID=13, tag type=c1, element length (in bytes) = 1, array dimensions = (0, 0, 0)
index 20: Tag name=Test_String_2, tag instance ID=14, tag type=8fce, element length (in bytes) = 88, array dimensions = (0, 0, 0)
index 21: Tag name=Test_String_3, tag instance ID=15, tag type=8fce, element length (in bytes) = 88, array dimensions = (0, 0, 0)
index 22: Tag name=Test_String_4, tag instance ID=16, tag type=8fce, element length (in bytes) = 88, array dimensions = (0, 0, 0)
index 23: Tag name=Test_Integer_Event_Tag, tag instance ID=17, tag type=c4, element length (in bytes) = 4, array dimensions = (0, 0, 0)
index 24: Tag name=Test_Real_Event_Tag, tag instance ID=18, tag type=ca, element length (in bytes) = 4, array dimensions = (0, 0, 0)
index 25: Tag name=Test_String_5, tag instance ID=19, tag type=8fce, element length (in bytes) = 88, array dimensions = (0, 0, 0)
index 26: Tag name=Test_UDT, tag instance ID=1a, tag type=8f83, element length (in bytes) = 12, array dimensions = (0, 0, 0)
index 27: Tag name=UDT_Test_Tag, tag instance ID=1b, tag type=8ee7, element length (in bytes) = 16, array dimensions = (0, 0, 0)
index 28: Tag name=UDT_PDT_Timer_Tag, tag instance ID=1c, tag type=832e, element length (in bytes) = 12, array dimensions = (0, 0, 0)
index 29: Tag name=UDT_PDT_Timer_X2, tag instance ID=1d, tag type=8ab9, element length (in bytes) = 24, array dimensions = (0, 0, 0)
index 30: Tag name=UDT_with_arrays, tag instance ID=1e, tag type=84fd, element length (in bytes) = 620, array dimensions = (0, 0, 0)
index 31: Tag name=DINTArray, tag instance ID=1f, tag type=20c4, element length (in bytes) = 4, array dimensions = (5, 0, 0)


Regards,
Harry

Kyle

unread,
Jul 24, 2019, 10:32:25 AM7/24/19
to libplctag

Hi Harry,

Thanks for the dump.   Definitely looks odd in index 4.   However, I do not see any 0xA2 bytes.  For instance index 14 and 15 should have 0x02A2 as the type field if the value is prefixed with that header.   If the type is 4 bytes I would expect the rest of the tags to be all garbled, but they all seem fine.   It seems suspicious that the tags would all line up after that.   I think I need to see the raw data.

I reread my copy (we seem to have different versions of the doc, what year is yours?  Mine is from 2016), and I see a note about the type size on page 35 "Tag Type Service for Structures", but it is a little unclear whether it means that this is 4 bytes when reading a tag (it is) or when getting the tag listing.   

Can you edit "list_tags.c" to change the base tag string to have "&debug=4" at the end?   That will dump out all the raw bytes etc. and I can see exactly what is coming back.

I will try to set up a simple test case to see what happens with a UDT over the next few days.

Thanks for finding this!

Best,
Kyle

Kyle

unread,
Jul 24, 2019, 9:07:51 PM7/24/19
to libplctag
Sorry more brain dump...

The more I think about it, the more confused I am.   It definitely looks like something is wrong with the tag in index 4: the name does not look right.   But these tag entries are all different sizes.  The chances of things lining up afterward is very small.   It isn't an array of same-size structures.

You said something before that just struck me: some of the tags are missing.   It is a lot or just a few?   Are they all in a big batch or scattered around?

Sorry for all the questions, but now I am really wondering what is going on...

Best,
Kyle

Harry

unread,
Jul 26, 2019, 12:44:41 PM7/26/19
to libplctag
Hi Kyle,


I have attached the debug output as well as a copy of the L5K file for the demo project.
There are 26 controller tags in the project (including UDTs/PDTs) and 3 program tags.
The output includes all the Controller tags but none of the program tags.


Three of the tags listed are associated with the processor, I/O, and program

PROGRAM MainProgram (MAIN := "MainRoutine",
MODULE Discrete_IO (Parent := "Local",
MODULE Local (Parent := "Local",


I am not sure where the index 4 tag is coming from
index 4: Tag name=Cxn:Data:68375461, tag instance ID=4, tag type=107e, element length (in bytes) = 0, array dimensions = (0, 0, 0)


If the tag type were correct, we could screen out the UDTs and PDTs and list them separately if we are not able to list their elements.


Regards,
Harry
Test_1 - Copy.L5K
list_tags_debug - Copy.txt

Kyle

unread,
Jul 26, 2019, 8:13:40 PM7/26/19
to libplctag
Thanks, Harry!

Interesting data.   According to the doc, the type field is composed of two parts.   The key things are in the top nybble:

Bit  Value
12  supposed to be zero, but I see 1 values for what are system tags.
13-14 # of array dimensions: 00: 0 dim, 01: 1 dim, 10: 2 dim, 11: 3 dim.
15  0 = primitive/built-in type like DINT, 1 = UDT.

So if bit 15 is set, then the lower 12 bits are a template ID.

So it looks like if bit 12 is set, then we have a system type.  In the dump those come back with element size of 0 which means we cannot really get any good idea of how big they are and probably cannot read them.   Might be worth trying.

So, if we take the type word and do a check like this:

if(tag_type & 0x9000) { /* system type or UDT */ }
else { /* not system type or UDT */ }

We can filter out the non-system ones.   Now we may want to keep the UDTs in which case we would check against value 0x1000 for system types.

I wonder how to get the program tag?  The library can read them if you know the name.   You need to construct the full name, "PROGRAM:prog1.tagName" IIRC.    The document I have from AB is silent on how to read program tags.

Best,
Kyle

Harry

unread,
Jul 29, 2019, 9:21:03 AM7/29/19
to libplctag
Hi Kyle,

I compiled the information into one document to make it easier to read.
Please take a look to see if it might be helpful to you.

Regards,
Harry
AB Logic Communications CIP Services - Copy.docx

Kyle

unread,
Jul 29, 2019, 8:40:51 PM7/29/19
to libplctag
Thanks, Harry.

I think I have all this.   What is the date of your doc?   Mine is from 2016.  I'll see if I can attach it.

Best,
Kyle
Logix5000 Data Access - 1756-pm020_-en-p.pdf

Harry

unread,
Jul 30, 2019, 8:15:48 AM7/30/19
to libplctag
Hi Kyle,

Attached is the current document - January-2019

Regards,
Harry
Allen Bradley Logix Controllers Data Access.pdf
Reply all
Reply to author
Forward
0 new messages