I'm fairly new to C# programming and usually deal with PLC programming and HMI/SCADA systems development. The last one involves only a bit od C/VBS scripting.
Recently, in one of my projects I upgraded/migrated a SCADA systems initially developed in LabVIEW to the Siemens WinCC application. In the LabVIEW project, there was a part responsible for sending tags values (double values bundled together with tags names and converted to string) over TCP socket to another PC running C# application (TCP Client) which was receiving array of bytes and converting it to double value.
And part of the code of TCP Client running on another PC.
TcpClient _client;
NetworkStream _stream = null;
byte[] _data = new byte[8192];
{...}
try
{
_client = new TcpClient(properties.IpAddress, properties.Port);
}
{...}
try
{
_stream = _client.GetStream();
{...}
int NumBytesInRecvBuffer = _stream.Read(_data, 0, 8192);
{...}
if(_data[3] == paramCount)
{
for(int i = 0; i < paramCount; i++)
{
double Value = MakeDouble(_data, 4+(i*8));
fmacsParams[i].NumericalValue = Value;
}
}
}
MakeDouble() function in TCP Client.
private unsafe double MakeDouble(byte[] data, int StartIndex)
{
double result;
byte* b = (byte*)&result;
b[0] = data[StartIndex + 7];
b[1] = data[StartIndex + 6];
b[2] = data[StartIndex + 5];
b[3] = data[StartIndex + 4];
b[4] = data[StartIndex + 3];
b[5] = data[StartIndex + 2];
b[6] = data[StartIndex + 1];
b[7] = data[StartIndex + 0];
return(result);
}
I managed to develop a simple application in C#, which is a TCP listener and reading the tags values from WinCC Runtime as Object
object ft107Flow = oType.InvokeMember("GetValue", System.Reflection.BindingFlags.InvokeMethod, null, wincc, new object[] { "FT107_Flow" });
Then it is converted to double and to byte array. Finally all the bytes in the array are swapped with function SwapBytes() and put to data array so it can be sent as byte array over TCP.
{...}
const int paramCount = 30;
double[] WinCC_Tags = new double[paramCount];
WinCC_Tags[0] = Convert.ToDouble(ft107Flow);
{...}
byte[] data = new byte[4 + paramCount * 8];
Array.Clear(data, 0, data.Length);
byte[] UnswappedByte;
byte[] SwappedByte;
data[3] = paramCount;
for (int i = 0; i < paramCount; i++)
{
int j;
UnswappedByte = BitConverter.GetBytes(WinCC_Tags[i]);
SwappedByte = SwapBytes(UnswappedByte, 0);
j = (4 + i * 8);
data[j] = SwappedByte[0];
data[j + 1] = SwappedByte[1];
data[j + 2] = SwappedByte[2];
data[j + 3] = SwappedByte[3];
data[j + 4] = SwappedByte[4];
data[j + 5] = SwappedByte[5];
data[j + 6] = SwappedByte[6];
data[j + 7] = SwappedByte[7];
}
The problem I'm facing at the moment is these values received by client are not the same. It seems like the precision is lost because of this double or even triple conversion (object -> double, double->byte array; byte array -> double).
For example, in WinCC Runtime the tag value reads 15.3. When it is converted to double, it equals 15.300000190734863. Then the data[12..19] = {0x40, 0x2e, 0x99, 0x99, 0xA0, 0x00, 0x00, 0x00}.
The data received by client looks as follows:
_data[12..19] = {0x40, 0x2e, 0x99, 0x99, 0xA0, 0x00, 0x00, 0x00}
Value = 15.001172065734863
It looks like the same data (byte array) are received by client, however the value after conversion does not equal to the value in WinCC Runtime.
Could you please confirm if there is a way of modifying the TCP Listener side so the TCP Client would receive the right data after conversion using MakeDouble() function and without touching TCP Client code (not allowed to modify it).
MakeDouble()function is working fine - Jan-Fokke