/// <summary> /// Округление и формирование остатков по суткам для сохранения /// </summary> /// <param name="av">ТИ</param> /// <param name="tiResidues">Остатки от округления с предыдущих суток</param> /// <returns></returns> public void CalculateResiudes(TArchivesValue av, DBResiduesTable prevDayResiude, bool isCalculateAnyway = false, bool useLossesCoefficient = false, bool isNotRecalculateResiudes = false) { if (av.Val_List == null || av.Val_List.Count == 0) { return; } if (!isCalculateAnyway || (!IsRecalculateResiduesAlways && prevDayResiude == null)) { _tiForRecalculateResiudes.Push(av); return; } var k = 1000.0 / (double)_unitDigit; //Округляем до кВт var reminder = 0.0; //Остаток от предыдущего периода if (prevDayResiude != null) { reminder = prevDayResiude.Val / 1000.0; } var indxHalfHour = 0; var indx24H = 0; var numbersInDay = _resiudes24Hindexes[indx24H]; //для часовых интервалов (80040) (в _resiudes24Hindexes - кол-во получасовых интервалов) if (_discreteType == enumTimeDiscreteType.DBHours) { numbersInDay = Convert.ToInt32(Math.Floor(numbersInDay / 2.0)); } //Пересчитываем с первой получасовки foreach (var tvaluesDb in av.Val_List) { var value = tvaluesDb.F_VALUE / k; var newValue = value + reminder; value = newValue < 0 ? 0 : Math.Round(newValue, MidpointRounding.AwayFromZero); tvaluesDb.F_VALUE = value * k; reminder = Math.Round(newValue - value, 3, MidpointRounding.AwayFromZero); //Здесь нужно округлять с большей точностью чем 3, иначе накапливается ошибка #region Формируем коллекцию для сохранения расчитанных остатков //if (!isNotRecalculateResiudes) { //переход на следующие сутки if (++indxHalfHour > numbersInDay) { indxHalfHour = 0; numbersInDay = _resiudes24Hindexes.ElementAtOrDefault(++indx24H); //для часовых интервалов (80040) if (_discreteType == enumTimeDiscreteType.DBHours) { numbersInDay = Convert.ToInt32(Math.Floor(numbersInDay / 2.0)); } TiForResaveResiudes.Push(new DBResiduesTable { EventDate = _dtStartServer.Date.AddDays(indx24H - 1), ChannelType = av.TI_Ch_ID.ChannelType, TI_ID = av.TI_Ch_ID.TI_ID, DataSourceType = av.DataSourceType, LatestDispatchDateTime = DateTime.Now, Val = reminder * 1000.0, UseLossesCoefficient = useLossesCoefficient, }); } } #endregion } }
public void RecalculateResiudes(TDRParams param, Action <TDRParams, ConcurrentStack <TArchivesValue>, List <TI_ChanelType>, bool> getArchivesAndCalculateResiudesFromMonthStartAction) { if (getArchivesAndCalculateResiudesFromMonthStartAction == null) { return; } #if DEBUG var sw = new System.Diagnostics.Stopwatch(); sw.Start(); #endif if (_tiForRecalculateResiudes != null && _tiForRecalculateResiudes.Count > 0) { //Перерасчет ТИ по которым небыло найдено остатков в предыдущем дне var tiArray = _tiForRecalculateResiudes .Select(t => new TI_ChanelType { TI_ID = t.TI_Ch_ID.TI_ID, ChannelType = t.TI_Ch_ID.ChannelType, ClosedPeriod_ID = t.TI_Ch_ID.ClosedPeriod_ID, DataSourceType = t.DataSourceType, IsCA = t.TI_Ch_ID.IsCA, MsTimeZone = t.TI_Ch_ID.MsTimeZone, TP_ID = t.TI_Ch_ID.TP_ID, }) .ToList(); var archivesValue30OrHour = new ConcurrentStack <TArchivesValue>(); //Дата/время с которого пересчитываем var dtStartMonthClient = param.DtServerStart.AddMonths(-MonthForRecalculateResidues); dtStartMonthClient = new DateTime(dtStartMonthClient.Year, dtStartMonthClient.Month, 1); var newParam = new TDRParams(param.IsCoeffEnabled, param.isCAEnabled, param.OvMode, param.isCAReverse, enumTimeDiscreteType.DBHalfHours, param.UnitDigit, param.IsPower, param.TIs, dtStartMonthClient.ClientToServer(param.ClientTimeZoneId), param.DtServerStart.AddMinutes(-30), MyListConverters.GetIntervalTimeList(dtStartMonthClient, param.DtServerStart.AddMinutes(-30).ServerToClient(param.ClientTimeZoneId), enumTimeDiscreteType.DBHalfHours, param.ClientTimeZoneId), param.ClientTimeZoneId, param.IsValidateOtherDataSource, param.IsReadCalculatedValues, param.IsReadAbsentChannel, param.RoundData) { IsNotRecalculateResiudes = true }; //Запрос данных с начала месяца getArchivesAndCalculateResiudesFromMonthStartAction(newParam, archivesValue30OrHour, tiArray, false); #if DEBUG sw.Stop(); Console.WriteLine("Запрос архивов для перерасчета - > ПУ: {0} шт, время: {1} млс", tiArray.Count, sw.ElapsedMilliseconds); sw.Restart(); #endif //if (newParam.RecalculatorResiudes != null && newParam.RecalculatorResiudes.TiForResaveResiudes.Count > 0) //{ //TiForResaveResiudes.PushRange(newParam.RecalculatorResiudes.TiForResaveResiudes.ToArray()); //} var prev24HEventDate = param.DtServerStart.AddMinutes(-30); Dictionary <int, List <DBResiduesTable> > resiudesDict = null; if (newParam.RecalculatorResiudes != null && newParam.RecalculatorResiudes.TiForResaveResiudes != null) { resiudesDict = newParam.RecalculatorResiudes.TiForResaveResiudes .GroupBy(r => r.TI_ID) .ToDictionary(k => k.Key, v => v.ToList()); } //Дорасчитываем остатки по ТИ у которых их нет в предыдущих сутках foreach (var av in _tiForRecalculateResiudes) { List <DBResiduesTable> resiudesTi; DBResiduesTable prevDayResiude = null; if (resiudesDict != null && resiudesDict.TryGetValue(av.TI_Ch_ID.TI_ID, out resiudesTi) && resiudesTi != null) { prevDayResiude = resiudesTi .FirstOrDefault(r => r.ChannelType == av.TI_Ch_ID.ChannelType && r.EventDate < prev24HEventDate && //Коллеция идет от Stack поэтому дата, время идет по убывающей r.DataSourceType == av.DataSourceType); } CalculateResiudes(av, prevDayResiude, true, param.UseLossesCoefficient, isNotRecalculateResiudes: param.IsNotRecalculateResiudes); } #if DEBUG sw.Stop(); Console.WriteLine("Расчет остатков - > {0} млс", sw.ElapsedMilliseconds); sw.Restart(); #endif } //Запускаем сохранение остатков и выходим if (TiForResaveResiudes.Count > 0) { Task.Factory.StartNew(() => SaveResiudes(TiForResaveResiudes, param.HalfHoursShiftClientFromServer, param.IsReadCalculatedValues)); } #if DEBUG sw.Stop(); Console.WriteLine("Сохранение остатков - > {0} млс", sw.ElapsedMilliseconds); #endif }