/// <summary>Получить значения параметров источника данных за указанный час</summary> /// <param name="begin_time">Начало часового интервала</param> /// <param name="source">Описатель источника данных</param> /// <param name="table">Буфер в памяти для приёма значений параметров</param> private static bool ReadInterval(SourceConfiguration source, DateTime begin_time, out MemoryTable table) { // диагностическая переменная для определения успешных запросов к группам источников данных bool result = false; // инициализация таблицы в памяти для приёма сырых данных table = new MemoryTable(); foreach (string _station in source.Stations) { // диагностическая переменная для определения успешных запросов к отдельным источникам данных bool _result_per_station = false; // чтение истории значений каждого параметра по отдельности foreach (KeyValuePair <ushort, ParameterConfiguration> __parameter in source.Parameters) { // обращение к источникам данных по типу switch (source.SourceType) { case SourceType.Kvint: _result_per_station = kvint_stations[_station].get_hour_interval(begin_time, __parameter.Key, __parameter.Value.Name, ref table); break; case SourceType.Odbc: _result_per_station = odbc_sources[_station].get_hour_interval(begin_time, __parameter.Key, __parameter.Value.sql_queries, ref table); break; default: break; } if (!_result_per_station) { break; // при отсутствии данных по одному из параметров, дальнейший запрос источника прекращается } } result = result || _result_per_station; // чтение интервала успешно, если успешен запрос хотя бы одного источника } return(result); }
/// <summary>Преобразование сырых данных в набор строк по шаблону</summary> /// <param name="config">Конфигурация</param> /// <param name="begin_time">Начало часового интервала</param> /// <param name="table">Таблица в памяти для принятых данных</param> /// <param name="buffer">Буфер в памяти для обработанных данных</param> private static long ProcessInterval(Configuration config, SourceConfiguration source, DateTime begin_time, ref MemoryTable table, out MemoryStream buffer) { long quality = 0; // результирующее качество выборки по числу строк с хорошим качеством buffer = new MemoryStream(); // создаёт поток в памяти для записи выборки из текущего источника StreamWriter writer = new StreamWriter(buffer, Encoding.UTF8); // создаёт писатель текстовых данных в бинарный поток for (uint row = 0; row < 3600; row++) { int row_quality = 0; // результирующее качество строки данных string result = config.pattern; // запрос строки: названия параметров заменяются ближайшими по времени значениями foreach (string _str in Regex.Split(config.pattern, @"[;:]+")) { result = result.Replace("_row_", row.ToString()); // вставка номера строки (секунды) if (Regex.IsMatch(_str, @"^[$][A-z]+[$]$")) { // чтобы получить номер параметра по его абстрактному имени, выполняем поиск ключа по значению перебором foreach (KeyValuePair <ushort, string> __abstract_name in config.abstract_parameter_names) { if (__abstract_name.Value == _str.Trim('$')) { // объявления переменных для запроса значения параметра bool column_quality; double column_value; DateTime column_timestamp; // запрос значения параметра, ближайшего по запрашиваемому времени if (table.read_sample(__abstract_name.Key, begin_time.AddSeconds(row), out column_timestamp, out column_value, out column_quality)) { // для шумоподобных параметров анализируется отставание метки времени последнего полученного значения от времени запроса // если отставание превышает maximum_latency секунд, строке присваивается плохое качество if (source.Parameters[__abstract_name.Key].Type == ParameterType.Noisy && (begin_time.AddSeconds(row) - column_timestamp).CompareTo(new TimeSpan(0, 0, config.maximum_latency)) > 0) { column_quality = false; } // замещение названий параметров значениями result = result.Replace(_str, String.Format(nfi, "{0:F" + config.precision + "}", column_value)); // подсчёт количества значений с хорошим качеством для последующей оценки качества строки row_quality = row_quality + (column_quality ? 1 : 0); } } } } } // вставка интегрального качества выборки, вычисляется сравнением количества значений с хорошим качеством, с общим количеством зарегистрированных параметров result = result.Replace("_quality_", row_quality == config.abstract_parameter_names.Count ? "1" : "0"); // запись строки с результатом в поток с результатом для текущего источника writer.WriteLine("{0}", result); // подсчёт строк с хорошим качеством if (row_quality > 0) { quality++; } } writer.Flush(); // сброс буфера текстового писателя потока buffer.Seek(0, SeekOrigin.Begin); // перемещение файлового указателя потока в начальную позицию return(quality); // возврат интегрального качества выборки }
/// <summary>Получает часовой набор значений указанного параметра за указанный период</summary> /// <param name="start_time">Начало интервала</param> /// <param name="parameter_index">Индекс параметра</param> /// <param name="station_parameter_name">Локальное название параметра архивной станции</param> /// <param name="table">Ссылка на таблицу в памяти для приёма истории значений параметра</param> /// <returns>Запрос успешен: параметр существует и получены данные с хорошим качеством</returns> internal bool get_hour_interval(DateTime start_time, ushort parameter_index, string station_parameter_name, ref MemoryTable table) { user_data_reference _user_data_ref = new user_data_reference(); // структура для передачи ссылки на дополнительные параметры через неуправляемый код UserData _user_data = new UserData(); // структура для транзита дополнительных параметров через неуправляемый код _user_data_ref.data = _user_data; // вставляем в структуру UserData ссылку на класс с дополнительными полями данных _user_data_ref.data.parameter_index = parameter_index; // индекс параметра _user_data_ref.data.count = 0; // счётчик принятых значений параметров с хорошим качеством _user_data_ref.data.table = table; // указательно на таблицу в памяти для приёма сырых значений IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(_user_data_ref)); // распределяем память в куче для копирования туда индекса кэша Marshal.StructureToPtr(_user_data_ref, pointer, false); // копируем индекс кэша в кучу Services.QAQuery query = new Services.QAQuery(); // структура, описывающая запрос к АС query.LinkId = _link_id; // дескриптор подключения к архивной станции query.Callback = new Services.QACallback(_get_historical_data); // процедура для приёма данных от АС query.BeginTime = new Services.QATime(start_time); // начало запрашиваемого интервала query.EndTime = new Services.QATime(start_time.AddHours(1)); // конец запрашиваемого интервала query.Flags = Services.QF_BEGINOUTSIDE | Services.QF_FILTLAST; // флаги запроса query.Accuracy = 1; // шаг времени для просеивания query.UserData = pointer; // указатель на буфер в неуправляемой памяти с дополнительными параметрами запроса bool parameter_exist = load_parameter(station_parameter_name, out query.ParamId); // проверка наличия параметра на АС и получение его атрибутов if (parameter_exist) { Logger.Log(4, String.Format(" INFO4: [{0}] запрос значений параметра {1}", _station, station_parameter_name)); _execute_query(ref query); } Marshal.FreeHGlobal(pointer); // освобождаем выделенную в куче память return(_user_data_ref.data.count > 0); // признак успешности запроса }
/// <summary>Получает часовой набор значений указанного параметра за указанный период</summary> /// <param name="start_time">Начало интервала</param> /// <param name="parameter_index">Индекс параметра</param> /// <param name="sql_queries">Название параметра (не используется в ODBC)</param> /// <param name="table">Ссылка на таблицу в памяти для приёма истории значений параметра</param> /// <returns>Запрос успешен: параметр существует и получены данные с хорошим качеством</returns> internal bool get_hour_interval(DateTime start_time, ushort index, Collection <string> sql_queries, ref MemoryTable table) { // счётчик принятых успешно данных uint count = 0; // обработка каждого, из ассоциированных с параметром, запросов foreach (var _query in sql_queries) { // выполняем вставку начала обрабатываемого часа в sql запрос string _sql_query = Regex.Replace(_query, "_unix_basetime_", (start_time - new DateTime(1970, 1, 1)).TotalSeconds.ToString()); // проверка доступности источника данных if (_source_active) { // выполнение запроса using (OdbcDataReader _reader = new OdbcCommand(_sql_query, _odbc_connection).ExecuteReader()) { // циклическое чтение данных while (_reader.Read()) { // подсчёт успешно принятых строк данных count++; // запись результатов в таблицу в памяти table.write_value(index, new DateTime(1970, 1, 1).AddSeconds(_reader.GetDouble(0)), _reader.GetDouble(1), true); } } } } return(count > 0); }