From 07325db2fafb1d6c58fea6201dfe30f7b3ad336d Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Tue, 29 Aug 2023 21:34:09 +0200 Subject: [PATCH] feat: Implement clock reading --- S7.Net/Plc.Clock.cs | 53 +++++++++++++++++++++++++++++++++++++++ S7.Net/PlcAsynchronous.cs | 5 +++- S7.Net/PlcSynchronous.cs | 5 +++- S7.Net/Types/DateTime.cs | 5 ++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 S7.Net/Plc.Clock.cs diff --git a/S7.Net/Plc.Clock.cs b/S7.Net/Plc.Clock.cs new file mode 100644 index 00000000..8b2e5bea --- /dev/null +++ b/S7.Net/Plc.Clock.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using System.Linq; +using S7.Net.Helper; +using S7.Net.Types; +using DateTime = System.DateTime; + +namespace S7.Net; + +partial class Plc +{ + private const byte SzlFunctionGroupTimers = 0x07; + private const byte SzlSubFunctionReadClock = 0x01; + + private static byte[] BuildClockReadRequest() + { + var stream = new MemoryStream(); + + WriteSzlRequestHeader(stream, SzlFunctionGroupTimers, SzlSubFunctionReadClock, 4); + stream.Write(new byte[] { 0x0a, 0x00, 0x00, 0x00 }); + + stream.SetLength(stream.Position); + return stream.ToArray(); + } + + private static DateTime ParseClockReadResponse(byte[] message) + { + const int pduErrOffset = 20; + const int dtResultOffset = pduErrOffset + 2; + const int dtLenOffset = dtResultOffset + 2; + const int dtValueOffset = dtLenOffset + 4; + + var pduErr = Word.FromByteArray(message.Skip(pduErrOffset).Take(2).ToArray()); + if (pduErr != 0) + { + throw new Exception($"Response from PLC indicates error 0x{pduErr:X4}."); + } + + var dtResult = message[dtResultOffset]; + if (dtResult != 0xff) + { + throw new Exception($"Response from PLC indicates error 0x{dtResult:X2}."); + } + + var len = Word.FromByteArray(message.Skip(dtLenOffset).Take(2).ToArray()); + if (len != Types.DateTime.Length) + { + throw new Exception($"Unexpected response length {len}, expected {Types.DateTime.Length}."); + } + + return Types.DateTime.FromByteArray(message.Skip(dtValueOffset).Take(Types.DateTime.Length).ToArray()); + } +} \ No newline at end of file diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 63edcd92..2dcd058e 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -320,7 +320,10 @@ public async Task> ReadMultipleVarsAsync(List dataItems /// A task that represents the asynchronous operation, with it's result set to the current PLC time on completion. public async Task ReadClockAsync(CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + var request = BuildClockReadRequest(); + var response = await RequestTsduAsync(request, cancellationToken); + + return ParseClockReadResponse(response); } /// diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 50483ada..d9537104 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -498,7 +498,10 @@ public void ReadMultipleVars(List dataItems) /// The current PLC time. public System.DateTime ReadClock() { - throw new NotImplementedException(); + var request = BuildClockReadRequest(); + var response = RequestTsdu(request); + + return ParseClockReadResponse(response); } /// diff --git a/S7.Net/Types/DateTime.cs b/S7.Net/Types/DateTime.cs index a685a210..993ca250 100644 --- a/S7.Net/Types/DateTime.cs +++ b/S7.Net/Types/DateTime.cs @@ -8,6 +8,11 @@ namespace S7.Net.Types /// public static class DateTime { + /// + /// The length in bytes of DateTime stored in the PLC. + /// + public const int Length = 10; + /// /// The minimum value supported by the specification. ///