/// <summary> /// Decode with data type as string /// </summary> /// <param name="encoder"></param> /// <param name="value"></param> /// <param name="type"></param> /// <param name="context"></param> /// <returns></returns> public static Variant Decode(this IVariantEncoder encoder, JToken value, string type, ServiceMessageContext context) { return(encoder.Decode(value, string.IsNullOrEmpty(type) || context == null ? BuiltInType.Null : TypeInfo.GetBuiltInType(type.ToNodeId(context)), context)); }
/// <summary> /// Create new /// </summary> /// <param name="session"></param> /// <param name="codec"></param> /// <returns></returns> internal void Create(Session session, IVariantEncoder codec) { Item = new MonitoredItem { Handle = this, DisplayName = Template.DisplayName, AttributeId = ((uint?)Template.AttributeId) ?? Attributes.Value, IndexRange = Template.IndexRange, RelativePath = Template.RelativePath? .ToRelativePath(session.MessageContext)? .Format(session.NodeCache.TypeTree), MonitoringMode = Template.MonitoringMode.ToStackType() ?? Opc.Ua.MonitoringMode.Reporting, StartNodeId = Template.StartNodeId.ToNodeId(session.MessageContext), QueueSize = Template.QueueSize ?? 0, SamplingInterval = (int?)Template.SamplingInterval?.TotalMilliseconds ?? -1, DiscardOldest = !(Template.DiscardNew ?? false), Filter = Template.DataChangeFilter.ToStackModel() ?? codec.Decode(Template.EventFilter, true) ?? ((MonitoringFilter)Template.AggregateFilter .ToStackModel(session.MessageContext)) }; }
/// <summary> /// Create source stream importer /// </summary> /// <param name="factory"></param> /// <param name="codec"></param> /// <param name="logger"></param> public SourceStreamImporter(IItemContainerFactory factory, IVariantEncoder codec, ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _codec = codec ?? throw new ArgumentNullException(nameof(codec)); _factory = factory ?? throw new ArgumentNullException(nameof(factory)); }
/// <summary> /// Convert to results /// </summary> /// <param name="codec"></param> /// <param name="result"></param> /// <returns></returns> public static HistoricValueModel[] DecodeValues(this IVariantEncoder codec, VariantValue result) { var extensionObject = codec.DecodeExtensionObject(result); if (extensionObject?.Body is HistoryData data) { var results = data.DataValues.Select(d => new HistoricValueModel { ServerPicoseconds = d.ServerPicoseconds.ToNullable((ushort)0), SourcePicoseconds = d.SourcePicoseconds.ToNullable((ushort)0), ServerTimestamp = d.ServerTimestamp.ToNullable(DateTime.MinValue), SourceTimestamp = d.SourceTimestamp.ToNullable(DateTime.MinValue), StatusCode = d.StatusCode.ToNullable(StatusCodes.Good)?.CodeBits, Value = d.WrappedValue == Variant.Null ? null : codec.Encode(d.WrappedValue) }).ToArray(); if (extensionObject?.Body is HistoryModifiedData modified) { if (modified.ModificationInfos.Count != data.DataValues.Count) { throw new FormatException("Modification infos and data value count is not the same"); } for (var i = 0; i < modified.ModificationInfos.Count; i++) { results[i].ModificationInfo = new ModificationInfoModel { ModificationTime = modified.ModificationInfos[i].ModificationTime.ToNullable(DateTime.MinValue), UserName = modified.ModificationInfos[i].UserName }; } } return(results); } return(null); }
/// <summary> /// Convert read processed details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, ReadProcessedValuesDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.EndTime == null && details.StartTime == null) { throw new ArgumentException("Start time and end time cannot both be null", nameof(details)); } // Convert aggregate id with a temporary namespace table that will contain the aggregate namespace var context = new ServiceMessageContext(); var aggregate = details.AggregateTypeId?.ToNodeId(context); return(codec.Encode(new ExtensionObject(new ReadProcessedDetails { EndTime = details.EndTime ?? DateTime.MinValue, StartTime = details.StartTime ?? DateTime.MinValue, AggregateType = aggregate == null ? null : new NodeIdCollection(aggregate.YieldReturn()), ProcessingInterval = details.ProcessingInterval ?? 0, AggregateConfiguration = details.AggregateConfiguration == null ? null : new AggregateConfiguration { PercentDataBad = details.AggregateConfiguration.PercentDataBad ?? 0, PercentDataGood = details.AggregateConfiguration.PercentDataGood ?? 0, TreatUncertainAsBad = details.AggregateConfiguration.TreatUncertainAsBad ?? false, UseServerCapabilitiesDefaults = details.AggregateConfiguration.UseServerCapabilitiesDefaults ?? false, UseSlopedExtrapolation = details.AggregateConfiguration.UseSlopedExtrapolation ?? false } }), context)); // Reapplies the aggregate namespace uri during encoding using the context's table }
/// <summary> /// Encode extension object /// </summary> /// <param name="codec"></param> /// <param name="o"></param> /// <param name="context"></param> /// <returns></returns> internal static JToken Encode(this IVariantEncoder codec, ExtensionObject o, ServiceMessageContext context = null) { var variant = o == null ? Variant.Null : new Variant(o); return(codec.Encode(variant, out _, context)); }
/// <summary> /// Create service /// </summary> /// <param name="client"></param> /// <param name="codec"></param> /// <param name="logger"></param> public HistoricAccessAdapter(IHistoricAccessServices <T> client, IVariantEncoder codec, ILogger logger) { _codec = codec ?? throw new ArgumentNullException(nameof(codec)); _client = client ?? throw new ArgumentNullException(nameof(client)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); }
/// <summary> /// Encode extension object /// </summary> /// <param name="codec"></param> /// <param name="result"></param> /// <returns></returns> internal static ExtensionObject DecodeExtensionObject(this IVariantEncoder codec, JToken result) { if (result == null) { return(null); } var variant = codec.Decode(result, BuiltInType.ExtensionObject); return(variant.Value as ExtensionObject); }
/// <summary> /// Convert to service model /// </summary> /// <param name="model"></param> /// <param name="encoder"></param> /// <returns></returns> public static ContentFilterModel Encode(this IVariantEncoder encoder, ContentFilter model) { if (model == null) { return(null); } return(new ContentFilterModel { Elements = model.Elements? .Select(e => encoder.Encode(e)) .ToList() }); }
/// <summary> /// Convert delete raw modified details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, DeleteModifiedValuesDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } return(codec.Encode(new ExtensionObject(new DeleteRawModifiedDetails { NodeId = NodeId.Null, EndTime = details.EndTime ?? DateTime.MinValue, StartTime = details.StartTime ?? DateTime.MinValue, IsDeleteModified = true }))); }
/// <summary> /// Convert from service result to diagnostics info /// </summary> /// <param name="result"></param> /// <param name="config"></param> /// <param name="codec"></param> /// <param name="code"></param> /// <returns></returns> public static DiagnosticInfo Decode(this IVariantEncoder codec, ServiceResultModel result, DiagnosticsModel config, out StatusCode code) { if (result == null) { code = StatusCodes.Good; return(null); } code = new StatusCode(result.StatusCode ?? StatusCodes.Good); var results = codec.Decode(result, config); return(results?.LastOrDefault()?.DiagnosticsInfo); }
/// <summary> /// Convert to stack model /// </summary> /// <param name="model"></param> /// <param name="encoder"></param> /// <param name="onlySimpleAttributeOperands"></param> /// <returns></returns> public static ContentFilter Decode(this IVariantEncoder encoder, ContentFilterModel model, bool onlySimpleAttributeOperands = false) { if (model == null) { return(new ContentFilter()); } return(new ContentFilter { Elements = new ContentFilterElementCollection(model.Elements == null ? Enumerable.Empty <ContentFilterElement>() : model.Elements .Select(e => encoder.Decode(e, onlySimpleAttributeOperands))) }); }
/// <summary> /// Convert to stack model /// </summary> /// <param name="model"></param> /// <param name="encoder"></param> /// <returns></returns> public static EventFilterModel Encode(this IVariantEncoder encoder, EventFilter model) { if (model == null) { return(null); } return(new EventFilterModel { SelectClauses = model.SelectClauses? .Select(c => c.ToServiceModel(encoder.Context)) .ToList(), WhereClause = encoder.Encode(model.WhereClause) }); }
/// <summary> /// Convert operation results to json /// </summary> /// <param name="results"></param> /// <param name="config"></param> /// <param name="codec"></param> /// <returns></returns> private static VariantValue Write(this IVariantEncoder codec, List <OperationResultModel> results, DiagnosticsModel config) { var level = config?.Level ?? Core.Models.DiagnosticsLevel.Status; if (level == Core.Models.DiagnosticsLevel.None) { return(null); } using (var stream = new MemoryStream()) { var root = kDiagnosticsProperty; using (var encoder = new JsonEncoderEx(stream, codec.Context) { UseAdvancedEncoding = true, IgnoreDefaultValues = true }) { switch (level) { case Core.Models.DiagnosticsLevel.Diagnostics: case Core.Models.DiagnosticsLevel.Verbose: encoder.WriteEncodeableArray(root, results); break; case Core.Models.DiagnosticsLevel.Operations: var codes = results .GroupBy(d => d.StatusCode.CodeBits); root = null; foreach (var code in codes) { encoder.WriteStringArray(StatusCode.LookupSymbolicId(code.Key), code.Select(c => c.Operation).ToArray()); } break; case Core.Models.DiagnosticsLevel.Status: var statusCodes = results .Select(d => StatusCode.LookupSymbolicId(d.StatusCode.CodeBits)) .Where(s => !string.IsNullOrEmpty(s)) .Distinct(); if (!statusCodes.Any()) { return(null); } encoder.WriteStringArray(root, statusCodes.ToArray()); break; } } var o = codec.Serializer.Parse(stream.ToArray()); return(root != null ? o[root] : o); } }
/// <summary> /// Convert to results /// </summary> /// <param name="codec"></param> /// <param name="result"></param> /// <returns></returns> public static HistoricEventModel[] DecodeEvents(this IVariantEncoder codec, JToken result) { var extensionObject = codec.DecodeExtensionObject(result); if (extensionObject?.Body is HistoryEvent ev) { return(ev.Events.Select(d => new HistoricEventModel { EventFields = d.EventFields .Select(v => v == Variant.Null ? null : codec.Encode(v)) .ToList() }).ToArray()); } return(null); }
/// <summary> /// Convert delete at time details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, DeleteValuesAtTimesDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.ReqTimes == null || details.ReqTimes.Length == 0) { throw new ArgumentException(nameof(details.ReqTimes)); } return(codec.Encode(new ExtensionObject(new DeleteAtTimeDetails { NodeId = NodeId.Null, ReqTimes = new DateTimeCollection(details.ReqTimes) }))); }
/// <summary> /// Convert to stack model /// </summary> /// <param name="model"></param> /// <param name="encoder"></param> /// <param name="onlySimpleAttributeOperands"></param> /// <returns></returns> public static ContentFilterElement Decode(this IVariantEncoder encoder, ContentFilterElementModel model, bool onlySimpleAttributeOperands = false) { if (model == null) { return(null); } return(new ContentFilterElement { FilterOperands = new ExtensionObjectCollection(model?.FilterOperands == null ? Enumerable.Empty <ExtensionObject>() : model.FilterOperands .Select(e => new ExtensionObject( encoder.Decode(e, onlySimpleAttributeOperands)))), FilterOperator = model.FilterOperator.ToStackType() }); }
/// <summary> /// Convert delete event details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, DeleteEventsDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.EventIds == null || details.EventIds.Count == 0) { throw new ArgumentException(nameof(details.EventIds)); } return(codec.Encode(new ExtensionObject(new DeleteEventDetails { NodeId = NodeId.Null, EventIds = new ByteStringCollection(details.EventIds) }))); }
/// <summary> /// Convert read at time details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, ReadValuesAtTimesDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.ReqTimes == null || details.ReqTimes.Length == 0) { throw new ArgumentException(nameof(details.ReqTimes)); } return(codec.Encode(new ExtensionObject(new ReadAtTimeDetails { ReqTimes = new DateTimeCollection(details.ReqTimes), UseSimpleBounds = details.UseSimpleBounds ?? false }))); }
/// <summary> /// Convert from diagnostics info to service result /// </summary> /// <param name="diagnostics"></param> /// <param name="code"></param> /// <param name="operation"></param> /// <param name="config"></param> /// <param name="codec"></param> /// <returns></returns> public static ServiceResultModel Encode(this IVariantEncoder codec, DiagnosticInfo diagnostics, StatusCode code, string operation, DiagnosticsModel config) { if (code == StatusCodes.Good) { return(null); } return(codec.Encode(new List <OperationResultModel> { new OperationResultModel { DiagnosticsInfo = diagnostics, Operation = operation, StatusCode = code } }, config)); }
/// <summary> /// Convert to service model /// </summary> /// <param name="model"></param> /// <param name="encoder"></param> /// <returns></returns> public static ContentFilterElementModel Encode(this IVariantEncoder encoder, ContentFilterElement model) { if (model == null) { return(null); } return(new ContentFilterElementModel { FilterOperands = model.FilterOperands .Select(e => e.Body) .Cast <FilterOperand>() .Select(o => encoder.Encode(o)) .ToList(), FilterOperator = model.FilterOperator.ToServiceType() }); }
/// <summary> /// Convert delete raw modified details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static VariantValue Encode(this IVariantEncoder codec, DeleteValuesDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.EndTime == null && details.StartTime == null) { throw new ArgumentException("Start time and end time cannot both be null", nameof(details)); } return(codec.Encode(new ExtensionObject(new DeleteRawModifiedDetails { NodeId = NodeId.Null, EndTime = details.EndTime ?? DateTime.MinValue, StartTime = details.StartTime ?? DateTime.MinValue, IsDeleteModified = false }))); }
/// <summary> /// Convert to stack model /// </summary> /// <param name="model"></param> /// <param name="encoder"></param> /// <param name="noDefaultFilter"></param> /// <returns></returns> public static EventFilter Decode(this IVariantEncoder encoder, EventFilterModel model, bool noDefaultFilter = false) { if (model == null || !(model.SelectClauses?.Any() ?? false)) { return(noDefaultFilter ? null : GetDefaultEventFilter()); } return(new EventFilter { SelectClauses = new SimpleAttributeOperandCollection( model.SelectClauses == null ? Enumerable.Empty <SimpleAttributeOperand>() : model.SelectClauses.Select(c => c.ToStackModel(encoder.Context))), // // Per Part 4 only allow simple attribute operands in where clause // elements of event filters. // WhereClause = encoder.Decode(model.WhereClause, true) }); }
/// <summary> /// Convert to service model /// </summary> /// <param name="diagnostics"></param> /// <param name="config"></param> /// <param name="codec"></param> /// <returns></returns> public static ServiceResultModel Encode(this IVariantEncoder codec, List <OperationResultModel> diagnostics, DiagnosticsModel config) { if ((diagnostics?.Count ?? 0) == 0) { return(null); // All well } var result = diagnostics.LastOrDefault(d => !d.TraceOnly); var statusCode = result?.StatusCode; return(new ServiceResultModel { // The last operation result is the one that caused the service to fail. StatusCode = statusCode?.Code, ErrorMessage = result?.DiagnosticsInfo?.AdditionalInfo ?? (statusCode == null ? null : StatusCode.LookupSymbolicId(statusCode.Value.CodeBits)), Diagnostics = codec.Write(diagnostics, config) }); }
/// <summary> /// Convert to service model /// </summary> /// <param name="model"></param> /// <param name="encoder"></param> /// <returns></returns> public static FilterOperandModel Encode(this IVariantEncoder encoder, FilterOperand model) { if (model == null) { return(null); } switch (model) { case ElementOperand elem: return(new FilterOperandModel { Index = elem.Index }); case LiteralOperand lit: return(new FilterOperandModel { Value = encoder.Encode(lit.Value, out _) }); case AttributeOperand attr: return(new FilterOperandModel { NodeId = attr.NodeId.AsString(encoder.Context), AttributeId = (NodeAttribute)attr.AttributeId, BrowsePath = attr.BrowsePath.AsString(encoder.Context), IndexRange = attr.IndexRange, Alias = attr.Alias }); case SimpleAttributeOperand sattr: return(new FilterOperandModel { NodeId = sattr.TypeDefinitionId.AsString(encoder.Context), AttributeId = (NodeAttribute)sattr.AttributeId, BrowsePath = sattr.BrowsePath? .Select(p => p.AsString(encoder.Context)) .ToArray(), IndexRange = sattr.IndexRange }); default: throw new NotSupportedException("Operand not supported"); } }
/// <summary> /// Convert read processed details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, ReadProcessedValuesDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.EndTime == null && details.StartTime == null) { throw new ArgumentException("Start time and end time cannot both be null", nameof(details)); } var aggregate = details.AggregateTypeId?.ToNodeId(codec.Context); return(codec.Encode(new ExtensionObject(new ReadProcessedDetails { EndTime = details.EndTime ?? DateTime.MinValue, StartTime = details.StartTime ?? DateTime.MinValue, AggregateType = aggregate == null ? null : new NodeIdCollection(aggregate.YieldReturn()), ProcessingInterval = details.ProcessingInterval ?? 0, AggregateConfiguration = details.AggregateConfiguration.ToStackModel() }))); // Reapplies the aggregate namespace uri during encoding using the context's table }
/// <summary> /// Convert read event details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, ReadEventsDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.EndTime == null && details.StartTime == null) { throw new ArgumentException("Start time and end time cannot both be null", nameof(details)); } if ((details.StartTime == null || details.EndTime == null) && ((details.NumEvents ?? 0) == 0)) { throw new ArgumentException("Value bound must be set", nameof(details.NumEvents)); } return(codec.Encode(new ExtensionObject(new ReadEventDetails { EndTime = details.EndTime ?? DateTime.MinValue, StartTime = details.StartTime ?? DateTime.MinValue, Filter = details.Filter?.ToObject <EventFilter>() ?? new EventFilter(), NumValuesPerNode = details.NumEvents ?? 0 }))); }
/// <summary> /// Convert read modified details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, ReadModifiedValuesDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.EndTime == null && details.StartTime == null) { throw new ArgumentException("Start time and end time cannot both be null", nameof(details)); } if ((details.StartTime == null || details.EndTime == null) && ((details.NumValues ?? 0) == 0)) { throw new ArgumentException("Value bound must be set", nameof(details.NumValues)); } return(codec.Encode(new ExtensionObject(new ReadRawModifiedDetails { EndTime = details.EndTime ?? DateTime.MinValue, StartTime = details.StartTime ?? DateTime.MinValue, IsReadModified = true, NumValuesPerNode = details.NumValues ?? 0 }))); }
/// <summary> /// Convert update event details /// </summary> /// <param name="codec"></param> /// <param name="details"></param> /// <returns></returns> public static JToken Encode(this IVariantEncoder codec, InsertEventsDetailsModel details) { if (details == null) { throw new ArgumentNullException(nameof(details)); } if (details.Events == null || details.Events.Count == 0) { throw new ArgumentException(nameof(details.Events)); } return(codec.Encode(new ExtensionObject(new UpdateEventDetails { NodeId = NodeId.Null, PerformInsertReplace = PerformUpdateType.Insert, Filter = codec.Decode(details.Filter), EventData = new HistoryEventFieldListCollection(details.Events .Select(d => new HistoryEventFieldList { EventFields = new VariantCollection(d.EventFields .Select(f => new Variant(new EncodeableJToken(f)))) })) }))); }
/// <summary> /// Convert to service model /// </summary> /// <param name="statusCode"></param> /// <param name="diagnosticsInfo"></param> /// <param name="config"></param> /// <param name="codec"></param> /// <returns></returns> public static ServiceResultModel Encode(this IVariantEncoder codec, StatusCode?statusCode, DiagnosticInfo diagnosticsInfo = null, DiagnosticsModel config = null) { if ((statusCode?.Code ?? StatusCodes.Good) == StatusCodes.Good) { return(null); // All well } return(new ServiceResultModel { // The last operation result is the one that caused the service to fail. StatusCode = statusCode?.Code, ErrorMessage = diagnosticsInfo?.AdditionalInfo ?? (statusCode == null ? null : StatusCode.LookupSymbolicId(statusCode.Value.CodeBits)), Diagnostics = config == null ? null : codec.Write( new List <OperationResultModel> { new OperationResultModel { DiagnosticsInfo = diagnosticsInfo, StatusCode = statusCode.Value } }, config) }); }