Skip to content

Commit b1f8243

Browse files
committed
Rebase MMS server R1-R2 onto current consolidated adapter
1 parent a5eb9aa commit b1f8243

19 files changed

Lines changed: 4264 additions & 0 deletions
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: MMS Direct Control Server CI
2+
3+
on:
4+
push:
5+
branches: ["agent/mms-server-direct-normal-control"]
6+
pull_request:
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build:
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
include:
18+
- os: ubuntu-latest
19+
cc: gcc
20+
cxx: g++
21+
- os: ubuntu-latest
22+
cc: clang
23+
cxx: clang++
24+
- os: windows-latest
25+
cc: ""
26+
cxx: ""
27+
runs-on: ${{ matrix.os }}
28+
env:
29+
CC: ${{ matrix.cc }}
30+
CXX: ${{ matrix.cxx }}
31+
steps:
32+
- uses: actions/checkout@v4
33+
- name: Configure
34+
run: cmake -S tools/mms_direct_control_server -B build-mms-direct-control -DCMAKE_BUILD_TYPE=Release
35+
- name: Build
36+
run: cmake --build build-mms-direct-control --config Release --parallel
37+
- name: Unit and help contract
38+
run: ctest --test-dir build-mms-direct-control -C Release --output-on-failure

‎docs/MMS_SERVER_DIRECT_CONTROL.md‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# MMS Server Direct-Normal Control Trial
2+
3+
This slice advances the clean MMS server prototype from read-only browse/read to a bounded, explicitly writable IEC 61850 control target.
4+
5+
## Implemented server model
6+
7+
Domain: `ESP32S3IOLD0`
8+
9+
Logical Nodes:
10+
11+
- `LLN0`
12+
- `LPHD1`
13+
- `GGIO1`
14+
15+
Read-only digital inputs:
16+
17+
- `GGIO1.Ind1` .. `GGIO1.Ind8`
18+
19+
Direct-with-normal-security Boolean controls:
20+
21+
- `ESP32S3IOLD0/GGIO1.SPCSO1`
22+
- `ESP32S3IOLD0/GGIO1.SPCSO2`
23+
- ...
24+
- `ESP32S3IOLD0/GGIO1.SPCSO8`
25+
26+
Each control exposes:
27+
28+
- `ST$SPCSO<n>$stVal`
29+
- `CF$SPCSO<n>$ctlModel` = `1` (Direct-with-normal-security)
30+
- `CO$SPCSO<n>$Oper`
31+
32+
The live `Oper` TypeSpecification is the bounded SPC shape:
33+
34+
1. `ctlVal` Boolean
35+
2. `origin` structure (`orCat` Unsigned, `orIdent` OctetString)
36+
3. `ctlNum` Unsigned
37+
4. `T` UTC time
38+
5. `Test` Boolean
39+
6. `Check` 2-bit BitString
40+
41+
A static non-deletable DataSet is also exposed:
42+
43+
`ESP32S3IOLD0/LLN0.Outputs`
44+
45+
It contains the eight `SPCSO<n>.stVal` status leaves and is intended to become the first static reporting DataSet in the next server-side RCB tranche.
46+
47+
## Safety semantics
48+
49+
The `Oper` decoder is allocation-free and rejects malformed or unexpected structures.
50+
51+
- `ctlNum=0` is rejected.
52+
- origin category outside the bounded IEC range is rejected.
53+
- synchro/interlock Check requests are fail-closed by default because this prototype does not yet implement those plant checks.
54+
- `Test=true` is accepted but does not change the live output state.
55+
- an application/hardware apply callback can reject the command before the published status changes.
56+
- there is no command retry.
57+
58+
This tranche implements only Direct-with-normal-security. SBO and enhanced-security CommandTermination belong to later server slices.
59+
60+
## Windows build
61+
62+
```powershell
63+
powershell.exe -ExecutionPolicy Bypass -File .\scripts\build_mms_direct_control_server.ps1
64+
```
65+
66+
The script configures, builds, and runs the direct-control unit/help contract.
67+
68+
## Run
69+
70+
Use a non-privileged lab port first:
71+
72+
```powershell
73+
.\build-mms-direct-control\Release\ariec61850_mms_direct_control_server.exe `
74+
--bind 0.0.0.0 `
75+
--port 8102 `
76+
--di-mask 0x55 `
77+
--do-mask 0x00
78+
```
79+
80+
Expected startup marker:
81+
82+
```text
83+
MMS_DIRECT_CONTROL_SERVER_READY ... controls=8 ctlModel=1 dataset=ESP32S3IOLD0/LLN0.Outputs
84+
```
85+
86+
For a standards-port laboratory run, use TCP 102 only when the host privileges/firewall configuration allow it.
87+
88+
## IEDScout trial
89+
90+
Connect IEDScout as an MMS/IEC 61850 client to the host running this executable.
91+
92+
Browse `ESP32S3IOLD0/GGIO1` and verify that `SPCSO1..8` expose `ctlModel=1`, `Oper`, and `stVal`. Operate one Boolean output and then read its `stVal`.
93+
94+
After the client disconnects, the server prints a summary such as:
95+
96+
```text
97+
MMS_DIRECT_CONTROL_STATE doMask=1 acceptedOps=1 rejectedOps=0
98+
```
99+
100+
Retain terminal output and a PCAP/PCAPNG for interoperability evidence. This is simulator/server interoperability evidence, not IEC 61850 conformance certification.
101+
102+
## Current boundary / next server tranche
103+
104+
Still pending after this slice:
105+
106+
1. server-side URCB/BRCB objects and InformationReport transmission;
107+
2. static DataSet report binding and GI/event triggers;
108+
3. SBO normal and enhanced control plus CommandTermination/LastApplError;
109+
4. concurrent-client ownership/contention;
110+
5. server-side FileDirectory/FileOpen/FileRead/FileClose;
111+
6. GPIO/lwIP adapter on the target ESP board;
112+
7. runtime SCL/CID-driven model generation;
113+
8. TLS/security profiles and broader multi-vendor evidence.

‎docs/MMS_SERVER_URCB_REPORT.md‎

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# MMS Server Static URCB / InformationReport Trial
2+
3+
This tranche adds a runnable server-side unbuffered report path on top of the bounded MMS server core.
4+
5+
## Model
6+
7+
Domain: `ESP32S3IOLD0`
8+
9+
Static DataSet:
10+
11+
`ESP32S3IOLD0/LLN0.Outputs`
12+
13+
Members, in fixed order:
14+
15+
- `GGIO1$ST$SPCSO1$stVal`
16+
- `GGIO1$ST$SPCSO2$stVal`
17+
- `GGIO1$ST$SPCSO3$stVal`
18+
- `GGIO1$ST$SPCSO4$stVal`
19+
- `GGIO1$ST$SPCSO5$stVal`
20+
- `GGIO1$ST$SPCSO6$stVal`
21+
- `GGIO1$ST$SPCSO7$stVal`
22+
- `GGIO1$ST$SPCSO8$stVal`
23+
24+
URCB:
25+
26+
`ESP32S3IOLD0/LLN0$RP$Outputs01`
27+
28+
Default `RptID`: `ARSTACK-Outputs01`.
29+
30+
The object bank exposes `RptID`, `RptEna`, `Resv`, `DatSet`, `ConfRev`, `OptFlds`, `BufTm`, `TrgOps`, `IntgPd`, `GI`, and `SqNum` as MMS variables.
31+
32+
## First acceptance profile
33+
34+
The first live gate is deliberately deterministic and GI-driven:
35+
36+
1. associate;
37+
2. inspect the static DataSet and URCB;
38+
3. reserve the URCB if the client uses `Resv`;
39+
4. set `RptEna=true`;
40+
5. set `GI=true`;
41+
6. receive one MMS `InformationReport` carrying all eight ordered DataSet values;
42+
7. verify `SqNum` advances only after a complete report frame is encoded;
43+
8. disable/release and disconnect.
44+
45+
The initial `OptFlds` enable sequence number, reason, DataSet name, data references, and configuration revision. Report time is intentionally omitted in this first profile so timestamp semantics do not obscure the basic interoperability gate.
46+
47+
## Safety and ownership boundary
48+
49+
This executable currently serves one client at a time.
50+
51+
`RptEna` and `Resv` are explicitly reset to false at connection boundaries so one disconnected client cannot leave a live reservation/enable state for the next client.
52+
53+
No dynamic DataSet mutation is available on this server. `LLN0.Outputs` is static and non-deletable.
54+
55+
No automatic report-control takeover or retry is performed.
56+
57+
## Build
58+
59+
```powershell
60+
powershell.exe -ExecutionPolicy Bypass -File .\scripts\build_mms_direct_control_server.ps1
61+
```
62+
63+
The helper builds/tests both server trial executables.
64+
65+
## Run
66+
67+
```powershell
68+
.\build-mms-direct-control\Release\ariec61850_mms_urcb_server.exe `
69+
--bind 0.0.0.0 `
70+
--port 8102 `
71+
--mask 0x55
72+
```
73+
74+
Expected startup marker:
75+
76+
```text
77+
MMS_URCB_SERVER_READY ... dataset=ESP32S3IOLD0/LLN0.Outputs urcb=ESP32S3IOLD0/LLN0$RP$Outputs01 members=8 ...
78+
```
79+
80+
On each emitted report the server prints:
81+
82+
```text
83+
MMS_URCB_REPORT_SENT index=0 sqNum=<n> reason=8 bytes=<n>
84+
```
85+
86+
Reason `8` is General Interrogation.
87+
88+
For TCP 102, use a laboratory host configuration that permits binding the standard MMS port.
89+
90+
## IEDScout trial
91+
92+
Connect IEDScout to the server host and browse `ESP32S3IOLD0`.
93+
94+
Verify the `LLN0.Outputs` DataSet and `LLN0$RP$Outputs01` URCB are visible. Enable the report and issue GI. The expected evidence is one report with eight Boolean values matching the configured `--mask` bit order.
95+
96+
Retain console output and PCAP/PCAPNG. Until this live run is performed, this tranche is classified as software/offline-tested rather than live-proven server interoperability.
97+
98+
## Still pending
99+
100+
- event-on-change/data-change triggering;
101+
- multiple URCBs and concurrent-client ownership;
102+
- BRCB retention/replay/EntryID/PurgeBuf on the runnable server adapter;
103+
- Direct/SBO enhanced server control and CommandTermination;
104+
- server-side MMS file services;
105+
- GPIO/lwIP hardware adapter;
106+
- runtime SCL/CID model generation;
107+
- TLS/security profiles and broader multi-vendor evidence.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
#pragma once
3+
4+
#include "ariec61850/mms/static_object_table.hpp"
5+
#include "ariec61850/wire/encode_result.hpp"
6+
7+
#include <cstddef>
8+
#include <cstdint>
9+
#include <span>
10+
11+
namespace ar::iec61850::mms {
12+
13+
struct MmsStaticDirectBooleanOperate final {
14+
bool control_value{};
15+
std::uint8_t origin_category{};
16+
std::uint8_t control_number{};
17+
bool test{};
18+
bool synchro_check{};
19+
bool interlock_check{};
20+
};
21+
22+
struct MmsStaticDirectBooleanControlState final {
23+
std::uint8_t value{};
24+
std::uint8_t last_control_number{};
25+
bool last_test{};
26+
std::size_t accepted_operations{};
27+
std::size_t rejected_operations{};
28+
};
29+
30+
using MmsStaticBooleanApplyCallback = bool (*)(
31+
void* context,
32+
bool value) noexcept;
33+
34+
struct MmsStaticDirectBooleanControlPolicy final {
35+
bool allow_test{true};
36+
bool allow_synchro_check{};
37+
bool allow_interlock_check{};
38+
std::uint32_t malformed_failure_code{7U}; // type-inconsistent
39+
std::uint32_t invalid_value_failure_code{11U}; // object-value-invalid
40+
std::uint32_t backend_failure_code{10U}; // object-non-existent/backend
41+
};
42+
43+
struct MmsStaticDirectBooleanControlBinding final {
44+
MmsStaticDirectBooleanControlState* state{};
45+
MmsStaticBooleanApplyCallback apply{};
46+
void* apply_context{};
47+
MmsStaticDirectBooleanControlPolicy policy{};
48+
};
49+
50+
// Decode one IEC 61850 Direct-with-normal-security Oper value with the exact
51+
// ordered structure used by the live TypeSpecification exposed by this server:
52+
// ctlVal, origin(orCat,orIdent), ctlNum, T, Test, Check.
53+
// The parser is allocation-free and accepts only the bounded scalar shape.
54+
[[nodiscard]] bool try_decode_static_direct_boolean_operate(
55+
std::span<const std::uint8_t> encoded_data,
56+
MmsStaticDirectBooleanOperate& decoded) noexcept;
57+
58+
// Read callbacks suitable for MmsStaticObjectEntry.
59+
[[nodiscard]] wire::EncodeResult mms_static_direct_boolean_read_state(
60+
const void* context,
61+
std::span<std::uint8_t> destination) noexcept;
62+
63+
[[nodiscard]] wire::EncodeResult mms_static_direct_normal_read_ctl_model(
64+
const void* context,
65+
std::span<std::uint8_t> destination) noexcept;
66+
67+
[[nodiscard]] wire::EncodeResult mms_static_control_read_unavailable(
68+
const void* context,
69+
std::span<std::uint8_t> destination) noexcept;
70+
71+
// Write callback suitable for the CO$...$Oper MmsStaticObjectEntry.
72+
// Test=true is acknowledged without changing the live output state. Check bits
73+
// are denied by default until the application explicitly enables them.
74+
[[nodiscard]] MmsStaticWriteResult mms_static_direct_boolean_write_oper(
75+
void* context,
76+
std::span<const std::uint8_t> encoded_data) noexcept;
77+
78+
} // namespace ar::iec61850::mms

0 commit comments

Comments
 (0)