|
|
|
@@ -1522,6 +1522,10 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
|
|
|
|
var parent = ResolveParentFolder(equipmentNodeId, realm);
|
|
|
|
|
|
|
|
|
|
AlarmConditionState alarm = CreateAlarmConditionOfType(alarmType, parent);
|
|
|
|
|
// SDK 2.0 creates optional confirmation nodes only when requested explicitly.
|
|
|
|
|
// Both are part of the application's existing Part 9 method/state contract.
|
|
|
|
|
alarm.ConfirmedState = new TwoStateVariableState(alarm);
|
|
|
|
|
alarm.Confirm = new AddCommentMethodState(alarm);
|
|
|
|
|
alarm.SymbolicName = displayName;
|
|
|
|
|
// HasComponent so the parent folder "owns" the condition (matches the T13 notes' pattern).
|
|
|
|
|
alarm.ReferenceTypeId = ReferenceTypeIds.HasComponent;
|
|
|
|
@@ -5582,14 +5586,14 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
|
|
|
|
// same HistoryRead authorization as a raw read (above); a backend with no trail ⇒ unsupported.
|
|
|
|
|
work.Add(() => ServeModifiedPagedAsync(
|
|
|
|
|
handle, session, nodesToRead, results, errors,
|
|
|
|
|
details.StartTime.ToDateTime(), details.EndTime.ToDateTime(), details.NumValuesPerNode,
|
|
|
|
|
SdkValueBoundary.ToClrTimestamp(details.StartTime), SdkValueBoundary.ToClrTimestamp(details.EndTime), details.NumValuesPerNode,
|
|
|
|
|
ct));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
work.Add(() => ServeRawPagedAsync(
|
|
|
|
|
handle, session, nodesToRead, results, errors,
|
|
|
|
|
details.StartTime.ToDateTime(), details.EndTime.ToDateTime(), details.NumValuesPerNode,
|
|
|
|
|
SdkValueBoundary.ToClrTimestamp(details.StartTime), SdkValueBoundary.ToClrTimestamp(details.EndTime), details.NumValuesPerNode,
|
|
|
|
|
ct, details.ReturnBounds && HonorReturnBounds));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -5649,8 +5653,8 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
|
|
|
|
var agg = aggregate.Value;
|
|
|
|
|
work.Add(() => ServeNodeAsync(handle, results, errors, (source, tagname, token) => source.ReadProcessedAsync(
|
|
|
|
|
tagname,
|
|
|
|
|
details.StartTime.ToDateTime(),
|
|
|
|
|
details.EndTime.ToDateTime(),
|
|
|
|
|
SdkValueBoundary.ToClrTimestamp(details.StartTime),
|
|
|
|
|
SdkValueBoundary.ToClrTimestamp(details.EndTime),
|
|
|
|
|
interval,
|
|
|
|
|
agg,
|
|
|
|
|
token), ct));
|
|
|
|
@@ -5827,8 +5831,8 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
|
|
|
|
var source = _historianDataSource;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var startUtc = details.StartTime.ToDateTime();
|
|
|
|
|
var endUtc = details.EndTime.ToDateTime();
|
|
|
|
|
var startUtc = SdkValueBoundary.ToClrTimestamp(details.StartTime);
|
|
|
|
|
var endUtc = SdkValueBoundary.ToClrTimestamp(details.EndTime);
|
|
|
|
|
var numValuesPerNode = details.NumValuesPerNode;
|
|
|
|
|
var boundarySkip = 0;
|
|
|
|
|
var walkHint = default(ReverseWalkHint); // a walked-back read's width + proven-empty stretch
|
|
|
|
@@ -6167,8 +6171,8 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
|
|
|
|
// Deleting the modified-value trail itself (Part 11 IsDeleteModified) is not supported: the trail is the
|
|
|
|
|
// audit record of every change and is aged out only by retention.
|
|
|
|
|
if (details.IsDeleteModified) return null;
|
|
|
|
|
var start = details.StartTime.ToDateTime();
|
|
|
|
|
var end = details.EndTime.ToDateTime();
|
|
|
|
|
var start = SdkValueBoundary.ToClrTimestamp(details.StartTime);
|
|
|
|
|
var end = SdkValueBoundary.ToClrTimestamp(details.EndTime);
|
|
|
|
|
// Part 11 requires both bounds of a raw delete; DateTime.MinValue is the wire's "unspecified". Refuse
|
|
|
|
|
// it here rather than let a backend read MinValue as "from the beginning of time".
|
|
|
|
|
if (start == DateTime.MinValue || end == DateTime.MinValue)
|
|
|
|
@@ -6531,7 +6535,7 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
|
|
|
|
if (string.IsNullOrWhiteSpace(typeName)) return ObjectTypeIds.AlarmConditionType;
|
|
|
|
|
return s_eventTypesByName.GetOrAdd(typeName, name =>
|
|
|
|
|
{
|
|
|
|
|
var field = typeof(ObjectTypeIds).GetField(name.EndsWith("Type", StringComparison.Ordinal) ? name : name + "Type",
|
|
|
|
|
var field = typeof(ObjectTypeIds).GetProperty(name.EndsWith("Type", StringComparison.Ordinal) ? name : name + "Type",
|
|
|
|
|
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
|
|
|
|
return field?.GetValue(null) is NodeId type && Server.TypeTree.IsTypeOf(type, ObjectTypeIds.BaseEventType)
|
|
|
|
|
? type
|
|
|
|
@@ -6838,7 +6842,7 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2
|
|
|
|
|
/// synthesised time-based:
|
|
|
|
|
/// <list type="bullet">
|
|
|
|
|
/// <item><b>Fresh read</b> (no inbound continuation point): read the window from
|
|
|
|
|
/// <c>details.StartTime.ToDateTime()</c> to <paramref name="endUtc"/> capped at
|
|
|
|
|
/// <c>SdkValueBoundary.ToClrTimestamp(details.StartTime)</c> to <paramref name="endUtc"/> capped at
|
|
|
|
|
/// <paramref name="numValuesPerNode"/> <b>+ 1</b> — one lookahead row. If the lookahead row came
|
|
|
|
|
/// back, return the first N, store a resume cursor and emit a continuation point; otherwise the page is
|
|
|
|
|
/// the last and carries none (so a total that is an exact multiple of N ends on a full page, not on an
|
|
|
|
|