From eb1fad93330ff9b51f51a63e457a63f8190c8587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20P=C4=85gowski?= Date: Wed, 23 Aug 2023 00:27:55 +0200 Subject: [PATCH 1/6] Add IEC date DataType --- S7.Net/Enums.cs | 5 +++ S7.Net/Helper/DateTimeExtensions.cs | 23 ++++++++++ S7.Net/Types/Date.cs | 70 +++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 S7.Net/Helper/DateTimeExtensions.cs create mode 100644 S7.Net/Types/Date.cs diff --git a/S7.Net/Enums.cs b/S7.Net/Enums.cs index 080dce65..3eb7e4ba 100644 --- a/S7.Net/Enums.cs +++ b/S7.Net/Enums.cs @@ -202,6 +202,11 @@ public enum VarType /// DateTIme variable type /// DateTime, + + /// + /// IEC date (legacy) variable type + /// + Date, /// /// DateTimeLong variable type diff --git a/S7.Net/Helper/DateTimeExtensions.cs b/S7.Net/Helper/DateTimeExtensions.cs new file mode 100644 index 00000000..7e128c17 --- /dev/null +++ b/S7.Net/Helper/DateTimeExtensions.cs @@ -0,0 +1,23 @@ +using System; +using S7.Net.Types; +using DateTime = System.DateTime; + +namespace S7.Net.Helper +{ + public static class DateTimeExtensions + { + public static ushort GetDaysSinceIecDateStart(this DateTime dateTime) + { + if (dateTime < Date.IecMinDate) + { + throw new ArgumentException($"DateTime must be at least {Date.IecMinDate:d}"); + } + if (dateTime > Date.IecMaxDate) + { + throw new ArgumentException($"DateTime must be lower than {Date.IecMaxDate:d}"); + } + + return (ushort)(dateTime - Date.IecMinDate).TotalDays; + } + } +} \ No newline at end of file diff --git a/S7.Net/Types/Date.cs b/S7.Net/Types/Date.cs new file mode 100644 index 00000000..980742ee --- /dev/null +++ b/S7.Net/Types/Date.cs @@ -0,0 +1,70 @@ +using System; +using S7.Net.Helper; + +namespace S7.Net.Types +{ + /// + /// Contains the conversion methods to convert Words from S7 plc to C#. + /// + public static class Date + { + /// + /// Minimum allowed date for the IEC date type + /// + public static readonly System.DateTime IecMinDate = new(year: 1990, month: 01, day: 01); + + /// + /// Maximum allowed date for the IEC date type + /// + public static readonly System.DateTime IecMaxDate = new(year: 2168, month: 12, day: 31); + + /// + /// Converts a word (2 bytes) to IEC date () + /// + public static System.DateTime FromByteArray(byte[] bytes) + { + if (bytes.Length != 2) + { + throw new ArgumentException("Wrong number of bytes. Bytes array must contain 2 bytes."); + } + + var daysSinceDateStart = Word.FromByteArray(bytes); + return IecMinDate.AddDays(daysSinceDateStart); + } + + /// + /// Converts a to word (2 bytes) + /// + public static byte[] ToByteArray(System.DateTime dateTime) => Word.ToByteArray(dateTime.GetDaysSinceIecDateStart()); + + /// + /// Converts an array of s to an array of bytes + /// + public static byte[] ToByteArray(System.DateTime[] value) + { + var arr = new ByteArray(); + foreach (var date in value) + arr.Add(ToByteArray(date)); + return arr.Array; + } + + /// + /// Converts an array of bytes to an array of s + /// + public static System.DateTime[] ToArray(byte[] bytes) + { + var values = new System.DateTime[bytes.Length / sizeof(ushort)]; + + for (int i = 0; i < values.Length; i++) + { + values[i] = FromByteArray( + new[] + { + bytes[i], bytes[i + 1] + }); + } + + return values; + } + } +} \ No newline at end of file From a55ceba679395863868cf9d1e8198e1ac8bda09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20P=C4=85gowski?= Date: Wed, 23 Aug 2023 00:38:24 +0200 Subject: [PATCH 2/6] Add IEC Date VarType support to PLCHelpers.cs --- S7.Net/PLCHelpers.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index fa016726..2a308452 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -244,6 +244,15 @@ private static void BuildReadDataRequestPackage(System.IO.MemoryStream stream, D { return TimeSpan.ToArray(bytes); } + case VarType.Date: + if (varCount == 1) + { + return Date.FromByteArray(bytes); + } + else + { + return Date.ToArray(bytes); + } default: return null; } @@ -273,6 +282,7 @@ internal static int VarTypeToByteLength(VarType varType, int varCount = 1) case VarType.Timer: case VarType.Int: case VarType.Counter: + case VarType.Date: return varCount * 2; case VarType.DWord: case VarType.DInt: From 8087b8d3151beb8d8cd61e5e0b09188b25b53795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20P=C4=85gowski?= Date: Wed, 23 Aug 2023 00:43:17 +0200 Subject: [PATCH 3/6] Change the thrown exceptions to ArgumentOutOfRangeException --- S7.Net/Helper/DateTimeExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/S7.Net/Helper/DateTimeExtensions.cs b/S7.Net/Helper/DateTimeExtensions.cs index 7e128c17..62432ee1 100644 --- a/S7.Net/Helper/DateTimeExtensions.cs +++ b/S7.Net/Helper/DateTimeExtensions.cs @@ -10,11 +10,11 @@ public static ushort GetDaysSinceIecDateStart(this DateTime dateTime) { if (dateTime < Date.IecMinDate) { - throw new ArgumentException($"DateTime must be at least {Date.IecMinDate:d}"); + throw new ArgumentOutOfRangeException($"DateTime must be at least {Date.IecMinDate:d}"); } if (dateTime > Date.IecMaxDate) { - throw new ArgumentException($"DateTime must be lower than {Date.IecMaxDate:d}"); + throw new ArgumentOutOfRangeException($"DateTime must be lower than {Date.IecMaxDate:d}"); } return (ushort)(dateTime - Date.IecMinDate).TotalDays; From 689e7ffd96d9ca377760f862d105fb2eaebb1278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20P=C4=85gowski?= Date: Wed, 23 Aug 2023 00:53:29 +0200 Subject: [PATCH 4/6] Increase the maximum date The spec goes up to a full 2168 year, but the PLC date type goes up to 2169 June 06 which is represented by 65535 (max ushort value). --- S7.Net/Types/Date.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/S7.Net/Types/Date.cs b/S7.Net/Types/Date.cs index 980742ee..ef6cc79f 100644 --- a/S7.Net/Types/Date.cs +++ b/S7.Net/Types/Date.cs @@ -15,8 +15,14 @@ public static class Date /// /// Maximum allowed date for the IEC date type + /// + /// Although the spec allows only a max date of 31-12-2168, the PLC IEC date goes up to 06-06-2169 (which is the actual + /// WORD max value - 65535) + /// /// - public static readonly System.DateTime IecMaxDate = new(year: 2168, month: 12, day: 31); + public static readonly System.DateTime IecMaxDate = new(year: 2169, month: 06, day: 06); + + private static readonly ushort MaxNumberOfDays = (ushort)(IecMaxDate - IecMinDate).TotalDays; /// /// Converts a word (2 bytes) to IEC date () @@ -29,6 +35,12 @@ public static System.DateTime FromByteArray(byte[] bytes) } var daysSinceDateStart = Word.FromByteArray(bytes); + if (daysSinceDateStart > MaxNumberOfDays) + { + throw new ArgumentException($"Read number exceeded the number of maximum days in the IEC date (read: {daysSinceDateStart}, max: {MaxNumberOfDays})", + nameof(bytes)); + } + return IecMinDate.AddDays(daysSinceDateStart); } From e4cc42fa51d15b68f3593bab29a17219bda3bdac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20P=C4=85gowski?= Date: Wed, 23 Aug 2023 01:04:23 +0200 Subject: [PATCH 5/6] Add support for serializing IEC date --- S7.Net/Protocol/Serialization.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/S7.Net/Protocol/Serialization.cs b/S7.Net/Protocol/Serialization.cs index 3d114b6f..fce3bef1 100644 --- a/S7.Net/Protocol/Serialization.cs +++ b/S7.Net/Protocol/Serialization.cs @@ -26,6 +26,11 @@ public static byte[] SerializeDataItem(DataItem dataItem) _ => Types.String.ToByteArray(s, dataItem.Count) }; + if (dataItem.VarType == VarType.Date) + { + return Date.ToByteArray((System.DateTime)dataItem.Value); + } + return SerializeValue(dataItem.Value); } From f227ad4b531750cf1b38420ac21058e624a701e9 Mon Sep 17 00:00:00 2001 From: bonk-dev <40630642+bonk-dev@users.noreply.github.com> Date: Wed, 23 Aug 2023 12:03:22 +0200 Subject: [PATCH 6/6] Expose IecMinDate and IecMaxDate as properties --- S7.Net/Types/Date.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/S7.Net/Types/Date.cs b/S7.Net/Types/Date.cs index ef6cc79f..5c53f19d 100644 --- a/S7.Net/Types/Date.cs +++ b/S7.Net/Types/Date.cs @@ -11,7 +11,7 @@ public static class Date /// /// Minimum allowed date for the IEC date type /// - public static readonly System.DateTime IecMinDate = new(year: 1990, month: 01, day: 01); + public static System.DateTime IecMinDate { get; } = new(year: 1990, month: 01, day: 01); /// /// Maximum allowed date for the IEC date type @@ -20,7 +20,7 @@ public static class Date /// WORD max value - 65535) /// /// - public static readonly System.DateTime IecMaxDate = new(year: 2169, month: 06, day: 06); + public static System.DateTime IecMaxDate { get; } = new(year: 2169, month: 06, day: 06); private static readonly ushort MaxNumberOfDays = (ushort)(IecMaxDate - IecMinDate).TotalDays; @@ -79,4 +79,4 @@ public static System.DateTime[] ToArray(byte[] bytes) return values; } } -} \ No newline at end of file +}