/// <summary> /// Saves the specified value to tag's archive candidate hash in the Redis database. /// </summary> /// <param name="value">The new archive candidate value.</param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// A task that will save the archive candidate value. /// </returns> private async Task SaveArchiveCandidateValueInternal(ArchiveCandidateValue value, CancellationToken cancellationToken) { if (_archiveCandidateValue?.Value == null || (value?.Value != null && value.Value.UtcSampleTime > _archiveCandidateValue.Value.UtcSampleTime)) { _archiveCandidateValue = value; await Task.WhenAny(_historian.Connection.GetDatabase().HashSetAsync(_archiveCandidateKey, GetHashEntriesForArchiveCandidateValue(value)), Task.Delay(-1, cancellationToken)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); } }
/// <summary> /// Creates a new <see cref="RedisTagDefinition"/> object. /// </summary> /// <param name="historian">The owning historian.</param> /// <param name="id">The tag ID.</param> /// <param name="settings">The tag settings.</param> /// <param name="metadata">The metadata for the tag.</param> /// <param name="initialTagValues">The initial tag values, used to prime the exception and compression filters for the tag.</param> /// <param name="changeHistory">The change history for the tag.</param> private RedisTagDefinition(RedisHistorian historian, string id, TagSettings settings, TagMetadata metadata, InitialTagValues initialTagValues, IEnumerable <TagChangeHistoryEntry> changeHistory) : base(historian, id, settings, metadata, CreateTagSecurity(), initialTagValues, changeHistory) { _historian = historian ?? throw new ArgumentNullException(nameof(historian)); _tagDefinitionKey = _historian.GetKeyForTagDefinition(Id); _snapshotKey = _historian.GetKeyForSnapshotData(Id); _archiveKey = _historian.GetKeyForRawData(Id); _archiveCandidateKey = _historian.GetKeyForArchiveCandidateData(Id); _archiveCandidateValue = new ArchiveCandidateValue(initialTagValues?.LastExceptionValue, initialTagValues?.CompressionAngleMinimum ?? Double.NaN, initialTagValues?.CompressionAngleMaximum ?? Double.NaN); Updated += TagUpdated; }
/// <summary> /// Converts an <see cref="ArchiveCandidateValue"/> into a set of <see cref="HashEntry"/> objects to write to Redis. /// </summary> /// <param name="value">The archive candidate tag value.</param> /// <returns> /// The equivalent Redis hash entries. /// </returns> private HashEntry[] GetHashEntriesForArchiveCandidateValue(ArchiveCandidateValue value) { if (value == null) { return(new HashEntry[0]); } var tagValueHashEntries = GetHashEntriesForTagValue(value.Value); return(tagValueHashEntries.Concat(new[] { new HashEntry("MI", value.CompressionAngleMinimum), new HashEntry("MA", value.CompressionAngleMaximum) }).ToArray()); }
/// <summary> /// Converts an Aika <see cref="ArchiveCandidateValue"/> into an Elasticsearch <see cref="TagValueDocument"/>. /// </summary> /// <param name="value">The Aika archive candidate value.</param> /// <param name="tag">The tag that the value is for.</param> /// <param name="documentId"> /// The optional document ID to assign to the <see cref="TagValueDocument"/>. Specify /// <see langword="null"/> for archive values, and the tag ID for snapshot and archive /// candidate values (so that the existing snapshot or archive candidate document for /// the tag will be replaced). /// </param> /// <returns> /// An equivalent <see cref="TagValueDocument"/>. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentNullException"><paramref name="tag"/> is <see langword="null"/>.</exception> public static TagValueDocument ToTagValueDocument(this ArchiveCandidateValue value, ElasticsearchTagDefinition tag, Guid?documentId) { if (value == null) { throw new ArgumentNullException(nameof(value)); } if (tag == null) { throw new ArgumentNullException(nameof(tag)); } var val = value?.Value.ToTagValueDocument(tag, documentId); val.Properties = new Dictionary <string, object>() { { "CompressionAngleMinimum", value.CompressionAngleMinimum }, { "CompressionAngleMaximum", value.CompressionAngleMaximum } }; return(val); }
/// <summary> /// Saves an updated <see cref="ArchiveCandidateValue"/> for a tag. /// </summary> /// <param name="tagId">The tag ID.</param> /// <param name="value">The archive candidate value.</param> internal void SaveArchiveCandidateValue(string tagId, ArchiveCandidateValue value) { _archiveCandidates[tagId] = value; }
/// <summary> /// Inserts tag values into the Elasticsearch archive. /// </summary> /// <param name="values">The values to write.</param> /// <param name="nextArchiveCandidate">The updated archive candidate value.</param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// A task that will return a <see cref="WriteTagValuesResult"/> for the operation. Due to /// the way that Elasticsearch writesare batched and delegated to a background class to be /// written in bulk, it is possible that write operations can fail after this method has /// returned. /// </returns> protected override Task <WriteTagValuesResult> InsertArchiveValues(IEnumerable <TagValue> values, ArchiveCandidateValue nextArchiveCandidate, CancellationToken cancellationToken) { return(_historian.InsertArchiveValues(this, values, nextArchiveCandidate, cancellationToken)); }
/// <summary> /// Writes archive values to Redis. /// </summary> /// <param name="values">The values to write.</param> /// <param name="nextArchiveCandidate">The next archive candiate value.</param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// A task that will return the write result. /// </returns> protected override async Task <WriteTagValuesResult> InsertArchiveValues(IEnumerable <TagValue> values, ArchiveCandidateValue nextArchiveCandidate, CancellationToken cancellationToken) { await SaveArchiveCandidateValueInternal(nextArchiveCandidate, cancellationToken).ConfigureAwait(false); await SaveArchiveValues(values, cancellationToken).ConfigureAwait(false); return(new WriteTagValuesResult(true, values?.Count() ?? 0, values?.FirstOrDefault()?.UtcSampleTime, values?.LastOrDefault()?.UtcSampleTime, null)); }
/// <summary> /// Inserts values into the historian archive for the tag. /// </summary> /// <param name="values">The values to archive.</param> /// <param name="nextArchiveCandidate"> /// The current archive candidate value for the tag (i.e. the next value that might /// potentially be written to the archive). /// </param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// The result of the write. /// </returns> protected override Task <WriteTagValuesResult> InsertArchiveValues(IEnumerable <TagValue> values, ArchiveCandidateValue nextArchiveCandidate, CancellationToken cancellationToken) { _historian.SaveArchiveCandidateValue(Id, nextArchiveCandidate); var result = !(values?.Any() ?? false) ? WriteTagValuesResult.CreateEmptyResult() : _historian.InsertArchiveValues(Id, values); return(Task.FromResult(result)); }
/// <summary> /// Enqueues the specified values for writing. /// </summary> /// <param name="tag">The tag that the values are being written for.</param> /// <param name="values">The values to write to the archive.</param> /// <param name="archiveCandidate">The current archive candidate value for the tag.</param> internal void WriteValues(ElasticsearchTagDefinition tag, IEnumerable <TagValue> values, ArchiveCandidateValue archiveCandidate) { _valuesLock.EnterReadLock(); try { foreach (var value in values ?? new TagValue[0]) { var op = new BulkIndexOperation <TagValueDocument>(value.ToTagValueDocument(tag, null)) { Index = IndexUtility.GetIndexNameForArchiveTagValue(_historian.ArchiveIndexNamePrefix, tag, value.UtcSampleTime, _historian.ArchiveIndexSuffixGenerator) }; _nextInsert.AddOperation(op); } if (archiveCandidate != null) { _archiveCandidateValues[tag.IdAsGuid] = archiveCandidate; } } finally { _valuesLock.ExitReadLock(); } }