diff --git a/include/ariec61850/mms/connection_runtime.hpp b/include/ariec61850/mms/connection_runtime.hpp index 8cb2ddbc8..2223a94b3 100644 --- a/include/ariec61850/mms/connection_runtime.hpp +++ b/include/ariec61850/mms/connection_runtime.hpp @@ -3,6 +3,7 @@ #include "ariec61850/mms/static_dispatcher.hpp" +#include #include #include #include @@ -31,9 +32,26 @@ enum class MmsStaticConnectionStatus : std::uint8_t { }; struct MmsStaticConnectionPolicy final { + static constexpr std::size_t maximum_owner_bytes = 16U; + std::uint16_t cotp_source_reference{0x1001U}; std::uint8_t maximum_tpdu_size_code{0x0AU}; bool require_end_of_transmission{true}; + + // Optional portable association identity used by contextual MMS writes. + // The transport/server adapter assigns association_id and stable Owner. + // Leaving these zero/empty preserves legacy non-contextual behavior. + std::uint64_t association_id{}; + std::array owner{}; + std::size_t owner_size{}; + + [[nodiscard]] constexpr MmsStaticRequestAccessContext access_context() const noexcept { + return { + association_id, + owner_size > 0U && owner_size <= owner.size() + ? std::span{owner.data(), owner_size} + : std::span{}}; + } }; struct MmsStaticConnectionResult final { @@ -81,6 +99,10 @@ class MmsStaticConnectionRuntime final { return mms_presentation_context_id_; } + [[nodiscard]] constexpr MmsStaticRequestAccessContext access_context() const noexcept { + return policy_.access_context(); + } + private: const MmsStaticApplicationDispatcher& dispatcher_; MmsStaticConnectionPolicy policy_{}; diff --git a/include/ariec61850/mms/static_dispatcher.hpp b/include/ariec61850/mms/static_dispatcher.hpp index 87c65d580..c8bd088af 100644 --- a/include/ariec61850/mms/static_dispatcher.hpp +++ b/include/ariec61850/mms/static_dispatcher.hpp @@ -58,12 +58,14 @@ class MmsStaticApplicationDispatcher final { [[nodiscard]] MmsStaticDispatchResult dispatch( std::span mms_request, std::span response, - std::span workspace) const noexcept; + std::span workspace, + const MmsStaticRequestAccessContext& access = {}) const noexcept; [[nodiscard]] MmsStaticDispatchResult dispatch( const MmsConfirmedPduView& request, std::span response, - std::span workspace) const noexcept; + std::span workspace, + const MmsStaticRequestAccessContext& access = {}) const noexcept; [[nodiscard]] constexpr const MmsStaticDispatchPolicy& policy() const noexcept { return policy_; diff --git a/include/ariec61850/mms/static_object_table.hpp b/include/ariec61850/mms/static_object_table.hpp index f533cdabe..9852feb57 100644 --- a/include/ariec61850/mms/static_object_table.hpp +++ b/include/ariec61850/mms/static_object_table.hpp @@ -24,6 +24,23 @@ using MmsStaticWriteCallback = MmsStaticWriteResult (*)( void* context, std::span encoded_data) noexcept; +// Per-request identity propagated by a connection/runtime adapter. The core +// deliberately treats Owner as opaque binary identity and does not bind it to +// an IP address, socket, TLS implementation, or operating-system handle. +struct MmsStaticRequestAccessContext final { + std::uint64_t association_id{}; + std::span owner{}; + + [[nodiscard]] constexpr bool valid() const noexcept { + return association_id != 0U && !owner.empty(); + } +}; + +using MmsStaticContextualWriteCallback = MmsStaticWriteResult (*)( + void* context, + std::span encoded_data, + const MmsStaticRequestAccessContext& access) noexcept; + struct MmsStaticObjectEntry final { std::string_view domain; std::string_view item; @@ -34,8 +51,12 @@ struct MmsStaticObjectEntry final { MmsStaticWriteCallback write{}; void* write_context{}; + // Appended for source compatibility with existing aggregate initializers. + // A contextual callback takes precedence over the legacy write callback. + MmsStaticContextualWriteCallback contextual_write{}; + [[nodiscard]] constexpr bool writable() const noexcept { - return write != nullptr; + return write != nullptr || contextual_write != nullptr; } }; diff --git a/src/mms/connection_runtime.cpp b/src/mms/connection_runtime.cpp index 88e18abfe..d0246f11b 100644 --- a/src/mms/connection_runtime.cpp +++ b/src/mms/connection_runtime.cpp @@ -337,7 +337,7 @@ MmsStaticConnectionResult MmsStaticConnectionRuntime::process_tcp_window( } const auto application = dispatcher_.dispatch( - pdv.single_asn1_type, response, workspace); + pdv.single_asn1_type, response, workspace, policy_.access_context()); if (!application.success()) { if (application.status == MmsStaticDispatchStatus::response_buffer_too_small) { const auto fully_encoded = osi::PresentationSpanCodec::fully_encoded_data_size( diff --git a/src/mms/static_dispatcher.cpp b/src/mms/static_dispatcher.cpp index ebc8ace4d..c3d35f50b 100644 --- a/src/mms/static_dispatcher.cpp +++ b/src/mms/static_dispatcher.cpp @@ -320,7 +320,8 @@ namespace { const MmsStaticObjectTable& objects, const MmsStaticDispatchPolicy& policy, const MmsConfirmedPduView& confirmed, - const std::span response) noexcept { + const std::span response, + const MmsStaticRequestAccessContext& access) noexcept { MmsWriteRequestView request; if (!MmsServiceSpanCodec::try_decode_write_request(confirmed, request)) { return make_status(MmsStaticDispatchStatus::malformed_request, confirmed); @@ -352,7 +353,9 @@ namespace { false, policy.access_denied_failure_code}; continue; } - const auto applied = object->write(object->write_context, values[index]); + const auto applied = object->contextual_write != nullptr + ? object->contextual_write(object->write_context, values[index], access) + : object->write(object->write_context, values[index]); results[index] = MmsWriteAccessResultInput{ applied.success, applied.success ? 0U : applied.failure_code}; @@ -371,7 +374,8 @@ namespace { MmsStaticDispatchResult MmsStaticApplicationDispatcher::dispatch( const std::span mms_request, const std::span response, - const std::span workspace) const noexcept { + const std::span workspace, + const MmsStaticRequestAccessContext& access) const noexcept { MmsConfirmedPduView request; if (!MmsPduSpanCodec::try_decode_confirmed_request_view(mms_request, request)) { return MmsStaticDispatchResult{ @@ -381,13 +385,14 @@ MmsStaticDispatchResult MmsStaticApplicationDispatcher::dispatch( 0U, 0U}; } - return dispatch(request, response, workspace); + return dispatch(request, response, workspace, access); } MmsStaticDispatchResult MmsStaticApplicationDispatcher::dispatch( const MmsConfirmedPduView& request, const std::span response, - const std::span workspace) const noexcept { + const std::span workspace, + const MmsStaticRequestAccessContext& access) const noexcept { if (!objects_.valid() || !data_sets_.valid_against(objects_) || !policy_valid(policy_)) { return make_status(MmsStaticDispatchStatus::invalid_object_table, request); } @@ -405,7 +410,7 @@ MmsStaticDispatchResult MmsStaticApplicationDispatcher::dispatch( case MmsWireConfirmedService::read: return dispatch_read(objects_, policy_, request, response, workspace); case MmsWireConfirmedService::write: - return dispatch_write(objects_, policy_, request, response); + return dispatch_write(objects_, policy_, request, response, access); default: return make_status(MmsStaticDispatchStatus::unsupported_service, request); }