Array of user-defined string as OUT port in FB with C#

Hello, I was looking GitHub - PLCnext/CSharpExamples: Collection of various C# sample code for PLCnext Technology controllers. · GitHub and several posts here in the Community (Arrays in C# or Return Array From C# FB) but I don’t have quitte clear how to proceed (or even if it’s possible) to create an array of user-defined strings in C#. [list] []To create my own string size it’s clear, I have to declare something like: [/list] // Define a string data type with a maximum string length of 200 characters // Size is string length + 4 byte header + 1 byte terminating zero + padding for two byte alignment // Mark the declaration with a String attribute and define the length [String(200)] [StructLayout(LayoutKind.Explicit, Size = 206)] public struct TString200 { // Fields [FieldOffset(0)] public IecStringEx s; // Methods //ctor is needed to set the maximum size and called in the initialization public void ctor() { this.s.maximumLength = 200; } public void rctor() { this.s.maximumLength = 200; } } [list] []To create my own array, it’s a bit confusing because create a struct is recommended, but it’s also more or less clear. I have to declare something like: [/list] // The IecArray must be defined as a struct with a fixed size. // The array definition MUST have following Attributes: // 1. Array (Actually only one-dimensional arrays are supported by the PCWorx Engineer) // 2. ArrayDimension // 3. DataType to define the data type of the array elements [Array(1), ArrayDimension(0, ArrayProperties.lowerBound, ArrayProperties.upperBound), DataType("TString200")] [StructLayout(LayoutKind.Explicit, Size = ArrayProperties.size)] public struct MyArray { // Helper containing constants to have a // clear and maintainable definition for boundaries and size struct ArrayProperties { public const int lowerBound = 0; public const int upperBound = 9; // the size must be changed to the correct size of your elements times the amount of elements public const int size = (upperBound - lowerBound + 1) * sizeof(int); } // Fields // The field "Anchor" defines the beginning of the array. [FieldOffset(0)] // The Anchor's data type is the child data type of the array public int Anchor; // The constants LB and UB define the upper and lower bound. Boundaries will be checked by using them. public const int LB = ArrayProperties.lowerBound; public const int UB = ArrayProperties.upperBound; public int this[int index] { get { if (index >= LB && index <= UB) { unsafe { fixed (int* pValue = &Anchor;) { int result = *(pValue + index); return result; } } } else { throw new IndexOutOfRangeException(); } } set { if (index >= LB && index <= UB) { unsafe { fixed (int* pValue = &Anchor;) { *(pValue + index) = value; } } } else { throw new IndexOutOfRangeException(); } } } } [list] [*]When I have to give the data type to the array, it’s supposed that I have to put my-own-string-type in the attribute DataType(“TString200” ) ?? [*]When I wnat to use it as a PORT, it’s enough with [Output] public MyArray iIN; or it’s required like [Output, DataType(nameof(MyArray ))] public MyArray OUT; ?? [/list] Then, after having this array defined in C# FB, what is needed to define from PnE to link a variable with this paramenter, an array or a struct? Thanks.

Hi Javir, well, this is indeed not very intuitive. I’ve create a small example which hopefully helps in your implementation. Type defnition using Eclr; using System; using System.Iec61131Lib; using System.Runtime.InteropServices; using Iec61131.Engineering.Prototypes.Types; using Iec61131.Engineering.Prototypes.Common; namespace PLCnext_CSharpExamples { [String(200)] [StructLayout(LayoutKind.Explicit, Size = 206)] public struct MyTString200 { // Fields [FieldOffset(0)] public IecStringEx s; // Methods //ctor is needed to set the maximum size and called in the initialization public void ctor() { this.s.maximumLength = 200; } public void rctor() { this.s.maximumLength = 200; } } // The IecArray must be defined as a struct with a fixed size. // The array definition MUST have following Attributes: // 1. Array (Actually only one-dimensional arrays are supported by the PCWorx Engineer) // 2. ArrayDimension // 3. DataType to define the data type of the array elements [Array(1), ArrayDimension(0, ArrayProperties.lowerBound, ArrayProperties.upperBound), DataType("MyTString200")] [StructLayout(LayoutKind.Explicit, Size = ArrayProperties.size)] public struct TestArrayT200 { // Helper containing constants to have a // clear and maintainable definition for boundaries and size struct ArrayProperties { public const int lowerBound = 0; public const int upperBound = 9; // the size must be changed to the correct size of your elements times the amount of elements public const int size = (upperBound - lowerBound + 1) * 206; //206 is the Size of MyTString200 } // Fields // The field "Anchor" defines the beginning of the array. [FieldOffset(0)] // The Anchor's data type is the child data type of the array public MyTString200 Anchor; // The constants LB and UB define the upper and lower bound. Boundaries will be checked by using them. public const int LB = ArrayProperties.lowerBound; public const int UB = ArrayProperties.upperBound; public MyTString200 this[int index] { get { if (index >= LB && index <= UB) { unsafe { fixed (MyTString200* pValue = &Anchor;) { MyTString200 result = *(pValue + index); return result; } } } else { throw new IndexOutOfRangeException(); } } set { if (index >= LB && index <= UB) { unsafe { fixed (MyTString200* pValue = &Anchor;) { *(pValue + index) = value; } } } else { throw new IndexOutOfRangeException(); } } } // Methods //ctor is needed to initialize the string elements public void ctor() { unsafe { fixed (MyTString200* pValue = &Anchor;) { for (int index = LB; index <= UB; index++) { pValue[index].ctor(); } } } } } } Usage in a functionblock using Iec61131.Engineering.Prototypes.Common; using Iec61131.Engineering.Prototypes.Methods; using Iec61131.Engineering.Prototypes.Types; using Iec61131.Engineering.Prototypes.Variables; using System; using System.Iec61131Lib; namespace PLCnext_CSharpExamples { [FunctionBlock] public class FB_with_udts { [Input] public TestArrayT200 IN1; [Input] public int Index; [Output] public MyTString200 OUT; MyTString200 dummy; [Initialization] public void __Init() { OUT.ctor(); IN1.ctor(); } [Execution] public void __Process() { MyTString200 dummy = IN1[Index]; IecStringEx.Copy(ref dummy.s, ref OUT.s); } } } I’ll tidy this example up and add this in our GitHub example collection. BR, Frank