Квитирование сообщений

<< Click to Display Table of Contents >>

Navigation:  Проект > Элементы дерева объектов > Палитра ФБ > Служебные > Скрипт > Руководство и примеры > Работа с архивом сообщений >

Квитирование сообщений

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

Для примера сделаем дерево следующей структуры:

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_kvitirovanie_soobshchenij1

У нас 4 объекта, во всех - по 3 сообщения разных категорий. С помощью скрипта нужно иметь возможность выполнить квитирование всех сообщений данных категорий. Создадим скрипт, в который добавим два входа КвитироватьАварии и КвитироватьПредупреждения.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_kvitirovanie_soobshchenij2

Сначала в методе Start найдем ID нужных нам категорий - аварии и предупреждения, и сохраним их ID в отдельных переменных - CatCrash и CatWarning.

bool? M = false;
bool? L = false;
uint CatCrash = 0;
uint CatWarning = 0;
 
public override void Start()
{
    var project = HostFB.TreeItemHlp.Project;
    //фильтр по определенным категориям
    foreach (var cat in project.SystemTreeRootItem.EventCategories.Values)
    {
        if (cat.Name == "Авария")
        {
            CatCrash = (uint)cat.ID;
            continue;
        }
        if (cat.Name == "Предупреждение")
        {
            CatWarning = (uint)cat.ID;
            continue;
        }
    }
}

В методе Execute мы будем вызывать метод, в который будем передавать ID категории, и который будет выполнять квитирование. Сначала напишем этот метод.

private void Ack(uint Cat)
{
    var filter_ack = new EventFilterData();
    filter_ack.OnlyNotAcked = true; //только неквитированные       
    filter_ack.EventCategories = new uint[1] { Cat };//фильтр по категории
    var events_ack = HostFB.TreeItemHlp.Project.AlarmManager.GetEvents(HostFB.TreeItemHlp.Parent, filter_ack, 50);
    EventID[] Ids = events_ack.Select(x => x.EventID).ToArray();
    HostFB.TreeItemHlp.Project.AlarmManager.AckEvents(Ids, "Comment");
}

Половина кода нам уже известна - создается фильтр, в его настройках указывается только неквитированные сообщения, а также фильтрация по переданной категории. При задании фильтра категории необходимо передать массив, поскольку у нас всего один элемент, то мы создаем массив с одним элементов и передаем ему в конструктор ID нашей категории.

Затем при помощи метода GetEvents запрашиваются сообщения текущего объекта, с настройками фильтра.

С помощью метода Select происходит выборка из массива events_ack, идентификаторов сообщений EventID. Чтобы объявление EventID стало бы доступным, нужно добавить пространство имен:

using MasterSCADA.Interfaces;

Главная строчка в данном коде - это квитирование в методе AckEvents.

HostFB.TreeItemHlp.Project.AlarmManager.AckEvents(Ids, "Comment");

В качестве аргументов данный метод принимает массив ID сообщений и комментарий, с которым будут квитированы сообщений.

Осталось только вызвать данный код в методе Execute.

public override void Execute()
{
    if (КвитироватьАварии == true && M == false)
    {
        Ack(CatCrash);
    }
    M = КвитироватьАварии;
 
    if (КвитироватьПредупреждения == true && L == false)
    {
        Ack(CatWarning);
    }
    L = КвитироватьПредупреждения;
}

Итоговый код будет выглядеть следующим образом.

public partial class ФБ : ScriptBase
{
    bool? M = false;
    bool? L = false;
    uint CatCrash = 0;
    uint CatWarning = 0;
 
    public override void Start()
    {
        var project = HostFB.TreeItemHlp.Project;
        //фильтр по определенным категориям
        foreach (var cat in project.SystemTreeRootItem.EventCategories.Values)
        {
            if (cat.Name == "Авария")
            {
                CatCrash = (uint)cat.ID;
                continue;
            }
            if (cat.Name == "Предупреждение")
            {
                CatWarning = (uint)cat.ID;
                continue;
            }
        }
    }
 
    public override void Execute()
    {
        if (КвитироватьАварии == true && M == false)
        {
            Ack(CatCrash);
        }
        M = КвитироватьАварии;
 
        if (КвитироватьПредупреждения == true && L == false)
        {
            Ack(CatWarning);
        }
        L = КвитироватьПредупреждения;
    }
 
    private void Ack(uint Cat)
    {
        var filter_ack = new EventFilterData();
        filter_ack.OnlyNotAcked = true; //только неквитированные       
        filter_ack.EventCategories = new uint[1] { Cat };//фильтр по категории
        var events_ack = HostFB.TreeItemHlp.Project.AlarmManager.GetEvents(HostFB.TreeItemHlp.Parent, filter_ack, 50);
        EventID[] Ids = events_ack.Select(x => x.EventID).ToArray();
        HostFB.TreeItemHlp.Project.AlarmManager.AckEvents(Ids, "Comment");
    }
}

Попробуем проверить. Запустим режим исполнения и включим несколько сообщений разных категорий и откроем журнал.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_kvitirovanie_soobshchenij3

Теперь включим вход КвитироватьАварии - сообщения квитируются.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_kvitirovanie_soobshchenij4

Попробуйте сделать аналогично и с сообщениями предупреждений.

Теперь попробуем сделать несколько сообщений одного источника.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_kvitirovanie_soobshchenij5

А теперь попробуем их квитировать.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_kvitirovanie_soobshchenij6

Квитировалось только последнее, а остальные остались не квитированными.

Дело в том, что в MasterSCADA есть кэш сообщений и есть архив сообщений. Сначала сообщение поступают в кэш, а спустя время, или если появится новое сообщение, попадают в архив. Метод GetEvents берет сообщения именно их кэша. Когда мы просто отслеживаем сообщения и сразу же на них реагируем, то это нормально, но если нам требуется выборку из архива сообщений, т.е. несколько сообщений от источника или вовсе сообщения с прошлого запуска, то и нужно обращаться к архиву. Для этого есть отдельный метод.

Рассмотрим данный метод на том же примере. Сделаем копию имеющегося скрипта и будем редактировать его - назовем его "Скрипт квитирования из архива". Изменения затронут только метод Ack - остальной код останется таким же.

Для того чтобы получить архив сообщений нужно сделать получить класс сервера сообщений, с нужными параметрами (объект и фильтр):

var server = HostFB.TreeItemHlp.Project.GetService<EventServer>();
var enumerator = server.CreateEnumerator(HostFB.TreeItemHlp.Parent, filter_ack, true);

А затем загружать из него сообщения порционно с помощью метода:

var archEvents = enumerator.Next(500);                          

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

Для того чтобы получить доступ к серверу сообщений нужно сначала подключить библиотеку MasterSCADA.Archive.dll, на вкладке Настройки.

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_kvitirovanie_soobshchenij7

А затем добавить пространство имен:

using MasterSCADA.Archive.Events;

Код метода Ack будет выглядеть следующим образом:

private void Ack(uint Cat)
{
    var filter_ack = new EventFilterData();
    filter_ack.OnlyNotAcked = true;
    filter_ack.EventCategories = new uint[1] { Cat };
    var server = HostFB.TreeItemHlp.Project.GetService<EventServer>();
    var enumerator = server.CreateEnumerator(HostFB.TreeItemHlp.Parent, filter_ack, true);
    while (true)
    {
        var archEvents = enumerator.Next(500);
        if (archEvents.Count == 0) break;
        EventID[] Ids = archEvents.Select(x => x.EventID).ToArray();
        HostFB.TreeItemHlp.Project.AlarmManager.AckEvents(Ids, "Comment");
    }
}

Запустим режим исполнения и попробуем теперь квитировать наши сообщения:

sluzhebnie_skript_rukovodstvo_i_primery_generaciya_arhiva_kvitirovanie_soobshchenij8

Теперь все сообщения квитировались корректно.

Однако Enumerator.Next выполняет обращение к базе, а как мы уже писали ранее такие запросы нужно выполнять в асинхронных метода. Но адаптировать его на асинхронный запрос не сложно - исправленный пример по ссылке.