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);
                }
            }
        }
Esempio n. 6
0
        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}");
            }
        }
Esempio n. 8
0
        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));
        }
Esempio n. 9
0
        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);
        }
Esempio n. 15
0
        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);
        }