using System; using System.Collections.Generic; using System.IO; namespace S7.Net.Types { /// /// Contains the methods to convert between and S7 representation of DateTimeLong (DTL) values. /// public static class DateTimeLong { public const int TypeLengthInBytes = 12; /// /// The minimum value supported by the specification. /// public static readonly System.DateTime SpecMinimumDateTime = new System.DateTime(1970, 1, 1); /// /// The maximum value supported by the specification. /// public static readonly System.DateTime SpecMaximumDateTime = new System.DateTime(2262, 4, 11, 23, 47, 16, 854); /// /// Parses a value from bytes. /// /// Input bytes read from PLC. /// A object representing the value read from PLC. /// /// Thrown when the length of /// is not 12 or any value in /// is outside the valid range of values. /// public static System.DateTime FromByteArray(byte[] bytes) { return FromByteArrayImpl(bytes); } /// /// Parses an array of values from bytes. /// /// Input bytes read from PLC. /// An array of objects representing the values read from PLC. /// /// Thrown when the length of /// is not a multiple of 12 or any value in /// is outside the valid range of values. /// public static System.DateTime[] ToArray(byte[] bytes) { if (bytes.Length % TypeLengthInBytes != 0) { throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length, $"Parsing an array of DateTimeLong requires a multiple of 12 bytes of input data, input data is '{bytes.Length}' long."); } var cnt = bytes.Length / TypeLengthInBytes; var result = new System.DateTime[cnt]; for (var i = 0; i < cnt; i++) { var slice = new byte[TypeLengthInBytes]; Array.Copy(bytes, i * TypeLengthInBytes, slice, 0, TypeLengthInBytes); result[i] = FromByteArrayImpl(slice); } return result; } private static System.DateTime FromByteArrayImpl(byte[] bytes) { if (bytes.Length != TypeLengthInBytes) { throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length, $"Parsing a DateTimeLong requires exactly 12 bytes of input data, input data is {bytes.Length} bytes long."); } var year = AssertRangeInclusive(Word.FromBytes(bytes[1], bytes[0]), 1970, 2262, "year"); var month = AssertRangeInclusive(bytes[2], 1, 12, "month"); var day = AssertRangeInclusive(bytes[3], 1, 31, "day of month"); var dayOfWeek = AssertRangeInclusive(bytes[4], 1, 7, "day of week"); var hour = AssertRangeInclusive(bytes[5], 0, 23, "hour"); var minute = AssertRangeInclusive(bytes[6], 0, 59, "minute"); var second = AssertRangeInclusive(bytes[7], 0, 59, "second"); ; var nanoseconds = AssertRangeInclusive(DWord.FromBytes(bytes[11], bytes[10], bytes[9], bytes[8]), 0, 999999999, "nanoseconds"); var time = new System.DateTime(year, month, day, hour, minute, second); return time.AddTicks(nanoseconds / 100); } /// /// Converts a value to a byte array. /// /// The DateTime value to convert. /// A byte array containing the S7 DateTimeLong representation of . /// /// Thrown when the value of /// is before /// or after . /// public static byte[] ToByteArray(System.DateTime dateTime) { if (dateTime < SpecMinimumDateTime) { throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime, $"Date time '{dateTime}' is before the minimum '{SpecMinimumDateTime}' supported in S7 DateTimeLong representation."); } if (dateTime > SpecMaximumDateTime) { throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime, $"Date time '{dateTime}' is after the maximum '{SpecMaximumDateTime}' supported in S7 DateTimeLong representation."); } var stream = new MemoryStream(TypeLengthInBytes); // Convert Year stream.Write(Word.ToByteArray(Convert.ToUInt16(dateTime.Year)), 0, 2); // Convert Month stream.WriteByte(Convert.ToByte(dateTime.Month)); // Convert Day stream.WriteByte(Convert.ToByte(dateTime.Day)); // Convert WeekDay. NET DateTime starts with Sunday = 0, while S7DT has Sunday = 1. stream.WriteByte(Convert.ToByte(dateTime.DayOfWeek + 1)); // Convert Hour stream.WriteByte(Convert.ToByte(dateTime.Hour)); // Convert Minutes stream.WriteByte(Convert.ToByte(dateTime.Minute)); // Convert Seconds stream.WriteByte(Convert.ToByte(dateTime.Second)); // Convert Nanoseconds. Net DateTime has a representation of 1 Tick = 100ns. // Thus First take the ticks Mod 1 Second (1s = 10'000'000 ticks), and then Convert to nanoseconds. stream.Write(DWord.ToByteArray(Convert.ToUInt32(dateTime.Ticks % 10000000 * 100)), 0, 4); return stream.ToArray(); } /// /// Converts an array of values to a byte array. /// /// The DateTime values to convert. /// A byte array containing the S7 DateTimeLong representations of . /// /// Thrown when any value of /// is before /// or after . /// public static byte[] ToByteArray(System.DateTime[] dateTimes) { var bytes = new List(dateTimes.Length * TypeLengthInBytes); foreach (var dateTime in dateTimes) { bytes.AddRange(ToByteArray(dateTime)); } return bytes.ToArray(); } private static T AssertRangeInclusive(T input, T min, T max, string field) where T : IComparable { if (input.CompareTo(min) < 0) { throw new ArgumentOutOfRangeException(nameof(input), input, $"Value '{input}' is lower than the minimum '{min}' allowed for {field}."); } if (input.CompareTo(max) > 0) { throw new ArgumentOutOfRangeException(nameof(input), input, $"Value '{input}' is higher than the maximum '{max}' allowed for {field}."); } return input; } } }