KB 0027
Handling strings inside UDTs (User Defined Types) and PDTs (Predefined Types)


CATEGORY

ASComm.NET

 

SYMPTOMS

Your application throws an exception with a message like one of the following when attempting to read or write an entire UDT/PDT that contains one or more strings:

PDT Exception

Controller data structure does not match ASComm.NET pre-defined type structure. Contact Automated Solutions Technical Support.

UDT Exception

Source data length xxx does not match computed data length yyy for structure. This can occur for the following reasons:
1. Object passed to Item.GetStructuredValues() method does not match signature of controller structure (UDT/PDT).
2. For arrays, ensure Item.Elements property specifies the same number of elements as defined in object passed to Item.GetStructuredValues() method.

 

CAUSE

ASComm.NET uses a sophisticated algorithm to map CLR classes to UDTs and PDTs. All atomic, PDT, and UDT types can easily be modeled in a CLR class, with the exception of strings.

Logix strings are hybrids that can be accessed like an atomic type but are actually stored as structured data with two fields: LEN and DATA.

The LEN field is a DINT (32-bit) type and describes the length of the string data. Since the DATA field is fixed length, LEN describes the actual string length, not the string capacity.

The DATA field is an array of SINT (8-bit). The default string is defined as SINT[82], but Logix allows you to define custom strings with fewer or greater capacity. Custom strings are the reason that ASComm.NET cannot determine the string's capacity by examining the return packet. 

In the current version of ASComm.NET, the structured data handling algorithm cannot directly map a CLR string type to a Logix string type due to ambiguity in the returned data size. ASComm.NET simply sees a string returned as a two field UDT/PDT and there is currently no way to inform ASComm.NET that there is a string in the UDT/PDT.

 

RESOLUTION

A very simple workaround is available. We have created a class that represents a Logix string and allows for easy encoding and decoding of the structured data.

To accommodate custom strings, copy the class below to create a new class with a different name and change the data field dimension to match your custom string's data field dimension.

 

C#

C# Class

[StructLayout(LayoutKind.Sequential)] public class LOGIX_STRING { #region Fields /// <summary> /// Logix string length field /// This is the actual string length, not the string capacity. /// </summary> public Int32 stringLength; /// <summary> /// Logix string data field /// Default string capacity is 82 bytes /// Change the array dimension for custom defined strings in RSLogix /// </summary> public Byte[] stringData = new Byte[82]; #endregion Fields #region Methods /// <summary> /// Returns LOGIX_STRING as a CLR string /// </summary> public override String ToString() { return ASCIIEncoding.ASCII.GetString(stringData, 0, stringLength); } /// <summary> /// Stores CLR string as a LOGIX_STRING /// </summary> public void SetString(String s) { if (s.Length > stringData.Length) throw new ArgumentOutOfRangeException("s", "String capacity is " + stringData.Length.ToString() + " bytes"); Buffer.BlockCopy(ASCIIEncoding.ASCII.GetBytes(s), 0, stringData, 0, s.Length); stringLength = s.Length; } #endregion Methods };

C# Usage

LOGIX_STRING logixString = new LOGIX_STRING(); logixString.SetString(textBox1.Text); label1.Text = logixString.ToString();

Visual Basic

VB Class

<StructLayout(LayoutKind.Sequential)>_ Public Class LOGIX_STRING #Region "Fields" ''' <summary> ''' Logix string length field ''' This is the actual string length, not the string capacity. ''' </summary> Public stringLength As Int32 ''' <summary> ''' Logix string data field ''' Default string capacity is 82 bytes ''' Change the array dimension for custom defined strings in RSLogix ''' </summary> Public stringData(81) As Byte #End Region #Region "Methods" ''' <summary> ''' Returns LOGIX_STRING as a CLR string ''' </summary> Public Overrides Function ToString() As String Return ASCIIEncoding.ASCII.GetString(stringData, 0, stringLength) End Function ''' <summary> ''' Stores CLR string as a LOGIX_STRING ''' </summary> Public Sub SetString(s As String) If s.Length > stringData.Length Then Throw New ArgumentOutOfRangeException("s", "String capacity is " & stringData.Length.ToString() & " bytes") End If Buffer.BlockCopy(ASCIIEncoding.ASCII.GetBytes(s), 0, stringData, 0, s.Length) stringLength = s.Length End Sub #End Region End Class

VB Usage

Dim logixString As New LOGIX_STRING() logixString.SetString(textBox1.Text) label1.Text = logixString.ToString()