Skip to content

Commit ab6308e

Browse files
authored
Merge pull request #506 from bonk-dev/s5-date
Add support for reading/writing the legacy DATE (IEC date) datatype
2 parents 76a7ea0 + 130eead commit ab6308e

5 files changed

Lines changed: 125 additions & 0 deletions

File tree

S7.Net/Enums.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ public enum VarType
202202
/// DateTIme variable type
203203
/// </summary>
204204
DateTime,
205+
206+
/// <summary>
207+
/// IEC date (legacy) variable type
208+
/// </summary>
209+
Date,
205210

206211
/// <summary>
207212
/// DateTimeLong variable type
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using S7.Net.Types;
3+
using DateTime = System.DateTime;
4+
5+
namespace S7.Net.Helper
6+
{
7+
public static class DateTimeExtensions
8+
{
9+
public static ushort GetDaysSinceIecDateStart(this DateTime dateTime)
10+
{
11+
if (dateTime < Date.IecMinDate)
12+
{
13+
throw new ArgumentOutOfRangeException($"DateTime must be at least {Date.IecMinDate:d}");
14+
}
15+
if (dateTime > Date.IecMaxDate)
16+
{
17+
throw new ArgumentOutOfRangeException($"DateTime must be lower than {Date.IecMaxDate:d}");
18+
}
19+
20+
return (ushort)(dateTime - Date.IecMinDate).TotalDays;
21+
}
22+
}
23+
}

S7.Net/PLCHelpers.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,15 @@ private static void BuildReadDataRequestPackage(System.IO.MemoryStream stream, D
251251
{
252252
return TimeSpan.ToArray(bytes);
253253
}
254+
case VarType.Date:
255+
if (varCount == 1)
256+
{
257+
return Date.FromByteArray(bytes);
258+
}
259+
else
260+
{
261+
return Date.ToArray(bytes);
262+
}
254263
default:
255264
return null;
256265
}
@@ -280,6 +289,7 @@ internal static int VarTypeToByteLength(VarType varType, int varCount = 1)
280289
case VarType.Timer:
281290
case VarType.Int:
282291
case VarType.Counter:
292+
case VarType.Date:
283293
return varCount * 2;
284294
case VarType.DWord:
285295
case VarType.DInt:

S7.Net/Protocol/Serialization.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public static byte[] SerializeDataItem(DataItem dataItem)
2626
_ => Types.String.ToByteArray(s, dataItem.Count)
2727
};
2828

29+
if (dataItem.VarType == VarType.Date)
30+
{
31+
return Date.ToByteArray((System.DateTime)dataItem.Value);
32+
}
33+
2934
return SerializeValue(dataItem.Value);
3035
}
3136

S7.Net/Types/Date.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System;
2+
using S7.Net.Helper;
3+
4+
namespace S7.Net.Types
5+
{
6+
/// <summary>
7+
/// Contains the conversion methods to convert Words from S7 plc to C#.
8+
/// </summary>
9+
public static class Date
10+
{
11+
/// <summary>
12+
/// Minimum allowed date for the IEC date type
13+
/// </summary>
14+
public static System.DateTime IecMinDate { get; } = new(year: 1990, month: 01, day: 01);
15+
16+
/// <summary>
17+
/// Maximum allowed date for the IEC date type
18+
/// <remarks>
19+
/// 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
20+
/// WORD max value - 65535)
21+
/// </remarks>
22+
/// </summary>
23+
public static System.DateTime IecMaxDate { get; } = new(year: 2169, month: 06, day: 06);
24+
25+
private static readonly ushort MaxNumberOfDays = (ushort)(IecMaxDate - IecMinDate).TotalDays;
26+
27+
/// <summary>
28+
/// Converts a word (2 bytes) to IEC date (<see cref="System.DateTime"/>)
29+
/// </summary>
30+
public static System.DateTime FromByteArray(byte[] bytes)
31+
{
32+
if (bytes.Length != 2)
33+
{
34+
throw new ArgumentException("Wrong number of bytes. Bytes array must contain 2 bytes.");
35+
}
36+
37+
var daysSinceDateStart = Word.FromByteArray(bytes);
38+
if (daysSinceDateStart > MaxNumberOfDays)
39+
{
40+
throw new ArgumentException($"Read number exceeded the number of maximum days in the IEC date (read: {daysSinceDateStart}, max: {MaxNumberOfDays})",
41+
nameof(bytes));
42+
}
43+
44+
return IecMinDate.AddDays(daysSinceDateStart);
45+
}
46+
47+
/// <summary>
48+
/// Converts a <see cref="System.DateTime"/> to word (2 bytes)
49+
/// </summary>
50+
public static byte[] ToByteArray(System.DateTime dateTime) => Word.ToByteArray(dateTime.GetDaysSinceIecDateStart());
51+
52+
/// <summary>
53+
/// Converts an array of <see cref="System.DateTime"/>s to an array of bytes
54+
/// </summary>
55+
public static byte[] ToByteArray(System.DateTime[] value)
56+
{
57+
var arr = new ByteArray();
58+
foreach (var date in value)
59+
arr.Add(ToByteArray(date));
60+
return arr.Array;
61+
}
62+
63+
/// <summary>
64+
/// Converts an array of bytes to an array of <see cref="System.DateTime"/>s
65+
/// </summary>
66+
public static System.DateTime[] ToArray(byte[] bytes)
67+
{
68+
var values = new System.DateTime[bytes.Length / sizeof(ushort)];
69+
70+
for (int i = 0; i < values.Length; i++)
71+
{
72+
values[i] = FromByteArray(
73+
new[]
74+
{
75+
bytes[i], bytes[i + 1]
76+
});
77+
}
78+
79+
return values;
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)