From 2b6287e3e3477378d3caa3d5c4ae1d9a51aab0dc Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 21:47:24 +0700 Subject: [PATCH 1/5] test(interop): expose untagged SV lab profile and independent RX first-packet check --- SUPPORT.md | 2 +- .../templates/arstack_4i4v_untagged_lab.scd | 70 +++++++++++++++++++ docs/INTEROP_FIRST_PACKET.md | 38 ++++++++++ tests/test_scl.cpp | 31 ++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 apps/arstack_studio/templates/arstack_4i4v_untagged_lab.scd create mode 100644 docs/INTEROP_FIRST_PACKET.md diff --git a/SUPPORT.md b/SUPPORT.md index 1851bd8b0..d202d1068 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -12,7 +12,7 @@ Useful details include the Studio release, Windows version, ESP32-P4 firmware bu **Can't find the board?** Check the USB data cable and serial device selection; use **Configuration → Firmware** to verify the ESP32-P4 and install matching firmware. -**Can't see SV in Wireshark?** Verify the isolated Ethernet topology, selected capture interface and whether a switch mirror/TAP is needed. +**SMV missing in ARSAS, DigSubAnalyzer or Wireshark?** Follow the [independent Ethernet receiver check](docs/INTEROP_FIRST_PACKET.md): verify SV RUNNING, capture on the actual NIC, and compare priority-tagged VLAN 0 with the optional untagged lab SCL. **Interested in IEC 61850 subscriber testing?** Share results in the [community interoperability guide](docs/COMMUNITY_TESTING.md). diff --git a/apps/arstack_studio/templates/arstack_4i4v_untagged_lab.scd b/apps/arstack_studio/templates/arstack_4i4v_untagged_lab.scd new file mode 100644 index 000000000..ad5186469 --- /dev/null +++ b/apps/arstack_studio/templates/arstack_4i4v_untagged_lab.scd @@ -0,0 +1,70 @@ + +
+ + + + +
+

01-0C-CD-04-00-01

+

4000

+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/INTEROP_FIRST_PACKET.md b/docs/INTEROP_FIRST_PACKET.md new file mode 100644 index 000000000..294a010b4 --- /dev/null +++ b/docs/INTEROP_FIRST_PACKET.md @@ -0,0 +1,38 @@ +# First SV packet — independent receiver check + +Use this when ARStack Studio reports SV publishing but ARSAS or DigSubAnalyzer shows no Sampled Values. The goal is to identify whether the frame is **missing from the receiving Ethernet interface** or **arriving but not being decoded**. + +## 1. Confirm SV is actually publishing + +Connect the ESP32-P4 to an **isolated laboratory Ethernet network**. Open ARStack Studio and confirm **SV RUNNING** with increasing transmit/frame counts. PTP RUNNING alone does not mean SV frames are being transmitted. + +## 2. Capture on the receiver's Ethernet adapter + +On the same Windows machine used for the analyzer: + +1. Select the **physical Ethernet adapter** connected to the ESP32-P4 laboratory network, rather than Wi-Fi or the Npcap loopback adapter. +2. In Wireshark, start a new capture **with no capture filter**. A capture filter is different from a display filter. +3. While Studio is publishing, apply the **display filter** `eth.type == 0x88ba`. +4. Record whether SV frames appear and whether the packet details contain `APPID`, `svID` and `smpCnt`. +5. Save **3–5 seconds** of traffic as `.pcapng` or `.pcap` and remove any confidential project information before sharing. + +The reference M3.H profile uses APPID `0x4000`, destination `01:0C:CD:04:00:01` and approximately **4,000 frames/s**. The stream ID depends on the profile deployed by Studio; do not assume a specific `svID` without examining the wire. + +## 3. Compare VLAN-tagged with untagged laboratory SV + +The included **reference SCL** requests Ethernet **802.1Q priority tagging**: VLAN ID `0`, PCP `4`. Some PC network or switch paths handle priority-tagged traffic differently. + +For diagnosis only, an alternative [untagged 4I+4V SCL](../apps/arstack_studio/templates/arstack_4i4v_untagged_lab.scd) keeps the same APPID, multicast MAC, data-set and sample rate **but omits VLAN tagging**. In Studio, stop publishing, load this SCL, select/deploy its SV stream and restart. Compare the receiving capture against the original configuration. + +**Do not change the VLAN scheme on an operating substation network.** This is an isolated-bench comparison to identify an Ethernet capture path difference; a change in receive behavior is evidence about the path, not proof of an arbitrary SV protocol fault. + +## 4. Interpret the result + +| What the receiver PC observes | Next engineering action | +| --- | --- | +| No `0x88BA` frames on the selected adapter | Check board link, Studio TX state, adapter choice, switch mirror/TAP and tagged VLAN forwarding. | +| SV appears in Wireshark but not in DigSubAnalyzer | Note raw frame and decoder counters and share the capture; inspect Npcap filter and raw BER path. | +| SV appears in Wireshark but ARSAS snapshot stays empty | Use ARSAS's **First received SV stream** option (when available), or match selected SCL APPID/MAC/`svID` to the actual packet. | +| SV decodes in both applications | Compare samples, `smpCnt` rollover and waveform plausibility, then continue to timing/interoperability checks. | + +Open [P0 receiver acceptance issue #324](https://github.com/masarray/arstack61850/issues/324) for incident tracking. A self-decoded PCAP from Studio cannot replace an **independent RX capture from the affected NIC**. diff --git a/tests/test_scl.cpp b/tests/test_scl.cpp index e9484016e..20948e78e 100644 --- a/tests/test_scl.cpp +++ b/tests/test_scl.cpp @@ -1204,6 +1204,36 @@ void parser_detects_duplicate_ieds_and_missing_dataset_references() { })); } +void arstack_untagged_lab_reference_compiles_without_vlan_tag() { + using namespace ar::iec61850::sampled_values; + using namespace ar::iec61850::scl; + + const auto path = std::filesystem::path{ARIEC61850_SOURCE_DIR} / + "apps" / "arstack_studio" / "templates" / + "arstack_4i4v_untagged_lab.scd"; + const auto document = SclParser{}.load(path); + CHECK(document.sampled_values_streams.size() == 1U); + const auto& stream = document.sampled_values_streams.front(); + CHECK(stream.address.app_id == std::optional{0x4000U}); + CHECK(!stream.address.vlan_id.has_value()); + CHECK(!stream.address.vlan_priority.has_value()); + CHECK(stream.entries.size() == 16U); + CHECK(stream.sample_rate == 4000U); + CHECK(stream.no_asdu == 1U); + + SvPublisherProfileCompileContext context; + context.profile_family = SvProfileFamily::legacy_9_2le; + context.sample_counter_modulus = static_cast(4000U); + const auto compiled = SvPublisherProfileCompiler::compile(stream, context); + CHECK(compiled.ok()); + CHECK(compiled.profile.has_value()); + CHECK(!compiled.profile->vlan_present); + CHECK(compiled.profile->app_id == 0x4000U); + CHECK(compiled.profile->payload_size_bytes == 64U); + CHECK(compiled.profile->timing.exact_frame_rate_hz() == + std::optional{4000U}); +} + void parser_detects_editions_from_root_metadata() { using namespace ar::iec61850::scl; @@ -1281,6 +1311,7 @@ int main() { {"Simulator semantic defaults and indexed RCBs", simulator_compiles_semantic_defaults_and_indexed_report_instances}, {"SCL SettingControl", parser_preserves_setting_control_and_invalid_state}, {"SCL conflicts and warnings", parser_detects_duplicate_ieds_and_missing_dataset_references}, + {"ARStack untagged SV lab profile", arstack_untagged_lab_reference_compiles_without_vlan_tag}, {"SCL edition detection", parser_detects_editions_from_root_metadata}, {"SCL prefixed namespace", parser_supports_prefixed_namespaces_and_predefined_entities}, {"SCL secure XML", secure_xml_reader_rejects_dtd_entities_and_malformed_documents}, From a25346850ab3e780bae32fd884dec8aae256208b Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 24 Oct 2026 21:55:09 +0700 Subject: [PATCH 2/5] test(scl): use explicit uint16 APPID literal for MSVC warnings-as-errors --- tests/test_scl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_scl.cpp b/tests/test_scl.cpp index 20948e78e..5028344c6 100644 --- a/tests/test_scl.cpp +++ b/tests/test_scl.cpp @@ -1214,7 +1214,7 @@ void arstack_untagged_lab_reference_compiles_without_vlan_tag() { const auto document = SclParser{}.load(path); CHECK(document.sampled_values_streams.size() == 1U); const auto& stream = document.sampled_values_streams.front(); - CHECK(stream.address.app_id == std::optional{0x4000U}); + CHECK(stream.address.app_id == std::optional{static_cast(0x4000U)}); CHECK(!stream.address.vlan_id.has_value()); CHECK(!stream.address.vlan_priority.has_value()); CHECK(stream.entries.size() == 16U); From bbbea729463ac2ffb0a0d15ac6ab4376eb8975ca Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sun, 25 Oct 2026 04:21:43 +0700 Subject: [PATCH 3/5] docs(p0): show tagged and untagged SV in independent receiver capture --- docs/INTEROP_FIRST_PACKET.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/INTEROP_FIRST_PACKET.md b/docs/INTEROP_FIRST_PACKET.md index 294a010b4..6eaf726a7 100644 --- a/docs/INTEROP_FIRST_PACKET.md +++ b/docs/INTEROP_FIRST_PACKET.md @@ -12,9 +12,9 @@ On the same Windows machine used for the analyzer: 1. Select the **physical Ethernet adapter** connected to the ESP32-P4 laboratory network, rather than Wi-Fi or the Npcap loopback adapter. 2. In Wireshark, start a new capture **with no capture filter**. A capture filter is different from a display filter. -3. While Studio is publishing, apply the **display filter** `eth.type == 0x88ba`. -4. Record whether SV frames appear and whether the packet details contain `APPID`, `svID` and `smpCnt`. -5. Save **3–5 seconds** of traffic as `.pcapng` or `.pcap` and remove any confidential project information before sharing. +3. While Studio is publishing, apply the **display filter** `eth.type == 0x88ba || vlan.etype == 0x88ba`. This covers both plain SV Ethernet frames and SV inside an 802.1Q priority/VLAN tag. The outer `eth.type` may be `0x8100` for tagged frames, so `eth.type == 0x88ba` **alone is not sufficient**. +4. Record whether SV frames appear and whether packet details show `APPID`, `svID`, `smpCnt`, destination MAC and, where present, VLAN ID/PCP. Check the actual packet bytes, not only the configured SCL; NIC VLAN offload/stripping can change what a host capture exposes. +5. Save **3–5 seconds** of traffic as `.pcapng` or `.pcap` with **no capture filter**, along with the selected NIC name, physical topology, Studio/firmware versions, analyzer versions and chosen stream filters. Remove confidential project information before sharing. The reference M3.H profile uses APPID `0x4000`, destination `01:0C:CD:04:00:01` and approximately **4,000 frames/s**. The stream ID depends on the profile deployed by Studio; do not assume a specific `svID` without examining the wire. @@ -30,7 +30,7 @@ For diagnosis only, an alternative [untagged 4I+4V SCL](../apps/arstack_studio/t | What the receiver PC observes | Next engineering action | | --- | --- | -| No `0x88BA` frames on the selected adapter | Check board link, Studio TX state, adapter choice, switch mirror/TAP and tagged VLAN forwarding. | +| No plain or VLAN-encapsulated SV frames on the selected adapter | Check board link, Studio TX state, adapter choice, switch mirror/TAP, NIC VLAN offload and tagged VLAN forwarding. First confirm the combined display filter was used. | | SV appears in Wireshark but not in DigSubAnalyzer | Note raw frame and decoder counters and share the capture; inspect Npcap filter and raw BER path. | | SV appears in Wireshark but ARSAS snapshot stays empty | Use ARSAS's **First received SV stream** option (when available), or match selected SCL APPID/MAC/`svID` to the actual packet. | | SV decodes in both applications | Compare samples, `smpCnt` rollover and waveform plausibility, then continue to timing/interoperability checks. | From 46ba4d0f74a18730636df76bf2d69b5799f608c4 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sun, 25 Oct 2026 04:23:55 +0700 Subject: [PATCH 4/5] test(p0): lock tagged/untagged SCL stream identity and payload parity --- tests/test_scl.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_scl.cpp b/tests/test_scl.cpp index 5028344c6..0f4ba98a0 100644 --- a/tests/test_scl.cpp +++ b/tests/test_scl.cpp @@ -1221,6 +1221,24 @@ void arstack_untagged_lab_reference_compiles_without_vlan_tag() { CHECK(stream.sample_rate == 4000U); CHECK(stream.no_asdu == 1U); + // A/B lab isolation must change only the outer Ethernet VLAN tag. + // Preserve the shipped reference stream's application identity and data layout. + const auto reference_path = std::filesystem::path{ARIEC61850_SOURCE_DIR} / + "apps" / "arstack_studio" / "templates" / + "arstack_4i4v_9-2le_reference.scd"; + const auto reference_document = SclParser{}.load(reference_path); + CHECK(reference_document.sampled_values_streams.size() == 1U); + const auto& reference = reference_document.sampled_values_streams.front(); + CHECK(reference.address.vlan_id == std::optional{0U}); + CHECK(reference.address.vlan_priority == std::optional{4U}); + CHECK(stream.address.destination_mac == reference.address.destination_mac); + CHECK(stream.address.app_id == reference.address.app_id); + CHECK(stream.sv_id == reference.sv_id); + CHECK(stream.configuration_revision == reference.configuration_revision); + CHECK(stream.sample_rate == reference.sample_rate); + CHECK(stream.no_asdu == reference.no_asdu); + CHECK(stream.entries.size() == reference.entries.size()); + SvPublisherProfileCompileContext context; context.profile_family = SvProfileFamily::legacy_9_2le; context.sample_counter_modulus = static_cast(4000U); From 83439a4a15f186b0e95b5ca139c336a271042bf1 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sun, 25 Oct 2026 04:30:23 +0700 Subject: [PATCH 5/5] test(p0): keep SCL VLAN parity assertions warning-free under MSVC /WX --- tests/test_scl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_scl.cpp b/tests/test_scl.cpp index 0f4ba98a0..333e663db 100644 --- a/tests/test_scl.cpp +++ b/tests/test_scl.cpp @@ -1229,8 +1229,8 @@ void arstack_untagged_lab_reference_compiles_without_vlan_tag() { const auto reference_document = SclParser{}.load(reference_path); CHECK(reference_document.sampled_values_streams.size() == 1U); const auto& reference = reference_document.sampled_values_streams.front(); - CHECK(reference.address.vlan_id == std::optional{0U}); - CHECK(reference.address.vlan_priority == std::optional{4U}); + CHECK(reference.address.vlan_id == std::optional{static_cast(0U)}); + CHECK(reference.address.vlan_priority == std::optional{static_cast(4U)}); CHECK(stream.address.destination_mac == reference.address.destination_mac); CHECK(stream.address.app_id == reference.address.app_id); CHECK(stream.sv_id == reference.sv_id);