feat(historian): OPC UA HistoryUpdate in the node manager + HistoryUpdate authorization (T6)
- OtOpcUaNodeManager overrides HistoryUpdateData / HistoryDeleteRawModified / HistoryDeleteAtTime, block-bridging to the optional IHistorianDataUpdater under the shared HistoryRead batch limiter + deadline. Insert/Replace/Update map to HistoryUpdateMode; Remove, IsDeleteModified, events and structure data are BadHistoryOperationUnsupported. Per-item codes stitched back in request order (MinValue SourceTimestamp => BadInvalidTimestamp, untypeable value => BadTypeMismatch); success clears the base's pre-seeded Unsupported error. - HistoryWrite AccessLevel bit OR-ed in for historized nodes when an updater is wired (raw + UNS); it is dispatch-load-bearing in the SDK base. OtOpcUaSdkServer.SetHistorianDataUpdater; the hosted service assigns it from an optional HistorianUpdaterHolder before SetSink, clears it on stop. - AuthorizeHistoryUpdate: ACL on => scoped OpcUaOperation.HistoryUpdate decision (replaces the role check); ACL off => #563 key re-check + new OpcUaDataPlaneRoles.HistoryUpdate role, conferred by ApiKeyInterimRoleMapper only from a Cluster-scoped HistoryUpdate grant (WriteOperate does not). - AdminUI ACL editor offers NodePermissions.HistoryUpdate. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -128,6 +128,25 @@ public sealed class OtOpcUaSdkServer : StandardServer
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wire the optional HistoryUpdate backend (the <see cref="IHistorianDataUpdater"/> the node manager's
|
||||
/// HistoryUpdate overrides block-bridge to) onto the created <see cref="OtOpcUaNodeManager"/>. Mirrors
|
||||
/// <see cref="SetHistorianDataSource"/>; <c>null</c> means "this backend cannot modify history".
|
||||
/// <para>
|
||||
/// ⚠️ Call this BEFORE any node materialises: whether an updater is wired decides the
|
||||
/// <c>HistoryWrite</c> AccessLevel bit at materialise time, and the SDK base admits a node to
|
||||
/// HistoryUpdate only when that bit is set. Nodes materialised earlier are not retro-updated.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="updater">The update backend, or <c>null</c> for none.</param>
|
||||
/// <returns><c>true</c> when set on a live node manager; <c>false</c> when no node manager exists yet.</returns>
|
||||
public bool SetHistorianDataUpdater(IHistorianDataUpdater? updater)
|
||||
{
|
||||
if (_otOpcUaNodeManager is null) return false;
|
||||
_otOpcUaNodeManager.HistorianDataUpdater = updater;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wire the node's data-plane authorization inputs (#520 stage 4) onto the created
|
||||
/// <see cref="OtOpcUaNodeManager"/> — the cluster it serves, the current auth generation, and the
|
||||
|
||||
Reference in New Issue
Block a user