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..62432ee1 --- /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 ArgumentOutOfRangeException($"DateTime must be at least {Date.IecMinDate:d}"); + } + if (dateTime > Date.IecMaxDate) + { + throw new ArgumentOutOfRangeException($"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/PLCHelpers.cs b/S7.Net/PLCHelpers.cs index fc6bb141..c85a2383 100644 --- a/S7.Net/PLCHelpers.cs +++ b/S7.Net/PLCHelpers.cs @@ -251,6 +251,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; } @@ -280,6 +289,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: 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); } diff --git a/S7.Net/Types/Date.cs b/S7.Net/Types/Date.cs new file mode 100644 index 00000000..5c53f19d --- /dev/null +++ b/S7.Net/Types/Date.cs @@ -0,0 +1,82 @@ +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 System.DateTime IecMinDate { get; } = new(year: 1990, month: 01, day: 01); + + /// + /// 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 System.DateTime IecMaxDate { get; } = new(year: 2169, month: 06, day: 06); + + private static readonly ushort MaxNumberOfDays = (ushort)(IecMaxDate - IecMinDate).TotalDays; + + /// + /// 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); + 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); + } + + /// + /// 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; + } + } +}