/// <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="source">Конфигурация источника данных</param> /// <param name="node_kvint_parameter">Узел дерева XML файла конфигурации, описывающий одиночный параметр</param> private void _get_parameter_properties(SourceConfiguration source, XmlNode node_kvint_parameter) { // чтобы получить номер параметра по его абстрактному имени, выполняем поиск ключа по значению перебором foreach (KeyValuePair <ushort, string> name in _abstract_parameter_names) { if (name.Value == node_kvint_parameter.Name) { source.Parameters[name.Key] = new ParameterConfiguration(); // по умолчанию относим сигнал к шумоподобным, если его линейность явно не указана if (node_kvint_parameter.Attributes.GetNamedItem("type") != null) { if (node_kvint_parameter.Attributes.GetNamedItem("type").Value == "linear") { source.Parameters[name.Key].Type = ParameterType.Linear; } } // записываем локальное название параметра в источнике данных, кроме источника типа odbc, где для доступа к истории значений используется коллекция sql-запросов if (source.SourceType == SourceType.Odbc) { // для источника данных odbc в качестве имени заносим абстрактное имя параметра source.Parameters[name.Key].Name = name.Value; // заполняем коллекцию sql запросов для получения значений параметров из базы данных foreach (XmlNode query in node_kvint_parameter.SelectNodes("*")) { // при использовании свойства InnerText происходит конвертация экранированных значений xml вида "<" в текстовое представление вида "<" if (query.Name == "query") { source.Parameters[name.Key].sql_queries.Add(query.InnerText); } } } else { // записываем локальное название параметра в источнике данных, используемое для доступа к истории параметра source.Parameters[name.Key].Name = node_kvint_parameter.InnerText; } } } }
/// <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> public void Load(string file) { XmlDocument xml = new XmlDocument(); // создаёт заготовку XML-документа try { xml.Load(String.Format(@"{0}\{1}", Program.base_path, file)); // загрузка файла конфигурации в память #region Загрузка настроек foreach (XmlNode __node in xml.SelectNodes("/settings/options/*")) { try { if (__node.Name == "path") { _path = __node.InnerText; } if (__node.Name == "pattern") { _pattern = __node.InnerText; } if (__node.Name == "precision") { _precision = int.Parse(__node.InnerText); } if (__node.Name == "deph") { _deph = int.Parse(__node.InnerText); } if (__node.Name == "maximum_latency") { _maximum_latency = int.Parse(__node.InnerText); } if (__node.Name == "debug_level_console") { _debug_level_console = int.Parse(__node.InnerText); } if (__node.Name == "debug_level_file") { _debug_level_file = int.Parse(__node.InnerText); } if (__node.Name == "debug_level_eventlog") { _debug_level_eventlog = int.Parse(__node.InnerText); } } catch (Exception ex) { } } _parse_pattern(_pattern); #endregion #region Загрузка конфигурации энергоблоков foreach (XmlNode block in xml.SelectNodes("/settings/blocks/*")) { if (block.Attributes.GetNamedItem("prefix") != null) { _blocks[block.Name] = new Block(); _blocks[block.Name].prefix = block.Attributes.GetNamedItem("prefix").Value; } else { break; } #region Загрузка конфигурации источников foreach (XmlNode node in xml.SelectNodes("/settings/blocks/" + block.Name + "/sources/*")) { try { SourceConfiguration source = new SourceConfiguration(); source.Name = node.Name; if (node.Attributes.GetNamedItem("type") != null) { if (node.Attributes.GetNamedItem("type").Value.ToLower() == "kvint") { source.SourceType = SourceType.Kvint; } if (node.Attributes.GetNamedItem("type").Value.ToLower() == "odbc") { source.SourceType = SourceType.Odbc; } if (node.Attributes.GetNamedItem("type").Value.ToLower() == "opc_ua") { source.SourceType = SourceType.OpcUa; } } #region Архивные станции Квинта if (source.SourceType == SourceType.Kvint) { foreach (XmlNode node_kvint in xml.SelectNodes("/settings/blocks/" + block.Name + "/sources/" + source.Name + "/*")) { if (node_kvint.Name == "station") { source.Stations.Add(node_kvint.InnerText); } if (node_kvint.Name == "parameters") { foreach (XmlNode node_kvint_parameters in xml.SelectNodes("/settings/blocks/" + block.Name + "/sources/" + source.Name + "/parameters/*")) { _get_parameter_properties(source, node_kvint_parameters); } } } } #endregion #region Источники данных ODBC if (source.SourceType == SourceType.Odbc) { foreach (XmlNode node_odbc in xml.SelectNodes("/settings/blocks/" + block.Name + "/sources/" + source.Name + "/*")) { if (node_odbc.Name == "station") { source.Stations.Add(node_odbc.InnerText); } if (node_odbc.Name == "parameters") { foreach (XmlNode node_kvint_parameter in xml.SelectNodes("/settings/blocks/" + block.Name + "/sources/" + source.Name + "/parameters/*")) { _get_parameter_properties(source, node_kvint_parameter); } } } } #endregion #region Серверы архивных данных стандарта OPC UA Historical Access (не поддерживается) #endregion _blocks[block.Name].sources.Add(source); // добавление конфигурации источника в общий список } catch (Exception ex) { } } #endregion } #endregion } catch (Exception ex) { Console.WriteLine("ERROR: ошибка загрузки конфигурации: {0}", ex.Message); } }