From cb24e9a04653451e24a263001753e350bdd681cd Mon Sep 17 00:00:00 2001 From: Michael Croes Date: Mon, 4 Sep 2023 22:15:52 +0200 Subject: [PATCH] feat: Implement clock write support --- S7.Net/Plc.Clock.cs | 60 +++++++++++++++++++++++++++++---------- S7.Net/PlcAsynchronous.cs | 5 +++- S7.Net/PlcSynchronous.cs | 5 +++- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/S7.Net/Plc.Clock.cs b/S7.Net/Plc.Clock.cs index 82394ce..45ccb36 100644 --- a/S7.Net/Plc.Clock.cs +++ b/S7.Net/Plc.Clock.cs @@ -11,6 +11,10 @@ partial class Plc { private const byte SzlFunctionGroupTimers = 0x07; private const byte SzlSubFunctionReadClock = 0x01; + private const byte SzlSubFunctionWriteClock = 0x02; + private const byte TransportSizeOctetString = 0x09; + private const int PduErrOffset = 20; + private const int UserDataResultOffset = PduErrOffset + 2; private static byte[] BuildClockReadRequest() { @@ -25,29 +29,55 @@ private static byte[] BuildClockReadRequest() 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; + const int udLenOffset = UserDataResultOffset + 2; + const int udValueOffset = udLenOffset + 4; - var pduErr = Word.FromByteArray(message.Skip(pduErrOffset).Take(2).ToArray()); - if (pduErr != 0) + AssertPduResult(message); + AssertUserDataResult(message, 0xff); + + var len = Word.FromByteArray(message.Skip(udLenOffset).Take(2).ToArray()); + if (len != Types.DateTime.Length) { - throw new Exception($"Response from PLC indicates error 0x{pduErr:X4}."); + throw new Exception($"Unexpected response length {len}, expected {Types.DateTime.Length}."); } - var dtResult = message[dtResultOffset]; - if (dtResult != 0xff) + return Types.DateTime.FromByteArray(message.Skip(udValueOffset).Take(Types.DateTime.Length).ToArray()); + } + + private static byte[] BuildClockWriteRequest(DateTime value) + { + var stream = new MemoryStream(); + + WriteUserDataRequest(stream, SzlFunctionGroupTimers, SzlSubFunctionWriteClock, 14); + stream.Write(new byte[] { 0xff, TransportSizeOctetString, 0x00, Types.DateTime.Length }); + stream.Write(new byte[] { 0x00, 0x19 }); // Start of actual DateTime value, DateTime.ToByteArray is broken + stream.Write(Types.DateTime.ToByteArray(value)); + + stream.SetLength(stream.Position); + return stream.ToArray(); + } + + private static void ParseClockWriteResponse(byte[] message) + { + AssertPduResult(message); + AssertUserDataResult(message, 0x0a); + } + + private static void AssertPduResult(byte[] message) + { + var pduErr = Word.FromByteArray(message.Skip(PduErrOffset).Take(2).ToArray()); + if (pduErr != 0) { - throw new Exception($"Response from PLC indicates error 0x{dtResult:X2}."); + throw new Exception($"Response from PLC indicates error 0x{pduErr:X4}."); } + } - var len = Word.FromByteArray(message.Skip(dtLenOffset).Take(2).ToArray()); - if (len != Types.DateTime.Length) + private static void AssertUserDataResult(byte[] message, byte expected) + { + var dtResult = message[UserDataResultOffset]; + if (dtResult != expected) { - throw new Exception($"Unexpected response length {len}, expected {Types.DateTime.Length}."); + throw new Exception($"Response from PLC was 0x{dtResult:X2}, expected 0x{expected:X2}."); } - - 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 7a1dd44..8b828f3 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -335,7 +335,10 @@ public async Task> ReadMultipleVarsAsync(List dataItems /// A task that represents the asynchronous operation. public async Task WriteClockAsync(System.DateTime value, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + var request = BuildClockWriteRequest(value); + var response = await RequestTsduAsync(request, cancellationToken); + + ParseClockWriteResponse(response); } /// diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index cfb4d99..2e28131 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -510,7 +510,10 @@ public System.DateTime ReadClock() /// The date and time to set the PLC clock to. public void WriteClock(System.DateTime value) { - throw new NotImplementedException(); + var request = BuildClockWriteRequest(value); + var response = RequestTsdu(request); + + ParseClockWriteResponse(response); } ///