Quick learn..
HI Axeltsob,
I'm not going to explain Marshaling as I'm no .NET expert, but I'll give you a quick run-through
1st Start with what you already (hopefully) know:
Code:
TYPE ST_CodeFonction :
STRUCT (*Handled in .NET as...*)
bEnable : BOOL; (*Byte sized boolean added this variable for completeness of explanation :ROFLMAO:*)
Fonction : INT:=1; (*INT16*)
Code : WORD:=0; (*UINT16*)
Attente : TIME:=t#1ms; (*UINT32 as in IEC61131 TIME takes Dword size*)
Position_Z : REAL:=0; (*Single*)
Position_X : REAL:=0; (*Single*)
Position_R : REAL:=0; (*Single*)
Vitesse_ZXR : REAL:=100; (*Single*)
Position_T : REAL:=0; (*Single*)
Vitesse_T : REAL:=26; (*Single*)
Debit : DWORD:=0; (*UINT32*)
END_STRUCT
END_TYPE
would give us in .NET (using c# as I'm not very known in VB.NET):
Code:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public class St_CodeFonction
{
[MarshalAs(UnmanagedType.I1)]
public Boolean bEnable;
public Int16 Fonction;
public UInt16 Code;
public UInt32 Attente;
public Single Position_Z;
public Single Position_X;
public Single Position_R;
public Single Vitesse_ZXR;
public Single Position_T;
public Single Vitesse_T;
public UInt32 Debit;
}
As the Boolean in the PLC is actual a byte size variable, you have to "explain" it also...
So the code thus far... don't look at the HOW it is shown, rather the WHAT is explained (remember I'm no .Net programmer)...
Code:
using TwinCAT.Ads;
using System.Runtime.InteropServices; //For the marshaling stuff :)
namespace WindowsFormsApplication1
{
[StructLayout(LayoutKind.Sequential, Pack=1)]
public class St_CodeFonction
{
[MarshalAs(UnmanagedType.I1)]
public Boolean bEnable;
public Int16 Fonction;
public UInt16 Code;
public UInt32 Attente;
public Single Position_Z;
public Single Position_X;
public Single Position_R;
public Single Vitesse_ZXR;
public Single Position_T;
public Single Vitesse_T;
public UInt32 Debit;
}
public partial class Form1 : Form
{
TcAdsClient tcClient = new TcAdsClient();
St_CodeFonction dataToRead;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
tcClient.Connect(801);
try
{
dataToRead = (St_CodeFonction)tcClient.ReadAny(tcClient.CreateVariableHandle(".varTest"), typeof(St_CodeFonction));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
Now we will step up one further, making it an array of code function without the string... I'll come back to it later in this explanation :wink:
so now for the VarTest as a variable of type
and recette being
Code:
TYPE ST_Recette :
STRUCT
Recette : ARRAY[1..20] OF ST_CodeFonction;
END_STRUCT
END_TYPE
This will give us in .NET:
Code:
using TwinCAT.Ads;
using System.Runtime.InteropServices; //For the marshaling stuff :)
namespace WindowsFormsApplication1
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct St_recette
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
St_CodeFonction[] Recette;
}
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct St_CodeFonction
{
[MarshalAs(UnmanagedType.I1)]
public Boolean bEnable;
public Int16 Fonction;
public UInt16 Code;
public UInt32 Attente;
public Single Position_Z;
public Single Position_X;
public Single Position_R;
public Single Vitesse_ZXR;
public Single Position_T;
public Single Vitesse_T;
public UInt32 Debit;
}
public partial class Form1 : Form
{
TcAdsClient tcClient = new TcAdsClient();
St_recette dataToRead;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
tcClient.Connect(801);
try
{
dataToRead = (St_recette)tcClient.ReadAny(tcClient.CreateVariableHandle(".varTest"), typeof(St_recette));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
and now for the additional string variable:
This is actually a special case, as it can be any size you want actually... so here we go
if you pnly want to read one string you could get away with
Code:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
public string Nom;
In SPS -> I just made it more interesting and made the string variable also array type, just to show you how powerfull it could become...
Code:
TYPE ST_Recette :
STRUCT
Nom : ARRAY [1..20] OF STRING; (*If no size is given it 80 char +1 for the NULL character so 81 bytes*)
Recette : ARRAY[1..20] OF ST_CodeFonction;
END_STRUCT
END_TYPE
will give us in .NET:
Code:
using TwinCAT.Ads;
using System.Runtime.InteropServices; //For the marshaling stuff :)
namespace WindowsFormsApplication1
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct St_recette
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public String80[] Nom;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
St_CodeFonction[] Recette;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct String80
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
public string String81;
}
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct St_CodeFonction
{
[MarshalAs(UnmanagedType.I1)]
public Boolean bEnable;
public Int16 Fonction;
public UInt16 Code;
public UInt32 Attente;
public Single Position_Z;
public Single Position_X;
public Single Position_R;
public Single Vitesse_ZXR;
public Single Position_T;
public Single Vitesse_T;
public UInt32 Debit;
}
public partial class Form1 : Form
{
TcAdsClient tcClient = new TcAdsClient();
St_recette dataToRead;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
tcClient.Connect(801);
try
{
dataToRead = (St_recette)tcClient.ReadAny(tcClient.CreateVariableHandle(".varTest"), typeof(St_recette));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
you could rewrite the .net variable Nom with you're own marshaler, but I didn't investigate that path yet... maybe futher steps, or perhaps if someone else could explain this, would be great!
It's a lot to go through, but if you have any further questions, just let me know...PM me!
Best regards,
Peter