Generic C# wrapper class to read the template of all structured tags

1,310 views
Skip to first unread message

Jochen Haar

unread,
Mar 19, 2021, 4:06:39 AM3/19/21
to libplctag
For those who might be interested in, please find attached a generic C# wrapper class to read the template of all structured tags in an Ethernet/IP-PLC.

Please keep in mind the notes below:

    /// <summary>
    /// Class to read the Tag and UDT template information from an Ethernet/IP PLC via a separate TcpClient
    /// connection to avoid any side effects in the library. This implementation is meant as a work-around
    /// only until the library provides a similar solution. It should work at least for CompactLogix and 
    /// ControlLogix devices connected to the PLC on-board Ethernet adapter. Backplane routing is not supported.
    /// Usage: Just instantiate an object by calling the Constructor with the IPAddress and Port of the
    /// PLC. The Constructor opens the connection, reads the data of interest, analyses the data and closes
    /// the connection. All Tag and Structured Tag information is available via the public methods and 
    /// public members.
    /// 
    /// Reference: 1756-PM020G-EN-P.pdf - published September 2020
    /// 
    /// Feel free to use this code as is and on your own risk. The risk is low, as we are reading data only.
    /// 
    /// A matter of course and very fair is to keep the name of the original author in the header of the code.
    /// March 2021 - Jochen Haar
    /// </summary>

Best regards
Jochen
EipEncapsulation.cs
EipConstants.cs
EipTagListFactory.cs

Jochen Haar

unread,
Mar 24, 2021, 5:15:38 AM3/24/21
to libplctag
All,

please find attached an updated version.

Changes:

- Added feature to read the program tags as well
- Unnecessary array size definitions have been removed

Best regards
Jochen

EipTagListFactory.cs
EipEncapsulation.cs
EipConstants.cs

Jochen Haar

unread,
Mar 25, 2021, 5:49:59 AM3/25/21
to libplctag
Folks,

I expected some questions here. The silence might be for two reasons:

- the wrapper class is well understood and there is no need for more explanation
- it is not very clear how to use the wrapper class

Just in case of the second reason one, I will explain a little bit more in detail and the usage in a real application. The base of my GUI application is the attached tag list class. The instance of this class needs to be filled with the information provided by the EipTagListFactory wrapper. This is done with the following two methods.

The usage of the wrapper in words:

- Run thru the EipTagListFactory.TagNameCollection or the EipTagListFactory.TagInstanceIdCollection
- You will get the tag definition of all tags in the PLC - controller and program tags
- Read the template with the symbolType of each tag, e.g. template = eipTagListFactory.GetTemplateByInstance(tag.SymbolType);
- if you got a null pointer it is a plain tag, like BOOL, DINT, REAL[x.y.z] or whatever
- if you got a valid template pointer it is the detailled information of the structured tag which might contain further structured tags - therefore the tree list

        /// <summary>
        /// Returns the EipTagList as a workaround as long as the open source library does not support the templates of structured tags
        /// </summary>
        /// <param name="deviceName">The device name we are connected to</param>
        /// <returns>The EipTagList with the tags in the PLC we are connected to</returns>
        //-----------------------------------------------------------------------------------------
        private EipTagList EipTagListFromEipTagListFactory(string deviceName)
        //-----------------------------------------------------------------------------------------
        {
            string tagName, typeAsString, detailledTypeDescription;
            UInt16 structureHandle;
            EipTagListFactory.EipTag tag;
            EipTagListFactory.EipTemplate template;
            EipTagList eipTagList;
            EipTagList childEipTagList;

            // read all tag and the template of all structured tags from the PLC - throws an Exception on errors which needs to be caught by the caller
            EipTagListFactory eipTagListFactory = new EipTagListFactory(this.DeviceIpAddress, this.DevicePort);
            // instantiate an EipTagProcessor instance to get the tag types as string
            EipTagProcessor eipTagProcessor = new EipTagProcessor();
            // create the EipTagList instance as the base for the GUI interfacing
            eipTagList = new EipTagList(deviceName);  // the name of the PLC is the root
            // show the browsing duration in the StatusBar
            this.LblStatusLeft.Content = @"Browsing duration:" + String.Format(@" {0:0},{1:00} s", eipTagListFactory.Duration.Seconds, eipTagListFactory.Duration.Milliseconds);

            // run thru the EipTagListFactory instance and fill the EipTagList instance which is the base of this GUI
            foreach (var pair in eipTagListFactory.TagNameCollection)
            {
                structureHandle = 0;
                tagName = pair.Key;
                tag = pair.Value;
                // get the template object instance or null if it is not a template
                template = eipTagListFactory.GetTemplateByInstance(tag.SymbolType);
                // create the symbol type as a string in a human readable form
                typeAsString = eipTagProcessor.GetTagDataType(tag.SymbolType, tag.Dimensions, out detailledTypeDescription);
                // create the default for plain symbols
                typeAsString += @" - " + detailledTypeDescription;
                // if it is a template, the type is the template name
                if (template != null) typeAsString = template.TemplateName + @" - UDT (" + template.TransferredWireBytesOnRead.ToString() + @" Bytes)";
                // if it is a template, set the StructureHandle for standard STRING indication - remember: handle 0xFCE is a STRING type
                if (template != null) structureHandle = template.StructureHandle;
                // add the tag to the list
                childEipTagList = eipTagList.AddTag(eipTagList, tag.SymbolName, tag.ElementSize, (uint)tag.InstanceId, tag.SymbolType, typeAsString, tag.Dimensions, structureHandle);
                // if the member is a template, we drill down
                if (template != null) this.AddTemplateToTag(eipTagListFactory, template, childEipTagList);
            }

            return eipTagList;
        }

        /// <summary>
        /// Adds the template data to the parent EipTagList
        /// </summary>
        /// <param name="eipTagListFactory">The EipTagListFactory instance object</param>
        /// <param name="template">The template instance object</param>
        /// <param name="parent">The EipTagList instance object of the parent</param>
        //-----------------------------------------------------------------------------------------
        private void AddTemplateToTag(EipTagListFactory eipTagListFactory, EipTagListFactory.EipTemplate template, EipTagList parent)
        //-----------------------------------------------------------------------------------------
        {
            string typeAsString, detailledTypeDescription, plcAccessTagName;
            uint[] dimensions = new uint[3] { 0, 0, 0 };  // the dimensions of the tag
            EipTagProcessor eipTagProcessor = new EipTagProcessor();  // just to get the tag type as a string
            EipTagListFactory.EipTemplate childTemplate;
            EipTagList childEipTagList;
            UInt16 structureHandle;

            // run thru the EipTagListFactory.EipTemplateMember instance and fill the EipTagList instance
            foreach (EipTagListFactory.EipTemplateMember member in template.TemplateMemberList)
            {
                // reset the previous settings of the structured member
                structureHandle = 0;
                // reset the previous dimension of the structured member
                dimensions[0] = 0;
                // get the template instance or null if it is not a template
                childTemplate = eipTagListFactory.GetTemplateByInstance(member.MemberType);
                // if it is an array set the array dimension which is defined in the member.MemberInfo property. Struct member arrays are always with a dimension[1]
                if (eipTagListFactory.IsArrayTag(member.MemberType)) dimensions[0] = member.MemberInfo;
                // create the symbol type as a string in a human readable form
                typeAsString = eipTagProcessor.GetTagDataType(member.MemberType, dimensions, out detailledTypeDescription);
                // create the default for plain symbols
                typeAsString += @" - " + detailledTypeDescription;
                // if it is template, the data type is the template name
                if (childTemplate != null) typeAsString = childTemplate.TemplateName + @" - UDT (" + childTemplate.TransferredWireBytesOnRead.ToString() + @" Bytes)";
                // if it is template, set the StructureHandle
                if (childTemplate != null) structureHandle = childTemplate.StructureHandle;
                // set the during drill down growing PlcAccessTagName for the element-wise access to the structured tag
                plcAccessTagName = parent.PlcAccessTagName + @"." + member.MemberName;
                // add the tag to the list - length and instanceId are not relevant here as the length is defined with the data type and the instanceId is the parent instanceId
                childEipTagList = parent.AddTag(parent, member.MemberName, 0, 0, member.MemberType, typeAsString, dimensions, structureHandle, plcAccessTagName);
                // if the member is a template, we need to place a recursive call as a drill down action
                if (childTemplate != null) this.AddTemplateToTag(eipTagListFactory, childTemplate, childEipTagList);
            }
        }

Hope this helps a little bit.
EipTagList.cs

Jody Koplo

unread,
Mar 26, 2021, 12:56:37 PM3/26/21
to Jochen Haar, libplctag
Jochen-

I haven't had a chance to look through it, let alone pull it into a project to test. It is functionality that's been a goal for awhile (listing UDT contents), so I'm excited to try it out. 

What's your thoughts on including this in libplctag.net? Would you like to put it in a branch and do a pull request?

Jody

--
You received this message because you are subscribed to the Google Groups "libplctag" group.
To unsubscribe from this group and stop receiving emails from it, send an email to libplctag+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/libplctag/cc213570-a5b0-4f5a-894d-b4d6e752bb5cn%40googlegroups.com.

tim...@gmail.com

unread,
Mar 26, 2021, 6:18:58 PM3/26/21
to libplctag
Does it use EEIP.NET?

Jochen Haar

unread,
Mar 27, 2021, 4:22:54 AM3/27/21
to libplctag
No, it is not using EEIP.NET. Only the the Encapsulation class is originally from Rossmann Engineering as mentioned in the class header.

All the remaining is my own development.

Best regards
Jochen

Nat Frampton

unread,
May 23, 2021, 11:05:23 PM5/23/21
to libplctag
Hi Jochen,

Should this work on a MicroLogix 1400?

I get a Exception Thrown because of a Status code of [5] - Path destination unknown

At line 752 of EipTagListFactory.

Thanks,
Nat


Kyle

unread,
May 23, 2021, 11:18:59 PM5/23/21
to libplctag
I am fairly sure this will not work on a MicroLogix.   Very different protocol set on those.   It is extremely similar to PLC-5.  The only guaranteed support for tag listing is with ControlLogix and CompactLogix.   This is not a missing feature in the library, but the underlying protocol in the PLC itself.  The only way to "list" tags on a DF1-based PLC is to do a dump of parts of the PLC memory (which is model specific IIRC) and decode that.  That is what the Rockwell tools do.   For odd-ball PLCs like Micro800, I am not sure what the tools use.   Rockwell firmly believes in a wide diversity of protocols and commands :-/

Also PLC-5, SLC and MicroLogix do not support path elements.   The C DLL will not return an error, but it will warn if you have logging turned on.  It will strip out the path.

Best,
Kyle

Jochen Haar

unread,
May 24, 2021, 10:20:31 AM5/24/21
to libplctag
Thanks Kyle.

The EipTagListFactory implements the data exchange according the Rockwell Publication: 1756-pm020_-en-p.pdf.

The front cover of this publication explains for which controller types the document is valid:

1756 ControlLogix, 1756 GuardLogix, 1769 CompactLogix, 1769 Compact GuardLogix, 1789 SoftLogix, 5069 CompactLogix, 5069 Compact GuardLogix, Studio 5000 Logix Emulate

Best regards
Jochen

Jochen Haar

unread,
Dec 30, 2021, 9:05:22 AM12/30/21
to libplctag
All,

and especially for those who might be interested in, please find attached an updated version of the EipTagListFactory. I've attached an UML diagram as well to provide a better overview.

There are a couple of changes:

   - Simplification of code
   . Minor bug fixing
   - Separation of nested classes into own class files
   - new features, like:
         - CSV export,  
         - ReadTagRefreshAttributes(...) to check if the Tag Database Definition has been changed - should be done frequently in a separate thread
         - Cancellation of an active browsing from a different thread, 
         - Access to the EipTagList of dedicated tags/UDT's via EipTagList.GetTagListOfTagName(yourUdtTagName)
         - and others

As always, please keep the notes below im mind:

    /// <summary>
    /// Class to read the Symbol/Tag and UDT template information from an Ethernet/IP PLC via a separate TcpClient

    /// connection to avoid any side effects in the library. This implementation is meant as a work-around
    /// only until the library provides a similar solution. It should work at least for CompactLogix and
    /// ControlLogix devices connected to the PLC on-board Ethernet adapter. Backplane routing is not supported.
    ///
    /// Usage: Just instantiate an object and call the the Browse()-method with the IP-Address and Port of
    /// the PLC. The Browse()-method opens the connection, reads the data of interest, analyses the data,
    /// closes the connection and returns an EipTagList.
    ///
    /// Furthermore, all Symbol/Tag and structured Symbol/Tag (UDT) information is available via the public
    /// methods and public members. The class provides two optional methods to create the complete tag tree list
    /// of the controller content:
    ///
    /// - GetEipTagList(...) creates a tree list where each tag has a collection property with all child objects
    /// - GetEipTagWithParentList(...) creates a flat tree list where each tag object contains a parent member
    ///
    /// The class provides the method ReadTagRefreshAttributes(...) which can be used to check if the Tag Database
    /// definition of the Ethernet/IP device has been changed and needs a refresh. You should use this method in
    /// a separate thread which reads the Attributes at the very first beginning and then in a frequent manner. If at
    /// least one of the five Attributes have changed, a Tag Database modification took place and a new Browse() is needed.

    ///
    /// Reference: 1756-PM020G-EN-P.pdf - published September 2020
    ///
    /// Feel free to use this code as is and on your own risk. The risk is low, as we are reading data only.
    ///
    /// A matter of course and very fair is to keep the name of the original author in the header of the code.
    /// March 2021 - Jochen Haar
    /// </summary>

Best regards
Jochen
UML - EipTagListFactory.pdf
EipTagListFactory.zip

Kyle

unread,
Dec 30, 2021, 10:40:59 AM12/30/21
to libplctag
Very nice, thanks for this, Jochen!
Reply all
Reply to author
Forward
0 new messages