Add LReal support, kill old "Double" sham.

- Adds true support for 64bit double / LReal datatype.
- Set old Types.Single and Types.Double to obselete. Both class names use .NET types instead of S7 type names, contrary to all other types.
- Remove already obsoleted conversion from DWord to Real. Why is this even necessary?
  For users caring about converting from DWord, they can still convert to single. But unless we get LWord support, there won't be a direct conversion to double/LReal.
- Adjust unit tests by removing rounding, testing directly double read/writes.

There is quite a bit of breaking changes at least in the automated Struct and object functions which automatically translate .NET types to appropriate S7 types.
My consideration was that if we ever want to support 64bit types, there is no way against breaking those existing incorrect conversions from 64bit .NET double to 32 bit S7 Real variables.
This commit is contained in:
Serge Camille
2020-08-21 19:21:58 +02:00
parent 3c91aa02b0
commit e8a9983367
15 changed files with 312 additions and 205 deletions

View File

@@ -35,12 +35,12 @@ namespace S7.Net.UnitTest.Helpers
/// <summary>
/// DB1.DBD4
/// </summary>
public double RealVariableDouble { get; set; }
public double LRealVariable { get; set; }
/// <summary>
/// DB1.DBD8
/// </summary>
public float RealVariableFloat { get; set; }
public float RealVariable { get; set; }
/// <summary>
/// DB1.DBD12

View File

@@ -35,12 +35,12 @@ namespace S7.Net.UnitTest.Helpers
/// <summary>
/// DB1.DBD4
/// </summary>
public double RealVariableDouble;
public double LRealVariable;
/// <summary>
/// DB1.DBD8
/// </summary>
public float RealVariableFloat;
public float RealVariable;
/// <summary>
/// DB1.DBD12

View File

@@ -101,20 +101,13 @@ namespace S7.Net.UnitTest
/// <summary>
/// Read/Write a single REAL with a single request.
/// Test that writing a double and reading it gives the correct value.
/// Test that writing a float and reading it gives the correct value.
/// </summary>
[TestMethod]
public async Task Test_Async_WriteAndReadRealVariables()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
// Reading and writing a double is quite complicated, because it needs to be converted to DWord before the write,
// then reconvert to double after the read.
double val = 35.68729;
await plc.WriteAsync("DB1.DBD40", val.ConvertToUInt());
double result = ((uint)await plc.ReadAsync("DB1.DBD40")).ConvertToDouble();
Assert.AreEqual(val, Math.Round(result, 5)); // float lose precision, so i need to round it
// Reading and writing a float is quite complicated, because it needs to be converted to DWord before the write,
// then reconvert to float after the read. Float values can contain only 7 digits, so no precision is lost.
float val2 = 1234567;
@@ -162,8 +155,8 @@ namespace S7.Net.UnitTest
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
LRealVariable = -154.789,
RealVariable = -154.789f,
DWordVariable = 850
};
@@ -175,8 +168,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariableDouble, Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable);
Assert.AreEqual(tc.RealVariable, tc2.RealVariable);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
}
@@ -219,8 +212,8 @@ namespace S7.Net.UnitTest
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
LRealVariable = -154.789,
RealVariable = -154.789f,
DWordVariable = 850
};
plc.WriteStruct(tc, DB2);
@@ -230,8 +223,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariableDouble, Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable);
Assert.AreEqual(tc.RealVariable, tc2.RealVariable);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
}
@@ -584,8 +577,8 @@ namespace S7.Net.UnitTest
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
LRealVariable = -154.789,
RealVariable = -154.789f,
DWordVariable = 850
};
@@ -599,8 +592,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariableDouble, tc2.RealVariableDouble, 0.1);
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable, 0.1);
Assert.AreEqual(tc.RealVariable, tc2.RealVariable);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
Assert.AreEqual(TestClassWithPrivateSetters.PRIVATE_SETTER_VALUE, tc2.PrivateSetterProperty);
@@ -632,8 +625,8 @@ namespace S7.Net.UnitTest
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
LRealVariable = -154.789,
RealVariable = -154.789f,
DWordVariable = 850
};
@@ -649,8 +642,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc2.BitVariable10, tc2Generic.BitVariable10);
Assert.AreEqual(tc2.DIntVariable, tc2Generic.DIntVariable);
Assert.AreEqual(tc2.IntVariable, tc2Generic.IntVariable);
Assert.AreEqual(Math.Round(tc2.RealVariableDouble, 3), Math.Round(tc2Generic.RealVariableDouble, 3));
Assert.AreEqual(tc2.RealVariableFloat, tc2Generic.RealVariableFloat);
Assert.AreEqual(Math.Round(tc2.LRealVariable, 3), Math.Round(tc2Generic.LRealVariable, 3));
Assert.AreEqual(tc2.RealVariable, tc2Generic.RealVariable);
Assert.AreEqual(tc2.DWordVariable, tc2Generic.DWordVariable);
}
@@ -675,8 +668,8 @@ namespace S7.Net.UnitTest
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
LRealVariable = -154.789,
RealVariable = -154.789f,
DWordVariable = 850
};
@@ -689,8 +682,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc2Generic.BitVariable00, tc2GenericWithClassFactory.BitVariable00);
Assert.AreEqual(tc2Generic.BitVariable10, tc2GenericWithClassFactory.BitVariable10);
Assert.AreEqual(tc2Generic.DIntVariable, tc2GenericWithClassFactory.DIntVariable);
Assert.AreEqual(Math.Round(tc2Generic.RealVariableDouble, 3), Math.Round(tc2GenericWithClassFactory.RealVariableDouble, 3));
Assert.AreEqual(tc2Generic.RealVariableFloat, tc2GenericWithClassFactory.RealVariableFloat);
Assert.AreEqual(Math.Round(tc2Generic.LRealVariable, 3), Math.Round(tc2GenericWithClassFactory.LRealVariable, 3));
Assert.AreEqual(tc2Generic.RealVariable, tc2GenericWithClassFactory.RealVariable);
Assert.AreEqual(tc2Generic.DWordVariable, tc2GenericWithClassFactory.DWordVariable);
}
@@ -747,8 +740,8 @@ namespace S7.Net.UnitTest
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
LRealVariable = -154.789,
RealVariable = -154.789f,
DWordVariable = 850
};
@@ -763,8 +756,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(ts2.BitVariable10, ts2Generic.BitVariable10);
Assert.AreEqual(ts2.DIntVariable, ts2Generic.DIntVariable);
Assert.AreEqual(ts2.IntVariable, ts2Generic.IntVariable);
Assert.AreEqual(Math.Round(ts2.RealVariableDouble, 3), Math.Round(ts2Generic.RealVariableDouble, 3));
Assert.AreEqual(ts2.RealVariableFloat, ts2Generic.RealVariableFloat);
Assert.AreEqual(ts2.LRealVariable, ts2Generic.LRealVariable);
Assert.AreEqual(ts2.RealVariable, ts2Generic.RealVariable);
Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable);
}
@@ -792,8 +785,8 @@ namespace S7.Net.UnitTest
BitVariable10 = true,
DIntVariable = -100000,
IntVariable = -15000,
RealVariableDouble = -154.789,
RealVariableFloat = -154.789f,
LRealVariable = -154.789,
RealVariable = -154.789f,
DWordVariable = 850
};
plc.WriteClass(tc, DB2);
@@ -883,16 +876,16 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.CustomTypes[1].Bools[1], tc2.CustomTypes[1].Bools[1]);
}
[TestMethod]
public async Task Test_Async_ReadWriteDouble()
{
double test_value = 55.66;
await plc.WriteAsync("DB1.DBD0", test_value);
var helper = await plc.ReadAsync("DB1.DBD0");
double test_value2 = Conversion.ConvertToDouble((uint)helper);
//[TestMethod]
//public async Task Test_Async_ReadWriteDouble()
//{
// double test_value = 55.66;
// await plc.WriteAsync("DB1.DBD0", test_value);
// var helper = await plc.ReadAsync("DB1.DBD0");
// double test_value2 = Conversion.ConvertToDouble((uint)helper);
Assert.AreEqual(test_value, test_value2, 0.01, "Compare Write/Read"); //Need delta here because S7 only has 32 bit reals
}
// Assert.AreEqual(test_value, test_value2, 0.01, "Compare Write/Read"); //Need delta here because S7 only has 32 bit reals
//}
[TestMethod]
public async Task Test_Async_ReadWriteSingle()

View File

@@ -146,20 +146,13 @@ namespace S7.Net.UnitTest
/// <summary>
/// Read/Write a single REAL with a single request.
/// Test that writing a double and reading it gives the correct value.
/// Test that writing a float and reading it gives the correct value.
/// </summary>
[TestMethod]
public void T03_WriteAndReadRealVariables()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
// Reading and writing a double is quite complicated, because it needs to be converted to DWord before the write,
// then reconvert to double after the read.
double val = 35.68729;
plc.Write("DB1.DBD40", val.ConvertToUInt());
double result = ((uint)plc.Read("DB1.DBD40")).ConvertToDouble();
Assert.AreEqual(val, Math.Round(result, 5)); // float lose precision, so i need to round it
// Reading and writing a float is quite complicated, because it needs to be converted to DWord before the write,
// then reconvert to float after the read. Float values can contain only 7 digits, so no precision is lost.
float val2 = 1234567;
@@ -186,8 +179,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.LRealVariable = -154.789;
tc.RealVariable = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
TestClass tc2 = new TestClass();
@@ -197,8 +190,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(Math.Round(tc.RealVariableDouble, 3), Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable);
Assert.AreEqual(tc.RealVariable, tc2.RealVariable);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
}
@@ -215,8 +208,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.LRealVariable = -154.789;
tc.RealVariable = -154.789f;
tc.DWordVariable = 850;
plc.WriteStruct(tc, DB2);
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
@@ -225,8 +218,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(tc.RealVariableDouble, Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable);
Assert.AreEqual(tc.RealVariable, tc2.RealVariable);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
}
@@ -575,8 +568,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.LRealVariable = -154.789;
tc.RealVariable = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
@@ -588,8 +581,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10);
Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable);
Assert.AreEqual(tc.IntVariable, tc2.IntVariable);
Assert.AreEqual(Math.Round(tc.RealVariableDouble, 3), Math.Round(tc2.RealVariableDouble, 3));
Assert.AreEqual(tc.RealVariableFloat, tc2.RealVariableFloat);
Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable);
Assert.AreEqual(tc.RealVariable, tc2.RealVariable);
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
Assert.AreEqual(TestClassWithPrivateSetters.PRIVATE_SETTER_VALUE, tc2.PrivateSetterProperty);
@@ -620,8 +613,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.LRealVariable = -154.789;
tc.RealVariable = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
@@ -635,8 +628,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc2.BitVariable10, tc2Generic.BitVariable10);
Assert.AreEqual(tc2.DIntVariable, tc2Generic.DIntVariable);
Assert.AreEqual(tc2.IntVariable, tc2Generic.IntVariable);
Assert.AreEqual(Math.Round(tc2.RealVariableDouble, 3), Math.Round(tc2Generic.RealVariableDouble, 3));
Assert.AreEqual(tc2.RealVariableFloat, tc2Generic.RealVariableFloat);
Assert.AreEqual(Math.Round(tc2.LRealVariable, 3), Math.Round(tc2Generic.LRealVariable, 3));
Assert.AreEqual(tc2.RealVariable, tc2Generic.RealVariable);
Assert.AreEqual(tc2.DWordVariable, tc2Generic.DWordVariable);
}
@@ -663,8 +656,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.LRealVariable = -154.789;
tc.RealVariable = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
@@ -677,8 +670,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc2Generic.BitVariable10, tc2GenericWithClassFactory.BitVariable10);
Assert.AreEqual(tc2Generic.DIntVariable, tc2GenericWithClassFactory.DIntVariable);
Assert.AreEqual(tc2Generic.IntVariable, tc2GenericWithClassFactory.IntVariable);
Assert.AreEqual(Math.Round(tc2Generic.RealVariableDouble, 3), Math.Round(tc2GenericWithClassFactory.RealVariableDouble, 3));
Assert.AreEqual(tc2Generic.RealVariableFloat, tc2GenericWithClassFactory.RealVariableFloat);
Assert.AreEqual(Math.Round(tc2Generic.LRealVariable, 3), Math.Round(tc2GenericWithClassFactory.LRealVariable, 3));
Assert.AreEqual(tc2Generic.RealVariable, tc2GenericWithClassFactory.RealVariable);
Assert.AreEqual(tc2Generic.DWordVariable, tc2GenericWithClassFactory.DWordVariable);
}
@@ -786,8 +779,8 @@ namespace S7.Net.UnitTest
ts.BitVariable10 = true;
ts.DIntVariable = -100000;
ts.IntVariable = -15000;
ts.RealVariableDouble = -154.789;
ts.RealVariableFloat = -154.789f;
ts.LRealVariable = -154.789;
ts.RealVariable = -154.789f;
ts.DWordVariable = 850;
plc.WriteStruct(ts, DB2);
@@ -800,8 +793,8 @@ namespace S7.Net.UnitTest
Assert.AreEqual(ts2.BitVariable10, ts2Generic.BitVariable10);
Assert.AreEqual(ts2.DIntVariable, ts2Generic.DIntVariable);
Assert.AreEqual(ts2.IntVariable, ts2Generic.IntVariable);
Assert.AreEqual(Math.Round(ts2.RealVariableDouble, 3), Math.Round(ts2Generic.RealVariableDouble, 3));
Assert.AreEqual(ts2.RealVariableFloat, ts2Generic.RealVariableFloat);
Assert.AreEqual(ts2.LRealVariable, ts2Generic.LRealVariable);
Assert.AreEqual(ts2.RealVariable, ts2Generic.RealVariable);
Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable);
}
@@ -831,8 +824,8 @@ namespace S7.Net.UnitTest
tc.BitVariable10 = true;
tc.DIntVariable = -100000;
tc.IntVariable = -15000;
tc.RealVariableDouble = -154.789;
tc.RealVariableFloat = -154.789f;
tc.LRealVariable = -154.789;
tc.RealVariable = -154.789f;
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
@@ -948,11 +941,10 @@ namespace S7.Net.UnitTest
public void T26_ReadWriteDouble()
{
double test_value = 55.66;
plc.Write("DB1.DBD0", test_value);
var helper = plc.Read("DB1.DBD0");
double test_value2 = Conversion.ConvertToDouble((uint)helper);
plc.Write(DataType.DataBlock, 1, 0, test_value);
var result = (double)plc.Read(DataType.DataBlock, 1, 0, VarType.LReal, 1);
Assert.AreEqual(test_value, test_value2, 0.01, "Compare Write/Read"); //Need delta here because S7 only has 32 bit reals
Assert.AreEqual(test_value, result, "Compare Write/Read");
}
[TestMethod]

View File

@@ -199,19 +199,6 @@ namespace S7.Net
return output;
}
/// <summary>
/// Converts from double to DWord (DBD)
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[Obsolete("Double support is obsolete. Use ConvertToUInt(float) instead.")]
public static UInt32 ConvertToUInt(this double input)
{
uint output;
output = S7.Net.Types.DWord.FromByteArray(S7.Net.Types.Double.ToByteArray(input));
return output;
}
/// <summary>
/// Converts from float to DWord (DBD)
/// </summary>
@@ -220,20 +207,7 @@ namespace S7.Net
public static UInt32 ConvertToUInt(this float input)
{
uint output;
output = S7.Net.Types.DWord.FromByteArray(S7.Net.Types.Single.ToByteArray(input));
return output;
}
/// <summary>
/// Converts from DWord (DBD) to double
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[Obsolete("Double support is obsolete. Use ConvertToFloat(uint) instead.")]
public static double ConvertToDouble(this uint input)
{
double output;
output = S7.Net.Types.Double.FromByteArray(S7.Net.Types.DWord.ToByteArray(input));
output = S7.Net.Types.DWord.FromByteArray(S7.Net.Types.Real.ToByteArray(input));
return output;
}
@@ -245,7 +219,7 @@ namespace S7.Net
public static float ConvertToFloat(this uint input)
{
float output;
output = S7.Net.Types.Single.FromByteArray(S7.Net.Types.DWord.ToByteArray(input));
output = S7.Net.Types.Real.FromByteArray(S7.Net.Types.DWord.ToByteArray(input));
return output;
}
}

View File

@@ -163,6 +163,11 @@
/// </summary>
Real,
/// <summary>
/// LReal variable type (64 bits, 8 bytes)
/// </summary>
LReal,
/// <summary>
/// String variable type (variable)
/// </summary>

View File

@@ -111,9 +111,14 @@ namespace S7.Net
return DInt.ToArray(bytes);
case VarType.Real:
if (varCount == 1)
return Types.Single.FromByteArray(bytes);
return Types.Real.FromByteArray(bytes);
else
return Types.Single.ToArray(bytes);
return Types.Real.ToArray(bytes);
case VarType.LReal:
if (varCount == 1)
return Types.LReal.FromByteArray(bytes);
else
return Types.LReal.ToArray(bytes);
case VarType.String:
return Types.String.FromByteArray(bytes);
@@ -192,6 +197,7 @@ namespace S7.Net
case VarType.DInt:
case VarType.Real:
return varCount * 4;
case VarType.LReal:
case VarType.DateTime:
return varCount * 8;
case VarType.DateTimeLong:

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using S7.Net.Types;
namespace S7.Net.Protocol
@@ -37,10 +38,10 @@ namespace S7.Net.Protocol
return Types.DInt.ToByteArray((Int32)value);
case "UInt32":
return Types.DWord.ToByteArray((UInt32)value);
case "Double":
return Types.Double.ToByteArray((double)value);
case "Single":
return Types.Single.ToByteArray((float)value);
return Types.Real.ToByteArray((float)value);
case "Double":
return Types.LReal.ToByteArray((double)value);
case "DateTime":
return Types.DateTime.ToByteArray((System.DateTime) value);
case "Byte[]":
@@ -53,10 +54,10 @@ namespace S7.Net.Protocol
return Types.DInt.ToByteArray((Int32[])value);
case "UInt32[]":
return Types.DWord.ToByteArray((UInt32[])value);
case "Double[]":
return Types.Double.ToByteArray((double[])value);
case "Single[]":
return Types.Single.ToByteArray((float[])value);
return Types.Real.ToByteArray((float[])value);
case "Double[]":
return Types.LReal.ToByteArray((double[])value);
case "String":
// Hack: This is backwards compatible with the old code, but functionally it's broken
// if the consumer does not pay attention to string length.

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
@@ -51,12 +52,17 @@ namespace S7.Net.Types
numBytes += 4;
break;
case "Single":
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
numBytes += 4;
break;
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
numBytes += 8;
break;
default:
var propertyClass = Activator.CreateInstance(type);
numBytes = GetClassSize(propertyClass, numBytes, true);
@@ -168,12 +174,12 @@ namespace S7.Net.Types
bytes[(int)numBytes + 3]);
numBytes += 4;
break;
case "Double":
case "Single":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
// hier auswerten
value = Double.FromByteArray(
value = Real.FromByteArray(
new byte[] {
bytes[(int)numBytes],
bytes[(int)numBytes + 1],
@@ -181,18 +187,15 @@ namespace S7.Net.Types
bytes[(int)numBytes + 3] });
numBytes += 4;
break;
case "Single":
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
var buffer = new byte[8];
Array.Copy(bytes, (int)numBytes, buffer, 0, 8);
// hier auswerten
value = Single.FromByteArray(
new byte[] {
bytes[(int)numBytes],
bytes[(int)numBytes + 1],
bytes[(int)numBytes + 2],
bytes[(int)numBytes + 3] });
numBytes += 4;
value = LReal.FromByteArray(buffer);
numBytes += 8;
break;
default:
var propClass = Activator.CreateInstance(propertyType);
@@ -277,11 +280,11 @@ namespace S7.Net.Types
case "UInt32":
bytes2 = DWord.ToByteArray((UInt32)propertyValue);
break;
case "Double":
bytes2 = Double.ToByteArray((double)propertyValue);
break;
case "Single":
bytes2 = Single.ToByteArray((float)propertyValue);
bytes2 = Real.ToByteArray((float)propertyValue);
break;
case "Double":
bytes2 = LReal.ToByteArray((double)propertyValue);
break;
default:
numBytes = ToBytes(propertyValue, bytes, numBytes);

View File

@@ -5,27 +5,13 @@ namespace S7.Net.Types
/// <summary>
/// Contains the conversion methods to convert Real from S7 plc to C# double.
/// </summary>
[Obsolete("Class Double is obsolete. Use Real instead for 32bit floating point, or LReal for 64bit floating point.")]
public static class Double
{
/// <summary>
/// Converts a S7 Real (4 bytes) to double
/// </summary>
public static double FromByteArray(byte[] bytes)
{
if (bytes.Length != 4)
{
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 4 bytes.");
}
// sps uses bigending so we have to reverse if platform needs
if (BitConverter.IsLittleEndian)
{
// create deep copy of the array and reverse
bytes = new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
return BitConverter.ToSingle(bytes, 0);
}
public static double FromByteArray(byte[] bytes) => Real.FromByteArray(bytes);
/// <summary>
/// Converts a S7 DInt to double
@@ -51,16 +37,7 @@ namespace S7.Net.Types
/// <summary>
/// Converts a double to S7 Real (4 bytes)
/// </summary>
public static byte[] ToByteArray(double value)
{
byte[] bytes = BitConverter.GetBytes((float)(value));
// sps uses bigending so we have to check if platform is same
if (!BitConverter.IsLittleEndian) return bytes;
// create deep copy of the array and reverse
return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
public static byte[] ToByteArray(double value) => Real.ToByteArray((float)value);
/// <summary>
/// Converts an array of double to an array of bytes

57
S7.Net/Types/LReal.cs Normal file
View File

@@ -0,0 +1,57 @@
using System;
using System.IO;
namespace S7.Net.Types
{
/// <summary>
/// Contains the conversion methods to convert Real from S7 plc to C# double.
/// </summary>
public static class LReal
{
/// <summary>
/// Converts a S7 LReal (8 bytes) to double
/// </summary>
public static double FromByteArray(byte[] bytes)
{
if (bytes.Length != 8)
{
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 8 bytes.");
}
var buffer = bytes;
// sps uses bigending so we have to reverse if platform needs
if (BitConverter.IsLittleEndian)
{
Array.Reverse(buffer);
}
return BitConverter.ToDouble(buffer, 0);
}
/// <summary>
/// Converts a double to S7 LReal (8 bytes)
/// </summary>
public static byte[] ToByteArray(double value)
{
var bytes = BitConverter.GetBytes(value);
// sps uses bigending so we have to check if platform is same
if (BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}
return bytes;
}
/// <summary>
/// Converts an array of double to an array of bytes
/// </summary>
public static byte[] ToByteArray(double[] value) => TypeHelper.ToByteArray(value, ToByteArray);
/// <summary>
/// Converts an array of S7 LReal to an array of double
/// </summary>
public static double[] ToArray(byte[] bytes) => TypeHelper.ToArray(bytes, FromByteArray);
}
}

75
S7.Net/Types/Real.cs Normal file
View File

@@ -0,0 +1,75 @@
using System;
using System.IO;
namespace S7.Net.Types
{
/// <summary>
/// Contains the conversion methods to convert Real from S7 plc to C# double.
/// </summary>
public static class Real
{
/// <summary>
/// Converts a S7 Real (4 bytes) to float
/// </summary>
public static float FromByteArray(byte[] bytes)
{
if (bytes.Length != 4)
{
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 4 bytes.");
}
// sps uses bigending so we have to reverse if platform needs
if (BitConverter.IsLittleEndian)
{
// create deep copy of the array and reverse
bytes = new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
/// Converts a float to S7 Real (4 bytes)
/// </summary>
public static byte[] ToByteArray(float value)
{
byte[] bytes = BitConverter.GetBytes(value);
// sps uses bigending so we have to check if platform is same
if (!BitConverter.IsLittleEndian) return bytes;
// create deep copy of the array and reverse
return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
/// <summary>
/// Converts an array of float to an array of bytes
/// </summary>
public static byte[] ToByteArray(float[] value)
{
var buffer = new byte[4 * value.Length];
var stream = new MemoryStream(buffer);
foreach (var val in value)
{
stream.Write(ToByteArray(val), 0, 4);
}
return buffer;
}
/// <summary>
/// Converts an array of S7 Real to an array of float
/// </summary>
public static float[] ToArray(byte[] bytes)
{
var values = new float[bytes.Length / 4];
int counter = 0;
for (int cnt = 0; cnt < bytes.Length / 4; cnt++)
values[cnt] = FromByteArray(new byte[] { bytes[counter++], bytes[counter++], bytes[counter++], bytes[counter++] });
return values;
}
}
}

View File

@@ -5,27 +5,13 @@ namespace S7.Net.Types
/// <summary>
/// Contains the conversion methods to convert Real from S7 plc to C# float.
/// </summary>
[Obsolete("Class Single is obsolete. Use Real instead.")]
public static class Single
{
/// <summary>
/// Converts a S7 Real (4 bytes) to float
/// </summary>
public static float FromByteArray(byte[] bytes)
{
if (bytes.Length != 4)
{
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 4 bytes.");
}
// sps uses bigending so we have to reverse if platform needs
if (BitConverter.IsLittleEndian)
{
// create deep copy of the array and reverse
bytes = new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
return BitConverter.ToSingle(bytes, 0);
}
public static float FromByteArray(byte[] bytes) => Real.FromByteArray(bytes);
/// <summary>
/// Converts a S7 DInt to float
@@ -51,16 +37,7 @@ namespace S7.Net.Types
/// <summary>
/// Converts a double to S7 Real (4 bytes)
/// </summary>
public static byte[] ToByteArray(float value)
{
byte[] bytes = BitConverter.GetBytes((float)(value));
// sps uses bigending so we have to check if platform is same
if (!BitConverter.IsLittleEndian) return bytes;
// create deep copy of the array and reverse
return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
}
public static byte[] ToByteArray(float value) => Real.ToByteArray(value);
/// <summary>
/// Converts an array of float to an array of bytes

View File

@@ -50,12 +50,17 @@ namespace S7.Net.Types
numBytes += 4;
break;
case "Single":
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
numBytes += 4;
break;
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
numBytes += 8;
break;
default:
numBytes += GetStructSize(info.FieldType);
break;
@@ -152,28 +157,27 @@ namespace S7.Net.Types
bytes[(int)numBytes + 3]));
numBytes += 4;
break;
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
// hier auswerten
info.SetValue(structValue, Double.FromByteArray(new byte[] { bytes[(int)numBytes],
bytes[(int)numBytes + 1],
bytes[(int)numBytes + 2],
bytes[(int)numBytes + 3] }));
numBytes += 4;
break;
case "Single":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
// hier auswerten
info.SetValue(structValue, Single.FromByteArray(new byte[] { bytes[(int)numBytes],
info.SetValue(structValue, Real.FromByteArray(new byte[] { bytes[(int)numBytes],
bytes[(int)numBytes + 1],
bytes[(int)numBytes + 2],
bytes[(int)numBytes + 3] }));
numBytes += 4;
break;
case "Double":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
// hier auswerten
var data = new byte[8];
Array.Copy(bytes, (int)numBytes, data, 0, 8);
info.SetValue(structValue, LReal.FromByteArray(data));
numBytes += 8;
break;
default:
var buffer = new byte[GetStructSize(info.FieldType)];
if (buffer.Length == 0)
@@ -244,11 +248,11 @@ namespace S7.Net.Types
case "UInt32":
bytes2 = DWord.ToByteArray((UInt32)info.GetValue(structValue));
break;
case "Double":
bytes2 = Double.ToByteArray((double)info.GetValue(structValue));
break;
case "Single":
bytes2 = Single.ToByteArray((float)info.GetValue(structValue));
bytes2 = Real.ToByteArray((float)info.GetValue(structValue));
break;
case "Double":
bytes2 = LReal.ToByteArray((double)info.GetValue(structValue));
break;
}
if (bytes2 != null)

View File

@@ -0,0 +1,43 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace S7.Net.Types
{
internal static class TypeHelper
{
/// <summary>
/// Converts an array of T to an array of bytes
/// </summary>
public static byte[] ToByteArray<T>(T[] value, Func<T, byte[]> converter) where T : struct
{
var buffer = new byte[Marshal.SizeOf(default(T)) * value.Length];
var stream = new MemoryStream(buffer);
foreach (var val in value)
{
stream.Write(converter(val), 0, 4);
}
return buffer;
}
/// <summary>
/// Converts an array of T repesented as S7 binary data to an array of T
/// </summary>
public static T[] ToArray<T>(byte[] bytes, Func<byte[], T> converter)
{
var typeSize = Marshal.SizeOf(default(T));
var entries = bytes.Length / typeSize;
var values = new T[entries];
for(int i = 0; i < entries; ++i)
{
var buffer = new byte[typeSize];
Array.Copy(bytes, i * typeSize, buffer, 0, typeSize);
values[i] = converter(buffer);
}
return values;
}
}
}