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 and writing PLC clock #507

Merged
merged 13 commits into from
Sep 5, 2023
259 changes: 259 additions & 0 deletions S7.Net.UnitTest/CommunicationTests/Clock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using S7.Net.Protocol;

namespace S7.Net.UnitTest.CommunicationTests;

[TestClass]
public class Clock
{
[TestMethod, Timeout(1000)]
public async Task Read_Clock_Value()
{
var cs = new CommunicationSequence
{
ConnectionOpenTemplates.ConnectionRequestConfirm,
ConnectionOpenTemplates.CommunicationSetup,
{
"""
// TPKT
03 00 00 1d

// COTP
02 f0 80

// S7 read clock
// UserData header
32 07 00 00 PDU1 PDU2
// Parameter length
00 08
// Data length
00 04

// Parameter
// Head
00 01 12
// Length
04
// Method (Request/Response): Req
11
// Type request (4...) Function group timers (...7)
47
// Subfunction: read clock
01
// Sequence number
00

// Data
// Return code
0a
// Transport size
00
// Payload length
00 00
""",
"""
// TPKT
03 00 00 2b

// COTP
02 f0 80

// S7 read clock response
// UserData header
32 07 00 00 PDU1 PDU2
// Parameter length
00 0c
// Data length
00 0e

// Parameter
// Head
00 01 12
// Length
08
// Method (Request/Response): Res
12
// Type response (8...) Function group timers (...7)
87
// Subfunction: read clock
01
// Sequence number
01
// Data unit reference
00
// Last data unit? Yes
00
// Error code
00 00

// Data
// Error code
ff
// Transport size: OCTET STRING
09
// Length
00 0a

// Timestamp
// Reserved
00
// Year 1
19
// Year 2
14
// Month
08
// Day
20
// Hour
11
// Minute
59
// Seconds
43
// Milliseconds: 912..., Day of week: ...4
91 24
"""
}
};

static async Task Client(int port)
{
var conn = new Plc(IPAddress.Loopback.ToString(), port, new TsapPair(new Tsap(1, 2), new Tsap(3, 4)));
await conn.OpenAsync();
var time = await conn.ReadClockAsync();

Assert.AreEqual(new DateTime(2014, 8, 20, 11, 59, 43, 912), time);
conn.Close();
}

await Task.WhenAll(cs.Serve(out var port), Client(port));
}

[TestMethod, Timeout(1000)]
public async Task Write_Clock_Value()
{
var cs = new CommunicationSequence
{
ConnectionOpenTemplates.ConnectionRequestConfirm,
ConnectionOpenTemplates.CommunicationSetup,
{
"""
// TPKT
03 00 00 27

// COTP
02 f0 80

// S7 read clock
// UserData header
32 07 00 00 PDU1 PDU2
// Parameter length
00 08
// Data length
00 0e

// Parameter
// Head
00 01 12
// Length
04
// Method (Request/Response): Req
11
// Type request (4...) Function group timers (...7)
47
// Subfunction: write clock
02
// Sequence number
00

// Data
// Return code
ff
// Transport size
09
// Payload length
00 0a

// Payload
// Timestamp
// Reserved
00
// Year 1
19
// Year 2
14
// Month
08
// Day
20
// Hour
11
// Minute
59
// Seconds
43
// Milliseconds: 912..., Day of week: ...4
91 24
""",
"""
// TPKT
03 00 00 21

// COTP
02 f0 80

// S7 read clock response
// UserData header
32 07 00 00 PDU1 PDU2
// Parameter length
00 0c
// Data length
00 04

// Parameter
// Head
00 01 12
// Length
08
// Method (Request/Response): Res
12
// Type response (8...) Function group timers (...7)
87
// Subfunction: write clock
02
// Sequence number
01
// Data unit reference
00
// Last data unit? Yes
00
// Error code
00 00

// Data
// Error code
0a
// Transport size: NONE
00
// Length
00 00
"""
}
};

static async Task Client(int port)
{
var conn = new Plc(IPAddress.Loopback.ToString(), port, new TsapPair(new Tsap(1, 2), new Tsap(3, 4)));
await conn.OpenAsync();
await conn.WriteClockAsync(new DateTime(2014, 08, 20, 11, 59, 43, 912));

conn.Close();
}

await Task.WhenAll(cs.Serve(out var port), Client(port));
}
}
27 changes: 17 additions & 10 deletions S7.Net/PLCHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,35 @@ private static void WriteUserDataHeader(System.IO.MemoryStream stream, int param
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;
Expand Down Expand Up @@ -343,7 +350,7 @@ private static byte[] BuildReadRequestPackage(IList<DataItemAddress> dataItems)
private static byte[] BuildSzlReadRequestPackage(ushort szlId, ushort szlIndex)
{
var stream = new System.IO.MemoryStream();

WriteSzlReadRequest(stream, szlId, szlIndex);
stream.SetLength(stream.Position);

Expand Down
Loading
Loading