/// <summary>
        /// Делаем расчет по формуле
        /// </summary>
        /// <param name="formulaId">Идентификатор формулы</param>
        /// <param name="inDataTI">Данные по ТИ</param>
        /// <param name="inDataIntegrals">Интегральные данные</param>
        /// <param name="inDataTP">Данные по ТП</param>
        /// <param name="inDataSection">Данные по сечению</param>
        /// <param name="indxStart">Начальный индекс, с которого формула активна</param>
        /// <param name="indxEnd">Конечный индекс</param>
        /// <param name="unitDigitCoeff">Коэфф. дорасчета ед. измерения</param>
        /// <param name="formulaTPType">Тип формулы ТП</param>
        /// <param name="formulasTable">Табличный тип формулы (обычная или ТП)</param>
        /// <param name="tpId">Идентификатор ТП к которой привязана формула</param>
        /// <param name="rangeInSectionForEntirePeriod">Используется ли формула в сечении, где действует ограничение на период действия ТП</param>
        /// <param name="rangeIndexesInSectionList">Индексы с которого действуют ТП в сечении</param>
        /// <param name="hiHiSetpoint">Верхняя уставка, по которой проверяем формулу</param>
        /// <param name="loLoSetpoint">Нижняя уставка, по которой проверяем формулу</param>
        /// <param name="inFormulaConstants">Данные по константам, используемым в формуле</param>
        /// <param name="formulasDisabledPeriods">Индексы получасовок между которыми формула не считается</param>
        /// <returns></returns>
        public List <TVALUES_DB> InterpretFormula(string formulaId
                                                  , bool isSumm
                                                  , int indxStart, int indxEnd, double unitDigitCoeff, enumClientFormulaTPType formulaTPType,
                                                  enumFormulasTable formulasTable, bool rangeInSectionForEntirePeriod = true,
                                                  IEnumerable <IPeriodIndexesTpInSection> rangeIndexesInSectionList   = null, double?hiHiSetpoint = null, double?loLoSetpoint = null,
                                                  IEnumerable <IFormulasDisabledPeriod> formulasDisabledPeriods       = null,
                                                  string measureUnitUn = null, PeriodFactory manualEnteredHalfHourIndexes = null)
        {
            var isCalculateBetweenIndexes = !InterpretatorParams.TechProfilePeriod.HasValue && (indxStart > 0 || indxEnd < (InterpretatorParams.NumbersHalfHours - 1));

            var isExistDisablePeriod = formulasDisabledPeriods != null && formulasDisabledPeriods.ToList().Count > 0;

            _recursionCallStack.Clear();

            double?coeff;

            if (string.IsNullOrEmpty(measureUnitUn))
            {
                if (unitDigitCoeff > 1)
                {
                    coeff = 1 / unitDigitCoeff;
                    if (InterpretatorParams.TypeInformation == enumTypeInformation.Power)
                    {
                        coeff *= 2;
                    }
                }
                else if (InterpretatorParams.TypeInformation == enumTypeInformation.Power)
                {
                    coeff = 2;
                }
                else
                {
                    coeff = null;
                }
            }
            else
            {
                coeff = null;
            }

            _formulasTable = formulasTable;

            var formula = GetFormulaByID(formulaId);

            var polskaParams = new PolskaParams(formula.F_ID, _formulasTable,
                                                InterpretatorParams.StartDateTime, InterpretatorParams.EndDateTime, InterpretatorParams.DiscreteType, InterpretatorParams.NumbersHalfHours, formula.UnitDigit, InterpretatorParams.TechProfilePeriod.HasValue);

            var parser = ComposeExpressionPolskaya(formula, polskaParams, indxStart, indxEnd, InterpretatorParams.NumbersHalfHours, isCalculateBetweenIndexes);

            parser.Compile();

            //var result = new List<TVALUES_DB>();

            using (var accamulator = new FormulaAccamulator(InterpretatorParams.IntervalTimeList, isSumm, formula.UnitDigit))
            {
                //Считаем всегда по получасовкам
                for (var halfHourIndex = 0; halfHourIndex < InterpretatorParams.NumbersHalfHours; halfHourIndex++)
                {
                    // рассчет по формуле
                    if (!isCalculateBetweenIndexes || halfHourIndex >= indxStart && halfHourIndex <= indxEnd)
                    {
                        #region Ограничиваем диапазоном в сечении

                        if (!rangeInSectionForEntirePeriod)
                        {
                            if (!rangeIndexesInSectionList.Any(range => range.StartIndex <= halfHourIndex && (range.FinishIndex ?? InterpretatorParams.NumbersHalfHours - 1) >= halfHourIndex))
                            {
                                accamulator.Accamulate(0, VALUES_FLAG_DB.TpNotInSectionRange, enumClientFormulaTPType.TpNotInSectionRange);
                                continue;
                            }
                        }

                        #endregion

                        #region Ограничиваем когда формула заблокирована напряму таблицей Info_Formula_DisabledPeriod

                        if (isExistDisablePeriod)
                        {
                            if (formulasDisabledPeriods.Any(range => halfHourIndex >= range.StartIndx && halfHourIndex <= range.FinishIndx))
                            {
                                accamulator.Accamulate(0, VALUES_FLAG_DB.FormulaNotInRange);
                                //result.Add(new Formula_VALUES_DB(VALUES_FLAG_DB.FormulaNotInRange, 0));
                                continue;
                            }
                        }

                        #endregion

                        #region Ограничиваем периодами, когда данные были введены вручную (считать не нужно)

                        if (manualEnteredHalfHourIndexes != null && manualEnteredHalfHourIndexes.HavePeriod(halfHourIndex))
                        {
                            //result.Add(new Formula_VALUES_DB(VALUES_FLAG_DB.None, 0));
                            accamulator.Accamulate(0, VALUES_FLAG_DB.None);
                            continue;
                        }

                        #endregion

                        var val = parser.EvaluateStringValue(halfHourIndex);

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

                        if (hiHiSetpoint.HasValue && val.F_VALUE >= hiHiSetpoint)
                        {
                            val.F_FLAG |= VALUES_FLAG_DB.HiHiSetpointExcess;
                        }
                        if (loLoSetpoint.HasValue && val.F_VALUE <= loLoSetpoint)
                        {
                            val.F_FLAG |= VALUES_FLAG_DB.LoLoSetpointExcess;
                        }

                        #endregion

                        //Учитываем что это мощность или размерность (кило, мега и т.д.)
                        if (coeff.HasValue)
                        {
                            val.F_VALUE *= coeff.Value;
                        }

                        val.FormulaTPType = formulaTPType;

                        accamulator.Accamulate(val.F_VALUE, val.F_FLAG, formulaTPType);
                        //result.Add(val.F_VALUE, val.F_FLAG);
                    }
                    else
                    {
                        accamulator.Accamulate(0, VALUES_FLAG_DB.FormulaNotInRange, enumClientFormulaTPType.NotInRange);
                        //Ограничения по времени действия формулы
                        //result.Add(new Formula_VALUES_DB(VALUES_FLAG_DB.FormulaNotInRange, 0, enumClientFormulaTPType.NotInRange));
                    }
                }

                return(accamulator.Result);
            }

            //return result;
        }
        /// <summary>
        /// Набор расчетных параметров для обсчета формулы
        /// </summary>
        /// <param name="formulaParams"></param>
        /// <param name="resultTi"></param>
        /// <param name="resultIntegral"></param>
        /// <param name="resultTp"></param>
        /// <param name="resultSection"></param>
        public void BuildVariableParams(IFormulaParam formulaParams, string msTimeZoneId = null, bool isArchTech = false)
        {
            if (_recursionCallStack.Contains(formulaParams.FormulaID))
            {
                throw new FormulaParseException("Обнаружена рекурсия формулы\n[" + GetOperNameFromDB(formulaParams.FormulaID, F_OPERATOR.F_OPERAND_TYPE.Formula, null) + "]");
            }

            _recursionCallStack.Add(formulaParams.FormulaID);

            _formulasTable = formulaParams.FormulasTable;

            int?tpId = null;

            if (formulaParams.tP_CH_ID != null)
            {
                tpId = formulaParams.tP_CH_ID.TP_ID;
            }

            if (Archives == null)
            {
                Archives = new FormulaArchives(isArchTech, tpId);
            }

            //Часовой пояс в котором запрашиваем данные
            //var tz = (string.IsNullOrEmpty(msTimeZoneId) ? formulaParams.MsTimeZoneId : msTimeZoneId);

            var f = GetFormulaByID(formulaParams.FormulaID);

            if (f == null)
            {
                //Ф-ла не описана, либо не входит в наш диапазон
                _recursionCallStack.Remove(formulaParams.FormulaID);
                return;
            }

            foreach (var operators in f.F_OPERATORS)
            {
                var operIdInt = 0;

                if (string.IsNullOrEmpty(operators.OPER_ID))
                {
                    continue;
                }

                if (operators.OPER_TYPE != F_OPERATOR.F_OPERAND_TYPE.Formula && operators.OPER_TYPE != F_OPERATOR.F_OPERAND_TYPE.FormulaConstant)
                {
                    if (int.TryParse(operators.OPER_ID, out operIdInt) == false)
                    {
                        throw new FormulaParseException("Идентификатор ТИ [" + operators.OPER_ID + "] должен быть целочисленным!");
                    }

                    if (!operators.TI_CHANNEL.HasValue)
                    {
                        throw new FormulaParseException("В формуле не указан канал измерения!");
                    }
                }

                switch (operators.OPER_TYPE)
                {
                case F_OPERATOR.F_OPERAND_TYPE.UANode:
                    Archives.FormulaUaNodeVariableDataTypeList.Add(new TUANodeDataId
                    {
                        UANodeId = operIdInt,
                        DataType = (UANodeDataIdDataTypeEnum)operators.TI_CHANNEL.GetValueOrDefault()
                    });
                    break;

                case F_OPERATOR.F_OPERAND_TYPE.Section:
                    Archives.SectionSorted.Add(new TSectionChannel {
                        Section_ID = operIdInt, ChannelType = operators.TI_CHANNEL.Value
                    });
                    break;

                case F_OPERATOR.F_OPERAND_TYPE.TP_channel:
                    HashSet <TP_ChanelType> tpChannelTypes;
                    if (!Archives.TPChanelTypeList.TryGetValue(operIdInt, out tpChannelTypes) || tpChannelTypes == null)
                    {
                        Archives.TPChanelTypeList[operIdInt] = tpChannelTypes = new HashSet <TP_ChanelType>(new TP_ChanelComparer());
                    }

                    tpChannelTypes.Add(new TP_ChanelType
                    {
                        TP_ID           = operIdInt,
                        ChannelType     = operators.TI_CHANNEL.Value,
                        ClosedPeriod_ID = formulaParams.ClosedPeriod_ID,
                    });
                    break;

                case F_OPERATOR.F_OPERAND_TYPE.Integral_Channel:
                    HashSet <TI_ChanelType> integralChannelTypes;
                    if (!Archives.IntegralChanelTypeList.TryGetValue(operIdInt, out integralChannelTypes) || integralChannelTypes == null)
                    {
                        Archives.IntegralChanelTypeList[operIdInt] = integralChannelTypes = new HashSet <TI_ChanelType>(new ITI_ChanelComparer());
                    }

                    integralChannelTypes.Add(new TI_ChanelType
                    {
                        TI_ID           = operIdInt,
                        ChannelType     = operators.TI_CHANNEL.Value,
                        IsCA            = operators.OPER_TYPE == F_OPERATOR.F_OPERAND_TYPE.ContrTI_Chanel,
                        TP_ID           = tpId,
                        DataSourceType  = InterpretatorParams.DataSourceType,
                        ClosedPeriod_ID = formulaParams.ClosedPeriod_ID,
                        //MsTimeZone = tz,
                    });
                    break;

                case F_OPERATOR.F_OPERAND_TYPE.ContrTI_Chanel:
                case F_OPERATOR.F_OPERAND_TYPE.TI_channel:
                    HashSet <TI_ChanelType> tiChannelTypes;
                    if (!Archives.TIChanelTypeList.TryGetValue(operIdInt, out tiChannelTypes) || tiChannelTypes == null)
                    {
                        Archives.TIChanelTypeList[operIdInt] = tiChannelTypes = new HashSet <TI_ChanelType>(new ITI_ChanelComparer());
                    }

                    tiChannelTypes.Add(new TI_ChanelType
                    {
                        TI_ID           = operIdInt,
                        ChannelType     = operators.TI_CHANNEL.Value,
                        IsCA            = operators.OPER_TYPE == F_OPERATOR.F_OPERAND_TYPE.ContrTI_Chanel,
                        TP_ID           = tpId,
                        DataSourceType  = InterpretatorParams.DataSourceType,
                        ClosedPeriod_ID = formulaParams.ClosedPeriod_ID,
                        //MsTimeZone = tz,
                    });
                    break;

                case F_OPERATOR.F_OPERAND_TYPE.FormulaConstant:
                    Archives.FormulaConstantIds.Add(operators.OPER_ID);
                    break;

                case F_OPERATOR.F_OPERAND_TYPE.Formula:

                    IFormulaParam fId = new FormulaParam
                    {
                        FormulaID     = operators.OPER_ID,
                        FormulasTable = _formulasTable,
                        tP_CH_ID      = formulaParams.tP_CH_ID,
                        MsTimeZoneId  = formulaParams.MsTimeZoneId
                    };

                    //Собираем переменные из вложенной формулы
                    BuildVariableParams(fId, msTimeZoneId);

                    break;
                }
            }

            _recursionCallStack.Remove(formulaParams.FormulaID);
        }