# TwinCAT3 - ADS - schreiben von Variablen struct mit array



## Kurt (16 August 2018)

TC3 4022.22, ADSdll 4.2.166.0 


TC Typen und Variable (vereinfacht)

```
TYPE ST_WorkList_1 :
STRUCT
    dnPcsCounter: UDINT;
    lrSheetlength: LREAL;
    arPointData : ARRAY[0..1] OF ST_WorkPoint;
END_STRUCT
END_TYPE
TYPE ST_WorkPoint :
STRUCT
    bActive: BYTE;
    lrLength: LREAL;
    lrBendingPos: LREAL;
END_STRUCT
END_TYPE

stProdList: ST_WorkList_1; // im Namespace HMI also HMI.stProdList
```

// C# Datendefinition

```
[Serializable]
    [StructLayout(LayoutKind.Sequential, Pack = 0)]
    public class WorkList
    {
        //public string sName;
        public uint nCountPcs;
        public double lrSheetlength;
        public double lrRadius;
        public double lrBendPreposition;
        public double lrProductionSpeed;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] // test mit und ohne Marshal
        public WorkPoint[] arPointData = new WorkPoint[2];

        public WorkList()
        {
            ;
        }
    }

    [Serializable]
    [StructLayout(LayoutKind.Sequential, Pack = 0)]
    public class WorkPoint
    {
        public byte bActive;
        public double lrLength;
        public double lrBendingPos;

        public WorkPoint()
        {
            ;
        }
    }
```

LESEN funktioniert:

```
string plcVarName = "HMI.stProdList";
data = (WorkList) AdsConnect.Connection.ReadSymbol(plcVarName, typeof (WorkList), true);
```

SCHREIBEN krieg ich nicht hin

```
// WriteAny schreibt die erste struktur correct, das array hat  illegale werte
 var symbolInfo = AdsConnect.Connection.ReadSymbolInfo(plcVarName);
 AdsConnect.Connection.WriteAny((uint)symbolInfo.IndexGroup, (uint)symbolInfo.IndexOffset, data);
```


```
// WriteSymbol bringt Exception: "Memory stream is not expandable."
 AdsConnect.Connection.WriteSymbol(plcVarName, prodData, true); // egal ob true oder false
```


```
// ADSstream schreibt ab der ersten lreal mist in die Spsvariable
AdsStream dataStream = new AdsStream();
BinaryWriter binWrite = new BinaryWriter(dataStream);
dataStream.Position = 0;
              
binWrite.Write(prodData.nCountPcs);
binWrite.Write(prodData.lrSheetlength);
foreach (var sample in prodData.arPointData)
{
     binWrite.Write(sample.bActive);
     binWrite.Write(sample.lrLength);
     binWrite.Write(sample.lrBendingPos);
}

var hVar = AdsConnect.Connection.CreateVariableHandle(plcVarName);
AdsConnect.Connection.Write(hVar, dataStream);
```

hat jemand einen tipp, wie das Schreiben von zusammegesetzten vars funktioniert.
die infosysbeispiele helfen mir nicht sehr.

kurt


----------



## Guga (17 August 2018)

Mal so eben aus der Hüfte geschossen (habe mir das Beispiel jetzt nicht im Detail angeschaut). 

 Wieso Pack-Mode = 0? TC3 nutzt ein 8 Byte Alignment und in deinem Typ ST_WorkPoint ist das relevant. (Alignment in TC3 per Attribute umändern oder eben "hier" per Marshalling)



Guga


----------



## Kurt (18 August 2018)

weil der Hr.Beckhoff das so im infosys schreibt...
https://infosys.beckhoff.com/index....csample_net/html/twincat.ads.sample07.htm&id=

```
[B][StructLayout(LayoutKind.Sequential, Pack=1)] // TwinCAT2->Pack=1 TwinCAT3->Pack=0[/B]
public class SimpleStruct
{
    public double lrealVal;
    public int dintVal1;
}
```


----------



## Guga (18 August 2018)

Link =Tc2=1 Byte alignement (fuer x86 systeme).
Tc3 ist immer 8 Byte aligned

Guga


----------



## Kurt (22 August 2018)

Da ich nicht erfolgreich war, hab ich den Hersteller kontaktiert:
Seine Lösung:

SPS Definition:

```
TYPE ST_WorkList_1 :
STRUCT
    dnPcsCounter: UDINT;
    lrSheetlength: LREAL;
    arPointData : ARRAY[0..1] OF ST_WorkPoint;
END_STRUCT
END_TYPE
TYPE ST_WorkPoint :
STRUCT
    bActive: BYTE;
    lrLength: LREAL;
    lrBendingPos: LREAL;
END_STRUCT
END_TYPE
// Variable:
    stProdList: ST_WorkList_1;
```

Datendefinition im C# prog

```
[StructLayout(LayoutKind.Sequential, Pack = 0)]
    public class WorkList
    {
        public uint nCountPcs;
        public double lrSheetlength;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] 
        public WorkPoint[] arPointData;

        public WorkList()
        {
            arPointData = new WorkPoint[2];
        }
    }

    public struct WorkPoint  // Anmerkung: warum hier struct sein muss und keine class ?
    {
        public byte bActive;
        public double lrLength;
        public double lrBendingPos;

        public WorkPoint(byte bActive, double lrLength, double lrBendingPos)
        {
            this.bActive = bActive;
            this.lrLength = lrLength;
            this.lrBendingPos = lrBendingPos;
        }
    }
```

Lesen und schreiben mit Any:

```
private string plcVarName = "HMI.stProdList";
private WorkList _worklist = new WorkList();

var handle = Connection.CreateVariableHandle(plcVarName);
_worklist = (WorkList)Connection.ReadAny(handle, typeof(WorkList));

var symbolInfo = Connection.ReadSymbolInfo(plcVarName);
Connection.WriteAny((uint)symbolInfo.IndexGroup, (uint)symbolInfo.IndexOffset, _worklist);
```

ich habs probiert TC3 4022.22 64Bit und es funktioniert.


----------



## Guga (22 August 2018)

Structs are _value types_ and classes are _reference types_.
Du willst  keinen Pointer auf irgendwohin im Speicher (class) anbieten sondern tatsächlich die Werte (struct).

Guga


----------

