From 9c3f95ce73d9568df7703d2099f097c0742c6dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sun, 26 Dec 2021 19:26:04 +0100 Subject: [PATCH 1/7] Flowed cancellation token to TcpClient.ConnectAsync in .NET 5.0 target --- S7.Net/PlcAsynchronous.cs | 9 +++++++-- S7.Net/S7.Net.csproj | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index a58f629..f75cf23 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -25,7 +25,7 @@ namespace S7.Net /// A task that represents the asynchronous open operation. public async Task OpenAsync(CancellationToken cancellationToken = default) { - var stream = await ConnectAsync().ConfigureAwait(false); + var stream = await ConnectAsync(cancellationToken).ConfigureAwait(false); try { await queue.Enqueue(async () => @@ -44,11 +44,16 @@ namespace S7.Net } } - private async Task ConnectAsync() + private async Task ConnectAsync(CancellationToken cancellationToken) { tcpClient = new TcpClient(); ConfigureConnection(); + +#if NET5_0_OR_GREATER + await tcpClient.ConnectAsync(IP, Port, cancellationToken).ConfigureAwait(false); +#else await tcpClient.ConnectAsync(IP, Port).ConfigureAwait(false); +#endif return tcpClient.GetStream(); } diff --git a/S7.Net/S7.Net.csproj b/S7.Net/S7.Net.csproj index 85ef368..fa85b08 100644 --- a/S7.Net/S7.Net.csproj +++ b/S7.Net/S7.Net.csproj @@ -1,7 +1,7 @@  - net452;netstandard2.0;netstandard1.3 + net452;netstandard2.0;netstandard1.3;net5.0 true Properties\S7.Net.snk S7.Net.UnitTest From 5d3f01e59e61720b3dedd3342b433824b6287214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Sun, 26 Dec 2021 19:26:44 +0100 Subject: [PATCH 2/7] Updated AppVeyor image to VS 2022 to allow better conditional compilation --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 3e3100d..28da709 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -image: Visual Studio 2019 +image: Visual Studio 2022 configuration: Release install: - choco install gitversion.portable -y From d808ea5eb6944c782f452f5f3ed38a1ec9db22cc Mon Sep 17 00:00:00 2001 From: diego Date: Thu, 16 Jun 2022 17:21:23 +0200 Subject: [PATCH 3/7] Give the option of changing the Encoding used to serialize and deserialize S7String, instead of always using Encoding.ASCII --- S7.Net/Types/S7String.cs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/S7.Net/Types/S7String.cs b/S7.Net/Types/S7String.cs index 5bc383e..671c8e6 100644 --- a/S7.Net/Types/S7String.cs +++ b/S7.Net/Types/S7String.cs @@ -9,6 +9,11 @@ namespace S7.Net.Types /// public static class S7String { + /// + /// The Encoding used when serializing and deserializing S7String (Encoding.ASCII by default) + /// + public static Encoding StringEncoding { get; set; } + /// /// Converts S7 bytes to a string /// @@ -30,7 +35,8 @@ namespace S7.Net.Types try { - return Encoding.ASCII.GetString(bytes, 2, length); + if (StringEncoding == null) StringEncoding = Encoding.ASCII; + return StringEncoding.GetString(bytes, 2, length); } catch (Exception e) { @@ -38,7 +44,6 @@ namespace S7.Net.Types $"Failed to parse {VarType.S7String} from data. Following fields were read: size: '{size}', actual length: '{length}', total number of bytes (including header): '{bytes.Length}'.", e); } - } /// @@ -54,9 +59,10 @@ namespace S7.Net.Types throw new ArgumentNullException(nameof(value)); } - if (reservedLength > 254) throw new ArgumentException($"The maximum string length supported is 254."); - - var bytes = Encoding.ASCII.GetBytes(value); + if (reservedLength > 254) throw new ArgumentException($"The maximum string length supported is 254."); + + if (StringEncoding == null) StringEncoding = Encoding.ASCII; + var bytes = StringEncoding.GetBytes(value); if (bytes.Length > reservedLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({reservedLength})."); var buffer = new byte[2 + reservedLength]; @@ -65,5 +71,13 @@ namespace S7.Net.Types buffer[1] = (byte)bytes.Length; return buffer; } + + /// + /// Initializes default static properties + /// + static S7String() + { + StringEncoding = Encoding.ASCII; + } } } From 2ecd2c6b4936fda6d7f5d7e2cd9d947a3c9dc6ab Mon Sep 17 00:00:00 2001 From: diego Date: Fri, 17 Jun 2022 11:08:09 +0200 Subject: [PATCH 4/7] Changes following code review --- S7.Net/Types/S7String.cs | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/S7.Net/Types/S7String.cs b/S7.Net/Types/S7String.cs index 671c8e6..8e67db6 100644 --- a/S7.Net/Types/S7String.cs +++ b/S7.Net/Types/S7String.cs @@ -8,11 +8,18 @@ namespace S7.Net.Types /// An S7 String has a preceeding 2 byte header containing its capacity and length /// public static class S7String - { - /// - /// The Encoding used when serializing and deserializing S7String (Encoding.ASCII by default) - /// - public static Encoding StringEncoding { get; set; } + { + private static Encoding stringEncoding; + + /// + /// The Encoding used when serializing and deserializing S7String (Encoding.ASCII by default) + /// + /// StringEncoding must not be null + public static Encoding StringEncoding + { + get => stringEncoding ??= Encoding.ASCII; + set => stringEncoding = value ?? throw new ArgumentNullException(nameof(StringEncoding)); + } /// /// Converts S7 bytes to a string @@ -35,7 +42,6 @@ namespace S7.Net.Types try { - if (StringEncoding == null) StringEncoding = Encoding.ASCII; return StringEncoding.GetString(bytes, 2, length); } catch (Exception e) @@ -59,9 +65,8 @@ namespace S7.Net.Types throw new ArgumentNullException(nameof(value)); } - if (reservedLength > 254) throw new ArgumentException($"The maximum string length supported is 254."); - - if (StringEncoding == null) StringEncoding = Encoding.ASCII; + if (reservedLength > 254) throw new ArgumentException($"The maximum string length supported is 254."); + var bytes = StringEncoding.GetBytes(value); if (bytes.Length > reservedLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({reservedLength})."); @@ -71,13 +76,5 @@ namespace S7.Net.Types buffer[1] = (byte)bytes.Length; return buffer; } - - /// - /// Initializes default static properties - /// - static S7String() - { - StringEncoding = Encoding.ASCII; - } } } From ec554ddb59c3011b9631e36641f0e33a6d891710 Mon Sep 17 00:00:00 2001 From: diego Date: Fri, 17 Jun 2022 11:23:58 +0200 Subject: [PATCH 5/7] Better stringEncoding initialization, as per code review comments --- S7.Net/Types/S7String.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/S7.Net/Types/S7String.cs b/S7.Net/Types/S7String.cs index 8e67db6..46c4808 100644 --- a/S7.Net/Types/S7String.cs +++ b/S7.Net/Types/S7String.cs @@ -9,7 +9,7 @@ namespace S7.Net.Types /// public static class S7String { - private static Encoding stringEncoding; + private static Encoding stringEncoding = Encoding.ASCII; /// /// The Encoding used when serializing and deserializing S7String (Encoding.ASCII by default) @@ -17,7 +17,7 @@ namespace S7.Net.Types /// StringEncoding must not be null public static Encoding StringEncoding { - get => stringEncoding ??= Encoding.ASCII; + get => stringEncoding; set => stringEncoding = value ?? throw new ArgumentNullException(nameof(StringEncoding)); } From 142f1ba90eed639e7b3a7063f6522b5a444de86d Mon Sep 17 00:00:00 2001 From: Mike Cremer <8211507+MCPC10@users.noreply.github.com> Date: Sat, 10 Dec 2022 18:59:25 +0100 Subject: [PATCH 6/7] Added support string/wstring for class type --- S7.Net.UnitTest/Helpers/TestClass.cs | 13 ++++ S7.Net.UnitTest/S7NetTestsAsync.cs | 29 +++++++-- S7.Net.UnitTest/S7NetTestsSync.cs | 19 ++++++ S7.Net/Types/Class.cs | 97 +++++++++++++++++----------- S7.Net/Types/S7StringAttribute.cs | 2 +- 5 files changed, 115 insertions(+), 45 deletions(-) diff --git a/S7.Net.UnitTest/Helpers/TestClass.cs b/S7.Net.UnitTest/Helpers/TestClass.cs index c810adc..2f2c775 100644 --- a/S7.Net.UnitTest/Helpers/TestClass.cs +++ b/S7.Net.UnitTest/Helpers/TestClass.cs @@ -1,4 +1,6 @@  +using S7.Net.Types; + namespace S7.Net.UnitTest.Helpers { class TestClass @@ -51,5 +53,16 @@ namespace S7.Net.UnitTest.Helpers /// DB1.DBD16 /// public ushort DWordVariable { get; set; } + + /// + /// DB1.DBX20.0 + /// + [S7String(S7StringType.S7WString, 10)] + public string WStringVariable { get; set; } + /// + /// DB1.DBX44.0 + /// + [S7String(S7StringType.S7String, 10)] + public string StringVariable { get; set; } } } diff --git a/S7.Net.UnitTest/S7NetTestsAsync.cs b/S7.Net.UnitTest/S7NetTestsAsync.cs index f94ddb8..731e802 100644 --- a/S7.Net.UnitTest/S7NetTestsAsync.cs +++ b/S7.Net.UnitTest/S7NetTestsAsync.cs @@ -7,6 +7,7 @@ using S7.Net.Types; using S7.UnitTest.Helpers; using System.Threading.Tasks; using System.Threading; +using System.Security.Cryptography; #endregion @@ -154,7 +155,9 @@ namespace S7.Net.UnitTest IntVariable = -15000, LRealVariable = -154.789, RealVariable = -154.789f, - DWordVariable = 850 + DWordVariable = 850, + WStringVariable = "ÄÜÉÊéà", + StringVariable = "Hallo" }; await plc.WriteClassAsync(tc, DB2); @@ -168,6 +171,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); } [TestMethod] @@ -580,7 +585,9 @@ namespace S7.Net.UnitTest IntVariable = -15000, LRealVariable = -154.789, RealVariable = -154.789f, - DWordVariable = 850 + DWordVariable = 850, + WStringVariable = "ÄÜÉÊéà", + StringVariable = "Hallo" }; await plc.WriteClassAsync(tc, DB2); @@ -628,7 +635,10 @@ namespace S7.Net.UnitTest IntVariable = -15000, LRealVariable = -154.789, RealVariable = -154.789f, - DWordVariable = 850 + DWordVariable = 850, + WStringVariable = "ÄÜÉÊéà", + StringVariable = "Hallo" + }; await plc.WriteClassAsync(tc, DB2); @@ -646,6 +656,9 @@ namespace S7.Net.UnitTest Assert.AreEqual(Math.Round(tc2.LRealVariable, 3), Math.Round(tc2Generic.LRealVariable, 3)); Assert.AreEqual(tc2.RealVariable, tc2Generic.RealVariable); Assert.AreEqual(tc2.DWordVariable, tc2Generic.DWordVariable); + Assert.AreEqual(tc2.WStringVariable, tc2Generic.WStringVariable); + Assert.AreEqual(tc2.StringVariable, tc2Generic.StringVariable); + } [TestMethod] @@ -671,7 +684,9 @@ namespace S7.Net.UnitTest IntVariable = -15000, LRealVariable = -154.789, RealVariable = -154.789f, - DWordVariable = 850 + DWordVariable = 850, + WStringVariable = "ÄÜÉÊéà", + StringVariable = "Hallo" }; await plc.WriteClassAsync(tc, DB2); @@ -686,6 +701,8 @@ namespace S7.Net.UnitTest Assert.AreEqual(Math.Round(tc2Generic.LRealVariable, 3), Math.Round(tc2GenericWithClassFactory.LRealVariable, 3)); Assert.AreEqual(tc2Generic.RealVariable, tc2GenericWithClassFactory.RealVariable); Assert.AreEqual(tc2Generic.DWordVariable, tc2GenericWithClassFactory.DWordVariable); + Assert.AreEqual(tc2Generic.WStringVariable, tc2GenericWithClassFactory.WStringVariable); + Assert.AreEqual(tc2Generic.StringVariable, tc2GenericWithClassFactory.StringVariable); } [TestMethod] @@ -792,7 +809,9 @@ namespace S7.Net.UnitTest IntVariable = -15000, LRealVariable = -154.789, RealVariable = -154.789f, - DWordVariable = 850 + DWordVariable = 850, + WStringVariable = "ÄÜÉÊéà", + StringVariable = "Hallo" }; plc.WriteClass(tc, DB2); diff --git a/S7.Net.UnitTest/S7NetTestsSync.cs b/S7.Net.UnitTest/S7NetTestsSync.cs index c84c1a7..76317c0 100644 --- a/S7.Net.UnitTest/S7NetTestsSync.cs +++ b/S7.Net.UnitTest/S7NetTestsSync.cs @@ -5,6 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using S7.Net.UnitTest.Helpers; using S7.Net.Types; using S7.UnitTest.Helpers; +using System.Security.Cryptography; #endregion @@ -183,6 +184,9 @@ namespace S7.Net.UnitTest tc.LRealVariable = -154.789; tc.RealVariable = -154.789f; tc.DWordVariable = 850; + tc.WStringVariable = "ÄÜÉÊéà"; + tc.StringVariable = "Hallo"; + plc.WriteClass(tc, DB2); TestClass tc2 = new TestClass(); // Values that are read from a class are stored inside the class itself, that is passed by reference @@ -194,6 +198,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); } /// @@ -577,6 +583,8 @@ namespace S7.Net.UnitTest tc.LRealVariable = -154.789; tc.RealVariable = -154.789f; tc.DWordVariable = 850; + tc.WStringVariable = "ÄÜÉÊéà"; + tc.StringVariable = "Hallo"; plc.WriteClass(tc, DB2); @@ -622,6 +630,8 @@ namespace S7.Net.UnitTest tc.LRealVariable = -154.789; tc.RealVariable = -154.789f; tc.DWordVariable = 850; + tc.WStringVariable = "ÄÜÉÊéà"; + tc.StringVariable = "Hallo"; plc.WriteClass(tc, DB2); @@ -637,6 +647,8 @@ namespace S7.Net.UnitTest Assert.AreEqual(Math.Round(tc2.LRealVariable, 3), Math.Round(tc2Generic.LRealVariable, 3)); Assert.AreEqual(tc2.RealVariable, tc2Generic.RealVariable); Assert.AreEqual(tc2.DWordVariable, tc2Generic.DWordVariable); + Assert.AreEqual(tc2.WStringVariable, tc2Generic.WStringVariable); + Assert.AreEqual(tc2.StringVariable, tc2Generic.StringVariable); } [TestMethod, ExpectedException(typeof(PlcException))] @@ -665,6 +677,8 @@ namespace S7.Net.UnitTest tc.LRealVariable = -154.789; tc.RealVariable = -154.789f; tc.DWordVariable = 850; + tc.WStringVariable = "ÄÜÉÊéà"; + tc.StringVariable = "Hallo"; plc.WriteClass(tc, DB2); @@ -679,6 +693,8 @@ namespace S7.Net.UnitTest Assert.AreEqual(Math.Round(tc2Generic.LRealVariable, 3), Math.Round(tc2GenericWithClassFactory.LRealVariable, 3)); Assert.AreEqual(tc2Generic.RealVariable, tc2GenericWithClassFactory.RealVariable); Assert.AreEqual(tc2Generic.DWordVariable, tc2GenericWithClassFactory.DWordVariable); + Assert.AreEqual(tc2Generic.WStringVariable, tc2GenericWithClassFactory.WStringVariable); + Assert.AreEqual(tc2Generic.StringVariable, tc2GenericWithClassFactory.StringVariable); } [TestMethod, ExpectedException(typeof(PlcException))] @@ -837,6 +853,9 @@ namespace S7.Net.UnitTest tc.LRealVariable = -154.789; tc.RealVariable = -154.789f; tc.DWordVariable = 850; + tc.WStringVariable = "ÄÜÉÊéà"; + tc.StringVariable = "Hallo"; + plc.WriteClass(tc, DB2); int expectedReadBytes = (int)Types.Class.GetClassSize(tc); diff --git a/S7.Net/Types/Class.cs b/S7.Net/Types/Class.cs index 0dd78fa..1aefc00 100644 --- a/S7.Net/Types/Class.cs +++ b/S7.Net/Types/Class.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; namespace S7.Net.Types { @@ -25,7 +26,7 @@ namespace S7.Net.Types } - private static double GetIncreasedNumberOfBytes(double numBytes, Type type) + private static double GetIncreasedNumberOfBytes(double numBytes, Type type, PropertyInfo? propertyInfo) { switch (type.Name) { @@ -38,30 +39,30 @@ namespace S7.Net.Types break; case "Int16": case "UInt16": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); numBytes += 2; break; case "Int32": case "UInt32": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); numBytes += 4; break; case "Single": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); numBytes += 4; break; case "Double": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); numBytes += 8; break; + case "String": + S7StringAttribute? attribute = propertyInfo?.GetCustomAttributes().SingleOrDefault(); + if (attribute == default(S7StringAttribute)) + throw new ArgumentException("Please add S7StringAttribute to the string field"); + + IncrementToEven(ref numBytes); + numBytes += attribute.ReservedLengthInBytes; + break; default: var propertyClass = Activator.CreateInstance(type); numBytes = GetClassSize(propertyClass, numBytes, true); @@ -93,12 +94,12 @@ namespace S7.Net.Types IncrementToEven(ref numBytes); for (int i = 0; i < array.Length; i++) { - numBytes = GetIncreasedNumberOfBytes(numBytes, elementType); + numBytes = GetIncreasedNumberOfBytes(numBytes, elementType, property); } } else { - numBytes = GetIncreasedNumberOfBytes(numBytes, property.PropertyType); + numBytes = GetIncreasedNumberOfBytes(numBytes, property.PropertyType, property); } } if (false == isInnerProperty) @@ -111,7 +112,7 @@ namespace S7.Net.Types return numBytes; } - private static object? GetPropertyValue(Type propertyType, byte[] bytes, ref double numBytes) + private static object? GetPropertyValue(Type propertyType, PropertyInfo? propertyInfo, byte[] bytes, ref double numBytes) { object? value = null; @@ -133,26 +134,20 @@ namespace S7.Net.Types numBytes++; break; case "Int16": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); // hier auswerten ushort source = Word.FromBytes(bytes[(int)numBytes + 1], bytes[(int)numBytes]); value = source.ConvertToShort(); numBytes += 2; break; case "UInt16": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); // hier auswerten value = Word.FromBytes(bytes[(int)numBytes + 1], bytes[(int)numBytes]); numBytes += 2; break; case "Int32": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); // hier auswerten uint sourceUInt = DWord.FromBytes(bytes[(int)numBytes + 3], bytes[(int)numBytes + 2], @@ -162,9 +157,7 @@ namespace S7.Net.Types numBytes += 4; break; case "UInt32": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); // hier auswerten value = DWord.FromBytes( bytes[(int)numBytes], @@ -174,9 +167,7 @@ namespace S7.Net.Types numBytes += 4; break; case "Single": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); // hier auswerten value = Real.FromByteArray( new byte[] { @@ -187,15 +178,31 @@ namespace S7.Net.Types numBytes += 4; break; case "Double": - numBytes = Math.Ceiling(numBytes); - if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0) - numBytes++; + IncrementToEven(ref numBytes); var buffer = new byte[8]; Array.Copy(bytes, (int)numBytes, buffer, 0, 8); // hier auswerten value = LReal.FromByteArray(buffer); numBytes += 8; break; + case "String": + S7StringAttribute? attribute = propertyInfo?.GetCustomAttributes().SingleOrDefault(); + if (attribute == default(S7StringAttribute)) + throw new ArgumentException("Please add S7StringAttribute to the string field"); + + IncrementToEven(ref numBytes); + + // get the value + var sData = new byte[attribute.ReservedLengthInBytes]; + Array.Copy(bytes, (int)numBytes, sData, 0, sData.Length); + value = attribute.Type switch + { + S7StringType.S7String => S7String.FromByteArray(sData), + S7StringType.S7WString => S7WString.FromByteArray(sData), + _ => throw new ArgumentException("Please use a valid string type for the S7StringAttribute") + }; + numBytes += sData.Length; + break; default: var propClass = Activator.CreateInstance(propertyType); numBytes = FromBytes(propClass, bytes, numBytes); @@ -227,7 +234,7 @@ namespace S7.Net.Types for (int i = 0; i < array.Length && numBytes < bytes.Length; i++) { array.SetValue( - GetPropertyValue(elementType, bytes, ref numBytes), + GetPropertyValue(elementType, property, bytes, ref numBytes), i); } } @@ -235,7 +242,7 @@ namespace S7.Net.Types { property.SetValue( sourceClass, - GetPropertyValue(property.PropertyType, bytes, ref numBytes), + GetPropertyValue(property.PropertyType, property, bytes, ref numBytes), null); } } @@ -243,7 +250,7 @@ namespace S7.Net.Types return numBytes; } - private static double SetBytesFromProperty(object propertyValue, byte[] bytes, double numBytes) + private static double SetBytesFromProperty(object propertyValue, PropertyInfo? propertyInfo, byte[] bytes, double numBytes) { int bytePos = 0; int bitPos = 0; @@ -285,6 +292,18 @@ namespace S7.Net.Types case "Double": bytes2 = LReal.ToByteArray((double)propertyValue); break; + case "String": + S7StringAttribute? attribute = propertyInfo?.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)propertyValue, attribute.ReservedLength), + S7StringType.S7WString => S7WString.ToByteArray((string)propertyValue, attribute.ReservedLength), + _ => throw new ArgumentException("Please use a valid string type for the S7StringAttribute") + }; + break; default: numBytes = ToBytes(propertyValue, bytes, numBytes); break; @@ -320,12 +339,12 @@ namespace S7.Net.Types Type elementType = property.PropertyType.GetElementType(); for (int i = 0; i < array.Length && numBytes < bytes.Length; i++) { - numBytes = SetBytesFromProperty(array.GetValue(i), bytes, numBytes); + numBytes = SetBytesFromProperty(array.GetValue(i), property, bytes, numBytes); } } else { - numBytes = SetBytesFromProperty(property.GetValue(sourceClass, null), bytes, numBytes); + numBytes = SetBytesFromProperty(property.GetValue(sourceClass, null), property, bytes, numBytes); } } return numBytes; diff --git a/S7.Net/Types/S7StringAttribute.cs b/S7.Net/Types/S7StringAttribute.cs index 4d6e107..768667d 100644 --- a/S7.Net/Types/S7StringAttribute.cs +++ b/S7.Net/Types/S7StringAttribute.cs @@ -2,7 +2,7 @@ namespace S7.Net.Types { - [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] public sealed class S7StringAttribute : Attribute { private readonly S7StringType type; From f47918946daff3bf633cdb0e35509a204c8c7fdc Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Sat, 10 Dec 2022 20:51:44 +0100 Subject: [PATCH 7/7] ci: Run test on ubuntu-20.04 due to lack of snap7 on newer ubuntu --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0d13810..ad5d9a9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,14 +14,14 @@ jobs: DOTNET_NOLOGO : 1 strategy: matrix: - os: [windows-latest, ubuntu-latest, macos-latest] + os: [windows-latest, ubuntu-20.04, macos-latest] test-framework: [netcoreapp3.1, net5.0] include: - - os: ubuntu-latest + - os: ubuntu-20.04 test-framework: netcoreapp3.1 installSnap7: true dotnet-sdk: '3.1.x' - - os: ubuntu-latest + - os: ubuntu-20.04 test-framework: net5.0 installSnap7: true dotnet-sdk: '5.0.x' @@ -48,7 +48,7 @@ jobs: - uses: actions/checkout@v2 - name: Install Snap7 Linux - if: ${{ matrix.installSnap7 && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.installSnap7 && matrix.os == 'ubuntu-20.04' }} run: | sudo add-apt-repository ppa:gijzelaar/snap7 sudo apt-get update