KB 0028
Handling strings inside S7 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.

S7 strings are hybrids that can be accessed like an atomic type but are actually stored as structured data with three fields:

Field Data Type Description
MaxLength Byte Describes the string's capacity. Valid values are 1 to 254.
ActualLength Byte Describes the length of the string. Valid values are 0 to MaxLength.
Data Byte[] String data

Step 7 allows you to define custom strings with capacity less than 254 bytes. Custom strings are the reason that ASComm.NET cannot determine the string's capacity by examining the return packet.

The table below shows the byte sequence if the STRING[4] data type is specified with output value 'AB':

Byte Value Description
0 4 Max length of the string
1 2 Actual length of the string
2 41 hex 'A'
3 42 hex 'B'
4 0 Padding character, could contain any value
5 0 Padding character, could contain any value

 

RESOLUTION

The ASComm.NET structured data handling algorithm cannot directly map a CLR string type to a S7 string type due to ambiguity in the returned data size. ASComm.NET simply sees a string returned as a three field UDT/PDT and there is currently no way to inform ASComm.NET that there is a string in the UDT/PDT.

A very simple workaround is available. We have created a class that represents an S7 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.

 

A very simple workaround is available. We have created a class that represents an S7 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 S7_STRING { #region Fields /// <summary> /// Maximum length field /// </summary> public Byte maxLen; /// <summary> /// Actual length field /// </summary> public Byte actualLen; /// <summary> /// Data field /// Change the array dimension for custom defined strings in Step 7 /// </summary> public Byte[] data = new Byte[254]; #endregion Fields #region Methods /// <summary> /// Returns S7_STRING as a CLR string /// </summary> public override String ToString() { return ASCIIEncoding.ASCII.GetString(data, 0, actualLen); } /// <summary> /// Stores CLR string as a S7_STRING /// </summary> public void SetString(String s) { if (s.Length > data.Length) throw new ArgumentOutOfRangeException("s", "Maximum allowable string length is " + data.Length.ToString() + " bytes"); Buffer.BlockCopy(ASCIIEncoding.ASCII.GetBytes(s), 0, data, 0, s.Length); actualLen = (byte)s.Length; } #endregion Methods };

C# Usage

S7_STRING s7String = new S7_STRING(); s7String.SetString(textBox1.Text); label1.Text = s7String.ToString();

Visual Basic

Visual Basic Class

<StructLayout(LayoutKind.Sequential)>_ Public Class S7_STRING #Region "Fields" ''' <summary> ''' Maximum length field ''' </summary> Public maxLen As Int32 ''' <summary> ''' Actual length field ''' </summary> Public actualLen As Int32 ''' <summary> ''' Data field ''' Change the array dimension for custom defined strings in RSLogix ''' </summary> Public data As Byte() = New Byte(254) {} #End Region #Region "Methods" ''' <summary> ''' Returns S7_STRING as a CLR string ''' </summary> Public Overloads Function ToString() As [String] Return ASCIIEncoding.ASCII.GetString(data, 0, actualLen) End Function ''' <summary> ''' Stores CLR string as an S7_STRING ''' </summary> Public Sub SetString(s As String) If s.Length > data.Length Then Throw New ArgumentOutOfRangeException("s", "Maximum allowable string length is " & data.Length.ToString() & " bytes") End If Buffer.BlockCopy(ASCIIEncoding.ASCII.GetBytes(s), 0, data, 0, s.Length) actualLen = s.Length End Sub #End Region End Class

Visual Basic Usage

Dim s7String As New S7_STRING() s7String.SetString(textBox1.Text) label1.Text = s7String.ToString()