public long GetNextBreaker(long breakerId, OutageTopologyModel topology) { if (!topology.OutageTopology.ContainsKey(breakerId)) { string message = $"Breaker with gid: 0x{breakerId:X16} is not in a topology model."; Logger.LogError(message); throw new Exception(message); } long nextBreakerId = -1; foreach (long elementId in topology.OutageTopology[breakerId].SecondEnd) { if (this.modelResourcesDesc.GetModelCodeFromId(elementId) == ModelCode.ACLINESEGMENT) { nextBreakerId = GetNextBreaker(elementId, topology); } else if (this.modelResourcesDesc.GetModelCodeFromId(elementId) != ModelCode.BREAKER) { return(-1); } else { return(elementId); } if (nextBreakerId != -1) { break; } } return(nextBreakerId); }
private long CorrectTheStartingSwitch(long outageGid, OutageTopologyModel topology, long startingSwitch) { if (!topology.OutageTopology.TryGetValue(outageGid, out OutageTopologyElement firstElement)) { string message = $"{baseLogString} GetAffectedConsumers => GID: 0x{outageGid:X16} not found in topologyModel.OutageTopology dictionary."; Logger.LogError(message); throw new Exception(message); } if (!topology.OutageTopology.TryGetValue(firstElement.FirstEnd, out OutageTopologyElement currentElementAbove)) { string message = $"{baseLogString} GetAffectedConsumers => GID: 0x{firstElement.FirstEnd:X16} not found in topologyModel.OutageTopology dictionary."; Logger.LogError(message); throw new Exception(message); } //MOVING UP to the top opened switch while (!currentElementAbove.DmsType.Equals("ENERGYSOURCE")) { if (currentElementAbove.IsOpen) { startingSwitch = currentElementAbove.Id; break; } if (!topology.OutageTopology.TryGetValue(currentElementAbove.FirstEnd, out currentElementAbove)) { string message = $"{baseLogString} GetAffectedConsumers => GID: 0x{currentElementAbove.FirstEnd:X16} not found in topologyModel.OutageTopology dictionary."; Logger.LogError(message); throw new Exception(message); } } return(startingSwitch); }
public List <long> GetAffectedConsumers(long outageGid, OutageTopologyModel topology, NetworkType networkType) { List <long> affectedConsumers = new List <long>(); Stack <long> nodesToBeVisited = new Stack <long>(); HashSet <long> visited = new HashSet <long>(); long startingSwitch = outageGid; //TODO: da li nam je ovo potrebno //startingSwitch = CorrectTheStartingSwitch(outageGid, topology, startingSwitch); nodesToBeVisited.Push(startingSwitch); while (nodesToBeVisited.Count > 0) { long currentNode = nodesToBeVisited.Pop(); if (visited.Contains(currentNode)) { continue; } visited.Add(currentNode); if (!topology.OutageTopology.TryGetValue(currentNode, out OutageTopologyElement topologyElement)) { string message = $"{baseLogString} GetAffectedConsumers => GID: 0x{currentNode:X16} not found in topologyModel.OutageTopology dictionary."; Logger.LogError(message); throw new Exception(message); } foreach (long adjNode in topologyElement.SecondEnd) { nodesToBeVisited.Push(adjNode); } if (topologyElement.DmsType != "ENERGYCONSUMER") { continue; } if (networkType == NetworkType.SCADA_NETWORK && !topologyElement.IsActive) { affectedConsumers.Add(currentNode); } else if (networkType == NetworkType.NON_SCADA_NETWORK && !topologyElement.IsRemote) { affectedConsumers.Add(currentNode); } } return(affectedConsumers); }
public async Task <bool> SendLocationIsolationCrew(long outageId) { Logger.LogVerbose($"{baseLogString} SendLocationIsolationCrew method started. OutageId {outageId}"); while (!ReliableDictionariesInitialized) { await Task.Delay(1000); } try { var result = await lifecycleHelper.GetCreatedOutage(outageId); if (!result.HasValue) { Logger.LogError($"{baseLogString} SendLocationIsolationCrew => Created Outage is null. OutageId {outageId}"); return(false); } var outageEntity = result.Value; var enumerableTopology = await OutageTopologyModel.GetEnumerableDictionaryAsync(); if (!enumerableTopology.ContainsKey(ReliableDictionaryNames.OutageTopologyModel)) { Logger.LogError($"{baseLogString} Start => Topology not found in Rel Dictionary: {ReliableDictionaryNames.OutageTopologyModel}."); return(false); } var topology = enumerableTopology[ReliableDictionaryNames.OutageTopologyModel]; if (!await StartLocationAndIsolationAlgorithm(outageEntity, topology)) { return(false); } return(await lifecycleHelper.PublishOutageAsync(Topic.ACTIVE_OUTAGE, outageMessageMapper.MapOutageEntity(outageEntity))); } catch (Exception e) { string message = $"{baseLogString} SendLocationIsolationCrew => Exception: {e.Message}"; Logger.LogError(message, e); return(false); } }
public async Task Start() { while (!ReliableDictionariesInitialized) { await Task.Delay(1000); } var enumerableStartedAlgorithms = await StartedIsolationAlgorithms.GetEnumerableDictionaryAsync(); if (enumerableStartedAlgorithms.Count == 0) { Logger.LogVerbose($"{baseLogString} Start => No started algorithms."); return; } var enumerableTopology = await OutageTopologyModel.GetEnumerableDictionaryAsync(); if (!enumerableTopology.ContainsKey(ReliableDictionaryNames.OutageTopologyModel)) { Logger.LogError($"{baseLogString} Start => Topology not found in Rel Dictionary: {ReliableDictionaryNames.OutageTopologyModel}."); return; } var topology = enumerableTopology[ReliableDictionaryNames.OutageTopologyModel]; var tasks = new List <Task <ConditionalValue <long> > >(); foreach (var algorithm in enumerableStartedAlgorithms.Values) { tasks.Add(StartIndividualAlgorithmCycle(algorithm, topology)); } var tasksArray = tasks.ToArray(); Task.WaitAll(tasksArray); foreach (var task in tasksArray) { //SVESNO SE POGRESNO KORISTI HasValue if (!task.Result.HasValue) { var headBreakerGid = task.Result.Value; await OnEndAlgorithmCleanUp(headBreakerGid); } } }
private long GetRecloserForHeadBreaker(long headBreakerId, OutageTopologyModel topology) { long recolserId = -1; if (!topology.OutageTopology.ContainsKey(headBreakerId)) { string message = $"Head switch with gid: {headBreakerId} is not in a topology model."; Logger.LogError(message); throw new Exception(message); } long currentBreakerId = headBreakerId; while (currentBreakerId != 0) { //currentBreakerId = TopologyModel.OutageTopology[currentBreakerId].SecondEnd.Where(element => modelResourcesDesc.GetModelCodeFromId(element) == ModelCode.BREAKER).FirstOrDefault(); currentBreakerId = lifecycleHelper.GetNextBreaker(currentBreakerId, topology); if (currentBreakerId == 0) { continue; } if (!topology.OutageTopology.ContainsKey(currentBreakerId)) { string message = $"Switch with gid: 0X{currentBreakerId:X16} is not in a topology model."; Logger.LogError(message); throw new Exception(message); } if (!topology.OutageTopology[currentBreakerId].NoReclosing) { recolserId = currentBreakerId; break; } } return(recolserId); }
public void TopologyToOMSConvertDelegate(List <IOutageTopologyModel> outageTopologyModels) { IOutageTopologyModel outageTopologyModel; if (outageTopologyModels.Count == 0) { outageTopologyModel = new OutageTopologyModel(); } else { outageTopologyModel = outageTopologyModels.First(); } OMSModelMessage message = new OMSModelMessage(outageTopologyModel); CalculationEnginePublication publication = new CalculationEnginePublication(Topic.OMS_MODEL, message); try { using (PublisherProxy publisherProxy = proxyFactory.CreateProxy <PublisherProxy, IPublisher>(EndpointNames.PublisherEndpoint)) { if (publisherProxy == null) { string errMessage = "TopologyToOMSConvertDelegate => PublisherProxy is null."; logger.LogError(errMessage); throw new NullReferenceException(errMessage); } publisherProxy.Publish(publication, "CE_PUBLICATION"); logger.LogDebug("Topology publisher published new oms model successfully."); } } catch (Exception ex) { logger.LogError($"Topology publisher failed to publish new oms model. Exception: {ex.Message}"); } }
private async Task <ConditionalValue <OutageEntity> > StoreActiveOutage(long elementGid, List <long> affectedConsumersGids, OutageTopologyModel topology) { var outageModelAccessClient = OutageModelAccessClient.CreateClient(); var allOutages = await outageModelAccessClient.GetAllOutages(); var targetedOutages = allOutages.Where(outage => outage.OutageElementGid == elementGid && outage.OutageState != OutageState.ARCHIVED); if (targetedOutages.FirstOrDefault() != null) { Logger.LogWarning($"{baseLogString} StoreActiveOutage => Malfunction on element with gid: 0x{elementGid:x16} has already been reported."); return(new ConditionalValue <OutageEntity>(false, null)); } List <Consumer> consumerDbEntities = await lifecycleHelper.GetAffectedConsumersFromDatabase(affectedConsumersGids); if (consumerDbEntities.Count != affectedConsumersGids.Count) { Logger.LogWarning($"{baseLogString} StoreActiveOutage => Some of affected consumers are not present in database."); return(new ConditionalValue <OutageEntity>(false, null)); } long recloserId = GetRecloserForHeadBreaker(elementGid, topology); List <Equipment> defaultIsolationPoints = await lifecycleHelper.GetEquipmentEntityAsync(new List <long> { elementGid, recloserId }); OutageEntity createdActiveOutage = new OutageEntity { OutageElementGid = elementGid, AffectedConsumers = consumerDbEntities, OutageState = OutageState.CREATED, ReportTime = DateTime.UtcNow, DefaultIsolationPoints = defaultIsolationPoints, }; var activeOutageDbEntity = await outageModelAccessClient.AddOutage(createdActiveOutage); if (activeOutageDbEntity == null) { Logger.LogError($"{baseLogString} StoreActiveOutage => activeOutageDbEntity is null."); return(new ConditionalValue <OutageEntity>(false, null)); } await UpdateRecloserOutageMap(recloserId, affectedConsumersGids, activeOutageDbEntity); return(new ConditionalValue <OutageEntity>(true, activeOutageDbEntity)); }
public async Task <bool> ReportPotentialOutage(long elementGid, CommandOriginType commandOriginType, NetworkType networkType) { Logger.LogVerbose($"{baseLogString} ReportPotentialOutage method started. ElementGid: 0x{elementGid:X16}, CommandOriginType: {commandOriginType}, NetworkType: {networkType}"); while (!ReliableDictionariesInitialized) { await Task.Delay(1000); } try { #region Preconditions var ceModelProviderClient = CeModelProviderClient.CreateClient(); if (await ceModelProviderClient.IsRecloser(elementGid)) { Logger.LogWarning($"{baseLogString} ReportPotentialOutage => Element with gid 0x{elementGid:X16} is a Recloser. Call to ReportPotentialOutage aborted."); return(false); } var enumerableTopology = await OutageTopologyModel.GetEnumerableDictionaryAsync(); if (!enumerableTopology.ContainsKey(ReliableDictionaryNames.OutageTopologyModel)) { Logger.LogError($"{baseLogString} ReportPotentialOutage => Topology not found in Rel Dictionary: {ReliableDictionaryNames.OutageTopologyModel}."); return(false); } var topology = enumerableTopology[ReliableDictionaryNames.OutageTopologyModel]; var affectedConsumersGids = lifecycleHelper.GetAffectedConsumers(elementGid, topology, networkType); var historyDBManagerClient = HistoryDBManagerClient.CreateClient(); if (!(await CheckPreconditions(elementGid, commandOriginType, affectedConsumersGids, historyDBManagerClient))) { Logger.LogWarning($"{baseLogString} ReportPotentialOutage => Parameters do not satisfy required preconditions. ElementId: 0x{elementGid:X16}, CommandOriginType: {commandOriginType}"); return(false); } #endregion Preconditions Logger.LogInformation($"{baseLogString} ReportPotentialOutage => Reporting outage for gid: 0x{elementGid:X16}, CommandOriginType: {commandOriginType}"); var result = await StoreActiveOutage(elementGid, affectedConsumersGids, topology); if (!result.HasValue) { Logger.LogError($"{baseLogString} ReportPotentialOutage => Storing outage on element 0x{elementGid:X16} FAILED."); return(false); } var createdOutage = result.Value; Logger.LogInformation($"{baseLogString} ReportPotentialOutage => Outage on element with gid: 0x{createdOutage.OutageElementGid:x16} is successfully stored in database."); //await historyDBManagerClient.OnSwitchOpened(elementGid, createdOutage.OutageId); //await historyDBManagerClient.OnConsumerBlackedOut(affectedConsumersGids, createdOutage.OutageId); return(await lifecycleHelper.PublishOutageAsync(Topic.ACTIVE_OUTAGE, outageMessageMapper.MapOutageEntity(createdOutage))); } catch (Exception e) { string message = $"{baseLogString} ReportPotentialOutage => exception: {e.Message}"; Logger.LogError(message, e); return(false); } }
private async Task SetOptimumIsolationPoints(OutageEntity outageEntity, IsolationAlgorithm algorithm, OutageTopologyModel topology) { var algorithmBaseLogString = $"{baseLogString} [HeadBreakerGid: 0x{algorithm.HeadBreakerGid:X16}]"; long firstOptimumIsolationPointGid = algorithm.CurrentBreakerGid; long secondOptimumIsolationPointGid = lifecycleHelper.GetNextBreaker(firstOptimumIsolationPointGid, topology); if (!topology.OutageTopology.ContainsKey(secondOptimumIsolationPointGid)) { string message = $"{algorithmBaseLogString} SetOptimumIsolationPoints => Breaker (next breaker) with id: 0x{secondOptimumIsolationPointGid:X16} is not in topology"; Logger.LogError(message); throw new Exception(message); } long outageElement = topology.OutageTopology[secondOptimumIsolationPointGid].FirstEnd; if (!topology.OutageTopology[firstOptimumIsolationPointGid].SecondEnd.Contains(outageElement)) { string message = $"{algorithmBaseLogString} SetOptimumIsolationPoints => Outage element with gid: 0x{outageElement:X16} is not on a second end of current breaker id"; Logger.LogError(message); throw new Exception(message); } outageEntity.OptimumIsolationPoints = await lifecycleHelper.GetEquipmentEntityAsync(new List <long>() { firstOptimumIsolationPointGid, secondOptimumIsolationPointGid }); if (outageEntity.OptimumIsolationPoints.Count != 2 || !outageEntity.OptimumIsolationPoints.Any(point => point.EquipmentId == firstOptimumIsolationPointGid) || !outageEntity.OptimumIsolationPoints.Any(point => point.EquipmentId == secondOptimumIsolationPointGid)) { string message = $"{algorithmBaseLogString} SetOptimumIsolationPoints => first OptimumIsolationPointGid [0x{firstOptimumIsolationPointGid:X16}] or second OptimumIsolationPointGid [0x{secondOptimumIsolationPointGid:X16}] were not found or created successfully."; Logger.LogError(message); throw new Exception(message); } await OptimumIsolationPoints.SetAsync(firstOptimumIsolationPointGid, algorithm.HeadBreakerGid); await OptimumIsolationPoints.SetAsync(secondOptimumIsolationPointGid, algorithm.HeadBreakerGid); Logger.LogInformation($"{algorithmBaseLogString} SetOptimumIsolationPoints => Optimum points 0x{firstOptimumIsolationPointGid:X16} and 0x{secondOptimumIsolationPointGid:X16}"); }
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); } }
/// <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)); } }
private async Task <bool> StartLocationAndIsolationAlgorithm(OutageEntity outageEntity, OutageTopologyModel topology) { long reportedGid = outageEntity.DefaultIsolationPoints.First().EquipmentId; if (!topology.GetElementByGid(reportedGid, out OutageTopologyElement topologyElement)) { Logger.LogError($"{baseLogString} StartLocationAndIsolationAlgorithm => element with gid 0x{reportedGid:X16} not found in outage topology model."); return(false); } long upBreaker; long outageElementGid = -1; Logger.LogInformation($"{baseLogString} StartLocationAndIsolationAlgorithm => Entering 10 sec delay."); await Task.Delay(10_000); Logger.LogInformation($"{baseLogString} StartLocationAndIsolationAlgorithm => 10 sec delay ended."); var outageSimulatorClient = OutageSimulatorClient.CreateClient(); var outageModelAccessClient = OutageModelAccessClient.CreateClient(); //Da li je prijaveljen element OutageElement if (await outageSimulatorClient.IsOutageElement(reportedGid)) { outageElementGid = reportedGid; } //Da li je mozda na ACL-novima ispod prijavljenog elementa else { for (int i = 0; i < topologyElement.SecondEnd.Count; i++) { var potentialOutageElementGid = topologyElement.SecondEnd[i]; if (!(await outageSimulatorClient.IsOutageElement(potentialOutageElementGid))) { continue; } if (outageElementGid == -1) { outageElementGid = potentialOutageElementGid; outageEntity.OutageElementGid = outageElementGid; //outageEntity.AffectedConsumers = await lifecycleHelper.GetAffectedConsumersFromDatabase(lifecycleHelper.GetAffectedConsumers(outageElementGid, topology, NetworkType.NON_SCADA_NETWORK)); } else { //KAKO SE ULAZI U OVAJ ELSE? => u else se ulazi tako sto se ide kroz for i prvi element se oznaci kao outage element, zatim se pronaje jos neki... znaci ovo je nacin da se kreira drugi, treci outage, na racvanju ispod elementa, po for-u.... var entity = new OutageEntity() { OutageElementGid = potentialOutageElementGid, ReportTime = DateTime.UtcNow }; await outageModelAccessClient.AddOutage(entity); } } } //Tragamo za ACL-om gore ka source-u while (outageElementGid == -1 && !topologyElement.IsRemote && topologyElement.DmsType != "ENERGYSOURCE") { if (await outageSimulatorClient.IsOutageElement(topologyElement.Id)) { outageElementGid = topologyElement.Id; outageEntity.OutageElementGid = outageElementGid; } topology.GetElementByGid(topologyElement.FirstEnd, out topologyElement); } if (outageElementGid == -1) { outageEntity.OutageState = OutageState.REMOVED; await outageModelAccessClient.RemoveOutage(outageEntity); Logger.LogError($"{baseLogString} StartLocationAndIsolationAlgorithm => End of feeder no outage detected."); return(false); } topology.GetElementByGidFirstEnd(outageEntity.OutageElementGid, out topologyElement); while (topologyElement.DmsType != "BREAKER") { topology.GetElementByGidFirstEnd(topologyElement.Id, out topologyElement); } upBreaker = topologyElement.Id; long nextBreaker = lifecycleHelper.GetNextBreaker(outageEntity.OutageElementGid, topology); if (!topology.OutageTopology.ContainsKey(nextBreaker)) { string message = $"{baseLogString} StartLocationAndIsolationAlgorithm => Breaker (next breaker) with id: 0x{nextBreaker:X16} is not in topology"; Logger.LogError(message); throw new Exception(message); } long outageElement = topology.OutageTopology[nextBreaker].FirstEnd; if (!topology.OutageTopology[upBreaker].SecondEnd.Contains(outageElement)) { string message = $"{baseLogString} StartLocationAndIsolationAlgorithm => Outage element with gid: 0x{outageElement:X16} is not on a second end of current breaker id"; Logger.LogError(message); throw new Exception(message); } outageEntity.OptimumIsolationPoints = await lifecycleHelper.GetEquipmentEntityAsync(new List <long>() { upBreaker, nextBreaker }); outageEntity.IsolatedTime = DateTime.UtcNow; outageEntity.OutageState = OutageState.ISOLATED; await outageModelAccessClient.UpdateOutage(outageEntity); var commands = new Dictionary <long, DiscreteCommandingType> { { upBreaker, DiscreteCommandingType.OPEN }, { nextBreaker, DiscreteCommandingType.OPEN }, }; var enumerableCommandedElements = await CommandedElements.GetEnumerableDictionaryAsync(); if (!await lifecycleHelper.SendMultipleScadaCommandAsync(commands, enumerableCommandedElements, CommandOriginType.LOCATION_AND_ISOLATING_ALGORITHM_COMMAND)) { string message = $"{baseLogString} StartLocationAndIsolationAlgorithm => Sending multiple command failed."; Logger.LogError(message); return(false); } commands.Keys.ToList().ForEach(async commandedElementGid => { await ElementsToBeIgnoredInReportPotentialOutage.SetAsync(commandedElementGid, DateTime.UtcNow); Logger.LogDebug($"{baseLogString} SendCommands => Element 0x{commandedElementGid:X16} set to collection '{ReliableDictionaryNames.ElementsToBeIgnoredInReportPotentialOutage}' at {DateTime.UtcNow}."); }); return(true); }
private List <long> GetAffectedConsumers(long potentialOutageGid, OutageTopologyModel topology, NetworkType networkType) { List <long> affectedConsumers = new List <long>(); Stack <long> nodesToBeVisited = new Stack <long>(); HashSet <long> visited = new HashSet <long>(); long startingSwitch = potentialOutageGid; //TODO: pogledati kad se bude testirala NoScada //if(networkType == NetworkType.NON_SCADA_NETWORK) //{ //TODO: cemu sluzi ova logika? -deluje da podize starter ka gore.... a cemu to sluzi boga pitaj, eventualno za neSkada deo, bolje razdvojiti metode... if (topology.OutageTopology.TryGetValue(potentialOutageGid, out OutageTopologyElement firstElement) && topology.OutageTopology.TryGetValue(firstElement.FirstEnd, out OutageTopologyElement currentElementAbove)) { while (!currentElementAbove.DmsType.Equals("ENERGYSOURCE")) { if (currentElementAbove.IsOpen) { startingSwitch = currentElementAbove.Id; break; } if (!topology.OutageTopology.TryGetValue(currentElementAbove.FirstEnd, out currentElementAbove)) { break; } } } //} nodesToBeVisited.Push(startingSwitch); while (nodesToBeVisited.Count > 0) { long currentNode = nodesToBeVisited.Pop(); if (!visited.Contains(currentNode)) { visited.Add(currentNode); if (!topology.OutageTopology.TryGetValue(currentNode, out OutageTopologyElement topologyElement)) { //TOOD string message = $"GID: 0x{currentNode:X16} not found in topologyModel.OutageTopology dictionary...."; Logger.LogError(message); continue; //or throw? //break } foreach (long adjNode in topologyElement.SecondEnd) { nodesToBeVisited.Push(adjNode); } if (topologyElement.DmsType != "ENERGYCONSUMER") { continue; } if (networkType == NetworkType.SCADA_NETWORK && !topologyElement.IsActive) { affectedConsumers.Add(currentNode); } else if (networkType == NetworkType.SCADA_NETWORK && !topologyElement.IsRemote) { affectedConsumers.Add(currentNode); } } } return(affectedConsumers); }
public async Task Start(List <long> calls) { Logger.LogDebug("Starting tracking algorithm."); //on every start tracking algorithm get up to date outage topology model var topologyProviderClient = TopologyProviderClient.CreateClient(); outageTopologyModel = await topologyProviderClient.GetOMSModel(); this.potentialOutages = LocateSwitchesUsingCalls(calls); this.outages = new List <long>(); HashSet <long> visited = new HashSet <long>(); bool foundOutage = false; long currentGid, previousGid; currentGid = this.potentialOutages[0]; try { while (this.potentialOutages.Count > 0) { currentGid = this.potentialOutages[0]; previousGid = currentGid; this.outages.Add(currentGid); outageTopologyModel.GetElementByGid(currentGid, out OutageTopologyElement topologyElement); this.potentialOutages.Remove(currentGid); while (topologyElement.DmsType != "ENERGYSOURCE" && !topologyElement.IsRemote && this.potentialOutages.Count > 0) { foundOutage = false; if (TraceDFS(visited, topologyElement, foundOutage)) { this.outages.Remove(previousGid); this.outages.Add(currentGid); previousGid = currentGid; } topologyElement = GetSwitch(topologyElement.FirstEnd); if (topologyElement == null) { break; } currentGid = topologyElement.Id; } } } catch (Exception ex) { string message = $"Tracing algorithm failed with error: {ex.Message}"; Logger.LogError(message); Console.WriteLine(message); } var reportOutageClient = PotentialOutageReportingClient.CreateClient(); foreach (var potentialOutageElementGid in this.outages) { var ceModelProviderClient = CeModelProviderClient.CreateClient(); if (!await ceModelProviderClient.IsRecloser(potentialOutageElementGid)) { //TODO: razdvojiti metode scada, noScada await reportOutageClient.ReportPotentialOutage(potentialOutageElementGid, CommandOriginType.NON_SCADA_OUTAGE, NetworkType.NON_SCADA_NETWORK); } else { Logger.LogDebug($"{baseLogString} Start => Element with gid 0x{potentialOutageElementGid:X16} is a Recloser. ReportPotentialOutage call is not required."); } } }
public IOutageTopologyModel ConvertTopologyToOMSModel(ITopology topology) { logger.LogDebug("Topology to OMS model convert started."); IOutageTopologyModel outageTopologyModel = new OutageTopologyModel(); Stack <long> stack = new Stack <long>(); var reclosers = Provider.Instance.ModelProvider.GetReclosers(); outageTopologyModel.FirstNode = topology.FirstNode; stack.Push(topology.FirstNode); List <long> secondEnd = new List <long>(); long nextElement = 0; long nextElementGid = 0; ITopologyElement element; bool isOpen; while (stack.Count > 0) { nextElementGid = stack.Pop(); if (topology.GetElementByGid(nextElementGid, out element)) { secondEnd.Clear(); if (!reclosers.Contains(nextElementGid)) { foreach (var child in element.SecondEnd) { nextElement = child.Id; if (ModelCodeHelper.ExtractTypeFromGlobalId(nextElement) == 0) { try { Field field = child as Field; nextElement = field.Members.First().Id; } catch (Exception) { logger.LogError($"[TopologyConverter] Error while getting field in Topology to OMSModel convert. Element is not field or field is empty."); } } secondEnd.Add(nextElement); stack.Push(nextElement); } } isOpen = false; foreach (var meausrementGid in element.Measurements.Keys) { if (Provider.Instance.MeasurementProvider.TryGetDiscreteMeasurement(meausrementGid, out DiscreteMeasurement discreteMeasurement)) { isOpen = discreteMeasurement.CurrentOpen; } } if (!outageTopologyModel.GetElementByGid(element.Id, out var _)) { outageTopologyModel.AddElement( new OutageTopologyElement(element.Id) { FirstEnd = (element.FirstEnd != null) ? element.FirstEnd.Id : 0, DmsType = element.DmsType, IsRemote = element.IsRemote, IsActive = element.IsActive, SecondEnd = new List <long>(secondEnd), NoReclosing = element.NoReclosing, IsOpen = isOpen }); } } else { logger.LogWarn($"[TopologyConverter] Error while getting topology element from topology. It does not exist in topology."); } } logger.LogDebug("Topology to OMSModel convert finished successfully."); return(outageTopologyModel); }
private async Task <bool> FinishIndividualAlgorithmCycle(IsolationAlgorithm algorithm, OutageTopologyModel topology) { var algorithmBaseLogString = $"{baseLogString} [HeadBreakerGid: 0x{algorithm.HeadBreakerGid:X16}]"; Logger.LogInformation($"{algorithmBaseLogString} entering FinishIndividualAlgorithmCycle."); if (algorithm.CurrentBreakerGid <= 0 || algorithm.CurrentBreakerGid == algorithm.RecloserGid) { string message = $"{algorithmBaseLogString} FinishIndividualAlgorithmCycle => End of the feeder, no outage detected."; Logger.LogWarning(message); return(false); } var getCreatedOutageResult = await lifecycleHelper.GetCreatedOutage(algorithm.OutageId); if (!getCreatedOutageResult.HasValue) { Logger.LogError($"{algorithmBaseLogString} FinishIndividualAlgorithmCycle => Created Outage is null. OutageId: {algorithm.OutageId}"); return(false); } var outageToIsolate = getCreatedOutageResult.Value; await SetDefaultIsolationPoints(outageToIsolate, algorithm); await SetOptimumIsolationPoints(outageToIsolate, algorithm, topology); //ISOLATE on optimum points var firstOptimumPoint = outageToIsolate.OptimumIsolationPoints[0]; var secondOptimumPoint = outageToIsolate.OptimumIsolationPoints[1]; var commands = new Dictionary <long, DiscreteCommandingType> { { algorithm.HeadBreakerGid, DiscreteCommandingType.CLOSE }, { firstOptimumPoint.EquipmentId, DiscreteCommandingType.OPEN }, { secondOptimumPoint.EquipmentId, DiscreteCommandingType.OPEN }, { algorithm.RecloserGid, DiscreteCommandingType.CLOSE }, }; var enumerableCommandedElements = await CommandedElements.GetEnumerableDictionaryAsync(); if (!await SendCommands(algorithm, commands, enumerableCommandedElements)) { string message = $"{algorithmBaseLogString} FinishIndividualAlgorithmCycle => Failed on SendMultipleScadaCommandAsync."; Logger.LogError(message); return(false); } long outageElementGid = topology.OutageTopology[secondOptimumPoint.EquipmentId].FirstEnd; //element iznad donjeg pointa - moze biti samo jedan gornji element (parent) if (!topology.OutageTopology[firstOptimumPoint.EquipmentId].SecondEnd.Contains(outageElementGid)) { string message = $"{algorithmBaseLogString} FinishIndividualAlgorithmCycle => Outage element with gid: 0x{outageElementGid:X16} is not on a second end of current breaker id"; Logger.LogError(message); return(false); } outageToIsolate.IsolatedTime = DateTime.UtcNow; outageToIsolate.OutageElementGid = outageElementGid; outageToIsolate.OutageState = OutageState.ISOLATED; var outageModelAccessClient = OutageModelAccessClient.CreateClient(); await outageModelAccessClient.UpdateOutage(outageToIsolate); Logger.LogInformation($"{algorithmBaseLogString} FinishIndividualAlgorithmCycle => Isolation of outage with id: {outageToIsolate.OutageId}. Optimum isolation points: 0x{outageToIsolate.OptimumIsolationPoints[0].EquipmentId:X16} and 0x{outageToIsolate.OptimumIsolationPoints[1].EquipmentId:X16}, and outage element id is 0x{outageElementGid:X16}"); await lifecycleHelper.PublishOutageAsync(Topic.ACTIVE_OUTAGE, outageMessageMapper.MapOutageEntity(outageToIsolate)); Logger.LogInformation($"{algorithmBaseLogString} FinishIndividualAlgorithmCycle => Outage with id: 0x{outageToIsolate.OutageId:x16} is successfully published."); await OnEndAlgorithmCleanUp(algorithm.HeadBreakerGid); return(true); }