Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for reading/writing the legacy DATE (IEC date) datatype #506

Merged
merged 7 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions S7.Net/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ public enum VarType
/// DateTIme variable type
/// </summary>
DateTime,

/// <summary>
/// IEC date (legacy) variable type
/// </summary>
Date,

/// <summary>
/// DateTimeLong variable type
Expand Down
23 changes: 23 additions & 0 deletions S7.Net/Helper/DateTimeExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
10 changes: 10 additions & 0 deletions S7.Net/PLCHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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:
Expand Down
5 changes: 5 additions & 0 deletions S7.Net/Protocol/Serialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
82 changes: 82 additions & 0 deletions S7.Net/Types/Date.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using S7.Net.Helper;

namespace S7.Net.Types
{
/// <summary>
/// Contains the conversion methods to convert Words from S7 plc to C#.
/// </summary>
public static class Date
{
/// <summary>
/// Minimum allowed date for the IEC date type
/// </summary>
public static readonly System.DateTime IecMinDate = new(year: 1990, month: 01, day: 01);
bonk-dev marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Maximum allowed date for the IEC date type
/// <remarks>
/// 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)
/// </remarks>
/// </summary>
public static readonly System.DateTime IecMaxDate = new(year: 2169, month: 06, day: 06);

private static readonly ushort MaxNumberOfDays = (ushort)(IecMaxDate - IecMinDate).TotalDays;

/// <summary>
/// Converts a word (2 bytes) to IEC date (<see cref="System.DateTime"/>)
/// </summary>
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);
}

/// <summary>
/// Converts a <see cref="System.DateTime"/> to word (2 bytes)
/// </summary>
public static byte[] ToByteArray(System.DateTime dateTime) => Word.ToByteArray(dateTime.GetDaysSinceIecDateStart());

/// <summary>
/// Converts an array of <see cref="System.DateTime"/>s to an array of bytes
/// </summary>
public static byte[] ToByteArray(System.DateTime[] value)
{
var arr = new ByteArray();
foreach (var date in value)
arr.Add(ToByteArray(date));
return arr.Array;
}

/// <summary>
/// Converts an array of bytes to an array of <see cref="System.DateTime"/>s
/// </summary>
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;
}
}
}