/// <summary> /// Gets the instantaneous snapshot value for the tag. /// </summary> /// <param name="identity">The identity of the caller.</param> /// <returns> /// The snapshot tag value. /// </returns> /// <exception cref="ObjectDisposedException">The historian has been disposed.</exception> public TagValue ReadSnapshotValue(ClaimsPrincipal identity) { _historian.ThrowIfDisposed(); if (!this.CanRead(identity)) { return(TagValue.CreateUnauthorizedTagValue(DateTime.MinValue)); } return(_snapshotValue); }
/// <summary> /// Updates the snapshot value for the tag. You do not need to call this method from your /// implementation unless you have disabled writing of new tag values from Aika (e.g. if /// you are integrating with an existing historian and do not want to allow writes via Aika). /// </summary> /// <param name="value">The updated value.</param> /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception> protected void UpdateSnapshotValue(TagValue value) { _historian.ThrowIfDisposed(); _snapshotValue = value ?? throw new ArgumentNullException(nameof(value)); DataFilter.ValueReceived(value); _historian.TaskRunner.RunBackgroundTask(ct => SaveSnapshotValue(value, ct)); foreach (var subscriber in _snapshotSubscriptions.Keys) { try { subscriber.OnValueChange(value); } catch (Exception e) { _logger?.LogError("An error occurred while notifying a snapshot subscriber of a change.", e); } } }
/// <summary> /// Attempts to validate an incoming tag value. /// </summary> /// <param name="tag">The tag that the value is for.</param> /// <param name="incoming">The incoming tag value.</param> /// <param name="stateSet"> /// The state set for the tag. Can be <see langword="null"/> when the tag's data type is not /// <see cref="TagDataType.State"/>. /// </param> /// <param name="validatedValue">The validated tag value.</param> /// <returns> /// <see langword="true"/> if the value could be validated, or <see langword="false"/> otherwise. /// When the result is <see langword="false"/>, <paramref name="validatedValue"/> will be /// <see langword="null"/>. /// </returns> internal static bool TryValidateIncomingTagValue(this TagDefinition tag, TagValue incoming, StateSet stateSet, out TagValue validatedValue) { if (tag == null) { throw new ArgumentNullException(nameof(tag)); } if (incoming == null) { throw new ArgumentNullException(nameof(incoming)); } bool result; switch (tag.DataType) { case TagDataType.FloatingPoint: result = TryValidateDoubleValue(tag, incoming, out validatedValue); break; case TagDataType.Integer: result = TryValidateInt32Value(tag, incoming, out validatedValue); break; case TagDataType.Text: result = TryValidateStringValue(tag, incoming, out validatedValue); break; case TagDataType.State: result = TryValidateStateValue(tag, incoming, stateSet, out validatedValue); break; default: validatedValue = null; result = false; break; } return(result); }
/// <summary> /// Sends a value change to the subscriber. /// </summary> /// <param name="value">The new value.</param> internal void OnValueChange(TagValue value) { _onValueChange?.Invoke(value); }
/// <summary> /// When implemented in a derived type, saves a new snapshot value to the back-end historian. /// </summary> /// <param name="value">The new snapshot value for the tag.</param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// A task that will save the new snapshot value. /// </returns> protected abstract Task SaveSnapshotValue(TagValue value, CancellationToken cancellationToken);
/// <summary> /// Creates a new <see cref="TagDefinition"/> object. /// </summary> /// <param name="historian">The <see cref="IHistorian"/> instance that the tag belongs to.</param> /// <param name="id">The tag ID. If <see langword="null"/>, a new tag ID will ge generated automatically.</param> /// <param name="settings">The tag settings.</param> /// <param name="metadata">The tag metadata.</param> /// <param name="security">The tag security settings.</param> /// <param name="initialTagValues">The initial values to configure the tag's exception and compression filters with.</param> /// <param name="changeHistory">The change history for the tag.</param> /// <exception cref="ArgumentNullException"><paramref name="historian"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentNullException"><paramref name="settings"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentNullException"><paramref name="metadata"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentNullException"><paramref name="security"/> is <see langword="null"/>.</exception> /// <exception cref="ValidationException"><paramref name="settings"/> is not valid.</exception> protected TagDefinition(HistorianBase historian, string id, TagSettings settings, TagMetadata metadata, TagSecurity security, InitialTagValues initialTagValues, IEnumerable <TagChangeHistoryEntry> changeHistory) { _historian = historian ?? throw new ArgumentNullException(nameof(historian)); _historian.ThrowIfDisposed(); _logger = _historian.LoggerFactory?.CreateLogger <TagDefinition>(); if (settings == null) { throw new ArgumentNullException(nameof(settings)); } Validator.ValidateObject(settings, new ValidationContext(settings), true); Id = id ?? CreateTagId(); Name = settings.Name; Description = settings.Description; Units = settings.Units; DataType = settings.DataType; StateSet = settings.StateSet; Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); Security = security ?? throw new ArgumentNullException(nameof(security)); var exceptionFilterSettings = settings.ExceptionFilterSettings?.ToTagValueFilterSettings() ?? new TagValueFilterSettings(false, TagValueFilterDeviationType.Absolute, 0, TimeSpan.FromDays(1)); var compressionFilterSettings = settings.CompressionFilterSettings?.ToTagValueFilterSettings() ?? new TagValueFilterSettings(false, TagValueFilterDeviationType.Absolute, 0, TimeSpan.FromDays(1)); if (changeHistory != null) { _changeHistory.AddRange(changeHistory); } _snapshotValue = initialTagValues?.SnapshotValue; DataFilter = new DataFilter(Name, new ExceptionFilterState(exceptionFilterSettings, _snapshotValue), new CompressionFilterState(compressionFilterSettings, initialTagValues?.LastArchivedValue, initialTagValues?.LastExceptionValue, initialTagValues?.CompressionAngleMinimum ?? Double.NaN, initialTagValues?.CompressionAngleMaximum ?? Double.NaN), _historian.LoggerFactory); DataFilter.Emit += (values, nextArchiveCandidate) => { if (values.Length > 0 && _logger.IsEnabled(LogLevel.Trace)) { _logger.LogTrace($"[{Name}] Archiving {values.Count()} values emitted by the compression filter."); } _pendingArchiveWrites.Enqueue(new PendingArchiveWrite() { ArchiveValues = values, NextArchiveCandidate = nextArchiveCandidate }); _historian.TaskRunner.RunBackgroundTask(async ct => { if (Interlocked.CompareExchange(ref _archiveLock, 1, 0) != 0) { return; } try { while (!ct.IsCancellationRequested && _pendingArchiveWrites.TryDequeue(out var item)) { await InsertArchiveValuesInternal(ClaimsPrincipal.Current, item.ArchiveValues, item.NextArchiveCandidate, false, ct).ConfigureAwait(false); } } catch (OperationCanceledException) { // App is shutting down... } catch (Exception e) { _logger?.LogError($"[{Name}] An error occurred while archiving values emitted by the compression filter.", e); } finally { _archiveLock = 0; } }); }; }
/// <summary> /// Creates a new <see cref="ArchiveCandidateValue"/> object. /// </summary> /// <param name="value">The tag value.</param> /// <param name="compressionAngleMinimum"> /// The minimum compression angle value that was calculated when the <paramref name="value"/> /// was received by the tag's compression filter. /// </param> /// <param name="compressionAngleMaximum"> /// The minimum compression angle value that was calculated when the <paramref name="value"/> /// was received by the tag's compression filter. /// </param> public ArchiveCandidateValue(TagValue value, double compressionAngleMinimum, double compressionAngleMaximum) { Value = value; CompressionAngleMinimum = compressionAngleMinimum; CompressionAngleMaximum = compressionAngleMaximum; }
/// <summary> /// Attempts to validate an incoming integer tag value. /// </summary> /// <param name="tag">The tag for the value.</param> /// <param name="originalValue">The incoming value.</param> /// <param name="validatedValue">The validated value.</param> /// <returns> /// A flag that indicates if the value was validated. /// </returns> private static bool TryValidateInt32Value(TagDefinition tag, TagValue originalValue, out TagValue validatedValue) { var intValue = (int)originalValue.NumericValue; validatedValue = new TagValue(originalValue.UtcSampleTime, intValue, Convert.ToString(intValue, CultureInfo.InvariantCulture), originalValue.Quality, tag.Units); return(true); }
/// <summary> /// Attempts to validate an incoming floating-point tag value. /// </summary> /// <param name="tag">The tag for the value.</param> /// <param name="originalValue">The incoming value.</param> /// <param name="stateSet">The state set for the <paramref name="tag"/>.</param> /// <param name="validatedValue">The validated value.</param> /// <returns> /// A flag that indicates if the value was validated. If <paramref name="stateSet"/> /// is <see langword="null"/>, or if it does not match the name specified in the /// <see cref="TagDefinition.StateSet"/> property of the <paramref name="tag"/>, the /// result will be <see langword="false"/>. /// </returns> private static bool TryValidateStateValue(TagDefinition tag, TagValue originalValue, StateSet stateSet, out TagValue validatedValue) { if (stateSet == null || !stateSet.Name.Equals(tag.StateSet, StringComparison.OrdinalIgnoreCase)) { validatedValue = null; return(false); } StateSetItem state = null; if (!String.IsNullOrWhiteSpace(originalValue.TextValue)) { state = stateSet[originalValue.TextValue]; } if (state == null) { var valAsInt = (int)originalValue.NumericValue; state = stateSet.States.FirstOrDefault(x => x.Value == valAsInt); } if (state == null) { validatedValue = null; return(false); } validatedValue = new TagValue(originalValue.UtcSampleTime, state.Value, state.Name, originalValue.Quality, null); return(true); }
/// <summary> /// Attempts to validate an incoming string tag value. /// </summary> /// <param name="tag">The tag for the value.</param> /// <param name="originalValue">The incoming value.</param> /// <param name="validatedValue">The validated value.</param> /// <returns> /// A flag that indicates if the value was validated. /// </returns> private static bool TryValidateStringValue(TagDefinition tag, TagValue originalValue, out TagValue validatedValue) { validatedValue = new TagValue(originalValue.UtcSampleTime, Double.NaN, originalValue.TextValue, originalValue.Quality, null); return(true); }