From 1201d5fcfbbaf0929285b27ca863a8abfcf0d6cd Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 7 Oct 2026 15:50:00 -0400 Subject: [PATCH] test(ab-transport): golden libplctag<->ab_server frame captures for Logix, Micro800 and PCCC 17 .hex fixtures captured from libplctag 1.5.2 (native 2.6.0) against otopcua-ab-server:libplctag-release, plus the CaptureFile loader, its length/alternation tests, and the re-runnable scripts/ab-capture/capture.sh. Observed and recorded in Captures/README.md "Routing observed": - Micro800 is NOT UCMM under libplctag 2.6.0: it opens a Large Forward Open (path 20 02 24 01) and reads connected; use_connected_msg=0 is ignored. - SLC500/MicroLogix/PLC-5 send Execute PCCC 0x4B unconnected and unwrapped; a configured path=1,0 never reaches the wire. - Only Large FO 0x5B was observed; no 0x54 fallback occurred. - LogixPccc is unreachable against ab_server (CIP 0x08); no fixture committed. --- scripts/ab-capture/capture.sh | 96 +++++++++++++++++++ .../Captures/CaptureFile.cs | 48 ++++++++++ .../Captures/CaptureFileTests.cs | 74 ++++++++++++++ .../Captures/README.md | 92 ++++++++++++++++++ .../Captures/logix-forward-close.hex | 9 ++ .../Captures/logix-forward-open.hex | 9 ++ .../Captures/logix-read-bool.hex | 11 +++ .../Captures/logix-read-dint-array16.hex | 11 +++ .../Captures/logix-read-dint.hex | 11 +++ .../Captures/logix-read-string.hex | 11 +++ .../Captures/logix-read-unknown-tag.hex | 9 ++ .../Captures/logix-register-session.hex | 9 ++ .../Captures/logix-tags-listing.hex | 9 ++ .../Captures/logix-write-dint.hex | 11 +++ .../Captures/logix-write-string.hex | 11 +++ .../Captures/micro800-read-dint.hex | 17 ++++ .../Captures/micrologix-read-n7.hex | 13 +++ .../Captures/plc5-read-n7.hex | 13 +++ .../Captures/slc500-read-f8.hex | 13 +++ .../Captures/slc500-read-n7.hex | 13 +++ .../Captures/slc500-write-n7.hex | 13 +++ ...W.OtOpcUa.Driver.Ab.Transport.Tests.csproj | 4 + 22 files changed, 507 insertions(+) create mode 100755 scripts/ab-capture/capture.sh create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/CaptureFile.cs create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/CaptureFileTests.cs create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/README.md create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-forward-close.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-forward-open.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-bool.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-dint-array16.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-dint.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-string.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-unknown-tag.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-register-session.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-tags-listing.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-write-dint.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-write-string.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/micro800-read-dint.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/micrologix-read-n7.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/plc5-read-n7.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-read-f8.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-read-n7.hex create mode 100644 tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-write-n7.hex diff --git a/scripts/ab-capture/capture.sh b/scripts/ab-capture/capture.sh new file mode 100755 index 00000000..b1fb4f7c --- /dev/null +++ b/scripts/ab-capture/capture.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# Golden-frame capture recipe for the native AB transport (Task 4 of +# docs/plans/2026-10-07-native-ab-transport.md). +# +# Captures the EtherNet/IP conversation between libplctag (the libplctag 1.5.2 +# NuGet wrapper, native core 2.6.0) and the ab_server simulator fixtures, and +# converts each pcap into the `.hex` line format consumed by +# tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures: +# +# > server EIP frame, uppercase hex, no spaces> +# < client EIP frame> +# # comment +# +# Packets are captured INSIDE the simulator container's network namespace +# (netshoot sidecar), where ab_server always listens on 44818 — so the same +# filter works for the AbLegacy containers whose host ports are 44819-44821. +# tshark also runs in the netshoot image on the docker host, so nothing needs +# installing on the workstation beyond python3. +# +# Usage: +# CAPDIR= scripts/ab-capture/capture.sh run -- +# scripts/ab-capture/capture.sh tohex +# +# The client command is whatever drives libplctag; the session used for the +# committed fixtures is a ~60-line console program over libplctag 1.5.2 (one tag, +# one Initialize + Read/Write, then Dispose + plc_tag_shutdown so the Forward Close +# and Unregister Session reach the wire). See Captures/README.md for every +# invocation. +# +# Fixtures (one AbCip profile at a time — they all bind host :44818): +# ssh dohertj2@10.100.0.35 'cd /opt/otopcua-abcip && docker compose --profile controllogix up -d' +# ssh dohertj2@10.100.0.35 'cd ~/otopcua-ablegacy && docker compose --profile slc500 up -d' +set -euo pipefail + +DOCKER_HOST_SSH="${DOCKER_HOST_SSH:-dohertj2@10.100.0.35}" +NETSHOOT="${NETSHOOT:-nicolaka/netshoot:v0.13}" +REMOTE_DIR="${REMOTE_DIR:-/tmp/abcap}" + +cmd="${1:-}"; shift || true + +case "$cmd" in + run) + container="$1"; name="$2"; shift 2 + [[ "${1:-}" == "--" ]] && shift + : "${CAPDIR:?set CAPDIR to a fresh empty directory}" + sidecar="abcap-$name" + ssh "$DOCKER_HOST_SSH" "mkdir -p $REMOTE_DIR && rm -f $REMOTE_DIR/$name.pcap && \ + docker run -d --rm --name $sidecar --net=container:$container -v $REMOTE_DIR:/cap $NETSHOOT \ + tcpdump -i eth0 -U -w /cap/$name.pcap tcp port 44818 >/dev/null" + sleep 2 # let tcpdump open the interface before the client connects + "$@" || echo "client exited non-zero: $?" >&2 + sleep 1 + ssh "$DOCKER_HOST_SSH" "docker stop $sidecar >/dev/null" + scp -q "$DOCKER_HOST_SSH:$REMOTE_DIR/$name.pcap" "$CAPDIR/$name.pcap" + echo "$CAPDIR/$name.pcap" + ;; + + tohex) + pcap="$1"; out="$2" + base="$(basename "$pcap")" + ssh "$DOCKER_HOST_SSH" "mkdir -p $REMOTE_DIR" + scp -q "$pcap" "$DOCKER_HOST_SSH:$REMOTE_DIR/tohex-$base" + # srcport + payload of every data-bearing, non-retransmitted segment, in capture order. + ssh "$DOCKER_HOST_SSH" "docker run --rm -v $REMOTE_DIR:/cap $NETSHOOT \ + tshark -r /cap/tohex-$base -T fields -e tcp.srcport -e tcp.payload \ + -Y 'tcp.len>0 && !tcp.analysis.retransmission'" \ + | python3 -I -c ' +import sys +# Re-join TCP segments into EIP encapsulation frames per direction, using the +# 24-byte header length field (offset 2, UInt16 LE) to find frame boundaries. +bufs = {True: b"", False: b""} +for line in sys.stdin: + line = line.strip() + if not line: + continue + port, payload = line.split("\t") + from_client = port != "44818" # srcport 44818 => server->client + bufs[from_client] += bytes.fromhex(payload.replace(":", "")) + b = bufs[from_client] + while len(b) >= 24: + total = 24 + int.from_bytes(b[2:4], "little") + if len(b) < total: + break + print(("> " if from_client else "< ") + b[:total].hex().upper()) + b = b[total:] + bufs[from_client] = b +for k, rest in bufs.items(): + if rest: + sys.exit(f"trailing partial frame ({len(rest)} bytes, client={k})") +' > "$out" + echo "$out: $(grep -c '^[<>]' "$out") frames" + ;; + + *) + sed -n '2,32p' "$0"; exit 2 ;; +esac diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/CaptureFile.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/CaptureFile.cs new file mode 100644 index 00000000..962db6d4 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/CaptureFile.cs @@ -0,0 +1,48 @@ +namespace ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.Captures; + +/// One EtherNet/IP encapsulation frame from a golden capture. +/// true for a client→server (>) frame. +/// The full frame: 24-byte encapsulation header plus data. +internal sealed record CapturedFrame(bool FromClient, byte[] Bytes); + +/// +/// Loads the golden libplctag ⇄ ab_server captures in Captures/*.hex. See +/// Captures/README.md for how each was taken. +/// +internal static class CaptureFile +{ + /// The directory the .hex fixtures are copied to. + public static string Dir => Path.Combine(AppContext.BaseDirectory, "Captures"); + + /// Loads every frame of a capture, in wire order; # lines are skipped. + /// The capture's file name, e.g. logix-read-dint.hex. + /// The frames. + public static IReadOnlyList Load(string name) => + File.ReadAllLines(Path.Combine(Dir, name)) + .Where(l => l.Length > 2 && l[0] is '>' or '<') + .Select(l => new CapturedFrame(l[0] == '>', Convert.FromHexString(l[2..].Trim()))) + .ToList(); + + /// The CIP payload of a SendRRData / SendUnitData frame (the last CPF item's data, + /// minus the 2-byte sequence count for connected data). + /// The frame. + /// The CIP payload, or empty for any other encapsulation command. + public static byte[] CipPayload(CapturedFrame f) + { + var b = f.Bytes; + var cmd = BitConverter.ToUInt16(b, 0); // test-only; fixtures are LE on a LE host + if (cmd is not (0x6F or 0x70)) return []; // only SendRRData / SendUnitData carry CPF items + var pos = 24 + 4 + 2; // header + interface handle + timeout + var itemCount = BitConverter.ToUInt16(b, pos); pos += 2; + byte[] last = []; + for (var i = 0; i < itemCount; i++) + { + var type = BitConverter.ToUInt16(b, pos); + var len = BitConverter.ToUInt16(b, pos + 2); + last = b.AsSpan(pos + 4, len).ToArray(); + if (type == 0x00B1) last = last[2..]; // strip connected sequence count + pos += 4 + len; + } + return last; + } +} diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/CaptureFileTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/CaptureFileTests.cs new file mode 100644 index 00000000..08382911 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/CaptureFileTests.cs @@ -0,0 +1,74 @@ +using Shouldly; +using Xunit; + +namespace ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.Captures; + +public sealed class CaptureFileTests +{ + private static readonly string[] Required = + [ + "logix-register-session.hex", "logix-forward-open.hex", + "logix-read-dint.hex", "logix-read-dint-array16.hex", "logix-read-string.hex", "logix-read-bool.hex", + "logix-write-dint.hex", "logix-write-string.hex", "logix-read-unknown-tag.hex", + "logix-tags-listing.hex", "logix-forward-close.hex", "micro800-read-dint.hex", + "slc500-read-n7.hex", "slc500-write-n7.hex", "slc500-read-f8.hex", + "micrologix-read-n7.hex", "plc5-read-n7.hex", + ]; + + public static TheoryData AllCaptures() + { + var data = new TheoryData(); + foreach (var f in Directory.GetFiles(CaptureFile.Dir, "*.hex").Order()) data.Add(Path.GetFileName(f)); + return data; + } + + [Fact] + public void Every_required_capture_is_present() => + Required.Where(n => !File.Exists(Path.Combine(CaptureFile.Dir, n))).ShouldBeEmpty(); + + [Theory] + [MemberData(nameof(AllCaptures))] + public void Every_frame_length_field_matches_its_data_length(string name) + { + var frames = CaptureFile.Load(name); + + frames.ShouldNotBeEmpty(); + foreach (var f in frames) + { + f.Bytes.Length.ShouldBeGreaterThanOrEqualTo(24, name); + BitConverter.ToUInt16(f.Bytes, 2).ShouldBe((ushort)(f.Bytes.Length - 24), name); + } + } + + [Theory] + [MemberData(nameof(AllCaptures))] + public void Frames_alternate_request_then_reply(string name) + { + var frames = CaptureFile.Load(name); + + frames.Count.ShouldBe(frames.Count / 2 * 2, name); + for (var i = 0; i < frames.Count; i++) frames[i].FromClient.ShouldBe(i % 2 == 0, $"{name} frame {i}"); + } + + [Fact] + public void CipPayload_strips_the_connected_sequence_count() + { + // Connected Read Tag Fragmented of TestDINT: service 0x52, path size 5 words, symbol 0x91 len 8. + var request = CaptureFile.Load("logix-read-dint.hex")[0]; + + CaptureFile.CipPayload(request)[..4].ShouldBe(new byte[] { 0x52, 0x05, 0x91, 0x08 }); + } + + [Fact] + public void CipPayload_returns_the_unconnected_item_for_SendRRData() + { + // Large Forward Open request goes out in an unconnected data item (0xB2): service 0x5B. + var request = CaptureFile.Load("logix-forward-open.hex")[0]; + + CaptureFile.CipPayload(request)[0].ShouldBe((byte)0x5B); + } + + [Fact] + public void CipPayload_is_empty_for_RegisterSession() => + CaptureFile.CipPayload(CaptureFile.Load("logix-register-session.hex")[0]).ShouldBeEmpty(); +} diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/README.md b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/README.md new file mode 100644 index 00000000..74ae2eb8 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/README.md @@ -0,0 +1,92 @@ +# Golden frame captures — libplctag ⇄ ab_server + +These `.hex` files are the exact EtherNet/IP frames libplctag exchanged with the `ab_server` +simulator. The native AB transport's codecs (plan Tasks 7 and 8) assert **byte parity** against +them. + +> ⚠️ They prove parity with **libplctag**, not correctness against a controller. The independent +> reference is the real-press captures in +> `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/CipSymbolObjectRealControllerCaptureTests.cs` +> and `CipTemplateObjectWireCaptureTests.cs`. + +## Format + +One frame per line: `> ` = client→server, `< ` = server→client, then the full encapsulation +frame (24-byte header + data) as uppercase hex with no spaces. `#` lines are comments; each file +opens with a comment block naming its client, server, profile, tag string and what the frames are. +`CaptureFile.Load` reads them; `CaptureFileTests` checks every frame's header length field +against its data length and that requests and replies alternate. + +Values are **little-endian on the wire** and carry live per-session values — session handle, +connection IDs, connection serial, PCCC TNS. A parity test must compare structure and the +fields it builds, not those session-assigned values. + +## How they were taken + +| | | +|---|---| +| Date | 2026-10-07 | +| Repo commit | `a0897fda` (fixture sources identical to the files on the docker host, checked by md5) | +| Client | **libplctag 1.5.2** (libplctag.NET NuGet, `Directory.Packages.props`) → libplctag.NativeImport 1.0.41 → native core **2.6.0** (read via `plc_tag_get_int_attribute(0,"version_*")`) | +| Server image | **`otopcua-ab-server:libplctag-release`** (ab_server from libplctag `v2.7.0` + vendored patches 0001 `@tags` listing and 0002 tag-not-found → CIP 0x04; both confirmed live — see `logix-tags-listing.hex` / `logix-read-unknown-tag.hex`) | +| AbCip compose | `/opt/otopcua-abcip` on `10.100.0.35` (= `tests/.../AbCip.IntegrationTests/Docker/`), host `:44818` | +| AbLegacy compose | `~/otopcua-ablegacy` on `10.100.0.35` (= `tests/.../AbLegacy.IntegrationTests/Docker/`), host `:44819` slc500 / `:44820` micrologix / `:44821` plc5 | +| Capture point | `tcpdump -i eth0 tcp port 44818` in a `nicolaka/netshoot:v0.13` sidecar sharing the simulator container's network namespace; `tshark` 4.2.4 in the same image; segments re-joined into frames by the 24-byte header length field | +| Recipe | `scripts/ab-capture/capture.sh run …` then `capture.sh tohex …` | + +The client was a ~60-line console program over libplctag 1.5.2 that mirrors how +`LibplctagTagRuntime` / `LibplctagLegacyTagRuntime` / `LibplctagTagEnumerator` build a tag +(`Gateway`, `Path`, `PlcType`, `Protocol=ab_eip`, `Name`, `Timeout=5s`, `ElementCount` when > 1; +no other attributes), then runs `InitializeAsync` + one `ReadAsync` or `WriteAsync`, disposes the +tag and calls `plc_tag_shutdown` so the Forward Close reaches the wire. **One fresh session per +capture**, one profile up at a time. Note that `InitializeAsync` itself performs a read, so a +"read" capture holds two read exchanges and a "write" capture holds a read then the write. + +### Per-file record + +All files: libplctag 1.5.2 (native 2.6.0), image `otopcua-ab-server:libplctag-release`. The +tag string is the libplctag attribute set the wrapper was given. + +| File | Profile | Tag string | Frames | +|---|---|---|---| +| `logix-register-session.hex` | controllogix | `protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestDINT` | RegisterSession (frames 1–2 of the `logix-read-dint` session) | +| `logix-forward-open.hex` | controllogix | same as above | Large Forward Open **0x5B** (frames 3–4) | +| `logix-read-dint.hex` | controllogix | `…&plc=controllogix&name=TestDINT` | 2× connected Read Tag Fragmented 0x52 (value 123456) | +| `logix-read-dint-array16.hex` | controllogix | `…&name=TestArray&elem_count=16` | 2× 0x52, 16 elements | +| `logix-read-string.hex` | controllogix | `…&name=TestString` | 2× 0x52, STRING `"HelloAB"` | +| `logix-read-bool.hex` | controllogix | `…&name=TestBOOL` | 2× 0x52, BOOL 0 | +| `logix-write-dint.hex` | controllogix | `…&name=TestDINT` | 0x52 read, then Write Tag 0x4D of 123456 | +| `logix-write-string.hex` | controllogix | `…&name=TestString` | 0x52 read, then 0x4D of `"HelloAB"` | +| `logix-read-unknown-tag.hex` | controllogix | `…&name=NoSuchTag` | 0x52 → general status **0x04** (patch 0002); libplctag raised `ErrorNotFound`, no retry | +| `logix-tags-listing.hex` | controllogix | `…&name=@tags` | Get Instance Attribute List **0x55** on Symbol class 0x6B (patch 0001); all 9 tags in one reply, status 0x00 | +| `logix-forward-close.hex` | controllogix | `…&name=TestDINT` | Forward Close 0x4E (last 2 frames of the `logix-read-dint` session) | +| `micro800-read-dint.hex` | micro800 | `protocol=ab_eip&gateway=10.100.0.35:44818&plc=micro800&name=TestDINT` | **Full session**: RegisterSession, Large FO, 2× 0x52 (value 987654), Forward Close | +| `slc500-read-n7.hex` | slc500 | `protocol=ab_eip&gateway=10.100.0.35:44819&path=1,0&plc=slc500&name=N7:0` | Full session: RegisterSession, 2× Execute PCCC 0x4B / PCCC 0x0F-0xA2 (value 4321) | +| `slc500-write-n7.hex` | slc500 | same | Full session: 0x0F-0xA2 read, then 0x0F-0xAA write of 4321 | +| `slc500-read-f8.hex` | slc500 | `…:44819&path=1,0&plc=slc500&name=F8:0` | Full session: 2× 0x0F-0xA2 (value 3.5) | +| `micrologix-read-n7.hex` | micrologix | `protocol=ab_eip&gateway=10.100.0.35:44820&plc=micrologix&name=N7:0` | Full session: 2× 0x0F-0xA2 (value 2468) | +| `plc5-read-n7.hex` | plc5 | `protocol=ab_eip&gateway=10.100.0.35:44821&path=1,0&plc=plc5&name=N7:0` | Full session: 2× PCCC 0x0F-**0x01** PLC-5 typed read (value 1357) | + +Logix files other than `register-session`/`forward-open`/`forward-close` hold **only the +operation's exchanges**; the handshake frames of their sessions are byte-for-byte the same shape +as those three files. The Micro800 and PCCC files hold the **whole session** so their routing is +visible in one place. No session in any family sent UnregisterSession (0x66). + +## Routing observed + +What libplctag 2.6.0 actually put on the wire. Tasks 9, 12 and 14 take their `Routing` choice +from this table. + +| Family | ForwardOpen | CIP request wrapped in Unconnected Send (0x52) | Route bytes | Requestor-id bytes (PCCC) | +|---|---|---|---|---| +| controllogix (`path=1,0`) | **yes** — Large FO 0x5B only (0x54 never sent; the 0x5B succeeded). FO itself is sent direct to the Connection Manager `20 06 24 01`, not wrapped. Connection path `01 00 20 02 24 01` (3 words), net params `0x42000FA2`, RPI 1 000 000 µs, transport `0xA3` | **no** — requests ride the connection (SendUnitData, item 0xB1) | `01 00` (port 1, slot 0), carried only inside the FO connection path | n/a | +| micro800 (no path) | **yes** — Large FO 0x5B, connection path `20 02 24 01` (2 words, no port segment). ⚠️ Not UCMM; see concerns | **no** — connected | none | n/a | +| slc500 (`path=1,0`) | **no** | **no** — Execute PCCC 0x4B sent unconnected (SendRRData, item 0xB2) straight to `20 67 24 01` (PCCC object) | **none — the `1,0` path was given but never appeared on the wire** | `07 3D F3 45 43 50 21` (length 7, vendor 0xF33D, serial 0x21504345) | +| micrologix (no path) | **no** | **no** — as slc500 | none | `07 3D F3 45 43 50 21` | +| plc5 (`path=1,0`) | **no** | **no** — as slc500 | **none — `1,0` not emitted** | `07 3D F3 45 43 50 21` | +| logixpccc (`path=1,0`, against the controllogix profile) | **no** | **yes** — Unconnected Send 0x52 to `20 06 24 01`, priority/tick `0A 05`, embedded 0x4B (len 0x1C) | `01 00` (path size 1 word, reserved, then `01 00`) | `07 3D F3 45 43 50 21` | + +**logixpccc is not reachable**: ab_server's ControlLogix mode has no PCCC object and answers +`CB 00 08 00` (service not supported). Its request shape above is read from the client side of +that rejected exchange; **no `.hex` fixture was committed for it**, since a parity fixture whose +reply is an error would teach nothing about the success path. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-forward-close.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-forward-close.hex new file mode 100644 index 00000000..5fedfbc4 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-forward-close.hex @@ -0,0 +1,9 @@ +# logix-forward-close.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestDINT +# session: logix-read-dint (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Forward Close 0x4E request/reply (last two frames of the logix-read-dint session). No UnregisterSession followed. +> 6F002800F3A5081A00000000F0B223110000000000000000000000000100020000000000B20018004E02200624010A05B1723DF3454350210300010020022401 +< 6F001E00F3A5081A00000000000000000000000000000000000000000100020000000000B2000E00CE000000B1723DF3454350210000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-forward-open.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-forward-open.hex new file mode 100644 index 00000000..fd300cb9 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-forward-open.hex @@ -0,0 +1,9 @@ +# logix-forward-open.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestDINT +# session: logix-read-dint (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Large Forward Open 0x5B request/reply (frames 3-4 of the logix-read-dint session). libplctag never sent a 0x54 here: the Large FO succeeded, so no fallback occurred. +> 6F004400F3A5081A00000000EFB223110000000000000000000000000100020000000000B20034005B02200624010A0500000000609E3F07B1723DF3454350210100000040420F00A20F004240420F00A20F0042A303010020022401 +< 6F002E00F3A5081A00000000000000000000000000000000000000000100020000000000B2001E00DB0000003F63DF2C609E3F07B1723DF34543502140420F0040420F000000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-bool.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-bool.hex new file mode 100644 index 00000000..bb6dca21 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-bool.hex @@ -0,0 +1,11 @@ +# logix-read-bool.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestBOOL +# session: logix-read-bool (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Two connected 0x52 exchanges for a BOOL (Initialize read + explicit Read). TestBOOL held 0. +> 7000280074EA6678000000000000000000000000000000000000000001000200A1000400ABDA0000B100140001005205910854657374424F4F4C010000000000 +< 70001D0074EA6678000000000000000000000000000000000000000001000200A100040039377043B10009000100D2000000C10000 +> 7000280074EA6678000000000000000000000000000000000000000001000200A1000400ABDA0000B100140002005205910854657374424F4F4C010000000000 +< 70001D0074EA6678000000000000000000000000000000000000000001000200A100040039377043B10009000200D2000000C10000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-dint-array16.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-dint-array16.hex new file mode 100644 index 00000000..7b57c3ab --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-dint-array16.hex @@ -0,0 +1,11 @@ +# logix-read-dint-array16.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestArray&elem_count=16 +# session: logix-read-dint-array16 (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Two connected 0x52 exchanges for 16 DINT elements (Initialize read + explicit Read). +> 70002A00C222CFC1000000000000000000000000000000000000000001000200A1000400B0B13F6BB100160001005206910954657374417272617900100000000000 +< 70005C00C222CFC1000000000000000000000000000000000000000001000200A1000400E59A1D32B10048000100D2000000C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +> 70002A00C222CFC1000000000000000000000000000000000000000001000200A1000400B0B13F6BB100160002005206910954657374417272617900100000000000 +< 70005C00C222CFC1000000000000000000000000000000000000000001000200A1000400E59A1D32B10048000200D2000000C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-dint.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-dint.hex new file mode 100644 index 00000000..2c9e47ab --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-dint.hex @@ -0,0 +1,11 @@ +# logix-read-dint.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestDINT +# session: logix-read-dint (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Two connected Read Tag Fragmented (0x52) exchanges: the read Initialize performs, then the explicit Read. TestDINT held 123456. +> 70002800F3A5081A000000000000000000000000000000000000000001000200A10004003F63DF2CB10014000100520591085465737444494E54010000000000 +< 70002000F3A5081A000000000000000000000000000000000000000001000200A1000400609E3F07B1000C000100D2000000C40040E20100 +> 70002800F3A5081A000000000000000000000000000000000000000001000200A10004003F63DF2CB10014000200520591085465737444494E54010000000000 +< 70002000F3A5081A000000000000000000000000000000000000000001000200A1000400609E3F07B1000C000200D2000000C40040E20100 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-string.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-string.hex new file mode 100644 index 00000000..ea4ace3b --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-string.hex @@ -0,0 +1,11 @@ +# logix-read-string.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestString +# session: logix-read-string (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Two connected 0x52 exchanges for a STRING (Initialize read + explicit Read). TestString held "HelloAB". +> 70002A00C782C518000000000000000000000000000000000000000001000200A10004003194EE20B100160001005206910A54657374537472696E67010000000000 +< 70007400C782C518000000000000000000000000000000000000000001000200A1000400C02DDF0FB10060000100D2000000D0000700000048656C6C6F41420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +> 70002A00C782C518000000000000000000000000000000000000000001000200A10004003194EE20B100160002005206910A54657374537472696E67010000000000 +< 70007400C782C518000000000000000000000000000000000000000001000200A1000400C02DDF0FB10060000200D2000000D0000700000048656C6C6F41420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-unknown-tag.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-unknown-tag.hex new file mode 100644 index 00000000..d2163619 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-read-unknown-tag.hex @@ -0,0 +1,9 @@ +# logix-read-unknown-tag.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=NoSuchTag +# session: logix-read-unknown-tag (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Initialize read of a non-existent tag; ab_server (patch 0002) answers general status 0x04. libplctag raised ErrorNotFound and sent no second read. +> 70002A00ABA8A2FA000000000000000000000000000000000000000001000200A100040003AB672DB10016000100520691094E6F5375636854616700010000000000 +< 70001A00ABA8A2FA000000000000000000000000000000000000000001000200A10004003CCC685DB10006000100D2000400 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-register-session.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-register-session.hex new file mode 100644 index 00000000..d1b63cfe --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-register-session.hex @@ -0,0 +1,9 @@ +# logix-register-session.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestDINT +# session: logix-read-dint (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : RegisterSession request/reply (frames 1-2 of the logix-read-dint session). +> 65000400000000000000000000000000000000000000000001000000 +< 65000400F3A5081A0000000000000000000000000000000001000000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-tags-listing.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-tags-listing.hex new file mode 100644 index 00000000..9e37d1ec --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-tags-listing.hex @@ -0,0 +1,9 @@ +# logix-tags-listing.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=@tags +# session: logix-tags-listing (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : One Get Instance Attribute List (0x55) on the Symbol class 0x6B (served by patch 0001); the reply lists all 9 seeded tags in one transfer. +> 70002800FF0C6DCD000000000000000000000000000000000000000001000200A10004006CA8729AB100140001005503206B2500000004000200070008000100 +< 70003301FF0C6DCD000000000000000000000000000000000000000001000200A10004004E31C665B1001F010100D500000001000000C120010001000000000000000000000009004D6F746F725F52756E02000000C42004000100000000000000000000000B004C696E65315F436F756E7403000000CA2004000100000000000000000000000C0050756D705F315F537065656404000000C4200400100000000000000000000000090054657374417272617905000000D02058000100000000000000000000000A0054657374537472696E6706000000C220010001000000000000000000000008005465737453494E5407000000C1200100010000000000000000000000080054657374424F4F4C08000000CA2004000100000000000000000000000800546573745245414C09000000C420040001000000000000000000000008005465737444494E54 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-write-dint.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-write-dint.hex new file mode 100644 index 00000000..ede25056 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-write-dint.hex @@ -0,0 +1,11 @@ +# logix-write-dint.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestDINT +# session: logix-write-dint (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Initialize read (0x52) then Write Tag (0x4D) of 123456. +> 70002800B517D7FE000000000000000000000000000000000000000001000200A10004000B9D929FB10014000100520591085465737444494E54010000000000 +< 70002000B517D7FE000000000000000000000000000000000000000001000200A1000400390C6A4CB1000C000100D2000000C40000000000 +> 70002A00B517D7FE000000000000000000000000000000000000000001000200A10004000B9D929FB100160002004D0591085465737444494E54C400010040E20100 +< 70001A00B517D7FE000000000000000000000000000000000000000001000200A1000400390C6A4CB10006000200CD000000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-write-string.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-write-string.hex new file mode 100644 index 00000000..e4bf2fd7 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/logix-write-string.hex @@ -0,0 +1,11 @@ +# logix-write-string.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: controllogix +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&path=1,0&plc=controllogix&name=TestString +# session: logix-write-string (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : Initialize read (0x52) then Write Tag (0x4D) of the STRING "HelloAB". +> 70002A003D5D525D000000000000000000000000000000000000000001000200A10004008DE304F6B100160001005206910A54657374537472696E67010000000000 +< 700074003D5D525D000000000000000000000000000000000000000001000200A1000400D01BC454B10060000100D2000000D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +> 700080003D5D525D000000000000000000000000000000000000000001000200A10004008DE304F6B1006C0002004D06910A54657374537472696E67D00001000700000048656C6C6F41420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +< 70001A003D5D525D000000000000000000000000000000000000000001000200A1000400D01BC454B10006000200CD000000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/micro800-read-dint.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/micro800-read-dint.hex new file mode 100644 index 00000000..bf4710c2 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/micro800-read-dint.hex @@ -0,0 +1,17 @@ +# micro800-read-dint.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: micro800 +# tag : protocol=ab_eip&gateway=10.100.0.35:44818&plc=micro800&name=TestDINT +# session: micro800-read-dint (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : FULL session. NOTE: libplctag 2.6.0 does NOT use UCMM for Micro800 - it opens a Large Forward Open (connection path 20 02 24 01, no port segment) and reads connected. TestDINT held 987654. +> 65000400000000000000000000000000000000000000000001000000 +< 65000400D4389A440000000000000000000000000000000001000000 +> 6F004200D4389A4400000000034C674E0000000000000000000000000100020000000000B20032005B02200624010A0500000000D717A44145043DF3454350210100000040420F00A20F004240420F00A20F0042A30220022401 +< 6F002E00D4389A4400000000000000000000000000000000000000000100020000000000B2001E00DB0000000E9EDB9ED717A44145043DF34543502140420F0040420F000000 +> 70002800D4389A44000000000000000000000000000000000000000001000200A10004000E9EDB9EB10014000100520591085465737444494E54010000000000 +< 70002000D4389A44000000000000000000000000000000000000000001000200A1000400D717A441B1000C000100D2000000C40006120F00 +> 70002800D4389A44000000000000000000000000000000000000000001000200A10004000E9EDB9EB10014000200520591085465737444494E54010000000000 +< 70002000D4389A44000000000000000000000000000000000000000001000200A1000400D717A441B1000C000200D2000000C40006120F00 +> 6F002600D4389A4400000000044C674E0000000000000000000000000100020000000000B20016004E02200624010A0545043DF345435021020020022401 +< 6F001E00D4389A4400000000000000000000000000000000000000000100020000000000B2000E00CE00000045043DF3454350210000 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/micrologix-read-n7.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/micrologix-read-n7.hex new file mode 100644 index 00000000..e3897199 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/micrologix-read-n7.hex @@ -0,0 +1,13 @@ +# micrologix-read-n7.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: micrologix (host :44820) +# tag : protocol=ab_eip&gateway=10.100.0.35:44820&plc=micrologix&name=N7:0 +# session: micrologix-read-n7 (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : FULL session (no path). PCCC 0x0F/0xA2 read, twice (Initialize + Read). N7:0 held 2468. +> 65000400000000000000000000000000000000000000000001000000 +< 65000400999FDCC20000000000000000000000000000000001000000 +> 6F002700999FDCC2000000001F7690460000000000000000000000000100020000000000B20017004B0220672401073DF3454350210F001D76A20207890000 +< 6F002100999FDCC200000000000000000000000000000000000000000100020000000000B2001100CB000000073DF3454350214F001D76A409 +> 6F002700999FDCC200000000217690460000000000000000000000000100020000000000B20017004B0220672401073DF3454350210F001F76A20207890000 +< 6F002100999FDCC200000000000000000000000000000000000000000100020000000000B2001100CB000000073DF3454350214F001F76A409 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/plc5-read-n7.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/plc5-read-n7.hex new file mode 100644 index 00000000..54c6c1e5 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/plc5-read-n7.hex @@ -0,0 +1,13 @@ +# plc5-read-n7.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: plc5 (host :44821) +# tag : protocol=ab_eip&gateway=10.100.0.35:44821&path=1,0&plc=plc5&name=N7:0 +# session: plc5-read-n7 (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : FULL session. PCCC 0x0F/0x01 PLC-5 typed read, twice (Initialize + Read). N7:0 held 1357. +> 65000400000000000000000000000000000000000000000001000000 +< 650004008543D8A50000000000000000000000000000000001000000 +> 6F002A008543D8A50000000075C4F9370000000000000000000000000100020000000000B2001A004B0220672401073DF3454350210F0073C4010000010006070002 +< 6F0021008543D8A500000000000000000000000000000000000000000100020000000000B2001100CB000000073DF3454350214F0073C44D05 +> 6F002A008543D8A50000000077C4F9370000000000000000000000000100020000000000B2001A004B0220672401073DF3454350210F0075C4010000010006070002 +< 6F0021008543D8A500000000000000000000000000000000000000000100020000000000B2001100CB000000073DF3454350214F0075C44D05 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-read-f8.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-read-f8.hex new file mode 100644 index 00000000..dde9799c --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-read-f8.hex @@ -0,0 +1,13 @@ +# slc500-read-f8.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: slc500 (host :44819) +# tag : protocol=ab_eip&gateway=10.100.0.35:44819&path=1,0&plc=slc500&name=F8:0 +# session: slc500-read-f8 (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : FULL session. PCCC 0x0F/0xA2 read of F8:0, twice (Initialize + Read). F8:0 held 3.5. +> 65000400000000000000000000000000000000000000000001000000 +< 650004002E82A6A40000000000000000000000000000000001000000 +> 6F0027002E82A6A400000000A1DFE9520000000000000000000000000100020000000000B20017004B0220672401073DF3454350210F009FDFA204088A0000 +< 6F0023002E82A6A400000000000000000000000000000000000000000100020000000000B2001300CB000000073DF3454350214F009FDF00006040 +> 6F0027002E82A6A400000000A3DFE9520000000000000000000000000100020000000000B20017004B0220672401073DF3454350210F00A1DFA204088A0000 +< 6F0023002E82A6A400000000000000000000000000000000000000000100020000000000B2001300CB000000073DF3454350214F00A1DF00006040 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-read-n7.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-read-n7.hex new file mode 100644 index 00000000..adc3f8cf --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-read-n7.hex @@ -0,0 +1,13 @@ +# slc500-read-n7.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: slc500 (host :44819) +# tag : protocol=ab_eip&gateway=10.100.0.35:44819&path=1,0&plc=slc500&name=N7:0 +# session: slc500-read-n7 (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : FULL session. Unconnected Execute PCCC (0x4B) to class 0x67 inst 1, PCCC 0x0F/0xA2 protected typed logical read, twice (Initialize + Read). N7:0 held 4321. +> 65000400000000000000000000000000000000000000000001000000 +< 65000400D0410F270000000000000000000000000000000001000000 +> 6F002700D0410F27000000009B87395A0000000000000000000000000100020000000000B20017004B0220672401073DF3454350210F009987A20207890000 +< 6F002100D0410F2700000000000000000000000000000000000000000100020000000000B2001100CB000000073DF3454350214F009987E110 +> 6F002700D0410F27000000009D87395A0000000000000000000000000100020000000000B20017004B0220672401073DF3454350210F009B87A20207890000 +< 6F002100D0410F2700000000000000000000000000000000000000000100020000000000B2001100CB000000073DF3454350214F009B87E110 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-write-n7.hex b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-write-n7.hex new file mode 100644 index 00000000..59c0d663 --- /dev/null +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/Captures/slc500-write-n7.hex @@ -0,0 +1,13 @@ +# slc500-write-n7.hex +# client : libplctag 1.5.2 (libplctag.NET wrapper; libplctag.NativeImport 1.0.41, native core 2.6.0) +# server : otopcua-ab-server:libplctag-release (ab_server from libplctag v2.7.0 + patches 0001/0002) +# profile: slc500 (host :44819) +# tag : protocol=ab_eip&gateway=10.100.0.35:44819&path=1,0&plc=slc500&name=N7:0 +# session: slc500-write-n7 (captured 2026-10-07, scripts/ab-capture/capture.sh) +# frames : FULL session. Initialize read (PCCC 0x0F/0xA2) then write (PCCC 0x0F/0xAA) of 4321. +> 65000400000000000000000000000000000000000000000001000000 +< 65000400E65307EA0000000000000000000000000000000001000000 +> 6F002700E65307EA00000000E7A3957B0000000000000000000000000100020000000000B20017004B0220672401073DF3454350210F00E5A3A20207890000 +< 6F002100E65307EA00000000000000000000000000000000000000000100020000000000B2001100CB000000073DF3454350214F00E5A30000 +> 6F002900E65307EA00000000E9A3957B0000000000000000000000000100020000000000B20019004B0220672401073DF3454350210F00E7A3AA0207890000E110 +< 6F001F00E65307EA00000000000000000000000000000000000000000100020000000000B2000F00CB000000073DF3454350214F00E7A3 diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.csproj b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.csproj index f3153044..7cf4a40c 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.csproj +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Tests.csproj @@ -19,6 +19,10 @@ + + + +