/// <summary> /// Цикл работы сервера (метод вызывается в отдельном потоке) /// </summary> private void Execute() { try { // запись информации о работе приложения workState = WorkStateNames.Normal; WriteInfo(); // выполнение действий модулей RaiseOnServerStart(); // инициализация адаптеров таблиц текущего среза и событий curSrezAdapter = new SrezAdapter(); curSrezCopyAdapter = new SrezAdapter(); eventAdapter = new EventAdapter(); eventCopyAdapter = new EventAdapter(); curSrezAdapter.FileName = ServerUtils.BuildCurFileName(Settings.ArcDir); curSrezCopyAdapter.FileName = ServerUtils.BuildCurFileName(Settings.ArcCopyDir); eventAdapter.Directory = Settings.ArcDir + "Events" + Path.DirectorySeparatorChar; eventCopyAdapter.Directory = Settings.ArcCopyDir + "Events" + Path.DirectorySeparatorChar; // инициализация кэша таблиц минутных и часовых срезов minSrezTableCache = new SortedList<DateTime, SrezTableCache>(); hrSrezTableCache = new SortedList<DateTime, SrezTableCache>(); // инициализация описания создаваемых срезов int cnlCnt = inCnls.Count; srezDescr = new SrezTable.SrezDescr(cnlCnt); for (int i = 0; i < cnlCnt; i++) srezDescr.CnlNums[i] = inCnls.Values[i].CnlNum; srezDescr.CalcCS(); // загрузка исходного текущего среза из файла SrezTableLight.Srez curSrezSrc = null; SrezTableLight tblCurSrezScr = new SrezTableLight(); try { if (File.Exists(curSrezAdapter.FileName)) { curSrezAdapter.Fill(tblCurSrezScr); if (tblCurSrezScr.SrezList.Count > 0) curSrezSrc = tblCurSrezScr.SrezList.Values[0]; } if (curSrezSrc == null) AppLog.WriteAction(Localization.UseRussian ? "Текущий срез не загружен" : "Current data are not loaded", Log.ActTypes.Action); else AppLog.WriteAction(Localization.UseRussian ? "Текущий срез загружен" : "Current data are loaded", Log.ActTypes.Action); } catch (Exception ex) { AppLog.WriteAction((Localization.UseRussian ? "Ошибка при загрузке текущего среза: " : "Error loading current data: ") + ex.Message, Log.ActTypes.Exception); } // инициализация текущего среза, предназначенного для формироваться данных сервера curSrez = new SrezTable.Srez(DateTime.MinValue, srezDescr, curSrezSrc); // инициализация данных для усреднения и времени активности каналов minAvgData = new AvgData[cnlCnt]; hrAvgData = new AvgData[cnlCnt]; activeDTs = new DateTime[cnlCnt]; DateTime nowDT = DateTime.Now; for (int i = 0; i < cnlCnt; i++) { minAvgData[i] = new AvgData() { Sum = 0.0, Cnt = 0 }; hrAvgData[i] = new AvgData() { Sum = 0.0, Cnt = 0 }; activeDTs[i] = nowDT; } // цикл работы сервера nowDT = DateTime.MaxValue; DateTime today = nowDT.Date; DateTime prevDT; DateTime writeCurSrezDT = DateTime.MinValue; DateTime writeMinSrezDT = DateTime.MinValue; DateTime writeHrSrezDT = DateTime.MinValue; DateTime calcMinDT = DateTime.MinValue; DateTime calcHrDT = DateTime.MinValue; DateTime clearCacheDT = nowDT; bool writeCur = Settings.WriteCur || Settings.WriteCurCopy; bool writeCurOnMod = Settings.WriteCurPer <= 0; bool writeMin = (Settings.WriteMin || Settings.WriteMinCopy) && Settings.WriteMinPer > 0; bool writeHr = (Settings.WriteHr || Settings.WriteHrCopy) && Settings.WriteHrPer > 0; curSrezMod = false; serverIsReady = true; while (!terminated) { prevDT = nowDT; nowDT = DateTime.Now; today = nowDT.Date; // расчёт времени записи срезов и вычисления значений дорасчётных каналов // при переводе времени назад или при первом проходе цикла if (prevDT > nowDT) { writeCurSrezDT = nowDT; writeMinSrezDT = CalcNextTime(nowDT, Settings.WriteMinPer); writeHrSrezDT = CalcNextTime(nowDT, Settings.WriteHrPer); calcMinDT = drmCnls.Count > 0 ? CalcNextTime(nowDT, 60) : DateTime.MaxValue; calcHrDT = drhCnls.Count > 0 ? CalcNextTime(nowDT, 3600) : DateTime.MaxValue; } // удаление устаревших файлов срезов и событий при изменении даты или при первом проходе цикла if (prevDT.Date != today) { ClearArchive(Settings.ArcDir + "Min", "m*.dat", today.AddDays(-Settings.StoreMinPer)); ClearArchive(Settings.ArcDir + "Hour", "h*.dat", today.AddDays(-Settings.StoreHrPer)); ClearArchive(Settings.ArcDir + "Events", "e*.dat", today.AddDays(-Settings.StoreEvPer)); ClearArchive(Settings.ArcCopyDir + "Min", "m*.dat", today.AddDays(-Settings.StoreMinPer)); ClearArchive(Settings.ArcCopyDir + "Hour", "h*.dat", today.AddDays(-Settings.StoreHrPer)); ClearArchive(Settings.ArcCopyDir + "Events", "e*.dat", today.AddDays(-Settings.StoreEvPer)); } lock (curSrez) { // установка недостоверности неактивных каналов SetUnreliable(); // вычисление дорасчётных каналов и выполнение действий модулей CalcDRCnls(drCnls, curSrez, true); RaiseOnCurDataCalculated(drCnlNums, curSrez); // вычисление минутных каналов и выполнение действий модулей if (calcMinDT <= nowDT) { CalcDRCnls(drmCnls, curSrez, true); RaiseOnCurDataCalculated(drmCnlNums, curSrez); calcMinDT = CalcNextTime(nowDT, 60); curSrezMod = true; } // вычисление часовых каналов и выполнение действий модулей if (calcHrDT <= nowDT) { CalcDRCnls(drhCnls, curSrez, true); RaiseOnCurDataCalculated(drhCnlNums, curSrez); calcHrDT = CalcNextTime(nowDT, 3600); curSrezMod = true; } // запись текущего среза if ((writeCurSrezDT <= nowDT || writeCurOnMod && curSrezMod) && writeCur) { if (writeCurOnMod) { WriteSrez(SrezTypes.Cur, nowDT); curSrezMod = false; writeCurSrezDT = DateTime.MaxValue; } else { WriteSrez(SrezTypes.Cur, writeCurSrezDT); writeCurSrezDT = CalcNextTime(nowDT, Settings.WriteCurPer); } } // запись минутного среза if (writeMinSrezDT <= nowDT && writeMin) { WriteSrez(SrezTypes.Min, writeMinSrezDT); writeMinSrezDT = CalcNextTime(nowDT, Settings.WriteMinPer); } // запись часового среза if (writeHrSrezDT <= nowDT && writeHr) { WriteSrez(SrezTypes.Hour, writeHrSrezDT); writeHrSrezDT = CalcNextTime(nowDT, Settings.WriteHrPer); } } // очистка устаревших данных кэша if (nowDT - clearCacheDT > CacheClearSpan || nowDT < clearCacheDT /*время переведено назад*/) { clearCacheDT = nowDT; ClearSrezTableCache(minSrezTableCache, MinCacheStorePer, MinCacheCapacity); ClearSrezTableCache(hrSrezTableCache, HourCacheStorePer, HourCacheCapacity); } // запись информации о работе приложения WriteInfo(); // задержка для экономиии ресурсов процессора Thread.Sleep(100); } } finally { // выполнение действий модулей RaiseOnServerStop(); // запись информации о работе приложения workState = WorkStateNames.Stopped; WriteInfo(); } }
private string workState; // состояние работы #endregion Fields #region Constructors /// <summary> /// Конструктор /// </summary> public MainLogic() { AppDirs = new AppDirs(); AppLog = new Log(Log.Formats.Full); AppLog.Encoding = Encoding.UTF8; Settings = new Settings(); infoFileName = ""; thread = null; terminated = false; serverIsReady = false; startDT = DateTime.MinValue; workState = ""; comm = new Comm(this); calculator = new Calculator(this); inCnls = new SortedList<int, InCnl>(); drCnls = null; drmCnls = null; drhCnls = null; drCnlNums = null; drmCnlNums = null; drhCnlNums = null; avgCnlInds = null; ctrlCnls = new SortedList<int, CtrlCnl>(); users = new SortedList<string, User>(); formulas = new List<string>(); curSrez = null; curSrezMod = false; srezDescr = null; minAvgData = null; hrAvgData = null; activeDTs = null; curSrezAdapter = null; curSrezCopyAdapter = null; eventAdapter = null; eventCopyAdapter = null; minSrezTableCache = null; hrSrezTableCache = null; modules = new List<ModLogic>(); }
/// <summary> /// Заполнить объект dest из файла срезов FileName /// </summary> protected void FillObj(object dest) { Stream stream = null; BinaryReader reader = null; DateTime fillTime = DateTime.Now; SrezTableLight srezTableLight = dest as SrezTableLight; DataTable dataTable = dest as DataTable; Trend trend = dest as Trend; SrezTable srezTable = srezTableLight as SrezTable; SrezTableLight.Srez lastStoredSrez = null; try { if (srezTableLight == null && dataTable == null && trend == null) throw new Exception("Destination object is invalid."); // подготовка объекта для хранения данных if (srezTableLight != null) { srezTableLight.Clear(); srezTableLight.TableName = tableName; if (srezTable != null) srezTable.BeginLoadData(); } else if (dataTable != null) { // формирование структуры таблицы dataTable.BeginLoadData(); dataTable.DefaultView.Sort = ""; if (dataTable.Columns.Count == 0) { dataTable.Columns.Add("DateTime", typeof(DateTime)); dataTable.Columns.Add("CnlNum", typeof(int)); dataTable.Columns.Add("Val", typeof(double)); dataTable.Columns.Add("Stat", typeof(int)); dataTable.DefaultView.AllowNew = false; dataTable.DefaultView.AllowEdit = false; dataTable.DefaultView.AllowDelete = false; } else { dataTable.Rows.Clear(); } } else // trend != null { trend.Clear(); trend.TableName = tableName; } // заполнение объекта данными stream = ioStream == null ? new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) : ioStream; reader = new BinaryReader(stream); DateTime date = Arithmetic.ExtractDate(tableName); // определение даты срезов SrezTable.SrezDescr srezDescr = null; // описание среза int[] cnlNums = null; // ссылка на номера входных каналов из описания среза while (stream.Position < stream.Length) { // считывание списка номеров каналов и КС int cnlNumCnt = reader.ReadUInt16(); if (cnlNumCnt > 0) { // загрузка номеров каналов в буфер для увеличения скорости работы int cnlNumSize = cnlNumCnt * 2; byte[] buf = new byte[cnlNumSize]; int readSize = reader.Read(buf, 0, cnlNumSize); // создание описания среза и заполнение номеров каналов из буфера // с проверкой их уникальности и упорядоченности if (readSize == cnlNumSize) { int prevCnlNum = -1; srezDescr = new SrezTable.SrezDescr(cnlNumCnt); cnlNums = srezDescr.CnlNums; for (int i = 0; i < cnlNumCnt; i++) { int cnlNum = BitConverter.ToUInt16(buf, i * 2); if (prevCnlNum >= cnlNum) throw new Exception("Table is incorrect."); cnlNums[i] = prevCnlNum = cnlNum; } srezDescr.CalcCS(); } } else if (srezDescr == null) { throw new Exception("Table is incorrect."); } // считывание и проверка КС ushort cs = reader.ReadUInt16(); bool csOk = cnlNumCnt > 0 ? srezDescr.CS == cs : cs == 1; // считывание данных среза int cnlCnt = cnlNums.Length; // количество каналов в срезе int srezDataSize = cnlCnt * 9; // размер данных среза if (csOk) { long srezPos = stream.Position; double time = reader.ReadDouble(); int hour, min, sec; Arithmetic.DecodeTime(time, out hour, out min, out sec); DateTime srezDT = new DateTime(date.Year, date.Month, date.Day, hour, min, sec); // инициализация нового среза SrezTableLight.Srez srez; if (srezTable != null) { srez = new SrezTable.Srez(srezDT, srezDescr) { State = DataRowState.Unchanged, Position = srezPos }; } else if (srezTableLight != null) { srez = new SrezTableLight.Srez(srezDT, cnlCnt); cnlNums.CopyTo(srez.CnlNums, 0); } else // srezTableLight == null { srez = null; } // считывание данных входных каналов int bufInd = 0; double val; byte stat; if (trend != null) { // выбор данных требуемого канала для тренда int index = Array.BinarySearch<int>(cnlNums, trend.CnlNum); if (index >= 0) { stream.Seek(index * 9, SeekOrigin.Current); byte[] buf = new byte[9]; int readSize = reader.Read(buf, 0, 9); if (readSize == 9) { ExtractCnlData(buf, ref bufInd, out val, out stat); Trend.Point point = new Trend.Point(srezDT, val, stat); trend.Points.Add(point); stream.Seek(srezDataSize - (index + 1) * 9, SeekOrigin.Current); } } else { stream.Seek(srezDataSize, SeekOrigin.Current); } } else { // загрузка данных среза в буфер для увеличения скорости работы byte[] buf = new byte[srezDataSize]; int readSize = reader.Read(buf, 0, srezDataSize); // заполение таблицы срезов из буфера if (srezTableLight != null) { for (int i = 0; i < cnlCnt; i++) { ExtractCnlData(buf, ref bufInd, out val, out stat); srez.CnlNums[i] = cnlNums[i]; srez.CnlData[i].Val = val; srez.CnlData[i].Stat = stat; if (bufInd >= readSize) break; } srezTableLight.AddSrez(srez); lastStoredSrez = srez; } else // dataTable != null { for (int i = 0; i < cnlCnt; i++) { ExtractCnlData(buf, ref bufInd, out val, out stat); DataRow row = dataTable.NewRow(); row["DateTime"] = srezDT; row["CnlNum"] = cnlNums[i]; row["Val"] = val; row["Stat"] = stat; dataTable.Rows.Add(row); if (bufInd >= readSize) break; } } } } else { // пропустить срез, считая его размер так, как при повторяющемся списке номеров каналов stream.Seek(srezDataSize + 8, SeekOrigin.Current); } } } catch (EndOfStreamException) { // нормальная ситуация окончания файла } catch { fillTime = DateTime.MinValue; throw; } finally { if (fileMode) { if (reader != null) reader.Close(); if (stream != null) stream.Close(); } if (srezTableLight != null) { srezTableLight.LastFillTime = fillTime; if (srezTable != null) { srezTable.LastStoredSrez = (SrezTable.Srez)lastStoredSrez; srezTable.EndLoadData(); } } else if (dataTable != null) { dataTable.EndLoadData(); dataTable.AcceptChanges(); dataTable.DefaultView.Sort = "DateTime, CnlNum"; } else if (trend != null) { trend.LastFillTime = fillTime; trend.Sort(); } } }