From e8a9983367c0bed2102e1ddaf30a5f8da0b66e68 Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Fri, 21 Aug 2020 19:21:58 +0200 Subject: [PATCH] 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. --- S7.Net.UnitTest/Helpers/TestClass.cs | 4 +- S7.Net.UnitTest/Helpers/TestStruct.cs | 4 +- S7.Net.UnitTest/S7NetTestsAsync.cs | 79 ++++++++++++--------------- S7.Net.UnitTest/S7NetTestsSync.cs | 68 ++++++++++------------- S7.Net/Conversion.cs | 30 +--------- S7.Net/Enums.cs | 5 ++ S7.Net/PLCHelpers.cs | 10 +++- S7.Net/Protocol/Serialization.cs | 13 +++-- S7.Net/Types/Class.cs | 33 ++++++----- S7.Net/Types/Double.cs | 29 +--------- S7.Net/Types/LReal.cs | 57 +++++++++++++++++++ S7.Net/Types/Real.cs | 75 +++++++++++++++++++++++++ S7.Net/Types/Single.cs | 29 +--------- S7.Net/Types/Struct.cs | 38 +++++++------ S7.Net/Types/TypeHelper.cs | 43 +++++++++++++++ 15 files changed, 312 insertions(+), 205 deletions(-) create mode 100644 S7.Net/Types/LReal.cs create mode 100644 S7.Net/Types/Real.cs create mode 100644 S7.Net/Types/TypeHelper.cs diff --git a/S7.Net.UnitTest/Helpers/TestClass.cs b/S7.Net.UnitTest/Helpers/TestClass.cs index 0c221e2..c810adc 100644 --- a/S7.Net.UnitTest/Helpers/TestClass.cs +++ b/S7.Net.UnitTest/Helpers/TestClass.cs @@ -35,12 +35,12 @@ namespace S7.Net.UnitTest.Helpers /// /// DB1.DBD4 /// - public double RealVariableDouble { get; set; } + public double LRealVariable { get; set; } /// /// DB1.DBD8 /// - public float RealVariableFloat { get; set; } + public float RealVariable { get; set; } /// /// DB1.DBD12 diff --git a/S7.Net.UnitTest/Helpers/TestStruct.cs b/S7.Net.UnitTest/Helpers/TestStruct.cs index 2b70374..c40c6f4 100644 --- a/S7.Net.UnitTest/Helpers/TestStruct.cs +++ b/S7.Net.UnitTest/Helpers/TestStruct.cs @@ -35,12 +35,12 @@ namespace S7.Net.UnitTest.Helpers /// /// DB1.DBD4 /// - public double RealVariableDouble; + public double LRealVariable; /// /// DB1.DBD8 /// - public float RealVariableFloat; + public float RealVariable; /// /// DB1.DBD12 diff --git a/S7.Net.UnitTest/S7NetTestsAsync.cs b/S7.Net.UnitTest/S7NetTestsAsync.cs index e03a490..47ff56c 100644 --- a/S7.Net.UnitTest/S7NetTestsAsync.cs +++ b/S7.Net.UnitTest/S7NetTestsAsync.cs @@ -101,20 +101,13 @@ namespace S7.Net.UnitTest /// /// 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. /// [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() diff --git a/S7.Net.UnitTest/S7NetTestsSync.cs b/S7.Net.UnitTest/S7NetTestsSync.cs index e2e563e..fd63448 100644 --- a/S7.Net.UnitTest/S7NetTestsSync.cs +++ b/S7.Net.UnitTest/S7NetTestsSync.cs @@ -146,20 +146,13 @@ namespace S7.Net.UnitTest /// /// 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. /// [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] diff --git a/S7.Net/Conversion.cs b/S7.Net/Conversion.cs index 09cdd4d..44f9e25 100644 --- a/S7.Net/Conversion.cs +++ b/S7.Net/Conversion.cs @@ -199,19 +199,6 @@ namespace S7.Net return output; } - /// - /// Converts from double to DWord (DBD) - /// - /// - /// - [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; - } - /// /// Converts from float to DWord (DBD) /// @@ -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; - } - - /// - /// Converts from DWord (DBD) to double - /// - /// - /// - [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; } } diff --git a/S7.Net/Enums.cs b/S7.Net/Enums.cs index 6082d28..fdb96ff 100644 --- a/S7.Net/Enums.cs +++ b/S7.Net/Enums.cs @@ -163,6 +163,11 @@ /// Real, + /// + /// LReal variable type (64 bits, 8 bytes) + /// + LReal, + /// /// String variable type (variable) /// diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 9325f8a..ae1ff29 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -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: diff --git a/S7.Net/Protocol/Serialization.cs b/S7.Net/Protocol/Serialization.cs index 66142c2..de7fb84 100644 --- a/S7.Net/Protocol/Serialization.cs +++ b/S7.Net/Protocol/Serialization.cs @@ -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. diff --git a/S7.Net/Types/Class.cs b/S7.Net/Types/Class.cs index ab76f8d..ecb59ac 100644 --- a/S7.Net/Types/Class.cs +++ b/S7.Net/Types/Class.cs @@ -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); diff --git a/S7.Net/Types/Double.cs b/S7.Net/Types/Double.cs index 857784f..c9daf24 100644 --- a/S7.Net/Types/Double.cs +++ b/S7.Net/Types/Double.cs @@ -5,27 +5,13 @@ namespace S7.Net.Types /// /// Contains the conversion methods to convert Real from S7 plc to C# double. /// + [Obsolete("Class Double is obsolete. Use Real instead for 32bit floating point, or LReal for 64bit floating point.")] public static class Double { /// /// Converts a S7 Real (4 bytes) to double /// - 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); /// /// Converts a S7 DInt to double @@ -51,16 +37,7 @@ namespace S7.Net.Types /// /// Converts a double to S7 Real (4 bytes) /// - 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); /// /// Converts an array of double to an array of bytes diff --git a/S7.Net/Types/LReal.cs b/S7.Net/Types/LReal.cs new file mode 100644 index 0000000..c541645 --- /dev/null +++ b/S7.Net/Types/LReal.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; + +namespace S7.Net.Types +{ + /// + /// Contains the conversion methods to convert Real from S7 plc to C# double. + /// + public static class LReal + { + /// + /// Converts a S7 LReal (8 bytes) to double + /// + 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); + } + + /// + /// Converts a double to S7 LReal (8 bytes) + /// + 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; + } + + /// + /// Converts an array of double to an array of bytes + /// + public static byte[] ToByteArray(double[] value) => TypeHelper.ToByteArray(value, ToByteArray); + + /// + /// Converts an array of S7 LReal to an array of double + /// + public static double[] ToArray(byte[] bytes) => TypeHelper.ToArray(bytes, FromByteArray); + + } +} diff --git a/S7.Net/Types/Real.cs b/S7.Net/Types/Real.cs new file mode 100644 index 0000000..1d28202 --- /dev/null +++ b/S7.Net/Types/Real.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; + +namespace S7.Net.Types +{ + /// + /// Contains the conversion methods to convert Real from S7 plc to C# double. + /// + public static class Real + { + /// + /// Converts a S7 Real (4 bytes) to float + /// + 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); + } + + /// + /// Converts a float to S7 Real (4 bytes) + /// + 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] }; + } + + /// + /// Converts an array of float to an array of bytes + /// + 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; + } + + /// + /// Converts an array of S7 Real to an array of float + /// + 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; + } + + } +} diff --git a/S7.Net/Types/Single.cs b/S7.Net/Types/Single.cs index 3121efa..4f27553 100644 --- a/S7.Net/Types/Single.cs +++ b/S7.Net/Types/Single.cs @@ -5,27 +5,13 @@ namespace S7.Net.Types /// /// Contains the conversion methods to convert Real from S7 plc to C# float. /// + [Obsolete("Class Single is obsolete. Use Real instead.")] public static class Single { /// /// Converts a S7 Real (4 bytes) to float /// - 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); /// /// Converts a S7 DInt to float @@ -51,16 +37,7 @@ namespace S7.Net.Types /// /// Converts a double to S7 Real (4 bytes) /// - 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); /// /// Converts an array of float to an array of bytes diff --git a/S7.Net/Types/Struct.cs b/S7.Net/Types/Struct.cs index 733800a..1e126a5 100644 --- a/S7.Net/Types/Struct.cs +++ b/S7.Net/Types/Struct.cs @@ -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) diff --git a/S7.Net/Types/TypeHelper.cs b/S7.Net/Types/TypeHelper.cs new file mode 100644 index 0000000..983299b --- /dev/null +++ b/S7.Net/Types/TypeHelper.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace S7.Net.Types +{ + internal static class TypeHelper + { + /// + /// Converts an array of T to an array of bytes + /// + public static byte[] ToByteArray(T[] value, Func 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; + } + + /// + /// Converts an array of T repesented as S7 binary data to an array of T + /// + public static T[] ToArray(byte[] bytes, Func 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; + } + } +}