//TODO: razmotriti stanje outage na ako se algoritam enduje sa nekim fejlom.... da li brisati sam outage npr... private async Task OnEndAlgorithmCleanUp(long headBreakerGid) { var algorithmBaseLogString = $"{baseLogString} [HeadBreakerGid: 0x{headBreakerGid:X16}]"; await StartedIsolationAlgorithms.TryRemoveAsync(headBreakerGid); await MonitoredHeadBreakerMeasurements.TryRemoveAsync(headBreakerGid); var enumerableCommandedElements = await CommandedElements.GetEnumerableDictionaryAsync(); var commandedElementsToBeRemoved = enumerableCommandedElements.Values.Where(element => element.CorrespondingHeadElementGid == headBreakerGid); foreach (var element in commandedElementsToBeRemoved) { await CommandedElements.TryRemoveAsync(element.ElementGid); } var enumerableOptimumIsolationPoints = await OptimumIsolationPoints.GetEnumerableDictionaryAsync(); var optimumIsolationPointsToBeRemovedGids = enumerableOptimumIsolationPoints.Where(kvp => kvp.Value == headBreakerGid).Select(kvp => kvp.Key); foreach (var gid in optimumIsolationPointsToBeRemovedGids) { await OptimumIsolationPoints.TryRemoveAsync(gid); } }
private async Task <bool> StartIsolationAlgorthm(OutageEntity outageToIsolate) { var result = await CreateIsolatingAlgorithm(outageToIsolate); if (!result.HasValue) { Logger.LogError($"{baseLogString} StartIsolationAlgorthm => CreateIsolatingAlgorithm did not return a vaule."); return(false); } var algorithm = result.Value; Logger.LogInformation($"{baseLogString} StartIsolationAlgorthm => HeadBreakerGid: 0x{algorithm.HeadBreakerGid:X16}, RecloserGd: 0x{algorithm.RecloserGid:X16} (Recloser gid is -1 if there is no recloser...)."); var scadaClient = ScadaIntegrityUpdateClient.CreateClient(); var scadaPublication = await scadaClient.GetIntegrityUpdateForSpecificTopic(Topic.SWITCH_STATUS); if (!(scadaPublication.Message is MultipleDiscreteValueSCADAMessage multipleDiscreteValueSCADAMessage)) { Logger.LogError($"{baseLogString} StartIsolationAlgorthm => ScadaPublication message is not of expected type: {typeof(MultipleDiscreteValueSCADAMessage)}, but of type: {scadaPublication.Message.GetType()}"); return(false); } if (!multipleDiscreteValueSCADAMessage.Data.ContainsKey(algorithm.HeadBreakerMeasurementGid)) { Logger.LogError($"{baseLogString} StartIsolationAlgorthm => HeadBreakerMeasurement with gid: 0x{algorithm.HeadBreakerMeasurementGid:X16} not found in integrity update data received from scada."); return(false); } var discreteModbusData = multipleDiscreteValueSCADAMessage.Data[algorithm.HeadBreakerMeasurementGid]; await MonitoredHeadBreakerMeasurements.SetAsync(algorithm.HeadBreakerMeasurementGid, discreteModbusData); await StartedIsolationAlgorithms.SetAsync(algorithm.HeadBreakerGid, algorithm); return(true); }
/// <summary> /// /// </summary> /// <param name="algorithm"></param> /// <param name="topology"></param> /// <returns>ConditionalValue with HeadElementGid as value - HasValue: false indicates that task ended unsuccessfully, value will never be null and will represent the id of the task -> HeadElementGid</returns> private async Task <ConditionalValue <long> > StartIndividualAlgorithmCycle(IsolationAlgorithm algorithm, OutageTopologyModel topology) { var algorithmBaseLogString = $"{baseLogString} [HeadBreakerGid: 0x{algorithm.HeadBreakerGid:X16}]"; //END CONDITION - poslednji otvoren brejker se nije otvorio vise od 'cycleUpperLimit' milisekundi => on predstavlja prvu optimalnu izolacionu tacku if (algorithm.CycleCounter * CycleInterval >= CycleUpperLimit) { Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => END CONDITION: {algorithm.CycleCounter} ms * {CycleInterval} ms > {CycleUpperLimit} ms."); var success = await FinishIndividualAlgorithmCycle(algorithm, topology); return(new ConditionalValue <long>(success, algorithm.HeadBreakerGid)); } else { Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => END CONDITION NOT MET: {algorithm.CycleCounter} ms * {CycleInterval} ms <= {CycleUpperLimit} ms."); } #region Check if HeadBreaker has OPENED after the last cycle var result = await MonitoredHeadBreakerMeasurements.TryGetValueAsync(algorithm.HeadBreakerMeasurementGid); if (!result.HasValue) { Logger.LogError($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => HeadBreakerMeasurement with gid: 0x{algorithm.HeadBreakerMeasurementGid:X16} not found in {ReliableDictionaryNames.MonitoredHeadBreakerMeasurements}."); return(new ConditionalValue <long>(false, algorithm.HeadBreakerGid)); } var headMeasurementData = result.Value; if (headMeasurementData.Value == (ushort)DiscreteCommandingType.CLOSE) { Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => HeadBreaker is CLOSED."); //by exiting now we apply the logical WAITING (cycle mechanism in RunAsync): //1) For HeadBreaker to open => moving to next breaker //2) For "time" to run up => FinishIndividualAlgorithmCycle //counting cycles from after the command was successfully executed var commandsCount = await CommandedElements.GetCountAsync(); if (commandsCount == 0) { algorithm.CycleCounter++; await StartedIsolationAlgorithms.SetAsync(algorithm.HeadBreakerGid, algorithm); Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => AlgorithCycleCounter set to {algorithm.CycleCounter}."); } else { Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => Skipping the cycle and waiting for {commandsCount} commands to be executed. HEAD is CLOSED. AlgorithCycleCounter remains set on {algorithm.CycleCounter}."); } return(new ConditionalValue <long>(true, algorithm.HeadBreakerGid)); } else if (headMeasurementData.Value == (ushort)DiscreteCommandingType.OPEN) { //skipping untill all commands were successfully executed var commandsCount = await CommandedElements.GetCountAsync(); if (commandsCount > 0) { Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => Skipping the cycle and waiting for {commandsCount} commands to be executed. HEAD is OPENED. AlgorithCycleCounter remains set on {algorithm.CycleCounter}."); return(new ConditionalValue <long>(true, algorithm.HeadBreakerGid)); } } else { Logger.LogError($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => headMeasurementData.Value is {headMeasurementData.Value} and cannot be casted to {typeof(DiscreteCommandingType)}."); return(new ConditionalValue <long>(false, algorithm.HeadBreakerGid)); } #endregion Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => HeadBreaker is OPENED."); //Closing current breaker, before moving to the next breaker var commands = new Dictionary <long, DiscreteCommandingType>(); if (algorithm.CurrentBreakerGid != algorithm.HeadBreakerGid) { Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => Preaparing command to CLOSE the current breaker 0x{algorithm.CurrentBreakerGid:X16}"); commands.Add(algorithm.CurrentBreakerGid, DiscreteCommandingType.CLOSE); } algorithm.CycleCounter = 0; //moving to the next breaker algorithm.CurrentBreakerGid = lifecycleHelper.GetNextBreaker(algorithm.CurrentBreakerGid, topology); Logger.LogDebug($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => Next breaker gid is 0x{algorithm.CurrentBreakerGid:X16}."); if (algorithm.CurrentBreakerGid <= 0 || !topology.GetElementByGid(algorithm.CurrentBreakerGid, out _)) { Logger.LogError($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => HeadBreakerMeasurement with gid: 0x{algorithm.HeadBreakerMeasurementGid:X16} not found in {ReliableDictionaryNames.MonitoredHeadBreakerMeasurements}."); return(new ConditionalValue <long>(false, algorithm.HeadBreakerGid)); } var enumerableCommandedElements = await CommandedElements.GetEnumerableDictionaryAsync(); //reaching the END of the FEEDER - ending the algorithm if (algorithm.CurrentBreakerGid == algorithm.RecloserGid) { string message = $"{algorithmBaseLogString} StartIndividualAlgorithmCycle => End of the feeder, no outage detected."; Logger.LogWarning(message); //TODO: HOW TO HANDEL - archived, deleted.... await SendCommands(algorithm, commands, enumerableCommandedElements); return(new ConditionalValue <long>(false, algorithm.HeadBreakerGid)); } if (!commands.ContainsKey(algorithm.CurrentBreakerGid) && !commands.ContainsKey(algorithm.HeadBreakerGid)) { commands.Add(algorithm.CurrentBreakerGid, DiscreteCommandingType.OPEN); Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => Preaparing command to OPEN the current breaker 0x{algorithm.CurrentBreakerGid:X16}"); commands.Add(algorithm.HeadBreakerGid, DiscreteCommandingType.CLOSE); Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => Preaparing command to CLOSE the head breaker 0x{algorithm.HeadBreakerGid:X16}"); } if (await SendCommands(algorithm, commands, enumerableCommandedElements)) { Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => Commands sent with success."); return(new ConditionalValue <long>(true, algorithm.HeadBreakerGid)); } else { Logger.LogInformation($"{algorithmBaseLogString} StartIndividualAlgorithmCycle => Send commands failed."); return(new ConditionalValue <long>(false, algorithm.HeadBreakerGid)); } }
public async Task Notify(IPublishableMessage message, string publisherName) { Logger.LogDebug($"{baseLogString} Notify method started."); while (!ReliableDictionariesInitialized) { await Task.Delay(1000); } try { if (message is MultipleDiscreteValueSCADAMessage multipleDiscreteValueSCADAMessage) { Logger.LogDebug($"{baseLogString} MultipleDiscreteValueSCADAMessage received."); var discreteData = multipleDiscreteValueSCADAMessage.Data; #region HeadBreakers var enumerableHeadBreakerMeasurements = await MonitoredHeadBreakerMeasurements.GetEnumerableDictionaryAsync(); foreach (var headMeasurementGid in enumerableHeadBreakerMeasurements.Keys) { if (!discreteData.ContainsKey(headMeasurementGid)) { continue; } await MonitoredHeadBreakerMeasurements.SetAsync(headMeasurementGid, discreteData[headMeasurementGid]); } #endregion HeadBreakers #region CommandedElements var measurementProviderClient = MeasurementProviderClient.CreateClient(); var enumerableCommandedElements = await CommandedElements.GetEnumerableDictionaryAsync(); foreach (var commandedElementGid in enumerableCommandedElements.Keys) { var measurementGid = (await measurementProviderClient.GetMeasurementsOfElement(commandedElementGid)).FirstOrDefault(); var measurement = await measurementProviderClient.GetDiscreteMeasurement(measurementGid); if (measurement is ArtificalDiscreteMeasurement) { await CommandedElements.TryRemoveAsync(commandedElementGid); Logger.LogInformation($"{baseLogString} Notify => Command on element 0x{commandedElementGid:X16} executed (ArtificalDiscreteMeasurement). New value: {measurement.CurrentOpen}"); continue; } if (!discreteData.ContainsKey(measurementGid)) { continue; } if (discreteData[measurementGid].Value == (ushort)enumerableCommandedElements[commandedElementGid].CommandingType) { if ((await CommandedElements.TryRemoveAsync(commandedElementGid)).HasValue) { Logger.LogInformation($"{baseLogString} Notify => Command on element 0x{commandedElementGid:X16} executed. New value: {discreteData[measurementGid].Value}"); } } } #endregion CommandedElements } else if (message is OMSModelMessage omsModelMessage) { Logger.LogDebug($"{baseLogString} OMSModelMessage received. Count {omsModelMessage.OutageTopologyModel.OutageTopology.Count}"); OutageTopologyModel topology = omsModelMessage.OutageTopologyModel; await OutageTopologyModel.SetAsync(ReliableDictionaryNames.OutageTopologyModel, topology); var reportingOutageClient = PotentialOutageReportingClient.CreateClient(); while (true) { var result = await PotentialOutagesQueue.TryDequeueAsync(); if (!result.HasValue) { break; } var command = result.Value; await reportingOutageClient.ReportPotentialOutage(command.ElementGid, command.CommandOriginType, command.NetworkType); Logger.LogInformation($"{baseLogString} PotentianOutageCommand executed. ElementGid: 0x{command.ElementGid:X16}, OriginType: {command.CommandOriginType}"); } } else { Logger.LogWarning($"{baseLogString} Notify => unexpected type of message: {message.GetType()}"); return; } } catch (Exception e) { string errorMessage = $"{baseLogString} Notify => Exception: {e.Message}"; Logger.LogError(errorMessage, e); } }