diff --git a/S7.Net.UnitTest/Helpers/TestStruct.cs b/S7.Net.UnitTest/Helpers/TestStruct.cs index c40c6f4..f07688a 100644 --- a/S7.Net.UnitTest/Helpers/TestStruct.cs +++ b/S7.Net.UnitTest/Helpers/TestStruct.cs @@ -1,4 +1,5 @@ - +using S7.Net.Types; + namespace S7.Net.UnitTest.Helpers { public struct TestStruct @@ -7,6 +8,7 @@ namespace S7.Net.UnitTest.Helpers /// DB1.DBX0.0 /// public bool BitVariable00; + public bool BitVariable01; public bool BitVariable02; public bool BitVariable03; @@ -19,6 +21,7 @@ namespace S7.Net.UnitTest.Helpers /// DB1.DBX1.0 /// public bool BitVariable10; + public bool BitVariable11; public bool BitVariable12; public bool BitVariable13; @@ -51,5 +54,17 @@ namespace S7.Net.UnitTest.Helpers /// DB1.DBD16 /// public ushort DWordVariable; + + /// + /// DB1.DBX20.0 + /// + [S7String(S7StringType.S7WString, 10)] + public string WStringVariable; + + /// + /// DB1.DBX44.0 + /// + [S7String(S7StringType.S7String, 10)] + public string StringVariable; } } diff --git a/S7.Net.UnitTest/S7NetTestsAsync.cs b/S7.Net.UnitTest/S7NetTestsAsync.cs index f90f250..cb63660 100644 --- a/S7.Net.UnitTest/S7NetTestsAsync.cs +++ b/S7.Net.UnitTest/S7NetTestsAsync.cs @@ -211,7 +211,9 @@ namespace S7.Net.UnitTest IntVariable = -15000, LRealVariable = -154.789, RealVariable = -154.789f, - DWordVariable = 850 + DWordVariable = 850, + WStringVariable = "ÄÜÉÊéà", + StringVariable = "Hallo" }; plc.WriteStruct(tc, DB2); // Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct @@ -223,6 +225,8 @@ namespace S7.Net.UnitTest Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable); Assert.AreEqual(tc.RealVariable, tc2.RealVariable); Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable); + Assert.AreEqual(tc.WStringVariable, tc2.WStringVariable); + Assert.AreEqual(tc.StringVariable, tc2.StringVariable); } /// @@ -739,7 +743,9 @@ namespace S7.Net.UnitTest IntVariable = -15000, LRealVariable = -154.789, RealVariable = -154.789f, - DWordVariable = 850 + DWordVariable = 850, + WStringVariable = "ÄÜÉÊéà", + StringVariable = "Hallo" }; plc.WriteStruct(ts, DB2); @@ -756,6 +762,8 @@ namespace S7.Net.UnitTest Assert.AreEqual(ts2.LRealVariable, ts2Generic.LRealVariable); Assert.AreEqual(ts2.RealVariable, ts2Generic.RealVariable); Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable); + Assert.AreEqual(ts2.WStringVariable, ts2Generic.WStringVariable); + Assert.AreEqual(ts2.StringVariable, ts2Generic.StringVariable); } [TestMethod] diff --git a/S7.Net.UnitTest/S7NetTestsSync.cs b/S7.Net.UnitTest/S7NetTestsSync.cs index 162c5a2..377506b 100644 --- a/S7.Net.UnitTest/S7NetTestsSync.cs +++ b/S7.Net.UnitTest/S7NetTestsSync.cs @@ -212,6 +212,9 @@ namespace S7.Net.UnitTest tc.LRealVariable = -154.789; tc.RealVariable = -154.789f; tc.DWordVariable = 850; + tc.WStringVariable = "ÄÜÉÊéà"; + tc.StringVariable = "Hallo"; + plc.WriteStruct(tc, DB2); // Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct TestStruct tc2 = (TestStruct)plc.ReadStruct(typeof(TestStruct), DB2); @@ -222,6 +225,8 @@ namespace S7.Net.UnitTest Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable); Assert.AreEqual(tc.RealVariable, tc2.RealVariable); Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable); + Assert.AreEqual(tc.WStringVariable, tc2.WStringVariable); + Assert.AreEqual(tc.StringVariable, tc2.StringVariable); } /// @@ -783,6 +788,8 @@ namespace S7.Net.UnitTest ts.LRealVariable = -154.789; ts.RealVariable = -154.789f; ts.DWordVariable = 850; + ts.WStringVariable = "ÄÜÉÊéà"; + ts.StringVariable = "Hallo"; plc.WriteStruct(ts, DB2); @@ -797,6 +804,8 @@ namespace S7.Net.UnitTest Assert.AreEqual(ts2.LRealVariable, ts2Generic.LRealVariable); Assert.AreEqual(ts2.RealVariable, ts2Generic.RealVariable); Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable); + Assert.AreEqual(ts2.WStringVariable, ts2Generic.WStringVariable); + Assert.AreEqual(ts2.StringVariable, ts2Generic.StringVariable); } [TestMethod, ExpectedException(typeof(PlcException))] diff --git a/S7.Net/Types/S7StringAttribute.cs b/S7.Net/Types/S7StringAttribute.cs new file mode 100644 index 0000000..4d6e107 --- /dev/null +++ b/S7.Net/Types/S7StringAttribute.cs @@ -0,0 +1,67 @@ +using System; + +namespace S7.Net.Types +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + public sealed class S7StringAttribute : Attribute + { + private readonly S7StringType type; + private readonly int reservedLength; + + /// + /// Initializes a new instance of the class. + /// + /// The string type. + /// Reserved length of the string in characters. + /// Please use a valid value for the string type + public S7StringAttribute(S7StringType type, int reservedLength) + { + if (!Enum.IsDefined(typeof(S7StringType), type)) + throw new ArgumentException("Please use a valid value for the string type"); + + this.type = type; + this.reservedLength = reservedLength; + } + + /// + /// Gets the type of the string. + /// + /// + /// The string type. + /// + public S7StringType Type => type; + + /// + /// Gets the reserved length of the string in characters. + /// + /// + /// The reserved length of the string in characters. + /// + public int ReservedLength => reservedLength; + + /// + /// Gets the reserved length in bytes. + /// + /// + /// The reserved length in bytes. + /// + public int ReservedLengthInBytes => type == S7StringType.S7String ? reservedLength + 2 : (reservedLength * 2) + 4; + } + + + /// + /// String type. + /// + public enum S7StringType + { + /// + /// ASCII string. + /// + S7String = VarType.S7String, + + /// + /// Unicode string. + /// + S7WString = VarType.S7WString + } +} diff --git a/S7.Net/Types/Struct.cs b/S7.Net/Types/Struct.cs index 1e126a5..1e95508 100644 --- a/S7.Net/Types/Struct.cs +++ b/S7.Net/Types/Struct.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Reflection; namespace S7.Net.Types @@ -18,11 +19,11 @@ namespace S7.Net.Types double numBytes = 0.0; var infos = structType - #if NETSTANDARD1_3 +#if NETSTANDARD1_3 .GetTypeInfo().DeclaredFields; - #else +#else .GetFields(); - #endif +#endif foreach (var info in infos) { @@ -61,6 +62,16 @@ namespace S7.Net.Types numBytes++; numBytes += 8; break; + case "String": + S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); + if (attribute == default(S7StringAttribute)) + throw new ArgumentException("Please add S7StringAttribute to the string field"); + + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + numBytes += attribute.ReservedLengthInBytes; + break; default: numBytes += GetStructSize(info.FieldType); break; @@ -91,11 +102,11 @@ namespace S7.Net.Types var infos = structValue.GetType() - #if NETSTANDARD1_3 +#if NETSTANDARD1_3 .GetTypeInfo().DeclaredFields; - #else +#else .GetFields(); - #endif +#endif foreach (var info in infos) { @@ -120,7 +131,7 @@ namespace S7.Net.Types numBytes = Math.Ceiling(numBytes); if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) numBytes++; - // hier auswerten + // get the value ushort source = Word.FromBytes(bytes[(int)numBytes + 1], bytes[(int)numBytes]); info.SetValue(structValue, source.ConvertToShort()); numBytes += 2; @@ -129,7 +140,7 @@ namespace S7.Net.Types numBytes = Math.Ceiling(numBytes); if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) numBytes++; - // hier auswerten + // get the value info.SetValue(structValue, Word.FromBytes(bytes[(int)numBytes + 1], bytes[(int)numBytes])); numBytes += 2; @@ -138,7 +149,7 @@ namespace S7.Net.Types numBytes = Math.Ceiling(numBytes); if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) numBytes++; - // hier auswerten + // get the value uint sourceUInt = DWord.FromBytes(bytes[(int)numBytes + 3], bytes[(int)numBytes + 2], bytes[(int)numBytes + 1], @@ -150,7 +161,7 @@ namespace S7.Net.Types numBytes = Math.Ceiling(numBytes); if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) numBytes++; - // hier auswerten + // get the value info.SetValue(structValue, DWord.FromBytes(bytes[(int)numBytes], bytes[(int)numBytes + 1], bytes[(int)numBytes + 2], @@ -161,7 +172,7 @@ namespace S7.Net.Types numBytes = Math.Ceiling(numBytes); if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) numBytes++; - // hier auswerten + // get the value info.SetValue(structValue, Real.FromByteArray(new byte[] { bytes[(int)numBytes], bytes[(int)numBytes + 1], bytes[(int)numBytes + 2], @@ -172,12 +183,38 @@ namespace S7.Net.Types numBytes = Math.Ceiling(numBytes); if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) numBytes++; - // hier auswerten + // get the value var data = new byte[8]; Array.Copy(bytes, (int)numBytes, data, 0, 8); info.SetValue(structValue, LReal.FromByteArray(data)); numBytes += 8; break; + case "String": + S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); + if (attribute == default(S7StringAttribute)) + throw new ArgumentException("Please add S7StringAttribute to the string field"); + + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + + // get the value + var sData = new byte[attribute.ReservedLengthInBytes]; + Array.Copy(bytes, (int)numBytes, sData, 0, sData.Length); + switch (attribute.Type) + { + case S7StringType.S7String: + info.SetValue(structValue, S7String.FromByteArray(sData)); + break; + case S7StringType.S7WString: + info.SetValue(structValue, S7WString.FromByteArray(sData)); + break; + default: + throw new ArgumentException("Please use a valid string type for the S7StringAttribute"); + } + + numBytes += sData.Length; + break; default: var buffer = new byte[GetStructSize(info.FieldType)]; if (buffer.Length == 0) @@ -209,11 +246,11 @@ namespace S7.Net.Types double numBytes = 0.0; var infos = type - #if NETSTANDARD1_3 +#if NETSTANDARD1_3 .GetTypeInfo().DeclaredFields; - #else +#else .GetFields(); - #endif +#endif foreach (var info in infos) { @@ -254,6 +291,18 @@ namespace S7.Net.Types case "Double": bytes2 = LReal.ToByteArray((double)info.GetValue(structValue)); break; + case "String": + S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); + if (attribute == default(S7StringAttribute)) + throw new ArgumentException("Please add S7StringAttribute to the string field"); + + bytes2 = attribute.Type switch + { + S7StringType.S7String => S7String.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength), + S7StringType.S7WString => S7WString.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength), + _ => throw new ArgumentException("Please use a valid string type for the S7StringAttribute") + }; + break; } if (bytes2 != null) { @@ -269,7 +318,5 @@ namespace S7.Net.Types } return bytes; } - - } }