public MemoryStream BuildBalanceFreeHier(BalanceFreeHierarchyCalculatedResult balanceCalculatedResult)
        {
            if (balanceCalculatedResult == null)
            {
                return(null);
            }

            bool isHeaderFormed;
            var  xls = InitBalanceFreeHierarchy(balanceCalculatedResult.BalanceFreeHierarchyUn, out isHeaderFormed);

            //Формируем балансы в зависимости от выбранного типа
            switch (balanceCalculatedResult.DocumentType)
            {
            case EnumBalanceFreeHierarchyType.БалансНаПодстанции:
            case EnumBalanceFreeHierarchyType.БалансЭлектростанции:
                return(ФормируемПодстанцийЭлектростанций(xls, balanceCalculatedResult, isHeaderFormed));

                break;

            case EnumBalanceFreeHierarchyType.АктУчетаЭэ:
                return(ФормируемАктУчетаЭэ(xls, balanceCalculatedResult, isHeaderFormed));

                break;

            case EnumBalanceFreeHierarchyType.Приложение51:
                return(ФормируемПриложение51(xls, balanceCalculatedResult, isHeaderFormed));

                break;

            case EnumBalanceFreeHierarchyType.СводныйИнтегральныйАкт:
                return(ФормируемСводныйИнтегральныйАкт(balanceCalculatedResult));

                break;

            default:
                lock (_errors)
                {
                    _errors.Append(balanceCalculatedResult.DocumentName + " - документ не создан");
                }

                break;
            }

            return(null);
        }
        private void  асчетПодстанцийЭлектростанций(BalanceFreeHierarchyCalculatedResult balanceResult)
        {
            var calculatedByDiscretePeriods = balanceResult.CalculatedByDiscretePeriods;

            #region Обсчитываем трансформаторы

            foreach (var transformator in balanceResult.Transformators)
            {
                transformator.CalculateLosses(_archiveHalfhours.ResultCommonForTransformatorsAndReactors,
                                              _numbersHalfHours, null, _errors, _dtStart, _dtEnd, _unitDigit);
                transformator.CalculateLossesByDiscretePeriod(enumTimeDiscreteType.DBHalfHours, _dtStart, _dtEnd,
                                                              _intervalTimeList);
            }

            #endregion

            #region Обсчитываем реакторы

            foreach (var reactor in balanceResult.Reactors)
            {
                reactor.CalculateLosses(_archiveHalfhours.ResultCommonForTransformatorsAndReactors, 0.5, null);
            }

            #endregion

            var ovInfos          = new List <IOV_Values>();    //Информация по ОВ которую нужно дополнить дополнительно
            var ovIdsForIntegral = new List <TI_ChanelType>(); //Идентификаторы ОВ для дозапросов и дорасчетов интегралов по ним

            foreach (var paramPair in balanceResult.ItemsParamsBySection)
            {
                foreach (var itemParam in paramPair.Value)
                {
                    _archiveHalfhours.PopulateArchivesByHierarchyType(itemParam, _tiForRequestAdditionalInfo,
                                                                      _discreteType, //В данном документе работаем только с получасовками
                                                                      true, balanceResult.Transformators, balanceResult.Reactors, _numbersHalfHours);

                    if (itemParam.OV_Values_List != null && itemParam.OV_Values_List.Count > 0)
                    {
                        ovInfos.AddRange(itemParam.OV_Values_List);
                    }
                }
            }

            //GetOvInfos(ovInfos);

            List <Dict_Balance_FreeHierarchy_Section> sections;
            SectionsByType.TryGetValue(balanceResult.BalanceFreeHierarchyType, out sections);
            if (sections == null)
            {
                return;
            }

            var dopustNebalnsUn = sections.First(s => Equals(s.MetaString1, "dopust_nebalns"))
                                  .BalanceFreeHierarchySection_UN;

            var hlv = balanceResult.HighLimitValue;
            var llv = balanceResult.LowerLimitValue;
            var hlp = balanceResult.HighLimit;
            var llp = balanceResult.LowerLimit;

            var balanceResultInfo = balanceResult.ResultInfo;

            //Для обсчетов итогов в заданном периоде дискретизации
            var discretePeriodStepOfReadHalfHours         = 0;
            var discretePeriodNumbersHalfHoursInOurPeriod = _intervalTimeList.FirstOrDefault();

            var ourPeriodCounter = 0;

            var sectionValues = sections.ToDictionary(k => k.BalanceFreeHierarchySection_UN, v => 0.0);

            var discretePeriodOtpusk    = 0.0;
            var discretePeriodPostupilo = 0.0;
            var discretePeriodFlag      = VALUES_FLAG_DB.None;
            var totalFlag          = VALUES_FLAG_DB.None;
            var totalBalanceStatus = EnumBalanceStatus.None;
            var totalOtpusk        = 0.0;
            var totalPostupilo     = 0.0;

            var    factUnBalancePercentAverage = 0.0;
            double?factUnBalancePercentMax     = null;
            double?factUnBalancePercentMin     = null;
            var    factUnBalancePercentMaxIndx = 0.0;
            var    factUnBalancePercentMinIndx = 0.0;

            for (var archiveIndex = 0; archiveIndex < _numbersHalfHours; archiveIndex++)
            {
                //Обсчитываем всё в рамках одной получасовки
                var o    = 0.0;
                var p    = 0.0;
                var flag = VALUES_FLAG_DB.None;

                #region Считаем сумму по подгруппам участвующим в балансе

                foreach (var paramPair in balanceResult.ItemsParamsBySection)
                {
                    var sectionUn   = paramPair.Key;
                    var halfhourSum = 0.0;
                    var integralSum = 0.0;

                    foreach (var itemParam in paramPair.Value)
                    {
                        if (!itemParam.IsInput && !itemParam.IsOutput || itemParam.HalfHours == null)
                        {
                            continue; //Объект не участвует в балансе
                        }
                        if (archiveIndex == 0)
                        {
                            integralSum = _archiveHalfhours.GetIntegral(itemParam);
                        }

                        var aVal = itemParam.HalfHours.ElementAtOrDefault(archiveIndex);

                        if (aVal == null || !itemParam.Coef.HasValue || itemParam.Coef.Value == 0)
                        {
                            continue;
                        }

                        var v = aVal.F_VALUE * itemParam.Coef.Value;
                        halfhourSum += v; //Сумма по подгруппе

                        if (itemParam.IsInput)
                        {
                            p += v;
                        }
                        else if (itemParam.IsOutput)
                        {
                            o += v;
                        }

                        flag = flag.CompareAndReturnMostBadStatus(aVal.F_FLAG);

                        itemParam.F_FLAG = itemParam.F_FLAG.CompareAndReturnMostBadStatus(aVal.F_FLAG);
                    }

                    sectionValues[sectionUn] += halfhourSum;
                }

                #endregion

                var    halfHourBalanceStatus = EnumBalanceStatus.None;
                double halfHourFactUnbalancePercent;
                double halfHourFactUnbalanceValues;
                double?halfHourResolvedUnbalancePercent;

                #region Смотрим превышение

                CalculateBalanceInfo(balanceResult.ItemsParamsBySection.Values, p, o, archiveIndex, 1,
                                     out halfHourFactUnbalancePercent, out halfHourFactUnbalanceValues,
                                     out halfHourResolvedUnbalancePercent);

                if (halfHourResolvedUnbalancePercent.HasValue)
                {
                    sectionValues[dopustNebalnsUn] += halfHourResolvedUnbalancePercent.Value;
                }

                if (hlv.HasValue && halfHourFactUnbalanceValues > hlv.Value)
                {
                    halfHourBalanceStatus |= EnumBalanceStatus.ExcessHiValueLimit;
                }
                else if (llv.HasValue && halfHourFactUnbalanceValues < llv.Value)
                {
                    halfHourBalanceStatus |= EnumBalanceStatus.ExcessLoValueLimit;
                }

                if (hlp.HasValue && halfHourFactUnbalancePercent > hlp.Value)
                {
                    halfHourBalanceStatus |= EnumBalanceStatus.ExcessHiPercentLimit;
                }
                else if (llp.HasValue && halfHourFactUnbalancePercent < llp.Value)
                {
                    halfHourBalanceStatus |= EnumBalanceStatus.ExcessLoPercentLimit;
                }

                if (Math.Abs(halfHourFactUnbalancePercent) - halfHourResolvedUnbalancePercent > 0)
                {
                    halfHourBalanceStatus |= EnumBalanceStatus.Unbalance;
                    //    balanceResult.FactUnbalanceInfo.UnbalanceDates.Add(timeList.ElementAtOrDefault(archiveIndex));
                }

                #endregion

                //Информация для графика
                balanceResult.BalanceHalfhours.Add(new TBalanceHalfhour
                {
                    BalanceStatus            = halfHourBalanceStatus,
                    FactUnBalancePercent     = halfHourFactUnbalancePercent,
                    FactUnBalanceValue       = halfHourFactUnbalanceValues,
                    ResolvedUnBalancePercent = halfHourResolvedUnbalancePercent.GetValueOrDefault(),
                    Flag = flag,
                });

                #region Накапливаем состояния для заданного периода дискретизации

                discretePeriodPostupilo += p;
                discretePeriodOtpusk    += o;
                discretePeriodFlag       = discretePeriodFlag.CompareAndReturnMostBadStatus(flag);
                totalBalanceStatus      |= halfHourBalanceStatus;

                #endregion

                #region Итоговые состояния

                factUnBalancePercentAverage = (halfHourFactUnbalancePercent + factUnBalancePercentAverage * archiveIndex) / (archiveIndex + 1);

                if (!factUnBalancePercentMax.HasValue || factUnBalancePercentMax < halfHourFactUnbalancePercent)
                {
                    factUnBalancePercentMax     = halfHourFactUnbalancePercent;
                    factUnBalancePercentMaxIndx = archiveIndex;
                }

                if (!factUnBalancePercentMin.HasValue || factUnBalancePercentMin > halfHourFactUnbalancePercent)
                {
                    factUnBalancePercentMin     = halfHourFactUnbalancePercent;
                    factUnBalancePercentMinIndx = archiveIndex;
                }

                #endregion

                #region Накапливаем результат в нужном нам периоде дискретизации

                if (discretePeriodStepOfReadHalfHours == discretePeriodNumbersHalfHoursInOurPeriod) //Закрываем нужный нам период дискретизвции
                {
                    double discretePeriodFactUnbalancePercent;
                    double discretePeriodFactUnbalanceValues;
                    double?discretePeriodResolvedUnbalancePercent;

                    if (_discreteType == enumTimeDiscreteType.DBHalfHours)
                    {
                        //Нужны получасовки, ничего дорасчитывать не нужно
                        discretePeriodFactUnbalancePercent     = halfHourFactUnbalancePercent;
                        discretePeriodFactUnbalanceValues      = halfHourFactUnbalanceValues;
                        discretePeriodResolvedUnbalancePercent = halfHourResolvedUnbalancePercent.GetValueOrDefault();
                    }
                    else
                    {
                        //Подитоги нужно дорасчитать только в нужном периоде дискретизации
                        CalculateBalanceInfo(balanceResult.ItemsParamsBySection.Values, discretePeriodPostupilo,
                                             discretePeriodOtpusk, archiveIndex, discretePeriodNumbersHalfHoursInOurPeriod + 1,
                                             out discretePeriodFactUnbalancePercent, out discretePeriodFactUnbalanceValues,
                                             out discretePeriodResolvedUnbalancePercent);
                    }

                    //Поитог по периоду дискретизации, для Excel и таблицы
                    calculatedByDiscretePeriods.Add(new BalanceFreeHierarchyCalculatedDiscretePeriod
                    {
                        SectionValues = sectionValues,

                        //Фактический небаланс ((I-(II+III)-IV-V - Потери автортансформаторов)/I)*100%
                        FactUnbalancePercent = discretePeriodFactUnbalancePercent,

                        //Небаланс, кВт*ч
                        FactUnbalanceValue = discretePeriodFactUnbalanceValues,

                        //Допустимый небаланс (VII), %
                        ResolvedUnBalanceValues = discretePeriodResolvedUnbalancePercent.GetValueOrDefault(),

                        //Отпуск и расход + трансформаторы и реакторы
                        OutValues = discretePeriodOtpusk,

                        //Поступило на шины, всего (I)
                        InBusValues = discretePeriodPostupilo,

                        F_FLAG = discretePeriodFlag,
                    });

                    //Накапливаем для общего итога
                    totalOtpusk    += discretePeriodOtpusk;
                    totalPostupilo += discretePeriodPostupilo;
                    totalFlag       = discretePeriodFlag.CompareAndReturnMostBadStatus(discretePeriodFlag);

                    if (++ourPeriodCounter < _intervalTimeList.Count)
                    {
                        //Сбрасываем накопленные состояния для накопления в последующем периоде (час, сутки или месяц)
                        discretePeriodNumbersHalfHoursInOurPeriod = _intervalTimeList[ourPeriodCounter];
                        discretePeriodFlag = VALUES_FLAG_DB.None;
                        discretePeriodStepOfReadHalfHours = 0;
                        discretePeriodOtpusk    = 0.0;
                        discretePeriodPostupilo = 0.0;
                        sectionValues           = sections.ToDictionary(k => k.BalanceFreeHierarchySection_UN, v => 0.0);
                        //totalBalanceStatus = EnumBalanceStatus.None;
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    discretePeriodStepOfReadHalfHours++; //Дальше считаем получасовки до нужного нам количества
                }

                #endregion
            } //Перебираем получасовки

            #region Общий итог

            balanceResultInfo.FactUnBalancePercentAverage = factUnBalancePercentAverage;
            balanceResultInfo.FactUnBalancePercentMax     = factUnBalancePercentMax;
            balanceResultInfo.FactUnBalancePercentMin     = factUnBalancePercentMin;

            if (factUnBalancePercentMax.HasValue)
            {
                balanceResultInfo.FactUnBalancePercentMaxDt = _dtServerStart.ServerToUtc()
                                                              .AddMinutes(factUnBalancePercentMaxIndx * 30).UtcToClient(_timeZoneId);
            }

            if (factUnBalancePercentMin.HasValue)
            {
                balanceResultInfo.FactUnBalancePercentMinDt = _dtServerStart.ServerToUtc()
                                                              .AddMinutes(factUnBalancePercentMinIndx * 30).UtcToClient(_timeZoneId);
            }

            double totalFactUnbalancePercent;
            double totalFactUnbalanceValue;
            double?totalResolvedUnbalancePercent;

            if (_discreteType != enumTimeDiscreteType.DBInterval || calculatedByDiscretePeriods.Count == 0)
            {
                CalculateBalanceInfo(balanceResult.ItemsParamsBySection.Values, totalPostupilo, totalOtpusk,
                                     _numbersHalfHours - 1, _numbersHalfHours,
                                     out totalFactUnbalancePercent, out totalFactUnbalanceValue, out totalResolvedUnbalancePercent);
            }
            else
            {
                //Для интервального общий итог не нужно пересчитывать, уже посчитали
                var cv = calculatedByDiscretePeriods.First();
                totalFactUnbalancePercent     = cv.FactUnbalancePercent;
                totalFactUnbalanceValue       = cv.FactUnbalanceValue;
                totalResolvedUnbalancePercent = cv.ResolvedUnBalanceValues;
            }

            balanceResultInfo.FactUnBalancePercent     = totalFactUnbalancePercent;
            balanceResultInfo.FactUnBalanceValue       = totalFactUnbalanceValue;
            balanceResultInfo.ResolvedUnBalancePercent = totalResolvedUnbalancePercent;
            balanceResultInfo.BalanceStatus            = totalBalanceStatus & (~EnumBalanceStatus.Unbalance); //Статус небаланса берем не из получасовок
            balanceResultInfo.TotalTiFlag = totalFlag;


            #endregion

            if (Math.Abs(balanceResultInfo.FactUnBalancePercent.GetValueOrDefault()) -
                balanceResultInfo.ResolvedUnBalancePercent > 0)
            {
                balanceResultInfo.BalanceStatus |= EnumBalanceStatus.Unbalance;
            }
        }