From d051b93bdc84b70a2de4d7a25444ddfda2229aef Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Sat, 17 Oct 2020 17:54:30 +0200 Subject: [PATCH 1/9] Fix OpenAsync function to rethrow exception during connection establish. This bug was introduced in 106e9912 --- S7.Net/PlcAsynchronous.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index b6b49c6..42b61a7 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -35,6 +35,7 @@ namespace S7.Net catch(Exception) { stream.Dispose(); + throw; } } From 0b04c86cb9cb568164ec9cc22f817ddec74a4065 Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 5 Jan 2021 22:07:11 +0100 Subject: [PATCH 2/9] Generate documentation file --- S7.Net/S7.Net.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/S7.Net/S7.Net.csproj b/S7.Net/S7.Net.csproj index 868f03b..85ef368 100644 --- a/S7.Net/S7.Net.csproj +++ b/S7.Net/S7.Net.csproj @@ -20,6 +20,7 @@ portable true snupkg + true From dfcc4c7408626434fd766da468c1d1d1e5fc76bd Mon Sep 17 00:00:00 2001 From: Mike Cremer <8211507+MCPC10@users.noreply.github.com> Date: Sun, 24 Jan 2021 12:16:32 +0100 Subject: [PATCH 3/9] Added WString support --- S7.Net.UnitTest/TypeTests/S7StringTests.cs | 2 +- S7.Net.UnitTest/TypeTests/S7WStringTests.cs | 134 ++++++++++++++++++++ S7.Net/Enums.cs | 5 + S7.Net/PLCHelpers.cs | 2 + S7.Net/Protocol/Serialization.cs | 16 ++- S7.Net/Types/S7String.cs | 4 +- S7.Net/Types/S7WString.cs | 72 +++++++++++ 7 files changed, 226 insertions(+), 9 deletions(-) create mode 100644 S7.Net.UnitTest/TypeTests/S7WStringTests.cs create mode 100644 S7.Net/Types/S7WString.cs diff --git a/S7.Net.UnitTest/TypeTests/S7StringTests.cs b/S7.Net.UnitTest/TypeTests/S7StringTests.cs index 0bc8ef9..e1cdc91 100644 --- a/S7.Net.UnitTest/TypeTests/S7StringTests.cs +++ b/S7.Net.UnitTest/TypeTests/S7StringTests.cs @@ -100,7 +100,7 @@ namespace S7.Net.UnitTest.TypeTests } [TestMethod] - public void WriteAbcWithStringLargetThanReservedLength() + public void WriteAbcWithStringLargerThanReservedLength() { Assert.ThrowsException(() => S7String.ToByteArray("Abc", 2)); } diff --git a/S7.Net.UnitTest/TypeTests/S7WStringTests.cs b/S7.Net.UnitTest/TypeTests/S7WStringTests.cs new file mode 100644 index 0000000..877777c --- /dev/null +++ b/S7.Net.UnitTest/TypeTests/S7WStringTests.cs @@ -0,0 +1,134 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using S7.Net.Types; +using System; + +namespace S7.Net.UnitTest.TypeTests +{ + [TestClass] + public class S7WStringTests + { + [TestMethod] + public void ReadEmptyStringWithZeroLength() + { + AssertFromByteArrayEquals("", 0, 0 , 0, 0); + } + + [TestMethod] + public void ReadEmptyStringWithOneCharLength() + { + AssertFromByteArrayEquals("", 0, 1, 0, 0, 0, 0); + } + + [TestMethod] + public void ReadEmptyStringWithOneCharGarbage() + { + + AssertFromByteArrayEquals("", 0, 1, 0, 0, 0x00, 0x41); + } + + [TestMethod] + public void ReadMalformedStringTooShort() + { + Assert.ThrowsException(() => AssertFromByteArrayEquals("", 0, 1)); + } + + [TestMethod] + public void ReadMalformedStringSizeLargerThanCapacity() + { + Assert.ThrowsException(() => S7WString.FromByteArray(new byte[] { 0, 3, 0, 5, 0, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41})); + } + + [TestMethod] + public void ReadMalformedStringCapacityTooLarge() + { + Assert.ThrowsException(() => AssertToByteArrayAndBackEquals("", 20000, 0)); + } + + [TestMethod] + public void ReadA() + { + AssertFromByteArrayEquals("A", 0, 1, 0, 1, 0x00, 0x41); + } + + [TestMethod] + public void ReadAbc() + { + AssertFromByteArrayEquals("Abc", 0, 3, 0, 3, 0x00, 0x41, 0x00, 0x62, 0x00, 0x63); + } + + [TestMethod] + public void WriteNullWithReservedLengthZero() + { + Assert.ThrowsException(() => AssertToByteArrayAndBackEquals(null, 0, 0, 0, 0, 0)); + } + + [TestMethod] + public void WriteEmptyStringWithReservedLengthZero() + { + AssertToByteArrayAndBackEquals("", 0, 0, 0, 0, 0); + } + + [TestMethod] + public void WriteAWithReservedLengthZero() + { + AssertToByteArrayAndBackEquals("", 0, 0, 0, 0, 0); + } + + [TestMethod] + public void WriteNullWithReservedLengthOne() + { + Assert.ThrowsException(() => AssertToByteArrayAndBackEquals(null, 1, 0, 1 , 0, 0)); + } + + [TestMethod] + public void WriteEmptyStringWithReservedLengthOne() + { + AssertToByteArrayAndBackEquals("", 1, 0, 1, 0, 0, 0, 0); + } + + [TestMethod] + public void WriteAWithReservedLengthOne() + { + AssertToByteArrayAndBackEquals("A", 1, 0, 1, 0, 1, 0x00, 0x41); + } + + [TestMethod] + public void WriteAWithReservedLengthTwo() + { + AssertToByteArrayAndBackEquals("A", 2, 0, 2, 0, 1, 0x00, 0x41, 0, 0); + } + + [TestMethod] + public void WriteAbcWithStringLargerThanReservedLength() + { + Assert.ThrowsException(() => S7WString.ToByteArray("Abc", 2)); + } + + [TestMethod] + public void WriteAbcWithReservedLengthThree() + { + AssertToByteArrayAndBackEquals("Abc", 3, 0, 3, 0, 3, 0x00, 0x41, 0x00, 0x62, 0x00, 0x63); + } + + [TestMethod] + public void WriteAbcWithReservedLengthFour() + { + AssertToByteArrayAndBackEquals("Abc", 4, 0, 4, 0, 3, 0x00, 0x41, 0x00, 0x62, 0x00, 0x63, 0 , 0); + } + + private static void AssertFromByteArrayEquals(string expected, params byte[] bytes) + { + var convertedString = S7WString.FromByteArray(bytes); + Assert.AreEqual(expected, convertedString); + } + + + private static void AssertToByteArrayAndBackEquals(string value, int reservedLength, params byte[] expected) + { + var convertedData = S7WString.ToByteArray(value, reservedLength); + CollectionAssert.AreEqual(expected, convertedData); + var convertedBack = S7WString.FromByteArray(convertedData); + Assert.AreEqual(value, convertedBack); + } + } +} diff --git a/S7.Net/Enums.cs b/S7.Net/Enums.cs index c523ad7..436d94b 100644 --- a/S7.Net/Enums.cs +++ b/S7.Net/Enums.cs @@ -178,6 +178,11 @@ /// S7String, + /// + /// S7 WString variable type (variable) + /// + S7WString, + /// /// Timer variable type /// diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index 1668e08..4c4f8ac 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -125,6 +125,8 @@ namespace S7.Net return Types.String.FromByteArray(bytes); case VarType.S7String: return S7String.FromByteArray(bytes); + case VarType.S7WString: + return S7WString.FromByteArray(bytes); case VarType.Timer: if (varCount == 1) diff --git a/S7.Net/Protocol/Serialization.cs b/S7.Net/Protocol/Serialization.cs index ceecaa6..3d114b6 100644 --- a/S7.Net/Protocol/Serialization.cs +++ b/S7.Net/Protocol/Serialization.cs @@ -17,10 +17,14 @@ namespace S7.Net.Protocol { throw new Exception($"DataItem.Value is null, cannot serialize. StartAddr={dataItem.StartByteAdr} VarType={dataItem.VarType}"); } + if (dataItem.Value is string s) - return dataItem.VarType == VarType.S7String - ? S7String.ToByteArray(s, dataItem.Count) - : Types.String.ToByteArray(s, dataItem.Count); + return dataItem.VarType switch + { + VarType.S7String => S7String.ToByteArray(s, dataItem.Count), + VarType.S7WString => S7WString.ToByteArray(s, dataItem.Count), + _ => Types.String.ToByteArray(s, dataItem.Count) + }; return SerializeValue(dataItem.Value); } @@ -46,7 +50,7 @@ namespace S7.Net.Protocol case "Double": return Types.LReal.ToByteArray((double)value); case "DateTime": - return Types.DateTime.ToByteArray((System.DateTime) value); + return Types.DateTime.ToByteArray((System.DateTime)value); case "Byte[]": return (byte[])value; case "Int16[]": @@ -64,10 +68,10 @@ namespace S7.Net.Protocol 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. - var stringVal = (string) value; + var stringVal = (string)value; return Types.String.ToByteArray(stringVal, stringVal.Length); case "DateTime[]": - return Types.DateTime.ToByteArray((System.DateTime[]) value); + return Types.DateTime.ToByteArray((System.DateTime[])value); case "DateTimeLong[]": return Types.DateTimeLong.ToByteArray((System.DateTime[])value); default: diff --git a/S7.Net/Types/S7String.cs b/S7.Net/Types/S7String.cs index 6210e53..11554d7 100644 --- a/S7.Net/Types/S7String.cs +++ b/S7.Net/Types/S7String.cs @@ -45,7 +45,7 @@ namespace S7.Net.Types /// Converts a to S7 string with 2-byte header. /// /// The string to convert to byte array. - /// The length (in bytes) allocated in PLC for string excluding header. + /// The length (in characters) allocated in PLC for the string. /// A containing the string header and string value with a maximum length of + 2. public static byte[] ToByteArray(string value, int reservedLength) { @@ -54,7 +54,7 @@ namespace S7.Net.Types throw new ArgumentNullException(nameof(value)); } - if (reservedLength > byte.MaxValue) throw new ArgumentException($"The maximum string length supported is {byte.MaxValue}."); + if (reservedLength >= byte.MaxValue) throw new ArgumentException($"The maximum string length supported is {byte.MaxValue}."); var bytes = Encoding.ASCII.GetBytes(value); if (bytes.Length > reservedLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({reservedLength})."); diff --git a/S7.Net/Types/S7WString.cs b/S7.Net/Types/S7WString.cs new file mode 100644 index 0000000..c310870 --- /dev/null +++ b/S7.Net/Types/S7WString.cs @@ -0,0 +1,72 @@ +using System; +using System.Text; + +namespace S7.Net.Types +{ + /// + /// Contains the methods to convert from S7 wstrings to C# strings + /// An S7 WString has a preceding 4 byte header containing its capacity and length + /// + public static class S7WString + { + /// + /// Converts S7 bytes to a string + /// + /// + /// + public static string FromByteArray(byte[] bytes) + { + if (bytes.Length < 4) + { + throw new PlcException(ErrorCode.ReadData, "Malformed S7 WString / too short"); + } + + int size = ((bytes[0] << 8) & 0xFF) | (bytes[1] & 0xFF); + int length = ((bytes[2] << 8) & 0xFF) | (bytes[3] & 0xFF); + + if (length > size) + { + throw new PlcException(ErrorCode.ReadData, "Malformed S7 WString / length larger than capacity"); + } + + try + { + return Encoding.BigEndianUnicode.GetString(bytes, 4, length * 2); + } + catch (Exception e) + { + throw new PlcException(ErrorCode.ReadData, + $"Failed to parse {VarType.S7WString} from data. Following fields were read: size: '{size}', actual length: '{length}', total number of bytes (including header): '{bytes.Length}'.", + e); + } + + } + + /// + /// Converts a to S7 wstring with 4-byte header. + /// + /// The string to convert to byte array. + /// The length (in characters) allocated in PLC for the string. + /// A containing the string header and string value with a maximum length of + 4. + public static byte[] ToByteArray(string value, int reservedLength) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (reservedLength > 16382) throw new ArgumentException("The maximum string length supported is 16382."); + + var buffer = new byte[4 + reservedLength * 2]; + buffer[0] = (byte)((reservedLength >> 8) & 0xFF); + buffer[1] = (byte)(reservedLength & 0xFF); + buffer[2] = (byte)((value.Length >> 8) & 0xFF); + buffer[3] = (byte)(value.Length & 0xFF); + + var stringLength = Encoding.BigEndianUnicode.GetBytes(value, 0, value.Length, buffer, 4) / 2; + if (stringLength > reservedLength) throw new ArgumentException($"The provided string length ({stringLength} is larger than the specified reserved length ({reservedLength})."); + + return buffer; + } + } +} From de0a9e64dc289de35cdc3f0c567f047cdf9afab4 Mon Sep 17 00:00:00 2001 From: Mike Cremer <8211507+MCPC10@users.noreply.github.com> Date: Sun, 24 Jan 2021 13:56:07 +0100 Subject: [PATCH 4/9] Added support for strings in Struct type class --- S7.Net.UnitTest/Helpers/TestStruct.cs | 16 ++++++- S7.Net.UnitTest/S7NetTestsAsync.cs | 12 ++++- S7.Net.UnitTest/S7NetTestsSync.cs | 9 ++++ S7.Net/Types/S7StringAttribute.cs | 67 ++++++++++++++++++++++++++ S7.Net/Types/Struct.cs | 69 ++++++++++++++++++++++----- 5 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 S7.Net/Types/S7StringAttribute.cs diff --git a/S7.Net.UnitTest/Helpers/TestStruct.cs b/S7.Net.UnitTest/Helpers/TestStruct.cs index c40c6f4..7171d83 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,16 @@ 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..50ad5fc --- /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 (default value is 254). + /// Please use a valid value for the string type + public S7StringAttribute(S7StringType type, int reservedLength = 254) + { + 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, + + /// + /// Unicode string. + /// + S7WString + } +} diff --git a/S7.Net/Types/Struct.cs b/S7.Net/Types/Struct.cs index 1e126a5..5f18980 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 field you are trying to read or write"); + + 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) { @@ -178,6 +189,32 @@ namespace S7.Net.Types 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 field you are trying to read or write"); + + numBytes = Math.Ceiling(numBytes); + if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) + numBytes++; + + // hier auswerten + 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 field you are trying to read or write"); + + 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; } - - } } From 9b89acfb91edc1b229eff06286fd43885027ac83 Mon Sep 17 00:00:00 2001 From: Mike Cremer <8211507+MCPC10@users.noreply.github.com> Date: Mon, 25 Jan 2021 21:42:08 +0100 Subject: [PATCH 5/9] Removed unnecessary bitwise and's --- S7.Net/Types/S7WString.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/S7.Net/Types/S7WString.cs b/S7.Net/Types/S7WString.cs index c310870..8d8aabf 100644 --- a/S7.Net/Types/S7WString.cs +++ b/S7.Net/Types/S7WString.cs @@ -21,8 +21,8 @@ namespace S7.Net.Types throw new PlcException(ErrorCode.ReadData, "Malformed S7 WString / too short"); } - int size = ((bytes[0] << 8) & 0xFF) | (bytes[1] & 0xFF); - int length = ((bytes[2] << 8) & 0xFF) | (bytes[3] & 0xFF); + int size = (bytes[0] << 8) | bytes[1]; + int length = (bytes[2] << 8) | bytes[3]; if (length > size) { From 926d74f1d227802e2e4b1a777d5c22305662ef19 Mon Sep 17 00:00:00 2001 From: Mike Cremer <8211507+MCPC10@users.noreply.github.com> Date: Wed, 27 Jan 2021 17:55:16 +0100 Subject: [PATCH 6/9] Fixed length check and corresponding message for S7String --- S7.Net/Types/S7String.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S7.Net/Types/S7String.cs b/S7.Net/Types/S7String.cs index 11554d7..5bc383e 100644 --- a/S7.Net/Types/S7String.cs +++ b/S7.Net/Types/S7String.cs @@ -54,7 +54,7 @@ namespace S7.Net.Types throw new ArgumentNullException(nameof(value)); } - if (reservedLength >= byte.MaxValue) throw new ArgumentException($"The maximum string length supported is {byte.MaxValue}."); + if (reservedLength > 254) throw new ArgumentException($"The maximum string length supported is 254."); var bytes = Encoding.ASCII.GetBytes(value); if (bytes.Length > reservedLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({reservedLength})."); From fdd4519f64b8f0b97b8133c9ec197dfdef978218 Mon Sep 17 00:00:00 2001 From: Mike Cremer <8211507+MCPC10@users.noreply.github.com> Date: Fri, 29 Jan 2021 21:13:38 +0100 Subject: [PATCH 7/9] Minor changes --- S7.Net.UnitTest/Helpers/TestStruct.cs | 3 ++- S7.Net/Types/S7StringAttribute.cs | 4 ++-- S7.Net/Types/Struct.cs | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/S7.Net.UnitTest/Helpers/TestStruct.cs b/S7.Net.UnitTest/Helpers/TestStruct.cs index 7171d83..f07688a 100644 --- a/S7.Net.UnitTest/Helpers/TestStruct.cs +++ b/S7.Net.UnitTest/Helpers/TestStruct.cs @@ -58,7 +58,8 @@ namespace S7.Net.UnitTest.Helpers /// /// DB1.DBX20.0 /// - [S7String(S7StringType.S7WString, 10)] public string WStringVariable; + [S7String(S7StringType.S7WString, 10)] + public string WStringVariable; /// /// DB1.DBX44.0 diff --git a/S7.Net/Types/S7StringAttribute.cs b/S7.Net/Types/S7StringAttribute.cs index 50ad5fc..3c7a415 100644 --- a/S7.Net/Types/S7StringAttribute.cs +++ b/S7.Net/Types/S7StringAttribute.cs @@ -57,11 +57,11 @@ namespace S7.Net.Types /// /// ASCII string. /// - S7String, + S7String = VarType.S7String, /// /// Unicode string. /// - S7WString + S7WString = VarType.S7WString } } diff --git a/S7.Net/Types/Struct.cs b/S7.Net/Types/Struct.cs index 5f18980..357820c 100644 --- a/S7.Net/Types/Struct.cs +++ b/S7.Net/Types/Struct.cs @@ -65,7 +65,7 @@ namespace S7.Net.Types case "String": S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); if (attribute == default(S7StringAttribute)) - throw new ArgumentException("Please add S7StringAttribute to the field you are trying to read or write"); + throw new ArgumentException("Please add S7StringAttribute to the string field"); numBytes = Math.Ceiling(numBytes); if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) @@ -192,7 +192,7 @@ namespace S7.Net.Types case "String": S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); if (attribute == default(S7StringAttribute)) - throw new ArgumentException("Please add S7StringAttribute to the field you are trying to read or write"); + throw new ArgumentException("Please add S7StringAttribute to the string field"); numBytes = Math.Ceiling(numBytes); if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) @@ -294,7 +294,7 @@ namespace S7.Net.Types case "String": S7StringAttribute? attribute = info.GetCustomAttributes().SingleOrDefault(); if (attribute == default(S7StringAttribute)) - throw new ArgumentException("Please add S7StringAttribute to the field you are trying to read or write"); + throw new ArgumentException("Please add S7StringAttribute to the string field"); bytes2 = attribute.Type switch { From 37384d2a928995f6f33fdd675417ff86863ab60c Mon Sep 17 00:00:00 2001 From: Serge Camille Date: Fri, 12 Mar 2021 08:17:37 +0100 Subject: [PATCH 8/9] Fix PLC parse for type "Z"/"C" --- S7.Net/PLCAddress.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/S7.Net/PLCAddress.cs b/S7.Net/PLCAddress.cs index fd30b94..9aea1b0 100644 --- a/S7.Net/PLCAddress.cs +++ b/S7.Net/PLCAddress.cs @@ -183,7 +183,7 @@ case "Z": case "C": // Counter - dataType = DataType.Timer; + dataType = DataType.Counter; dbNumber = 0; address = int.Parse(input.Substring(1)); varType = VarType.Counter; @@ -204,4 +204,4 @@ } } } -} \ No newline at end of file +} From 924eb9c48f09de29d38346ed2486cb451ebcd554 Mon Sep 17 00:00:00 2001 From: Mike Cremer <8211507+MCPC10@users.noreply.github.com> Date: Sat, 13 Mar 2021 12:00:32 +0100 Subject: [PATCH 9/9] Minor changes + removed default length in S7StringAttribute --- S7.Net/Types/S7StringAttribute.cs | 4 ++-- S7.Net/Types/Struct.cs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/S7.Net/Types/S7StringAttribute.cs b/S7.Net/Types/S7StringAttribute.cs index 3c7a415..4d6e107 100644 --- a/S7.Net/Types/S7StringAttribute.cs +++ b/S7.Net/Types/S7StringAttribute.cs @@ -12,9 +12,9 @@ namespace S7.Net.Types /// Initializes a new instance of the class. /// /// The string type. - /// Reserved length of the string in characters (default value is 254). + /// Reserved length of the string in characters. /// Please use a valid value for the string type - public S7StringAttribute(S7StringType type, int reservedLength = 254) + public S7StringAttribute(S7StringType type, int reservedLength) { if (!Enum.IsDefined(typeof(S7StringType), type)) throw new ArgumentException("Please use a valid value for the string type"); diff --git a/S7.Net/Types/Struct.cs b/S7.Net/Types/Struct.cs index 357820c..1e95508 100644 --- a/S7.Net/Types/Struct.cs +++ b/S7.Net/Types/Struct.cs @@ -131,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; @@ -140,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; @@ -149,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], @@ -161,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], @@ -172,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], @@ -183,7 +183,7 @@ 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)); @@ -198,7 +198,7 @@ namespace S7.Net.Types if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) numBytes++; - // hier auswerten + // get the value var sData = new byte[attribute.ReservedLengthInBytes]; Array.Copy(bytes, (int)numBytes, sData, 0, sData.Length); switch (attribute.Type)