Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 25 additions & 41 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.20)

project(ARIEC61850Cpp
VERSION 0.2.0
VERSION 0.3.0
DESCRIPTION "Incremental C++20 port of the ARIEC61850 protocol stack"
LANGUAGES CXX)

Expand All @@ -13,6 +13,8 @@ add_library(ariec61850_core
src/ethernet/ethernet.cpp
src/capture/pcap.cpp
src/goose/retransmission_schedule.cpp
src/goose/pdu_codec.cpp
src/goose/frame_codec.cpp
src/mms/utc_time.cpp
src/mms/data_value.cpp
src/mms/data_codec.cpp)
Expand All @@ -25,55 +27,37 @@ target_include_directories(ariec61850_core
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

if(MSVC)
target_compile_options(ariec61850_core PRIVATE /W4 /permissive-)
if(ARIEC61850_WARNINGS_AS_ERRORS)
target_compile_options(ariec61850_core PRIVATE /WX)
endif()
else()
target_compile_options(ariec61850_core PRIVATE -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion)
if(ARIEC61850_WARNINGS_AS_ERRORS)
target_compile_options(ariec61850_core PRIVATE -Werror)
endif()
endif()

if(ARIEC61850_BUILD_TESTS)
include(CTest)
enable_testing()

add_executable(ariec61850_core_tests tests/test_main.cpp)
target_link_libraries(ariec61850_core_tests PRIVATE ARIEC61850::core)
target_compile_features(ariec61850_core_tests PRIVATE cxx_std_20)

function(ariec61850_apply_warnings target)
if(MSVC)
target_compile_options(ariec61850_core_tests PRIVATE /W4 /permissive-)
target_compile_options(${target} PRIVATE /W4 /permissive-)
if(ARIEC61850_WARNINGS_AS_ERRORS)
target_compile_options(ariec61850_core_tests PRIVATE /WX)
target_compile_options(${target} PRIVATE /WX)
endif()
else()
target_compile_options(ariec61850_core_tests PRIVATE -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion)
target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion)
if(ARIEC61850_WARNINGS_AS_ERRORS)
target_compile_options(ariec61850_core_tests PRIVATE -Werror)
target_compile_options(${target} PRIVATE -Werror)
endif()
endif()
endfunction()

add_test(NAME ariec61850_core_tests COMMAND ariec61850_core_tests)
ariec61850_apply_warnings(ariec61850_core)

add_executable(ariec61850_mms_tests tests/test_mms.cpp)
target_link_libraries(ariec61850_mms_tests PRIVATE ARIEC61850::core)
target_compile_features(ariec61850_mms_tests PRIVATE cxx_std_20)
if(ARIEC61850_BUILD_TESTS)
include(CTest)
enable_testing()

if(MSVC)
target_compile_options(ariec61850_mms_tests PRIVATE /W4 /permissive-)
if(ARIEC61850_WARNINGS_AS_ERRORS)
target_compile_options(ariec61850_mms_tests PRIVATE /WX)
endif()
else()
target_compile_options(ariec61850_mms_tests PRIVATE -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion)
if(ARIEC61850_WARNINGS_AS_ERRORS)
target_compile_options(ariec61850_mms_tests PRIVATE -Werror)
endif()
endif()
add_executable(ariec61850_core_tests tests/test_main.cpp)
target_link_libraries(ariec61850_core_tests PRIVATE ARIEC61850::core)
target_compile_features(ariec61850_core_tests PRIVATE cxx_std_20)
ariec61850_apply_warnings(ariec61850_core_tests)
add_test(NAME ariec61850_core_tests COMMAND ariec61850_core_tests)

add_test(NAME ariec61850_mms_tests COMMAND ariec61850_mms_tests)
foreach(test_name IN ITEMS mms goose)
add_executable(ariec61850_${test_name}_tests tests/test_${test_name}.cpp)
target_link_libraries(ariec61850_${test_name}_tests PRIVATE ARIEC61850::core)
target_compile_features(ariec61850_${test_name}_tests PRIVATE cxx_std_20)
ariec61850_apply_warnings(ariec61850_${test_name}_tests)
add_test(NAME ariec61850_${test_name}_tests COMMAND ariec61850_${test_name}_tests)
endforeach()
endif()
101 changes: 101 additions & 0 deletions MIGRATION_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# ARIEC61850 C++ Migration Checklist

Status legend: `[x]` implemented and regression-tested, `[~]` partially implemented,
`[ ]` not started. The C# repository remains the behavioral oracle until laboratory
interoperability is complete.

## Phase 0 — portable deterministic foundation

- [x] C++20 static library and CMake build.
- [x] Strict compiler warnings promoted to errors.
- [x] GitHub Actions matrix: GCC, Clang, and Windows MSVC.
- [x] BER identifier, length, TLV, integer, boolean, float, string, and UTC-time primitives.
- [x] MAC address, VLAN, and Ethernet frame codec.
- [x] IEC 61850 process-bus header and APDU framing.
- [x] Classic PCAP Ethernet reader/writer.
- [x] GOOSE retransmission schedule.

**Phase 0 status: complete.**

## Phase 1 — deterministic process-bus codecs

- [x] IEC 61850 8-byte UTC time with quality byte.
- [x] Recursive MMS Data value model.
- [x] MMS AllData BER encoder/decoder.
- [x] GOOSE PDU model.
- [x] GOOSE PDU BER encoder/decoder.
- [x] GOOSE `numDatSetEntries` consistency validation.
- [x] GOOSE Ethernet/VLAN/process-bus frame encoder/decoder.
- [x] GOOSE C#-compatible byte-for-byte golden vectors.
- [x] GOOSE malformed/truncated-input regression tests.
- [ ] GOOSE subscriber TTL, `stNum`/`sqNum` supervision, and duplicate detection.
- [ ] GOOSE publisher session and retransmission state machine.
- [ ] Sampled Values ASDU model.
- [ ] Sampled Values APDU and Ethernet frame codec.
- [ ] Sampled Values sequence supervision and stream diagnostics.
- [ ] Captured PCAP equivalence tests from real or simulated IED traffic.
- [ ] Decoder fuzzing corpus for BER, GOOSE, and SV.

**Phase 1 status: GOOSE wire codec complete; runtime publisher/subscriber behavior and
Sampled Values remain.**

## Phase 2 — engineering file formats

- [ ] SCL object model.
- [ ] SCL parser and validation diagnostics.
- [ ] SCL workspace, profile extraction, and export.
- [ ] COMTRADE CFG/DAT reader.
- [ ] COMTRADE channel scaling and mapping.
- [ ] Expected-versus-observed engineering profiles.

## Phase 3 — MMS transport and association

- [ ] TPKT framing.
- [ ] COTP connection and data TPDU.
- [ ] Presentation layer.
- [ ] ACSE association and release.
- [ ] MMS confirmed/unconfirmed PDU envelopes.
- [ ] Invoke-ID routing with one receive pump.
- [ ] Name lists and variable-access attributes.
- [ ] MMS read/write services.

## Phase 4 — reporting, control, and file service

- [ ] DataSet and RCB inventory.
- [ ] Buffered and unbuffered report parsing.
- [ ] Report subscription, GI, and persistent monitoring.
- [ ] Direct and SBO control state machines.
- [ ] Enhanced-security CommandTermination and LastApplError.
- [ ] Cancel, timeout, disconnect, and cleanup behavior.
- [ ] MMS file directory and fault-record transfer.

## Phase 5 — simulator and applications

- [ ] Deterministic IED simulation runtime.
- [ ] Read-only MMS server baseline.
- [ ] CLI parity.
- [ ] Windows Npcap transport abstraction.
- [ ] Native desktop UI architecture.
- [ ] Engineering Workbench UI.
- [ ] IED Simulator UI.
- [ ] Discovery and SV Publisher UI.

## Current acceptance evidence

- [x] GCC C++20 build with warnings as errors.
- [x] Clang C++20 build with warnings as errors.
- [x] Windows MSVC build for Phase 1A.
- [x] Windows MSVC build and CTest for Phase 1B.
- [x] MMS C#-derived golden vector.
- [x] GOOSE PDU C#-derived golden vector.
- [x] GOOSE complete Ethernet frame golden vector with VLAN and process-bus header.
- [x] Malformed BER and GOOSE count-mismatch rejection.
- [ ] C# and C++ executable oracle comparison in one CI job.
- [ ] Sanitizer jobs and fuzzing.
- [ ] Isolated-lab IED interoperability.

## Safety gate

Active GOOSE publishing, MMS write/control, and raw-Ethernet transmission remain outside
the enabled runtime surface. They should only be enabled after sequence, timeout,
cancellation, disconnect cleanup, PCAP equivalence, and isolated-lab tests pass.
67 changes: 30 additions & 37 deletions PORT_STATUS.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,42 @@
# C++ Port Status

## Repository audit
## Current milestone: Phase 1B

- Original implementation: C# / .NET 8.
- C# source files: 384.
- Approximate C# source size: 66,024 lines across libraries, applications, and tests.
- Core protocol library: 227 files / approximately 39,062 lines.
- Existing C# tests: 90 files / approximately 12,003 lines.
- Desktop UI: WPF/XAML and therefore requires a UI rewrite rather than direct translation.
The C++20 port now includes the deterministic foundation, MMS Data values, IEC 61850 UTC
time, and the complete GOOSE wire codec from BER PDU through Ethernet/VLAN/process-bus
framing.

## Conversion delivered through Phase 1A

The port now covers the deterministic Phase 0 foundation plus the C# MMS data-value and UTC-time modules:

| Original area | C++ target | Status |
| Original C# area | C++ target | Status |
|---|---|---|
| `Asn1/Ber*` | `include/ariec61850/asn1`, `src/asn1` | Ported |
| `Ethernet/*` | `include/ariec61850/ethernet`, `src/ethernet` | Ported |
| `Capture/Pcap*` | `include/ariec61850/capture`, `src/capture` | Ported |
| `Goose/GooseRetransmissionSchedule` | `include/ariec61850/goose` | Ported |
| IEC 61850 UTC time and MMS Data values | `include/ariec61850/mms`, `src/mms` | Ported |
| GOOSE PDU | Phase 1B | Pending |
| Sampled Values codec and publisher core | Phase 1 | Pending |
| ASN.1 BER primitives | `asn1` | Ported and tested |
| Ethernet, VLAN, process-bus header | `ethernet` | Ported and tested |
| Classic PCAP | `capture` | Ported and tested |
| GOOSE retransmission schedule | `goose/retransmission_schedule` | Ported and tested |
| IEC 61850 UTC time | `mms/utc_time` | Ported and tested |
| MMS Data / AllData | `mms/data_value`, `mms/data_codec` | Ported and tested |
| GOOSE PDU | `goose/pdu`, `goose/pdu_codec` | Ported and tested |
| GOOSE Ethernet frame | `goose/frame`, `goose/frame_codec` | Ported and tested |
| GOOSE runtime publisher/subscriber | Phase 1C | Pending |
| Sampled Values | Phase 1D | Pending |
| SCL and COMTRADE | Phase 2 | Pending |
| TPKT/COTP/ACSE/MMS client | Phase 3 | Pending |
| Reporting, control, file service | Phase 4 | Pending |
| Simulation, CLI parity, native desktop UI | Phase 5 | Pending |
| TPKT/COTP/ACSE/MMS association | Phase 3 | Pending |
| Reporting, control, and file service | Phase 4 | Pending |
| Simulator, CLI, and native UI | Phase 5 | Pending |

## Validation performed
## Phase 1B validation

- GNU C++ 14.2, C++20, warnings as errors: passed.
- Clang 17, C++20, warnings as errors: passed.
- CTest regression executable: 12/12 checks passed under both compilers.
- C#-derived MMS golden vector: byte-for-byte match.
- GitHub Actions workflow added for GCC, Clang, and Windows MSVC.
- No external C++ runtime dependency is required through Phase 1A.
- C++20 GCC 14.2, warnings as errors: passed.
- C++20 Clang 17, warnings as errors: passed.
- Three CTest executables: core, MMS, and GOOSE.
- GOOSE PDU golden vector matches the C# encoder field order and BER representation.
- Complete VLAN-tagged GOOSE Ethernet frame has a byte-for-byte golden vector.
- PDU and frame round trips pass.
- Dataset entry-count mismatch, invalid UTC time, wrong outer tag, and wrong EtherType are rejected.
- No external C++ runtime dependency is required.

The sandbox did not contain the .NET SDK, so the original C# solution could not be rebuilt
inside this environment. The ported regression vectors were derived directly from the
repository source and its existing C# tests. Before merging into the main branch, run the
existing `dotnet test` suite on a machine with .NET 8 and preserve its output as the C#
baseline oracle.
See `MIGRATION_CHECKLIST.md` for the complete roadmap and current checkboxes.

## Safety boundary

This phase contains no active IED control and no raw-Ethernet publishing session. Active
paths should only be enabled after cross-language equivalence, cancellation, timeout,
association-loss cleanup, and isolated-lab interoperability have been demonstrated.
This milestone provides offline codecs only. It does not start a raw-Ethernet publisher,
subscribe to a network interface, perform MMS writes, or execute IED control.
61 changes: 25 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,39 @@
# ARIEC61850 C++ Port

This directory is the incremental C++20 migration of the existing ARIEC61850 C# stack.
The C# implementation remains the behavioral reference until every migrated module has
cross-language golden-vector coverage.

## Implemented

- CMake-based C++20 static library.
- BER TLV reader/writer and primitive encoders.
- MAC address, VLAN, Ethernet frame, and IEC 61850 process-bus header codecs.
- Classic PCAP Ethernet reader/writer.
- GOOSE retransmission schedule.
- Standalone regression tests derived from the C# test suite.
- IEC 61850 8-byte UTC time value with quality byte.
- Recursive MMS Data value model and BER codec for Array, Structure, Boolean, BitString, Integer, Unsigned, FloatingPoint, OctetString, VisibleString, BinaryTime, MmsString, UTC time, and unknown context-specific tags.
- GitHub Actions matrix for GCC, Clang, and Windows MSVC.
Incremental C++20 migration of the ARIEC61850 C# stack. The C# implementation remains
the behavioral oracle while each module gains byte-for-byte vectors and malformed-input
tests.

## Build
## Implemented through Phase 1B

### Windows: Visual Studio 2022
- BER primitives, Ethernet/VLAN/process-bus framing, PCAP, and GOOSE retransmission timing.
- IEC 61850 UTC time and recursive MMS Data / AllData codecs.
- GOOSE PDU encoder/decoder for `gocbRef`, `timeAllowedToLive`, `datSet`, `goID`, `t`,
`stNum`, `sqNum`, `test`, `confRev`, `ndsCom`, `numDatSetEntries`, and `allData`.
- Complete GOOSE Ethernet frame encoder/decoder with optional VLAN.
- GCC, Clang, and MSVC GitHub Actions coverage.
- C#-compatible MMS and GOOSE golden vectors.

```powershell
cd arstack61850
cmake -S . -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure
```
See [`MIGRATION_CHECKLIST.md`](MIGRATION_CHECKLIST.md) for detailed progress.

### Windows/Linux: Ninja
## Build

```bash
cd arstack61850
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
ctest --test-dir build --output-on-failure
```

## Migration rule
For Visual Studio multi-configuration builds:

Do not delete the C# module immediately after porting it. For each module:
```powershell
cmake -S . -B build -A x64
cmake --build build --config Release --parallel
ctest --test-dir build -C Release --output-on-failure
```

1. Capture deterministic C# input/output vectors.
2. Implement the same public behavior in C++.
3. Run byte-for-byte codec comparisons and semantic state-machine tests.
4. Validate with laboratory PCAP/SCL evidence.
5. Only then switch the application boundary to C++.
## Migration rule

Active control and publishing paths must remain disabled by default until the C++ path
has equivalent guardrails, cancellation, cleanup, and evidence handling.
A module is complete only after C++ tests, C#-versus-C++ vectors, malformed-input coverage,
and relevant PCAP or laboratory evidence. Active publishing and control remain disabled
until their state-machine safety gates pass.
23 changes: 23 additions & 0 deletions include/ariec61850/goose/frame.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "ariec61850/ethernet/ethernet.hpp"
#include "ariec61850/goose/pdu.hpp"

#include <cstdint>
#include <optional>

namespace ar::iec61850::goose {

struct GooseFrame final {
ethernet::MacAddress destination;
ethernet::MacAddress source;
std::optional<ethernet::VlanTag> vlan;
std::uint16_t app_id{};
std::uint16_t reserved1{};
std::uint16_t reserved2{};
GoosePdu pdu;
};

} // namespace ar::iec61850::goose
20 changes: 20 additions & 0 deletions include/ariec61850/goose/frame_codec.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "ariec61850/goose/frame.hpp"

#include <cstdint>
#include <span>
#include <vector>

namespace ar::iec61850::goose {

class GooseFrameCodec final {
public:
[[nodiscard]] static std::vector<std::uint8_t> encode(const GooseFrame& frame);
[[nodiscard]] static bool try_decode(
std::span<const std::uint8_t> frame_bytes, GooseFrame& frame) noexcept;
};

} // namespace ar::iec61850::goose
Loading
Loading