Skip to content

Commit 76a7ea0

Browse files
authored
Merge pull request #507 from mycroes/clock
Add support for reading and writing PLC clock
2 parents 0de9364 + 4764c99 commit 76a7ea0

5 files changed

Lines changed: 421 additions & 10 deletions

File tree

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
using System;
2+
using System.Net;
3+
using System.Threading.Tasks;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
using S7.Net.Protocol;
6+
7+
namespace S7.Net.UnitTest.CommunicationTests;
8+
9+
[TestClass]
10+
public class Clock
11+
{
12+
[TestMethod, Timeout(1000)]
13+
public async Task Read_Clock_Value()
14+
{
15+
var cs = new CommunicationSequence
16+
{
17+
ConnectionOpenTemplates.ConnectionRequestConfirm,
18+
ConnectionOpenTemplates.CommunicationSetup,
19+
{
20+
"""
21+
// TPKT
22+
03 00 00 1d
23+
24+
// COTP
25+
02 f0 80
26+
27+
// S7 read clock
28+
// UserData header
29+
32 07 00 00 PDU1 PDU2
30+
// Parameter length
31+
00 08
32+
// Data length
33+
00 04
34+
35+
// Parameter
36+
// Head
37+
00 01 12
38+
// Length
39+
04
40+
// Method (Request/Response): Req
41+
11
42+
// Type request (4...) Function group timers (...7)
43+
47
44+
// Subfunction: read clock
45+
01
46+
// Sequence number
47+
00
48+
49+
// Data
50+
// Return code
51+
0a
52+
// Transport size
53+
00
54+
// Payload length
55+
00 00
56+
""",
57+
"""
58+
// TPKT
59+
03 00 00 2b
60+
61+
// COTP
62+
02 f0 80
63+
64+
// S7 read clock response
65+
// UserData header
66+
32 07 00 00 PDU1 PDU2
67+
// Parameter length
68+
00 0c
69+
// Data length
70+
00 0e
71+
72+
// Parameter
73+
// Head
74+
00 01 12
75+
// Length
76+
08
77+
// Method (Request/Response): Res
78+
12
79+
// Type response (8...) Function group timers (...7)
80+
87
81+
// Subfunction: read clock
82+
01
83+
// Sequence number
84+
01
85+
// Data unit reference
86+
00
87+
// Last data unit? Yes
88+
00
89+
// Error code
90+
00 00
91+
92+
// Data
93+
// Error code
94+
ff
95+
// Transport size: OCTET STRING
96+
09
97+
// Length
98+
00 0a
99+
100+
// Timestamp
101+
// Reserved
102+
00
103+
// Year 1
104+
19
105+
// Year 2
106+
14
107+
// Month
108+
08
109+
// Day
110+
20
111+
// Hour
112+
11
113+
// Minute
114+
59
115+
// Seconds
116+
43
117+
// Milliseconds: 912..., Day of week: ...4
118+
91 24
119+
"""
120+
}
121+
};
122+
123+
static async Task Client(int port)
124+
{
125+
var conn = new Plc(IPAddress.Loopback.ToString(), port, new TsapPair(new Tsap(1, 2), new Tsap(3, 4)));
126+
await conn.OpenAsync();
127+
var time = await conn.ReadClockAsync();
128+
129+
Assert.AreEqual(new DateTime(2014, 8, 20, 11, 59, 43, 912), time);
130+
conn.Close();
131+
}
132+
133+
await Task.WhenAll(cs.Serve(out var port), Client(port));
134+
}
135+
136+
[TestMethod, Timeout(1000)]
137+
public async Task Write_Clock_Value()
138+
{
139+
var cs = new CommunicationSequence
140+
{
141+
ConnectionOpenTemplates.ConnectionRequestConfirm,
142+
ConnectionOpenTemplates.CommunicationSetup,
143+
{
144+
"""
145+
// TPKT
146+
03 00 00 27
147+
148+
// COTP
149+
02 f0 80
150+
151+
// S7 read clock
152+
// UserData header
153+
32 07 00 00 PDU1 PDU2
154+
// Parameter length
155+
00 08
156+
// Data length
157+
00 0e
158+
159+
// Parameter
160+
// Head
161+
00 01 12
162+
// Length
163+
04
164+
// Method (Request/Response): Req
165+
11
166+
// Type request (4...) Function group timers (...7)
167+
47
168+
// Subfunction: write clock
169+
02
170+
// Sequence number
171+
00
172+
173+
// Data
174+
// Return code
175+
ff
176+
// Transport size
177+
09
178+
// Payload length
179+
00 0a
180+
181+
// Payload
182+
// Timestamp
183+
// Reserved
184+
00
185+
// Year 1
186+
19
187+
// Year 2
188+
14
189+
// Month
190+
08
191+
// Day
192+
20
193+
// Hour
194+
11
195+
// Minute
196+
59
197+
// Seconds
198+
43
199+
// Milliseconds: 912..., Day of week: ...4
200+
91 24
201+
""",
202+
"""
203+
// TPKT
204+
03 00 00 21
205+
206+
// COTP
207+
02 f0 80
208+
209+
// S7 read clock response
210+
// UserData header
211+
32 07 00 00 PDU1 PDU2
212+
// Parameter length
213+
00 0c
214+
// Data length
215+
00 04
216+
217+
// Parameter
218+
// Head
219+
00 01 12
220+
// Length
221+
08
222+
// Method (Request/Response): Res
223+
12
224+
// Type response (8...) Function group timers (...7)
225+
87
226+
// Subfunction: write clock
227+
02
228+
// Sequence number
229+
01
230+
// Data unit reference
231+
00
232+
// Last data unit? Yes
233+
00
234+
// Error code
235+
00 00
236+
237+
// Data
238+
// Error code
239+
0a
240+
// Transport size: NONE
241+
00
242+
// Length
243+
00 00
244+
"""
245+
}
246+
};
247+
248+
static async Task Client(int port)
249+
{
250+
var conn = new Plc(IPAddress.Loopback.ToString(), port, new TsapPair(new Tsap(1, 2), new Tsap(3, 4)));
251+
await conn.OpenAsync();
252+
await conn.WriteClockAsync(new DateTime(2014, 08, 20, 11, 59, 43, 912));
253+
254+
conn.Close();
255+
}
256+
257+
await Task.WhenAll(cs.Serve(out var port), Client(port));
258+
}
259+
}

S7.Net/PLCHelpers.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,35 @@ private static void WriteUserDataHeader(System.IO.MemoryStream stream, int param
5656
WriteS7Header(stream, s7MessageTypeUserData, parameterLength, dataLength);
5757
}
5858

59-
private static void WriteSzlReadRequest(System.IO.MemoryStream stream, ushort szlId, ushort szlIndex)
59+
private static void WriteUserDataRequest(System.IO.MemoryStream stream, byte functionGroup, byte subFunction, int dataLength)
6060
{
61-
WriteUserDataHeader(stream, 8, 8);
61+
WriteUserDataHeader(stream, 8, dataLength);
6262

6363
// Parameter
64-
const byte szlMethodRequest = 0x11;
65-
const byte szlTypeRequest = 0b100;
66-
const byte szlFunctionGroupCpuFunctions = 0b100;
67-
const byte subFunctionReadSzl = 0x01;
64+
const byte userDataMethodRequest = 0x11;
65+
const byte userDataTypeRequest = 0x4;
6866

6967
// Parameter head
7068
stream.Write(new byte[] { 0x00, 0x01, 0x12 });
7169
// Parameter length
7270
stream.WriteByte(0x04);
7371
// Method
74-
stream.WriteByte(szlMethodRequest);
72+
stream.WriteByte(userDataMethodRequest);
7573
// Type / function group
76-
stream.WriteByte(szlTypeRequest << 4 | szlFunctionGroupCpuFunctions);
74+
stream.WriteByte((byte)(userDataTypeRequest << 4 | (functionGroup & 0x0f)));
7775
// Subfunction
78-
stream.WriteByte(subFunctionReadSzl);
76+
stream.WriteByte(subFunction);
7977
// Sequence number
8078
stream.WriteByte(0);
79+
}
80+
81+
private static void WriteSzlReadRequest(System.IO.MemoryStream stream, ushort szlId, ushort szlIndex)
82+
{
83+
// Parameter
84+
const byte szlFunctionGroupCpuFunctions = 0b100;
85+
const byte subFunctionReadSzl = 0x01;
86+
87+
WriteUserDataRequest(stream, szlFunctionGroupCpuFunctions, subFunctionReadSzl, 8);
8188

8289
// Data
8390
const byte success = 0xff;
@@ -343,7 +350,7 @@ private static byte[] BuildReadRequestPackage(IList<DataItemAddress> dataItems)
343350
private static byte[] BuildSzlReadRequestPackage(ushort szlId, ushort szlIndex)
344351
{
345352
var stream = new System.IO.MemoryStream();
346-
353+
347354
WriteSzlReadRequest(stream, szlId, szlIndex);
348355
stream.SetLength(stream.Position);
349356

0 commit comments

Comments
 (0)