docs(s7-fixture): recapture the TM/CT addressing probe against the shipped profile; note CountingPlcFactory thread-safety
This commit is contained in:
@@ -142,17 +142,22 @@ counter to an `int` count (`Counter.FromByteArray`, raw word — **not** BCD).
|
||||
|
||||
Live-probed 2026-08-17: snap7's server resolves the TM/CT request address as a
|
||||
**bit address** — it serves byte `addr >> 3` of the area — whereas S7.Net, and a
|
||||
real S7 CPU, put the device **number** in that field. Measured directly against
|
||||
`10.100.0.35:1102`:
|
||||
real S7 CPU, put the device **number** in that field. Measured with S7netplus
|
||||
0.20.0 against `10.100.0.35:1102` running **the profile above**:
|
||||
|
||||
```
|
||||
ReadBytes(Timer,db=0,start=0,count=8) => [00 00 00 00 24 56 00 00] # bytes 0..7 (T2 seeded at byte 4)
|
||||
ReadBytes(Timer,db=0,start=2,count=2) => [00 00] # 2>>3 = byte 0, NOT timer 2 at byte 4
|
||||
ReadBytes(Timer,db=0,start=4,count=2) => [00 00] # 4>>3 = byte 0
|
||||
ReadBytes(Timer,db=0,start=32,count=2) => [24 56] # 32>>3 = byte 4 ← the timer-2 cell
|
||||
ReadBytes(Counter,db=0,start=48,count=2) => [12 67] # 48>>3 = byte 6 ← the counter-3 cell
|
||||
ReadBytes(Timer,db=0,start=0,count=8) => [24 56 00 00 00 00 00 00] # TM bytes 0..7: T0 = 0x2456
|
||||
ReadBytes(Timer,db=0,start=0,count=2) => [24 56] # T0 -> byte 0. 0>>3 == 2*0, so BOTH conventions agree here
|
||||
ReadBytes(Timer,db=0,start=5,count=2) => [24 56] # T5 -> 5>>3 = byte 0. WRONG cell: T5 lives at bytes 10..11 (0x1123)
|
||||
ReadBytes(Timer,db=0,start=80,count=2) => [11 23] # byte 10 needs start 80 (= 10*8): the T5 cell, reached the bit-address way
|
||||
ReadBytes(Counter,db=0,start=0,count=2) => [12 67] # C0 -> byte 0 = 4711. Both conventions agree
|
||||
ReadBytes(Counter,db=0,start=7,count=2) => [12 67] # C7 -> 7>>3 = byte 0. WRONG cell: C7 lives at bytes 14..15 (0x7A69 = 31337)
|
||||
ReadBytes(Counter,db=0,start=112,count=2) => [7A 69] # byte 14 needs start 112 (= 14*8): the C7 cell
|
||||
```
|
||||
|
||||
Note lines 2 and 3: asking for `T0` and asking for `T5` return the **same bytes**
|
||||
— every device 0..7 collapses onto TM byte 0. Same for `C0` / `C7`.
|
||||
|
||||
No error is raised — the reads succeed, they just land on the wrong cell. Device
|
||||
**0** is the single fixed point of the two conventions (`0 >> 3 == 2 × 0 == 0`),
|
||||
so `T0` / `C0` are the only cells whose *value* the fixture can pin; that is what
|
||||
|
||||
+7
@@ -488,6 +488,13 @@ public sealed class S7_1500SpanReadTests(Snap7ServerFixture sim)
|
||||
/// test can assert the wire shape rather than infer it. Delegates to the real factory, so
|
||||
/// every counted call is the one production would have issued.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>Not thread-safe, by design.</b> Every <c>ReadBatchAsync</c> builds its own factory and
|
||||
/// the driver serialises all wire calls on its <c>_gate</c>, so nothing here is ever touched
|
||||
/// concurrently today. A future caller that shared one factory across parallel reads would
|
||||
/// silently corrupt the counts and shape lists rather than fail loudly — give it a lock, or
|
||||
/// a factory per batch, before doing that.
|
||||
/// </remarks>
|
||||
/// <param name="inner">The factory producing the real connection.</param>
|
||||
private sealed class CountingPlcFactory(IS7PlcFactory inner) : IS7PlcFactory
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user