diff --git a/S7.Net.UnitTest/S7NetTestsAsync.cs b/S7.Net.UnitTest/S7NetTestsAsync.cs
index 5d598824..20338845 100644
--- a/S7.Net.UnitTest/S7NetTestsAsync.cs
+++ b/S7.Net.UnitTest/S7NetTestsAsync.cs
@@ -1006,7 +1006,7 @@ public async Task Test_Async_WriteLargeByteArrayWithCancellation()
var db = 2;
randomEngine.NextBytes(data);
- cancellationSource.CancelAfter(TimeSpan.FromMilliseconds(5));
+ cancellationSource.CancelAfter(System.TimeSpan.FromMilliseconds(5));
try
{
await plc.WriteBytesAsync(DataType.DataBlock, db, 0, data, cancellationToken);
@@ -1045,7 +1045,7 @@ public async Task Test_Async_WriteLargeByteArrayWithCancellationWithMemory()
var db = 2;
randomEngine.NextBytes(data.Span);
- cancellationSource.CancelAfter(TimeSpan.FromMilliseconds(5));
+ cancellationSource.CancelAfter(System.TimeSpan.FromMilliseconds(5));
try
{
await plc.WriteBytesAsync(DataType.DataBlock, db, 0, data, cancellationToken);
diff --git a/S7.Net.UnitTest/TypeTests/TimeSpanTests.cs b/S7.Net.UnitTest/TypeTests/TimeSpanTests.cs
new file mode 100644
index 00000000..5f129fb4
--- /dev/null
+++ b/S7.Net.UnitTest/TypeTests/TimeSpanTests.cs
@@ -0,0 +1,82 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace S7.Net.UnitTest.TypeTests
+{
+ public static class TimeSpanTests
+ {
+ private static readonly TimeSpan SampleTimeSpan = new TimeSpan(12, 0, 59, 37, 856);
+
+ private static readonly byte[] SampleByteArray = { 0x3E, 0x02, 0xE8, 0x00 };
+
+ private static readonly byte[] SpecMinByteArray = { 0x80, 0x00, 0x00, 0x00 };
+
+ private static readonly byte[] SpecMaxByteArray = { 0x7F, 0xFF, 0xFF, 0xFF };
+
+ [TestClass]
+ public class FromByteArray
+ {
+ [TestMethod]
+ public void Sample()
+ {
+ AssertFromByteArrayEquals(SampleTimeSpan, SampleByteArray);
+ }
+
+ [TestMethod]
+ public void SpecMinimum()
+ {
+ AssertFromByteArrayEquals(Types.TimeSpan.SpecMinimumTimeSpan, SpecMinByteArray);
+ }
+
+ [TestMethod]
+ public void SpecMaximum()
+ {
+ AssertFromByteArrayEquals(Types.TimeSpan.SpecMaximumTimeSpan, SpecMaxByteArray);
+ }
+
+ private static void AssertFromByteArrayEquals(TimeSpan expected, params byte[] bytes)
+ {
+ Assert.AreEqual(expected, Types.TimeSpan.FromByteArray(bytes));
+ }
+ }
+
+ [TestClass]
+ public class ToByteArray
+ {
+ [TestMethod]
+ public void Sample()
+ {
+ AssertToByteArrayEquals(SampleTimeSpan, SampleByteArray);
+ }
+
+ [TestMethod]
+ public void SpecMinimum()
+ {
+ AssertToByteArrayEquals(Types.TimeSpan.SpecMinimumTimeSpan, SpecMinByteArray);
+ }
+
+ [TestMethod]
+ public void SpecMaximum()
+ {
+ AssertToByteArrayEquals(Types.TimeSpan.SpecMaximumTimeSpan, SpecMaxByteArray);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void ThrowsOnTimeBeforeSpecMinimum()
+ {
+ Types.TimeSpan.ToByteArray(TimeSpan.FromDays(-25));
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
+ public void ThrowsOnTimeAfterSpecMaximum()
+ {
+ Types.TimeSpan.ToByteArray(new TimeSpan(30, 15, 15, 15, 15));
+ }
+
+ private static void AssertToByteArrayEquals(TimeSpan value, params byte[] expected)
+ {
+ CollectionAssert.AreEqual(expected, Types.TimeSpan.ToByteArray(value));
+ }
+ }
+ }
+}
diff --git a/S7.Net/Enums.cs b/S7.Net/Enums.cs
index fc412d3e..080dce65 100644
--- a/S7.Net/Enums.cs
+++ b/S7.Net/Enums.cs
@@ -206,6 +206,11 @@ public enum VarType
///
/// DateTimeLong variable type
///
- DateTimeLong
+ DateTimeLong,
+
+ ///
+ /// S7 TIME variable type - serialized as S7 DInt and deserialized as C# TimeSpan
+ ///
+ Time
}
}
diff --git a/S7.Net/PLCHelpers.cs b/S7.Net/PLCHelpers.cs
index 4109e536..fa016726 100644
--- a/S7.Net/PLCHelpers.cs
+++ b/S7.Net/PLCHelpers.cs
@@ -235,6 +235,15 @@ private static void BuildReadDataRequestPackage(System.IO.MemoryStream stream, D
{
return DateTimeLong.ToArray(bytes);
}
+ case VarType.Time:
+ if (varCount == 1)
+ {
+ return TimeSpan.FromByteArray(bytes);
+ }
+ else
+ {
+ return TimeSpan.ToArray(bytes);
+ }
default:
return null;
}
@@ -268,6 +277,7 @@ internal static int VarTypeToByteLength(VarType varType, int varCount = 1)
case VarType.DWord:
case VarType.DInt:
case VarType.Real:
+ case VarType.Time:
return varCount * 4;
case VarType.LReal:
case VarType.DateTime:
diff --git a/S7.Net/Types/Struct.cs b/S7.Net/Types/Struct.cs
index 6f29447d..136638ac 100644
--- a/S7.Net/Types/Struct.cs
+++ b/S7.Net/Types/Struct.cs
@@ -45,6 +45,7 @@ public static int GetStructSize(Type structType)
break;
case "Int32":
case "UInt32":
+ case "TimeSpan":
numBytes = Math.Ceiling(numBytes);
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
numBytes++;
@@ -215,6 +216,21 @@ public static int GetStructSize(Type structType)
numBytes += sData.Length;
break;
+ case "TimeSpan":
+ numBytes = Math.Ceiling(numBytes);
+ if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
+ numBytes++;
+
+ // get the value
+ info.SetValue(structValue, TimeSpan.FromByteArray(new[]
+ {
+ bytes[(int)numBytes + 0],
+ bytes[(int)numBytes + 1],
+ bytes[(int)numBytes + 2],
+ bytes[(int)numBytes + 3]
+ }));
+ numBytes += 4;
+ break;
default:
var buffer = new byte[GetStructSize(info.FieldType)];
if (buffer.Length == 0)
@@ -311,6 +327,9 @@ static TValue GetValueOrThrow(FieldInfo fi, object structValue) where TV
_ => throw new ArgumentException("Please use a valid string type for the S7StringAttribute")
};
break;
+ case "TimeSpan":
+ bytes2 = TimeSpan.ToByteArray((System.TimeSpan)info.GetValue(structValue));
+ break;
}
if (bytes2 != null)
{
diff --git a/S7.Net/Types/TimeSpan.cs b/S7.Net/Types/TimeSpan.cs
new file mode 100644
index 00000000..20f05d87
--- /dev/null
+++ b/S7.Net/Types/TimeSpan.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+
+namespace S7.Net.Types
+{
+ ///
+ /// Contains the methods to convert between and S7 representation of TIME values.
+ ///
+ public static class TimeSpan
+ {
+ ///
+ /// The minimum value supported by the specification.
+ ///
+ public static readonly System.TimeSpan SpecMinimumTimeSpan = System.TimeSpan.FromMilliseconds(int.MinValue);
+
+ ///
+ /// The maximum value supported by the specification.
+ ///
+ public static readonly System.TimeSpan SpecMaximumTimeSpan = System.TimeSpan.FromMilliseconds(int.MaxValue);
+
+ ///
+ /// 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 4 or any value in
+ /// is outside the valid range of values.
+ public static System.TimeSpan FromByteArray(byte[] bytes)
+ {
+ var milliseconds = DInt.FromByteArray(bytes);
+ return System.TimeSpan.FromMilliseconds(milliseconds);
+ }
+
+ ///
+ /// 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 4 or any value in
+ /// is outside the valid range of values.
+ public static System.TimeSpan[] ToArray(byte[] bytes)
+ {
+ const int singleTimeSpanLength = 4;
+
+ if (bytes.Length % singleTimeSpanLength != 0)
+ throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length,
+ $"Parsing an array of {nameof(System.TimeSpan)} requires a multiple of {singleTimeSpanLength} bytes of input data, input data is '{bytes.Length}' long.");
+
+ var result = new System.TimeSpan[bytes.Length / singleTimeSpanLength];
+
+ var milliseconds = DInt.ToArray(bytes);
+ for (var i = 0; i < milliseconds.Length; i++)
+ result[i] = System.TimeSpan.FromMilliseconds(milliseconds[i]);
+
+ return result;
+ }
+
+ ///
+ /// Converts a value to a byte array.
+ ///
+ /// The TimeSpan value to convert.
+ /// A byte array containing the S7 date time representation of .
+ /// Thrown when the value of
+ /// is before
+ /// or after .
+ public static byte[] ToByteArray(System.TimeSpan timeSpan)
+ {
+ if (timeSpan < SpecMinimumTimeSpan)
+ throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan,
+ $"Time span '{timeSpan}' is before the minimum '{SpecMinimumTimeSpan}' supported in S7 time representation.");
+
+ if (timeSpan > SpecMaximumTimeSpan)
+ throw new ArgumentOutOfRangeException(nameof(timeSpan), timeSpan,
+ $"Time span '{timeSpan}' is after the maximum '{SpecMaximumTimeSpan}' supported in S7 time representation.");
+
+ return DInt.ToByteArray(Convert.ToInt32(timeSpan.TotalMilliseconds));
+ }
+
+ ///
+ /// Converts an array of values to a byte array.
+ ///
+ /// The TimeSpan values to convert.
+ /// A byte array containing the S7 date time representations of .
+ /// Thrown when any value of
+ /// is before
+ /// or after .
+ public static byte[] ToByteArray(System.TimeSpan[] timeSpans)
+ {
+ var bytes = new List(timeSpans.Length * 4);
+ foreach (var timeSpan in timeSpans) bytes.AddRange(ToByteArray(timeSpan));
+
+ return bytes.ToArray();
+ }
+ }
+}
\ No newline at end of file