Enum Datatype

360 views
Skip to first unread message

Chirico Costal

unread,
Mar 2, 2021, 5:00:02 AM3/2/21
to libplctag
Hi,
I noticed that when I want to list all the items of the plc the DataType is expressed in the form of a decimal which translated into hexadecimal corresponds to the codes listed below, I was wondering why an enum of this type is not implemented:

    bool = 0xc1
    sint = 0xc2 # signed 8-bit integer
    int = 0xc3 # signed 16-bit integer
    dint = 0xc4 # signed 32-bit integer
    lint = 0xc5 # signed 64-bit integer
    usint = 0xc6 # unsigned 8-bit integer
    uint = 0xc7 # unsigned 16-bit integer
    udint = 0xc8 # unsigned 32-bit integer
    ulint = 0xc9 # unsigned 64-bit integer
    real = 0xca # 32-bit floating point
    lreal = 0xcb # 64-bit floating point
    estimates = 0xcc # synchronous time
    date = 0xcd
    time_of_day = 0xce
    date_and_time = 0xcf
    string = 0xd0 # character string (1 byte per character)
    byte = 0xd1 # byte string 8-bits
    word = 0xd2 # byte string 16-bits
    dword = 0xd3 # byte string 32-bits
    lword = 0xd4 # byte string 64-bits
    string2 = 0xd5 # character string (2 bytes per character)
    ftime = 0xd6 # duration high resolution
    ltime = 0xd7 # duration long
    itime = 0xd8 # duration short
    stringn = 0xd9 # character string (n bytes per character)
    short_string = 0xda # character string (1 byte per character 1 byte length indicator)
    time = 0xdb # duration in milliseconds
    epath = 0xdc # cip path segment
    engunit = 0xdd # engineering units
    string = 0xde # international character string

I saw that there was an issue on GitHun (https://github.com/libplctag/libplctag.NET/issues/40#) now closed in which we talked about this Enum and its implementation with Generics, it is still in writing stage?

Another question, I noticed that when the tags are arrays, the code is preceded by a "20", at least for DINT and REAL, this also applies to all other atomic types or are there other codes that add to these?
Immagine 2021-03-02 105620.png


Immagine 2021-03-02 105515.png

(A message from Kyle)

#define AB_CIP_DATA_BIT ((uint8_t)0xC1) /* Boolean value, 1 bit */
#define AB_CIP_DATA_SINT ((uint8_t)0xC2) /* Signed 8–bit integer value */
#define AB_CIP_DATA_INT ((uint8_t)0xC3) /* Signed 16–bit integer value */
#define AB_CIP_DATA_DINT ((uint8_t)0xC4) /* Signed 32–bit integer value */
#define AB_CIP_DATA_LINT ((uint8_t)0xC5) /* Signed 64–bit integer value */
#define AB_CIP_DATA_USINT ((uint8_t)0xC6) /* Unsigned 8–bit integer value */
#define AB_CIP_DATA_UINT ((uint8_t)0xC7) /* Unsigned 16–bit integer value */
#define AB_CIP_DATA_UDINT ((uint8_t)0xC8) /* Unsigned 32–bit integer value */
#define AB_CIP_DATA_ULINT ((uint8_t)0xC9) /* Unsigned 64–bit integer value */
#define AB_CIP_DATA_REAL ((uint8_t)0xCA) /* 32–bit floating point value, IEEE format */
#define AB_CIP_DATA_LREAL ((uint8_t)0xCB) /* 64–bit floating point value, IEEE format */
#define AB_CIP_DATA_STIME ((uint8_t)0xCC) /* Synchronous time value */
#define AB_CIP_DATA_DATE ((uint8_t)0xCD) /* Date value */
#define AB_CIP_DATA_TIME_OF_DAY ((uint8_t)0xCE) /* Time of day value */
#define AB_CIP_DATA_DATE_AND_TIME ((uint8_t)0xCF) /* Date and time of day value */
#define AB_CIP_DATA_STRING ((uint8_t)0xD0) /* Character string, 1 byte per character */
#define AB_CIP_DATA_BYTE ((uint8_t)0xD1) /* 8-bit bit string */
#define AB_CIP_DATA_WORD ((uint8_t)0xD2) /* 16-bit bit string */
#define AB_CIP_DATA_DWORD ((uint8_t)0xD3) /* 32-bit bit string */
#define AB_CIP_DATA_LWORD ((uint8_t)0xD4) /* 64-bit bit string */
#define AB_CIP_DATA_STRING2 ((uint8_t)0xD5) /* Wide char character string, 2 bytes per character */
#define AB_CIP_DATA_FTIME ((uint8_t)0xD6) /* High resolution duration value */
#define AB_CIP_DATA_LTIME ((uint8_t)0xD7) /* Medium resolution duration value */
#define AB_CIP_DATA_ITIME ((uint8_t)0xD8) /* Low resolution duration value */
#define AB_CIP_DATA_STRINGN ((uint8_t)0xD9) /* N-byte per char character string */
#define AB_CIP_DATA_SHORT_STRING ((uint8_t)0xDA) /* Counted character sting with 1 byte per character and 1 byte length indicator */
#define AB_CIP_DATA_TIME ((uint8_t)0xDB) /* Duration in milliseconds */
#define AB_CIP_DATA_EPATH ((uint8_t)0xDC) /* CIP path segment(s) */
#define AB_CIP_DATA_ENGUNIT ((uint8_t)0xDD) /* Engineering units */
#define AB_CIP_DATA_STRINGI ((uint8_t)0xDE) /* International character string (encoding?) */

/* aggregate data type byte values */
#define AB_CIP_DATA_ABREV_STRUCT ((uint8_t)0xA0) /* Data is an abbreviated struct type, i.e. a CRC of the actual type descriptor */
#define AB_CIP_DATA_ABREV_ARRAY ((uint8_t)0xA1) /* Data is an abbreviated array type. The limits are left off */
#define AB_CIP_DATA_FULL_STRUCT ((uint8_t)0xA2) /* Data is a struct type descriptor */
#define AB_CIP_DATA_FULL_ARRAY ((uint8_t)0xA3) /* Data is an array type descriptor */


Jochen Haar

unread,
Mar 2, 2021, 10:03:44 AM3/2/21
to libplctag
Hi,

maybe this helps a little bit:

        /// <summary>
        /// Returns the Data Type of the PLC object as a string
        /// </summary>
        /// <param name="type">The Type of the PLC oject</param>
        /// <param name="dimensions">The Dimensions of the PLC object</param>
        /// <param name="isAOI">Reference bool to indicate if the PLC object is an Add On Instruction</param>
        /// <param name="isArray">Reference bool to indicate if the PLC object is an Array</param>
        /// <param name="isUDT">Reference bool to indicate if the PLC object is an User Defined Type</param>
        /// <returns>The tag data type as string</returns>
        //-----------------------------------------------------------------------------------------
        public string GetTagDataType(int type, uint[] dimensions, out bool isAOI, out bool isArray, out bool isUDT)
        //-----------------------------------------------------------------------------------------
        {
            string value = @"";
            string array = @"";

            isAOI = (type & 0x1000) == 0x1000;
            isArray = (type & 0x2000) == 0x2000;
            isUDT = (type & 0x8000) == 0x8000;

            if (isArray)
            {
                array = @"[" + dimensions[0].ToString();
                if (dimensions[1] > 0) array += @"," + dimensions[1].ToString();
                if (dimensions[2] > 0) array += @"," + dimensions[2].ToString();
                array += @"]";
            }

            type &= 0x1fff;  // AOI
            type &= 0x2fff;  // Array
            type &= 0x8fff;  // UDT

            switch (type)
            {
                case 0xC1: value = @"BOOL{0} - Boolean value, 1 bit"; break;
                case 0xC2: value = @"SINT{0} - Signed 8–bit integer value"; break;
                case 0xC3: value = @"INT{0} - Signed 16–bit integer value"; break;
                case 0xC4: value = @"DINT{0} - Signed 32–bit integer value"; break;
                case 0xC5: value = @"LINT{0} - Signed 64–bit integer value"; break;
                case 0xC6: value = @"SINT{0} - Unsigned 8–bit integer value"; break;
                case 0xC7: value = @"INT{0} - Unsigned 16–bit integer value"; break;
                case 0xC8: value = @"DINT{0} - Unsigned 32–bit integer value"; break;
                case 0xC9: value = @"LINT{0} - Unsigned 64–bit integer value"; break;
                case 0xCA: value = @"REAL{0} - 32–bit floating point value, IEEE format"; break;
                case 0xCB: value = @"LREAL{0} - 64–bit floating point value, IEEE format"; break;
                case 0xCC: value = @"LINT{0} - Synchronous time value"; break;
                case 0xCD: value = @"LINT{0} - Date value"; break;
                case 0xCE: value = @"LINT{0} - Time of day value"; break;
                case 0xCF: value = @"LINT{0} - Date and time of day value"; break;
                case 0xD0: value = @"STRING{0} - Character string, 1 byte per character"; break;
                case 0xD1: value = @"STRING{0} - 8-bit bit string"; break;
                case 0xD2: value = @"STRING{0} - 16-bit bit string"; break;
                case 0xD3: value = @"STRING{0} - 32-bit bit string"; break;
                case 0xD4: value = @"STRING{0} - 64-bit bit string"; break;
                case 0xD5: value = @"STRING{0} - Wide char character string, 2 bytes per character"; break;
                case 0xD6: value = @"LINT{0} - High resolution duration value"; break;
                case 0xD7: value = @"LINT{0} - Medium resolution duration value"; break;
                case 0xD8: value = @"LINT{0} - Low resolution duration value"; break;
                case 0xD9: value = @"STRING{0} - N-byte per char character string"; break;
                case 0xDA: value = @"STRING{0} - Counted character sting with 1 byte per character and 1 byte length indicator"; break;
                case 0xDB: value = @"LINT{0} - Duration in milliseconds"; break;
                case 0xDC: value = @"STRING{0} - CIP path segment"; break;
                case 0xDD: value = @"STRING{0} - Engineering units"; break;
                case 0xDE: value = @"STRING{0} - International character string"; break;
                case 0xA0: value = @"UDT{0} - Data is an abbreviated struct type, i.e. a CRC of the actual type descriptor"; break;
                case 0xA1: value = @"ARRAY{0} - Data is an abbreviated array type. The limits are left off"; break;
                case 0xA2: value = @"UDT{0} - Data is a struct type descriptor"; break;
                case 0xA3: value = @"ARRAY{0} - Data is an array type descriptor"; break;
            }

            value = String.Format(value, array);

            if (isAOI) value = @"AOI - Add On Instruction";
            if (isUDT) value = @"UDT - User Defined Type";

            return value;
        }


Best regards
Jochen

Jochen Haar

unread,
Mar 2, 2021, 10:44:54 AM3/2/21
to libplctag
Uuups,

I just saw that I implemented some kind of nonsense.

Please replace:

            type &= 0x1fff;  // AOI
            type &= 0x2fff;  // Array
            type &= 0x8fff;  // UDT

with

            type &= 0xfff;

This will lead to the same result with less workload.

Best regards
Jochen

Chirico Costal

unread,
Mar 3, 2021, 2:45:50 AM3/3/21
to libplctag
Thank you very much, one more question, why do you still use the same type for different types, for example below, do you use the same type of data for different types of plc data, are they really attributable to all LINTs or is it an arrangement?


case 0xCC: value = @"LINT{0} - Synchronous time value"; break;
case 0xCD: value = @"LINT{0} - Date value"; break;
case 0xCE: value = @"LINT{0} - Time of day value"; break;
case 0xCF: value = @"LINT{0} - Date and time of day value"; break;

Jochen Haar

unread,
Mar 3, 2021, 3:16:18 AM3/3/21
to libplctag
Hi Chirico,

The PLC data type is always the same. The wording explains the usage in the PLC a little bit more in detail. The LINTs in the described example are Timer ticks. It is nearly the same as in Windows with the DateTime object and there specifically the Ticks property - it could be a Time, a Date, a Date and Time, a period of time, an interval, a duration or whatever. 

If you read the data from the PLC and if you do some kind of interpretation on the PC-level above, it is always a good idea to talk to the PLC programmer why and how he used this tag data type for what reason and for what purpose - just to avoid any misinterpretation on upper levels.

Best regards
Jochen

Chirico Costal

unread,
Mar 3, 2021, 4:54:50 AM3/3/21
to libplctag
Hi Jochen,
Thanks again, but I noticed that it doesn't catch me the string type, it returns me as if it were a UDT, I don't know if there is something wrong with the declaration of my variable at the plc level, but if I force it to string while reading tag instead everything works.

Chirico Costal

unread,
Mar 3, 2021, 5:01:01 AM3/3/21
to libplctag
Can you confirm that the codes of the string types are only those there?

Jochen Haar

unread,
Mar 3, 2021, 5:40:20 AM3/3/21
to libplctag
Hi Chirico,

The pure PLC type of a STRING is limited to a certain length, somewhat in the range up to 80 characters, if I remember correctly. With a DINT as the dope vector for the Length first. All other and longer STRING types need to be handled as an UDT -  once again, the DINT dope vector first and then the string content.

For example:

Name                       DataType        Comment
---------------------------------------------------------------------------------------------
YOUR_STRING       STR_1024        this is now an UDT
   |
   +-- LEN                 DINT                 the dope vector
   |
   +-- DATA              SINT[1024]      the string content

The total length of this example would be 1028 bytes.

So, there is nothing wrong with your declaration. It's just the way how to handle it.

Jochen Haar

unread,
Mar 3, 2021, 5:54:26 AM3/3/21
to libplctag
Try to read your string as an array of SINT with your variable length and you will see your STRING content is starting in SINT[4].
SINT[0] to SINT[3] represents the dope vector. Please bear in mind the little and big endian order, because it is reality a DINT which was read as an array of SINT.

Chirico Costal

unread,
Mar 3, 2021, 6:05:32 AM3/3/21
to libplctag
i tried to take the code that the UDT of String gives to me and added it to the switch in hex now i can manage the UTD of String and the Array of UDT/no UDT of String.
I dont know if it is the correct approach but work.

case 0xFCE: return typeof(string); //UDT OF STRING 

Kyle

unread,
Mar 3, 2021, 11:22:03 AM3/3/21
to libplctag
Hi,

Note that there are some different behaviors here.   The bit string types are usually used for BOOL arrays.  Except when they are not.   For instance, individual BOOL fields in a UDT get packed into a leading byte that is invisible.

BOOL arrays get set up as packed 32-bit integers.

These types:

              case 0xD1: value = @"STRING{0} - 8-bit bit string"; break;
                case 0xD2: value = @"STRING{0} - 16-bit bit string"; break;
                case 0xD3: value = @"STRING{0} - 32-bit bit string"; break;
                case 0xD4: value = @"STRING{0} - 64-bit bit string"; break;

Usually end up used for BOOL arrays, not strings.

Best,
Kyle

Jochen Haar

unread,
Mar 3, 2021, 2:15:54 PM3/3/21
to libplctag
Hi Kyle,

my fault - thanks for clarification and sorry for any confusion.

It should be read like this:

                case 0xD1: value = @"SINT{0} - 8-bit bit string"; break;
                case 0xD2: value = @"INT{0} - 16-bit bit string"; break;
                case 0xD3: value = @"DINT{0} - 32-bit bit string"; break;
                case 0xD4: value = @"LINT{0} - 64-bit bit string"; break;

Best regards
Jochen

Reply all
Reply to author
Forward
0 new messages