public Polskaya(PolskaParams polskaParams, IDBInterfaceAdapter nameInterface, IFormulaArchivesPrecalculator formulaArchivesPrecalculator, Dictionary <string, Variable> variablesDict) { _polskaParams = polskaParams; NameInterface = nameInterface; _operationStack = new List <PolskayaVariable>(MemoryAllockStep_bytes); OutString = new List <PolskayaVariable>(); Expression = new StringBuilder(); _variablesDict = variablesDict; #region Ф-ии CreateFunction(new MinFunction()); CreateFunction(new MaxFunction()); CreateFunction(new SinFunction()); CreateFunction(new CosFunction()); CreateFunction(new PowFunction()); CreateFunction(new ExpFunction()); CreateFunction(new LogFunction()); CreateFunction(new IfFunction()); CreateFunction(new MacroIfFunction()); CreateFunction(new RoundFunction()); CreateFunction(new ValueStatusCodeContains()); CreateFunction(new ValueStatusCodeContainsAllAlarmStatuses()); #endregion // Добавляем возможность использовать пользовательский функции CreateFunction(new GetHalfHourIndex()); CreateFunction(new UserDefinedSQLFunction(nameInterface)); CreateFunction(new UserDefinedCSharpFunction()); CreateFunction(new GetHalfHourDateTime()); CreateFunction(new AbsFunction()); #region Добавление ф-ий которые требуют предварительной подгрузки данных _precalculatedFunctionDict = new Dictionary <string, PrecalculatedFunctionVariable>(); foreach (var precalculatedFormulasFunction in Enum.GetNames(typeof(EnumFormulasFunction)) .Select(s => (EnumFormulasFunction)Enum.Parse(typeof(EnumFormulasFunction), s)).Select( formulasFunction => formulasFunction.CreatePrecalculatedFormulasFunctionDescription(formulaArchivesPrecalculator))) { _precalculatedFunctionDict[precalculatedFormulasFunction.Id] = precalculatedFormulasFunction; } _precalculatedFunctionBooleanDict = new Dictionary <string, Function>(); var f = new IfEnabledFunction(); _precalculatedFunctionBooleanDict[f.id] = f; #endregion }
/// <summary> /// Проверка формулы /// </summary> /// <param name="formulaParams">формула</param> /// <param name="dataSourceType">Источник данных</param> public void CheckFormula(IFormulaParam formulaParams) { _recursionCallStack.Clear(); int?tpId = null; if (formulaParams.tP_CH_ID != null) { tpId = formulaParams.tP_CH_ID.TP_ID; } var formula = GetFormulaByID(formulaParams.FormulaID); var polskaParams = new PolskaParams(formula.F_ID, _formulasTable, formulaParams.StartDateTime, formulaParams.FinishDateTime.GetValueOrDefault(), enumTimeDiscreteType.DBHalfHours, 1, null); var parser = ComposeExpressionPolskaya(formula, polskaParams, 0, 1, 1, false); parser.EvaluateStringValue(0); }
/// <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; }
private Polskaya ComposeExpressionPolskaya(FORMULA f, PolskaParams polskaParams , int indxStart, int indxEnd, int numbersHalfHours, bool isCalculateBetweenIndexes = true) { var parser = new Polskaya(polskaParams, _nameInterface, _formulaArchivesPrecalculator, _variablesDict); if (_recursionCallStack.Contains(f.F_ID)) { throw new FormulaParseException("Обнаружена рекурсия формулы\n[" + GetOperNameFromDB(f.F_ID, F_OPERATOR.F_OPERAND_TYPE.Formula, null) + "]"); } _recursionCallStack.Add(f.F_ID); foreach (F_OPERATOR operators in f.F_OPERATORS) { switch (operators.OPER_TYPE) { case F_OPERATOR.F_OPERAND_TYPE.Section: case F_OPERATOR.F_OPERAND_TYPE.TP_channel: case F_OPERATOR.F_OPERAND_TYPE.ContrTI_Chanel: case F_OPERATOR.F_OPERAND_TYPE.Integral_Channel: case F_OPERATOR.F_OPERAND_TYPE.TI_channel: case F_OPERATOR.F_OPERAND_TYPE.FormulaConstant: case F_OPERATOR.F_OPERAND_TYPE.UANode: if (!parser.ContainsVariable(operators.Name)) { Variable var; if (Archives == null) { // режим поиска параметров и проверки правильности var = new Variable(operators.Name, indxStart, indxEnd, isCalculateBetweenIndexes: isCalculateBetweenIndexes); } else { IGetAchives data = Archives.GetArchiveByOperandType(operators); if (data == null && !Archives.IsArchTech) { if (operators.OPER_TYPE == F_OPERATOR.F_OPERAND_TYPE.TP_channel) { throw new FormulaParseException("Расчет формулы невозможен. Не найдено значение для ТП \"" + GetOperNameFromDB(operators.OPER_ID, operators.OPER_TYPE, operators.TI_CHANNEL) + "\"\nНе описан канал или не найдена для него формула."); } throw new FormulaParseException("Расчет формулы невозможен. Отсутствуют значения для \"" + GetOperNameFromDB(operators.OPER_ID, operators.OPER_TYPE, operators.TI_CHANNEL) + "\"\n из формулы <" + GetOperNameFromDB(f.F_ID, F_OPERATOR.F_OPERAND_TYPE.Formula, null) + ">"); } var = new Variable(operators.Name, indxStart, indxEnd, data, operators.TI_CHANNEL, isCalculateBetweenIndexes); } parser.CreateVariable(var); } parser.Expression.Append(operators.PRE_OPERANDS ?? "").Append(operators.Name).Append(operators.AFTER_OPERANDS ?? ""); break; case F_OPERATOR.F_OPERAND_TYPE.Formula: parser.Expression.Append(operators.PRE_OPERANDS ?? "").Append(operators.Name).Append(operators.AFTER_OPERANDS ?? ""); if (!parser.ContainsVariable(operators.Name)) { var innerFId = GetFormulaByID(operators.OPER_ID); if (innerFId != null) { var p = new PolskaParams(innerFId.F_ID, _formulasTable, polskaParams.StartDateTime, polskaParams.EndDateTime, polskaParams.DiscreteType, numbersHalfHours, innerFId.UnitDigit); var formulaExpression = ComposeExpressionPolskaya(innerFId, p, indxStart, indxEnd, numbersHalfHours, isCalculateBetweenIndexes); //Время дейстаия внутренней формулы берем из основной var var = new Variable(operators.Name, indxStart, indxEnd, formulaExpression, isCalculateBetweenIndexes); parser.CreateVariable(var); } else { parser.CreateVariable(new Variable(operators.Name, -1, -1)); } } break; case F_OPERATOR.F_OPERAND_TYPE.Constanta: parser.Expression.Append(operators.PRE_OPERANDS).Append(operators.AFTER_OPERANDS ?? ""); break; case F_OPERATOR.F_OPERAND_TYPE.None: if (string.IsNullOrEmpty(operators.PRE_OPERANDS) && string.IsNullOrEmpty(operators.AFTER_OPERANDS)) { throw new FormulaParseException("Формула не описана!"); } parser.Expression.Append(operators.PRE_OPERANDS ?? "").Append(operators.AFTER_OPERANDS ?? ""); break; default: throw new FormulaParseException("Неизвестный тип оператора!"); } } _recursionCallStack.Remove(f.F_ID); return(parser); }