Формирование отчета с помощью ФБ READ_ARCHIVE_DATA

<< Click to Display Table of Contents >>

Navigation:  Проект в MasterSCADA 4D > Рекомендации по созданию проектов >

Формирование отчета с помощью ФБ READ_ARCHIVE_DATA

Содержание статьи:

1.Общее описание

2.Постановка задачи

3.Генерация архивного значения

4.READ_ARCHIVE_DATA

5.Массив структур типа SYSTEM_*_PARAM

6.Таблица данных. Условное выделение ячеек

7.Выгрузка данных в .CSV файл с помощью ФБ StringToFile

1. Общее описание.

На большинстве контроллеров формирование отчетов, с использованием стандартного редактора отчетов, технически невозможно, поэтому если стоит задача отобразить архивные данные в табличном виде на контроллере, то придется использовать ФБ READ_ARCHIVE_DATA (далее RAD) с выводом результата в графический элемент Таблица данных и записью в сторонний .csv файл. Этот пример может быть также полезен для дополнительного освоения MasterSCADA 4D в других прикладных задачах.

2. Постановка задачи.

Сгенерировать архивные значения параметра объекта, при этом формируя недостоверный признак качества. Отобразить этот параметр на графике, выделив аварийный диапазон значений. Использовать начало и конец отображаемого интервала тренда для формирования массивов значений, признаков качества и меток времени с помощью RAD. Полученные массивы собрать в единый массив структур и вывести его на таблицу данных. В таблице выделить ячейки с аварийными значениями, а также строку с недостоверным признаком качества. Также этот массив нужно записать в сторонний .csv файл.

3. Генерация архивного значения

 

В проекте есть архивируемый параметр типа SYSTEM_LREAL_PARAM с настройками архивирования по умолчанию. Нужно задать значения полей Value, SourceTime и StatusCode.

9dCbqyzVQJXiwnDGVbqGPMqvXi-bwCEU-7uIue7Ha2wmk-zDeoHmyXbMuJw8aIeXPCCOCPd09nPklCe-HDBStI3F-GE2D85kcIQ5zChqWpggcY71Zb2danNr8I1FAL6JQTAgMCPo

Делаем это с помощью генераторов сигналов из библиотеки OSCAT:

Oovj7pHieoGq-vjwbZ-pjowOJ0_9a5mHSxj9nqxwzNer2cwFWzeGi-u-L6PRmxjq-tltzHY5GjRhuoarcjKtGxTTaPCxTlM9vgrOEMQ_quLAVJTjYuWZbpL1q6h9tE9KfflcgSEh

 

Т.е. значение меняется произвольно от 0 до 100 раз в 1 секунду, метка времена формируется с помощью функции GET_UTC_TIME, а признак качества будет недостоверным в течении 2 секунд.

У параметра установим настройки шкалы AI, будем использовать верхний и нижний аварийные значения:

8Ex8u7uxbYNRHpE2zXCFVVdCH-4hJh1-KdYeYKOQtjz1-vozC0ciTMO1Hi5ICddE1gy2wFxHYnfniJVA1hRudEbt4ZaDX-xSLmwYNeBozCWHRDUSYTi36sJk_UrYxsVyJW5CBtAE

Нужно обработать полученные архивные данные, отобразить эти данные в табличном виде и экспортировать эти данные. Для решения такой задачи обычно используется стандартный редактор отчетов, но на контроллерах сформировать их нельзя. В таком случае можно воспользоваться графическим элементом Тренд. На стартовое окно из палитры нужно вытащить тренд и архивный параметр перетащить на график.

Получим результат:

zf6HQXk7wvz2UhTatRrALgn_6PLmeryLBc2pFY0YoCVNNdVCtk4Wh_29xTbgcjVhrkg731jaBbAbijZEiughKQMVVJauTTfv0XBBokCESku24MWWESww-LnPWP5R-3Haymgvtsec

 

Тренд позволяет вывести архивные значения за отображаемый интервал, экспортировать эти данные в .csv, а с помощью легенды тренда можно получить максимальное и минимальное значения за отображаемый период.

VXHjvKn-UJXeaKGbbbqvPQVloGVdhTPl-QRDEgPoTQh3gie2EQyeW9va4Xa5Ug0Z3qefSD6QlwkVZkktoy2RQNH9fhfzcv5pUyA_GxHzHAH_vh9i70K8E5jo-wI-c-uWFxtdt-e7

Если на данном этапе для вас и заказчика проблема решена - то можно смело дальше не читать. Но если: тренд вообще не предполагался, выводимая таблица слишком маленькая и без выделения всех ячеек, в таблице нет признака качества, обработок в легенде недостаточно и вообще требуется построить аналог периодического отчета, а не отчета по изменению - приступаем к следующему пункту.

4. READ_ARCHIVE_DATA

ФБ READ_ARCHIVE_DATA позволяет получить массив значений, массив меток времени и массив признаков качества за определенный промежуток времени, а также выполнить обработку архивируемых значений за интервал.

Вид блока в редакторе FBD:

zEttmrB6_UCZKIwTf-hCmZr5YVIqJp1ueeXKb8H8KMmD9jOHQQB54JmMHPQDhsAHOgIsCIKk26sQ-CpaRnJj0-nJIbJhPzX83L2NagfMb_c0SvDx2H6SMWPDqEBP2xg1ABgJcVcd

В нашем примере начало и конец будем брать из графика, так как тренд может возвращать по исходящей связи значения интервала и конца:

 

-VFCsxSg-iDUiN-81a2kKx-sq0RqQdwQIDHbkO0RjSkj4rGkcintkwh1GTjzJbDGz0oPl5l3YJfGSTqeJ_mpRMlGazDn4oTUUztM6CEA0igBpEUBwh_DTBiwZ4j7Di1MWKA9ZFiV

Здесь мы это делаем для удобства, в вашем проекте будут свои начало и конец для RAD. Тренд не возвращает свойство Начало, поэтому его мы посчитаем отдельно в программе:

 

J9WMLFuXy71-t0oYkpNni2o-uqFpn0U7v09jTgI9xHScx0VbNn6o3JHYmXXMF49_tRVDfffr5lmdF7Zhrv25fgzXhtgWxsESaxbtWjZGMS7DpfeTZU7-hRrOas5nBZeS7gfjLKDQ

 

Далее нужно задать параметр, по которому будем производить запрос в базу. Для этого нужно задать входную переменную Item. Это можно сделать вручную, узнав id архивного параметра в служебных свойствах, либо создать входную переменную программы Ссылка типа REF_TO (этот тип возвращает id элемента, с которым связан), связать с архивным параметром и ножкой Item ФБ RAD:

tk-KwCGZ0gXP8dQzXtd4JmIt0Myq3OM-Ls1RfzG2BLTIrxUmsBvtWU8z5rcXGplkBqZquAcIzgZ1t9iMj1gDcKrI_o_mh-PdCt4BGjb4ba8OXQCRtbI2hkEY9ANMfPGgRfaAQouX

Далее определим, как обрабатывать архивные значения параметров с помощью входа Aggregate. По умолчанию вход имеет значение Sampling. В таком случае после вызова ФБ вернется массив архивных значений без какой-либо обработки, т.е. результатом будет та же таблица, которую формирует тренд, или отчет по изменению. Но если стоит задача провести обработку архивных значений за небольшие интервалы, т.е. построить периодический отчет, то нужно поменять значения входа Aggregate и Interval.

У нас есть архивные значения за большой промежуток времени, например, расход электричества за год, и нужно посчитать среднее потребление в месяц. В таком случае выбирается значение входа Aggregate - Average, а интервал 1 месяц, разница между Begin и End будет составлять один год. Результатом будет массив из 12 значений, и каждое значение будет среднее за интервал в 1 месяц, т.е. система автоматически просуммирует все значения и поделит на число таких значений. Тут важно еще следить за входом Bound, чтобы учитывать значения на границах интервалов. В примере мы с вами посмотрим все возможные обработки, поэтому Aggregate будем менять с помощью выпадающего списка на окне.

LHKyXMQt1HopSUkH7foPSmKZTGBD73_GTr9XyRkmcVpZquC9s_7UHw9ttNVAJDsmszXPAepPGeeksRbKBG2Xi7TDfCJq2ep33kc9THLUeqPBkh2d0waX69BM8jR4MxgqWA-5U9un

После вызова этого ФБ с помощью задания True входа Run (на окне обычной кнопкой) получим нужные нам массивы, а также количество элементов в этих массивах:

OEZZkYivpnYr0pIpYpm6TwfeqIcsSa3eKK1-9MJxfW3quwcDPCrZpmcPqTVu-_I7luNEL-22IIj29dJQxGz79F7i6DdnCT1o-zz5kjqRbZtIjHmOFIf7KEbxHT2kSHoGhjkahhZw

Далее с массивами можно делать любые операции. В частности, если текущих обработок недостаточно и у вас свои уникальные расчеты, то далее нужно писать свой ФБ, который бы производил эти вычисления с полученными данными. Нам также нужно это сделать, нам нужно собрать массив значений, меток времени и признаков качества в единый массив структур, так как источником данных для таблицы является именно массив структур.

5. Массив структур типа SYSTEM_*_PARAM

В локальную библиотеку добавим ФБ на языке ST. Входными переменными будут динамические массивы типа LREAL, DT и UDINT, так как количество значений, возвращаемых RAD, разное после каждого вызова ФБ:

jJFMQgLObKLCsi3jDEukAtDDhbMb8mZagNAvq0WUJt6HgZBp5Ax7blVApUtkI3eWIUJ-UuShVI4aVAlJEhfWm1nJRCOP99vXj9E2UuYb2oeTBFEu2D_kb9zdiYq8Q9U2ksMh8U01

Также входной переменной будет число элементов массива, а результатом ФБ - массив структур типа SYSTEM_LREAL_PARAM:

 

sJO6GcimUKJwBjxeTdflGpBd5k5GoE8o3qiqbISgBVW-S8MVY2RkkWeTE4NGc75ToxqWTDBgnkUTRha1U18aj_Z73_tKP2WiyjUNCQ9mHXJfddMQQDYW3VFeBVptWOVvdQcLrKNd

Тело программы:

OutArray := CREATE_ARRAY(INIT:= Переменная_2, SIZE:= Count); //каждый раз создаем новый массив с новой длиной

 

for i := 1 to Count do

  OutArray[i].Value := InputArrayData[i]; //простое присвоение

  OutArray[i].SourceTime := InputArrayDate[i];

  OutArray[i].StatusCode := InputArrayStatusCode[i];

END_FOR;

 

И экземпляр этого ФБ используемой в основной программе:

aX5RIpbKn_XRfAitQhNus3dKHSNzWMlIgZQrh06bn6BAqZeAyfvwDegL5T_CK60YaxETDMHBWvpsOnmDmh_Rhf8uaW-b9vnOfEj416b1r8AOjXXpNOseujYwQv2V-qAdGUEYTjp6

 

OutArray будем использовать в качестве источника для таблицы данных.

 

6. Таблица данных. Условное выделение ячеек

 

На окно вытащим графический элементТаблица данных и установим связь свойства Источник данных этой таблицы с параметром типа динамический массив структур SYSTEM_LREAL_PARAM:

VrhDH9cV17_XILrQHB2TBoGtMYbO4Y8naxdbSjG44PPkWhTtKOTaIy4qRpKoRekVVFwu49wrCVYYBvsj4RQRJxDAgDnZhh4fdUDDqFnpSkpC9MSunajPu5gJ83uVfUzl52ZywnG6

Дважды совершим клик по таблице и в редакторе таблицы зададим столбцы:

jYYUbM0V9yXaKkBIKns7fNPrnBzYebzca6dxR9ZzLpSU62dDR5x0Avs_xe69N-44arONBVIju4enEDF_V4cHi6XTDZjUBs1FoGe9AMcA6FwpU9RgQgt5dkxLOR2Na7KOyF_GwLWd

Необходимо выделить в таблице те ячейки, которые не попали в нормальный диапазон 10..90, а также выделить строки, соответствующие плохому признаку качества. Для начала перейдем в стиль строки и зададим точечную конвертацию по StatusCode:

isy1p2NTR0xIcvPQor9eZ6eXVuDVPdXS9U8i6HA50s14tBYGlZj2oMK588DC9b1k9UKZJRYqXnjK7aQWf6OwBxd3g2KxWk45-6La7Bzy1jObICtGUhqHklRJgWtXNGv-o9F9hxkq

 

Затем в стиле ячейки создадим стиль Авария, в котором по значениям Value будем менять цвет текста и его жирность:

zBjVpEjwCsPSEJWd3WHcoWsdkCHXuGkGBVmxhdeTzn3sMFfv0c_bDIGMwnEiTE-b7WWWY-WplrsHiNEmvqnkL5BlTv6fx81kClF0-dx81407BA0kG_daSS0LX8-Rim6LjB4rQGWV

Вернемся в редактор столбцов таблицы и для столбца Value установим стиль ячеек Авария:

uMn9k6feBdP3jj_-uhCekx5JQHs7Y9RW23VhThfq6hzkMCx17W1yIEr7H16qqlXIEvzMLIl5w6tOzjIDFPhKeeGdD2WqntcmO_tPxLXqI3lYeYWNa5visLeWxdUt0_MrXTPp2WuP

В результате в среде исполнения вы сможете задать обработку аварийных значений на схеме, а также выделить нужные значения в таблице данных:

 

eKrqg4dswjShnhnnZ3m1ATvlcHVfEaRWfgyh0wIE-73fwNCiuMYsdIT8mGiCuRw7L8ngeIBJDKlUlhlfZ0k5N0oWTC3AhCSYXaLpdS8g2oNnI96f-mog6KBAdFQ7TMc4XgA6097y

7. Выгрузка данных в .CSV файл с помощью ФБ StringToFile

В примере выше есть один большой недостаток - эту таблицу не экспортировать и не распечатать, на момент написания этого документа такая задача еще стоит в очереди на разработку. Но есть способ записать массив структур в сторонний .csv с помощью ФБ StringToFile. Создадим программу:

 

count := UPPER_BOUND(ARR:= Результат, DIM:= 1);

a_1 := Результат[1].Value;

b_1 := Результат[count].Value;

 

if  a_1 <> a and b_1 <> b THEN

    Переменная_7 := Переменная_8;

   a := Результат[1].Value;

   b := Результат[count].Value;

ELSIF a = a_1 and b = b_1 and i <> count THEN

  for i := 1 to count do

       Переменная_1 := CONCAT(IN1:= LREAL_TO_STRING(Результат[i].Value) , IN2:= Переменная_2);

       Переменная_4 := CONCAT(IN1:= DT_TO_STRING(Результат[i].SourceTime), IN2:= Переменная_2);

       Переменная_5 := CONCAT(IN1:= UDINT_TO_STRING(Результат[i].StatusCode), IN2:= "");

       Переменная_6 := CONCAT(IN1:= CONCAT(IN1:= CONCAT(IN1:= Переменная_1, IN2:= Переменная_4) , IN2:=Переменная_5 ) , IN2:=Переменная_3 );

       Переменная_7 := CONCAT(IN1:= Переменная_7, IN2:= Переменная_6);

  END_FOR;

END_IF;

 

StringToFile_1(Input:= Переменная_7, FileName:="C:\Users\User\Desktop\43.csv" , Write:= Запись);

 

Что здесь происходит?

count := UPPER_BOUND(ARR:= Результат, DIM:= 1); - получаем количество элементов массива структур

a_1 := Результат[1].Value; - получаем первый элемент массива структур

b_1 := Результат[count].Value; - получаем последний элемент массива структур

Эти значения нам нужны для того, чтобы затем обнулять результат записи в файл.

if  a_1 <> a and b_1 <> b THEN

   Переменная_7 := Переменная_8;

   a := Результат[1].Value;

   b := Результат[count].Value;

 

Переменная_8 - пустая строка, Переменная_7 - результат, который надо записать в csv файл. Поэтому если мы поменяли интервал, то предыдущий результат надо обнулить.

 

ELSIF a = a_1 and b = b_1 and i <> count THEN  - если же интервал не менялся, то можно задать значение  переменной типа STRING

 

  for i := 1 to count do

       Переменная_1 := CONCAT(IN1:= LREAL_TO_STRING(Результат[i].Value) , IN2:= Переменная_2);

       Переменная_4 := CONCAT(IN1:= DT_TO_STRING(Результат[i].SourceTime), IN2:= Переменная_2);

       Переменная_5 := CONCAT(IN1:= UDINT_TO_STRING(Результат[i].StatusCode), IN2:= "");

       Переменная_6 := CONCAT(IN1:= CONCAT(IN1:= CONCAT(IN1:= Переменная_1, IN2:= Переменная_4) , IN2:=Переменная_5 ) , IN2:=Переменная_3 );

       Переменная_7 := CONCAT(IN1:= Переменная_7, IN2:= Переменная_6);

  END_FOR;

END_IF;

 

Почему так сложно?

Потому что разделителем между столбцами в csv является точка с запятой “;”, а переходом на следующую строку перевод каретки. Поэтому в цикле мы каждый раз приклеиваем сначала к Value значение SourceTime через точку с запятой, затем к этой новой строке приклеиваем значение признака качества, а уже потом к строке целиком добавляем перевод каретки. И так каждый элемент массива.  

StringToFile_1(Input:= Переменная_7, FileName:="C:\Users\User\Desktop\43.csv" , Write:= Запись);

Это вызов ФБ SrtingToFile - на вход идет стринговое представление массива структур, далее нужно обязательно указать путь к файлу, куда пишем значения.

В результате получим:

KI-jjbeOoAX2HCasSeyyUad6ClEgT9_VWVSvlJCdUMGqtzmizrFi7KJfuOGplkeaCS0pEkjOrFWCIrva3VkU2OXw3q8A9iwb6ZNhLsjeQ0Lx6IyzlIy5mVtvSKF8D2q5ghw3eviS