/// <summary> /// Enqueues the specified snapshot value for writing to Redis. /// </summary> /// <param name="tag">The tag that the value belongs to.</param> /// <param name="value">The value.</param> private static void EnqueueSnapshotValueSave(RedisTagDefinition tag, TagValue value) { // Enqueue the value. tag._pendingSnapshotWrites.Enqueue(value); // If a write task is already in progress, we'll just exit now. if (Interlocked.CompareExchange(ref tag._snapshotUpdateLock, 1, 0) != 0) { return; } tag._historian.RunBackgroundTask(async ct => { try { do { while (tag._pendingSnapshotWrites.TryDequeue(out var val)) { await tag.SaveSnapshotValueInternal(val, ct).ConfigureAwait(false); } } while (!tag._pendingSnapshotWrites.IsEmpty); } finally { tag._snapshotUpdateLock = 0; } }); }
/// <summary> /// Creates a new tag. /// </summary> /// <param name="identity">The identity of the caller.</param> /// <param name="tag">The tag definition.</param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// The new tag definition. /// </returns> protected override async Task <TagDefinition> CreateTag(ClaimsPrincipal identity, TagSettings tag, CancellationToken cancellationToken) { var tagDefinition = await RedisTagDefinition.Create(this, tag, identity, cancellationToken).ConfigureAwait(false); _tags[tagDefinition.Id] = tagDefinition; return(tagDefinition); }
/// <summary> /// Creates a new <see cref="RedisTagDefinition"/>. /// </summary> /// <param name="historian">The historian for the tag.</param> /// <param name="settings">The tag settings.</param> /// <param name="creator">The tag's creator.</param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// A task that will create a new <see cref="RedisTagDefinition"/> and save it to the /// historian's Redis server. /// </returns> internal static async Task <RedisTagDefinition> Create(RedisHistorian historian, TagSettings settings, ClaimsPrincipal creator, CancellationToken cancellationToken) { var now = DateTime.UtcNow; var result = new RedisTagDefinition(historian, null, settings, new TagMetadata(DateTime.UtcNow, creator?.Identity.Name), null, new[] { TagChangeHistoryEntry.Created(creator) }); var key = historian.GetKeyForTagIdsList(); await Task.WhenAny(Task.WhenAll(result.Save(cancellationToken), historian.Connection.GetDatabase().ListRightPushAsync(key, result.Id)), Task.Delay(-1, cancellationToken)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); return(result); }
/// <summary> /// Initializes the <see cref="RedisHistorian"/>. /// </summary> /// <param name="cancellationToken">A cancellation token that can be used to cancel initialization.</param> /// <returns> /// A task that will initialize the historian. /// </returns> protected override async Task Init(CancellationToken cancellationToken) { void onConnected(object sender, ConnectionFailedEventArgs args) { _logger?.LogInformation($"Redis connection established: {args.EndPoint} (Connection Type = {args.ConnectionType})"); Properties[Resources.Properties_Connected] = Connection.IsConnected; }; void onConnectionFailed(object sender, ConnectionFailedEventArgs args) { _logger?.LogError($"Redis connection failed: {args.EndPoint} (Connection Type = {args.ConnectionType}, Failure Type = {args.FailureType}).", args.Exception); Properties[Resources.Properties_Connected] = Connection.IsConnected; }; try { _isInitializing = true; var configurationOptions = ConfigurationOptions.Parse(_redisSettings); configurationOptions.ClientName = "Aika"; Connection = await ConnectionMultiplexer.ConnectAsync(configurationOptions).ConfigureAwait(false); Connection.ConnectionRestored += onConnected; Connection.ConnectionFailed += onConnectionFailed; Properties[Resources.Properties_Connected] = Connection.IsConnected; // Load state sets first so that they are already loaded when we start loading tags. await RedisStateSet.LoadAll(this, stateSet => _stateSets[stateSet.Name] = stateSet, cancellationToken).ConfigureAwait(false); await RedisTagDefinition.LoadAll(this, tag => _tags[tag.Id] = tag, cancellationToken).ConfigureAwait(false); } catch { if (Connection != null) { Connection.ConnectionRestored -= onConnected; Connection.ConnectionFailed -= onConnectionFailed; Connection.Dispose(); } throw; } finally { _isInitializing = false; } }
/// <summary> /// Loads a tag definition from the Redis database. /// </summary> /// <param name="historian">The Redis historian to load the tag from.</param> /// <param name="tagId">The ID of the tag to load.</param> /// <param name="cancellationToken">The cancellation token for the request.</param> /// <returns> /// A task that will return the loaded tag definition. /// </returns> internal static async Task <RedisTagDefinition> Load(RedisHistorian historian, string tagId, CancellationToken cancellationToken) { var values = await historian.Connection.GetDatabase().HashGetAllAsync(historian.GetKeyForTagDefinition(tagId)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); string name = null; string description = null; string units = null; TagDataType dataType = default(TagDataType); string stateSet = null; bool exceptionFilterEnabled = false; TagValueFilterDeviationType exceptionFilterLimitType = default(TagValueFilterDeviationType); double exceptionFilterLimit = 0; TimeSpan exceptionFilterWindowSize = default(TimeSpan); bool compressionFilterEnabled = false; TagValueFilterDeviationType compressionFilterLimitType = default(TagValueFilterDeviationType); double compressionFilterLimit = 0; TimeSpan compressionFilterWindowSize = default(TimeSpan); DateTime createdAt = DateTime.MinValue; string creator = null; DateTime modifiedAt = DateTime.MinValue; string modifiedBy = null; foreach (var item in values) { switch (item.Name.ToString()) { case "NAME": name = item.Value; break; case "DESC": description = item.Value; break; case "UNITS": units = item.Value; break; case "TYPE": dataType = (TagDataType)((int)item.Value); break; case "SSET": stateSet = item.Value; break; case "EXC_ENABLED": exceptionFilterEnabled = Convert.ToBoolean((int)item.Value); break; case "EXC_LIMIT_TYPE": exceptionFilterLimitType = (TagValueFilterDeviationType)((int)item.Value); break; case "EXC_LIMIT": exceptionFilterLimit = (double)item.Value; break; case "EXC_WINDOW": exceptionFilterWindowSize = TimeSpan.Parse(item.Value); break; case "COM_ENABLED": compressionFilterEnabled = Convert.ToBoolean((int)item.Value); break; case "COM_LIMIT_TYPE": compressionFilterLimitType = (TagValueFilterDeviationType)((int)item.Value); break; case "COM_LIMIT": compressionFilterLimit = (double)item.Value; break; case "COM_WINDOW": compressionFilterWindowSize = TimeSpan.Parse(item.Value); break; case "MD_CREATEDAT": createdAt = new DateTime((long)item.Value, DateTimeKind.Utc); break; case "MD_CREATEDBY": creator = item.Value; break; case "MD_MODIFIEDAT": modifiedAt = new DateTime((long)item.Value, DateTimeKind.Utc); break; case "MD_MODIFIEDBY": modifiedBy = item.Value; break; } } if (String.IsNullOrWhiteSpace(name)) { name = tagId; } var settings = new TagSettings() { Name = name, Description = description, Units = units, DataType = dataType, StateSet = stateSet, ExceptionFilterSettings = new TagValueFilterSettingsUpdate() { IsEnabled = exceptionFilterEnabled, LimitType = exceptionFilterLimitType, Limit = exceptionFilterLimit, WindowSize = exceptionFilterWindowSize }, CompressionFilterSettings = new TagValueFilterSettingsUpdate() { IsEnabled = compressionFilterEnabled, LimitType = compressionFilterLimitType, Limit = compressionFilterLimit, WindowSize = compressionFilterWindowSize } }; var metadata = new TagMetadata(createdAt, creator, modifiedAt, modifiedBy); var snapshotTask = LoadSnapshotValue(historian, tagId, cancellationToken); var lastArchivedTask = LoadLastArchivedValue(historian, tagId, cancellationToken); var archiveCandidateTask = LoadArchiveCandidateValue(historian, tagId, cancellationToken); await Task.WhenAll(snapshotTask, lastArchivedTask, archiveCandidateTask).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); var initialValues = new InitialTagValues(snapshotTask.Result, lastArchivedTask.Result, archiveCandidateTask.Result.Value, archiveCandidateTask.Result.CompressionAngleMinimum, archiveCandidateTask.Result.CompressionAngleMaximum); var result = new RedisTagDefinition(historian, tagId, settings, metadata, initialValues, null); return(result); }