/// <summary> /// Запуск симуляции /// </summary> /// <param name="graph">Граф</param> /// <param name="start">Начальная вершина</param> /// <param name="end">Конечная вершина</param> /// <param name="settings">Настройки симуляции</param> /// <param name="progress">Програсс симуляции</param> public SimulationReport Simulate(Graph.Graph graph, Vertex start, Vertex end, SimulationSettings settings, IProgress <int> progress = null) { var failureTimes = new List <double>(); var repairTimes = new List <double>(); var cGraph = new ComputationGraph(graph, _pathFinder, start, end); // Симуляция for (int i = 0; i < settings.NumRuns; i++) { cGraph.Reset(); var rep = RunIteration(cGraph, settings); // Все, что больше MaxTime - мусор if (rep.FailureTime <= settings.MaxTime) { failureTimes.Add(rep.FailureTime); } repairTimes.AddRange(rep.RepairTimes); progress?.Report((int)(i / (float)settings.NumRuns * 100)); } // Формирование отчета var report = new SimulationReport(); report.MinFailureTime = failureTimes.Min(); report.MaxFailureTime = failureTimes.Max(); report.AverageFailureTime = failureTimes.Average(); report.AverageRepairTime = repairTimes.Count > 0 ? repairTimes.Average() : 0; report.AvailabilityRate = report.AverageFailureTime / (report.AverageFailureTime + report.AverageRepairTime); report.Pathes = cGraph.Pathes; // TODO: Вероятность безотказной работы системы // TODO: Гррафик зависимости безотказной работы системы от времени Pc(t) report.FailureBarChart = ToHistogram(failureTimes, settings.BarChartCount, report.MinFailureTime, report.MaxFailureTime); if (repairTimes.Count > 0) { report.RepairBarChart = ToHistogram(repairTimes, settings.BarChartCount); } // Отдельный запуск для сохранения диаграммы восстановления cGraph.Reset(); report.TimeDiagram = RunIteration(cGraph, settings, true).TimeDiagram; progress?.Report(100); return(report); }
// Возвращает суммарную интенсивность отказов не отказавших // элементов private double GetTotalFailIntensity(ComputationGraph graph) { double sum = 0; foreach (var destroyableElement in graph.Elements) { if (!destroyableElement.Value.IsDestroyed) { sum += destroyableElement.Value.FailIntensity; } } return(sum); }
// Генерирует очередное событие отказа private SimulationEvent GenerateFailureEvent(ComputationGraph cGraph, double t) { var element = GetFailedElement(cGraph); if (element == null) { return(null); } double tFail = GetFailureTime(element); t += tFail; return(new SimulationEvent() { Time = t, Element = element, Type = EventType.FAILURE }); }
// Выбирает отказавший элемент private DestroyableElement GetFailedElement(ComputationGraph graph) { double rand = _rnd.NextDouble() * GetTotalFailIntensity(graph); double sum = 0; foreach (var destroyableElement in graph.Elements) { if (destroyableElement.Value.IsDestroyed) { continue; } sum += destroyableElement.Value.FailIntensity; if (sum > rand) { return(destroyableElement.Value); } } return(null); }
// Симлуляция одной итерации до отказа (или истечения времени) private IterationReport RunIteration(ComputationGraph cGraph, SimulationSettings settings, bool saveTimeline = false) { /* Идея алгоритма * Есть события (отказ или восстанолвение), которые * хранятся в куче. На вершине кучи всегда самое ближайшее событие * * Есть заявки на восстановление, которые помещаются в очередь с * какой-то политикой * * - Генерируем событие отказа, помещаем его в кучу * - Извлекаем из кучи очередное событие * - Отказ: * - Отказываем элемент * - Помещаем заявку на восстановление в очередь * - Генерируем новый отказ, помещаем в кучу * - Восстановление * - Восстанавливаем элемент * * - Каждый раз мы берем свободных воркеров и раздаем * им задачи из очереди. Каждая задача становится событием * восстановления через некоторое время */ var repairTimeList = new List <double>(); TimeDiagram diagram = null; if (saveTimeline) { diagram = new TimeDiagram(); foreach (var element in cGraph.Elements) { var tl = new TimeLine(); tl.Add(new StatePoint() { State = ElementState.OK, Time = 0 }); diagram.Add(element.Value.Data, tl); } } var heap = new Heap <SimulationEvent>(new MinPriorityComparer <SimulationEvent>()); var repairQueue = settings.RepairFactory.CreateQueue(); double t = 0; int freeWorkers = settings.RepairTeamsCount; // Генерируем первый отказ SimulationEvent ev = GenerateFailureEvent(cGraph, t); heap.Add(ev); while (cGraph.IsPathExists() && t < settings.MaxTime) { ev = heap.Pop(); t = ev.Time; if (ev.Type == EventType.FAILURE) { ev.Element.IsDestroyed = true; // Добавляем задачу в очередь на восстановление if (settings.IsRepair) { CreateRepairTask(ev, t, settings.RepairIntensity, repairQueue); } // Сохраняем событие на диаграмме if (saveTimeline) { SaveEvent(diagram, ev.Element, ElementState.FAILURE, t); } ev = GenerateFailureEvent(cGraph, t); heap.Add(ev); } else { ev.Element.IsDestroyed = false; freeWorkers++; // Сохраняем событие на диаграмме if (saveTimeline) { SaveEvent(diagram, ev.Element, ElementState.OK, t); } } // Начинаем восстанвливать while (freeWorkers > 0 && repairQueue.Count > 0 && settings.IsRepair) { var task = repairQueue.Pop(); ev = new SimulationEvent() { Element = task.Element, Time = t + task.TimeToRepair, Type = EventType.REPAIR }; heap.Add(ev); freeWorkers--; // Сохраняем статистику по восстановлению repairTimeList.Add(task.TimeToRepair); // Сохраняем событие на диаграмме if (saveTimeline) { SaveEvent(diagram, ev.Element, ElementState.REPAIR, t); } } } return(new IterationReport() { FailureTime = t, RepairTimes = repairTimeList, TimeDiagram = diagram }); }