diff --git a/S7.Net.UnitTest/Helpers/S7TestServer.cs b/S7.Net.UnitTest/Helpers/S7TestServer.cs
index e8d6cf4..8db9328 100644
--- a/S7.Net.UnitTest/Helpers/S7TestServer.cs
+++ b/S7.Net.UnitTest/Helpers/S7TestServer.cs
@@ -9,6 +9,7 @@ namespace S7.Net.UnitTest.Helpers
static private byte[] DB1 = new byte[1024]; // Our DB1
static private byte[] DB2 = new byte[64000]; // Our DB2
static private byte[] DB3 = new byte[1024]; // Our DB3
+ static private byte[] DB4 = new byte[6] { 3, 128, 1, 0, 197, 104 }; // Our DB4
private static S7Server.TSrvCallback TheEventCallBack; // <== Static var containig the callback
private static S7Server.TSrvCallback TheReadCallBack; // <== Static var containig the callback
@@ -36,9 +37,10 @@ namespace S7.Net.UnitTest.Helpers
1, // Its number is 1 (DB1)
DB1, // Our buffer for DB1
DB1.Length); // Its size
- // Do the same for DB2 and DB3
+ // Do the same for DB2, DB3, and DB4
Server.RegisterArea(S7Server.srvAreaDB, 2, DB2, DB2.Length);
Server.RegisterArea(S7Server.srvAreaDB, 3, DB3, DB3.Length);
+ Server.RegisterArea(S7Server.srvAreaDB, 4, DB4, DB4.Length);
// Exclude read event to avoid the double report
// Set the callbacks (using the static var to avoid the garbage collect)
diff --git a/S7.Net.UnitTest/Helpers/TestClassWithNestedClass.cs b/S7.Net.UnitTest/Helpers/TestClassWithNestedClass.cs
new file mode 100644
index 0000000..0d5d417
--- /dev/null
+++ b/S7.Net.UnitTest/Helpers/TestClassWithNestedClass.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace S7.Net.UnitTest.Helpers
+{
+ class TestClassInnerWithBool
+ {
+ public bool BitVariable00 { get; set; }
+ }
+
+ class TestClassInnerWithByte
+ {
+ public byte ByteVariable00 { get; set; }
+ }
+
+ class TestClassInnerWithShort
+ {
+ public short ShortVarialbe00 { get; set; }
+ }
+
+ class TestClassWithNestedClass
+ {
+ ///
+ /// DB1.DBX0.0
+ ///
+ public bool BitVariable00 { get; set; }
+
+ ///
+ /// DB1.DBX0.1
+ ///
+ public TestClassInnerWithBool BitVariable01 { get; set; } = new TestClassInnerWithBool();
+
+ ///
+ /// DB1.DBB1.0
+ ///
+ public TestClassInnerWithByte ByteVariable02 { get; set; } = new TestClassInnerWithByte();
+
+ ///
+ /// DB1.DBX2.0
+ ///
+ public bool BitVariable03 { get; set; }
+
+ ///
+ /// DB1.DBW4
+ ///
+ public TestClassInnerWithShort ShortVariable04 { get; set; } = new TestClassInnerWithShort();
+ }
+}
diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj
index cbe6c47..dabda6d 100644
--- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj
+++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj
@@ -62,6 +62,7 @@
+
diff --git a/S7.Net.UnitTest/S7NetTestsAsync.cs b/S7.Net.UnitTest/S7NetTestsAsync.cs
index 0adb91b..31f99eb 100644
--- a/S7.Net.UnitTest/S7NetTestsAsync.cs
+++ b/S7.Net.UnitTest/S7NetTestsAsync.cs
@@ -663,6 +663,28 @@ namespace S7.Net.UnitTest
}
}
+ [TestMethod]
+ public async Task Test_Async_ReadClassWithNestedClassAfterBit()
+ {
+ Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
+
+ Assert.AreEqual(6, Types.Class.GetClassSize(new TestClassWithNestedClass()));
+
+ TestClassWithNestedClass tc = new TestClassWithNestedClass();
+ tc.BitVariable00 = true;
+ tc.BitVariable01.BitVariable00 = true;
+ tc.ByteVariable02.ByteVariable00 = 128;
+ tc.BitVariable03 = true;
+ tc.ShortVariable04.ShortVarialbe00 = -15000;
+
+ TestClassWithNestedClass tc2 = await plc.ReadClassAsync(DB4);
+ Assert.AreEqual(tc.BitVariable00, tc2.BitVariable00);
+ Assert.AreEqual(tc.BitVariable01.BitVariable00, tc2.BitVariable01.BitVariable00);
+ Assert.AreEqual(tc.ByteVariable02.ByteVariable00, tc2.ByteVariable02.ByteVariable00);
+ Assert.AreEqual(tc.BitVariable03, tc2.BitVariable03);
+ Assert.AreEqual(tc.ShortVariable04.ShortVarialbe00, tc2.ShortVariable04.ShortVarialbe00);
+ }
+
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task Test_Async_ReadStructThrowsExceptionPlcIsNotConnected()
@@ -737,7 +759,7 @@ namespace S7.Net.UnitTest
};
plc.WriteClass(tc, DB2);
- int expectedReadBytes = Types.Class.GetClassSize(tc);
+ int expectedReadBytes = (int)Types.Class.GetClassSize(tc);
TestClass tc2 = new TestClass();
// Values that are read from a class are stored inside the class itself, that is passed by reference
diff --git a/S7.Net.UnitTest/S7NetTestsSync.cs b/S7.Net.UnitTest/S7NetTestsSync.cs
index a3b01c9..1b648f2 100644
--- a/S7.Net.UnitTest/S7NetTestsSync.cs
+++ b/S7.Net.UnitTest/S7NetTestsSync.cs
@@ -40,6 +40,7 @@ namespace S7.Net.UnitTest
{
#region Constants
const int DB2 = 2;
+ const int DB4 = 4;
#endregion
#region Private fields
@@ -694,6 +695,29 @@ namespace S7.Net.UnitTest
}
}
+ [TestMethod]
+ public void T31_ReadClassWithNestedClassAfterBit()
+ {
+ Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
+
+ Assert.AreEqual(6, Types.Class.GetClassSize(new TestClassWithNestedClass()));
+
+ TestClassWithNestedClass tc = new TestClassWithNestedClass();
+ tc.BitVariable00 = true;
+ tc.BitVariable01.BitVariable00 = true;
+ tc.ByteVariable02.ByteVariable00 = 128;
+ tc.BitVariable03 = true;
+ tc.ShortVariable04.ShortVarialbe00 = -15000;
+
+ TestClassWithNestedClass tc2 = new TestClassWithNestedClass();
+ plc.ReadClass(tc2, DB4);
+ Assert.AreEqual(tc.BitVariable00, tc2.BitVariable00);
+ Assert.AreEqual(tc.BitVariable01.BitVariable00, tc2.BitVariable01.BitVariable00);
+ Assert.AreEqual(tc.ByteVariable02.ByteVariable00, tc2.ByteVariable02.ByteVariable00);
+ Assert.AreEqual(tc.BitVariable03, tc2.BitVariable03);
+ Assert.AreEqual(tc.ShortVariable04.ShortVarialbe00, tc2.ShortVariable04.ShortVarialbe00);
+ }
+
[TestMethod, ExpectedException(typeof(PlcException))]
public void T18_ReadStructThrowsIfPlcIsNotConnected()
{
@@ -767,7 +791,7 @@ namespace S7.Net.UnitTest
tc.DWordVariable = 850;
plc.WriteClass(tc, DB2);
- int expectedReadBytes = Types.Class.GetClassSize(tc);
+ int expectedReadBytes = (int)Types.Class.GetClassSize(tc);
TestClass tc2 = new TestClass();
// Values that are read from a class are stored inside the class itself, that is passed by reference
diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs
index 38cb7c6..f3457ae 100644
--- a/S7.Net/PlcAsynchronous.cs
+++ b/S7.Net/PlcAsynchronous.cs
@@ -141,7 +141,7 @@ namespace S7.Net
/// The number of read bytes
public async Task> ReadClassAsync(object sourceClass, int db, int startByteAdr = 0)
{
- int numBytes = Class.GetClassSize(sourceClass);
+ int numBytes = (int)Class.GetClassSize(sourceClass);
if (numBytes <= 0)
{
throw new Exception("The size of the class is less than 1 byte and therefore cannot be read");
diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs
index dd7030c..4c30636 100644
--- a/S7.Net/PlcSynchronous.cs
+++ b/S7.Net/PlcSynchronous.cs
@@ -162,7 +162,7 @@ namespace S7.Net
/// The number of read bytes
public int ReadClass(object sourceClass, int db, int startByteAdr = 0)
{
- int numBytes = Class.GetClassSize(sourceClass);
+ int numBytes = (int)Class.GetClassSize(sourceClass);
if (numBytes <= 0)
{
throw new Exception("The size of the class is less than 1 byte and therefore cannot be read");
diff --git a/S7.Net/Types/Class.cs b/S7.Net/Types/Class.cs
index 81f2c19..c1dc14f 100644
--- a/S7.Net/Types/Class.cs
+++ b/S7.Net/Types/Class.cs
@@ -25,10 +25,8 @@ namespace S7.Net.Types
}
- private static double GetIncreasedNumberOfBytes(double startingNumberOfBytes, Type type)
+ private static double GetIncreasedNumberOfBytes(double numBytes, Type type)
{
- double numBytes = startingNumberOfBytes;
-
switch (type.Name)
{
case "Boolean":
@@ -61,7 +59,7 @@ namespace S7.Net.Types
break;
default:
var propertyClass = Activator.CreateInstance(type);
- numBytes += GetClassSize(propertyClass);
+ numBytes = GetClassSize(propertyClass, numBytes, true);
break;
}
@@ -73,10 +71,8 @@ namespace S7.Net.Types
///
/// An instance of the class
/// the number of bytes
- public static int GetClassSize(object instance)
+ public static double GetClassSize(object instance, double numBytes = 0.0, bool isInnerProperty = false)
{
- double numBytes = 0.0;
-
var properties = GetAccessableProperties(instance.GetType());
foreach (var property in properties)
{
@@ -99,11 +95,14 @@ namespace S7.Net.Types
numBytes = GetIncreasedNumberOfBytes(numBytes, property.PropertyType);
}
}
- // enlarge numBytes to next even number because S7-Structs in a DB always will be resized to an even byte count
- numBytes = Math.Ceiling(numBytes);
- if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
- numBytes++;
- return (int)numBytes;
+ if (false == isInnerProperty)
+ {
+ // enlarge numBytes to next even number because S7-Structs in a DB always will be resized to an even byte count
+ numBytes = Math.Ceiling(numBytes);
+ if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
+ numBytes++;
+ }
+ return numBytes;
}
private static object GetPropertyValue(Type propertyType, byte[] bytes, ref double numBytes)
@@ -196,14 +195,8 @@ namespace S7.Net.Types
break;
default:
var propClass = Activator.CreateInstance(propertyType);
- var buffer = new byte[GetClassSize(propClass)];
- if (buffer.Length > 0)
- {
- Buffer.BlockCopy(bytes, (int)Math.Ceiling(numBytes), buffer, 0, buffer.Length);
- FromBytes(propClass, buffer);
- value = propClass;
- numBytes += buffer.Length;
- }
+ numBytes = FromBytes(propClass, bytes, numBytes);
+ value = propClass;
break;
}
@@ -215,16 +208,10 @@ namespace S7.Net.Types
///
/// The object to fill in the given array of bytes
/// The array of bytes
- public static void FromBytes(object sourceClass, byte[] bytes)
+ public static double FromBytes(object sourceClass, byte[] bytes, double numBytes = 0, bool isInnerClass = false)
{
if (bytes == null)
- return;
-
- if (bytes.Length != GetClassSize(sourceClass))
- return;
-
- // and decode it
- double numBytes = 0.0;
+ return numBytes;
var properties = GetAccessableProperties(sourceClass.GetType());
foreach (var property in properties)
@@ -248,6 +235,8 @@ namespace S7.Net.Types
null);
}
}
+
+ return numBytes;
}
private static void ToBytes(object propertyValue, byte[] bytes, ref double numBytes)
@@ -317,7 +306,7 @@ namespace S7.Net.Types
/// A byte array or null if fails.
public static byte[] ToBytes(object sourceClass)
{
- int size = GetClassSize(sourceClass);
+ int size = (int)GetClassSize(sourceClass);
byte[] bytes = new byte[size];
double numBytes = 0.0;