@@ -96,21 +96,94 @@ def once(old: str, new: str, label: str) -> None:
9696''' ,
9797'enhanced select evidence' )
9898
99- # MmsInformationReportSpanCodec validates data_set_reference even when the
100- # DataSetName optional field is not selected. Supply a valid internal reference;
101- # it is not emitted on the wire because OptFlds keeps DataSetName disabled.
99+ # CommandTermination is an ordinary MMS InformationReport with an explicit
100+ # variable-access reference to CO$...$Oper. RCB report framing is valid MMS but
101+ # does not populate MmsInformationReport::variable_references, so the command
102+ # decoder cannot correlate it to a pending enhanced-security Oper.
102103once (
103- ''' report.report_id = "CommandTermination";
104- report.optional_fields = opt;
105- report.conf_revision = 1U;
104+ '''#include "ariec61850/mms/data_codec.hpp"
105+ #include "ariec61850/mms/services.hpp"
106+ ''' ,
107+ '''#include "ariec61850/mms/data_codec.hpp"
108+ #include "ariec61850/mms/reporting.hpp"
109+ #include "ariec61850/mms/services.hpp"
106110''' ,
107- ''' report.report_id = "CommandTermination";
111+ 'control report codec include' )
112+
113+ once (
114+ '''[[nodiscard]] bool encode_host_control_termination(
115+ const mms::MmsStaticConnectionRuntime& connection,
116+ HostControl& host,
117+ ConnectionBuffers& buffers,
118+ std::size_t& bytes_written) noexcept {
119+ bytes_written = 0U;
120+ if (connection.state() != mms::MmsStaticConnectionState::established ||
121+ connection.mms_presentation_context_id() == 0U || host.last_oper.empty()) return false;
122+ const std::array<std::uint8_t, 2U> opt{{0x04U, 0x00U}}; // dataRef
123+ const std::array<mms::MmsInformationReportReferenceInput, 1U> refs{{
124+ {host.manifest->domain, host.oper_item}}};
125+ const std::array<mms::MmsReadAccessResultInput, 1U> results{{
126+ {true, host.last_oper, 0U}}};
127+ mms::MmsInformationReportSnapshotInput report;
128+ report.report_id = "CommandTermination";
108129 report.optional_fields = opt;
109- report.data_set_reference = mms::MmsInformationReportReferenceInput{
110- host.manifest->domain, host.oper_item};
111130 report.conf_revision = 1U;
131+ report.member_references = refs;
132+ report.member_results = results;
133+ report.reason_for_inclusion = 0x40U;
134+ const auto raw = mms::MmsInformationReportSpanCodec::encode_snapshot_into(
135+ report, buffers.report_response);
136+ if (!raw.success()) return false;
137+ const auto p_data = ar::iec61850::osi::PresentationSpanCodec::encode_p_data_into(
138+ std::span<const std::uint8_t>{buffers.report_response.data(), raw.bytes_written},
139+ buffers.report_workspace,
140+ connection.mms_presentation_context_id(),
141+ true);
142+ ''' ,
143+ '''[[nodiscard]] bool encode_host_control_termination(
144+ const mms::MmsStaticConnectionRuntime& connection,
145+ HostControl& host,
146+ ConnectionBuffers& buffers,
147+ std::size_t& bytes_written) noexcept {
148+ bytes_written = 0U;
149+ if (connection.state() != mms::MmsStaticConnectionState::established ||
150+ connection.mms_presentation_context_id() == 0U || host.last_oper.empty()) return false;
151+ try {
152+ mms::MmsInformationReport report;
153+ report.variable_references.push_back(
154+ mms::MmsObjectName::domain_specific(host.manifest->domain, host.oper_item));
155+ report.items.push_back({
156+ 0U,
157+ mms::MmsDataCodec::decode(host.last_oper),
158+ std::nullopt});
159+ const auto raw = mms::MmsInformationReportCodec::encode_pdu(report);
160+ if (raw.empty() || raw.size() > buffers.report_response.size()) return false;
161+ std::copy(raw.begin(), raw.end(), buffers.report_response.begin());
162+ const auto p_data = ar::iec61850::osi::PresentationSpanCodec::encode_p_data_into(
163+ std::span<const std::uint8_t>{buffers.report_response.data(), raw.size()},
164+ buffers.report_workspace,
165+ connection.mms_presentation_context_id(),
166+ true);
167+ ''' ,
168+ 'command termination report shape' )
169+
170+ once (
171+ ''' bytes_written = tpkt.bytes_written;
172+ return true;
173+ }
174+
175+ [[nodiscard]] bool poll_host_control_terminations(
176+ ''' ,
177+ ''' bytes_written = tpkt.bytes_written;
178+ return true;
179+ } catch (...) {
180+ return false;
181+ }
182+ }
183+
184+ [[nodiscard]] bool poll_host_control_terminations(
112185''' ,
113- 'command termination internal reference ' )
186+ 'command termination exception boundary ' )
114187
115188p .write_text (s , encoding = 'utf-8' )
116- print ('P3 lifetime/exact-type/termination repair applied' )
189+ print ('P3 lifetime/exact-type/control- termination repair applied' )
0 commit comments