mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 22:38:27 +08:00
Merge branch 'main' into s5-date
This commit is contained in:
@@ -56,28 +56,35 @@ namespace S7.Net
|
||||
WriteS7Header(stream, s7MessageTypeUserData, parameterLength, dataLength);
|
||||
}
|
||||
|
||||
private static void WriteSzlReadRequest(System.IO.MemoryStream stream, ushort szlId, ushort szlIndex)
|
||||
private static void WriteUserDataRequest(System.IO.MemoryStream stream, byte functionGroup, byte subFunction, int dataLength)
|
||||
{
|
||||
WriteUserDataHeader(stream, 8, 8);
|
||||
WriteUserDataHeader(stream, 8, dataLength);
|
||||
|
||||
// Parameter
|
||||
const byte szlMethodRequest = 0x11;
|
||||
const byte szlTypeRequest = 0b100;
|
||||
const byte szlFunctionGroupCpuFunctions = 0b100;
|
||||
const byte subFunctionReadSzl = 0x01;
|
||||
const byte userDataMethodRequest = 0x11;
|
||||
const byte userDataTypeRequest = 0x4;
|
||||
|
||||
// Parameter head
|
||||
stream.Write(new byte[] { 0x00, 0x01, 0x12 });
|
||||
// Parameter length
|
||||
stream.WriteByte(0x04);
|
||||
// Method
|
||||
stream.WriteByte(szlMethodRequest);
|
||||
stream.WriteByte(userDataMethodRequest);
|
||||
// Type / function group
|
||||
stream.WriteByte(szlTypeRequest << 4 | szlFunctionGroupCpuFunctions);
|
||||
stream.WriteByte((byte)(userDataTypeRequest << 4 | (functionGroup & 0x0f)));
|
||||
// Subfunction
|
||||
stream.WriteByte(subFunctionReadSzl);
|
||||
stream.WriteByte(subFunction);
|
||||
// Sequence number
|
||||
stream.WriteByte(0);
|
||||
}
|
||||
|
||||
private static void WriteSzlReadRequest(System.IO.MemoryStream stream, ushort szlId, ushort szlIndex)
|
||||
{
|
||||
// Parameter
|
||||
const byte szlFunctionGroupCpuFunctions = 0b100;
|
||||
const byte subFunctionReadSzl = 0x01;
|
||||
|
||||
WriteUserDataRequest(stream, szlFunctionGroupCpuFunctions, subFunctionReadSzl, 8);
|
||||
|
||||
// Data
|
||||
const byte success = 0xff;
|
||||
@@ -353,7 +360,7 @@ namespace S7.Net
|
||||
private static byte[] BuildSzlReadRequestPackage(ushort szlId, ushort szlIndex)
|
||||
{
|
||||
var stream = new System.IO.MemoryStream();
|
||||
|
||||
|
||||
WriteSzlReadRequest(stream, szlId, szlIndex);
|
||||
stream.SetLength(stream.Position);
|
||||
|
||||
|
||||
92
S7.Net/Plc.Clock.cs
Normal file
92
S7.Net/Plc.Clock.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
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 const byte SzlSubFunctionWriteClock = 0x02;
|
||||
private const byte TransportSizeOctetString = 0x09;
|
||||
private const int PduErrOffset = 20;
|
||||
private const int UserDataResultOffset = PduErrOffset + 2;
|
||||
|
||||
/// <summary>
|
||||
/// The length in bytes of DateTime stored in the PLC.
|
||||
/// </summary>
|
||||
private const int DateTimeLength = 10;
|
||||
|
||||
private static byte[] BuildClockReadRequest()
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
|
||||
WriteUserDataRequest(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 udLenOffset = UserDataResultOffset + 2;
|
||||
const int udValueOffset = udLenOffset + 2;
|
||||
const int dateTimeSkip = 2;
|
||||
|
||||
AssertPduResult(message);
|
||||
AssertUserDataResult(message, 0xff);
|
||||
|
||||
var len = Word.FromByteArray(message.Skip(udLenOffset).Take(2).ToArray());
|
||||
if (len != DateTimeLength)
|
||||
{
|
||||
throw new Exception($"Unexpected response length {len}, expected {DateTimeLength}.");
|
||||
}
|
||||
|
||||
// Skip first 2 bytes from date time value because DateTime.FromByteArray doesn't parse them.
|
||||
return Types.DateTime.FromByteArray(message.Skip(udValueOffset + dateTimeSkip)
|
||||
.Take(DateTimeLength - dateTimeSkip).ToArray());
|
||||
}
|
||||
|
||||
private static byte[] BuildClockWriteRequest(DateTime value)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
|
||||
WriteUserDataRequest(stream, SzlFunctionGroupTimers, SzlSubFunctionWriteClock, 14);
|
||||
stream.Write(new byte[] { 0xff, TransportSizeOctetString, 0x00, DateTimeLength });
|
||||
// Start of DateTime value, DateTime.ToByteArray only serializes the final 8 bytes
|
||||
stream.Write(new byte[] { 0x00, 0x19 });
|
||||
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{pduErr:X4}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertUserDataResult(byte[] message, byte expected)
|
||||
{
|
||||
var dtResult = message[UserDataResultOffset];
|
||||
if (dtResult != expected)
|
||||
{
|
||||
throw new Exception($"Response from PLC was 0x{dtResult:X2}, expected 0x{expected:X2}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,6 +312,35 @@ namespace S7.Net
|
||||
return dataItems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the PLC clock value.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is None.
|
||||
/// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, with it's result set to the current PLC time on completion.</returns>
|
||||
public async Task<System.DateTime> ReadClockAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var request = BuildClockReadRequest();
|
||||
var response = await RequestTsduAsync(request, cancellationToken);
|
||||
|
||||
return ParseClockReadResponse(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the PLC clock value.
|
||||
/// </summary>
|
||||
/// <param name="value">The date and time to set the PLC clock to</param>
|
||||
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is None.
|
||||
/// Please note that cancellation is advisory/cooperative and will not lead to immediate cancellation in all cases.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task WriteClockAsync(System.DateTime value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var request = BuildClockWriteRequest(value);
|
||||
var response = await RequestTsduAsync(request, cancellationToken);
|
||||
|
||||
ParseClockWriteResponse(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the current status from the PLC. A value of 0x08 indicates the PLC is in run status, regardless of the PLC type.
|
||||
/// </summary>
|
||||
|
||||
@@ -492,6 +492,30 @@ namespace S7.Net
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the PLC clock value.
|
||||
/// </summary>
|
||||
/// <returns>The current PLC time.</returns>
|
||||
public System.DateTime ReadClock()
|
||||
{
|
||||
var request = BuildClockReadRequest();
|
||||
var response = RequestTsdu(request);
|
||||
|
||||
return ParseClockReadResponse(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the PLC clock value.
|
||||
/// </summary>
|
||||
/// <param name="value">The date and time to set the PLC clock to.</param>
|
||||
public void WriteClock(System.DateTime value)
|
||||
{
|
||||
var request = BuildClockWriteRequest(value);
|
||||
var response = RequestTsdu(request);
|
||||
|
||||
ParseClockWriteResponse(response);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the current status from the PLC. A value of 0x08 indicates the PLC is in run status, regardless of the PLC type.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user